STM8自制125K RFID读卡器:谐振天线与EM4100解码

STM8自制125K RFID读卡器:谐振天线与EM4100解码 简介面向嵌入式初学者与RFID应用开发者这份方案围绕125kHz RFID读卡器展开使用STM8S系列单片机搭建分立元件电路实现卡片ID读取与曼彻斯特解码。压缩包共2个文件含一个C源码文件与一个头文件整体仅2KB代码覆盖初始化、信号捕获、曼彻斯特解码及ID读取等核心环节便于直接阅读或移植。已有1108人学习下载。通过学习该示例可以掌握STM8S的I/O配置、定时器捕获、曼彻斯特编码原理与解码流程以及低频RFID通信的软硬件协同调试思路。此方案适用于门禁、动物追踪等125kHz低频场景是一份可用于快速上手的参考模板能帮助理解从天线信号到最终ID输出的完整链路。1. 125K RFID电路配STM8是读EM4100最顺手的组合如果你只想做一个能读门禁卡ID的装置市面上几十块的125K读卡模块拿来即用。但一旦想把读卡距离从2cm拉到5cm、想用自己的PCB天线、想把ID直接送到MCU做二次处理成品模块的黑盒就成了瓶颈——天线谐振点调不了解码逻辑也看不见。用STM8搭整套125K RFID电路恰好是成本和解耦难度都比较低的一条路STM8主频16MHz定时器分频后能精确输出125K载波EM4100这类125K ID卡的数据率只有约2kbps留给软件解码的时间余量非常充裕。这篇文章会把LC谐振驱动、EM4100帧解析和天线参数调整这三块拆开讲每一步都给出可直接照做的配置和代码。2. 电感耦合与谐振STM8输出125K载波的驱动电路2.1 125K天线为什么必须工作在LC谐振点125K RFID的能量来自读取器天线的交变磁场而天线本质上就是一匝或多匝电感。把电感直接接到一个方波驱动上电流会被电感的内阻限制住产生不了足够的磁场强度。常见做法是让天线线圈和一个电容组成并联LC谐振回路把驱动能量集中到谐振频率上在电感两端形成几十伏到上百伏的正弦电压磁场强度也随之放大Q倍。以典型的天线电感L1mH为例谐振频率f0125kHz需要的并联电容C满足f0 1 / (2π√(LC))代入得到C≈1.62nF。实际绕制的线圈电感量存在±10%误差所以电容很少用单只而是用1.5nF并联一个20pF~100pF的可调电容或者用两只固定电容并联后实测调整。谐振不准的直接表现是天线两端电压明显偏低、读取距离缩水严重时卡贴上才能读到ID。2.2 STM8定时器输出125K方波的配置STM8S003F3的主时钟HSI是16MHzTIM1是16位高级定时器输出125kHz方波只需要把计数周期设为128拍16MHz/128125kHz。占空比50%对应比较值64。这样PWM输出的基波正好落在LC谐振点上谐波分量被谐振回路滤掉天线上的波形近似正弦。// STM8S003F3TIM1_CH1(PD2)输出125kHz PWM驱动天线NMOS void rf_carrier_init(void) { CLK-CKDIVR 0x00; // 主时钟16MHz禁止分频 PD-DDR | 0x04; // PD2设为输出 PD-CR1 | 0x04; // PD2推挽输出 PD-CR2 ~0x04; // 不开启斜率控制 TIM1-CR1 0x00; // 停表再配置 TIM1-PSCRH 0x00; TIM1-PSCRL 0x00; // 预分频1即不分频 TIM1-ARRH 0x00; TIM1-ARRL 127; // 重装值127计数0~127共128拍 TIM1-CCR1H 0x00; TIM1-CCR1L 64; // 比较值64占空比50% TIM1-CCMR1 0x68; // PWM模式1输出比较预装载使能 TIM1-CCER1 0x01; // CH1输出使能 TIM1-BDTR 0x80; // TIM1主输出MOE使能 TIM1-CR1 0x01; // 启动定时器 }ARR127、CCR164这两个值是整套电路的基础参数改时钟频率时必须同步重算。如果MCU换了主频不是16MHz的型号先把CLK-CKDIVR和TIM1预分频组合成16MHz再沿用这个配置即可。需要确认波形时用示波器看PD2引脚应该得到幅度接近3.3V、占空比50%的方波。方波有轻微振铃不影响后级因为LC回路只对基波起增益。2.3 从PWM到天线的单管驱动与检波链路驱动级最稳妥的简单方案是一个NMOS做开关把LC谐振网络的下端对地导通和关断。PWM信号经100Ω电阻接到MOS管栅极栅极对地再并一个10kΩ泄放电阻防止关断时栅极悬空。天线线圈上端接电源下端接MOS管漏极谐振电容并联在线圈两端。MOS管选2N7002不够时换AO3400或SI2302这类低导通电阻的小型NMOS5V/12V供电下都能工作。接收部分是另一个重点。标签通过负载调制改变线圈阻抗这个变化反映在LC回路两端电压的微小起伏上。从天线抽头取出信号后经过一只BAT54S肖特基二极管做半波整流、再经10nF电容滤波得到包络信号。包络幅度只有几十到几百毫伏必须经过比较器整形后才能进STM8的GPIO。LM393比较器的一路输入接包络另一路用两个电阻分压设定比较阈值。比较器输出端接一只10kΩ上拉电阻到3.3V再串联1kΩ限流电阻后接到STM8的PA1。器件参数说明天线线圈0.2mm丝包线60~80匝目标电感1mH左右谐振电容1.5nF 可调电容并联在LC两端驱动NMOSAO3400/SI2302Vds耐压25V检波二极管BAT54S压降约0.3V滤波电容10nF滤除125K载波比较器LM393输出接PA1比较器输出低电平表示包络低于阈值高电平表示包络高于阈值。卡不在场时天线空载包络基本稳定在一个直流电平上卡进入场区后负载调制让包络出现幅度调制比较器输出即为还原出的基带数据流这一步做完软件解码的输入信号就准备好了。3. EM4100的64位帧结构STM8该在哪一步还原ID3.1 曼彻斯特编码和数据率125K ID卡里最常见的是EM4100兼容芯片。它以RF/64作为编码时钟125kHz/64≈1953Hz所以每位周期约512μs。编码方式为曼彻斯特编码位周期前半段有载波、后半段无载波表示逻辑0反过来前半段无载波、后半段有载波表示逻辑1。比较器输出的数字信号里逻辑1对应低-高的跳变逻辑0对应高-低的跳变。EM4100完整帧长64位9位全1起始头10组数据每组5位4位数据1位偶校验随后4位列校验位最后1位停止位0。9个起始1也被称为同步头解码器先识别这9个1然后从第10位开始收集数据。帧格式是固定的所以解码的核心是把曼彻斯特编码还原成原始位流再做行列校验。3.2 包络检波后的信号与STM8解码采样比较器输出的基带信号是512μs一个位周期而STM8主频16MHz处理这个速度非常轻松。常见做法是把PA1配置成外部中断检测到第一个上升沿时启动一个定时器用定时中断在每位周期内固定采集多个点用多数表决判断该位是0还是1。这里有一个细节EM4100上电后第一个发送的位是逻辑1而逻辑1的波形是前半段无载波、后半段有载波。卡刚进入场区时比较器输出为低第一个上升沿必然出现在第一个逻辑1的位周期中点也就是第9个起始1的第一个上升沿。以这个沿作为采样启动点天然和位边界对齐不需要额外做复杂的位同步。采样周期取64μs正好每个位周期采样8次。每次定时中断读一次PA18次累加后如果高电平次数大于低电平次数该位判为1否则为0。8次过采样对比较器输出边沿处的抖动有抑制作用比单次采样稳定得多。3.3 过采样解码状态机与帧校验下面这段代码实现过采样解码并把原始位按位号存入数组raw_bits[64]。// TIM2 每64us中断8次采样还原1位64位收满后置标志 #define MAX_BITS 64 volatile uint8_t raw_bits[MAX_BITS]; volatile uint8_t bit_cnt 0; volatile uint8_t rx_done 0; void TIM2_UPD_ISR(void) __interrupt(13) { static uint8_t sample_cnt 0; static uint8_t high_cnt 0; uint8_t lvl (GPIOA-IDR 0x02) ? 1 : 0; // 读PA1 high_cnt lvl; if (sample_cnt 8) { // 一个位周期结束 if (bit_cnt MAX_BITS) { raw_bits[bit_cnt] (high_cnt 4) ? 1 : 0; } sample_cnt 0; high_cnt 0; if (bit_cnt MAX_BITS) { TIM2-CR1 ~0x01; // 关闭定时器等待主循环处理 rx_done 1; } } }PA1电平读取用的是GPIOA的IDR寄存器0x02对应PA1这一位。high_cnt统计8次采样中高电平的次数等于4及以上判1。启动这个定时器的时机放在PA1的外部中断里检测到首个上升沿后先延时约128μs再启动TIM2让采样窗口落在位周期的中后段避免采到跳变沿附近的不稳定电平。主循环收到64位后做帧校验// 校验EM4100帧并输出40位ID返回1表示校验通过 uint8_t em4100_check(const uint8_t *raw, uint8_t *id) { uint8_t i, j, row_par, col_par[4] {0,0,0,0}; for (i 0; i 9; i) if (raw[i] ! 1) return 0; // 9个起始1 for (i 0; i 10; i) { // 10组4数据位1校验位 row_par 0; for (j 0; j 4; j) { row_par ^ raw[9 i * 5 j]; // 组内偶校验 col_par[j] ^ raw[9 i * 5 j]; // 列偶校验 } if (row_par ! raw[9 i * 5 4]) return 0; } for (j 0; j 4; j) if (col_par[j] ! raw[59 j]) return 0; // 4位列校验 if (raw[63] ! 0) return 0; // 停止位必须为0 for (i 0; i 10; i) id[i] (raw[9 i * 5] 3) | (raw[10 i * 5] 2) | (raw[11 i * 5] 1) | raw[12 i * 5]; return 1; }位索引需要对照帧格式看raw[0]到raw[8]是起始头第i组数据的4个数据位在raw[9i*5]到raw[12i*5]组校验位在raw[13i*5]10组全部处理完后raw[59]到raw[62]是4位列校验raw[63]是停止位。校验通过后40位ID被拆成10个字节存入id数组。需要注意EM4100的40位数据并不全是卡号不同厂家的卡可能在其中编码了厂商代码和卡号具体位定义要看发卡方的格式。4. 谐振电容、Q值和比较器阈值125K读卡距离的3个必调参数4.1 谐振电容的计算和微调谐振电容的理论值按C1/(4π²f²L)计算但实际绕制的线圈电感量往往和估算值差不少。上电后用示波器探头用10x挡减小探头电容影响看NMOS漏极波形正常谐振时漏极电压幅度最大、波形呈干净正弦。若波形幅度偏低且顶部塌陷多半是电容偏离谐振点。常见做法是先用万用表电感挡测实际电感量再按公式算电容值。如果手头没有电感表就准备一组电容从1.0nF到2.7nF逐只替换每次看漏极波形幅度。幅度最大的那一个就是当前天线的最佳谐振电容。串一个几百欧电阻在供电回路里可以限制谐振电压避免MOS管过压击穿。4.2 Q值与读取距离、解码成功率的取舍谐振回路的品质因数Q决定了两个互相矛盾的指标Q越高线圈两端电压越高、读取距离越远但Q越高带宽越窄负载调制产生的边带会被压掉解码成功率反而下降。125K标签负载调制的数据率约2kbps边带范围大约±2kHz所以天线回路带宽至少要4kHz。Q与带宽的关系是Bf0/Q125kHz下带宽4kHz对应Q≈31。Q值带宽读取距离表现解码稳定性1012.5kHz很近通常2cm非常稳定206.25kHz中等2~3cm稳定304.17kHz较远3~4cm多数情况下稳定502.5kHz最远但边带衰减大卡位略有偏斜就解不出控制Q值的方法是给LC回路并联一个阻尼电阻。电阻越小Q越低。一般从470kΩ开始往下试直到读取距离和解码成功率折中最合适。还有一个细节天线附近有金属或人体手靠近时等效Q会明显下降所以封装后必须重新检查漏极波形幅度。4.3 比较器阈值和迟滞对解码的影响LM393的另一个输入是分压电阻设定的阈值电压。阈值设得太高包络的无载波部分仍然高于阈值比较器输出一直是高电平解码全是1设得太低噪声会被当成有效信号。正确做法是先不刷卡看比较器输出的噪声基底再把阈值调到比噪声高大约200mV。比较器还必须加正反馈迟滞否则包络在阈值附近抖动时输出会反复翻转采样到的全是毛刺。在LM393的输出端和同相输入端之间接一只100kΩ反馈电阻就构成约几十毫伏的迟滞窗口。如果实际波形边沿仍有抖动把迟滞电阻降到47kΩ。排查顺序建议是先确认载波频率、再调谐振、再调阈值。很多rfid数据连接错误的问题出在阈值设错导致输出波形失真而不是后续的通信环节。你也可以串口打印解码得到的原始bit数组如果卡在同一个ID上反复出现某几位翻转优先怀疑的就是比较器阈值和采样相位这两个地方。5. 验证与进阶把ID稳定读出来前先看这4个波形5.1 载波、包络、比较器输出三路波形对照接好电路后不要急着解码用示波器同时看三个点PD2的PWM输出、检波电容两端的包络波形、LM393的输出。三路波形必须满足三个对应关系PWM稳定在125kHz占空比50%包络在无卡时是一条平直线刷卡时出现明显的上下起伏起伏周期对应约500μs比较器输出的数字波形方波干净上升沿和下降沿没有反复抖动。包络波形如果只是微微抖动而不是明显的整幅起伏说明检波链路增益不够或阈值太高比较器输出如果出现连续窄脉冲说明迟滞不够。这三路波形对齐后解码代码不该有大问题。5.2 连续读5次取一致的软件过滤就算硬件调整到位读卡距离边缘处仍然可能出现偶发的错码。最简单的过滤办法是连续解码多次取一致结果才认为是有效ID。配合rx_done标志位每收到一帧校验通过后启动新一轮采集连续3到5次ID相同再对外输出。uint8_t buf[5][10]; // 5轮ID uint8_t valid_cnt 0; if (rx_done) { rx_done 0; if (em4100_check(raw_bits, buf[valid_cnt])) { valid_cnt; if (valid_cnt 5) { // 5轮全部相同才认为ID有效 if (memcmp(buf[0], buf[1], 10) 0 memcmp(buf[0], buf[2], 10) 0 memcmp(buf[0], buf[3], 10) 0 memcmp(buf[0], buf[4], 10) 0) { // 输出buf[0]给上层应用 } valid_cnt 0; } } else { valid_cnt 0; } bit_cnt 0; TIM2-CR1 | 0x01; // 重新开启解码定时器 }注意每轮解码之间要清掉bit_cnt再启动定时器否则下一帧数据会从64位之后继续写入破坏数组边界。判定ID有效后再通过串口发送串口波特率建议用9600和125K解码互不干扰。5.3 天线线圈引线和磁棒方向的两个易错点最后提两个容易被忽略的工程细节。天线线圈到驱动板之间的引线如果超过10cm一定要用双绞线或者屏蔽线并且两端各加一个几十皮法的滤波电容到地否则引线寄生电感会改变谐振频率同时线上拾取的噪声直接恶化包络信号。另一个是磁棒天线的方向性带磁棒的线圈磁场集中在轴向卡的线圈需要和磁棒平行摆放才能获得最大耦合装外壳时不要随手横竖不分地固定。验证时可以拿着卡在读取器上方缓慢平移画出读取区域的边界如果某个朝向完全无响应先调磁棒姿态再考虑调Q。本文还有配套的精品资源点击获取