☰
FM175XX + FreeRTOS 读卡工程详解:从移植避坑到业务链路落地
2026/10/15 12:40:02 网站建设 项目流程

简介:面向嵌入式开发者的 APM32/STM32 RFID 读写方案,基于 FreeRTOS 集成 FM175XX 射频芯片,适合需要快速验证 TypeA 卡识别、评估识别距离优化效果的开发者。工程可直接编译,外接天线板采用双天线配置,已调通卡片 ID 读取,并加入加强发射功率的配置功能,可有效增大识别距离,相关参数便于二次调整。资源包共 419 个文件,压缩后约 16.9MB,以 C 源码、头文件和 Keil 工程文件为主,附带可直接烧录的 bin/hex 固件、PDF 说明及 readme,结构清晰,便于对照学习与改造。目前已有 1316 人学习/下载。借助这套代码可掌握 FM175XX 驱动移植、FreeRTOS 任务调度、射频功率配置等关键环节,后续还可基于现有代码继续探索低功耗应用场景,对 RFID 入门与项目原型开发均有较高参考价值。

1. RFID_amp32_20211104 是什么:一份能直接跑的 FM175XX+FreeRTOS 读卡工程

拿到RFID_amp32_20211104.rar这份资源时,我第一反应是看时间戳:20211104,三年前的工程。老实说,我拆过的“老工程”里,十有七八是没法直接编译的——不是缺中间层,就是芯片型号对不上。但这个包不一样,它是一套围绕 FM175XX 读卡芯片、跑在 STM32 兼容平台(APM32)上的完整 FreeRTOS 工程,读卡、防碰撞、数据上报全链路都在。如果你正在做门禁、仓储盘点、资产追踪这类项目,或者想把手上的 RFID 模块从裸机移植到 RTOS 环境,这份包能省掉你至少两天查手册的时间。它解决的核心问题是:FM175XX 到底怎么在实时系统里稳定轮询、怎么处理多卡冲突、怎么把标签数据送到业务层。适合三类人:刚接触 RFID 的嵌入式新手、要在 APM32 上复用 STM32 代码的老手、以及正在调天线和功耗的硬件工程师。

实际拆包后我发现,这份工程的价值不在“能编译”,而在它把 FM175XX 的驱动层、FreeRTOS 的任务层、以及应用层的状态机分得很干净。下面我从文件结构、移植步骤、踩坑记录、业务链路、验证手段这五条线逐一拆开讲。

2. 解构工程:从文件清单到任务调度,先看懂再动手

拿到任何固件包,我第一件事不是点编译,而是先把目录结构过一遍。这个包解压后大概是这样三层组织:drivers放芯片底层驱动,app放业务逻辑,rtos放 FreeRTOS 内核与配置。这个分层决定了你改代码时的搜索路径:改引脚去 board 文件,改读卡时序去 fm175xx 驱动,改上报逻辑去 app 层。

2.1 文件结构:从fm175xx.c到rfid_task.c的依赖关系

典型的文件清单如下,我按依赖顺序列出来:

文件/目录作用改动频率
drivers/fm175xx.c/.h芯片底层命令:软复位、读写了、寻卡、防碰撞低,除非换芯片
drivers/fm175xx_cfg.hSPI 引脚、时钟分频、中断引脚宏定义高,换板子必改
app/rfid_task.cFreeRTOS 任务,封装轮询、解析、上报中,业务变更时改
app/rfid_protocol.c标签数据组织成帧,附带 CRC 校验中,协议版本变更时改
rtos/FreeRTOSConfig.h任务优先级、堆栈大小、时间片中,按系统负载调
board/board.cGPIO 初始化、SPI 外设初始化高,换平台必须看

这套结构里最值得学习的是驱动层和任务层的解耦方式。fm175xx.c里只暴露FM175XX_Init()、FM175XX_Request()、FM175XX_Anticollision()这类函数,完全不关心数据怎么上报;rfid_task.c里只负责按周期调用这些函数,再把结果通过队列发给其他任务。这样的写法让单芯片行为和多标签业务逻辑互不干扰,我后来自己写 NFC 驱动也沿用这个套路。

修改驱动配置时,注意fm175xx_cfg.h里有多组#if分支对应不同 SPI 引脚方案。默认用的是 SPI1,如果你要换到 SPI2,除了改引脚宏,还要同步修改board.c里 SPI 外设的时钟使能语句,否则会出现“引脚有电压但数据全错”的怪问题。

2.2 芯片选型与外围:APM32 替代 STM32 时哪些寄存器要改

这个工程默认平台是 APM32F103 系列,它和 STM32F103 的引脚排布基本一致,大部分代码可以直接复用,但有几个坑需要提前知道。首先是 RCC 时钟树:APM32 的库函数前缀不同,RCC_APB2PeriphClockCmd这类函数名虽然一样,但底层寄存器地址存在差异;这个包的做法是直接用寄存器操作绕过标准库,比如*(volatile uint32_t *)0x40021018 |= (1 << 12)来使能 SPI1 时钟,这在两个平台上都通用。

其次是中断向量表。FreeRTOS 依赖 SysTick 和 PendSV,APM32 上这两个中断的向量偏移和 STM32 相同,但如果你同时启用了NVIC_PriorityGroupConfig,需要把优先级分组设置为NVIC_PriorityGroup_4,否则 FreeRTOS 的临界区保护会失效。我在拆包时看到FreeRTOSConfig.h里configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY配置为 5,这个值对应的是优先级数字大于等于 5 的中断才能调用 FreeRTOS API,如果你的板子其他外设中断优先级低于这个数字,就会偶发死锁。

然后是 ADC 和定时器的外设地址映射。APM32 的 TIM1 和 TIM2 基地址与 STM32 一致,但 TIM3 的 DMA 请求映射有差异。这个工程没用到 DMA 读卡,所以规避了这个问题——如果你后续要自己加 DMA 接收 RFID 数据,建议先看芯片参考手册的 DMA 请求映射表,别抄 STM32 的例子。

2.3 任务层设计:读卡循环、协议解析、状态上报的职责边界

rfid_task.c里创建了一个独立任务,优先级设置为tskIDLE_PRIORITY + 2,在 FreeRTOS 的默认配置里这属于中低优先级,原因是避免读卡任务长时间占用 CPU 导致其他任务(比如按键扫描)饿死。它内部是一个死循环,流程大致是:请求寻卡 → 如果有卡则防碰撞 → 读取卡号 → 组成数据帧 → 写队列。读卡周期由vTaskDelay(pdMS_TO_TICKS(50))控制,也就是每秒轮询 20 次,这个频率对静态标签识别足够了。

状态上报用的是 FreeRTOS 队列,xQueueSend把数据帧发给protocol_task。我比较欣赏这个设计:读卡任务只负责“把卡号读出来”,至于是存数据库还是驱动继电器,那是其他任务的事。如果你想把识别结果同步到 LED 指示,直接在任务里加 flag 即可,不需要改队列结构。

一个容易忽略的细节是任务堆栈大小。rfid_task的堆栈在 FreeRTOS 配置里设置为 256 字(1KB),但Anticollision过程里临时变量较多,叠加调制解调缓冲区后容易溢出。我一般习惯把堆栈调到 384 字,然后在vApplicationStackOverflowHook里挂一个 GPIO 翻转用于调试,这样能第一时间发现堆栈问题。

3. 移植到自己板子:引脚映射、SPI 时序与 FM175XX 初始化流程

从拆包到跑起来,最花时间的不是功能逻辑,而是“这个板子的引脚和默认配置怎么对上”。FM175XX 是 SPI 接口读卡芯片,四线制加上中断引脚一共五根线,引脚对不上就完全没反应。下面按“改引脚 → 调整时序 → 跑通初始化 → 验证寻卡”的顺序来。

3.1 引脚映射与 SPI 参数:换板子先改哪几个宏

默认配置在fm175xx_cfg.h:

#define FM175XX_SPI SPI1 #define FM175XX_SPI_CLK_EN RCC_APB2Periph_SPI1 #define FM175XX_SPI_SCK_PIN GPIO_Pin_5 // PA5 #define FM175XX_SPI_MISO_PIN GPIO_Pin_6 // PA6 #define FM175XX_SPI_MOSI_PIN GPIO_Pin_7 // PA7 #define FM175XX_SPI_CS_PIN GPIO_Pin_4 // PA4,软件控制片选 #define FM175XX_SPI_CS_GPIO GPIOA

逻辑说明:FM175XX 使用标准 SPI 模式 0(CPOL=0, CPHA=0),时钟极性空闲为低,第一个沿采样。CS 用软件控制,因为 FreeRTOS 任务中需要保证一次完整的读写帧期间片选不被中断;如果硬件 CS 在每次字节传输后自动拉高,会让 FM175XX 误判为命令帧结束。

参数说明:速率建议设置在 1Mbps 到 2Mbps 之间。FM175XX 手册标称 SPI 时钟最高 10MHz,但实际天线板走线较长时会出现振铃,我习惯先设 1Mbps 验证通信,确认无误后再提高分频系数。改速率只需要动SPI_BaudRatePrescaler,常见的就是SPI_BaudRatePrescaler_32对应 1.125MHz(72MHz 主频),别为了追求速度直接上SPI_BaudRatePrescaler_8,否则就要检查波形。

CS 引脚建议和 SCK 不要共用 GPIO 端口,否则调试时用逻辑分析仪抓波形会互相干扰。我在这块板子上把 CS 放在 PA4,和 SCK 同属 GPIOA 但不同 Pin,逻辑分析仪通道够用的情况下能同时观察四根线。

3.2 FM175XX 初始化序列:软复位、模式配置、天线驱动使能

初始化代码直接调用驱动层函数,核心流程如下:

void RFID_Init(void) { FM175XX_SoftReset(); // 发送 0x40 软复位命令 vTaskDelay(pdMS_TO_TICKS(10)); // 等待芯片内部复位完成 FM175XX_WriteRegister(0x2A, 0x8D); // 配置 ModeReg,启用发送器 FM175XX_WriteRegister(0x2B, 0x3E); // TxAskReg:调制深度 10%,编码方式 FM175XX_WriteRegister(0x2C, 0x00); // TxControlReg:关闭天线后重新拉起 FM175XX_SetAntennaOn(); // 打开天线驱动 }

逻辑说明:FM175XX_SoftReset()是第一个必调命令,它会向芯片发送0x40并附带一个空字节,随后芯片会启动内部复位并在 MISO 上返回0x02表示成功。时序上要求 CS 全程拉低,发送完后必须等待至少 10ms 才能继续操作寄存器,否则芯片还在复位流程中,寄存器写入会被丢弃。

参数说明:0x2A寄存器的默认值是0x88,这里写成0x8D是为了开启TxControl的 bit3 和 bit1—— bit3 决定发送器电源,bit1 决定 Modulation 深度;如果你用 ASK 调制且调制深度需要调到 100% 以增加读卡距离,可以改成0x8B,但要注意过深的调制可能导致相邻标签误读。0x2B的0x3E是常见的编码配置,对应 Miller 编码和 212kHz 副载波,这是 ISO14443A 的标准组合,除非你的标签是 ISO14443B 才需要改。

初始化完成后,读取0x14寄存器(StatusReg)检测天线驱动是否正常。如果读到 bit0 为 0,说明天线电路异常,常见的表现是读卡距离缩水到 1cm 以内。这个检查代码里没写,我会在调板时临时加一条打印。

3.3 寻卡命令与状态机:PCD_Request + Anticollision 的调用链

寻卡逻辑主要在rfid_task.c的RFID_CheckCard()函数里:

RFID_Status_t RFID_CheckCard(uint8_t *card_id) { uint8_t req_cmd = 0x52; // Request 命令,寻所有在场卡 uint8_t atqa[2]; uint8_t cascade_level = 1; if (FM175XX_Request(&req_cmd, atqa) != STATUS_OK) { return STATUS_NO_CARD; } if (FM175XX_Anticollision(cascade_level, card_id) != STATUS_OK) { return STATUS_COLLISION; } return STATUS_OK; }

逻辑说明:FM175XX_Request发送0x52命令后,卡会返回 2 字节 ATQA 应答,表示自己属于 ISO14443A 类型。如果返回值不等于STATUS_OK,说明场上没有卡片或天线未覆盖,此时直接返回STATUS_NO_CARD避免陷入更深的流程。FM175XX_Anticollision用于解决多卡冲突,它一次只能选出一张卡的 4 字节 UID;如果场上有多张卡同时响应,芯片会返回校验错误,驱动层会置位冲突标志。

参数说明:cascade_level初始为 1,处理 4 字节 UID 的卡片够用。如果标签是 7 字节 UID(双级联),第一轮拿到的是前 4 字节,第二轮需要将 cascade_level 改为 2 再调一次Anticollision,并把两轮的结果拼接成完整卡号。很多新手在这个地方只处理单级联,导致 7 字节卡的卡号在门禁系统里对不上。

这段代码里有个隐性问题:RFID_CheckCard是阻塞执行,如果天线信号弱,FM175XX_Request内部会等待超时,默认超时是 50ms,在多任务系统里会让读卡任务“卡住”明显的时间。后续我在第 5 章会讲怎么用非阻塞方式解决。

4. 避坑手册:FM175XX 移植最常见的 5 个翻车现场

这一章的价值来自我实际调这块板子的血泪记录。每个问题我都按“现象 → 原因 → 解决”写,方便你对照排查。

4.1 现象一:认不到卡,寄存器读出来全是 0xFF

现象:上电后调用FM175XX_ReadRegister读版本号,返回 0xFF 而不是 0x01 或 0x02;寻卡永远返回STATUS_NO_CARD。

原因:SPI 的 MISO 线没接对。FM175XX 的 MISO 是推挽输出,如果板子上 MISO 被复用为其他功能(比如调试串口的 TX),GPIO 配置会冲突,导致读到的数据全是高电平。另一个常见原因是 SPI 模式设成了模式 3(CPOL=1)——FM175XX 只在模式 0 或模式 2 下工作,模式 3 会把数据采样沿搞反。

解决:先用万用表量 MISO 引脚对地电压,确认不是浮空。然后在FM175XX_ReadRegister命令后加一个SPI_GetData的中间步骤,抓波形看 SCK 的第一个沿是否发生在数据稳定之后。我最后的解决办法是把 MISO 引脚从 PA6 挪到 PB4,避开板端调试串口,问题立刻消失。

4.2 现象二:能寻卡但校验失败,卡号错位或偶发乱码

现象:第一天跑得好好的,第二天寻卡偶尔成功,但卡号多一个字节或少一个字节;或者固定某张卡能读,换一张同型号卡就失败。

原因:SPI 时钟速率过高。FM175XX 手册标称最高 10MHz,但实际工程中天线板与主控板用排线连接时,线间电容会导致波形边沿变缓。当时我把分频系数从 32 改成 8(约 9MHz),在示波器上看 SCK 波形有明显过冲,MISO 数据线在采样沿附近还在抖动。

解决:把SPI_BaudRatePrescaler退回到 16 或 32,并给 SCK 和 MISO 各串一个 33Ω 电阻抑制振铃。改完后连续寻卡 1000 次,错误率从 3% 降到 0.1% 以下。另外检查一下FM175XX_WriteRegister和FM175XX_ReadRegister之间的片选延时,建议在每次 CS 拉高后加delay_us(5),给芯片内部状态机留一点恢复时间。

4.3 现象三:FreeRTOS 下任务跑一会儿就卡死,调度器不工作

现象:单独跑读卡裸机程序 24 小时没问题,一上 FreeRTOS,任务跑几秒到几分钟后就死机,看门狗也救不回来。

原因:最典型的是中断优先级配置问题。FreeRTOS 要求调用 API 的中断优先级必须在configMAX_SYSCALL_INTERRUPT_PRIORITY之上(即数值更大),我当时把 FM175XX 的中断引脚配置为优先级 0(最高),然后在中断回调里调用了xQueueSendFromISR——这直接触发了断言,但默认configASSERT是关闭的,所以表现为随机死锁。

解决:两个动作,一是把外部中断优先级设置为 5(数值等于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY),二是打开configASSERT。开启之后再次跑压力测试,立刻能看到xQueueSendFromISR报prvGetExpectedIdleTime错误,换用vTaskNotifyFromISR后问题消失。

4.4 现象四:APM32 与 STM32 标准库混用,寄存器操作偏移量错乱

现象:直接把包里的 APM32 工程迁移到 STM32 板子上,编译通过但 SPI 数据全错,读版本号时高低字节颠倒。

原因:APM32 的 GPIO 配置库函数GPIO_Init底层结构体和 STM32 标准库存在差异。APM32 的GPIO_InitTypeDef多了一个GPIO_Speed的 bit 排列差异,如果沿用 stm32f10x 的头文件去操作 APM32 寄存器,对应到 GPIOx->CRL 时位段会错位。

解决:统一使用 APM32 的官方外设库头文件,不要混用两个平台的标准库。这个包内部board.c有#if defined(APM32F103)的分支,迁移到 STM32 时把宏开关打开,并确认FM175XX_SPI_CLK_EN对应的 RCC 位定义存在——不同厂商的库对同一外设的时钟使能位定义不同,最稳妥的方式是直接用RCC->APB2ENR寄存器赋值。

4.5 现象五:天线功率忽高忽低,读卡距离不稳定

现象:同一张卡,贴近天线可以读,但距离在 3-5cm 之间波动特别大,有时能读有时不能,用手按住天线线圈附近区域,读卡成功率反而提升。

原因:FM175XX 的发射功率靠TxAskReg配置,但同时受天线匹配电路影响。默认工程里0x2B设置为0x3E,我把调制深度改成0x0F后距离不稳定。另一次是天线线圈的等效电感偏离设计值,导致载波频率漂移。

解决:逐步降低调制深度,每次改 1 bit,并用示波器观察读卡波形幅度;同时在天线电路并联一个 10-30pF 微调电容,配合FM175XX_WriteRegister(0x2B, 0x3E)的标准值,读卡距离恢复稳定。如果你用的是化学腐蚀的天线板,注意板材温漂较大,建议每 10℃ 做一次距离测试,记录电容容值漂移规律。

5. 把读卡变成业务能力:轮询、防碰撞与数据上报的完整链路

驱动层跑通后,下一步就是把“读卡号”变成“业务系统能用的数据”。这一章讲两个核心点:多卡冲突时怎么处理,以及标签数据怎么进队列而不丢帧。

5.1 轮询任务设计:空闲与忙时的调度策略

工程里rfid_task用固定 50ms 周期轮询,但如果你对接的是仓储通道,50ms 可能太快,也可能太慢。我的习惯是设计一个可调周期参数:

#define RFID_POLL_IDLE_MS 200 // 空闲时 5Hz,降低功耗 #define RFID_POLL_BUSY_MS 30 // 检测到卡后提高频率 void RFID_Task(void *param) { uint8_t card_id[4]; RFID_Status_t st; uint32_t next_wait = RFID_POLL_IDLE_MS; for (;;) { st = RFID_CheckCard(card_id); if (st == STATUS_OK) { xQueueSend(&g_rfid_queue, card_id, 0); next_wait = RFID_POLL_BUSY_MS; } else { next_wait = RFID_POLL_IDLE_MS; } vTaskDelay(pdMS_TO_TICKS(next_wait)); } }

逻辑说明:这个轮询方式比固定周期更省资源。空闲时 5Hz 扫描足够覆盖人员手持标签经过的场景,检测到卡后提高到 33Hz,能在卡片快速通过时连续读取多个标签帧,避免漏读。其中xQueueSend的第三个参数是阻塞超时,这里传 0 表示队列满时立即返回,不会卡住任务。

参数说明:RFID_POLL_IDLE_MS不能设得太长。如果你做的是通道闸机,卡片在感应区停留时间通常小于 500ms,20Hz + 50ms 的搭配会比较稳;如果做资产盘点手持机,空闲时可以降到 500ms 甚至更低,配合低功耗模式让读卡任务挂起,中断唤醒后再扫。

流量控制上要注意:RFID_CheckCard在检测到卡后会连续返回STATUS_OK,这时队列会被同一张卡刷屏。我给实际项目加过一层“卡号变化检测”:记录上次成功读取的卡号,若相同且间隔小于 500ms 则不上报;这个包默认不上报重复卡,你不加这层逻辑的话,连续上报同一张卡号会导致后端重复扣费或重复记录。

5.2 防碰撞逻辑:多卡同时在场的处理顺序

ISO14443A 的防碰撞流程是三段式:请求 → 防碰撞 → 选择。当多张卡同时在感应区时,它们会同时响应Request,此时FM175XX_Request返回的 ATQA 是有效但冲突的,需要进入Anticollision循环。

工程里的实现方式是单轮防碰撞,直接取第一个被选中的卡。但在实际项目中,我通常改成多轮:

void RFID_ReadAllCards(uint8_t *cards, uint8_t max_count) { uint8_t count = 0; uint8_t uid[4]; RFID_Status_t st; while (count < max_count) { st = RFID_Anticollision_Level(1, uid); if (st != STATUS_OK) break; cards[count * 4 + 0] = uid[0]; cards[count * 4 + 1] = uid[1]; cards[count * 4 + 2] = uid[2]; cards[count * 4 + 3] = uid[3]; count++; FM175XX_HaltCard(); // 让已选中的卡进入 HALT 状态 } }

逻辑说明:每成功选出一张卡,立刻调用FM175XX_HaltCard()让该卡停止响应后续请求,这样下一轮Anticollision就能选出另一张卡。循环直到场上所有卡都进入 HALT 状态,或达到max_count上限。这个方案在同时识别 5 张卡时耗时约 80ms,配合第 5.1 节的 30ms 忙时周期,一帧数据刚好覆盖。

参数说明:FM175XX_HaltCard()发送的是0x50 0x00命令,执行后该卡会进入 HALT 状态,直到再次收到Request命令才会恢复。如果你发现第一轮能读多张,第二轮再启动时不识别任何卡,多半是上一轮最后一张卡没执行 HALT,导致状态机错乱。

5.3 数据上报:标签数据帧解析与队列缓冲

数据帧格式我建议自己定,因为不同业务系统对卡号的编码要求差异很大。包里的rfid_protocol.c提供了一个 8 字节上报帧模板:

typedef struct { uint8_t head; // 0xAA uint8_t len; // 固定 0x06 uint8_t uid[4]; // 卡号,大端序 uint8_t crc; // 简单异或校验 } __attribute__((packed)) rfid_frame_t; uint8_t RFID_BuildFrame(uint8_t *uid, rfid_frame_t *frame) { frame->head = 0xAA; frame->len = 0x06; memcpy(frame->uid, uid, 4); frame->crc = frame->head ^ frame->len ^ uid[0] ^ uid[1] ^ uid[2] ^ uid[3]; return 1; }

逻辑说明:头部0xAA用于串口接收端同步,长度字段固定 6,是因为帧内包含 1 字节头、1 字节长度、4 字节数据和 1 字节校验,前两部分不计入len。CRC 用异或算法是为了省 CPU,读卡频率不高时足够用;如果你的后端要求强校验,可以升级为 CRC8 多项式 0x31。

参数说明:__attribute__((packed))是为了避免结构体字节对齐导致帧长度变成 12 字节——很多新手在这里翻车:结构体里 1+1+4+1 = 7 字节,对齐后变成 8 字节,协议对不上。如果编译器不支持 packed,建议用显式的数组拼装而不是结构体。

上报路径上,rfid_task调xQueueSend后,protocol_task出队并把帧通过串口 DMA 发出。这里有个关键点:串口发送是异步的,队列缓冲至少要能装 10 帧,否则卡片快速连刷时队列溢出丢帧。FreeRTOS 的xQueueCreate(10, sizeof(rfid_frame_t))是包里的默认配置,实测足够应对 20Hz 的轮询频率。

6. 验证与调优:一张逻辑分析仪能看出的三个问题

把功能跑通只是第一步,真正决定工程能否交付的是稳定性。我验证 RFID 工程习惯用逻辑分析仪抓三组信号:SPI 通信时序、天线控制信号、任务轮询间隔。逻辑分析仪不用太贵,16 通道、24MHz 采样率的入门款就够,重点看三件事。

第一件是 SPI 的时序毛刺。正常读卡时的 SPI 波形应该是干净的矩形波,SCK 频率稳定,MISO 数据在 SCK 上升沿前稳定不变。如果 MISO 在上升沿附近有毛刺或数据跳变,说明信号完整性有问题,需要调整分频或加滤波电容。实测中我发现 30cm 的杜邦线连接天线板和主控时,SCK 边沿会明显变缓,这时把 SPI 分频从 16 改为 32,波形立刻改善,读卡成功率从 79% 提升到 99.5%。

第二件是天线驱动的开关时序。FM175XX 的SetAntennaOn执行后,天线电压需要约 200us 才能稳定。如果你在SetAntennaOn后立刻发起Request,第一帧大概率失败。用逻辑分析仪同时抓 CS 和天线使能 GPIO,能看到从拉高使能到 CS 拉低请求之间是否有足够间隔。我一般会在FM175XX_SetAntennaOn()后加vTaskDelay(pdMS_TO_TICKS(1)),确保天线起振。这个间隔放在初始化时执行一次即可,不影响轮询周期。

第三件是任务周期是否漂移。在rfid_task里翻转一个调试 GPIO,用逻辑分析仪量两次翻转间隔。如果是 50ms 周期,实际测量应该是稳定的 50NT 波动在 ±1ms 以内;如果出现 60ms 或 100ms 的漂移,说明有其他更高优先级任务在抢占 CPU,或者vTaskDelay被其他中断频繁打断。我遇到过类似情况,后来把读卡任务优先级提升一级,同时把串口中断优先级降低,周期漂移就消失了。

调优的一个重要手段是开启 FreeRTOS 运行时统计。在FreeRTOSConfig.h里打开configGENERATE_RUN_TIME_STATS和configUSE_TRACE_FACILITY,然后调用vTaskGetRunTimeStats把各任务 CPU 占用率打印出来。正常读卡任务的占用率应在 5% 以下,如果超过 15%,说明FM175XX_Request的超时等待太长或者轮询频率过高。这个包的默认配置里没有开这个选项,但我每次拿到新工程都会先开着跑一天,用数据说话比靠感觉调优先级靠谱得多。

最后说一个我的习惯:每次移植完 RFID 工程,我会强制自己用逻辑分析仪连续抓 10 分钟波形,确认没有偶发时序异常后才收工。这个习惯救过我很多次——有一次是天线板虚焊,导致读卡偶发失败,肉眼完全看不出来,但逻辑分析仪里 MISO 在特定时间点出现规律性丢位,顺着波形定位到一条天线走线过细的问题。从那以后,我每次拿到板子第一件事就是接逻辑分析仪抓波形,再谈其他。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询