简介这份资源是面向51单片机开发者的CC1101无线收发器驱动代码合集针对TI超低功耗ISM频段射频芯片的嵌入式通信开发需求提供可直接参考移植的完整工程。包内共39个文件以c源码、h头文件、uvproj/uvopt工程配置、hex烧录文件及M51编译映射文件为主另含bak备份与plg编译日志压缩包约103KB覆盖发送与接收两个演示工程。代码同时给出IO模拟SPI与STC15内部硬件SPI两套通信实现并包含CC1101寄存器配置、初始化、数据收发及状态检查等模块便于对比两种SPI方式的时序差异与效率取舍。已有176人学习下载适合正在做无线传感器节点、遥控系统或短距离通信装置的开发者可借此快速理解SPI协议与射频芯片的配合方式缩短底层调试周期。1. 拆开一个 CC1101 程序压缩包从射频寄存器到能跑通的收发链路拿到一个名为 CC1101 程序.rar 的压缩包多数人的第一反应是解压、找 main.c、编译、烧录然后发现要么收不到数据要么一发就死。CC1101 是 TI 的一款 Sub-1GHz 射频收发芯片工作在 300–928MHz 的免许可频段常出现在无线抄表、遥控、传感器组网这类场景里。它本身不带协议栈所有收发逻辑都得靠 MCU 通过 SPI 配置几十个寄存器来实现。所以一个 CC1101 程序包的核心价值不在于代码写得多花哨而在于它有没有把频率、调制方式、数据率、同步字、包格式这几件事配对。这篇笔记面向的是手里已经有一个 CC1101 程序包、想把它跑起来并理解每一行配置含义的嵌入式开发者。我会按“先看懂寄存器配置、再搭最小收发链路、然后排错、最后做参数优化”的顺序讲中间会给出可直接抄的代码片段和参数表。2. 先看懂 CC1101 的寄存器配置频率、调制与数据率怎么算2.1 频率合成器三组寄存器决定你落在哪个频点CC1101 的载波频率由 FREQ2、FREQ1、FREQ0 三个 8 位寄存器共同决定公式是f_carrier (FREQ216 | FREQ18 | FREQ0) × f_XOSC / 2^16其中 f_XOSC 是晶振频率常见为 26MHz 或 27MHz。以 433.92MHz、26MHz 晶振为例计算出的 FREQ 寄存器值约为 0x10B13A。很多程序包里直接写死三个字节但不写注释换一个晶振就全错。我一般会在代码里保留这个宏定义方便换板子时重算// 假设 XOSC 26MHz目标频率 433.92MHz // FREQ (433.92e6 * 2^16) / 26e6 ≈ 0x10B13A #define CC1101_FREQ2 0x10 #define CC1101_FREQ1 0xB1 #define CC1101_FREQ0 0x3A void cc1101_set_frequency(void) { cc1101_write_reg(CC1101_FREQ2, CC1101_FREQ2); cc1101_write_reg(CC1101_FREQ1, CC1101_FREQ1); cc1101_write_reg(CC1101_FREQ0, CC1101_FREQ0); }逻辑说明这三个寄存器是频率合成器的频率控制字写入后芯片内部 PLL 会锁定到对应频点。参数说明如果你的板子用的是 27MHz 晶振同样的 433.92MHz 目标频率寄存器值会变成约 0x10A2B6直接抄 26MHz 的配置会偏出几十 kHz导致接收灵敏度急剧下降。所以拿到程序包第一件事就是确认晶振频率和频点是否匹配。2.2 调制方式与数据率MDMCFG 系列寄存器的联动关系CC1101 支持 2-FSK、GFSK、ASK/OOK、4-FSK 等调制方式由 MDMCFG2 寄存器的低三位选择。数据率则由 MDMCFG3 和 MDMCFG4 共同决定公式比较复杂TI 提供了 SmartRF Studio 工具直接生成配置。但如果你只有程序包没有工程文件就需要手动反推。常见做法是先看 MDMCFG2 的值判断调制方式再看 MDMCFG4 的高四位信道带宽和低四位数据率指数结合 MDMCFG3 算出实际速率。比如 MDMCFG40x2D、MDMCFG30x3B对应约 38.4kbps、GFSK、信道带宽 100kHz。这个组合在遥控和传感器场景里很常见。我一般会把这些配置整理成一张表方便对照寄存器典型值含义改动影响MDMCFG20x13GFSK无前导码质量门限改低三位切调制方式MDMCFG40x2D信道带宽 100kHz数据率指数改高四位影响接收带宽MDMCFG30x3B数据率尾数与 MDMCFG4 共同决定速率DEVIATN0x47频偏约 25kHz频偏太小接收端解调困难参数说明频偏 DEVIATN 必须和数据率匹配。数据率越高频偏需要越大。38.4kbps 下 25kHz 频偏是合理的但如果把数据率降到 1.2kbps 还保持 25kHz 频偏接收带宽就会浪费灵敏度反而下降。这是很多程序包里不会写、但实际调试时必须注意的点。2.3 包格式与同步字决定你能不能收到正确数据CC1101 支持定长包和变长包由 PKTCTRL0 寄存器控制。同步字由 SYNC1、SYNC0 两个寄存器定义通常设为 0xD391 或 0xAAAA。如果收发双方同步字不一致接收端永远收不到数据。程序包里如果只给发送端代码接收端就得自己配一套匹配的同步字和包格式。// 配置为变长包开启 CRC 校验白化关闭 void cc1101_set_packet_format(void) { // PKTCTRL0: 变长包CRC 使能白化关闭 cc1101_write_reg(CC1101_PKTCTRL0, 0x05); // 同步字 0xD391 cc1101_write_reg(CC1101_SYNC1, 0xD3); cc1101_write_reg(CC1101_SYNC0, 0x91); // 包长度最大 64 字节 cc1101_write_reg(CC1101_PKTLEN, 64); }逻辑说明PKTCTRL0 的 bit2 控制变长/定长bit1 控制 CRC 使能bit0 控制白化。0x05 表示变长包、CRC 使能、白化关闭。参数说明白化功能用于避免连续相同位导致的直流偏置但在低速短包场景下可以关闭简化调试。如果接收端收到数据但 CRC 一直报错优先检查同步字和 CRC 配置是否一致。3. 用 SPI 把 CC1101 驱动起来最小收发链路的搭建步骤3.1 硬件连接与 SPI 时序确认CC1101 通过 4 线 SPI 与 MCU 通信CSn、SCLK、SI、SO。另外还有 GDO0/GDO2 用于中断和状态输出。常见坑是 SPI 模式选错——CC1101 要求 SPI 模式 0CPOL0CPHA0且时钟极性不能反。我见过有人用模式 3 驱动结果读出来的寄存器值全是 0xFF。接线确认清单CSn 接 MCU 的普通 GPIO不要接硬件 SPI 的片选因为 CC1101 需要手动控制片选时序SCLK 空闲低电平SI 在 SCLK 上升沿采样SO 在 SCLK 下降沿变化GDO0 接 MCU 外部中断用于收发完成通知// SPI 读写一个字节模式 0 uint8_t spi_transfer(uint8_t data) { uint8_t i, recv 0; for (i 0; i 8; i) { // 上升沿前准备数据 if (data 0x80) SI_HIGH(); else SI_LOW(); data 1; SCLK_HIGH(); // 上升沿CC1101 采样 SI __delay_us(1); recv 1; if (SO_READ()) recv | 0x01; // 下降沿前读 SO SCLK_LOW(); __delay_us(1); } return recv; }逻辑说明这是软件模拟 SPI 的写法如果你用硬件 SPI直接配置为模式 0 即可。参数说明延时 1us 对应约 500kHz 时钟CC1101 支持最高 10MHz但低速更稳。注意 CSn 在每次读写寄存器前后都要拉低拉高且拉低后要等一小段时间再发命令。3.2 寄存器读写与命令选通CC1101 的寄存器访问分两种配置寄存器0x00–0x2E和命令选通寄存器0x30–0x3D。读配置寄存器时地址要或上 0x80写时直接写地址。命令选通寄存器写入后芯片会执行对应动作比如 0x34 是进入发送状态0x36 是进入接收状态。// 写配置寄存器 void cc1101_write_reg(uint8_t addr, uint8_t value) { CS_LOW(); while (SO_READ()); // 等待 SO 变低表示芯片就绪 spi_transfer(addr); // 地址 spi_transfer(value); // 数据 CS_HIGH(); } // 读配置寄存器 uint8_t cc1101_read_reg(uint8_t addr) { uint8_t value; CS_LOW(); while (SO_READ()); spi_transfer(addr | 0x80); // 读操作地址最高位置 1 value spi_transfer(0x00); CS_HIGH(); return value; } // 发送命令选通 void cc1101_strobe(uint8_t cmd) { CS_LOW(); while (SO_READ()); spi_transfer(cmd); CS_HIGH(); }逻辑说明读寄存器时地址或上 0x80 是 CC1101 的协议规定漏掉这一步读出来全是无效值。参数说明命令选通后不需要额外数据但部分命令如 TX/RX 需要等待 GDO0 中断确认完成。我一般会在发送后轮询 GDO0 或延时 10ms 再切回空闲。3.3 初始化序列与最小收发测试初始化顺序很重要先复位再写配置寄存器最后写频率和同步字。复位可以通过拉低 CSn 再拉高或者写 SRES 命令0x30。void cc1101_init(void) { CS_HIGH(); __delay_ms(10); CS_LOW(); __delay_ms(10); CS_HIGH(); __delay_ms(40); // 等待芯片内部复位完成 cc1101_strobe(CC1101_SRES); // 软复位 __delay_ms(10); // 写入配置寄存器序列 cc1101_write_reg(CC1101_IOCFG2, 0x29); // GDO2 输出 cc1101_write_reg(CC1101_IOCFG0, 0x06); // GDO0 输出同步字 cc1101_write_reg(CC1101_PKTCTRL0, 0x05); cc1101_write_reg(CC1101_MDMCFG4, 0x2D); cc1101_write_reg(CC1101_MDMCFG3, 0x3B); cc1101_write_reg(CC1101_MDMCFG2, 0x13); cc1101_write_reg(CC1101_DEVIATN, 0x47); cc1101_write_reg(CC1101_FREQ2, 0x10); cc1101_write_reg(CC1101_FREQ1, 0xB1); cc1101_write_reg(CC1101_FREQ0, 0x3A); cc1101_write_reg(CC1101_SYNC1, 0xD3); cc1101_write_reg(CC1101_SYNC0, 0x91); cc1101_write_reg(CC1101_PKTLEN, 64); }逻辑说明复位后先写 IOCFG 配置 GDO 引脚功能再写包控制、调制、频率、同步字。参数说明IOCFG0 设为 0x06 表示 GDO0 在发送同步字时拉高接收时同步字到达拉高可用于中断判断。初始化完成后发送端写 TX 命令接收端写 RX 命令就能做最小收发测试。4. 收发不通怎么排查从寄存器回读到频谱的逐层定位4.1 先确认 SPI 通信是否正常最直接的验证方法是读 CC1101 的版本寄存器0x31VERSION。正常值应该是 0x14 或 0x04不同批次略有差异。如果读出来是 0x00 或 0xFF说明 SPI 时序或接线有问题。排查步骤用示波器看 SCLK 和 SI确认在 CSn 拉低期间有 8 个时钟脉冲检查 SO 是否在 CSn 拉低后先变低再输出数据确认 MCU 的 SPI 模式为 0时钟不超过 10MHz如果读出来是 0x00检查 MISO 是否接反或上拉电阻缺失我遇到过一块板子VERSION 读出来一直是 0x00最后发现是 CSn 接在了硬件 SPI 的片选上导致时序被硬件自动控制和软件模拟的节奏冲突。改成普通 GPIO 后立刻正常。4.2 发送端有动作但接收端无数据如果发送端能进 TX 状态GDO0 也有翻转但接收端收不到优先查三件事频点是否一致用频谱仪看发送端是否有载波频点偏差超过 100kHz 就可能收不到同步字是否一致收发双方 SYNC1/SYNC0 必须完全相同数据率与频偏是否匹配接收端带宽要覆盖发送端频偏否则解调失败// 读取发送端和接收端的配置做对比 void cc1101_dump_config(void) { printf(FREQ2%02X FREQ1%02X FREQ0%02X\n, cc1101_read_reg(CC1101_FREQ2), cc1101_read_reg(CC1101_FREQ1), cc1101_read_reg(CC1101_FREQ0)); printf(MDMCFG4%02X MDMCFG3%02X MDMCFG2%02X\n, cc1101_read_reg(CC1101_MDMCFG4), cc1101_read_reg(CC1101_MDMCFG3), cc1101_read_reg(CC1101_MDMCFG2)); printf(SYNC1%02X SYNC0%02X\n, cc1101_read_reg(CC1101_SYNC1), cc1101_read_reg(CC1101_SYNC0)); }逻辑说明把两端配置打印出来逐项对比是最快定位不一致的方法。参数说明如果 FREQ 寄存器差一个字节频点可能差几百 kHz如果 MDMCFG2 低三位不同调制方式就不一样根本解调不了。4.3 收到数据但 CRC 校验失败CRC 失败通常有三种原因数据在传输中被干扰、白化配置不一致、包长度字段错误。先确认 PKTCTRL0 的 bit0白化两端是否一致再检查 PKTLEN 是否足够大。如果数据率较高且环境干扰大可以适当降低数据率或增加前导码长度。常见做法是把数据率从 38.4kbps 降到 9.6kbps接收灵敏度能提升约 6dBCRC 错误率明显下降。代价是传输时间变长适合对实时性要求不高的场景。5. 避坑与常见问题CC1101 调试中最容易翻车的五个点5.1 现象上电后读 VERSION 返回 0x00原因SPI 模式错误或 CSn 时序不对。CC1101 要求 CSn 在每次传输前拉低且拉低后要等待 SO 变低才能发数据。如果 CSn 一直拉低或一直拉高芯片不会响应。解决确认 CSn 由 GPIO 手动控制每次读写前拉低、读写后拉高。拉低后加 1–2us 延时再发第一个时钟。5.2 现象发送端能进 TX但 GDO0 不翻转原因IOCFG0 配置错误或者发送命令选通后没有等待芯片进入发送状态。CC1101 从空闲到 TX 需要约 1ms 的 PLL 锁定时间。解决检查 IOCFG0 是否设为 0x06 或 0x46发送后延时 2ms 再检查 GDO0。如果仍不翻转读 MARCSTATE 寄存器0x35确认芯片是否真的进入了 TX 状态。5.3 现象接收端偶尔收到数据但丢包严重原因接收带宽太窄或频偏不匹配。如果发送端频偏 25kHz接收端带宽只有 50kHz边带就被滤掉了。解决把 MDMCFG4 的高四位调大增加接收带宽。比如从 100kHz 调到 200kHz丢包率会明显改善但灵敏度会下降约 3dB。需要在距离和稳定性之间折中。5.4 现象换一块板子后通信距离骤降原因晶振频率不同或天线匹配网络差异。26MHz 和 27MHz 晶振对应的 FREQ 寄存器值不同直接抄配置会导致频点偏移。解决确认晶振频率重新计算 FREQ2/FREQ1/FREQ0。天线部分检查巴伦和匹配电容是否按参考设计焊接偏差超过 10% 就会明显影响辐射效率。5.5 现象低功耗模式下电流降不下来原因初始化后没有进入 SLEEP 或 IDLE 状态或者 GDO 引脚配置为输出且一直驱动负载。解决不用时写 SIDLE 命令0x36再写 SPWD 命令0x39进入休眠。GDO 引脚如果不用IOCFG 设为 0x2E高阻避免漏电。6. 把 CC1101 程序包用出价值参数优化与验证方法拿到一个 CC1101 程序包能跑通只是第一步。真正决定它能不能用在产品里的是你能不能根据场景调整参数并验证效果。我一般会做三组对比测试频偏扫描、数据率扫描、接收带宽扫描。每组只改一个变量记录丢包率和通信距离。频偏扫描时保持数据率不变把 DEVIATN 从 0x47 逐步调到 0x4F观察接收端 CRC 通过率。通常频偏越大抗频偏能力越强但占用带宽也越大。数据率扫描则从 1.2kbps 到 76.8kbps 分几档测试低速档灵敏度高、距离远高速档适合大数据量短距离。接收带宽扫描通过改 MDMCFG4 高四位实现带宽越宽对频偏容忍度越高但底噪也越大。验证方法上我习惯用两块板子做对拉测试一块固定发送一块移动接收每走一段距离记录一次丢包率。同时用频谱仪看发送端的载波和边带确认频偏和功率在预期范围内。如果没有频谱仪可以用 CC1101 自身的 RSSI 寄存器0x34读接收信号强度虽然精度有限但能判断趋势。一个容易被忽略的技巧是在发送端和接收端都开启 CRC 校验但接收端不要自动丢弃 CRC 错误包而是把错误包也读出来统计错误率。这样能区分是干扰导致的偶发错误还是配置不匹配导致的系统性错误。我一般会在接收中断里读 PKTSTATUS 寄存器0x38看 CRC_OK 位和 RSSI 值再决定是重发还是调参数。最后说一个血泪经验CC1101 的配置寄存器有几十个但真正影响通信成败的不到十个。与其把每个寄存器都背下来不如把 SmartRF Studio 生成的配置和实际板子上的晶振、天线匹配结合起来先保证频点、调制、同步字、数据率这四项一致再去调其他参数。我见过太多人卡在同步字不一致上折腾一整天最后发现只是程序包里发送端和接收端用了两套不同的配置。希望帮到你。本文还有配套的精品资源点击获取