十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

STM8实现125K RFID读卡器:电路、解调与解码全攻略

STM8实现125K RFID读卡器:电路、解调与解码全攻略 简介一份面向嵌入式开发者和RFID技术初学者的资料包聚焦基于STM8S系列单片机的125kHz低频RFID读卡器设计采用分立元件搭建天线与前端调理电路解决ID卡号读取与曼彻斯特解码问题。包内共2个文件以C源码与头文件为主整体仅2KB虽然体积小巧却包含完整的读卡器核心逻辑便于直接学习、移植或嵌入到门禁控制、资产跟踪等项目中。已有1108人浏览学习受到嵌入式爱好者一定关注适合课程设计、毕业设计或实际产品预研参考。源码涵盖RFID信号捕获、曼彻斯特解码、ID卡号处理等关键代码同时涉及STM8S的初始化、I/O配置与定时器资源使用能够帮助读者理解125kHz RFID系统工作原理并掌握从分立元件电路连接到软件解码调试的完整流程为后续功能扩展提供清晰参考。1. 125K RFID 读取链路为什么选 STM8 而不是专用读卡芯片做门禁、考勤或仓库盘点125K RFID 是绕不开的低频方案EM4100 兼容卡占了门禁卡的绝大多数。标题里那串 rfid.rar_125K rfid电路_STM8 125K RF 是一个完整项目的三块线索125K 谐振天线电路、STM8 解调与解码固件、把 ID 送到上位机的输出逻辑。选 STM8 不是因为性能而是这类 64 位只读 ID 解码负载很轻16MHz 的 8 位机就能消化成本比 EM4095 等专用读卡芯片低一个量级。真正的难点在射频前端天线是否谐振在 125K、解调出来的脉冲干不干净、曼彻斯特解码怎么对齐和校验。下文从电路做到门禁匹配熟手可以重点看 3.3 节的解码对齐和 4.3 节的原始脉冲日志排错。2. 125K RFID 电路前端LC 谐振天线、差分驱动与包络解调125K 读卡器电路的核心不是单片机而是天线前端。它要干两件事把 125kHz 载波发射出去给卡片供电再把卡片负载调制引起的包络变化解调成一根数字线。这一级项目里最常见的路子是分立元件射频前端 STM8 定时器测宽先花的钱在几个电容电阻和一个比较器上而不是在专用芯片上。2.1 125K 谐振频率计算与天线参数选型天线线圈在电气上等效为一个电感和并联电容组成 LC 谐振回路谐振频率由 f01/(2π√(LC)) 决定。线圈电感取决于匝数、直径和线径直径 5cm 的空心线圈用 0.2mm 漆包线绕 100 圈电感量大致落在 0.91.2mH配 1.22.2nF 电容就能压在 125K 附近。实际装机后的寄生电容线圈分布电容、走线、三极管结电容会吃掉几十到上百 pF所以理论值只做起点最后一步靠并联小电容微调。# 计算 125K 谐振对应的并联电容 import math f0 125_000.0 for L_mH in (0.68, 0.82, 1.0, 1.2): L L_mH / 1000.0 C 1.0 / ((2 * math.pi * f0) ** 2 * L) print(fL{L_mH:4} mH - C{C*1e9:6.2f} nF)这个脚本就是把谐振公式翻译成数值表唯一可调参数是频率 f0 和电感 L。你手里线圈电感不确定时用电感表量一遍再代进脚本比凭感觉换电容快得多。线圈电感理论电容常用标称组合0.68 mH2.39 nF2.2 nF 220 pF0.82 mH1.98 nF1.8 nF 220 pF1.00 mH1.62 nF1.5 nF 120 pF1.20 mH1.35 nF1.2 nF 150 pF谐振电容优先选 NP0/C0G 材质云母更好X7R 在 125K 下损耗角明显偏大Q 值上不去读距就短。表里的组合都留了并联微调余量调试时在两端并一个 1047pF 电容慢慢加直到读距最大为止。2.2 STM8 驱动 125K 天线的三种 rfid电路 接法天线驱动从简单到复杂有三条路按成本和读距取舍。最省的是纯 GPIO 差分驱动STM8 两个引脚输出互补的 125K 方波各串一个 220Ω 电阻接天线两端LC 并联谐振网络把方波滤成近似正弦。两脚反相等于在天线两端得到 ±VDD 摆幅比单端驱动峰值翻倍很多低成本门禁读卡的 demo 板就是这么做的。注意 STM8 单个 GPIO 持续输出能力有限220Ω 串阻在 3.3V 下约 15mA占空比 50% 的方波已经接近极限别再追求更大的天线电流。驱动方式主要元器件典型读距调试难度适用场景GPIO 差分驱动2×220Ω LC 谐振24 cm低门禁 demo、桌面读卡器NPN 共射放大8050 基极电阻47 cm中低成本但读距要够专用基站前端EM4095 等10 cm 以上低量产和读距一致性优先NPN 接法是 GPIO 经 1kΩ4.7kΩ 进 8050 基极集电极接 LC 并联网络做开关放大读距能到 5cm 以上代价是 125K 开关噪声会串进电源解调部分得单独加磁珠或小 LDO 隔离。专用芯片方案读距和一致性最好但一颗 EM4095 的价格顶好几颗 STM8S003个人项目和原型阶段一般不值得。2.3 解调输出从 RF 载波到 DEMOD 数字脉冲把 ID 从载波里抠出来的这一步就是常说的 RF 数据转换卡片靠近时通过负载调制改变天线阻抗125K 载波包络出现以 256/512μs 为刻度的起伏解调电路把这个包络取出来整形成数字电平。常见做法是二极管半波整流加 RC 低通天线两端经 1N4148要求高点用 1N60 或 BAT54整流1kΩ10nF 滤波时间常数约 10μs远小于半位 256μs然后把包络送进 LM393 和参考电压比较输出 DEMOD 数字脉冲。比较器必须加正反馈电阻做滞回阻值 100kΩ1MΩ 都行目的是防止包络毛刺在阈值附近反复翻转产生抖动边沿。调好的状态是无卡时 DEMOD 恒为高电平有卡时出现干净的方波脉冲串。另外门禁卡绝大多数是 EM4100/TK4100 兼容卡帧格式固定 64 位HID 卡是 26 位 Wiegand 另一套协议下面的解码代码不适用。提示解调元件参数都是起点先示波器挂 DEMOD 看波形再回调参考电压。验收标准只有一条静态高、有卡低、边沿干净。3. STM8 125K 解码固件EM4100 帧格式、曼彻斯特解码与 ID 输出电路把 125K 载波变成一根数字线之后剩下的全在固件里。STM8 要做的无非三件事用定时器记录 DEMOD 每个边沿的时间戳把时间戳还原成曼彻斯特位流再按 EM4100 帧结构校验出 40 位 ID。这段链路里最容易翻车的是第二件事也就是半位对齐。3.1 EM4100 数据帧与 64 位 ID 结构EM4100 的数据率是载波频率除以 64即 125kHz/64≈1953bps一个位元 512μs。曼彻斯特编码约定位 1 的前半周期为高、后半为低位 0 反过来。帧结构固定为 64 位先发 9 个连续 1 做同步头接着 10 组数据每组 4 位数据加 1 位列奇校验最后是 4 位行校验和 1 位停止位。字段位长作用帧头9全 1用于同步和对齐数据组5010 组 × (4 数据位 1 列奇校验位)行校验4每组同一位置的共 10 位做奇校验停止位1固定为 040 位数据按组逐行发完拼出来就是读卡器上显示的 10 个十六进制位。第一组 D00D03 多半是版本号EM4100 常为 0TK4100 也有差异上位机比对 ID 时要分清哪部分是真正的卡号。3.2 用 STM8 定时器捕获边沿间隔解码的原始素材是边沿间隔而不是电平本身。STM8 的 TIM1 输入捕获每检测到一个边沿就把计数器值锁进 CCR1中断里存下时间戳同时翻转捕获极性这样下降沿和上升沿都能抓到。预分频设 1计数频率 16MHz一个 tick 是 62.5ns半位 256μs 对应 4096 tick整位 512μs 对应 8192 tick。/* STM8S003F3 16MHz, DEMOD 接 PB4, TIM1 输入捕获双沿采样 */ #define EDGE_RING 64 volatile uint16_t edge_buf[EDGE_RING]; volatile uint8_t edge_wr 0, edge_rd 0; void TIM1_Init(void) { CLK_PCKENR1 | 0x01; /* 打开 TIM1 时钟 */ TIM1_PSCRH 0x00; TIM1_PSCRL 0x00; /* 预分频 1: 16MHz 计数 */ TIM1_CCMR1 0x01; /* CC1 输入映射 TI1, 不分频 */ TIM1_CCER1 0x03; /* 使能 CC1, 先捕获下降沿 */ TIM1_IER | 0x02; /* 开 CC1 捕获中断 */ TIM1_CR1 | 0x01; /* 启动计数器 */ } INTERRUPT_HANDLER(TIM1_CAP_COM_IRQHandler, 12) { uint16_t now (uint16_t)((TIM1_CCR1H 8) | TIM1_CCR1L); edge_buf[edge_wr 0x3F] now; /* 只存时间戳, 环形缓冲 */ TIM1_CCER1 ^ 0x02; /* 翻转 CC1P, 边沿极性交替 */ TIM1_SR1 (uint8_t)~0x02; /* 清 CC1IF 标志 */ }逻辑上这个 ISR 很薄中断里只写缓冲和翻极性不做任何解码运算曼彻斯特解析全部放主循环。这样即使解码逻辑写错了也不影响边沿采集的实时性。16 位计数器回绕周期约 4.096ms而 EM4100 最长脉冲只有 512μs时间戳差值直接用 uint16 减法不会出错环形缓冲 64 个深度足够主循环慢慢消费。中断向量号按你所用编译器头文件里的 TIM1_CAP_COM 定义为准Cosmic、IAR 和 SDCC 的编号写法可能差 1。3.3 曼彻斯特解码与 ID 输出的最小 C 代码解码的关键是半位对齐。DEMOD 无卡时恒为高卡片进入后第一帧从 9 个 1 开始第一个下降沿恰好落在帧头第 1 位的中间也就是位元的中点。从它开始后面每个边沿间隔要么是 256μs 表示一个半位要么是 512μs 表示两个同电平的半位。位元的第一个半位电平就是这个位的值第二个半位必然与它相反恰好可以作为校验。/* 主循环消费时间戳, 还原成位流 */ static uint8_t half_k 0; /* 已发出的半位数 */ static uint8_t cur_bit 1; /* 帧头第一位固定为 1 */ static uint8_t edge_level 0; /* 首个下降沿之后为低电平 */ void decode_process(void) { while ((uint8_t)(edge_wr - edge_rd) 2) { uint16_t prev edge_buf[edge_rd 0x3F]; uint16_t now edge_buf[(edge_rd 1) 0x3F]; edge_rd; uint16_t d (uint16_t)(now - prev); if (d 9830) { half_k 0; edge_level 0; continue; } /* 512us20%: 帧间隙 */ if (d 3000) { continue; } /* 毛刺, 丢弃 */ uint8_t cells (d 6144) ? 2 : 1; /* 512us 长脉冲 两个同值半位 */ push_half(edge_level, cells); edge_level !edge_level; /* 每个边沿后电平翻转 */ } } /* 每来一个半位调用一次; level 为该半位的电平 */ void push_half(uint8_t level, uint8_t cells) { while (cells--) { half_k; if (half_k 1) { /* 位元第二半: 应和第一半相反 */ if (cur_bit level) { half_k 0; em_sync 0; fpos 0; } } else { /* 位元第一半: 电平即该位值 */ cur_bit level; push_bit(cur_bit); } } }参数上 6144 是 4096 和 8192 的中点用来区分长短脉冲9830 是 8192 加 20% 容差超过就判定为帧间隙并重新同步。HSI 时钟本身的 ±1% 误差对这个判据影响不大。需要强调 polarity 问题如果比较器输出反相DEMOD 初始电平就变成低解出来的 bit 全部取反现象是所有校验都失败。碰到这种情况把 push_half 里的 level 取反一次即可不用改电路。/* 帧解析: 9 个 1 帧头, 然后收 55 位交给奇偶校验 */ static uint8_t run1 0, fpos 0, em_sync 0; static uint8_t frm[55]; void push_bit(uint8_t b) { if (!em_sync) { run1 (b 1) ? (run1 1) : 0; if (run1 9) { em_sync 1; fpos 0; } return; } frm[fpos] b; if (fpos 55) { if (em4100_check(frm)) { /* 校验通过, 40 位 ID 在每组前 4 位 */ } em_sync 0; run1 0; fpos 0; } } uint8_t parity_odd(uint8_t v) { return (0x6996u (v 0x0F)) 1; /* 4bit 奇偶查找表 */ } uint8_t em4100_check(const uint8_t *f) { uint8_t i, b; for (i 0; i 10; i) { /* 列校验: 4 数据 1 校验, 整体为奇 */ uint8_t d (f[i*5]3)|(f[i*51]2)|(f[i*52]1)|f[i*53]; if (parity_odd(d) f[i*54]) return 0; } for (b 0; b 4; b) { /* 行校验: 10 组同一位累加 */ uint8_t v 0; for (i 0; i 10; i) v ^ f[i*5b]; if (parity_odd(v) f[50b]) return 0; } return (f[54] 0) ? 1 : 0; /* 停止位必须为 0 */ }校验函数里 0x6996 是 4 位二进制数的奇偶查找表第 n 位为 1 表示 n 的二进制里 1 的个数是奇数。列校验判断的是 4 个数据位加校验位共 5 位的 1 总数为奇所以直接比较 parity_odd(d) 和校验位是否不同即可行校验同理10 个同位置位先异或成一位再比对。真机上第一次跑通这套代码应该拿一张已知卡号的 EM4100 卡验证门禁卡或测试卡都行。读出 ID 之后如果要复制得用 T5577 这类可写卡写卡是在发射通路做 OOK 调制属于另一段代码但天线和驱动电路是同一套。4. stm8 125K 读卡器联调排错谐振频率、比较器阈值与解码时序硬件和固件分开都正常合在一起读不到卡是常态。排错顺序固定为先确认天线真的在 125K 谐振再看 DEMOD 波形干不干净最后才怀疑解码代码。跳过前两步直接调代码容易在错误的信号上做无用功。4.1 用示波器校准 125K 谐振频率示波器探头挂在天线两端或驱动管集电极对地正常应看到 125kHz 左右的正弦波形幅度稳定不发散。频率偏低就把并联微调电容减小偏高就增加手上没有频率计或示波器时可以用数字万用表交流档测谐振电容两端电压微调电容让电压读数最大这个位置就是谐振点。波形如果出现削顶或明显失真多半是驱动电流太大或者天线 Q 值过高把串阻从 220Ω 加到 470Ω 再测。校准完成后做一次快速验证用手持式频率计或示波器的频率测量功能读取载波频率允许偏差 ±2%。偏差过大时不要只调电容还要检查线圈是不是被旁边的金属外壳或螺丝吸走了磁场这会把等效电感拉偏。4.2 DEMOD 信号噪声与比较器阈值调整解调波形是排查分界线。天线正常但 DEMOD 静态不是高电平问题在比较器这级参考电压设反了或者整流后直流分量太低。10kΩ 电位器把参考电压从 0 往 3.3V 慢慢调调到无卡稳定输出高、卡片靠近时出现方波脉冲为止。脉冲边沿如果有抖动加大正反馈电阻到 1MΩ 量级滞回区间变宽后抗噪能力上升但阈值也会变迟钝实际调到刚好不抖为最优。很多人会把卡片放在金属桌面或金属门框上测试发现读距骤降甚至完全读不到这不是电路坏了。125K 靠磁场耦合金属涡流会吸收天线能量这是物理现象工程上的解决办法是给天线加磁片导磁或者调整天线朝向避开金属面。判断电路本身是否正常的办法很简单把卡垫在 3cm 泡棉上再测。4.3 解码失败排查原始脉冲宽度日志解码不出的第一件事不是改算法而是把边沿间隔打出来。串口 115200 8N1 输出每个 delta 的十六进制值看分布是否清晰分成两簇。/* 调试模式: 主循环把相邻边沿间隔发到串口 */ while (1) { if ((uint8_t)(edge_wr - edge_rd) 2) { uint16_t d edge_buf[(edge_rd1)0x3F] - edge_buf[edge_rd0x3F]; edge_rd; uart_puthex16(d); /* 逐字节 putchar, 别用完整 printf 库 */ uart_putc(\r); uart_putc(\n); } }正常数据里应该看到 0x10004096和 0x20008192两个明显的聚集值。如果只有一簇 8192 且持续不断说明抓到的是干扰或卡片帧间隙同步头没有正确进入如果数值非常散优先怀疑比较器抖动其次才是代码。串口上位机报数据连接错误时多数情况不是读卡器问题而是 USB-TTL 没共地、电平不匹配STM8 5V 对 3.3V 串口或波特率偏差先做回环测试确认链路。现象大概率原因按顺序检查DEMOD 静态低电平比较器参考电压反相调电位器让无卡输出高串口无任何数据天线没起振或 DEMOD 没接对示波器逐级看天线、整流、比较器有数据但校验全错解码极性反或首边沿不对push_half 里电平取反确认首个下降沿读距小于 1cmQ 值低或驱动电流不足换 C0G 电容、加大驱动、避开金属面时读时不读阈值临界或接触不良加大滞回、补焊天线两端5. 把 125K RFID 读卡器做成产品门禁匹配、串口输出与低功耗代码跑通能出 ID 之后剩下的工程化问题有三个白名单放在哪、串口协议怎么定、电池供电怎么降功耗。单机版本门锁、展柜白名单直接放 STM8 内部 EEPROM 就够多读卡器系统则让 MCU 只负责实时上送 ID匹配统一放上位机这样换卡不用逐个刷读卡器。/* EEPROM 存 8 组白名单(每组 5 字节), 连续两次读到同一 ID 才动作 */ static uint64_t last_id 0; static uint8_t same_cnt 0, i, d; if (id last_id) { if (same_cnt 2) { for (i 0; i 8; i) { if (memcmp((void*)wl[i], idb, 5) 0) { GPIOB-ODR | (1 5); /* 开锁输出 */ for (volatile uint32_t t 0; t 20000; t); /* 粗延时 */ GPIOB-ODR ~(1 5); break; } } same_cnt 0; } } else { last_id id; same_cnt 1; }连续两次判断是为了防误触发比如卡片在感应区边缘抖动时不会立刻动作。memcmp 比较的 5 字节是 40 位 ID 的二进制字节序注意和串口上位机显示的十六进制字符串对齐EM4100 的版本号那 4 位通常不是卡号主体。产品上延时不要用这种空转循环改成定时器或使用 WFI 等待空转只是演示逻辑。串口协议建议输出固定行格式IDXXXXXXXXXX\r\n十个十六进制位方便上位机按行解析。低功耗处理对门锁这类电池设备很关键。主循环改成 AWU 定时唤醒周期 300ms唤醒后开射频 80ms 等待一张卡完整的 64 位帧没有有效帧就再次进入 HALT。注意 HALT 模式下 GPIO 输出状态保持不变天线驱动引脚必须显式关掉否则射频部分静态电流还在走。整机电流取决于比较器和 LDO 的静态功耗实测通常能从常开的 8mA 压到 23mA 级别。烧录用 ST-Link 或市售兼容 STM8 编程器都可以注意 SWIM 接口三根线 SWIM、RST、GND 的顺序接反会识别不到芯片。最后留一个调 125K 读卡器最有价值的习惯每次改版前把同一张卡、同一个摆放位置解出的 40 位 ID 和原始边沿间隔日志各存一份做回归基准。读距或误码率一有变化先对比这两样数据再动电路比重新抓一遍波形省时间。本文还有配套的精品资源点击获取
返回列表