| 本帖最后由 正点原子 于 2013-3-26 22:53 编辑 
 
 //该函数初始化NRF24L01到RX模式 //设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR //当CE变高后,即进入RX模式,并可以接收数据了        void NRF24L01_RX_Mode(void) {     NRF24L01_CE=0;         NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH); //写RX节点地址        NRF24L01_Write_Reg(WRITE_REG+EN_AA,0x01);    //使能通道0的自动应答         NRF24L01_Write_Reg(WRITE_REG+EN_RXADDR,0x01);//使能通道0的接收地址           NRF24L01_Write_Reg(WRITE_REG+RF_CH,40);      //设置RF通信频率             NRF24L01_Write_Reg(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH); //选择通道0的有效数据宽度          NRF24L01_Write_Reg(WRITE_REG+RF_SETUP,0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启        NRF24L01_Write_Reg(WRITE_REG+CONFIG, 0x0f); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式      NRF24L01_CE = 1; //CE为高,进入接收模式  }                        //该函数初始化NRF24L01到TX模式 //设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,选择RF频道, //波特率和LNA HCURR //PWR_UP,CRC使能 //当CE变高后,即进入RX模式,并可以接收数据了        //CE为高大于10us,则启动发送.    void NRF24L01_TX_Mode(void) {                                                            NRF24L01_CE=0;           NRF24L01_Write_Buf(WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH); //写TX节点地址      NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);  //设置TX节点地址,主要为了使能ACK          NRF24L01_Write_Reg(WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答         NRF24L01_Write_Reg(WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址       NRF24L01_Write_Reg(WRITE_REG+SETUP_RETR,0x1a); //设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次     NRF24L01_Write_Reg(WRITE_REG+RF_CH,40);       //设置RF通道为40     NRF24L01_Write_Reg(WRITE_REG+RF_SETUP,0x0f);   //设置TX发射参数,0db增益,2Mbps,低噪声增益开启        NRF24L01_Write_Reg(WRITE_REG+CONFIG,0x0e);     //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断     NRF24L01_CE=1;//CE为高,10us后启动发送 } 此部分代码我们不多介绍,在这里强调一个要注意的地方,在NRF24L01_Init函数里面,我们调用了SPI2_Init()函数,该函数我们在第二十八章曾有提到,在第二十八章的设置里面,SCK空闲时为高,但是NRF24L01的SPI通信时序如图37.3.1所示 :  
 图37.3.1 NRF24L01读写操作时序
 上图中Cn代表指令位,Sn代表状态寄存器位,Dn代表数据位。从图中可以看出,SCK空闲的时候是低电平的,而数据在SCK的上升沿被读写。所以,我们需要设置SPI的CPOL和CPHA均为0,来满足NRF24L01对SPI操作的要求。所以,我们在NRF24L01_Init函数里面又单独添加了将CPOL和CPHA设置为0的代码。保存24l01.c文件,加入到HARDWARE组下。接下来打开24l01.h,输入如下代码: #ifndef __24L01_H #define __24L01_H                      #include "sys.h"     //NRF24L01寄存器操作命令 #define READ_REG        0x00  //读配置寄存器,低5位为寄存器地址 ......//省略部分定义 #define FIFO_STATUS     0x17  //FIFO状态寄存器;bit0,RX FIFO寄存器空标志; //bit1,RX FIFO满标志;bit2,3,保留 bit4,TX FIFO空标志;bit5,TX FIFO满标志; //bit6,1, 循环发送上一数据包.0,不循环; //24L01操作线 #define NRF24L01_CE   PGout(6) //24L01片选信号 #define NRF24L01_CSN  PGout(7) //SPI片选信号      #define NRF24L01_IRQ  PGin(8)  //IRQ主机数据输入 //24L01发送接收数据宽度定义 #define TX_ADR_WIDTH    5       //5字节的地址宽度 #define RX_ADR_WIDTH    5       //5字节的地址宽度 #define TX_PLOAD_WIDTH  32     //32字节的用户数据宽度 #define RX_PLOAD_WIDTH  32     //32字节的用户数据宽度 void NRF24L01_Init(void);                 //初始化 void NRF24L01_RX_Mode(void);       //配置为接收模式 void NRF24L01_TX_Mode(void);              //配置为发送模式 u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 u8s); //写数据区 u8 NRF24L01_Read_Buf(u8 reg, u8 *pBuf, u8 u8s);  //读数据区               u8 NRF24L01_Read_Reg(u8 reg);                                   //读寄存器 u8 NRF24L01_Write_Reg(u8 reg, u8 value);              //写寄存器 u8 NRF24L01_Check(void);                                     //检查24L01是否存在 u8 NRF24L01_TxPacket(u8 *txbuf);                         //发送一个包的数据 u8 NRF24L01_RxPacket(u8 *rxbuf);                        //接收一个包的数据 #endif 部分代码,主要定义了一些24L01的命令字(这里我们省略了一部分),以及函数声明,这里还通过TX_PLOAD_WIDTH和RX_PLOAD_WIDTH决定了发射和接收的数据宽度,也就是我们每次发射和接受的有效字节数。NRF24L01每次最多传输32个字节,再多的字节传输则需要多次传送。 保存24l01.h文件,接下来我们在主函数里面写入我们的实现代码,来达到我们所要求的功能。打开test.c文件在该文件内修改main函数如下: int main(void) {                             u8 key,mode;        u16 t=0;                        u8 tmp_buf[33];         Stm32_Clock_Init(9);    //系统时钟设置        uart_init(72,9600);      //串口初始化为9600        delay_init(72);                  //延时初始化         LED_Init();                 //初始化与LED连接的硬件接口        LCD_Init();                  //初始化LCD        usmart_dev.init(72);      //初始化USMART               KEY_Init();                  //按键初始化       NRF24L01_Init();      //初始化NRF24L01         POINT_COLOR=RED;//设置字体为红色         LCD_ShowString(60,50,200,16,16,"WarShip STM32");            LCD_ShowString(60,70,200,16,16,"NRF24L01 TEST");           LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");        LCD_ShowString(60,110,200,16,16,"2012/9/13");                     while(NRF24L01_Check())   //检查NRF24L01是否在位.         {               LCD_ShowString(60,130,200,16,16,"NRF24L01 Error"); delay_ms(200);                             LCD_Fill(60,130,239,130+16,WHITE); delay_ms(200);                  }                                                                 LCD_ShowString(60,130,200,16,16,"NRF24L01 OK");       while(1)//在该部分确定进入哪个模式!        {               key=KEY_Scan(0);               if(key==KEY_RIGHT)               {                      mode=0; break;                                 }else if(key==KEY_DOWN)               {                      mode=1; break;                                   }               t++;               if(t==100)LCD_ShowString(10,150,230,16,16,"KEY0:RX_Mode  KEY1:TX_Mode" ); //闪烁显示提示信息              if(t==200)               {                           LCD_Fill(10,150,230,150+16,WHITE); t=0;                                   }               delay_ms(5);             }          LCD_Fill(10,150,240,166,WHITE);//清空上面的显示                      POINT_COLOR=BLUE;//设置字体为蓝色            if(mode==0)//RX模式        {               LCD_ShowString(60,150,200,16,16,"NRF24L01 RX_Mode");                 LCD_ShowString(60,170,200,16,16,"Received DATA:");                  NRF24L01_RX_Mode();                               while(1)               {                                                                                           if(NRF24L01_RxPacket(tmp_buf)==0)//一旦接收到信息,则显示出来.                      {                             tmp_buf[32]=0;//加入字符串结束符                             LCD_ShowString(0,190,239,32,16,tmp_buf);                          }else delay_us(100);                               t++;                      if(t==10000) {t=0; LED0=!LED0;}//大约1s钟改变一次状态                                   };            }else//TX模式        {                                                                  LCD_ShowString(60,150,200,16,16,"NRF24L01 TX_Mode");                 NRF24L01_TX_Mode();               mode=' ';//从空格键开始                 while(1)               {                                                                   if(NRF24L01_TxPacket(tmp_buf)==TX_OK)                      {                             LCD_ShowString(60,170,239,32,16,"Sended DATA:");                                  LCD_ShowString(0,190,239,32,16,tmp_buf);                              key=mode;                             for(t=0;t<32;t++)                             {                                    key++;                                    if(key>('~'))key=' ';                                    tmp_buf[t]=key;                                  }                             mode++;                              if(mode>'~')mode=' ';                                 tmp_buf[32]=0;//加入结束符                                       }else                      {                                                                                                     LCD_ShowString(60,170,239,32,16,"Send Failed ");                              LCD_Fill(0,188,240,218,WHITE);//清空上面的显示                                        };                      LED0=!LED0; delay_ms(1500);                                                                };        }      } 至此,我们整个实验的软件设计就完成了。37.4 下载验证 在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上,可以看到LCD显示如图37.4.1所示的内容(默认NRF24L01已经接上了): 图37.4.1 选择工作模式界面
 
 通过KEY0和KEY1来选择NRF24L01模块所要进入的工作模式,我们两个开发板一个选择发送,一个选择接收就可以了。 设置好后通信界面如图37.4.2所示: 
 图37.4.2 通信界面
 
 上图中,左侧的图片来自开发板A,工作在发送模式。右侧的图片来自开发板B,工作在接收模式,A发送,B接收。图中左右图片的数据不一样,是因为我们拍照的时间不一样导致的。 |