ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

S2-LP无线收发器FIFO机制详解:从硬件结构到收发调试

S2-LP无线收发器FIFO机制详解:从硬件结构到收发调试 前阵子在做一套433MHz的无线传感器网关方案里选的是ST的S2-LP。调试过程中数据包经常出现“发了对方没收到”“收着收着就断流”的情况最后定位来定位去根子全在FIFO机制上。LAT1224这个编号看起来是ST应用笔记里的一个条目我这里不纠结它具体对应官方哪一篇只围绕S2-LP的FIFO机制把硬件结构、收发流程、寄存器操作和排查经验完整梳理一遍。如果你是刚开始用S2-LP或者以前用过CC1101/SX127x想快速迁移这篇应该能帮你在少踩几个坑的前提下把FIFO用明白。1. FIFO机制的整体设计思路为什么收发器非要加一个64字节的缓冲区1.1 没有FIFO的射频链路有多难写射频收发这件事物理层是连续比特流和MCU的“想发就发、想停就停”完全是两种节奏。以433MHz、2-FSK、9.6kbps为例一个字节在空中的时间大约是833微秒看起来不短但MCU在做协议栈任务、响应定时器中断、处理低功耗唤醒状态切换时调度延时的抖动很容易超过这个数值。一旦数据率提高到100kbps以上一个字节只剩80微秒主控稍微晚到一会儿发射链路就断流了接收端跟着丢同步。FIFO在这里就是给两侧解耦的“异步缓冲区”。发射时先把准备发的一整段数据一次性塞进FIFO然后语音芯片按自己的时钟把数据一位一位调制出去接收时解调器把收到的有效数据先存进FIFOMCU抽空来取。打个比方就像产线上游零件到了先放暂存区下游工人按自己的节奏从暂存区取料就算上游偶尔迟到几分钟产线也不会因为缺料直接停摆。1.2 独立的双通道64字节为什么是这个容量S2-LP内部给发送和接收各分配了64字节FIFO发送通道和接收通道完全独立互不占用。MCU没法像读普通RAM那样直接寻址这些缓冲只能通过SPI命令向TX FIFO写入、从RX FIFO读出。FIFO的名字已经说明了顺序先写入的字节最先被发送接收时先落地的字节最先出现在读出口这天然匹配无线报文串行传输的特性。64字节这个容量不是随便定的。主流Sub-1G应用——智能抄表、无线门锁、温湿度传感器单包载荷大多落在40字节以内64字节给了协议开销和二次开发的空间做太大硅片面积和静态功耗都会上去这和S2-LP主打超低功耗的定位是冲突的。做太小比如只有16字节那MCU就得高频次搬数据低功耗优势就全没了。1.3 硬件“包处理引擎”和FIFO的分工边界S2-LP内置了一个数据包处理引擎很多人把FIFO和它混为一谈其实分工很明确。开启数据包模式后前导码、同步字、CRC这些物理层活儿全部由硬件自动生成和校验完全不占用FIFO空间FIFO里放的只是有效载荷可能还包含地址字段、长度字段取决于你配置。这样MCU只管准备“货物”打包和拆包的活儿芯片自己干了。数据包组成部分由谁处理会不会进FIFO前导码硬件自动生成/检测不进同步字硬件自动生成/锁定不进地址字段可选软硬件配合可进取决于配置长度字段可选软硬件配合可进取决于配置有效载荷软件写入/读取进CRC校验硬件自动生成/校验不进还有一种直接模式数据包处理引擎被绕过FIFO相当于旁路数据直接从GPIO口进出所有协议工作完全由外部MCU承担。这种模式适合特殊码率或私有协议但绝大多数项目用数据包模式就足够了。我自己的习惯是拿到新板子第一件事先确认芯片工作在哪种模式否则后面读FIFO时多出来的字节、少掉的字节会让你怀疑人生。2. 动手之前与FIFO相关的命令、寄存器和中断2.1 SPI读写FIFO的三条核心路径S2-LP的FIFO操作归纳起来就三条路径配置寄存器、写TX FIFO、读RX FIFO。配置寄存器这条路径用来设置包长、FIFO阈值、CRC使能、长度模式等写TX FIFO用来把待发送的数据装进去读RX FIFO用来把接收到的数据取出来。这三个操作全部通过SPI命令完成。值得提醒的是CSN拉低期间整条命令必须连续传输中间不要随意把CSN拉高再拉低。如果SPI事务被打断S2-LP会把当前的写FIFO操作当作异常结束内部FIFO指针可能错位后面继续写就会出现数据错乱。我在调试时用逻辑分析仪抓SPI波形发现很多“灵异问题”其实是主控在写长包时被高优先级中断打断CSN时序出现了一个微小毛刺导致的。// 伪代码往TX FIFO写一帧数据 csn_low(); spi_byte(CMD_WRITE_TX_FIFO); // 命令码在驱动库中定义以S2-LP手册为准 for (int i 0; i len; i) { spi_byte(payload[i]); } csn_high();2.2 用状态寄存器观察FIFO的“水位”FIFO使用过程中最该养成的习惯是随时看状态。S2-LP通过TX_FIFO_STATUS和RX_FIFO_STATUS这类寄存器上报当前FIFO里有多少字节、处于空还是满的状态。具体地址和位宽不同驱动库可能有所差异但字段含义是通用的。寄存器/字段说明实际使用场景TX FIFO当前字节数还没被调制器消费掉的字节数发送前确认FIFO空循环发送时判断是否还有残留RX FIFO当前字节数已经从解调器写入、等待MCU读走的字节数读取前判断包是否完整读取后确认是否已清零FIFO空标志FIFO里没有数据发送侧判断是否可以写新包FIFO满标志FIFO没有多余空间接收侧报警提醒马上读走我在写业务逻辑时会把“读FIFO状态”封装成一个函数几乎所有分支都会用到。发送前读一次确认上一包已经发完接收中断里读一次确认要取多少字节异常恢复时再读一次确认缓冲已经清干净。别看这一步简单它能省掉后面一大半调试时间。2.3 阈值中断让MCU在正确时机搬数据S2-LP支持FIFO阈值中断这是长包收发和低功耗设计的关键。发送侧一般配置成“FIFO里剩余字节数低于某个值”时触发中断提醒软件赶紧补数据防止发射中间FIFO空掉接收侧配置成“FIFO里已存入字节数超过某个值”时触发中断提醒软件赶紧取数据防止溢出。常用中断标志包括TX_FIFO_EMPTY、TX_FIFO_UNDERRUN、RX_FIFO_FULL、RX_FIFO_OVERRUN以及代表“收到了一个完整数据包”的RX_DATA_READY类标志。FIFO溢出是异步突发事件强烈建议用中断而不是主循环轮询。轮询模式在MCU负载低的时候看着没问题一旦主控被更紧急的中断占住几百微秒接收侧FIFO很可能就溢出了。中断里读到标志后还要记得清标志否则会反复触发这个坑后面细说。3. 发送链路实操把数据装进TX FIFO的正确姿势3.1 发射前先定好包长、CRC和长度模式很多新手一上手就往TX FIFO里写数据写完直接发STX命令结果芯片傻傻地不知道发几个字节。问题出在没有先把包长寄存器配置好。S2-LP的发送长度有两个来源固定长度模式下包长寄存器直接决定发射字节数动态长度模式下FIFO的第一个字节就是长度字节硬件先读它再按它来决定后续发射长度。我以一套实际参数为例434MHz、2-GFSK、9.6kbps、固定长度模式、载荷11字节。配置顺序是先把包长寄存器写成11再把CRC使能打开然后把11个载荷字节写进TX FIFO最后发STX。如果开的是动态长度模式那么FIFO第一个字节必须填11接着才是11个载荷字节包长寄存器反而不用设置但发送端和接收端必须约定一致。长度模式FIFO内容包长寄存器固定长度纯载荷必须设置直接决定发射长度动态长度长度字节 载荷可不设置硬件读FIFO首字节决定这里最容易犯的错是固定长度模式下包长寄存器和实际写入FIFO的字节数不一致。写入多了发送环节可能把上一包残留数据也发出去写入少了后半个包直接丢失。我的习惯是在驱动层封一个函数内部先写包长寄存器再写FIFO从代码结构上保证两者不分离。3.2 写TX FIFO的完整时序发送一帧数据的标准流程是这样的先确认芯片处于STANDBY或READY状态然后把包长寄存器和FIFO阈值设好CSN拉低发写FIFO命令跟着连续写入所有载荷字节CSN拉高最后发STX命令让芯片开始发射。整个过程在SPI层面是串行完成的中间不要做其他无关操作。uint8_t pkt[11] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B}; // 1. 设置包长固定长度模式 spi_write_reg(REG_PKT_LEN, sizeof(pkt)); // 2. 一次性写入TX FIFO csn_low(); spi_byte(CMD_WRITE_TX_FIFO); for (int i 0; i sizeof(pkt); i) { spi_byte(pkt[i]); } csn_high(); // 3. 触发发射 spi_byte(CMD_STX);一个非常容易踩的坑是为了省事先把STX发了再往FIFO里补数据。在数据率较低时这也许能跑通但数据率一高芯片可能已经在消费FIFO了而你写的速度跟不上就会出现TX FIFO下溢发射中断对方收到一堆残缺字节。正确做法是把整包数据一次性填完再触发发射。3.3 发送完成后的FIFO清理与状态确认发射完成后TX FIFO里的数据已经被调制器消费掉了正常情况下FIFO会自然变空。但有些异常场景比如发射被错误中断打断、包长设置大于实际写入字节数FIFO里可能残留旧数据。残留数据不清理下一包写入时新旧数据混在一起接收端拿到的一定是错的。所以循环发送时下一包开始前要先读TX FIFO状态确认空的标志已经置位。如果发现还有残留最稳妥的办法是回到STANDBY状态按驱动库提供的清空FIFO接口处理或者干脆把整条链路重新初始化。我在实际测试中就遇到过一包发完之后根本没做状态检查15秒之后下一包发出来前面莫名其妙多了十几个字节抓了半天波形才发现是残留数据搞的鬼。4. 接收链路实操从RX FIFO把数据取出来4.1 同步字锁定后有效载荷才进FIFO接收侧和发送侧是逆过程。芯片上电进入RX状态后解调器一直在搜索空中信号的前导码检测到前导码后开始等同步字同步字匹配成功后才把后续的有效载荷按顺序写进RX FIFO。这意味着FIFO里不会出现空中的随机噪声或突发干扰它里面是已经和物理层对齐的有效数据。这个机制帮我们省了很多事但也带来一个隐性问题CRC错误并不一定阻止数据进FIFO。很多配置下即使CRC校验失败有效载荷也已经被写进了RX FIFO只是状态寄存器里同步抛出一个CRC错误标志。如果你只读FIFO不看状态就会把一包损坏的数据当正常数据交给上层协议轻则丢包统计不准重则直接误动作。因此接收处理时一定要在读取FIFO内容的同时检查CRC相关标志。4.2 读RX FIFO的三种策略与代码接收数据的读取策略大致有三种实际项目中按包长和MCU负载来选。第一种是整包读出收到“数据准备好”类中断后先读RX FIFO状态拿到本次接收的字节数再一次性把数据全部读走。这种方式最简单适合单包不超过64字节、主控没有太多抢占的场景。第二种是阈值中断分段读把接收FIFO阈值设成32字节FIFO里数据一过半就中断一次MCU分两次把整包读走。这种方式适合包长接近64字节、或主控还要应付其他实时任务的场景。第三种是
返回列表