1、NRF24L01
我用的就长这个样子~~~
2.管脚功能
3、51单片机的I/O口输出为5V,而NRF24L01的电源输入为1.9—3.6V,如果直接接入单片机的电源会烧坏模块,因而要对单片机的电源接口进行压降,这里我选用二极管进行压降,二极管型号为IN4001.
工欲善其事,必先利其器。这一步完成之后就可以把引脚用杜邦线连接到单片机的I/O口,定义管脚就可以开始编程了。
4、要用NRF24L01进行通信首先知道它的发送和接收流程
发送流程:
① MCU通过SPI对NRF24L01进行基本配置,,配置自动应答通道使能,设置自动重发次数不为0(在此设置可以重发数据包)设置为发送模式,还有其他配置等等
② MCU把要发送的数据和接收数据设备的地址通过SPI写入NRF24L01
③ CE引脚置高,启动发送
④ 此时有两种情况:
1.在有限时间内收到应答信号,则TX_DS置高(发送数据成功标志位),并引发IRQ中断(引脚IRQ置低),并清除TX BUf(此为发送缓冲寄存器,自行写代码清除),IRQ中断需要写状态寄存器进行复位(因为此处IRQ由TX_DS引发,将TX_DS复位即可使IRQ复位)
2.重发数据次数超过设定值,则MAX_RT置高(达到最多重发次数标志位),并引发IRQ中断(引脚IRQ置低),不清除TX BUf,IRQ中断需要写状态寄存器进行复位(因为此处IRQ由MAX_RT引发,将MAX_RT复位即可使IRQ复位)
⑤ 接收到应答信号产生中断或者达到最大重发次数产生中断后,NRF24L01继续发下一包数据。
⑥ 当TX BUf为空时,进入待机模式二(当CE为高,TX BUf为空时,进入待机模式二;NRF24L01的工作模式图表在最后,工作模式不需过多理会,只要在适当时候拉高CE进行发送即可,配置NRF24L01时CE置低)
接收流程:
① 与发送模式一样,一开始MCU通过SPI对NRF24L01进行基本配置,设置数据通道自动应答使能(在EN_AA寄存器进行设置,即收到数据后,向主机发送应答信号)还有进行接收数据通道使能(在EN_RXADDR寄存器配置,即选择六个接收通道的某一通道来接收数据,设置为接收模式,还有其他等配置。
② 拉高CE引脚(CE置高),启动接收状态
③ 接收到一个有效数据包后,数据存储在RX BUf,并产生RX_DR中断(RX_DR为接收数据成功标志位,接收成功置1),中断和发送模式一样,同样需要复位。
④ 接收设备自动向发送设备发送确认信号(无需自己写代码)
⑤ 设置CE引脚为低,NRF24L01进入待机模式一
⑥ MCU通过SPI读取NRF24L01收到的数据
总结为:
发送过程:
a. MCU通过SPI对NRF24L01进行基本配置,配置好NRF24L01
b. MCU将要发送的数据与接收数据设备的地址写入NRF24L01
c. CE引脚置高,启动发送
接收过程:
a. MCU通过SPI对NRF24L01进行基本配置,配置好NRF24L01
b. CE引脚置高,启动接收
c. MCU对 NRF24L01进行数据读取
5、我们在使用NRF24L01的时候有一个头文件,是关于NRF24L01的寄存器定义和相关函数的编写,这个文档网上有很多,我们只需要按照它的发送和接受流程调用相关函数配置寄存器就可以了。
//===================================================================================
// 主 程 序 开 始
//===================================================================================
void main(void)
{
uchar i = 0;
uchar j = 0;
uchar a,b;
unsigned char TxBuf[12] ={2,3,1,4,0,3,1,3,0,2,1,1};
unsigned char RxBuf[12] ;
LCD_init();
init_NRF24L01();
SPI_Read_Buf(RX_ADDR_P0,RxBuf,5);
Delay(100);
SetRX_Mode();
lcd2004_pos(0,4);
dat('T');
dat('X');
dat(' ');
dat(' ');
dat('T');
dat('E');
dat('X');
dat('T');
dat('.');
dat('.');
dat('.');
status = SPI_Read(STATUS);
a=status / 10;
b=status % 10;
lcd2004_pos(1,0);
dat('S');
dat('T');
dat('A');
dat(':');
dat(0x30+a);
dat(0x30+b);
fifo = SPI_Read(FIFO_STATUS);
a=fifo / 10;
b=fifo % 10;
lcd2004_pos(1,8);
dat('F');
dat('I');
dat('F');
dat('O');
dat(':');
dat(0x30+a);
dat(0x30+b);
while(1)
{
//===================================================================================
// 发 送 程 序
//===================================================================================
SPI_RW_Reg(WRITE_REG+STATUS,0xFF);
SPI_Write_Buf(WR_TX_PLOAD,TxBuf,TX_PLOAD_WIDTH);
nRF24L01_TxPacket(TxBuf);
status = SPI_Read(STATUS);
a=status / 10;
b=status % 10;
lcd2004_pos(1,0);
dat('S');
dat('T');
dat('A');
dat(':');
dat(0x30+a);
dat(0x30+b);
fifo = SPI_Read(FIFO_STATUS);
a=fifo / 10;
b=fifo % 10;
lcd2004_pos(1,8);
dat('F');
dat('I');
dat('F');
dat('O');
dat(':');
dat(0x30+a);
dat(0x30+b);
SPI_RW_Reg(WRITE_REG+STATUS,0xFF);
//==================================================================================
// 接 收 程 序
//==================================================================================
lcd2004_pos(0,0);
dat('R');dat('X');dat('.');dat('.');dat('.');
j = SPI_Read(CD);
lcd2004_pos(0,6);
dat('C');dat('D');dat(':');
lcd2004_pos(0,10);
dat(0x30+j);
Delay(1000);
if(nRF24L01_RxPacket(RxBuf)==1)
{
lcd2004_pos(1,0);
for(i=0;i<12;i++)
{
dat(0X30+RxBuf[i]);
}
}
这里只是程序的一部分,用到了1602液晶显示。程序中通过读取寄存器STATUS、FIFO来判断发射部分的状态,如果发射成功,则寄存器的数值就会发生改变。
同理,在接收端,我也读取了寄存器CD的值。意思大概就是如果接收端收到载波,则寄存器的值就会变成1,如果没有检测到任何的载波信号,那么寄存器的值就为0。
通过检测寄存器的状态就可以判断两个模块是否正常工作。举个例子,如果发射端的两个寄存器的值正常改变,那么说明发生端是正常工作的。再到接收端观察,如果CD的值始终为0,那么说明接受端的模块出现问题。就这样可以缩小排错的范围。
正常情况下来说,如果发射和接收都正常的话,通过函数nRF24L01_RxPacket(RxBuf)就可以读取接收端缓冲区的值,这个数组值的大小与你发送的数组大小一样。
6、我在调试过程中遇到的问题
按照上述的方法,我的三个寄存器都有数值上的变化,说明我的发射端正常工作,接受端也检测到了载波信号,但是仍然读不出数据。经过检查,我的有问题的地方如下:
1、程序我是在单机收发的基础上修改的,所以配置寄存器的程序有两套。然后我在读取RXBUF的时候用到了我只定义但并没有写内容,然后编译器也没有报错,所以检查了好久。所以今后的程序但凡有重复的都要多加注意,可能稍不注意就用到了别的类似的函数,然后编译器还不会报错。
2、解决掉上面的问题后,我的功能还是没有实现,检查良久之后,发现我在发射检测得时候,把自动应答关掉了,就是SPI_RW_Reg_A(WRITE_REG + EN_AA, 0x01); 它啦。改回来之后就实现功能了。这个收获就是凡是在调试阶段改变的寄存器,在最后都要改回来,因为调试的时候因为某些原因不得不关掉某些寄存器的功能,但在最后通信的时候,这些功能都要打开。
7、总结
1、读取相关寄存器的值可以辅助我们判断模块的工作状态。
2、调试改变的东西都要记录下来,在最后都是要改回来的。
3、在有相同功能的两个函数(用于不同的模块)时,一点要加倍注意调用的是哪个模块的函数。
参考资料:http://wenku.baidu.com/view/45d4ba90dd88d0d233d46a41.html
这个写的很不错,通过寄存器来调试就是从这学来的。