ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

FM17XX读写芯片参考代码详解:ISO14443A/B读卡流程与移植要点

FM17XX读写芯片参考代码详解:ISO14443A/B读卡流程与移植要点 简介FM17XX系列非接触式读卡芯片的参考代码包面向嵌入式开发及智能门禁、读卡设备调试人员解决ISO14443A/B协议下寻卡和底层驱动实现问题。资源内含基于STM32F10x的完整工程涵盖TypeA/TypeB寻卡流程、PcdRequest等函数调用、SPI读写时序、ADC与TIM外设配置并附带常见门锁电机控制示例便于理解读卡逻辑与硬件协同。整个压缩包共259个文件4.33MB以C源码、头文件、汇编启动文件及编译中间文件为主其中50个.h和43个.c构成主要代码框架另含工程配置文件、hex烧录文件及文档适合直接参考或二次开发。目前已有1173人浏览学习门禁/读卡项目开发者可借此快速上手FM17XX系列芯片缩短底层驱动调试周期。1. 项目概述与核心价值1.1 这个参考代码到底解决什么问题FM17XX系列是复旦微电子推出的非接触式读写卡芯片常见型号包括FM1702SL、FM1712SL、FM1722SL等主要面向13.56MHz频段的RFID/NFC应用。这颗芯片的底层操作并不复杂但初次接触的人很容易被寄存器配置、时序控制、防碰撞流程这些细节搞懵。我最初接手这个项目时手头只有一份芯片数据手册和几段零散的示例代码光是把一张ISO14443A类型的M1卡读到UID就折腾了两三天。后来把读卡流程理顺、把参考代码整理成可复用的模块后才发现很多坑其实是可以提前避开的。这次分享的FM17XX读卡参考代码覆盖ISO14443A和ISO14443B两种协议类型包含从芯片初始化、天线开启、请求卡片、防碰撞、选卡到读取UID/ATQB的完整流程。适合三类人一是刚接触FM17XX系列芯片、想快速跑通读卡功能的嵌入式开发者二是需要把FM17XX移植到不同MCU平台STM32、51、GD32等的工程师三是做门禁、读卡器、小额支付终端等项目需要理解NFC底层交互细节的软硬件开发人员。1.2 为什么选择FM17XX而不是其他读卡芯片市面上13.56MHz的读卡芯片方案很多比如NXP的RC522、RC522A、RC523恩智浦的PN532复旦微的FM17XX系列等。从成本角度看FM17XX在国内市场有优势供货稳定价格比同类进口芯片低不少。从功能上看FM17XX支持ISO14443A/B双协议部分型号还支持ISO15693这让它在门禁、读卡器等对成本敏感的产品中非常有竞争力。不过FM17XX和RC522的寄存器地址并不完全一致虽然都是SPI/I2C/串口接口但内部寄存器映射有差异。如果之前写过RC522的驱动迁移到FM17XX时不能直接替换需要对照数据手册逐项核对寄存器操作。这也解释了为什么网上很多RC522的例程不能直接用于FM17XX——指令集和寄存器定义确实不同。另外FM17XX的模拟参数配置比如发射增益、接收灵敏度是通过一组特殊的寄存器配置的这些参数的调整对读卡距离和稳定性影响很大后面我会详细展开。2. 整体设计思路与协议背景2.1 ISO14443A/B的关键差异ISO14443是13.56MHz非接触式IC卡的国际标准分为A、B两种类型。两者在物理层调制方式、编码方式、帧格式上都有区别。Type A采用ASK调制100%调制深度Miller编码通信速率通常为106kbpsType B采用ASK调制10%调制深度NRZ编码同样支持106kbps及更高速率。简单类比Type A像是对讲机里说话的人信号通断明显但容易受干扰Type B像戴着耳机听音乐信号平稳但幅度变化小对接收电路的信噪比要求更高。FM17XX设计上同时支持这两种类型但芯片在初始化时需要分别配置对应的寄存器。我们常见的门禁卡大多是Type A的M1卡如S50、S70公交卡和部分银行IC卡则可能采用Type B协议比如第二代身份证、部分CPU卡。所以一套代码能不能同时兼容A/B直接决定读卡器能否覆盖更多应用场景。2.2 读卡流程的抽象分层参考代码的核心思路是把读卡操作抽象成几个独立的步骤每一层职责单一方便复用和调试。我建议把整个流程划分为四层物理层负责SPI/I2C/UART通信读写FM17XX寄存器。命令层封装FM17XX的请求、防碰撞、选卡等指令对应ISO14443规定的命令帧。协议层处理卡片返回的状态字节、CRC校验、位冲突检测等。应用层最终拿到卡片序列号UID或ATQB或数据块内容供业务逻辑使用。这种分层方式最大的好处是当底层MCU更换时只需要改物理层当需要新增对某种卡片的支持时只需要在命令层增加对应的命令帧构造与解析函数。参考代码里我正是按照这个思路组织的实测下来维护成本比单个大而全的读卡函数低很多。2.3 为什么参考代码选择SPI作为默认接口FM17XX支持SPI、I2C和UART三种接口我提供的参考代码默认使用SPI接口主要原因是SPI通信速率高、实现简单、占用引脚少SCK、MOSI、MISO、CS即可。在多数MCU平台上SPI外设都是标配硬件SPI或者软件模拟SPI都能快速跑通。如果项目里SPI总线被别的设备占用也可以考虑I2C但I2C速率通常限制在400kHz以下对防碰撞这种需要快速响应的场景略有压力。UART接口则适合用串口透传方式控制芯片但需要额外的板级配套。参考代码里提供了SPI读写寄存器的底层函数协议部分不依赖具体MCU的SPI寄存器所以移植时只需要替换几个底层宏和字节收发函数上层代码可以原封不动。3. 核心细节解析与实操要点3.1 初始化阶段容易被忽略的寄存器FM17XX的初始化不只是配置几个通信寄存器那么简单。我见过不少人在初始化时只设置Mode寄存器、TMode寄存器和Timer寄存器结果天线能打开但读卡距离极近或者根本读不到卡。这里有几个关键点要特别注意。首先FM17XX内部有发送器和接收器发送器输出由TxControl寄存器控制天线驱动由所谓的“Analog Select”寄存器控制。中文数据手册里通常建议在初始化时按顺序写入一组固定模拟参数这些参数决定了发射信号的波形和接收放大倍数。以FM1702SL为例关键的模拟配置寄存器和推荐值如下寄存器推荐值作用说明RegChannel0x0E设置发射通道和接收通道的功率/增益RegGsNHigh0x00控制天线输出高压侧驱动强度RegCWGsP0x32控制天线输出低压侧驱动强度RegModWidth0x26设置调制脉冲宽度影响Type A调制特性RegRxThreshold0x55接收器比较器阈值影响灵敏度RegDemod0x24解调器配置影响信号解调质量这些值直接照抄RC522的初始化值是不行的因为FM17XX的寄存器地址和位定义有差异。比如ModWidth寄存器在RC522里地址是0x24但在FM17XX里同样是0x24但位宽含义有所区别。所以建议以FM17XX的数据手册为准最好用逻辑分析仪观察天线信号波形调节GsN和GsP使ASK调制深度符合规范。3.2 ISO14443A读卡的完整请求序列Type A协议的读卡流程严格遵循“请求-防碰撞-选卡”三步曲。参考代码里分别封装了以下函数fm17xx_request_a()发送REQA命令0x26进入Type A识别模式。卡片在有效场内会应答ATQA2字节。fm17xx_anticoll_a()发送防碰撞命令0x93 0x20读取4字节UID若有多张卡同时在场会检测到冲突位返回冲突位置。fm17xx_select_a()发送选卡命令0x93 0x70完成选卡卡片返回SAK确认。这里有个细节防碰撞命令分为两层。第一级使用级联标签如0x93处理4字节UID的第一段0x95处理第二段0x97处理第三段。常见的M1 S50卡UID是4字节所以只用0x93即可但一些CPU卡或者双界面卡的UID可能是7字节或10字节就需要级联处理。参考代码里实现了完整的级联逻辑可以应对不同长度的UID。实操中发送防碰撞命令后CCCRC校验可以置0由硬件自动计算也可以手动计算。FM17XX有硬件CRC模块在寄存器配置正确的前提下软件只需将要发送的数据帧写入FIFO然后启动发送硬件会在帧尾自动附加CRC。这一点非常方便省去了软件计算CRC_A的时间和代码量。3.3 ISO14443B读卡的关键差异Type B协议与Type A完全不同。Type B通过REQB命令0x05启动通讯卡片回复ATQB其中包含PUPI类似于UID、协议信息和厂商信息。读取ATQB的过程不像Type A那样有“防碰撞”命令而是通过参数来区分不同卡片。FM17XX对Type B的支持需要在初始化时把Mode寄存器中的ProtocolType设置成Type B模式。参考代码里通过fm17xx_set_protocol(FM17XX_PROTOCOL_ISO14443B)切换。随后发送REQB命令后从FIFO读取ATQB数据帧。ATQB长度通常为12字节含CRC但也可能因为厂商数据扩展而更长所以读取FIFO时要根据接收长度寄存器动态判断不能固定只读12字节。这里有个坑Type B的传输速率虽然也是106kbps但其帧格式是SOF数据EOFSOF和EOF的时序与Type A完全不同。FM17XX内部解调器能自动识别但寄存器里有个RxNoise位如果环境中噪声较大需要调整接收阈值寄存器。我在实际测试中靠近电机、电源适配器这些强干扰源时Type B的读卡成功率明显下降后来把RxThreshold调高了一档情况明显改善。3.4 天线匹配与EMC设计FM17XX的读卡距离和稳定性很大程度上取决于天线设计。参考代码只能保证芯片逻辑正确天线匹配不好照样读卡失败。FM17XX典型的天线是PCB线圈或铜绕线圈并联一个谐振电容使13.56MHz频率谐振。我这里给出一个常用的匹配计算方式天线电感L通过阻抗分析仪测得比如常见的PCB天线测得约1.4μH那么并联谐振电容C由公式 f 1 / (2π√(LC)) 推导C ≈ 1 / (4π² × f² × L)。对于13.56MHzC ≈ 1 / (39.48 × (13.56×10⁶)² × 1.4×10⁻⁶) ≈ 98.7pF实际取100pF即可。但要注意这个电容需要考虑FM17XX的输出电容和PCB寄生电容所以最终取值可能需要微调。调试时用网络分析仪或示波器观察天线两端波形调整到正弦波幅度最大、波形无畸变就好。另外天线走线尽量远离MCU时钟线和电源线最好铺地隔离。FM17XX的模拟电源和数字电源要分开避免数字噪声耦合到天线。我在项目板上电源设计比较随意结果发现读卡距离从4厘米掉到1.5厘米后来加了磁珠和100nF的电容滤波才恢复正常。4. 实操过程与核心环节实现4.1 硬件连接准备以STM32F103和FM1702SL为例SPI接线如下FM1702SL引脚STM32F103引脚说明SCLKPA5SPI时钟MOSIPA7主发从收MISOPA6主收从发CSPA4片选低有效RSTPB0复位输入IRQPB1中断输出可不接FM1702SL工作电压3.3VSTM32F103也可3.3V供电注意不要直接用5V电平驱动SPI以免损伤芯片。如果MCU是5V供电需要加电平转换。硬件连接完毕后上电复位时序也需要注意。FM1702SL上电后需要至少10ms稳定时间然后拉低RST再释放等待芯片Ready信号。参考代码里实现了软件延时保证时序稳定。4.2 初始化代码的工程化实现参考代码里将初始化函数命名为fm17xx_init()内部做了四件事复位芯片、配置通信模式、设置模拟参数、开启天线。关键片段如下uint8_t fm17xx_init(void) { fm17xx_reset(); /* 硬件复位 */ fm17xx_cmd(0x01); /* 软件复位命令 */ fm17xx_write_reg(RegMode, 0x3F); /* 设置基本模式 */ fm17xx_write_reg(RegTxControl, 0x83); /* 开启天线 */ /* 写入一组推荐模拟参数 */ fm17xx_write_reg(RegChannel, 0x0E); fm17xx_write_reg(RegGsNHigh, 0x00); fm17xx_write_reg(RegCWGsP, 0x32); fm17xx_write_reg(RegModWidth, 0x26); fm17xx_write_reg(RegRxThreshold, 0x55); fm17xx_write_reg(RegDemod, 0x24); fm17xx_set_protocol(FM17XX_PROTOCOL_ISO14443A); return 0; }需要注意的是reg_config顺序不能乱有些寄存器必须在芯片空闲时写入。比如设置协议类型要在开启天线之前完成否则可能出现配置失败。另外软件复位命令0x01执行后需要等待至少1ms再写其他寄存器不然芯片可能还没准备好。4.3 ISO14443A读卡主流程实现下面这段代码实现了完整的A卡读UID流程可以直接作为参考uint8_t fm17xx_read_card_a(uint8_t *uid) { uint16_t atqa; uint8_t len; /* 请求 Type A 卡片 */ if (fm17xx_request_a(atqa) ! 0) return 0; /* 防碰撞读取 UID 级联1 */ if (fm17xx_anticoll_a(uid) ! 0) return 0; /* 选卡 */ if (fm17xx_select_a(uid) ! 0) return 0; return 1; }fm17xx_request_a发送REQA后读取FIFO返回的ATQA数据。需要注意FM17XX接收到的数据都带有CRC需要验证CRC正确性。硬件接收时如果开启CRC校验错误帧会被硬件丢弃但我们仍然要检查状态寄存器中的错误标志位。防碰撞函数中如果检测到冲突参考代码会返回冲突位coll_pos接着可以做多卡处理。但大多数应用只需要读取一张卡所以简化为只要有冲突就返回失败由上位机提示用户移开多张卡。4.4 ISO14443B读卡流程实现Type B的流程相对简单参考代码如下uint8_t fm17xx_read_card_b(uint8_t *atqb) { uint16_t len; uint8_t buf[32]; fm17xx_set_protocol(FM17XX_PROTOCOL_ISO14443B); /* 发送 REQBAFI0x00参数0x08单卡请求 */ buf[0] 0x06; /* 长度字节 */ buf[1] 0x00; /* 首字节未用 */ buf[2] 0x08; /* REQB */ buf[3] 0x00; /* AFI */ buf[4] 0x08; /* PARAM */ fm17xx_transceive(buf, 5, 0); /* 发送并接收 */ len fm17xx_read_fifo(atqb, 32); if (len 0) return 0; /* 在这里可以解析ATQB中的PUPI前4字节 */ return 1; }这里有一个关键点Type B的REQB帧长度字节len是包括自身字节和后续所有字节的所以长度应为5示例中0x06是意味着到CRC之前的长度。实际上ISO14443B的帧格式比较繁琐需要仔细对照协议文档。参考代码简化了长度计算读者可根据自己需求调整。接收到的ATQB数据从FIFO读出后需要判断帧是否以CRC正确结束。FM17XX会在状态寄存器中报告CRC错误如果错误一般需要重新发送REQB。4.5 移植到其他平台的注意事项参考代码的底层函数只有以下几个与平台相关fm17xx_spi_write_byte(uint8_t data)fm17xx_spi_read_byte(void)fm17xx_cs_low()/fm17xx_cs_high()fm17xx_delay_us(uint32_t us)只要实现这四个函数整个协议栈就能跑起来。以51单片机为例可以这样软件模拟SPIvoid fm17xx_spi_write_byte(uint8_t data) { for (uint8_t i 0; i 8; i) { SCLK 0; if (data 0x80) MOSI 1; else MOSI 0; data 1; SCLK 1; } }软件模拟SPI的速率不能太快FM17XX最大SPI时钟一般为10MHz但软件模拟建议控制在1MHz以下否则可能因为时序不稳定导致读写错误。实测STM32硬件SPI的2MHz频率没有问题4MHz也行但再高就可能出现偶发读错寄存器的问题。5. 常见问题与排查技巧实录5.1 读不到卡或读卡距离极近这个问题占我遇到的故障中最常见的七成以上。排查思路按优先级排列现象可能原因解决方法完全读不到卡天线未开启检查TxControl寄存器Bit1必须为1完全读不到卡SPI通信错误用示波器看SPI时序确认CS/SCK/MOSI电平正确读卡距离小于1cm模拟参数配置不当检查RegCWGsP和RegGsNHigh尝试增大发射功率读卡距离小于1cm天线谐振频偏用网络分析仪测天线谐振点调整谐振电容间歇性读卡失败电源纹波过大在FM17XX电源端加100nF去耦电容另外一个常见低级错误是FM17XX的复位引脚没有正确释放芯片一直处于复位状态。上电后要确保RST为高电平至少保持延时后再开始通信。5.2 读A卡正常但读B卡失败如果代码同时适配A/B协议读A卡正常切换B卡后始终失败多半是协议切换不彻底。FM17XX并不是自动识别A/B卡的必须要在切换时重新配置对应协议相关的寄存器。有些工程师只改了Mode寄存器但没有重新设置发射控制、接收配置等项导致B卡信号调制解调不匹配。检查参考代码中的fm17xx_set_protocol函数确保它写入了如下寄存器void fm17xx_set_protocol(uint8_t proto) { if (proto FM17XX_PROTOCOL_ISO14443B) { fm17xx_write_reg(RegMode, 0x3F); /* 重置模式 */ fm17xx_write_reg(RegTxControl, 0x80); /* 关闭天线 */ /* 设置B类相关寄存器 */ fm17xx_write_reg(RegBitFraming, 0x00); /* 其他B类专属配置 */ fm17xx_write_reg(RegTxControl, 0x83); /* 重新开启天线 */ } else { /* A类配置 */ } }记得在切换协议时关闭天线改完配置后再重新开启否则芯片状态可能残留导致新协议帧无法正常发送。5.3 FIFO溢出导致数据丢失当卡片返回的数据超过FIFO容量64字节时FM17XX会产生FIFO溢出中断。如果读取过程中没有及时清空FIFO后面的数据会被丢弃。这通常发生在读取ATQB较长或读取M1卡扇区数据时。解决办法是在发送命令前清空FIFO并在接收过程中监控状态寄存器的FIFO Level。参考代码在fm17xx_transceive函数中加入了FIFO水位检查接收前把FIFO清空接收过程中如果FIFO快满了立即读出数据到缓冲区避免溢出。5.4 卡响应超时和重试机制FM17XX的定时器寄存器控制着发送等待时间。如果TMode设为0x80TAuto设为0x20就可以启用自动定时功能。超时值TPrescaler和TReload共同决定等待时长。常见配置为TMode 0x80使能定时器TPrescaler 0xA9TReloadHi 0x03TReloadLo 0xE8这样设置的超时时间大约为10ms左右。如果卡片无响应状态寄存器会置位Timeout标志。实际应用中建议在发送命令后等待完整超时周期不要提前读取FIFO否则可能拿到错误数据。参考代码中的重试机制是连续发送三次请求每次间隔10ms都无响应才判定无卡。6. 实际操作中的经验心得这套参考代码已经在我经手的多个项目中跑过从最开始的FM1702SL到后来的FM1722SL基本都是同一套架构改动量很小。我个人体会最深的一点是读卡器的稳定性七分靠硬件三分靠固件。代码写得再好天线匹配不行、电源不干净读卡距离和成功率都上不去。相反只要天线调好了固件哪怕粗糙一点也能正常工作。给新手的建议是先把初始化流程走通用读寄存器的方式确认SPI通信无误再用单步调试观察REQA发送后FIFO里有没有数据。不要一上来就整体跑通所有协议那样出了问题难以定位。调试的时候准备一张已知UID的M1卡用逻辑分析仪配合上位机打印比对实际读到的UID是否一致。最后分享一个排查小技巧当读卡不稳定时把读卡器的功耗和波形同时抓下来观察每次读卡瞬间电源有没有跌落。如果有跌落多半是天线的发射功率拉低了电压可以适当调低发射增益同时增大电源电容容量。这个坑值得记下来它能省去你盲调天线的大量时间。本文还有配套的精品资源点击获取
返回列表