简介:这份资源面向使用51系列单片机开发无线通信的工程师与学习者,围绕TI超低功耗ISM频段收发器CC1101,提供可直接参考的驱动与示例工程,解决从寄存器配置到数据收发链路的落地问题。压缩包共39个文件、约103KB,以c与h源码、uvproj/uvopt工程文件、hex烧录文件为主,另含M51、plg等编译辅助文件,并区分IO模拟SPI与STC15内部硬件SPI两套实现,便于对比时序差异与移植。内容涵盖初始化、寄存器配置、发送与接收函数、错误处理及TX/RX演示工程,可帮助读者理解SPI通信机制、快速搭建无线传感器节点或遥控装置原型。目前已有176人学习下载,适合希望掌握CC1101驱动与短距离无线通信开发的初学者和进阶开发者参考。
1. 拿到 CC1101程序.rar 之后:一个 433MHz 无线链路的起点
很多人第一次接触 CC1101,是从一个压缩包开始的——名字往往就叫「CC1101程序.rar」。它大概率包含一份单片机工程、一份寄存器配置头文件、一份收发例程,可能还有一份原理图或引脚说明。你手上有一块 CC1101 模块、一颗主控(STM32、STC、Arduino 或 ESP32 都常见),目标很明确:让两块板子隔着几米到几十米,把几个字节稳定地发出去、收回来。这个标题背后真正要解决的问题不是「CC1101 是什么」,而是「这份程序怎么跑起来、参数怎么改、为什么我照着接完线却收不到数据」。它适合正在做无线遥控、传感器采集、低功耗节点、工业现场短距通信的嵌入式从业者,也适合刚上手 Sub-1GHz 射频的新手。CC1101 本身是一颗 Sub-1GHz 射频收发芯片,常用 433MHz、868MHz、915MHz 频段,SPI 接口,寄存器多、配置敏感,程序能不能跑通,八成取决于配置和时序,而不是代码写得多漂亮。
2. 先看懂 CC1101 程序里的四层结构:从 SPI 到数据包
一份能用的 CC1101 程序,不管主控是什么,结构基本都逃不出四层:SPI 读写层、寄存器配置层、状态机与中断层、数据包收发层。很多人拿到压缩包直接编译烧录,结果串口打印「初始化失败」,就是因为没先看清这四层各自负责什么。
2.1 SPI 读写层:所有玄学的源头
CC1101 通过 4 线 SPI 通信:CSN、SCLK、SI、SO。注意它还有两个引脚容易忽略:GDO0/GDO2 用于中断和状态输出,以及一个可选的 MISO 方向问题。SPI 模式是 Mode 0(CPOL=0,CPHA=0),时钟极性搞反是最常见的翻车点。
// CC1101 SPI 单字节读写,Mode 0 uint8_t cc1101_spi_rw(uint8_t tx) { uint8_t rx = 0; for (int i = 0; i < 8; i++) { // 先拉低时钟,准备数据 SCLK_LOW(); if (tx & 0x80) SI_HIGH(); else SI_LOW(); tx <<= 1; delay_us(1); // 速率别超过 10MHz,保守用 1us SCLK_HIGH(); // 上升沿采样 rx <<= 1; if (SO_READ()) rx |= 0x01; delay_us(1); } SCLK_LOW(); return rx; }逻辑说明:CC1101 在 SCLK 上升沿采样 SI,下降沿输出 SO,所以先摆数据再拉高。参数说明:delay_us(1)对应约 500kHz,实际 CC1101 支持到 10MHz,但长排线、杜邦线场景下建议降到 1MHz 以下,否则读回的状态字节会随机错。读寄存器时要先发地址字节(带 0x80 读标志),再发一个空字节拿数据;写寄存器则是地址字节加数据字节。
2.2 寄存器配置层:一份配置决定生死
CC1101 有 47 个配置寄存器、若干状态寄存器和 FIFO。压缩包里的cc1101_config.h或类似文件,通常是一长串{地址, 值}数组。核心参数只有几个:频段、调制方式、数据率、频偏、接收带宽、同步字、包长模式。
| 参数 | 典型值(433MHz) | 作用 | 改错的后果 |
|---|---|---|---|
| FREQ2/1/0 | 0x10/0xB0/0x71 | 载波频率 | 收发频率不一致,完全收不到 |
| MDMCFG4 | 0xF6 | 接收带宽、数据率指数 | 带宽太窄,频偏容不下 |
| MDMCFG3 | 0x83 | 数据率尾数 | 速率对不上,误码率高 |
| DEVIATN | 0x15 | 频偏 | 2-FSK 解调失败 |
| PKTCTRL0 | 0x05 | 包长模式、CRC | 变长包读不出长度 |
| SYNC1/0 | 0xD3/0x91 | 同步字 | 收不到同步头,永远进不了 RX |
常见做法是先用现成配置(比如 433MHz、2-FSK、38.4kbps、20kHz 频偏),跑通后再用工具微调。我一般会先把PKTCTRL0设成固定长度、关 CRC,排除变量,确认链路通了再开 CRC 和变长包。
2.3 状态机与中断层:别用延时等收发
CC1101 内部有 IDLE、RX、TX、FSTXON 等状态。正确做法是发命令选通(SIDLE、STX、SRX),然后靠 GDO0 中断或轮询MARCSTATE状态寄存器判断完成。用delay_ms(100)硬等是最坑的写法,温度、供电一变就失效。
// 进入接收,等待 GDO0 拉高表示收到同步字 void cc1101_rx_mode(void) { cc1101_strobe(SIDLE); cc1101_strobe(SFRX); // 清 RX FIFO cc1101_strobe(SRX); // 进入接收 // GDO0 配置为 0x06:收到同步字拉高,包结束拉低 }参数说明:SFRX清 FIFO 必须在SRX之前,否则残留数据会污染下一包。GDO0 的 IOCFG0 寄存器决定它输出什么信号,0x06 是「同步字到达」,0x01 是「RX FIFO 有数据」,按你的中断策略选。
2.4 数据包收发层:FIFO 读写的顺序不能乱
发送时先SIDLE,写 TX FIFO,再STX;接收时等 GDO0 或查RXBYTES,再读 FIFO。FIFO 是 64 字节,超过要分包。读 FIFO 用0xBF突发读地址,一次把整包拉出来。
// 发送一包,len 不超过 61(留同步和 CRC 空间) void cc1101_send(uint8_t *buf, uint8_t len) { cc1101_strobe(SIDLE); cc1101_strobe(SFTX); // 清 TX FIFO cc1101_write_burst(0x3F, buf, len); // 0x3F 是 TX FIFO 突发写 cc1101_strobe(STX); // 等 GDO0 或 MARCSTATE 回到 IDLE }逻辑说明:SFTX清空发送缓冲,0x3F是 TX FIFO 的突发访问地址,STX触发发送。参数说明:如果开了变长包模式,第一个字节要写长度;固定长度模式则直接写数据。发送完成后一定要回 IDLE 或 RX,否则芯片停在 TX 状态,下次发不出去。
3. 把压缩包跑起来:最小可复现步骤与参数核对
拿到「CC1101程序.rar」后,别急着改代码。先按下面顺序走一遍,能排除八成问题。
3.1 解压后先确认三件事
第一,看工程里主控型号和你的板子是否一致,STM32F103 和 STM32F407 的 SPI 初始化不同。第二,看引脚定义文件,CSN、SCLK、SI、SO、GDO0 分别接在哪,和你的接线核对。第三,看晶振配置,CC1101 外部晶振通常是 26MHz,如果程序里按 27MHz 算频率,实际频点会偏。
// 典型引脚宏定义,按实际接线改 #define CC1101_CSN_PORT GPIOA #define CC1101_CSN_PIN GPIO_PIN_4 #define CC1101_SCLK_PORT GPIOA #define CC1101_SCLK_PIN GPIO_PIN_5 #define CC1101_SI_PORT GPIOA #define CC1101_SI_PIN GPIO_PIN_7 #define CC1101_SO_PORT GPIOA #define CC1101_SO_PIN GPIO_PIN_6 #define CC1101_GDO0_PORT GPIOB #define CC1101_GDO0_PIN GPIO_PIN_0参数说明:SPI 的 MISO/MOSI 别接反,SI 是主控输出、CC1101 输入,SO 相反。GDO0 建议接带外部中断的引脚,方便做收发完成判断。
3.2 读版本寄存器:第一行调试代码
CC1101 有一个版本寄存器VERSION(地址 0x31 加读标志),正常读回 0x14 或 0x04 等固定值。如果读回 0x00 或 0xFF,说明 SPI 根本没通。
uint8_t ver = cc1101_read_reg(0x31 | 0x80); printf("CC1101 VERSION = 0x%02X\n", ver);逻辑说明:这是上电后第一条该跑的代码。参数说明:读回 0x00 查 CSN 和 SCLK;读回 0xFF 查 SO 是否被拉高、供电是否 3.3V。注意 CC1101 是 3.3V 器件,5V 会烧。
3.3 用固定包长打通第一包
先别开 CRC、别开变长包。配置成固定 4 字节、无 CRC、2-FSK、433MHz,发0x01 0x02 0x03 0x04,接收端轮询RXBYTES。
// 轮询接收 uint8_t rxbytes = cc1101_read_status(0x3B); // RXBYTES if (rxbytes > 0) { uint8_t buf[4]; cc1101_read_burst(0xBF, buf, 4); // 0xBF 是 RX FIFO 突发读 // 处理 buf }逻辑说明:0x3B是 RXBYTES 状态寄存器,0xBF是 RX FIFO 突发读。参数说明:读之前确认MARCSTATE在 RX 状态,读完如果还有残留要SFRX。这一步通了,再逐步开 CRC、变长包、地址过滤。
3.4 用频谱或第二块板验证频点
如果收发都对但距离极短,用另一块已知正常的 CC1101 板子对发,或者用频谱仪看 433MHz 附近有没有载波。常见问题是频点算错,比如把 433.92MHz 配成 433.00MHz,接收端带宽又窄,直接对不上。
4. 避坑与排查:CC1101 程序最常见的五个翻车现场
4.1 现象:VERSION 读回 0x00 或 0xFF
原因:SPI 时序不对、CSN 没拉低、供电不足、SO 被其他外设占用。解决:先用示波器看 SCLK 和 SI 有没有波形;确认 CSN 在传输期间保持低;量 VCC 是否稳定 3.3V;检查 SO 是否被配置成推挽输出而不是输入。
4.2 现象:能发不能收,或收发距离只有几厘米
原因:天线没接、天线阻抗不对、频偏或接收带宽配置不匹配。解决:433MHz 用 17cm 左右导线做天线;检查DEVIATN和MDMCFG4的带宽是否覆盖频偏;把数据率降到 9.6kbps 试,速率越高越难调。
4.3 现象:偶尔收到一包,之后再也收不到
原因:FIFO 没清、状态机卡在 TX、GDO0 中断没清标志。解决:每次收发前SIDLE+SFRX/SFTX;中断里读RXBYTES后必须读 FIFO 清空;确认MARCSTATE回到 IDLE 再进下一步。
4.4 现象:CRC 一直错,数据看起来对但校验不过
原因:同步字配错、包长模式不一致、CRC 初值或多项式理解偏差。解决:收发两端SYNC1/SYNC0必须完全一致;固定包长和变长包不能混用;CC1101 的 CRC 是硬件自动算的,PKTCTRL0里开 CRC 使能即可,别自己再算一遍。
4.5 现象:低功耗模式下电流降不下来
原因:没进 SLEEP 或 WOR 配置不对、GDO 引脚漏电、SPI 引脚悬空。解决:不用时发SIDLE再SPWD;WOR 模式要配MCSM2和WOREVT;所有 SPI 引脚在休眠前设成模拟输入或低电平,避免倒灌。
5. 进阶:把 CC1101 程序改成能落地的低功耗采集节点
跑通收发只是开始。真正要投入项目,得解决三件事:功耗、抗干扰、多节点组网。我一般会先把 CC1101 配成 WOR(Wake-On-Radio)模式,让节点大部分时间睡觉,定时醒来听前导码。
// WOR 模式关键配置 cc1101_write_reg(0x18, 0x18); // MCSM2:RX 超时后回 IDLE cc1101_write_reg(0x19, 0x00); // MCSM1:RX 后进 IDLE cc1101_write_reg(0x1A, 0x00); // MCSM0:自动校准 cc1101_write_reg(0x1B, 0x00); // FOCCFG cc1101_write_reg(0x20, 0xFB); // WORCTRL:事件 0 约 1 秒 cc1101_write_reg(0x21, 0x00); // WOREVT1 cc1101_write_reg(0x22, 0x00); // WOREVT0 cc1101_strobe(0x38); // SWOR 进入 WOR参数说明:WOREVT决定唤醒周期,配合WORCTRL的事件 0 可以做到几百毫秒到几秒。发送端要在数据前加足够长的前导码,让接收端醒来能同步上。前导码长度用MDMCFG1的低两位设,建议至少 4 字节。
抗干扰方面,433MHz 是公共频段,干扰源多。我习惯把数据率降到 4.8kbps 以下,接收带宽收窄到 100kHz 以内,同步字用 4 字节而不是 2 字节,能明显降低误触发。多节点组网时,用地址过滤(ADDR寄存器 +PKTCTRL1的地址检查使能),每个节点一个地址,广播用 0x00 或 0xFF。
验证方法很简单:拿两块板子,一块发、一块收,中间隔几堵墙,看丢包率。如果丢包超过 10%,先查天线和供电,再查频偏。我自己的血泪经验是,CC1101 的坑九成在硬件和配置,代码逻辑本身很少出问题。每次改完配置,先读回所有关键寄存器确认写进去了,再跑收发。这个习惯帮我省了无数个抓头的夜晚。希望帮到你。
本文还有配套的精品资源,点击获取