本帖最后由 正点原子 于 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接收。图中左右图片的数据不一样,是因为我们拍照的时间不一样导致的。 |