
-
UID:1463162
-
- 注册时间2013-06-16
- 最后登录2024-07-28
- 在线时间782小时
-
-
访问TA的空间加好友用道具
|
nRF24L01应该算得上应用比较广泛的2.4G无线通讯模块了。 SPI的接口,能达到比较高的通讯速率,模块体积小,待机功耗也不错,一般的单片机教程都会教。 网上的资料也多,方便学习。 这个模块我老早就买了,只是一直没时间玩。 这两天抽点时间学了一下。 无线这东西,看不见摸不着,出问题了也不知道是接收有问题还是发送有问题。 先说说这个模块吧 2.4G无线通讯,可设置频段,有一个发送通道,6个接收通道。 6个接收通道的地址可以设置为3字节、4字节、5字节。 0通道可以独立设置,2~5通道的地址高位字节要与1通道相同,只有最低字节能独立设置。 由于有6个接收通道,所以一次设置最多可以接收6个发送主机的信息。 当然可以分时接收,那就不止6个了。 两个模块要建立通信的条件是1、发射频段相同,2、发送、接收地址相同,3、通信速率、数据包格式相同 对于发射主机,如果要接收应答信号,其接收通道0地址要和发送地址相同。 数据包格式主要指数据包长度,可以设置动态长度。 话不多说,我们看图说话 这是nRF24L01的状态机 有掉电模式、待机I和待机II、工作模式 有些寄存器在掉点或者待机模式下才能修改,所以一般先初始化配置 图上写的很清楚,这里就不多解释了。 几种状态的解释如上图 要和模块通讯,首先要看的就是通讯时序 上面是读写时序 我们能得到的信息就是空闲时钟状态和相位 正确设置SPI才能正常通讯 nRF24L01的数据包格式 引导码,表示数据传送开始 地址码,接收机和自己的地址比较 控制字符,携带数据包长度信息,是否重发包等等 数据字节,需要传送的主体 CRC校验码 一般的流程是主机发送一段数据, 接收机读取,当检测到和自己地址相符的数据包则开始读入, 当数据包校验通过则向主机发送应答信号 主机接收到应答则传输结束。 主机未接收到应答则等待一段时间后重发, 重发次数超过设定后停止发送。 从机也会检测数据包是否为新包,如果是新包则产生接收中断,让单片机读取。 这个模块的好处就是发送和接收自动完成,数据的打包、校验、重传都不需要单片机干预。 单片机只需要启动发送,和收到数据时启动接收就可以了。 发送流程 接收流程 多机通信 这里主要是看各个模块的地址设置,正确设置才能通信成功 关于nRF24L01的具体设置我不想讲太多,网上资料很多,讲的也比我清楚, 我只讲我遇到的问题 我的实验是主机发送数据,从机接收数据,并在应答包中返回数据。 一开始总是接收不到应答包,从机数据接收正常。 后面检查发现数据是接收到了,只是我在主机发送完成后清状态位时把接收完成的状态给清掉了 后面改成只清发送完成就正常了。 后来发现了一个更诡异的问题 接受的字符串乱码 我一般喜欢用在线仿真代替串口打印 因为不用设置,直接就能读取到数据 单独检查字符串,发送前和发送后读取出来都是正常的,从机接收也是正常 但是主机接收的应答包里面的数据是乱码的 从机发送前后读取的数据也是正常的 检查程序,各种修改也不行。 有时候能正常接收,有时候死活不行。 打印到串口也是乱码 郁闷的不行 甚至读取nRF24L01的配置寄存器都是错误的! 然而,停止仿真后,单片机自动复位运行 居然是正常的 what?! 再仿真还是乱码 停止仿真,正常! 我的天,搞了那么久查原因。。。 有时改几句代码就又正常,正常后将代码原封不动改回来也是正常! 结果浪费了很多时间,找不到原因。。。 不管了,总之是没找到具体原因 接收端一直是正常的,不过接收和发送端单片机不同 除了硬件仿真有问题,其它的都正常了,就这样吧! 最后说一个prinf的问题。 开始时使用的全功能的printf 结果占用了差不多7K的空间。 换成tiny的printf,几百个字节,但是不能打印变量。 最后设定成自动选择,大概1k多大小,显示也正常,就这样吧! 贴上nRF24L01的相关代码 供大家参考、指正! nRF24L01.h - #ifndef __24L01_H__
- #define __24L01_H__
- #include<iostm8s105k4.h>
- #include<spi.h>
- #include<delay.h>
- #define CSN PC_ODR_ODR4 //宏定义
- #define CE PC_ODR_ODR3
- #define IRQ PB_IDR_IDR4
- #define TX_MODE //模式定义,注释掉此行则为接收模式
- extern unsigned char const ADDR0[3]; //地址数组
- extern unsigned char const ADDR1[3];
- extern unsigned char const ADDR2[3];
- extern unsigned char const ADDR3[3];
- extern unsigned char const ADDR4[3];
- extern unsigned char const ADDR5[3];
- /*寄存器操作命令*/
- #define R_REGISTER 0x00 // 读配置寄存器, 低 5 位为寄存器地址
- #define W_REGISTER 0x20 // 写配置寄存器, 低 5 位为寄存器地址(待机和停机模式有效)
- #define R_RX_PAYLOAD 0x61 // 读取 Rx 有效数据, 1-32 字节
- #define W_TX_PAYLOAD 0xa0 // 写 Tx 有效数据, 1-32 字节
- #define FLUSH_TX 0xe1 // 清除 Tx FIFO 寄存器,发射模式下用
- #define FLUSH_RX 0xe2 // 清除 Rx FIFO 寄存器,接收模式下用
- #define REUSE_TX_PL 0xe3 // 重新使用上一包数据
- #define ACTIVATE 0x50 // ACTIVATE加0x73激活或停用下方三个寄存器(待机或停机模式下更改)
- #define R_RX_PL_WID 0x60 // 读取顶层RX FIFO 数据长度
- #define W_ACK_PAYLOAD 0xa8 // 接收模式下,写应答数据包附加数据
- #define W_TX_PAYLOAD_NOACK 0xb0 // 发送模式下,停用自动应答
- #define NOP 0xff // 空指令
- /*寄存器地址*/
- #define CONFIG 0x00 // 配置寄存器 bit0(PRIM_RX):0 发送,1接收; bit(PWR_UP):0 power down,1 power on;
- // bit2(CRCO):0 1byte,1 2bytes; bit3(EN_CRC):0 disable,1 enable;
- // bit4(MASK_MAX_RT):0 使能MAX_RT中断,1 屏蔽MAX_RT中断;
- // bit5(MASK_TX_DS):0 使能TX_DS中断,1 屏蔽TX_DS中断; bit6(MASK_RX_DS):0 使能RX_DS中断,1 屏蔽RX_DS中断;
- #define EN_AA 0x01 // 使能自动应答功能 Bit0 - 5, 对应通道 0 - 5
- #define EN_RXADDR 0x02 // 接收地址允许, Bit0 - 5, 对应通道 0 - 5
- #define SETUP_AW 0x03 // 设置地址宽度 ( 所有数据通道 ) : Bit0-1: 00 3字节 01 4字节 10 5字节
- #define SETUP_RETR 0x04 // 建立自动重发, Bit0-3: 自动重发计数, Bit4-7: 自动重发延时 250 * x + 86us
- #define RF_CH 0x05 // RF 通道, Bit0-6: 工作通道频率
- #define RF_SETUP 0x06 // RF 寄存器, Bit3: 传输速度 (0: 1 Mbps, 1: 2 Mbps), Bit1-2: 发射功率, Bit0: 低噪声发大器增益
- #define STATUS 0x07 // 状态寄存器, Bit0: RX FIFO 满标志, Bit1-3: 接收数据通道号 (最大: 6 ), Bit4: 达到最多次重发, Bit5: 数据发送完成中断, Bit6: 接收数据中断
- #define OBSERVE_TX 0x08 // 发送检测寄存器, Bit4-7: 数据包丢失计数器, Bit0-3: 重发计数器
- #define CD 0x09 // 载波检测, Bit0: 载波检测
- #define RX_ADDR_P0 0x0a // 接收通道0地址, 最大长度 5 个字节, 低字节在前
- #define RX_ADDR_P1 0x0b // 接收通道1地址, 最大长度 5 个字节, 低字节在前
- #define RX_ADDR_P2 0x0c // 接收通道2地址,最低字节可设置,其它同RX_ADDR_P1
- #define RX_ADDR_P3 0x0d // 接收通道3地址,最低字节可设置,其它同RX_ADDR_P1
- #define RX_ADDR_P4 0x0e // 接收通道4地址,最低字节可设置,其它同RX_ADDR_P1
- #define RX_ADDR_P5 0x0f // 接收通道5地址,最低字节可设置,其它同RX_ADDR_P1
- #define TX_ADDR 0x10 // 发送地址(低字节在前), ShockBurstTM 模式下, RX_ADDR_P0 与地址相等
- #define RX_PW_P0 0x11 // 接收数据通道 0 有效数据宽度 (1-32字节), 设置为 0 则非法
- #define RX_PW_P1 0x12 // 接收数据通道 1 有效数据宽度 (1-32字节), 设置为 0 则非法
- #define RX_PW_P2 0x13 // 接收数据通道 2 有效数据宽度 (1-32字节), 设置为 0 则非法
- #define RX_PW_P3 0x14 // 接收数据通道 3 有效数据宽度 (1-32字节), 设置为 0 则非法
- #define RX_PW_P4 0x15 // 接收数据通道 4 有效数据宽度 (1-32字节), 设置为 0 则非法
- #define RX_PW_P5 0x16 // 接收数据通道 5 有效数据宽度 (1-32字节), 设置为 0 则非法
- #define FIFO_STATUS 0x17 // FIFO 状态寄存器, Bit0: RX FIFO 寄存器空标志, Bit1 RX FIFO 满标志, Bit2-3 保留
- // Bit4 TX FIFO 空标志, Bit5 TX FIFO 满标志, Bit6 1 循环发送上一次数据包, 0 不循环
- #define DYNPD 0x1C // 启用动态数据长度, Bit6-7: NULL, Bit5: 启用动态数据长度管道 5...
- #define FEATRUE 0x1D // 功能寄存器, Bit3-7: 无用,总是为 0, Bit2: 启用动态数据长度, Bit1: 启用 ACK, Bit0: 启用 W_TX_PAYLOAD_NOACK 命令
- void W_CMD(unsigned char CMD);
- unsigned char R_REG(unsigned char reg);
- void W_REG(unsigned char reg,unsigned char dat);
- void R_buf(unsigned char reg,unsigned char *pBuf,unsigned char len);
- void W_buf(unsigned char reg,unsigned char *pBuf,unsigned char len);
- void RX_mode();
- void TX_mode();
- unsigned char nRF24L01_send(unsigned char const *addr,unsigned char *pBuf);
- void nRF24L01_send_noack(unsigned char const *addr,unsigned char *pBuf);
- void nRF24L01_ack_sendback(unsigned char pipe,unsigned char *pBuf);
- void nRF24L01_recive(unsigned char *pBuf);
- void nRF24L01_init();
- #endif
nRF24L01.c - #include<24l01.h>
- #include<string.h>
- unsigned char const ADDR0[3]={0xa0,0xa0,0xa0};
- unsigned char const ADDR1[3]={0xa1,0xaa,0xaa};
- unsigned char const ADDR2[3]={0xa2,0xaa,0xaa};
- unsigned char const ADDR3[3]={0xa3,0xaa,0xaa};
- unsigned char const ADDR4[3]={0xa4,0xaa,0xaa};
- unsigned char const ADDR5[3]={0xa5,0xaa,0xaa};
- /*
- **写单字节命令
- **如:FLUSH_RX,FLUSH_TX
- **CMD:命令
- */
- void W_CMD(unsigned char CMD)
- {
- CSN=0;
- SPI_RW(CMD);
- CSN=1;
- }
- /*
- **读寄存器值
- **如:config,FIFO_status
- **reg:寄存器名
- **返回寄存器值
- */
- unsigned char R_REG(unsigned char reg)
- {
- unsigned char dat;
-
- CSN=0;
- SPI_RW(reg);
- dat=SPI_RW(NOP);
- CSN=1;
-
- return dat;
- }
- /*
- **写寄存器值
- **如:config
- **reg:寄存器名;dat:寄存器值
- */
- void W_REG(unsigned char reg,unsigned char dat)
- {
- CSN=0;
- SPI_RW(W_REGISTER|reg);
- SPI_RW(dat);
- CSN=1;
- }
- /*
- **读buf
- **如:读RD_RX_PLOAD
- **reg:寄存器名,*pBuf:接收数组,len:读取长度
- */
- void R_buf(unsigned char reg,unsigned char *pBuf,unsigned char len)
- {
- CSN=0;
- SPI_RW(reg);
- if(len>32) len=32;
- while(len--)
- *pBuf++=SPI_RW(NOP);
-
- CSN=1;
- }
- /*
- **写buf
- **如:写TX_ADDR、RX_ADDR_P0
- **reg:寄存器名,*pBuf:写入的数据,len:写入的长度
- */
- void W_buf(unsigned char reg,unsigned char *pBuf,unsigned char len)
- {
- CSN=0;
- SPI_RW(reg);
- if(len>32) len=32;
- while(len--)
- SPI_RW(*pBuf++);
-
- CSN=1;
- }
- /*
- **接收模式初始化
- */
- void RX_mode()
- {
- CE=0;
- W_REG(SETUP_AW,0x01); //addr width 3bytes
- W_REG(RF_CH,0x40); //RF CH
- W_REG(RF_SETUP,0x07); //1M,0DB,LNA
- W_REG(DYNPD,0x3f); //enable pipe0~pipd5 dyn
- W_REG(FEATRUE,0x07); //enable dyn,enable payload with ACK,enable NACK payload
- W_buf(RX_ADDR_P0,(unsigned char *)ADDR0,3); //pipe0 addr
- W_buf(RX_ADDR_P1,(unsigned char *)ADDR1,3); //pipe1 addr
- W_buf(RX_ADDR_P2,(unsigned char *)ADDR2,3); //pipe2 addr
- W_buf(RX_ADDR_P3,(unsigned char *)ADDR3,3); //pipe3 addr
- W_buf(RX_ADDR_P4,(unsigned char *)ADDR4,3); //pipe4 addr
- W_buf(RX_ADDR_P5,(unsigned char *)ADDR5,3); //pipe5 addr
- W_REG(EN_AA,0x3f); //enable ACK
- W_REG(EN_RXADDR,0x3f); //enable pipe0~5
- W_CMD(FLUSH_TX); //flush TX fifo
- W_CMD(FLUSH_RX); //flush RX fifo
- W_REG(STATUS,0xf0); //clear status
- W_REG(CONFIG,0x3b); //PRX mode,power up,1byte CRC,enable CRC,enable RX_DS interrupt
- CE=1; //enable RX mode
-
- }
- /*
- **发送模式初始化
- */
- void TX_mode()
- {
- CE=0;
- W_REG(SETUP_AW,0x01); //addr width 3bytes
- W_REG(SETUP_RETR,0x2f); //waite 750us,retransmit 15times
- W_REG(RF_CH,0x40); //RF CH
- W_REG(RF_SETUP,0x07); //1M,0DB,LNA
- W_REG(DYNPD,0x01); //enable pipe0 dyn
- W_REG(FEATRUE,0x07); //enable dyn,enable ACK,enable NACK payload
- W_buf(RX_ADDR_P0,(unsigned char *)ADDR0,3); //pipe0 addr
- W_buf(TX_ADDR,(unsigned char *)ADDR0,3); //TX addr
- W_REG(EN_AA,0x01); //enable ACK
- W_REG(EN_RXADDR,0x01); //enable pipe0
- W_CMD(FLUSH_TX); //flush TX fifo
- W_CMD(FLUSH_RX); //flush RX fifo
- W_REG(STATUS,0xf0); //clear status
- W_REG(CONFIG,0x3a); //PTX mode,power up,1byte CRC,enable CRC,enable,enable RX_DS interrupt
- }
- /*
- **发送数据
- **向接收机发送数据,数据长度在32字节以内
- ** *addr:接收机地址,*pBuf:发送的数据
- **发送成功返回1,发送超时返回0
- */
- unsigned char nRF24L01_send(unsigned char const *addr,unsigned char *pBuf)
- {
- W_buf(RX_ADDR_P0,(unsigned char *)addr,3); //设置通道0地址,与发送地址相同,用来接收应答信息
- W_buf(TX_ADDR,(unsigned char *)addr,3); //发送地址,与接收机的接收通道地址相同
-
- W_CMD(FLUSH_TX); //清理发送缓存
- W_buf(W_TX_PAYLOAD,pBuf,strlen((char const *)pBuf)); //发送数据写入缓存
-
- CE=1;
- delay10us(2); //触发发送,时间大于10us
- CE=0;
-
- while(!(R_REG(STATUS)&0x20)) //判断发送完成
- {
- if(R_REG(STATUS)&0x10) //发送超时
- {
- W_REG(STATUS,0x10); //清超时标志
- return 0; //超时退出
- }
- }
- W_REG(STATUS,0x20); //清发送完成标志
- return 1; //发送完成退出
- }
- /*
- **发送无需应答的数据包
- ***addr:发送地址,*pBuf:发送的数据
- */
- void nRF24L01_send_noack(unsigned char const *addr,unsigned char *pBuf)
- {
- W_buf(TX_ADDR,(unsigned char *)addr,3); //设置发送地址,与接收机接收通道地址相同
-
- W_CMD(FLUSH_TX); //清发送缓存
- W_buf(W_TX_PAYLOAD_NOACK,pBuf,strlen((char const *)pBuf)); //写发送数据
- CE=1;
- delay10us(2); //激活发送
- CE=0;
- }
- /*
- **写应答数据包,接收机应答时发送给主机的数据,需在接收前写入
- **pipe:接收机接收通道,*pBuf:需要发送的数据
- */
- void nRF24L01_ack_sendback(unsigned char pipe,unsigned char *pBuf)
- {
- if(pipe<6)
- {
- W_CMD(FLUSH_TX); //清发送寄存器
- W_buf(W_ACK_PAYLOAD|pipe,pBuf,strlen((char const *)pBuf)); //写如数据
- }
- }
- /*
- **读取接收数据
- ***pBuf:数据存放地址
- */
- void nRF24L01_recive(unsigned char *pBuf)
- {
- memset(pBuf,0,32); //清空接收数据缓存
- if(R_REG(STATUS)&0x40) //判断有数据接收
- {
- R_buf(R_RX_PAYLOAD,pBuf,R_REG(R_RX_PL_WID)); //读取接收到的数据
- }
- W_REG(STATUS,0x40); //清接收标志位
- }
- /*
- **nRF24L01初始化
- */
- void nRF24L01_init()
- {
- CSN=1;
- CE=0;
-
- PC_DDR_DDR4=1; //端口配置
- PC_CR1_C14=1;
- PC_CR2_C24=1;
-
- PC_DDR_DDR3=1;
- PC_CR1_C13=1;
- PC_CR2_C23=1;
-
- PB_DDR_DDR4=0;
- PB_CR1_C14=1;
- PB_CR2_C24=0;
- #ifdef TX_MODE //模式选择,在头文件中定义
- TX_mode();
- #else
- RX_mode();
- #endif
-
- }
照例打包上传 THE END!
|