简介:这份资源是面向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 = (FREQ2<<16 | FREQ1<<8 | 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 算出实际速率。比如 MDMCFG4=0x2D、MDMCFG3=0x3B,对应约 38.4kbps、GFSK、信道带宽 100kHz。这个组合在遥控和传感器场景里很常见。
我一般会把这些配置整理成一张表,方便对照:
| 寄存器 | 典型值 | 含义 | 改动影响 |
|---|---|---|---|
| MDMCFG2 | 0x13 | GFSK,无前导码质量门限 | 改低三位切调制方式 |
| MDMCFG4 | 0x2D | 信道带宽 100kHz,数据率指数 | 改高四位影响接收带宽 |
| MDMCFG3 | 0x3B | 数据率尾数 | 与 MDMCFG4 共同决定速率 |
| DEVIATN | 0x47 | 频偏约 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 模式 0(CPOL=0,CPHA=0),且时钟极性不能反。我见过有人用模式 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 的版本寄存器(0x31,VERSION)。正常值应该是 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,接收灵敏度能提升约 6dB,CRC 错误率明显下降。代价是传输时间变长,适合对实时性要求不高的场景。
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 生成的配置和实际板子上的晶振、天线匹配结合起来,先保证频点、调制、同步字、数据率这四项一致,再去调其他参数。我见过太多人卡在同步字不一致上折腾一整天,最后发现只是程序包里发送端和接收端用了两套不同的配置。希望帮到你。
本文还有配套的精品资源,点击获取