NRF24L01双向传输(一对一)
简介
本文章记录两个NRF24L01无线模块实现双向传输的软件设计~
为什么可以双向传输呢?这要归功于它具有Enhanced ShockBurst,可以工作在主接收和主发送模式,在主接收方可以将我们自己的数据附在ACK Packet实现双向传输,所以此次双向传输会将两个NRF分别设置成主接收和主发送。
· 实物(模块非单独芯片):
网上最便宜的就这种不带运放的模块,大家如果想用NRF24L01的话,建议大家使用加了运放的模块 ,传输距离会比较远一点,没运放的话距离10米就开始出现丢包现象了。另外,进口的和国产的用起来距离一样,所以优先选择国产,因为进口的还贵点。
·芯片引脚定义说明(芯片):
·**数据包格式 **:
这里没必要介绍NRF24L01了,相信要用这芯片或者模块的朋友应该提前了解过了。
· 寄存器表
该芯片有命令寄存器和功能寄存器。在使用每一款芯片之前,要养成查看数据手册的习惯。这是我自己边看边翻译的,可能有翻译地不对的,见谅哈。
命令寄存器表:
[tr]命令名命令字#数据字节操作[/tr]
R_REGISTER | 000A AAAA | 1到5,低位在前 | 读命令/状态寄存器 |
W_REGISTER | 001A AAAA | 1到5,低位在前 | 写命令/状态寄存器 |
R_RX_PAYLOAD | 0110 0001 | 1到32,低位在前 | 读RX_Payload(1到32个字节) |
W_TX_PAYLOAD | 1010 0000 | 1到32,低位在前 | 写TX_Payload(1到32个字节) |
FLUSH_TX | 1110 0001 | 0 | Flush TX FIFO,发送模式使用 |
FLUSH_RX | 1110 0010 | 0 | Flush RX FIFO,接收模式使用 |
REUSE_TX_PL | 1110 0011 | 0 | 重用上次发送的载荷(数据包) |
R_RX_PL_WID | 0110 0000 | 1 | 读取RX payload宽度 |
W_ACK_PAYLOAD | 1010 1PPP | 1到32,低位在前 | 写载荷(这些载荷数据同应答包一起从PPP通道发出) |
W_TX_PAYLOAD_NO ACK | 1011 0000 | 1到32,低位在前 | 失能自动应答,用于发送模式 |
NOP | 1111 1111 | 0 | 无操作,可用于读取状态寄存器 |
上面的AAAAA就是后面即将看到的功能寄存器的映射地址。
功能寄存器地址映射表:
[tr]地址助记符位复位值类型描述[/tr]
00 | CONFIG |
|
|
| 配置寄存器 |
| Reserved | 7 | 0 | R/w | 只能为0 |
| MASK_RX_DR | 6 | 0 | R/w | 接收中断标志。0:使能 |
| MASK_TX_D | 5 | 0 | R/w | 发送中断标志。0:使能 |
| MASK_MAX_RT | 4 | 0 | R/w | 最大重发次数中断。0:使能 |
| EN_CRC | 3 | 1 | R/w | 使能CRC |
| CRCO | 2 | 0 | R/w | CRC编码方案选择,1~2bytes |
| PWR_UP | 1 | 0 | R/w | 1:上电,0:下电 |
| PRIM_RX | 0 | 0 | R/w | 收发控制。1:PRX;0:PTX |
01 | EN_AA |
|
|
| 使能自动应答 |
| Reserved | 7:6 | 00 | R/w | 只能为0 |
| ENAA_P5 | 5 | 1 | R/w | 使能通道5自动应答机制 |
| ENAA_P4 | 4 | 1 | R/w | 使能通道4自动应答机制 |
| ENAA_P3 | 3 | 1 | R/w | 使能通道3自动应答机制 |
| ENAA_P2 | 2 | 1 | R/w | 使能通道2自动应答机制 |
| ENAA_P1 | 1 | 1 | R/w | 使能通道1自动应答机制 |
| ENAA_P0 | 0 | 1 | R/w | 使能通道0自动应答机制 |
02 | EN_RXADDR |
|
|
| 使能 RX 地址 |
| Reserved | 7:6 | 00 | R/w | 只能为0 |
| ERX_P5 | 5 | 0 | R/w | 使能通道5 |
| ERX_P4 | 4 | 0 | R/w | 使能通道4 |
| ERX_P3 | 3 | 0 | R/w | 使能通道3 |
| ERX_P2 | 2 | 0 | R/w | 使能通道2 |
| ERX_P1 | 1 | 0 | R/w | 使能通道1 |
| ERX_P0 | 0 | 0 | R/w | 使能通道0 |
03 | SETUP_AW |
|
|
| 设置地址宽度(所有通道的地址) |
| Reserved | 7:2 | 000000 | R/w | 只能为0 |
| AW | 1:0 | 11 | R/w | 收发地址宽度,’11’-5bytes |
04 | SETUP_RETR |
|
|
| 设置自动重发射机制 |
| ARD | 7:4 | 0000 | R/w | 自动重发延时’0001’-等待500us |
| ARC | 3:0 | 0011 | R/w | 自动重发计数 |
05 | RF_CH |
|
|
| 设置RF频宽 |
| Reserved | 7 | 0 | R/w | 只能为0 |
| RF_CH | 6:0 | 0000010 | R/w | 设置RF的工作频宽 |
06 | RF_SETUP |
|
|
| RF设置寄存器 |
| CONT_WAVE | 7 | 0 | R/w | 为1使能连续传送载波 |
| Reserved | 6 | 0 | R/w | 只能为0 |
| RF_DR_LOW | 5 | 0 | R/w | 设置RF数据250kbps |
| PLL_LOCK | 4 | 0 | R/w | 仅使用于测试? |
| RF_DR_HIGH | 3 | 1 | R/w | ‘00’-1M,’01’-2M,’10’-250kbps |
| RF_PWR | 2:1 | 11 | R/w | 设置RF输出增益 ’00’-18dbm,’11’-0dbm |
| Obsolete | 0 |
|
| 无意义 |
07 | STATUS |
|
|
| 状态寄存器 |
| Reserved | 7 | 0 | R/w | 只能为0 |
| RX_DR | 6 | 0 | R/w | 数据就绪RXFIFO中断,写1清除 |
| TX_DS | 5 | 0 | R/w | 数据发送RXFIFO中断,写1清除 |
| MAX_RT | 4 | 0 | R/w | 最大字节重发中断,写1清除 |
| RX_P_NO | 3:1 | 111 | R | 数据通道号 |
| TX_FULL | 0 | 0 | R | TX FIFO满标志(满为1) |
08 | OBSERVE_TX |
|
|
| 发射监测寄存器 |
| PLOS_CNT | 7:4 | 0 | R | 丢包计数 |
| ARC_CNT | 3:0 | 0 | R | 重发射数据包次数 |
09 | RPD |
|
|
| 接收电源检测 |
| Reserved | 7:1 | 000000 | R | 只能为0 |
| RPD | 0 | 0 | R | 接收电源检测 |
0A | RX_ADDR_P0 | 39:0 | E7E7E7E7E7 | R/W | 通道0接收地址(5个字节) |
0B | RX_ADDR_P1 | 39:0 | C2C2C2C2C2 | R/W | 通道1接收地址(5个字节) |
0C | RX_ADDR_P2 | 7:0 | C3 | R/W | 通道2接收地址(1个字节(低)) |
0D | RX_ADDR_P3 | 7:0 | C4 | R/W | 通道3接收地址(1个字节(低)) |
0E | RX_ADDR_P4 | 7:0 | C5 | R/W | 通道4接收地址(1个字节(低)) |
0F | RX_ADDR_P5 | 7:0 | C6 | R/W | 通道5接收地址(1个字节(低)) |
10 | TX_ADDR | 39:0 | E7E7E7E7E7 | R/W | 发射地址,仅适用于PTX |
11 | RX_PW_P0 |
|
|
| 在通道0中RX_Payload的字节个数 |
| Reserved | 7:6 | 00 | R/W | 只能为0 |
| RX_PW_P0 | 5:0 | 0 | R/W | 接收数据通道0字节数 |
12 | RX_PW_P1 | … | … | … | 在通道1中RX_Payload的字节个数 |
13 | RX_PW_P2 | … | … | … | 在通道2中RX_Payload的字节个数 |
14 | RX_PW_P3 | … | … | … | 在通道3中RX_Payload的字节个数 |
15 | RX_PW_P4 | … | … | … | 在通道4中RX_Payload的字节个数 |
16 | RX_PW_P5 | … | … | … | 在通道5中RX_Payload的字节个数 |
17 | FIFO_STATUS |
|
|
| FIFIO状态寄存器 |
| Reserved | 7 | 0 | R/W | 只能为0 |
| TX_REUSE | 6 | 0 | R | 重用TX Payload |
| TX_FULL | 5 | 0 | R | TX FIFO满标志 |
| TX_EMPTY | 4 | 1 | R | TX FIFO空标志 |
| Reserved | 3:2 | 00 | R/W | 只能为0 |
| RX_FULL | 1 | 0 | R | RX FIFO满标志 |
| RX_EMPTY | 0 | 1 | R | RX FIFO空标志 |
1C | DYNPD |
|
|
| 使能动态数据包长度 |
| Reserved | 7:6 | 0 | R/W | 只能为0 |
| DPL_P5 | 5 | 0 | R/W | 使能pipe 5动态数据包长度 |
| DPL_P4 | 4 | 0 | R/W | 使能pipe 4动态数据包长度 |
| DPL_P3 | 3 | 0 | R/W | 使能pipe 3动态数据包长度 |
| DPL_P2 | 2 | 0 | R/W | 使能pipe 2动态数据包长度 |
| DPL_P1 | 1 | 0 | R/W | 使能pipe 1动态数据包长度 |
| DPL_P0 | 0 | 0 | R/W | 使能pipe 0动态数据包长度 |
1D | FEATURE |
|
| R/W | 特征寄存器 |
| Reserved | 7:3 | 0 | R/W | 只能为0 |
| EN_DPL | 2 | 0 | R/W | 使能动态数据包长度 |
| EN_ACK_PAY | 1 | 0 | R/W | 使能数据包应答 |
| EN_DYN_ACK | 0 | 0 | R/W | 使能写发送数据包非应答命令 |
如果NRF24L01用作具有Enhanced ShockedBurst 的PTX设备,那么将TX_ADDR寄存器里边的数值(地址)设置成通道0接收地址,使两个相同。
接下来看看该芯片的时序图:
(1) 写时序
(2) 读时序
注:C7-C0 表示命令,S7~S0表示状态,Dx为数据位。
从时序上可以知道,读写时序是:先拉低片选CSN,在SCK的第一个上升沿开始传输数据;SCK在无效的状态下为低电平。这就给我们提示:在配置硬件SPI的时候要配置成SPI模式0(时钟极性0,时钟相位0)。
那么现在根据当前了解到的知识,配置STM32的SPI,这里使用标准库函数:
//初始化24L01的IO口
//这个程序是在开发板正点原子精英板F103上面调试的,SPI2的总线上挂载两个设备,将另一个设备(W25Q)的片选拉高,不选中该设备,防止它的影响
#define W25Q_CSN_PIN GPIO_Pin_12
#define NRF_CSN_PIN GPIO_Pin_7
//初始化相关的IO口(片选以及SPI)
void MySPI2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG, ENABLE); //使能PB,G端口时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//SPI2时钟使能
GPIO_InitStructure.GPIO_Pin = W25Q_CSN_PIN; //PB12上拉 防止W25X的干扰
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化指定IO
GPIO_SetBits(GPIOB,W25Q_CSN_PIN);//上拉
GPIO_InitStructure.GPIO_Pin = NRF_CSN_PIN|NRF_CE_PIN; //PG8 7 推挽
GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化指定IO
GPIO_InitStructure.GPIO_Pin = NRF_IRQ_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PG6 输入
GPIO_Init(GPIOG, &GPIO_InitStructure);
GPIO_ResetBits(GPIOG,NRF_IRQ_PIN|NRF_CSN_PIN|NRF_CE_PIN);//PG6,7,8上拉
/****************************SPI2_Init*******************************/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PB13/14/15上拉
SPI_Cmd(SPI2, DISABLE); // SPI外设不使能
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //SPI主机
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //时钟悬空低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //数据捕获于第1个时钟沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由软件控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //定义波特率预分频的值:波特率预分频值为16
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_Cmd(SPI2, ENABLE); //使能SPI外设
/********************************************************************/
}
//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
{
retry++;
if(retry>200)return 0;
}
SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位
{
retry++;
if(retry>200)return 0;
}
return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据
}
至此,SPI的初始化配置和通行API函数已经写好,接下来define下片选以及CE使能的IO口:
#define NRF24L01_CE PGout(8) //24L01片选信号
#define NRF24L01_CSN PGout(7) //SPI片选信号
/******下面的中断引脚程序上不用,调试的时候用过而已******/
#define NRF24L01_IRQ PGin(6) //IRQ主机数据输入
接下来就进一步根据时序图来编写读写函数了:
/***************************************************************
* 写寄存器
****************************************************************/
uint8_t Write_Reg(uint8_t reg, uint8_t value)
{
uint8_t status;
NRF24L01_CSN = 0; /* 选通器件 */
status = SPI2_ReadWriteByte(reg); /* 写寄存器地址 */
SPI2_ReadWriteByte(value); /* 写数据 */
NRF24L01_CSN = 1; /* 禁止该器件 */
return status;
}
/***************************************************************
* 读寄存器
****************************************************************/
uint8_t Read_Reg(uint8_t reg)
{
uint8_t reg_val;
NRF24L01_CSN = 0; /* 选通器件 */
SPI2_ReadWriteByte(reg); /* 写寄存器地址 */
reg_val = SPI2_ReadWriteByte(0); /* 读取该寄存器返回数据 */
NRF24L01_CSN = 1; /* 禁止该器件 */
return reg_val;
}
扩展至连续读连续写,那么还有下面函数:
/****************************************************************
* 写缓冲区----------------------------------
*****************************************************************/
uint8_t Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars)
{
uint8_t i;
uint8_t status;
NRF24L01_CSN = 0; /* 选通器件 */
status = SPI2_ReadWriteByte(reg); /* 写寄存器地址 */
for(i=0; i
{
SPI2_ReadWriteByte(pBuf
); /* 写数据 */
}
NRF24L01_CSN = 1; /* 禁止该器件 */
return status;
}
/****************************************************************
* 读缓冲区-------------------------------
****************************************************************/
uint8_t Read_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars)
{
uint8_t i;
uint8_t status;
NRF24L01_CSN = 0; /* 选通器件 */
status = SPI2_ReadWriteByte(reg); /* 写寄存器地址 */
for(i=0; i
{
pBuf = SPI2_ReadWriteByte(0); /* 读取返回数据 */
}
NRF24L01_CSN = 1; /* 禁止该器件 */
return status;
}
NRF24L01双向传输(一对一)
简介
本文章记录两个NRF24L01无线模块实现双向传输的软件设计~
为什么可以双向传输呢?这要归功于它具有Enhanced ShockBurst,可以工作在主接收和主发送模式,在主接收方可以将我们自己的数据附在ACK Packet实现双向传输,所以此次双向传输会将两个NRF分别设置成主接收和主发送。
· 实物(模块非单独芯片):
网上最便宜的就这种不带运放的模块,大家如果想用NRF24L01的话,建议大家使用加了运放的模块 ,传输距离会比较远一点,没运放的话距离10米就开始出现丢包现象了。另外,进口的和国产的用起来距离一样,所以优先选择国产,因为进口的还贵点。
·芯片引脚定义说明(芯片):
·**数据包格式 **:
这里没必要介绍NRF24L01了,相信要用这芯片或者模块的朋友应该提前了解过了。
· 寄存器表
该芯片有命令寄存器和功能寄存器。在使用每一款芯片之前,要养成查看数据手册的习惯。这是我自己边看边翻译的,可能有翻译地不对的,见谅哈。
命令寄存器表:
[tr]命令名命令字#数据字节操作[/tr]
R_REGISTER | 000A AAAA | 1到5,低位在前 | 读命令/状态寄存器 |
W_REGISTER | 001A AAAA | 1到5,低位在前 | 写命令/状态寄存器 |
R_RX_PAYLOAD | 0110 0001 | 1到32,低位在前 | 读RX_Payload(1到32个字节) |
W_TX_PAYLOAD | 1010 0000 | 1到32,低位在前 | 写TX_Payload(1到32个字节) |
FLUSH_TX | 1110 0001 | 0 | Flush TX FIFO,发送模式使用 |
FLUSH_RX | 1110 0010 | 0 | Flush RX FIFO,接收模式使用 |
REUSE_TX_PL | 1110 0011 | 0 | 重用上次发送的载荷(数据包) |
R_RX_PL_WID | 0110 0000 | 1 | 读取RX payload宽度 |
W_ACK_PAYLOAD | 1010 1PPP | 1到32,低位在前 | 写载荷(这些载荷数据同应答包一起从PPP通道发出) |
W_TX_PAYLOAD_NO ACK | 1011 0000 | 1到32,低位在前 | 失能自动应答,用于发送模式 |
NOP | 1111 1111 | 0 | 无操作,可用于读取状态寄存器 |
上面的AAAAA就是后面即将看到的功能寄存器的映射地址。
功能寄存器地址映射表:
[tr]地址助记符位复位值类型描述[/tr]
00 | CONFIG |
|
|
| 配置寄存器 |
| Reserved | 7 | 0 | R/w | 只能为0 |
| MASK_RX_DR | 6 | 0 | R/w | 接收中断标志。0:使能 |
| MASK_TX_D | 5 | 0 | R/w | 发送中断标志。0:使能 |
| MASK_MAX_RT | 4 | 0 | R/w | 最大重发次数中断。0:使能 |
| EN_CRC | 3 | 1 | R/w | 使能CRC |
| CRCO | 2 | 0 | R/w | CRC编码方案选择,1~2bytes |
| PWR_UP | 1 | 0 | R/w | 1:上电,0:下电 |
| PRIM_RX | 0 | 0 | R/w | 收发控制。1:PRX;0:PTX |
01 | EN_AA |
|
|
| 使能自动应答 |
| Reserved | 7:6 | 00 | R/w | 只能为0 |
| ENAA_P5 | 5 | 1 | R/w | 使能通道5自动应答机制 |
| ENAA_P4 | 4 | 1 | R/w | 使能通道4自动应答机制 |
| ENAA_P3 | 3 | 1 | R/w | 使能通道3自动应答机制 |
| ENAA_P2 | 2 | 1 | R/w | 使能通道2自动应答机制 |
| ENAA_P1 | 1 | 1 | R/w | 使能通道1自动应答机制 |
| ENAA_P0 | 0 | 1 | R/w | 使能通道0自动应答机制 |
02 | EN_RXADDR |
|
|
| 使能 RX 地址 |
| Reserved | 7:6 | 00 | R/w | 只能为0 |
| ERX_P5 | 5 | 0 | R/w | 使能通道5 |
| ERX_P4 | 4 | 0 | R/w | 使能通道4 |
| ERX_P3 | 3 | 0 | R/w | 使能通道3 |
| ERX_P2 | 2 | 0 | R/w | 使能通道2 |
| ERX_P1 | 1 | 0 | R/w | 使能通道1 |
| ERX_P0 | 0 | 0 | R/w | 使能通道0 |
03 | SETUP_AW |
|
|
| 设置地址宽度(所有通道的地址) |
| Reserved | 7:2 | 000000 | R/w | 只能为0 |
| AW | 1:0 | 11 | R/w | 收发地址宽度,’11’-5bytes |
04 | SETUP_RETR |
|
|
| 设置自动重发射机制 |
| ARD | 7:4 | 0000 | R/w | 自动重发延时’0001’-等待500us |
| ARC | 3:0 | 0011 | R/w | 自动重发计数 |
05 | RF_CH |
|
|
| 设置RF频宽 |
| Reserved | 7 | 0 | R/w | 只能为0 |
| RF_CH | 6:0 | 0000010 | R/w | 设置RF的工作频宽 |
06 | RF_SETUP |
|
|
| RF设置寄存器 |
| CONT_WAVE | 7 | 0 | R/w | 为1使能连续传送载波 |
| Reserved | 6 | 0 | R/w | 只能为0 |
| RF_DR_LOW | 5 | 0 | R/w | 设置RF数据250kbps |
| PLL_LOCK | 4 | 0 | R/w | 仅使用于测试? |
| RF_DR_HIGH | 3 | 1 | R/w | ‘00’-1M,’01’-2M,’10’-250kbps |
| RF_PWR | 2:1 | 11 | R/w | 设置RF输出增益 ’00’-18dbm,’11’-0dbm |
| Obsolete | 0 |
|
| 无意义 |
07 | STATUS |
|
|
| 状态寄存器 |
| Reserved | 7 | 0 | R/w | 只能为0 |
| RX_DR | 6 | 0 | R/w | 数据就绪RXFIFO中断,写1清除 |
| TX_DS | 5 | 0 | R/w | 数据发送RXFIFO中断,写1清除 |
| MAX_RT | 4 | 0 | R/w | 最大字节重发中断,写1清除 |
| RX_P_NO | 3:1 | 111 | R | 数据通道号 |
| TX_FULL | 0 | 0 | R | TX FIFO满标志(满为1) |
08 | OBSERVE_TX |
|
|
| 发射监测寄存器 |
| PLOS_CNT | 7:4 | 0 | R | 丢包计数 |
| ARC_CNT | 3:0 | 0 | R | 重发射数据包次数 |
09 | RPD |
|
|
| 接收电源检测 |
| Reserved | 7:1 | 000000 | R | 只能为0 |
| RPD | 0 | 0 | R | 接收电源检测 |
0A | RX_ADDR_P0 | 39:0 | E7E7E7E7E7 | R/W | 通道0接收地址(5个字节) |
0B | RX_ADDR_P1 | 39:0 | C2C2C2C2C2 | R/W | 通道1接收地址(5个字节) |
0C | RX_ADDR_P2 | 7:0 | C3 | R/W | 通道2接收地址(1个字节(低)) |
0D | RX_ADDR_P3 | 7:0 | C4 | R/W | 通道3接收地址(1个字节(低)) |
0E | RX_ADDR_P4 | 7:0 | C5 | R/W | 通道4接收地址(1个字节(低)) |
0F | RX_ADDR_P5 | 7:0 | C6 | R/W | 通道5接收地址(1个字节(低)) |
10 | TX_ADDR | 39:0 | E7E7E7E7E7 | R/W | 发射地址,仅适用于PTX |
11 | RX_PW_P0 |
|
|
| 在通道0中RX_Payload的字节个数 |
| Reserved | 7:6 | 00 | R/W | 只能为0 |
| RX_PW_P0 | 5:0 | 0 | R/W | 接收数据通道0字节数 |
12 | RX_PW_P1 | … | … | … | 在通道1中RX_Payload的字节个数 |
13 | RX_PW_P2 | … | … | … | 在通道2中RX_Payload的字节个数 |
14 | RX_PW_P3 | … | … | … | 在通道3中RX_Payload的字节个数 |
15 | RX_PW_P4 | … | … | … | 在通道4中RX_Payload的字节个数 |
16 | RX_PW_P5 | … | … | … | 在通道5中RX_Payload的字节个数 |
17 | FIFO_STATUS |
|
|
| FIFIO状态寄存器 |
| Reserved | 7 | 0 | R/W | 只能为0 |
| TX_REUSE | 6 | 0 | R | 重用TX Payload |
| TX_FULL | 5 | 0 | R | TX FIFO满标志 |
| TX_EMPTY | 4 | 1 | R | TX FIFO空标志 |
| Reserved | 3:2 | 00 | R/W | 只能为0 |
| RX_FULL | 1 | 0 | R | RX FIFO满标志 |
| RX_EMPTY | 0 | 1 | R | RX FIFO空标志 |
1C | DYNPD |
|
|
| 使能动态数据包长度 |
| Reserved | 7:6 | 0 | R/W | 只能为0 |
| DPL_P5 | 5 | 0 | R/W | 使能pipe 5动态数据包长度 |
| DPL_P4 | 4 | 0 | R/W | 使能pipe 4动态数据包长度 |
| DPL_P3 | 3 | 0 | R/W | 使能pipe 3动态数据包长度 |
| DPL_P2 | 2 | 0 | R/W | 使能pipe 2动态数据包长度 |
| DPL_P1 | 1 | 0 | R/W | 使能pipe 1动态数据包长度 |
| DPL_P0 | 0 | 0 | R/W | 使能pipe 0动态数据包长度 |
1D | FEATURE |
|
| R/W | 特征寄存器 |
| Reserved | 7:3 | 0 | R/W | 只能为0 |
| EN_DPL | 2 | 0 | R/W | 使能动态数据包长度 |
| EN_ACK_PAY | 1 | 0 | R/W | 使能数据包应答 |
| EN_DYN_ACK | 0 | 0 | R/W | 使能写发送数据包非应答命令 |
如果NRF24L01用作具有Enhanced ShockedBurst 的PTX设备,那么将TX_ADDR寄存器里边的数值(地址)设置成通道0接收地址,使两个相同。
接下来看看该芯片的时序图:
(1) 写时序
(2) 读时序
注:C7-C0 表示命令,S7~S0表示状态,Dx为数据位。
从时序上可以知道,读写时序是:先拉低片选CSN,在SCK的第一个上升沿开始传输数据;SCK在无效的状态下为低电平。这就给我们提示:在配置硬件SPI的时候要配置成SPI模式0(时钟极性0,时钟相位0)。
那么现在根据当前了解到的知识,配置STM32的SPI,这里使用标准库函数:
//初始化24L01的IO口
//这个程序是在开发板正点原子精英板F103上面调试的,SPI2的总线上挂载两个设备,将另一个设备(W25Q)的片选拉高,不选中该设备,防止它的影响
#define W25Q_CSN_PIN GPIO_Pin_12
#define NRF_CSN_PIN GPIO_Pin_7
//初始化相关的IO口(片选以及SPI)
void MySPI2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG, ENABLE); //使能PB,G端口时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//SPI2时钟使能
GPIO_InitStructure.GPIO_Pin = W25Q_CSN_PIN; //PB12上拉 防止W25X的干扰
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化指定IO
GPIO_SetBits(GPIOB,W25Q_CSN_PIN);//上拉
GPIO_InitStructure.GPIO_Pin = NRF_CSN_PIN|NRF_CE_PIN; //PG8 7 推挽
GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化指定IO
GPIO_InitStructure.GPIO_Pin = NRF_IRQ_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PG6 输入
GPIO_Init(GPIOG, &GPIO_InitStructure);
GPIO_ResetBits(GPIOG,NRF_IRQ_PIN|NRF_CSN_PIN|NRF_CE_PIN);//PG6,7,8上拉
/****************************SPI2_Init*******************************/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PB13/14/15上拉
SPI_Cmd(SPI2, DISABLE); // SPI外设不使能
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //SPI主机
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //时钟悬空低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //数据捕获于第1个时钟沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由软件控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //定义波特率预分频的值:波特率预分频值为16
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_Cmd(SPI2, ENABLE); //使能SPI外设
/********************************************************************/
}
//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
{
retry++;
if(retry>200)return 0;
}
SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位
{
retry++;
if(retry>200)return 0;
}
return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据
}
至此,SPI的初始化配置和通行API函数已经写好,接下来define下片选以及CE使能的IO口:
#define NRF24L01_CE PGout(8) //24L01片选信号
#define NRF24L01_CSN PGout(7) //SPI片选信号
/******下面的中断引脚程序上不用,调试的时候用过而已******/
#define NRF24L01_IRQ PGin(6) //IRQ主机数据输入
接下来就进一步根据时序图来编写读写函数了:
/***************************************************************
* 写寄存器
****************************************************************/
uint8_t Write_Reg(uint8_t reg, uint8_t value)
{
uint8_t status;
NRF24L01_CSN = 0; /* 选通器件 */
status = SPI2_ReadWriteByte(reg); /* 写寄存器地址 */
SPI2_ReadWriteByte(value); /* 写数据 */
NRF24L01_CSN = 1; /* 禁止该器件 */
return status;
}
/***************************************************************
* 读寄存器
****************************************************************/
uint8_t Read_Reg(uint8_t reg)
{
uint8_t reg_val;
NRF24L01_CSN = 0; /* 选通器件 */
SPI2_ReadWriteByte(reg); /* 写寄存器地址 */
reg_val = SPI2_ReadWriteByte(0); /* 读取该寄存器返回数据 */
NRF24L01_CSN = 1; /* 禁止该器件 */
return reg_val;
}
扩展至连续读连续写,那么还有下面函数:
/****************************************************************
* 写缓冲区----------------------------------
*****************************************************************/
uint8_t Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars)
{
uint8_t i;
uint8_t status;
NRF24L01_CSN = 0; /* 选通器件 */
status = SPI2_ReadWriteByte(reg); /* 写寄存器地址 */
for(i=0; i
{
SPI2_ReadWriteByte(pBuf
); /* 写数据 */
}
NRF24L01_CSN = 1; /* 禁止该器件 */
return status;
}
/****************************************************************
* 读缓冲区-------------------------------
****************************************************************/
uint8_t Read_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars)
{
uint8_t i;
uint8_t status;
NRF24L01_CSN = 0; /* 选通器件 */
status = SPI2_ReadWriteByte(reg); /* 写寄存器地址 */
for(i=0; i
{
pBuf = SPI2_ReadWriteByte(0); /* 读取返回数据 */
}
NRF24L01_CSN = 1; /* 禁止该器件 */
return status;
}
举报