简介CI24R1无线收发模块与STC8H系列单片机的配套收发例程面向嵌入式开发者、电子竞赛参赛者及物联网项目爱好者重点解决NOACK无应答模式下无线数据连续发送与可靠接收问题。压缩包共220个文件以C源码、头文件、汇编、Keil工程及编译生成文件为主同时包含hex烧录文件、库文件与工程备份约980KB整体简洁紧凑便于按需移植与二次开发。目前已有329人学习。例程提供一套完整可运行的参考工程覆盖串口参数初始化、数据包结构定义、NOACK模式发射流程、串口中断接收解析等关键环节并给出CRC校验与错误排查思路。代码注释较为细致支持通过串口打印直观比对收发数据便于开发者快速验证无线链路状态对理解数据包封装、无线通信时序及后续扩展多点组网、功率控制等功能均具良好参考价值。开发者可参照例程提取通信模块嵌入自有项目从而缩短无线功能开发周期也可以根据实际应用自定义数据包格式灵活适配不同业务需求。1. CI24R1到底是个什么芯片——先搞清楚再动手说实话我第一次拿到CI24R1这颗料的时候第一反应是去翻手册确认它到底是不是一颗完整的射频SoC。这年头市面上打着“无线收发芯片”旗号的东西太多了有的是纯射频前端需要自己配MCU和协议栈有的是集成好的收发SoC寄存器一配就能跑。CI24R1属于后者——内置了2.4GHz频段的射频收发链路也集成了必要的数据缓冲、地址匹配、CRC校验逻辑外部只需挂一颗普通单片机51、STM32、AVR都行通过SPI接口下发配置、写入发送数据或者读出接收数据。这颗芯片的定位和nRF24L01PALNA那类模块比较接近但成本更低、外围更省比较适合做遥控器、无线数传、智能家居网关节点这类数据量不大、但要求低功耗和稳定性的场景。它工作在2.4GHz ISM频段也就是2.400GHz到2.4835GHz这个范围和Wi-Fi、蓝牙生活在一起所以实际项目里信道选择和时间规划不能太随意后面我会专门说这一块。很多初学者拿到CI24R1容易卡在一个认知误区上以为这芯片“上电就能收发”。实际上它和很多射频SoC一样上电后默认处于掉电模式必须由单片机通过SPI先把寄存器配置好、把收发模式切到工作状态然后CE引脚拉高它才开始真正干活。换句话说CI24R1只是一个“听话的外设”所有协议逻辑都在你的单片机代码里。理解了这一点下面所有寄存器配置和时序操作就都好理解了。这颗芯片的常见搭配是STC89C52、STM32F103这种入门级MCU。SPI接口频率控制在10MHz以内比较稳我曾试过硬拉高到15MHz以上偶尔会出现收发状态读写错位的问题后来老老实实按手册推荐值跑问题就消失了。所以这里也提醒一句不要因为单片机跑得快就把SPI时钟往死里调射频芯片对SPI时序的要求再宽松也有自己的上限。2. 硬件准备与接线——照着接就能跑CI24R1的封装一般是SOP16或者QFN为了调试方便我习惯直接买集成好的模块来用模块上已经画好了天线、晶振和去耦电容四周引出了电源、地、SPI四根线以及CE和IRQ两个控制脚。如果你用的是裸片就得自己画射频部分天线走线、晶振负载电容、电源滤波这些对新手来说坑比较多建议先模块调通再考虑裸片。模块接线其实非常简单以51单片机为例SPI可以用硬件的也可以用IO口模拟。我建议开始阶段用IO口模拟SPI原因很简单硬件SPI虽然快但很多51单片机没有硬件SPI接口得用I/O口加软件翻转即便有引脚分时复用和模式配置也会增加初期的调试变量。软件模拟SPI在收发例程这种低速场景下完全够用而且出问题时更容易排查。接线表我列一下这是我在STC89C52上实测过的一组配置CI24R1引脚单片机引脚说明VCC3.3V注意看模块是否带稳压必须统一供电域GNDGND共地是必须的否则逻辑电平无参考CSNP1.0片选拉低开始SPI通信SCKP1.1SPI时钟MOSIP1.2单片机→CI24R1数据线MISOP1.3CI24R1→单片机数据线CEP1.4芯片使能控制收发切换IRQP1.5中断输出低电平有效电源这里要特别说一句CI24R1的工作电压范围通常是1.9V到3.6V如果你手头的开发板是5V供电务必给模块单独供3.3V同时所有控制信号线最好经过电平匹配。很多初学者图纸上看着没问题一上电就测不到数据拿万用表一量才发现模块供电已经到了4.8V芯片早就进入保护或者工作异常状态了。另外模块旁边的VCC和GND之间加一个10uF电解电容加一个0.1uF陶瓷电容做去耦不要省射频芯片对电源纹波敏感供电不干净会直接体现在收发距离和误码率上。IRQ引脚我建议一定要接。虽然轮询状态寄存器也能完成收发但中断方式能让你在收发数据的同时干别的活这在后面想扩展多任务调度时非常关键。哪怕现阶段用轮询也把IRQ脚预留出来后面改起来方便。3. 收发核心流程从寄存器配置到数据流水线CI24R1的收发流程不复杂但必须严格遵循状态切换顺序。我直接按发射和接收两条线来写代码以C语言51单片机为例寄存器名不同芯片可能略有差异以你手里的数据手册为准但流程是完全通用的。3.1 发射端代码配置、写数据、拉高CE发射端的第一个动作是初始化SPI和GPIO随后向CI24R1写入配置寄存器。这组配置包括工作频率通常设置在2.4GHz附近的一个具体频点、发射功率0dBm、-6dBm等档位、空中速率1Mbps或2Mbps、CRC校验位数8位或16位、以及收发双方的地址。地址匹配是CI24R1判断“这包数据是不是发给我的”的重要依据两端地址必须完全一致。// 初始化CI24R1为发射模式 void CI24R1_TX_Init(void) { CE_LOW(); // 先禁用芯片 CSN_HIGH(); // 片选释放 // 写入配置寄存器伪代码寄存器名以手册为准 CI24R1_Write_Reg(CONFIG, 0x0E); // 上电、CRC使能、收发模式置为发送 CI24R1_Write_Reg(EN_AA, 0x01); // 使能自动应答通道0 CI24R1_Write_Reg(EN_RXADDR,0x01); // 使能接收通道0 CI24R1_Write_Reg(SETUP_AW, 0x03); // 地址宽度5字节 CI24R1_Write_Reg(SETUP_RETR,0x1A); // 自动重发延时重发次数 CI24R1_Write_Reg(RF_CH, 0x40); // 选择射频信道 CI24R1_Write_Reg(RF_SETUP, 0x0F); // 2Mbps、0dBm发射功率 CI24R1_Write_Reg(TX_ADDR, tx_addr); // 写入发射地址 CI24R1_Write_Reg(RX_ADDR_P0, rx_addr); // 接收通道0地址 }初始化完成后每次发送一包数据时要做的是写TX_PAYLOAD寄存器然后把CE引脚拉高至少10us芯片就会把缓冲区的数据调制发射出去。数据发完后会自动清TX_DS发送完成标志同时IRQ引脚会拉低通知单片机。记住写发送数据之前一定要确认芯片当前处于待机模式CE为低CE拉高发射后发射完成要再把CE拉低回到待机状态然后清标志、准备下一包。如果CE一直拉着不放芯片会一直处于发射状态反而没法进入下一个工作周期。// 发送一包数据 void CI24R1_Send(unsigned char *buf, unsigned char len) { CE_LOW(); // 确保待机 CSN_LOW(); // 写发送数据命令伪代码 SPI_Write_Bytes(WR_TX_PAYLOAD, buf, len); CSN_HIGH(); CE_HIGH(); // 拉高CE启动发射 delay_us(20); // 保持CE高至少10us CE_LOW(); // 发射完毕回到待机 // 等待发送完成中断或查询状态寄存器 while(!(CI24R1_Read_Status() TX_DS)); // 清标志准备下一次发送 CI24R1_Write_Reg(STATUS, TX_DS); }3.2 接收端代码配置、等待、读数据接收端初始化和发射端非常相似最大的区别是CONFIG寄存器中最末位的PRIM_RX位要置1把芯片从发送模式切到接收模式然后CE拉高芯片开始持续监听信道。// 初始化CI24R1为接收模式 void CI24R1_RX_Init(void) { CE_LOW(); CI24R1_Write_Reg(CONFIG, 0x0F); // 上电、CRC使能、接收模式 CI24R1_Write_Reg(EN_AA, 0x01); CI24R1_Write_Reg(EN_RXADDR,0x01); CI24R1_Write_Reg(SETUP_AW, 0x03); CI24R1_Write_Reg(RF_CH, 0x40); // 和发射端同一信道 CI24R1_Write_Reg(RF_SETUP, 0x0F); CI24R1_Write_Reg(RX_ADDR_P0, rx_addr); // 接收地址必须和发送端TX_ADDR一致 CI24R1_Write_Reg(TX_ADDR, tx_addr); CE_HIGH(); // 开始监听 }数据到达时芯片会自动把payload搬到RX_FIFO中RX_DR位置1IRQ引脚拉低。单片机收到中断后读状态寄存器、读出RX_PAYLOAD然后清标志继续等待下一包。这里有个很多人踩过的坑读payload时CSN拉低、发送RD_RX_PAYLOAD命令、连续读长度的数据读完后要把STATUS寄存器的RX_DR位写1清掉。如果忘了清标志或者清早了芯片可能会重复上报同一包数据造成看起来像是“收到了重复数据”的假象。// 接收中断处理 void CI24R1_IRQ_Handler(void) { unsigned char status CI24R1_Read_Status(); if(status RX_DR) // 有数据到达 { CE_LOW(); // 先停接收 CSN_LOW(); SPI_Write_Byte(RD_RX_PAYLOAD); for(i0; ilen; i) { rx_buf[i] SPI_Read_Byte(); } CSN_HIGH(); CE_HIGH(); // 重新回到接收状态 CI24R1_Write_Reg(STATUS, RX_DR); // 清中断标志 } }这条数据流水线用生活中的话来说就是发射端写一封信贴好邮票配置发送地址投进邮筒拉高CE邮局按地址送信接收端一直守在邮箱旁CE保持高电平监听收到信后按门铃IRQ拉低你开门取信读RX_PAYLOAD然后继续等待下一封。整个过程的核心就是把握好CE的电平时机和中断标志的清理时机这两个节奏对了收发就成功了一大半。4. 丢包、错包、乱码——收发不稳的常见坑和排查思路代码写完、两端都能跑通“自发自收”之后真正让人头疼的是放到实际环境里出现丢包、错包、乱码。这类问题我在CI24R1和nRF24L01上碰到过不止一次特征高度相似根因却五花八门。我按排查顺序从高频到低频列一下。4.1 两端参数不匹配地址、信道、速率、CRC必须完全一致问题特征发射端显示发送成功接收端完全收不到或者偶尔收到第一包后就再无后续。排查思路先检查信道两端的RF_CH寄存器是否一致再检查地址接收端的RX_ADDR_P0和发射端的TX_ADDR是否完全一致注意地址是逐字节比较一个字节对不上都收不到然后检查速率两端RF_SETUP中速率位必须一样1Mbps和2Mbps互不兼容最后检查CRC两端如果一边使能CRC一边没使能即使能收到也会因为校验不过而丢弃。这些参数检查起来不复杂但每项都要逐寄存器比对。我自己的习惯是写一个打印函数初始化完成后把关键寄存器读出来在串口上打印一遍肉眼比对这样可以避免“我以为配置了其实没写进去”的低级错误。4.2 供电不足或纹波过大导致灵敏度下降问题特征近距离收发都正常距离一拉远就丢包率骤升或者同样距离下时好时坏。很多项目的无线模块和电机、继电器、数码管共用电源电机启动的瞬间会把电源电压拉低一大截射频芯片的工作电压低于规格下限后发射功率和接收灵敏度都会明显劣化。排查时可以拿示波器在CI24R1的VCC脚上观察纹波如果发现毛刺幅度超过100mV就要考虑加LC滤波或者单独用LDO供电。最彻底的做法是无线模块的电源树单独走一条不要和电机驱动共用粗放型降压电路。实测下来电源干净与否对通信距离的影响经常比天线本身还大。4.3 中断标志没清干净导致“假死”问题特征程序跑了一段时间后接收端突然不再响应任何数据但发射端一直在发且发送端显示已发送成功。这个问题的根源通常在于接收中断处理里少了清标志的步骤或者清标志的时机不对。CI24R1的IRQ引脚是低电平有效的开漏输出如果中断标志一直处于置位状态IRQ会一直被拉低单片机的中断触发条件就永远停在“有中断”但你又没有新数据可读循环就卡死了。解决办法是每次读走payload后立即写STATUS清标志如果同一中断里处理了多个事件发送完成接收完成要一次性把对应标志全清掉不能只清一个。4.4 SPI时序问题时钟极性、相位和速率问题特征寄存器读写时灵时不灵初始化时好时坏换一块单片机或者换一个IO口模拟方式后问题复现但表现不同。排查SPI时先看CPOL和CPHA是否匹配CI24R1手册规定的模式。很多51单片机用IO口模拟SPI代码里SCK空闲电平和高低翻转边沿采样没按芯片手册来时钟极性和相位错了读到的一律是全FF或者全00。其次看SPI速率前面我说过10MHz以内比较稳妥如果单片机主频较高、IO翻转很快可以在SPI字节传输函数里加几个_nop_()延时给芯片足够的建立时间。最后看CSN时序CSN拉低后不能立刻传数据最好间隔几微秒确保芯片状态机已经进入命令接收状态CSN拉高后也要留一点间隔再操作其他引脚避免状态机还没复位就收到下一条命令。4.5 环境干扰和多设备共存问题特征同一频段有Wi-Fi或者蓝牙设备通信质量起伏明显尤其在公司、商场等无线电环境复杂的地方调试时问题加重。2.4GHz是个拥挤的频段Wi-Fi的1、6、11信道实际上就把整个2.4GHz范围分得差不多了。CI24R1的RF_CH寄存器可选的频点很多避开Wi-Fi主通道是提高成功率的有效手段。实测中我习惯把RF_CH设到2405MHz附近尽量避开Wi-Fi的主力频点和蓝牙跳频最活跃的区域。当然如果设备间要做双向实时性和多设备并发最好引入跳频机制这个属于进阶玩法后面再展开。4.6 简易排查流程如果你在调试现场一时间不知道该从哪里下手我建议按这个顺序走用固定发射、固定接收的测试程序确认硬件和基本收发链路是通的。把两端串口打印打开发送端每次发送后打印状态寄存器值接收端收到一包打印一包。近距离先测模块相距半米内若还丢包基本是配置或硬件问题。确认没有丢包后逐步拉开距离观察多少米开始出现丢包评估是否在项目预期范围内。再做屏蔽测试把发射模块放进金属盒里接收端应该完全收不到数据如果还能收到说明硬件上的屏蔽或隔离有问题。这套流程排下来90%以上CI24R1收发不稳定的问题都能定位到根因而不是靠猜。5. 实测中容易忽略的射频细节——天线、地平面和晶振很多人把代码的问题调通后就以为项目结束了开始测试距离结果发现号称能传几十米的模块实际在室内隔一堵墙就不行了。这时候问题多半不在代码而在硬件的射频细节。CI24R1模块上只有一个2.4GHz的PCB天线或者弹簧天线。天线下方对应位置如果被单片机的排针、杜邦线或者金属外壳遮住辐射效率和接收灵敏度会直线下降。我实测过模块天线朝上、周围5mm内没有其他金属件时通信距离明显比紧贴着塑料外壳引脚向上延伸时好。这个差距不是10%20%甚至可以到一倍以上。所以结构设计阶段就要给天线留出净空区不要把天线正上方压着线束。地平面的完整性也很关键。模块上如果只有一层PCB天线下方最好有完整的参考地不要被走线割裂成几个碎块。自己画板子的朋友特别注意天线附近的顶层和底层不要铺乱七八糟的铜箔更不要放高噪声的DC-DC电感和开关节点。这些高频噪声会通过空间耦合进天线相当于从内部抬高了噪声底距离自然上不去。晶振部分同样容易被忽略。CI24R1需要一颗16MHz的晶振作为射频参考时钟。晶振的负载电容如果匹配不对频率偏差超过一定范围收发双方的信道就会错位。之前我遇到过一包都收不到、检查所有寄存器都正常最后拿频谱仪看发射频谱发现中心频率偏了300多kHz就是因为晶振负载电容匹配不当。这个问题在批量生产时尤其值得关注如果一批模块一致性不好很多其实是晶振批次差异导致的。对只用成品模块的读者来说这些硬件的坑你没法改但至少要学会识别同样是CI24R1模块不同家的PCB天线设计、晶振匹配、电源滤波品质差别很大不要只看价格。我踩过的教训是某一次图便宜买了一堆模块结果同一套代码在同一距离下有的模块稳定有的模块疯狂丢包后来拆开对比才发现天线匹配电路的元件参数都不一样。所以前期打样时多买几家模块来对比测试远比事后排查省时间。6. 进阶体验中断驱动、自动重发和低功耗设计基础收发例程跑通后接下来要思考的是怎么把CI24R1用到真实产品里这时候有两个方向值得花时间一个是从轮询改成中断驱动一个是针对电池供电场景做低功耗设计。6.1 用中断驱动替代轮询给MCU留出干活的时间轮询模式代码简单但主循环被无线收发占满MCU没法干其他事。改成中断驱动后IRQ引脚接单片机外部中断输入每次有数据到达或者发送完成时硬件自动跳进中断服务函数处理主循环专注做业务逻辑。改驱动方式时有个容易出问题的地方IRQ引脚是低电平有效如果寄存器配置成低电平触发的外部中断中断服务函数执行过程中如果芯片又来了新事件IRQ会第二次拉低而你的中断函数可能还在处理上一次的事件甚至还没来得及清标志。这时候就会产生中断重入或者丢事件的问题。我的做法是外部中断配置成下降沿触发进入中断函数后先读状态寄存器、清标志再处理数据处理数据的耗时尽量短不要在中断里做阻塞式串口打印或者延时等待。对于51单片机中断服务函数里把payload先拷到全局缓冲区置一个标志位具体解析和业务处理放到主循环里做这是最稳妥的方案。6.2 自动重发数据可靠性的第一道防线如果CI24R1支持自动重发机制很多同类型芯片有就一定要用起来。所谓自动重发是指发射端发完数据后如果在一段时间内没有收到接收端回送的ACK应答帧芯片会自动把同一包数据重新发送若干次直到收到ACK或者重发次数耗尽。这个机制对提高无线数传的可靠性帮助巨大尤其是在有突发干扰的场景下比你用单片机上层的超时重发效率高得多因为它不用MCU参与芯片自己就在链路层完成了重发。配置自动重发时重点设置SETUP_RETR寄存器里的重发延时和重发次数。重发延时需要根据空中速率和payload长度来估算设置太短接收端还没处理完上一包就收到重发包反而增加碰撞概率设置太长单次发送的最长耗时变长影响吞吐。重发次数默认配置5次是常见起点很多场景也够用。如果应用对实时性要求高可以适当调小重发次数让失败快速上报到上层如果更看重可靠性就调大次数。6.3 低功耗让模块在休眠与唤醒之间切换低功耗场景下CI24R1不能一直待在接收模式否则电流消耗动辄十几毫安对纽扣电池供电的设备不太友好。常见做法是让模块大部分时间处于掉电模式然后定时唤醒接收一小段时间如果收到有效数据就进入数据处理流程收不到就继续睡。这个“间歇性监听”策略和很多低功耗无线协议比如BLE的广播、扫描机制思路一致只是用CI24R1的寄存器配置来实现。切换到掉电模式就是往CONFIG写入PWR_UP0进入待机再唤醒注意时序上不能从掉电模式直接跳到接收模式需要先进入待机等晶振稳定后再切接收。晶振从起振到稳定的时间通常需要毫秒级这个时间不能省否则发射频谱不对接收也会失败。定时唤醒用的是MCU里的定时器中断到时间了就拉高CE进入监听同时开启一个接收超时定时器。如果超时还没收到数据就关CE、掉电、继续睡。这套机制做下来平均功耗能压到微安到毫安级别具体数值取决于唤醒周期和每次监听时长。6.4 多任务场景下的调度思路热搜词里有“单片机如何实现同时运行多个任务调度机制”这也是无线模块应用中很现实的问题。CI24R1的收发不应该是阻塞式的否则接收数据的同时没法处理按键扫描和显示刷新。建议在主循环里做一个非常轻量的时间片轮询把无线收发拆成“请求发送”“等待完成中断”“处理接收数据”几个状态主循环每次跑一圈就检查一次这些状态是否满足切换条件。这样一来无线收发只是主循环任务中的一个状态机其他任务照常运行整个系统的实时性和扩展性都会好很多。这种状态机思想其实和CI24R1芯片本身的寄存器状态机是呼应的芯片有掉电、待机、发射、接收多个状态你在软件里也维护一个对应的高层状态机两端步调一致项目就稳了。7. 写在最后这是我调完CI24R1之后最想说的几句CI24R1这颗芯片本身算不上复杂但“收发例程”这四个字背后的门道远远不止把代码下载进去看两个LED亮起来那么简单。从寄存器配置、SPI时序到硬件供电、天线净空再到中断设计、低功耗策略每一层都能坑掉大批初学者。我见过不少人拿着例程跑通了收发以为大功告成结果换到真正的产品环境里隔一堵墙就丢包然后开始怀疑芯片是不是假的、模块是不是次品——其实很多时候问题就出在电源纹波和天线周边环境这些外人很难一眼看出来的地方。如果你现在正卡在某个具体问题上我的建议是先把硬件最小系统确认干净再用串口把每一个关键寄存器的值打印出来核对一遍最后才去怀疑协议和天线。这个顺序不会错。另外调试无线的时候逻辑分析仪或者示波器真的是救命的东西抓一下SPI时序和IRQ波形很多“灵异问题”当场就现原形了。最后分享一个小技巧在开发阶段给CI24R1的收发函数里加一个计数器每次发送或接收成功就累加并且把计数器的值在串口上周期性地打印出来。这样你调程序的时候不用总是盯着模块的小灯看直接看串口数字就能判断当前的收发是否还活着。这个习惯我保留到了现在特别是在调低功耗和跳频机制的时候它比示波器还直观。希望这篇东西能帮你少踩几个坑让你和CI24R1的合作从“能跑”尽快走向“跑得稳”。本文还有配套的精品资源点击获取