
1. 项目缘起与整体设计思路1.1 为什么选择STM32RFID做仓库管理做过仓库管理的人都知道最头疼的不是货物多而是账实不符。手工记账、Excel表格、扫码枪这些方案我都试过问题集中在两点一是效率低二是容易出错。尤其是中小型仓库SKU几百到几千个每天出入库频繁靠人工核对基本等于给自己埋雷。这个项目的核心思路很直接用STM32做主控搭配RFID读写模块实现货物标签的自动识别与数据记录。为什么选STM32而不是51单片机原因很实际——51的资源跑个LCD刷新都吃力更别说同时处理RFID读写、数据存储、串口通信和上位机交互了。STM32F103系列性价比高外设丰富社区资料多踩坑了也容易找到答案。RFID方案我选的是13.56MHz频段的ISO 15693协议。有人会问为什么不选125kHz的低频方案低频卡便宜但读写距离短通常2-5cm而且不支持防冲突一次只能读一张卡。ISO 15693支持同时识别多张标签读写距离能到10cm以上对于仓库场景更实用。高频方案在金属和液体环境下的表现也相对可控虽然不如超高频但成本低很多。整个系统的定位是中小型仓库、实验室器材管理、工具借还、档案管理等场景。适合有STM32基础、想做一个完整嵌入式项目的开发者也适合作为毕业设计或课程设计的参考方案。1.2 系统架构与模块划分系统整体分为三层感知层、控制层、应用层。感知层就是RFID标签和读写器。每个货物贴一张ISO 15693标签标签内存储货物ID、名称、入库时间等信息。读写器负责与标签通信通过SPI或UART接口与STM32连接。控制层是STM32主控板负责调度所有外设、处理数据、执行逻辑。具体包括RFID读写控制、LCD显示、按键输入、蜂鸣器提示、EEPROM/Flash存储、串口通信。应用层是上位机软件通过串口与STM32通信实现数据可视化、报表导出、库存查询等功能。上位机我用PythonPyQt写的也可以用LabVIEW或者C#看个人习惯。模块之间的数据流是这样的RFID读写器读到标签→STM32解析数据→判断操作类型入库/出库/盘点→更新本地存储→刷新LCD显示→通过串口上报上位机→上位机更新数据库。这个架构的好处是解耦。每个模块独立工作调试时可以单独测试。比如RFID模块读不到卡我就先不管LCD和上位机直接用串口打印原始数据确认是硬件问题还是协议问题。1.3 关键器件选型与成本控制选型这块我踩过不少坑这里直接给结论。主控芯片用STM32F103C8T6也就是常说的蓝板。72MHz主频64KB Flash20KB RAM对于这个项目绰绰有余。价格便宜一片不到10块钱。如果要做更复杂的界面或者数据量更大可以上STM32F407但成本会翻几倍。RFID读写模块我用的是MFRC522的升级版——RC522只支持ISO 14443Mifare卡不支持ISO 15693。要读ISO 15693标签得用MFRC631或者ST25R3911。我最终选了MFRC631SPI接口3.3V供电和STM32直连没问题。模块价格大概30-50元比RC522贵但支持协议更全。标签选的是I-CODE SLIXISO 15693协议256位用户存储区够存货物ID和简单信息。单价大概1-2元批量买更便宜。如果预算紧张可以用I-CODE SLI存储区小一点但够用。显示部分用2.4寸TFT LCDILI9341驱动SPI接口。分辨率320x240显示菜单和货物信息足够。价格20元左右。有人问为什么不直接用OLEDOLED小显示不了几行字仓库管理需要同时看多条记录TFT更合适。存储用AT24C256 EEPROMI2C接口32KB容量。存几百条货物记录没问题。如果要存更多可以换W25Q64 Flash8MB容量SPI接口价格差不多。电源部分用AMS1117-3.3稳压输入5V输出3.3V给STM32和RFID模块。注意RFID模块峰值电流可能到100mAAMS1117要加散热片或者换LM2596开关电源模块。整体BOM成本控制在80-120元之间具体看采购渠道和数量。2. 核心细节解析与实操要点2.1 ISO 15693协议的关键参数与配置ISO 15693是高频RFID的国际标准载波频率13.56MHz读写距离可达1米以上取决于天线和功率。协议的核心是防冲突机制和命令集。防冲突是ISO 15693的亮点。它支持同时识别多张标签通过时隙ALOHA算法实现。具体流程是读写器发送Inventory命令→标签在随机时隙响应→读写器逐个获取标签UID→完成盘点。这个过程在MFRC631内部自动完成STM32只需要读取结果。命令集方面常用的有命令功能使用场景Inventory盘点标签批量识别Read Single Block读单个块读取货物信息Write Single Block写单个块写入货物信息Lock Block锁定块防止数据被篡改Get System Information获取标签信息读取标签容量配置MFRC631时关键参数是调制深度和通信速率。ISO 15693支持两种速率低速率1.65kbps和高速率26.48kbps。高速率读写快但距离短低速率距离远但慢。仓库管理建议用高速率因为标签距离近10cm以内速度优先。调制深度选10%还是100%10%调制距离更远但抗干扰能力弱100%调制距离近但稳定。我实测下来仓库环境用100%调制更稳因为货架金属多干扰大。天线设计也很关键。MFRC631需要外接天线我用的是PCB环形天线直径5cm电感量1.5uH左右。天线匹配电路要调具体是调整串联电容和并联电容的值使谐振频率落在13.56MHz。没有网络分析仪的话可以用示波器看天线两端波形调整到幅度最大即可。2.2 STM32外设配置与中断优先级STM32F103C8T6的外设配置有几个坑我一个个说。SPI接口用于RFID模块和LCD。RFID用SPI1LCD用SPI2。SPI1配置为全双工主模式时钟极性低、相位第一边沿预分频89MHz。MFRC631最高支持10MHz SPI时钟9MHz没问题。注意SPI的NSS引脚要么用软件控制要么配置为硬件输出否则通信会失败。I2C接口用于EEPROM。STM32的硬件I2C有已知bug容易卡死。我建议用软件模拟I2C虽然速度慢一点但稳定。软件I2C的延时用SysTick实现延时2us左右对应100kHz速率。UART用于上位机通信。USART1配置为115200波特率8数据位1停止位无校验。中断优先级要设好RFID中断最高UART次之定时器最低。因为RFID数据不能丢UART可以缓冲定时器只是刷新显示。中断优先级分组用NVIC_PriorityGroup_22位抢占优先级2位响应优先级。RFID中断抢占优先级0UART抢占优先级1定时器抢占优先级2。这样RFID中断可以打断UART和定时器保证实时性。定时器用TIM2配置为1ms中断用于系统时基和按键消抖。TIM3配置为PWM输出用于蜂鸣器。蜂鸣器用无源蜂鸣器PWM频率2kHz占空比50%声音最响。2.3 数据存储结构与读写策略EEPROM存储结构设计得好后期扩展才不痛苦。我的方案是分三个区系统区、货物区、日志区。系统区从地址0x0000开始存系统参数货物数量、日志数量、RFID功率等级、LCD亮度等。每个参数2字节共32字节。货物区从地址0x0100开始每条记录64字节。结构如下typedef struct { uint8_t uid[8]; // 标签UID char name[24]; // 货物名称 uint16_t quantity; // 数量 uint32_t in_time; // 入库时间戳 uint32_t out_time; // 出库时间戳 uint8_t status; // 状态0在库1出库 uint8_t reserved[21]; // 预留 } GoodsRecord;64字节对齐方便读写。AT24C256页大小64字节正好一页写一条记录效率最高。日志区从地址0x4000开始每条日志16字节时间戳4字节、货物UID 8字节、操作类型1字节、操作员ID 1字节、预留2字节。日志循环覆盖最多存1024条。读写策略上我用了写缓存定期刷写的方式。货物数据先写到STM32的RAM缓存每10秒或者缓存满时统一写入EEPROM。这样减少EEPROM擦写次数延长寿命。AT24C256擦写寿命100万次如果每次操作都写一天100次操作不到30年就废了。用缓存后一天最多写8640次但实际也就几百次寿命足够。注意EEPROM写入需要5ms左右的等待时间期间不能响应I2C请求。我用了状态机超时机制写完后置标志位主循环检测标志位再继续。3. 实操过程与核心环节实现3.1 硬件连接与调试步骤硬件连接是第一步也是最容易出问题的一步。我按模块说。STM32与MFRC631的连接SPI1的SCK接PA5MISO接PA6MOSI接PA7NSS接PA4。另外MFRC631的IRQ引脚接PB0用于中断通知。复位引脚接PB1用软件控制复位。电源3.3V地共地。STM32与LCD的连接SPI2的SCK接PB13MISO接PB14不用MOSI接PB15CS接PB12DC接PB11RST接PB10背光接PB9。注意LCD的MISO可以不接因为只写不读。STM32与EEPROM的连接软件I2CSCL接PB6SDA接PB7。上拉电阻4.7kΩ接到3.3V。STM32与上位机的连接USART1的TX接PA9RX接PA10。通过CH340模块转USB接到电脑。调试顺序很重要。先测电源用万用表量3.3V和5V确认没有短路。再测晶振用示波器看8MHz晶振是否起振。然后测STM32最小系统烧录一个LED闪烁程序确认能运行。接着测LCD显示纯色确认SPI通信正常。再测EEPROM读写一个字节确认I2C正常。最后测RFID读标签UID确认SPI和中断正常。每一步都要确认后再进行下一步否则出了问题很难定位。我一开始图快全部焊完再调试结果LCD不亮查了半天发现是SPI2的NSS引脚配置错了。3.2 RFID读写核心代码实现RFID读写是项目的核心代码量最大。我基于MFRC631的官方库做了封装关键函数如下。初始化函数void RFID_Init(void) { MFRC631_Init(); // 硬件初始化 MFRC631_SetProtocol(PROTOCOL_ISO15693); // 设置协议 MFRC631_SetPower(0x3F); // 设置功率0x3F最大 MFRC631_SetModulation(0x10); // 100%调制 MFRC631_SetSpeed(SPEED_HIGH); // 高速率 MFRC631_AntennaOn(); // 开启天线 }盘点函数uint8_t RFID_Inventory(uint8_t *uid_list, uint8_t *count) { uint8_t status; uint8_t buffer[16]; status MFRC631_Inventory(CMD_INVENTORY, buffer); if (status ! MI_OK) { return status; } // 解析UID *count buffer[0]; // 标签数量 for (uint8_t i 0; i *count; i) { memcpy(uid_list[i*8], buffer[1i*8], 8); } return MI_OK; }读块函数uint8_t RFID_ReadBlock(uint8_t *uid, uint8_t block, uint8_t *data) { uint8_t status; uint8_t buffer[8]; status MFRC631_ReadBlock(uid, block, buffer); if (status ! MI_OK) { return status; } memcpy(data, buffer, 4); // ISO 15693每块4字节 return MI_OK; }写块函数uint8_t RFID_WriteBlock(uint8_t *uid, uint8_t block, uint8_t *data) { uint8_t status; status MFRC631_WriteBlock(uid, block, data); if (status ! MI_OK) { return status; } // 等待写入完成 HAL_Delay(10); return MI_OK; }注意ISO 15693的块大小是4字节不是8字节。写货物名称时需要分多次写。比如名称电阻10K12个字节需要写3个块。我封装了一个WriteMultiBlock函数自动分块。中断处理函数void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) ! RESET) { rfid_irq_flag 1; // 置标志位 EXTI_ClearITPendingBit(EXTI_Line0); } }主循环中检测标志位调用RFID_Inventory。注意中断里不要做耗时操作只置标志位。3.3 上位机通信协议与数据解析STM32与上位机的通信协议我自定义了一套简单可靠。帧格式帧头0xAA 0x55 命令字1字节 数据长度1字节 数据N字节 校验和1字节 帧尾0x0D 0x0A。命令字定义命令字功能数据方向0x01查询库存上位机→STM320x02返回库存STM32→上位机0x03入库操作上位机→STM320x04出库操作上位机→STM320x05盘点操作上位机→STM320x06返回盘点结果STM32→上位机0x07设置参数上位机→STM320x08返回参数STM32→上位机校验和用累加和从命令字到数据末尾取低8位。STM32端的解析代码void UART_ParseFrame(uint8_t *frame, uint8_t len) { if (frame[0] ! 0xAA || frame[1] ! 0x55) return; if (frame[len-2] ! 0x0D || frame[len-1] ! 0x0A) return; uint8_t cmd frame[2]; uint8_t data_len frame[3]; uint8_t checksum 0; for (uint8_t i 2; i 4 data_len; i) { checksum frame[i]; } if (checksum ! frame[4data_len]) return; switch (cmd) { case 0x01: UART_SendInventory(); break; case 0x03: UART_HandleInbound(frame[4]); break; case 0x04: UART_HandleOutbound(frame[4]); break; case 0x05: UART_HandleCheck(); break; default: break; } }上位机用Python的pyserial库发送和接收都很方便。我写了一个简单的GUI用PyQt5左边是库存列表右边是操作按钮底部是日志输出。数据解析时注意字节序。STM32是小端模式Python的struct.unpack默认也是小端但要用明确指定。比如解析uint32_tstruct.unpack(I, data[0:4])[0]。4. 常见问题与排查技巧实录4.1 RFID读不到卡的排查思路RFID读不到卡是最常见的问题原因可能出在硬件、软件、环境三个方面。我按排查顺序说。第一步确认天线是否开启。用示波器测天线两端应该有13.56MHz正弦波幅度几伏到十几伏。如果没有波形检查MFRC631的AntennaOn命令是否执行或者天线匹配电路是否焊错。第二步确认SPI通信是否正常。读MFRC631的版本寄存器应该返回0x92或0x91。如果返回0x00或0xFF说明SPI通信失败。检查SCK、MOSI、MISO、NSS的连接特别是NSS必须拉低才能通信。第三步确认协议配置是否正确。ISO 15693和ISO 14443的配置不同如果配错了读不到卡。检查MFRC631_SetProtocol的参数确认是PROTOCOL_ISO15693。第四步确认标签是否兼容。ISO 15693标签有很多种有些标签的UID长度不同有些标签需要密码认证。用手机NFC读一下标签看能不能读到UID。如果手机读不到标签可能坏了。第五步确认距离和角度。ISO 15693的读写距离和天线方向有关。标签平面要和天线平面平行距离在10cm以内。如果标签贴在金属上距离会缩短到2-3cm。可以试着调整标签位置。我遇到过一次读不到卡查了半天发现是天线匹配电容焊错了谐振频率偏到12MHz重新计算电容值后正常。4.2 EEPROM数据丢失的解决方法EEPROM数据丢失通常是因为写入过程中断电或者I2C通信错误。我总结了几个防护措施。第一写入前先读回确认。每次写EEPROM后立即读回同一地址比较数据是否一致。如果不一致重写一次。第二加掉电检测。用STM32的PVD可编程电压检测器当电压低于2.9V时触发中断在中断里紧急保存关键数据。PVD配置如下void PVD_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); PWR_PVDLevelConfig(PWR_PVDLevel_2V9); PWR_PVDCmd(ENABLE); EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line EXTI_Line16; EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd ENABLE; EXTI_Init(EXTI_InitStructure); NVIC_EnableIRQ(PVD_IRQn); } void PVD_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line16) ! RESET) { EEPROM_SaveCriticalData(); // 紧急保存 EXTI_ClearITPendingBit(EXTI_Line16); } }第三用双备份。重要数据存两份地址A和地址B。读取时比较两份数据如果一致就用不一致就用带CRC校验的那份。写入时先写A再写B确保至少有一份是完整的。第四定期自检。上电时读取所有货物记录检查CRC。如果发现损坏从备份恢复或者标记为无效。我实测下来加了PVD和双备份后即使频繁断电数据也没丢过。4.3 常见问题速查表问题现象可能原因排查方法解决方案LCD白屏背光未开测背光引脚电压拉高背光引脚LCD花屏SPI速率过高降低SPI预分频预分频改为16RFID读不到卡天线未开启测天线波形执行AntennaOnRFID读卡距离短调制深度不对检查调制配置改为100%调制EEPROM读写失败I2C上拉电阻未接测SCL/SDA电压接4.7kΩ上拉串口无数据波特率不匹配示波器测TX波形统一为115200按键无响应消抖时间太短调整消抖参数改为20ms系统死机中断优先级冲突检查NVIC配置重新分配优先级蜂鸣器不响PWM未启动测PWM输出启动TIM3 PWM上位机收不到数据帧头帧尾错误打印原始数据检查协议格式这个表是我调试过程中总结的基本上覆盖了80%的问题。遇到新问题先查表查不到再逐步排查。4.4 实操心得与避坑建议做了这个项目我最大的体会是硬件调试比软件调试难十倍。软件错了可以改硬件错了可能要重新打板。所以PCB设计一定要留测试点关键信号SPI时钟、I2C数据、RFID天线都要引出测试点方便用示波器测量。第二个体会是电源一定要干净。RFID模块对电源噪声很敏感如果电源纹波大读卡距离会缩短甚至读不到卡。我在AMS1117输出端加了100uF电解电容和0.1uF陶瓷电容效果很明显。第三个体会是代码要模块化。我一开始把所有代码写在main.c里后来改一个功能要翻半天。后来拆成rfid.c、lcd.c、eeprom.c、uart.c每个模块独立测试效率高很多。第四个体会是注释要写清楚。特别是寄存器配置每个位什么意思为什么要这么配都要写。过一个月再看代码没有注释根本看不懂。第五个体会是版本控制很重要。我用Git管理代码每次改动都提交出问题了可以回滚。硬件版本也要记录PCB改版后要更新版本号否则焊错板子都不知道。最后分享一个小技巧RFID标签贴的时候不要贴在金属表面要垫一层泡棉或者塑料片距离金属至少5mm。这样读卡距离能增加一倍。如果必须贴在金属上可以用抗金属标签但价格贵很多。这个项目后续还可以扩展加WiFi模块实现远程监控加摄像头实现货物拍照加称重传感器实现重量核对。但核心的RFID读写和STM32控制已经跑通了扩展只是加模块的事。