国产CI24R1平替nRF24L01:51单片机无线收发例程详解

发布时间:2026/9/3 4:58:24
国产CI24R1平替nRF24L01:51单片机无线收发例程详解 简介CI24R1单片机收发例程是一份面向单片机开发者的无线通信实战代码包围绕STC8H系列单片机与CI24R1模块的串口通信讲解NOACK无应答模式下的数据收发流程适合需要快速上手2.4GHz无线数据传输的嵌入式初学者或项目工程师。压缩包内共220个文件主要包含68个h头文件与50个c源文件另有lst、obj、lib、asm等编译中间文件与工程配置可帮助开发者从源码层面理解初始化、数据包构造、串口中断接收及CRC校验等关键环节整体约980KB。已有329人学习该资源。例程附带的工程文件可直接在开发环境中加载调试代码注释清晰便于对照模块技术手册进行二次开发也可作为多点通信、功率控制等进阶功能的基础参考。 做无线遥控、数据采集这类项目最烦的就是通信这块动不动就不通。之前我一直用nRF24L01芯片是好用可价格涨得离谱货期还没谱。后来朋友推荐了国产的CI24R1说是和nRF24L01引脚兼容、寄存器兼容改几行代码就能替换价格却便宜一大截。当时我半信半疑结果实测下来确实能直接平替收发稳定度也够用。如果你手头正好有CI24R1模块或者正纠结选型这篇就围绕CI24R1单片机收发例程把硬件连接、寄存器配置、收发逻辑和调试过程中踩过的坑一次性讲清楚。这套方案适合谁搞51单片机、STM32做无线透传的做遥控小车、无线温湿度、智能家居节点或者毕业设计需要低成本2.4G通信的可以参考。下面内容以51单片机为例展开但核心寄存器操作和收发时序在STM32上同样适用直接平移就行。1. CI24R1核心特性与方案选型1.1 为什么选CI24R1而不是nRF24L01先说结论CI24R1是一个2.4GHz频段的无线收发芯片GFSK调制内部集成链路层协议支持1Mbps和2Mbps两种空中速率125个可配置频道发射功率四档可调。最关键的一点它的引脚和寄存器映射与nRF24L01基本一致所以市面上大量现成的nRF24L01驱动代码拿过来改一改引脚宏定义就能跑。价格才是真正的杀手锏。同样在立创商城搜nRF24L01模组普遍十几块起步CI24R1模组几块钱就能拿下。而且CI24R1的PA版本也就是标着CI24R1-PA的模块发射功率能做到20dBm左右空旷环境下传个几百米没太大压力这对做遥控、农业数据采集这类场景非常实用。除了成本优势CI24R1的电源适应性也不错工作电压2.0V到3.6V都能正常工作不过需要注意的是逻辑电平最好是3.3V。51单片机通常是5V供电所以硬件上必须加电平匹配不然SPI通信大概率会有偶发乱码。1.2 芯片内部结构和工作原理CI24R1内部大致可以分成三个部分射频前端、基带控制逻辑、SPI寄存器接口。射频前端负责把数据调制到2.4GHz载波上发出去同时把接收到的射频信号解调成数字基带信号基带控制逻辑负责处理地址匹配、CRC校验、自动应答、重发这些链路层协议SPI寄存器接口则负责和单片机通信单片机通过读写寄存器来控制芯片的工作模式、配置收发参数。实际使用中你需要关心的核心寄存器不多大致就是配置寄存器、状态寄存器、收发地址寄存器、频道寄存器、射频参数寄存器这几个。想要进入发送模式核心就是两个动作把PRIM_RX位清零表示发送模式然后拉高CE引脚启动发送想要进入接收模式就是把PRIM_RX置1同样拉高CE进入接收状态。整个工作流程可以类比成一个对讲机你和对方约定好同一个频道、同一个组呼ID按下发送键讲话松开就等对方回话。频道对应RF_CH寄存器组呼ID就是收发地址CE引脚就相当于对讲机的PTT按键。2. 硬件连接与最小系统搭建2.1 51单片机与CI24R1接线方案CI24R1通信接口是4线SPI外加CE和IRQ两个控制引脚一共6根线接线关系非常固定。单片机引脚CI24R1模块引脚说明P1.0CSN片选低电平有效P1.1SCKSPI时钟P1.2MOSI主机输出从机输入P1.3MISO从机输出主机输入P1.4CE收发控制高电平启动P1.5IRQ中断输出低电平有效我用的51单片机是STC89C52RC3.3V和5V同时存在。CI24R1模块VCC接3.3V但SPI引脚直连5V单片机会有风险。稳妥的做法是MISO引脚加一个10kΩ电阻上拉到3.3VMOSI、SCK、CSN、CE这四个由单片机输出到芯片输入的引脚串一个1kΩ电阻再连过去实测跑20cm排线、SPI时钟设到1MHz以下完全没问题。如果用STM32就直接多了全部引脚配置为复用推挽输出STM32F103的引脚耐压是5V容忍的接线省心不少。2.2 供电和去耦的细节CI24R1这种2.4G射频芯片对电源纹波比较敏感发送瞬间电流能达到几十毫安供电没处理好会出现发送成功率低、接收灵敏度差等诡异问题。我习惯在模块的VCC和GND之间并两个电容一个10μF电解电容一个0.1μF瓷片电容。电解电容负责稳住瞬间大电流瓷片电容负责滤掉高频噪声。另外模块的地线要尽量短最好是在PCB或面包板上单独拉一条模拟地和单片机数字地分开走最后在电源入口处单点汇合。如果你用的是那种带PA的CI24R1模块注意天线区域周围不要走线更不要有铜皮不然阻抗匹配被破坏发射功率再高也发射不出去。3. 软件架构与核心代码实现3.1 SPI底层通信封装CI24R1的SPI时序定义为片选CSN拉低后SCK上升沿写入数据下降沿读取数据一次写操作最多可以连续读写5个字节。和常见的存储芯片SPI时序有些差别不能用那种上升沿锁存的固定模板直接套需要对照数据手册的时序图写。下面是51单片机GPIO模拟SPI的代码我用的是STC89C52主频12MHzSPI速率设置到几百kHz实测足够稳定// 引脚定义 sbit CE P1^4; sbit CSN P1^0; sbit SCK P1^1; sbit MOSI P1^2; sbit MISO P1^3; // SPI写一个字节 void SPI_WriteByte(uint8_t dat) { uint8_t i; for (i 0; i 8; i) { SCK 0; if (dat 0x80) MOSI 1; else MOSI 0; SCK 1; dat 1; } SCK 0; } // SPI读一个字节 uint8_t SPI_ReadByte(void) { uint8_t i, dat 0; for (i 0; i 8; i) { SCK 0; SCK 1; dat 1; if (MISO) dat | 0x01; } SCK 0; return dat; } // 读写寄存器 uint8_t SPI_ReadReg(uint8_t reg) { uint8_t val; CSN 0; SPI_WriteByte(reg 0x1F); // R_REGISTER命令 val SPI_ReadByte(); CSN 1; return val; } void SPI_WriteReg(uint8_t reg, uint8_t val) { CSN 0; SPI_WriteByte((0x20) | (reg 0x1F)); // W_REGISTER命令 SPI_WriteByte(val); CSN 1; }这里有个容易搞混的点读写寄存器命令的最高位不一样。读寄存器是0x00开头写寄存器是0x20开头也就是最高位为1。如果这个命令字写错了后面所有操作全是无效的。初始化时可以先读一遍CONFIG寄存器看看能不能读到默认值0x08这是检验SPI时序是否正确的最快方法。3.2 芯片初始化配置流程初始化流程一般是检查SPI通信正常、写收发地址、配置自动应答和重发、设置频道和速率、配置CRC、上电、进入接收或发送模式。下面这段代码是完整的初始化#define TX_ADDR_WIDTH 5 #define RX_ADDR_WIDTH 5 // 初始化CI24R1 void CI24R1_Init(void) { CE 0; // 先让芯片待机 CSN 1; // 写入收发地址这里用0xE7E7E7E7E7做示例 SPI_WriteReg(0x10, 0xE7); // TX_ADDR寄存器5字节地址 SPI_WriteReg(0x11, 0xE7); SPI_WriteReg(0x12, 0xE7); SPI_WriteReg(0x13, 0xE7); SPI_WriteReg(0x14, 0xE7); SPI_WriteReg(0x0A, 0xE7); // RX_ADDR_P0寄存器 SPI_WriteReg(0x0B, 0xE7); SPI_WriteReg(0x0C, 0xE7); SPI_WriteReg(0x0D, 0xE7); SPI_WriteReg(0x0E, 0xE7); // 使能通道0接收地址开启自动应答 SPI_WriteReg(0x01, 0x01); // EN_AA使能通道0自动应答 SPI_WriteReg(0x02, 0x01); // EN_RX_ADDR使能通道0 // 地址宽度5字节 SPI_WriteReg(0x03, 0x03); // 自动重发500次间隔250us SPI_WriteReg(0x04, 0x1A); // 设置频道这里用2.4GHz 40 2.440GHz SPI_WriteReg(0x05, 40); // 射频参数2Mbps发射功率0dBm开启LNA SPI_WriteReg(0x06, 0x0E); // 配置寄存器使能CRC16位CRC上电发送模式 SPI_WriteReg(0x00, 0x0E); }关于地址是所有入网设备共用的一个概念。同一个网络里的所有模块地址必须完全一致否则接收端无法产生地址匹配中断数据就丢了。地址用5字节还是4字节可以在SETUP_AW寄存器里设置我的建议是直接用5字节降低误匹配的概率。频道也是同理一个系统里所有模块必须工作在同一频道。频道号计算公式是2400 RF_CH单位MHz比如RF_CH40就是2440MHz。如果周围2.4G环境干扰重可以试试换个频道避开WiFi常用的1、6、11信道。3.3 发送端完整代码实现发送端的核心逻辑是把要发的数据写入TX_FIFO拉高CE至少10μs芯片自动完成整个数据包的调制发送发送完成后会产生TX_DS中断。如果开了自动应答还会在发送后等待接收端的应答包超时未收到就自动重发重发超过上限会产生MAX_RT中断。// 发送一包数据最多32字节 uint8_t CI24R1_Send(uint8_t *buf, uint8_t len) { uint8_t status; if (len 32) len 32; // 拉低CE进入待机状态 CE 0; // 清空TX_FIFO CSN 0; SPI_WriteByte(0xE1); // FLUSH_TX命令 CSN 1; // 写TX_PAYLOAD CSN 0; SPI_WriteByte(0xA0); // W_TX_PAYLOAD命令 for (uint8_t i 0; i len; i) { SPI_WriteByte(buf[i]); } CSN 1; // 拉高CE启动发送 CE 1; delay_us(20); CE 0; // 等待发送完成这里做个简单超时 uint16_t timeout 10000; do { status SPI_ReadReg(0x07); // STATUS寄存器 timeout--; } while (!(status 0x20) !(status 0x10) timeout 0); // 清中断标志 SPI_WriteReg(0x07, status | 0x30); if (status 0x10) { return 1; // 重发超限发送失败 } if (status 0x20) { return 0; // 发送成功 } return 2; // 超时 }发送完成标志在STATUS寄存器的TX_DS位bit5重发超限标志在MAX_RT位bit4。这两个标志位都是写1清零所以代码里清中断用的是读状态后或上0x30再写回去。这个操作要小心别把RX_DR位也顺手清了接收模式下数据还没读出来就清中断会导致数据丢失。延时函数要准。CE高电平持续时间在数据手册里写的是10μs以上我实际测试2Mbps速率下20μs比较稳妥。如果用带PA的模块这个时间稍微放大一点到50μs保证PA完全使能。3.4 接收端完整代码实现接收端的逻辑就更简单了拉高CE后芯片自动进入接收状态检测到有效数据包后把数据存入RX_FIFO同时置位RX_DR中断标志单片机读到中断后去读RX_PAYLOAD。// 进入接收模式 void CI24R1_RXMode(void) { CE 0; // CONFIG寄存器使能CRC16位CRC上电接收模式 SPI_WriteReg(0x00, 0x0F); // 清空RX_FIFO CSN 0; SPI_WriteByte(0xE2); // FLUSH_RX命令 CSN 1; // 清状态寄存器 SPI_WriteReg(0x07, 0x70); CE 1; delay_us(100); } // 查询方式接收一包数据 uint8_t CI24R1_Receive(uint8_t *buf, uint8_t *len) { uint8_t status; uint8_t payload_len; uint8_t i; status SPI_ReadReg(0x07); // 没有收到数据 if (!(status 0x40)) return 0; // 读RX_PAYLOAD宽度 CSN 0; SPI_WriteByte(0x60); // R_RX_PL_WID命令 payload_len SPI_ReadByte(); CSN 1; // 读取数据 CSN 0; SPI_WriteByte(0x61); // R_RX_PAYLOAD命令 for (i 0; i payload_len; i) { buf[i] SPI_ReadByte(); } CSN 1; *len payload_len; // 清RX_DR中断 SPI_WriteReg(0x07, status | 0x40); return 1; }接收模式初始化时有个小技巧先清一次状态寄存器再拉高CE。因为芯片在上电或复位瞬间STATUS寄存器可能会有残留标志位不清掉的话第一次查询就可能误判为有数据读出来的却是空包。R_RX_PL_WID命令是读取当前RX_FIFO中第一个数据包的长度这个字节必须在读数据之前先读。有些驱动图省事直接固定读32字节或者按发送端已知长度去读这在固定长度通信时没问题但做动态长度协议时最好每次都读否则数据错位很难排查。3.5 自动应答与重发机制CI24R1的自动应答功能Enhanced ShockBurst非常实用。开启EN_AA后接收端收到数据会自动在极短时间内切换为发送模式回一个ACK包给发送端发送端如果没收到ACK就会自动重发直到收到ACK或重发次数达到上限。这个机制的好处是收发双方不用自己实现应答和超时重传可靠性大幅提升。比如做无线遥控数据包丢了重发接收端如果收到了重复包但没来得及去重就有问题。好在CI24R1在接收端收到重复数据包时会把STATUS寄存器的RX_DR位置位但不会把重复数据写入新FIFO位置所以需要在应用层判断一下重复包。我的建议是在发送的数据格式里加一个包序号字段接收端判断序号是否变化没变化就认为是重发包直接丢弃。这样配合硬件自动重传整个链路可靠性会非常接近有线通信。4. 调试过程与实测数据记录4.1 用逻辑分析仪验证SPI时序我调试这类模块的习惯是先把逻辑分析仪接到CSN、SCK、MOSI、MISO四个引脚上抓一发初始化时序看看。第一次上电时我发现MISO线上完全没有波形排查了半天最后发现是模块的VCC接到了5V上。CI24R1标称最大3.6V5V直接怼进去芯片虽然没烧但内部LDO可能已经进入保护状态SPI接口完全不响应。换成3.3V供电后才正常。之后再看SPI波形确认写寄存器的命令字是0x20开头读寄存器是0x00开头数据在SCK上升沿稳定输出这样初始化部分才算真正跑通。我建议你调试时优先抓这两个点命令字最高位对不对、CE拉高的时机对不对。RF芯片调试90%的问题都出在这两个地方。4.2 整机收发实测流程我的测试环境两块STC89C52开发板各接一个CI24R1模块A板按键触发发送B板上OLED显示收到的数据距离控制在5米内。发送端逻辑上电初始化后进入发送模式按下按键就发送一帧12字节数据分别是帧头、包序号、8字节载荷、CRC校验值。发送完成后通过串口打印状态码0表示成功1表示重发失败2表示超时。接收端逻辑初始化后进入接收模式主循环查询STATUS寄存器有数据就读取并打印到OLED和串口同时把收到数据的包序号回传给发送端显示。实测1000包在2Mbps速率下5米距离成功率100%拉远到30米穿一堵墙成功率降到87%左右。改成1Mbps速率后同样条件下成功率回升到96%。这说明CI24R1在低速率的接收灵敏度确实更好距离和穿墙性能也更强。如果你的数据量不大建议优先用1Mbps换来的可靠性提升非常明显。4.3 中断方式还是查询方式前面示例代码用的是查询方式主循环里不断读STATUS寄存器。这种方法简单但有个明显问题单片机在忙其他任务时可能来不及查询数据包就积压在FIFO里FIFO满了之后新包会被丢弃。如果想提高实时性推荐用IRQ引脚接单片机外部中断。CI24R1的IRQ引脚在发生RX_DR、TX_DS、MAX_RT这三个事件时会被拉低。51单片机的外部中断INT0是低电平触发直接把IRQ接P3.2即可。中断服务函数里要做的事情很少读STATUS寄存器根据标志位判断是收到数据还是发送完成然后处理相应的业务逻辑最后清状态寄存器。注意中断服务函数里不要做耗时操作比如OLED显示、串口打印这些全部放到主循环里用标志位去执行否则中断里待太久可能导致射频芯片接口超时。5. 常见问题与避坑经验5.1 发送失败、读不到状态最典型的现象是寄存器读出来全是0xFF或者0x00。0xFF大概率是SPI通信没建立检查CSN、SCK、MOSI接线顺序是否一致特别注意模块上的丝印引脚顺序有些模块标号和51单片机开发板上的丝印顺序是镜像的第一次接反很正常。0x00则大概率是供电电压不对或者芯片没有正常上电复位断电重新上电试试。另一个可能性是模块进入了一种叫挂起的异常状态。解决办法是给CE和CSN都拉低延时几毫秒再重新初始化。如果还不行就直接断电给芯片复位。5.2 收发两端无法通信确认几个参数完全一致频道RF_CH、收发地址、空中速率、地址宽度、CRC宽度。任何一项不一致数据都会在物理层就被丢弃接收端完全收不到任何中断。我遇到过最坑的一次是两端代码看起来完全相同但一端用的nRF24L01模块另一端用的CI24R1模块。虽然两者寄存器兼容但一个型号的模块上电后默认唤醒模式和另一个型号不一样导致一端在发送前先进入了睡眠另一端等不到。解决办法是统一用同一型号的模块或者在各自初始化代码里显式把PWR_UP位置1。5.3 近距离通信反而失败这是2.4G模块比较经典的现象俗称近场死区。两个模块距离非常近小于10cm时发射端的信号会过载接收端的前端电路导致接收端无法解调。解决办法是拉远距离或者适当降低发射功率。把RF_SETUP寄存器里发射功率从0dBm降到-12dBm近距离通信就正常了。如果你做的是桌面上的无线调试工具长期短距离工作建议默认把发射功率调低一档对功耗和稳定性都有好处。5.4 数据偶发错位和乱码排查思路按下面表格快速定位现象首选排查项解决方案偶发乱码供电纹波模块供电处加10μF电解电容低频错位SPI速率太高降低SPI时钟到500kHz以下特定频道乱码2.4G环境干扰用频谱仪或软件扫描选干净频道距离稍远就错天线损伤检查天线焊点不要弯折天线只发不收收发模式配置检查PRIM_RX位切换模式如果你的应用场景是电池供电还要注意发送时瞬间电流较大电池内阻大的话电压跌落会导致芯片复位。解决办法是加一个大电容储能或者用锂电池这类内阻小的电源。6. 总结与实用建议CI24R1作为nRF24L01的国产平替方案我认为在成本和供货稳定性上优势非常明显功能上完全覆盖常规无线透传需求而且寄存器级兼容让代码迁移成本几乎为零。如果你之前用过nRF24L01直接跳过来没有任何学习成本。最后分享两个小技巧第一批量生产时每块模块上电后先读一次芯片版本寄存器确认芯片正常响应再进入业务逻辑避免焊接不良导致现场批量故障排查困难第二如果数据量大建议把数据拆成28字节以内的小包分批发给协议头留出空间实测这样比一次塞满32字节的成功率更高因为FIFO操作和天线切换的时间余量更充裕。我这套例程已经在小批量项目里跑了大半年稳定性和重连机制都验证过了。你照着上面的代码搭一遍大概率一次就能通。要是调试过程中有问题欢迎在评论区交流。本文还有配套的精品资源点击获取