
简介这份源码面向嵌入式无线通信开发者与STM32学习者提供基于SI4463射频收发芯片与STM32F103微控制器的完整软件实现可用于搭建点对点无线数据传输系统并具备向其他单片机平台移植的参考价值。压缩包共252个文件约3.46MB以c与h源码文件为主辅以o、crf、d等编译中间文件以及uvproj、uvopt工程配置、hex烧录文件、map与lst映射清单和sct分散加载脚本覆盖从源码到可执行固件的完整工程结构。资源围绕发送与接收两套示例展开涉及芯片初始化配置、数据包编解码、错误检测与校正、射频调制解调等关键环节并提示了外设接口适配、中断处理、时序时钟与功耗管理等移植要点。目前已有182人学习下载适合希望快速理解SI4463驱动流程、对照工程结构排查通信问题或进行跨平台移植的读者参考。1. 拿到 SI4463 和 STM32 之后先搞清楚这套组合到底在解决什么问题如果你手头有一块 SI4463 模块和一块 STM32 开发板想跑通点对点无线通信大概率会经历这样一个过程翻遍资料找到一份“能编译”的源码烧进去串口没输出或者输出了但收不到数据。SI4463 这颗 Sub-1GHz 射频收发芯片本身不算新但它的配置寄存器多达几百个API 文档厚得让人头皮发麻。STM32 源码的价值就在于把射频初始化、收发中断、FIFO 管理这些底层细节封装好让你专注在业务逻辑上。这套组合适合谁做无线抄表、工业遥控、传感器组网、安防报警这类低速率、远距离、低功耗场景的嵌入式工程师。它不适合跑视频或大数据量传输SI4463 的空中速率最高也就 500kbps 左右实际用 10kbps 到 100kbps 居多。源码的核心价值是给你一个可复现的起点而不是一个拿来就量产的黑匣子。2. SI4463 与 STM32 的硬件连接和 SPI 通信配置2.1 先确认你的模块引脚定义和供电范围市面上常见的 SI4463 模块引脚排列不完全一致但核心信号线就那么几根。我一般拿到模块第一件事是拿万用表蜂鸣档把每个引脚和芯片焊盘对一遍别信丝印。典型连接如下SI4463 引脚STM32 引脚说明VDD3.3V供电 1.8V~3.6V典型 3.3VGNDGND共地SDISPI_MOSI主机输出从机输入SDOSPI_MISO主机输入从机输出SCLKSPI_SCK时钟最高 10MHzCSnGPIO 输出片选低有效nIRQGPIO 外部中断中断输出低有效SDNGPIO 输出关断控制高有效GPIO0/1可悬空或接 LED可配置为收发状态指示供电这里有个血泪经验SI4463 对电源纹波比较敏感尤其是发射瞬间电流能冲到 80mA 以上。如果你用 STM32 板子上的 LDO 直接供电最好在模块 VDD 脚旁边并一个 10uF 钽电容加一个 0.1uF 陶瓷电容否则容易出现发射时复位或接收灵敏度下降的玄学问题。2.2 SPI 初始化的关键参数和代码STM32 的 SPI 配置不算复杂但 SI4463 对时序有要求。我一般用 SPI1 或 SPI2模式 0CPOL0CPHA08 位数据MSB 先出时钟分频到 4MHz 左右先跑通稳定后再往上提。// spi_config.c // STM32F1 标准库风格HAL 库同理改一下句柄即可 #include stm32f10x.h void SI4463_SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; // 使能 SPI1 和 GPIOA 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA, ENABLE); // PA5SCK, PA6MISO, PA7MOSI 复用推挽 GPIO_InitStructure.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // MISO 浮空输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode SPI_Mode_Master; SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; // 空闲低 SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; // 第一个边沿采样 SPI_InitStructure.SPI_NSS SPI_NSS_Soft; // 软件片选 SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_8; // 72M/89M先降速 SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial 7; SPI_Init(SPI1, SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); }这段代码里SPI_BaudRatePrescaler_8对应 9MHz如果你发现读写寄存器返回值不对先降到SPI_BaudRatePrescaler_32甚至64试。SI4463 的 SPI 时序要求 CSn 拉低到第一个 SCLK 上升沿之间至少 20nsSTM32 硬件 SPI 一般能满足但如果你用软件模拟 SPI记得加__NOP()延时。片选和 SDN 引脚我习惯用 PA4 和 PA3初始化成推挽输出默认 CSn 拉高、SDN 拉低。SDN 拉高会让芯片进入关断模式电流降到微安级但所有寄存器配置丢失重新上电后必须重新初始化。2.3 上电时序和复位流程SI4463 上电不是给电就能干活。正确顺序是VDD 稳定 → SDN 拉低 → 等待至少 10ms → 拉低 CSn → 发送上电命令。我见过有人 SDN 悬空结果芯片状态随机有时候能跑有时候不能跑查了半天以为是代码问题。// si4463_power.c void SI4463_PowerUp(void) { SI4463_SDN_LOW(); // 退出关断 delay_ms(20); // 等内部上电复位完成 SI4463_CS_LOW(); // 片选拉低 // 发送 POWER_UP 命令具体命令字和参数见 API 文档 // 这里只示意流程实际参数需要根据晶振频率和 API 版本填 SI4463_WriteCmd(POWER_UP_CMD, power_up_args, 4); SI4463_CS_HIGH(); delay_ms(10); }delay_ms(20)这个值不是随便写的SI4463 数据手册里写的是从上电到可以接受命令需要等内部 30MHz 晶振起振稳定典型值 15ms 左右我留 20ms 余量。如果你用外部 TCXO这个时间可以缩短但没必要省这点时间。3. 射频参数配置从寄存器到空中速率3.1 用 WDS 生成配置头文件别手撸寄存器SI4463 有几百个寄存器手撸不现实。常见做法是用厂家提供的 Wireless Development SuiteWDS工具选好频段、调制方式、空中速率、频偏、滤波带宽生成一个radio_config.h头文件。这个文件里是一长串{0xXX, 0xYY}的数组你的源码只需要按顺序写进去就行。我一般会生成两组配置一组 433MHz、10kbps、GFSK、20kHz 频偏用于远距离低速另一组 470MHz、100kbps、GFSK、50kHz 频偏用于近距离高速。两组配置的寄存器数组分别存成两个头文件切换时重新加载。// radio_config_433m_10kbps.h 片段示意 // 实际数组由 WDS 生成这里只展示结构 #define RADIO_CONFIG_DATA_SIZE 128 const uint8_t RadioConfigData[RADIO_CONFIG_DATA_SIZE] { 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // ... 后续上百个字节省略 };加载配置的代码逻辑是拉低 CSn → 发送SET_PROPERTY命令 → 发送属性组 ID 和起始索引 → 发送配置数组 → 拉高 CSn。注意数组长度不能超过 SPI 单次传输缓冲STM32 的 SPI 发送函数如果一次发 128 字节最好用 DMA 或者分块发否则容易丢字节。3.2 空中速率、频偏和接收带宽的匹配关系这三个参数是绑死的不能随便改。空中速率决定码元宽度频偏决定频率偏移量接收带宽必须大于2 × 频偏 空中速率。举个例子10kbps 速率、20kHz 频偏接收带宽至少 50kHz实际取 60kHz 到 80kHz 比较稳。如果你把接收带宽设成 30kHz灵敏度会急剧下降甚至完全收不到。空中速率推荐频偏最小接收带宽实测灵敏度433MHz1.2kbps5kHz15kHz-120dBm 左右10kbps20kHz50kHz-110dBm 左右50kbps25kHz100kHz-100dBm 左右100kbps50kHz200kHz-95dBm 左右这个表是我自己在实验室用信号源和频谱仪粗略测的不同模块的匹配网络和晶振精度会影响结果但量级不会差太多。如果你发现灵敏度比表里低 10dB 以上先查天线和匹配再查电源纹波。3.3 发射功率和 GPIO 配置SI4463 的发射功率通过PA_PWR_LVL属性设置范围 0 到 127对应约 -20dBm 到 20dBm。但注意20dBm 不是所有模块都能跑很多模块的 PA 供电和散热只支持到 13dBm 或 17dBm。我一般默认设 10dBm 先跑通稳定后再往上加每加 3dB 用频谱仪看一眼谐波。GPIO0 和 GPIO1 可以配置成多种功能我习惯把 GPIO0 配成“发射状态指示”GPIO1 配成“接收状态指示”接两个 LED调试时一眼就能看出芯片在干什么。配置方法是通过GPIO_PIN_CFG属性具体值查 API 文档里的 GPIO 功能映射表。4. 收发流程实现中断、FIFO 和状态机4.1 中断驱动还是轮询我选中断加状态机SI4463 的 nIRQ 引脚在事件发生时会拉低STM32 配置成下降沿触发外部中断。中断服务函数里不要做太多事只置一个标志位主循环里根据标志位和当前状态机状态决定下一步。轮询也不是不行但会浪费 CPU 且响应慢尤其在收发切换频繁的场景。状态机我一般设这几个状态IDLE、TX、RX、TX_DONE、RX_DONE、ERROR。上电初始化后进IDLE调用发送函数进TX发送完成中断进TX_DONE然后自动切到RX等待回复。接收中断进RX_DONE处理完数据再切回RX或IDLE。// si4463_fsm.c typedef enum { STATE_IDLE, STATE_TX, STATE_RX, STATE_TX_DONE, STATE_RX_DONE, STATE_ERROR } Si4463State_t; volatile Si4463State_t g_state STATE_IDLE; volatile uint8_t g_irq_flag 0; // 外部中断服务函数只置标志 void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) ! RESET) { g_irq_flag 1; EXTI_ClearITPendingBit(EXTI_Line0); } } // 主循环里的状态机处理 void Si4463_Process(void) { if (!g_irq_flag) return; g_irq_flag 0; uint8_t irq_status[8]; SI4463_GetIrqStatus(irq_status); // 读中断状态寄存器 switch (g_state) { case STATE_TX: if (irq_status[0] 0x20) { // 发送完成标志位具体位查文档 g_state STATE_TX_DONE; } break; case STATE_RX: if (irq_status[0] 0x04) { // 接收完成标志位 g_state STATE_RX_DONE; } break; default: break; } }中断状态寄存器的位定义每个型号略有差异一定要对着你用的 API 文档确认。我见过有人直接抄别人的位定义结果收发标志搞反了发送完成当成接收完成查了一整天。4.2 FIFO 读写和包处理SI4463 的 TX FIFO 和 RX FIFO 各 64 字节。发送时先把数据写进 TX FIFO再发START_TX命令。接收时收到RX_FIFO_ALMOST_FULL或RX_DONE中断后用READ_RX_FIFO命令把数据读出来。包处理有两种模式固定包长和可变包长。固定包长适合数据长度固定的场景配置简单。可变包长需要在包头发一个长度字节接收端先读长度再读数据。我一般用可变包长灵活一些。// 发送一个可变包长数据 void SI4463_SendPacket(uint8_t *data, uint8_t len) { uint8_t tx_buf[70]; tx_buf[0] len; // 第一个字节是长度 memcpy(tx_buf[1], data, len); SI4463_CS_LOW(); SI4463_WriteCmd(WRITE_TX_FIFO_CMD, tx_buf, len 1); SI4463_CS_HIGH(); SI4463_CS_LOW(); SI4463_WriteCmd(START_TX_CMD, NULL, 0); SI4463_CS_HIGH(); g_state STATE_TX; }注意WRITE_TX_FIFO命令之后必须拉高 CSn 再拉低发START_TX中间不能连续发否则芯片不认。这个坑我踩过连续发两个命令结果芯片只执行了第一个。4.3 收发切换的延时和状态清理从 TX 切到 RX或者从 RX 切到 TX不能直接发命令必须先发CHANGE_STATE到READY或IDLE等状态稳定后再切。我一般加 1ms 到 2ms 的延时或者读GET_DEVICE_STATE命令确认当前状态。另外每次收发切换前最好清一下 FIFO 和中断标志否则残留数据会导致下一包出错。FIFO_RESET命令可以同时清 TX 和 RX FIFO但注意清 FIFO 会丢失未发送完的数据所以只在确认空闲时用。5. 避坑与常见问题排查5.1 现象SPI 读寄存器全返回 0x00 或 0xFF原因SPI 模式不对、片选时序不对、或者 MISO 引脚没配置成输入。还有一种可能是 SDN 引脚状态不对芯片处于关断模式。解决先用示波器看 SCLK、MOSI、CSn 三根线确认波形正常。然后读一个已知的只读寄存器比如PART_INFO正常应该返回芯片型号和版本号。如果还是不对把 SPI 时钟降到 1MHz 以下试排除时序问题。5.2 现象能发送但接收端收不到或者接收灵敏度极低原因收发双方射频参数不匹配最常见的是频偏、接收带宽、同步字不一致。也有可能是天线匹配没做好或者电源纹波导致接收前端饱和。解决先用频谱仪看发射频谱确认中心频率和频偏。然后检查双方的radio_config.h是不是同一组参数生成的。同步字必须完全一致四个字节的同步字错一个字节就收不到。天线用矢量网络分析仪看回波损耗433MHz 下 S11 小于 -10dB 才算及格。5.3 现象通信距离短隔一堵墙就断原因发射功率不够、天线增益低、接收带宽太宽导致灵敏度下降、或者环境噪声太大。解决先把发射功率加到模块允许的最大值看距离有没有改善。然后收窄接收带宽比如从 100kHz 降到 50kHz灵敏度能提升 3dB 到 6dB。天线换成 1/4 波长鞭状天线比 PCB 板载天线增益高 2dB 到 5dB。如果还不行考虑降低空中速率1.2kbps 的灵敏度比 100kbps 高 20dB 以上。5.4 现象长时间运行后死机或通信中断原因中断标志没清干净导致中断风暴或者 FIFO 溢出没处理或者看门狗没喂。解决在中断服务函数里只清当前中断标志不要清所有标志。FIFO 溢出中断要处理收到后清 FIFO 并重新进接收状态。看门狗定时器别忘了喂但喂狗前确认状态机没有卡死。我一般加一个软件超时计数器如果TX或RX状态超过 100ms 没变化强制复位射频芯片重新初始化。5.5 现象发射时 STM32 复位或程序跑飞原因电源电流不够发射瞬间电压跌落导致 STM32 欠压复位。或者射频辐射耦合到 SPI 线上导致误触发。解决给 SI4463 单独供电或者加大 LDO 输出电容到 100uF 以上。SPI 线尽量短必要时加 22 欧姆串联电阻和 10pF 对地电容做滤波。STM32 的复位引脚加 0.1uF 电容电源引脚加磁珠隔离。6. 进阶技巧用 GPIO 快速切换收发模式和低功耗设计6.1 用 GPIO0/GPIO1 做硬件收发切换前面提到 GPIO0 和 GPIO1 可以配置成状态指示其实它们还能配置成“TX/RX 状态输出”直接驱动射频开关。如果你的模块带外部 PA 和 LNA需要收发切换用 GPIO 控制比软件命令快得多切换时间从几百微秒降到几微秒。配置方法是在GPIO_PIN_CFG属性里把 GPIO0 设成TX_STATEGPIO1 设成RX_STATE。这样发射时 GPIO0 输出高接收时 GPIO1 输出高直接接射频开关的控制脚。注意 GPIO 驱动能力有限一般 10mA 左右驱动射频开关够了驱动继电器不够。6.2 低功耗设计休眠、唤醒和占空比接收电池供电的场景功耗是命门。SI4463 支持多种低功耗模式STANDBY、SLEEP、SENSOR。STANDBY电流约 1mASLEEP约 40uASENSOR模式可以周期性唤醒接收前导码电流取决于占空比。我一般用SENSOR模式做接收配置成每 100ms 唤醒一次每次监听 10ms占空比 10%平均电流约 500uA。发射时切到TX状态发完立刻回SENSOR。STM32 这边配合进STOP模式用 RTC 定时唤醒整体平均电流能压到 1mA 以下。// 配置 SENSOR 模式接收 void SI4463_EnterSensorMode(void) { // 设置 SENSOR 模式的前导码长度和监听周期 // 具体属性值查 API 文档这里只示意流程 uint8_t sensor_args[4] {0x00, 0x00, 0x00, 0x00}; SI4463_CS_LOW(); SI4463_WriteCmd(SET_PROPERTY_CMD, sensor_args, 4); SI4463_CS_HIGH(); SI4463_CS_LOW(); SI4463_WriteCmd(START_RX_CMD, NULL, 0); SI4463_CS_HIGH(); }SENSOR模式的参数需要根据你的前导码长度和唤醒周期算WDS 工具里可以直接生成。注意SENSOR模式下接收窗口太短会漏包太长功耗上去了需要根据实际场景权衡。6.3 用 RSSI 和误码率做链路质量评估SI4463 可以读 RSSI接收信号强度指示和误码率。RSSI 值在接收完成后读GET_MODEM_STATUS命令获取单位是 dBm但需要根据数据手册的公式换算。我一般把 RSSI 和误码率一起用RSSI 低于 -100dBm 且误码率高于 1% 就认为链路不可靠触发重传或降低速率。实测中RSSI 在 -80dBm 以上时误码率通常低于 0.1%-100dBm 左右误码率升到 1% 到 5%-110dBm 以下基本不可用。这个数据随环境变化很大建议在自己的场景里实测一遍建一个 RSSI 和误码率的对应表。6.4 一个我常用的调试习惯每次改完射频参数先别急着跑业务逻辑用两个模块对发固定数据包连续跑 24 小时统计丢包率和误码率。丢包率低于 0.1% 才算稳定。这个习惯帮我省了很多后期排查的时间因为很多射频问题在短时间测试里看不出来跑几个小时才暴露。希望帮到你。本文还有配套的精品资源点击获取