资讯详情

资讯详情

STM32+CC1101实现433MHz无线发射:硬件接线到寄存器配置实战

简介STM32连接CC1101模块的发射程序是一份经过实际电路测试的无线通信工程代码面向需要快速实现Sub-GHz无线发射功能的嵌入式开发者。程序基于STM32的SPI总线驱动CC1101覆盖寄存器初始化、数据打包、FIFO写入与发射启动等关键环节可直接用于无线传感器网络、遥控系统或物联网节点开发。压缩包共113个文件约1MB以h头文件、c源码、o目标文件及hex/axf固件为主同时包含uvproj工程配置、map映射和lst列表等辅助文件便于查看编译细节与代码结构。已有784人学习下载说明这一程序受到一定关注。相比网上常见的残缺驱动该工程完整且经过硬件验证学习者可对照实际电路理解SPI通信与CC1101配置流程也能在现有代码基础上调整频率、功率等参数适用于中高级嵌入式开发者进行二次开发与调试。 STM32 和 CC1101 这个组合做无线发射其实比很多人想象中简单但也比想象中容易踩坑。最近在调一个远程数据采集的小项目通信方案就选了 CC1101 做 433MHz 无线发射这两天把 SPI 时序、寄存器配置、发射流程这几个坎儿都趟过去了。这篇文章就把整套发射程序从头到尾拆开讲清楚包括硬件连接、工程配置、核心代码和实际调试点如果你手上正好有 STM32 和 CC1101 模块照着往下抄大概率一次跑通。这套方案适合的场景其实挺明确低速、远距离、低功耗的无线数据传输。比如温湿度采集器、遥控开关、农业大棚监控、智能家居里面的一些传感器节点都是 CC1101 的典型用途。Sub-1GHz 频段绕射能力强、穿墙效果好比 2.4G 的 nRF24L01 更适合在这种场景下干活。发射速率不需要高几百 bps 到几十 kbps 完全够用。最适合的读者就是手里有 STM32 开发板、想快速把无线发射跑起来的人不需要对射频有多深的理论基础但基本的 SPI 和寄存器操作得有点概念。1. 项目背景与整体设计思路1.1 为什么选 CC1101 而不是其他无线方案做无线方案选型的时候大部分人第一反应是 nRF24L01因为它便宜、资料多、网上代码遍地都是。但我在这个项目里没选它原因是 2.4GHz 频段在室外复杂环境里绕射能力真的不行穿一堵墙信号就掉得厉害更别说隔着几堵墙的工业现场。而 CC1101 工作在 410~464MHz 这个 Sub-1GHz 频段波长更长绕射和穿透能力明显更强空旷环境下配合好一点的天线收发距离可以跑到几百米甚至更远接收灵敏度能做到 -112dBm 左右发射功率也能通过寄存器配置从 -30dBm 到 10dBm 可调功耗控制很灵活。和 LoRa比如 SX1278相比CC1101 的极限距离确实不如 LoRa但 LoRa 模块价格更贵、配置也更复杂而且很多应用根本不需要那几公里的距离。CC1101 的成本和复杂度刚好卡在中间几十块钱就能买到不错的模块配置虽然有一堆寄存器但实际要关心的就那么几个。这个项目里传输的是温湿度和设备状态这种低速数据我用的是 38.4kbps 的 GFSK 调制兼顾了传输速率和抗干扰能力接收端用另一个 CC1101 模块配对接收验证完全没问题。1.2 整体任务拆解整个工程拆开看其实就四件事。第一件是硬件连接把 STM32 的 SPI 引脚和 CC1101 模块对应接好再把 GDO0 状态引脚接出来。第二件是软件工程搭建用 STM32CubeMX 生成 SPI 基础工程然后自己封装寄存器读写函数。第三件是参数计算根据工作频率和晶振频率算出 FREQ、MDMCFG 这些核心寄存器值。第四件是发射流程实现把数据写进 TX FIFO发 STX 命令让芯片进入发射状态再通过 GDO0 引脚确认数据包发送完成。这里多说一句很多新手一上来就急着在网上找“现成的完整程序”结果拿过来发现频率不对、晶振不对、模块不一样完全跑不通。CC1101 的程序不像普通单片机 GPIO 控制那么“拿来即用”因为它涉及射频参数晶振频率差一点、寄存器值差一个字节结果就是“看着都对了但就是没信号”。所以这篇里我会重点讲清楚参数怎么算、寄存器为什么这么配这样你换成别的频率或者别的模块也能自己调。2. 硬件连接与工程配置2.1 引脚接线CC1101 模块引脚不多真正用到的也就 7 根其中 SPI 占 4 根GDO0 状态输出必须接另外就是电源和地。我用 STM32F103 的 SPI1接线表如下CC1101 引脚STM32 引脚说明CSNPA4片选软件控制低电平有效SCKPA5SPI1 时钟MOSIPA7主发从收MISOPA6主收从发GDO0PA3状态输出用于判断发送完成VCC3.3V注意不要接 5VGNDGND共地GDO0 这根线特别提醒一下一定不要省。CC1101 在发送过程中会通过 GDO0 引脚输出状态信号比如同步字已经发出去、数据包发送完成MCU 就是靠这个引脚来判断什么时候发完、什么时候能发下一包。如果你不接就只能靠延时猜时间要么等太久了浪费效率要么等太短了数据还没发完就切走了。电平匹配也要注意。CC1101 模块基本都是 3.3V 供电STM32F103 的 IO 也是 3.3V 电平可以直接连。但如果你用的是 5V 供电的单片机比如 Arduino UNO去控制它就必须加电平转换不然长期使用很容易把芯片搞坏这不是“临时能用就行”的问题。2.2 CubeMX 工程配置要点在 STM32CubeMX 里配置 SPI1 的时候有几个点需要说清楚。模式选 Full-Duplex Master硬件 NSS 这里我建议直接 Disable片选用软件 GPIO 控制。数据宽度 8 位MSB FirstCPOL 和 CPHA 都选 Low 和 1 Edge也就是 SPI Mode 0这是 CC1101 要求的通信模式搞错了通信必然失败。时钟分频我习惯配成 32 分频在 72MHz 主频下 SPI 时钟大概是 2.8MHz。CC1101 数据手册标称支持最高 10MHz 的 SPI 时钟但实际模块走线、芯片批次都有差异频率拉太高容易出怪问题。无线发射这个场景对 SPI 速率要求真不高2.8MHz 完全够用稳定压倒一切。GPIO 方面PA3 和 PA4 都设为推挽输出PA4片选初始化为高电平PA3GDO0 输入这里有个细节我在 CubeMX 里直接把 PA3 配成了 GPIO_Input用来检测 CC1101 的状态变化。有些朋友会把 GDO0 配成外部中断这个也可以但发射流程里轮询读取就够用了没必要上中断。2.3 底层 SPI 读写实现底层 SPI 读写是整个程序的地基我直接用 HAL 库实现。这里有个小经验读写两个方向最好封装成一个函数因为 SPI 是全双工每次发送一个字节的同时必然收到一个字节而且 CC1101 的状态字节就是靠这个返回路径带回来的。uint8_t CC1101_ReadWriteByte(uint8_t data) { uint8_t rx 0; HAL_SPI_TransmitReceive(hspi1, data, rx, 1, 100); return rx; } void CC1101_CS_LOW(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); } void CC1101_CS_HIGH(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); }片选的拉低和拉高之间我建议稍微给一点延时尤其是从高到低的瞬间要确保芯片先进入片选状态再送时钟否则偶尔会出现第一个字节写不进的情况。调试的时候如果发现寄存器写进去读出来不对先怀疑片选时序这个坑我踩过不止一次。3. CC1101 核心寄存器配置与发射流程3.1 寄存器读写命令格式CC1101 的 SPI 读写和普通 SPI 器件不太一样命令字节本身就有讲究。第一个字节的高两位用来表示操作类型bit7 是读写方向1 读、0 写bit6 是突发模式标志1 表示突发读写连续访问多个字节bit5~bit0 是寄存器地址。#define CC1101_READ_SINGLE 0x80 #define CC1101_WRITE_BURST 0x40 #define CC1101_READ_BURST 0xC0我封装了三个最常用的函数写单字节寄存器、读单字节寄存器、发送命令。void CC1101_WriteReg(uint8_t addr, uint8_t value) { CC1101_CS_LOW(); CC1101_ReadWriteByte(addr); CC1101_ReadWriteByte(value); CC1101_CS_HIGH(); } uint8_t CC1101_ReadReg(uint8_t addr) { uint8_t value; CC1101_CS_LOW(); CC1101_ReadWriteByte(addr | CC1101_READ_SINGLE); value CC1101_ReadWriteByte(0xFF); CC1101_CS_HIGH(); return value; } uint8_t CC1101_Strobe(uint8_t cmd) { uint8_t status; CC1101_CS_LOW(); status CC1101_ReadWriteByte(cmd); CC1101_CS_HIGH(); return status; }Strobe 命令是 CC1101 特有的一种操作比如 SIDLE、SFTX、SFRX、STX这些命令通过单独一个字节就能让芯片切换状态机。注意 Strobe 函数返回的字节其实是芯片状态字里面包含了当前芯片状态调试的时候可以读出来看芯片是不是卡在哪个状态里了。3.2 频率和数据速率参数计算这就是很多人觉得 CC1101 难的地方其实公式很简单。频率寄存器 FREQ2、FREQ1、FREQ0 共同组成一个 24 位的值计算公式是FREQ (目标频率 x 2^16) / 晶振频率以 433.92MHz 工作频率、26MHz 晶振为例(433,920,000 x 65,536) / 26,000,000 约等于 1,093,752转成十六进制是 0x10B078所以 FREQ20x10、FREQ10xB0、FREQ00x78。数据速率配置用的是 MDMCFG4 和 MDMCFG3 两个寄存器公式是R (256 DRATE_M) x 2^DRATE_E x f_xosc / 2^28反过来推38.4kbps 对应 DRATE_M0x83、DRATE_E10也就是 MDMCFG30x83MDMCFG4 低四位0x0A。这个手算过程比较繁琐我实际开发中是先用 TI 官方的 SmartRF Studio 生成一组参考配置再对照公式把关键参数核对一遍。这个工具是免费的选好芯片型号、输入晶振频率、工作频率、调制方式和数据速率它直接给出整套寄存器值强烈建议每个人都下载一个备用。下面是这个工程里实际使用的配置数组433.92MHz、38.4kbps、GFSK、26MHz 晶振直接复制就能用const uint8_t cc1101_cfg[] { 0x0D, // IOCFG2 GDO2 输出 0x2E, // IOCFG1 高阻 0x06, // IOCFG0 GDO0 同步字检测 0x07, // FIFOTHR FIFO 阈值 0x00, // PKTCTRL1 关闭地址过滤不附加状态字节 0x32, // PKTCTRL0 可变长度包CRC 使能数据白化 0x06, // FSCTRL1 中频频率 0x10, // FREQ2 433.92MHz 配置 0xB0, // FREQ1 0x78, // FREQ0 0x0A, // MDMCFG4 38.4kbps 0x83, // MDMCFG3 0x03, // MDMCFG2 GFSK 调制 0x22, // MDMCFG1 前导码最低 2 字节 0xF8, // MDMCFG0 信道间隔 0x35, // DEVIATN 频偏 0x07, // MCSM2 0x30, // MCSM1 发送完成后回到 IDLE 0x18, // MCSM0 自动校准 0x16, // FOCCFG 0x6C, // BSCFG 0x43, // AGCCTRL2 0x40, // AGCCTRL1 0x91, // AGCCTRL0 0x87, // WOREVT 0xFB, // WORCTRL 0x56, // FREND1 0x11, // FREND0 0xE9, // FSCAL3 0x2A, // FSCAL2 0x00, // FSCAL1 0x1F, // FSCAL0 0x81, // TEST2 0x35, // TEST1 0x09, // TEST0 0xFF, // PATABLE 输出功率表 };这个数组里面大部分值直接用 SmartRF Studio 生成的我只改了几个自己关心的PKTCTRL1 改成了 0x00 关闭地址过滤这样收发双方不需要配地址PKTCTRL0 用 0x32 开启可变长度包模式MCSM1 设为 0x30让芯片发送完自动回到 IDLE。发射功率表 PATABLE 这里我填的是 0xFF对应最大发射功率实际调试的时候如果发现距离太近先不要急着找硬件问题检查一下是不是功率没配上去。3.3 初始化与发射函数实现初始化函数其实做的事情很少就是 SPI 准备好之后把上面的配置数组按顺序写入芯片寄存器。写完后发一个 SIDLE 命令稳妥进入空闲态再发一个 SFTX 命令清空发送 FIFO。void CC1101_Init(void) { CC1101_CS_HIGH(); HAL_Delay(10); CC1101_Strobe(CC1101_SIDLE); CC1101_Strobe(CC1101_SFTX); for (uint8_t i 0; i sizeof(cc1101_cfg); i) { CC1101_WriteReg(i, cc1101_cfg[i]); } // 写入 PATABLE地址 0x3E 是 PATABLE 的起始地址 CC1101_CS_LOW(); CC1101_ReadWriteByte(0x3E | CC1101_WRITE_BURST); CC1101_ReadWriteByte(0xFF); CC1101_CS_HIGH(); CC1101_Strobe(CC1101_SCAL); // 手动校准 HAL_Delay(2); }发射函数是整个程序的核心逻辑其实很直白清空 FIFO、写数据、拉高片选后发 STX 命令、等待 GDO0 指示完成。void CC1101_TX_Packet(uint8_t *data, uint8_t len) { // 1. 回到 IDLE清空 FIFO避免残留数据干扰 CC1101_Strobe(CC1101_SIDLE); CC1101_Strobe(CC1101_SFRX); CC1101_Strobe(CC1101_SFTX); // 2. 写长度字节 数据 CC1101_CS_LOW(); CC1101_ReadWriteByte(0x3F | CC1101_WRITE_BURST); // TXFIFO 突发写 CC1101_ReadWriteByte(len); for (uint8_t i 0; i len; i) { CC1101_ReadWriteByte(data[i]); } CC1101_CS_HIGH(); // 3. 进入发送状态 CC1101_Strobe(CC1101_STX p a hrefhttps://download.csdn.net/download/hxg791206/3837855 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →