1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
写在前面:这一篇先介绍一下两个51单片机之间通过nRF24L01模块通信的过程,下一篇我会写 51单片机 和 STM32F407 单片机通信过程。
关于nRF24L01这个模块,网上可以说是资料非常多了,我参考的是云佳科技的pdf以及官方的datasheet英文数据手册。 另外,关于这个模块的介绍以及能够用来做什么也不过多的废话,数据手册及说明书都有。 51单片机我使用的是买来的开发板,核心是STC89C52,大家不必担心平台不同,都是使用软件模拟spi,引脚怎么定义都可以,也可以选择跟我使用的不一样的引脚,都Ok的。 一、硬件介绍 1、 nRF24L01模块接口电路见下图 单片机是作为主机的,即Master nRF24L01作为从机,即Slave; 这样大家应该会很好理解MOSI和MISO了,,在我的另一篇博客也有SPI介绍,STM32F407使用MFRC522射频卡调试及程序移植成功, 这个我是在STM32上调试的,可以参考一下里面对spi MOSI和MISO的解释 1 GND ------>> 接地(与单片机共地)** PS:** 1) VCC电压供电范围要求1.9~3.6V之间,由于51单片机大多是5V,所以自己的开发板上没有无线模块接口的要注意,把VCC另外接到这个范围的电压上,电压过高会烧坏模块。。 推荐 3.3V 其他引脚无电压要求 2) 用普通单片机IO口模拟SPI协议即可控制该模块,我一般都是用模拟SPI,可移植性高 该模块使用的芯片方框图如下 2、 单片机2.4G模块引脚接口 3、 单片机按键引脚图 4、 硬件连接实物图(我用的两个相同的51单片机,所以引脚都一样,只是程序里面接收模式和发送模式略微不同) 二、软件部分 对于某个模块写程序是一定要参照datasheet的时序图, 这样才可以保证不出错。 下面是我从 nRF24L01 datasheet上截的SPI 时序图 变量设置及宏定义 接收端和发射端一样 // 宏定义 #define uchar unsigned char #define uint unsigned int #define TX_ADR_WIDTH 5 // 5字节宽度的发送/接收地址 #define TX_PLOAD_WIDTH 4 // 数据通道有效数据宽度 // LED灯及按键位定义 ***it LED = P1^0; ***it KEY1 = P3^0; ***it KEY2 = P3^1; ***it BEEP = P2^3; uchar code TX_ADDRESS[TX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01}; // 定义一个静态发送地址 uchar RX_BUF[TX_PLOAD_WIDTH]; uchar TX_BUF[TX_PLOAD_WIDTH]; uchar flag; uchar DATA = 0x01; uchar bdata sta; ***it RX_DR = sta^6; ***it TX_DS = sta^5; ***it MAX_RT = sta^4; // NRF24L01 模块引脚位定义 ***it CE = P1^2; ***it CSN = P1^3; ***it SCK = P1^7; ***it MOSI= P1^5; ***it MISO= P1^6; ***it IRQ = P1^4; 寄存器设置 /* SPI(nRF24L01) 指令设置 指令格式 <命令字 : 由高位到低位(每字节)> <数据字节: 低字节到高字节,每一字节高位在前> */ #define READ_REG 0x00 // Define read command to register #define WRITE_REG 0x20 // Define write command to register #define RD_RX_PLOAD 0x61 // Define RX payload register address #define WR_TX_PLOAD 0xA0 // Define TX payload register address #define FLUSH_TX 0xE1 // 清除 TX FIFO寄存器 应用于发射模式下 #define FLUSH_RX 0xE2 // 清除 RX FIFO寄存器 应用于接收模式下。 #define REUSE_TX_PL 0xE3 // 重新使用上一包有效数据。 当CE=1,数据包被不断重新发射 发射过程中必须禁止数据包重利用功能 #define NOP 0xFF // 空操作。可以用来读状态寄存器 /* SPI(nRF24L01) registers(addresses) */ #define CONFIG 0x00 // 'Config' register address #define EN_AA 0x01 // 'Enable Auto Acknowledgment' register address #define EN_RXADDR 0x02 // 'Enabled RX addresses' register address #define SETUP_AW 0x03 // 'Setup address width' register address #define SETUP_RETR 0x04 // 'Setup Auto. Retrans' register address #define RF_CH 0x05 // 'RF channel' register address #define RF_SETUP 0x06 // 'RF setup' register address #define STATUS 0x07 // 'Status' register address #define OBSERVE_TX 0x08 // 'Observe TX' register address #define CD 0x09 // 'Carrier Detect' register address #define RX_ADDR_P0 0x0A // 'RX address pipe0' register address #define RX_ADDR_P1 0x0B // 'RX address pipe1' register address #define RX_ADDR_P2 0x0C // 'RX address pipe2' register address #define RX_ADDR_P3 0x0D // 'RX address pipe3' register address #define RX_ADDR_P4 0x0E // 'RX address pipe4' register address #define RX_ADDR_P5 0x0F // 'RX address pipe5' register address #define TX_ADDR 0x10 // 'TX address' register address #define RX_PW_P0 0x11 // 'RX payload width, pipe0' register address #define RX_PW_P1 0x12 // 'RX payload width, pipe1' register address #define RX_PW_P2 0x13 // 'RX payload width, pipe2' register address #define RX_PW_P3 0x14 // 'RX payload width, pipe3' register address #define RX_PW_P4 0x15 // 'RX payload width, pipe4' register address #define RX_PW_P5 0x16 // 'RX payload width, pipe5' register address #define FIFO_STATUS 0x17 // 'FIFO Status Register' register address 1 简介一下 Enhanced ShockBurstTM发射流程 A. 把接收机的地址和要发送的数据按时序送入NRF24L01; B. 配置CONFIG寄存器,使之进入发送模式。 C. 微控制器把CE置高(至少10us),激发NRF24L01进行Enhanced ShockBurstTM发射; D. N24L01的Enhanced ShockBurstTM发射 (1) 给射频前端供电; (2) 射频数据打包(加字头、CRC校验码); (3) 高速发射数据包; (4) 发射完成,NRF24L01进入空闲状态。 ********** ** 发射端代码 ** ********* 1) 首先初始化IO口 // 初始化IO void init_io(void) { CE = 0; // 待机 CSN = 1; // SPI禁止 SCK = 0; // SPI时钟置低 IRQ = 1; // 中断复位 LED = 1; // 关闭指示灯 } 2) 通过SPI对24L01进行读写的函数 返回读取的字节 uchar SPI_RW(uchar byte) { uchar bit_ctr; // output 8-bits for (bit_ctr = 0; bit_ctr < 8; bit_ctr++) { MOSI = (byte & 0x80); // output ‘byte’ MSB to MOSI byte = (byte << 1); // shift next bit into MSB.. SCK = 1; // Set SCK high.. 24L01 read 1-bit from MOSI and output 1-bit to MISO byte |= MISO; // capture current MISO bit SCK = 0; // ..then set SCK low again } return (byte); // return read byte } 3) 通过SPI协议向寄存器reg 写入数据value uchar SPI_RW_Reg(uchar reg, uchar value) { uchar status; CSN = 0; // CSN low, init SPI transaction, start transmitting data status = SPI_RW(reg); // select register and return status byte SPI_RW(value); // ..and write value to it.. CSN = 1; // CSN high again, transmission end return(status); // return nRF24L01 status byte } 4) 从寄存器reg中读数据 返回读取的数据 uchar SPI_Read(uchar reg) { uchar reg_val; CSN = 0; // CSN置低,开始传输数据 SPI_RW(reg); // 选择寄存器 reg_val = SPI_RW(0); // 然后从该寄存器读数据 CSN = 1; // CSN拉高,结束数据传输 return(reg_val); // 返回寄存器数据 } 5) 从reg寄存器读 bytes 个字节 uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar bytes) { uchar status,byte_ctr; CSN = 0; // Set CSN low, init SPI tranaction status = SPI_RW(reg); // Select register to read & return status byte for (byte_ctr = 0; byte_ctr < bytes; byte_ctr++) pBuf[byte_ctr] = SPI_RW(0); //逐个字节从nRF24L01读出 CSN = 1; // set CSN high, stop transaction return(status); // return nRF24L01 status byte } 6) 往reg寄存器写入 bytes 个字节 uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar bytes) { uchar status,byte_ctr; CSN = 0; // Set CSN low, init SPI tranaction status = SPI_RW(reg); // Select register to write to & return status byte for (byte_ctr = 0; byte_ctr < bytes; byte_ctr++) SPI_RW(*pBuf++); // 逐个字节写入nRF24L01 CSN = 1; // Set CSN high again 结束数据传输 return(status); // 返回状态寄存器 } 7) 设置nRF24L01为接收模式的函数,等待接收发送设备的数据包 void RX_Mode(void) { CE = 0; SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); // 接收设备接收通道0使用和发送设备相同的发送地址 SPI_RW_Reg(WRITE_REG + EN_AA, 0x01); // 使能接收通道0自动应答 SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); // 使能接收通道0 SPI_RW_Reg(WRITE_REG + RF_CH, 40); // 选择射频通道0x40 SPI_RW_Reg(WRITE_REG + RX_PW_P0, TX_PLOAD_WIDTH); // 接收通道0选择和发送通道相同有效数据宽度 SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07); // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益 SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f); // CRC使能,16位CRC校验,上电,接收模式 delay_ms(150); CE = 1; // 拉高CE启动接收设备 } 8) 设置nRF24L01为发送模式 void TX_Mode(uchar *BUF) { CE = 0; SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH); // 写入发送地址 SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); // 为了应答接收设备,接收通道0地址和发送地址相同 SPI_Write_Buf(WR_TX_PLOAD, BUF, TX_PLOAD_WIDTH); // 写数据包到TX FIFO SPI_RW_Reg(WRITE_REG + EN_AA, 0x01); // 使能接收通道0自动应答 SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); // 使能接收通道0 SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x0a); // 自动重发延时等待250us+86us,自动重发10次 SPI_RW_Reg(WRITE_REG + RF_CH, 40); // 选择射频通道0x40 SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07); // 数据传输率1Mbps,发射功率0dBm,低噪声放大器增益 SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e); // CRC使能,16位CRC校验,上电 delay_ms(150); CE = 1; } 9) 检查接收设备有无接收到数据包 uchar Check_ACK(bit clear) { delay_ms(200); while(IRQ); // 等待数据接收完成 sta = SPI_RW(NOP); // 返回状态寄存器 if(TX_DS) { LED0 = ~LED0; delay_ms(200); LED0 = ~LED0; delay_ms(200); LED0 = ~LED0; delay_ms(200); } if(MAX_RT) if(clear) // 是否清除TX FIFO,没有清除在复位MAX_RT中断标志后重发 SPI_RW(FLUSH_TX); SPI_RW_Reg(WRITE_REG + STATUS, sta); // 清除TX_DS或MAX_RT中断标志 IRQ = 1; if(TX_DS) return(0x00); else return(0xff); } 10) 按键扫描 // 按键扫描 void CheckButtons() { if(KEY1 == 0) { delay_ms(10); if(KEY1 == 0) { while(!KEY1); TX_BUF[0] = 1; // 数据送到缓存 TX_Mode(TX_BUF); // 把nRF24L01设置为发送模式并发送数据 Check_ACK(0); // 等待发送完毕,清除TX FIFO delay_ms(250); delay_ms(250); } } if(KEY2 == 0) { delay_ms(10); if(KEY2 == 0) { while(!KEY2); TX_BUF[0] = 2; // 数据送到缓存 TX_Mode(TX_BUF); // 把nRF24L01设置为发送模式并发送数据 Check_ACK(0); // 等待发送完毕,清除TX FIFO delay_ms(250); delay_ms(250); } } } 11) 主函数 void main(void) { init_io(); // 初始化IO while(1) { CheckButtons(); // 按键扫描 } } 2 Enhanced ShockBurstTM接收流程 A. 配置本机地址和要接收的数据包大小; B. 配置CONFIG寄存器,使之进入接收模式,把CE置高。 C. 130us后,NRF24L01进入监视状态,等待数据包的到来; D. 当接收到正确的数据包(正确的地址和CRC校验码),NRF2401自动把字 头、地址和CRC校验位移去; E. NRF24L01通过把STATUS寄存器的RX_DR置位( STATUS一般引起微 控制器中断 )通知微控制器; F. 微控制器把数据从 NewMsg_RF2401 读出; G. 所有数据读取完毕后,可以清除STATUS寄存器。NRF2401可以进入 四种主要的模式之一。 接收端代码和发射端是一样的 注意发射端地址和接收端地址一致即可 接收端主函数 void main(void) { init_io(); // 初始化IO RX_Mode(); // 设置为接收模式 while(1) { sta = SPI_Read(STATUS); // 读状态寄存器 //delay_ms(200); if(RX_DR) // 判断是否接受到数据 { SPI_Read_Buf(RD_RX_PLOAD, RX_BUF, TX_PLOAD_WIDTH); // 从RX FIFO读出数据 flag = 1; } SPI_RW_Reg(WRITE_REG + STATUS, sta); // 清除RX_DS中断标志 if(flag) // 接受完成 { if(RX_BUF[0] == 1)// KEY1按下 则蜂鸣器 响1下 { BEEP = 0; delay_ms(500); BEEP = 1; delay_ms(500); } if(RX_BUF[0] == 2) // KEY2按下 蜂鸣器响3下 { BEEP = 0; delay_ms(500); BEEP = 1; delay_ms(500); BEEP = 0; delay_ms(500); BEEP = 1; delay_ms(500); BEEP = 0; delay_ms(500); BEEP = 1; delay_ms(500); } flag = 0; // 清标志 delay_ms(250); delay_ms(250); LED = 1; // 关闭LED } } } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1820 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1634 浏览 1 评论
1104 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
740 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1692 浏览 2 评论
1951浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
756浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
586浏览 3评论
605浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
568浏览 3评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-30 21:55 , Processed in 0.653642 second(s), Total 48, Slave 42 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号