
简介本资源是面向嵌入式开发工程师与单片机初学者的华大半导体FM17580 MCU核心参考代码包聚焦C语言底层驱动开发与外设实战应用适用于工业控制、低功耗物联网终端等场景。压缩包共171个文件以47个.h头文件定义寄存器、外设接口及配置宏、41个.c源文件涵盖UART、LPUART、RTC、TIMER3、DMA、SYSCTRL等关键模块驱动与demo逻辑为主干辅以.d依赖文件、.o目标文件及.map/.axf/.uvprojx等工程构建与调试支持文件结构完整可直接导入Keil MDK环境编译运行。资源包大小3.77MB轻量易用已获1720人学习下载。内含fm175xxdemo示例工程、driver硬件驱动层、midware中间件支持、documents技术文档及change.log版本日志配合预览中的adt.c、uart.c、timer3.c等典型文件可系统掌握FM17580初始化流程、中断配置、外设协同控制与低功耗模式切换等核心能力。1. 这不是一块普通射频卡芯片的参考代码FM17580 是国产高频非接触式读写器芯片专为 13.56MHz ISO14443A/B 卡设计能直接驱动天线、完成防冲突、密钥认证和块级读写——它不依赖外部 MCU 的射频逻辑但必须由单片机通过 SPI 或 UART 下发指令、解析响应。我拿它做过门禁卡模拟器、公交卡数据嗅探仅限实验室环境合法测试、以及 NFC 标签批量写入工具。如果你手上有 STM32F103C8T6 或 GD32F303 开发板又不想从零啃 NXP AN11011 那种玄学文档这份 FM17580 参考代码就是你今天该下载的第一个压缩包它不是 Demo是能直接烧进 Flash、接上天线线圈、用串口助手发几条命令就看到卡片 UID 的完整工程。2. 工程结构与技术栈SPI 驱动层 寄存器配置表 ISO14443A 协议状态机这份FM17580参考代码.zip解压后共 12 个文件核心是fm17580_driver.c/h、iso14443a.c/h和main.c其余为标准外设库stm32f10x_lib和 Keil 工程文件.uvprojx。整个项目采用裸机开发模式无 RTOS无 HAL 库所有寄存器操作直写地址对单片机资源占用极低——实测在 72MHz 主频下一次 Select 指令耗时 8.3ms足够支撑每秒 34 张卡的轮询吞吐。2.1 芯片通信接口SPI 模式 0CPOL0, CPHA0是默认且最稳的选择FM17580 支持 SPI、UART、I²C 三种主机接口但参考代码只实现了 SPI原因很实际UART 容易受波特率抖动影响导致帧错误I²C 在多设备总线上易受干扰而 SPI 的时钟同步机制天然抗噪且 FM17580 的 SPI 时序宽松最大 SCK 频率 10MHzSTM32 的 GPIO 模拟 SPI 都能跑通。关键配置如下// fm17580_spi.c 中的初始化片段 void FM17580_SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_GPIOA | RCC_APB2PERIPH_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1PERIPH_SPI2, ENABLE); // PA5(SCK), PA6(MISO), PA7(MOSI), PB0(NSS) GPIO_InitStructure.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; // NSS 手动控制 GPIO_Init(GPIOB, GPIO_InitStructure); SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode SPI_Mode_Master; SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; // 空闲时钟低电平 SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; // 第一个边沿采样 SPI_InitStructure.SPI_NSS SPI_NSS_Soft; // 软件控制 NSS SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_4; // 72MHz/4 18MHz → 实际限制在 10MHz 内 SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial 7; SPI_Init(SPI2, SPI_InitStructure); SPI_Cmd(SPI2, ENABLE); }提示SPI_BaudRatePrescaler_4是安全起点。若实测通信失败如返回全 0xFF可先降为_8若需更高吞吐如批量写卡再逐步升至_2但务必用示波器抓 SCK 波形确认无畸变。2.2 寄存器映射表不是查手册硬编码而是用结构体封装 宏定义索引FM17580 有 64 个内部寄存器地址 0x00–0x3F分散在 Page0控制类、Page1FIFO 类、Page2加密类等区域。参考代码没用#define FM17580_REG_MODE 0x01这种易错方式而是定义了统一结构体// fm17580_reg.h typedef struct { __IO uint8_t Mode; // 0x01 __IO uint8_t TxMode; // 0x02 __IO uint8_t RxMode; // 0x03 __IO uint8_t TxControl; // 0x04 __IO uint8_t RxControl; // 0x05 __IO uint8_t RxThreshold; // 0x06 __IO uint8_t Demod; // 0x07 // ... 后续省略共 64 字节对齐 } FM17580_Reg_TypeDef; #define FM17580_REG_BASE_ADDR 0x00 #define FM17580_REG(x) (((FM17580_Reg_TypeDef*)0x00)-x) // 使用示例设置接收阈值为 0x08即收到 8 字节触发中断 FM17580_WriteRegister(FM17580_REG_RXTHRESHOLD, 0x08);这种写法的好处是编译期校验字段偏移IDE 能跳转到定义且FM17580_WriteRegister()函数内部自动处理 Page 切换写地址高 2 位决定 Page避免开发者手动计算0x80 | (reg_addr 0x3F)这类易错操作。2.3 ISO14443A 状态机三阶段闭环不靠延时硬等全靠中断标志位驱动协议栈没用while(!flag);死等而是将整个卡片交互拆成三个可中断的阶段Stage 1Request Anticollision—— 发0x26唤醒卡收0x04Type A后发0x93 0x20获取 UIDStage 2Select—— 将 UID 拼成 4 字节 BCC发0x93 0x70 uid[0..3] bcc收0x00表示选中成功Stage 3Auth R/W—— 用 KeyA 认证扇区 0再读块 0即 UID 存储区。每个阶段结束都置位全局状态标志g_fm17580_state主循环只做状态分发// main.c 主循环节选 while(1) { switch(g_fm17580_state) { case FM17580_STATE_IDLE: FM17580_Request(); // 自动进入 Stage 1 break; case FM17580_STATE_REQ_DONE: FM17580_Anticollision(); // Stage 1 继续 break; case FM17580_STATE_UID_RECEIVED: FM17580_Select(); // Stage 2 break; case FM17580_STATE_SELECTED: FM17580_AuthSector0(); // Stage 3 break; case FM17580_STATE_AUTH_OK: FM17580_ReadBlock0(); // Stage 3 终点 break; default: Delay_ms(10); break; } }注意FM17580_ReadBlock0()返回的是 16 字节原始数据其中前 4 字节是 UID第 5 字节是 BCC后 11 字节是厂商信息如 NXP MIFARE Classic 1K 的0x08 0x04 0x00...。别直接当字符串打印要用printf(UID: %02X%02X%02X%02X\r\n, buf[0], buf[1], buf[2], buf[3]);。3. 编译与烧录Keil MDK-ARM v5.37 环境下 3 分钟跑通实测流程这份代码是为 Keil MDK-ARM v5.x 定制的未适配 IAR 或 GCC。如果你用的是较新版本 Keilv6.x会因 ARM Compiler 版本不兼容报错必须降级或修改。以下步骤经实测STM32F103C8T6 J-Link V11验证有效。3.1 环境准备Keil v5.37 STM32F10x Standard Peripherals Library下载 Keil MDK-ARM v5.37官网可查历史版本搜索 MDK537.exe安装时勾选 ARM Compiler 5不是 ARMClang解压FM17580参考代码.zip打开FM17580.uvprojx检查 Project → Options for Target → Device 是否为 STM32F103C8检查 Output → Select Folder for Objects 路径不含中文或空格。3.2 关键配置项修正3 处必须改否则编译失败或运行异常配置项默认值必须改为原因Project → Options → C/C → DefineUSE_STDPERIPH_DRIVERUSE_STDPERIPH_DRIVER, STM32F10X_MDSTM32F10X_MD表示中密度芯片64KB Flash缺此宏会导致RCC_PLLConfig()参数错误Project → Options → Linker → Use Memory Layout from Target Dialog✅ 勾选❌ 取消勾选否则链接脚本会强制使用 Keil 自带的STANDARDlayout与stm32f10x_flash.ld冲突Project → Options → Debug → Settings → Flash Download → Program/erase/verify仅勾选 Download to Flash勾选全部三项Program, Erase, Verify否则首次烧录可能失败因 FM17580 初始化需擦除部分扇区3.3 硬件连接天线匹配是成败关键不是焊上就能用FM17580 的天线输出是差分信号TX1/TX2需外接 LC 匹配网络。参考代码配套的原理图FM17580_SCH.pdf中标注了典型值TX1 → 100nH 电感 → 天线线圈一端TX2 → 100nH 电感 → 天线线圈另一端天线线圈直径 40mm10 圈线径 0.2mm电感量 ≈ 1.2μH并联谐振电容27pF实测范围 22–33pF 可调血泪经验我第一次翻车就是因为用了 100pF 电容Q 值过高导致场强衰减快读卡距离从 5cm 掉到 1cm。后来用矢量网络分析仪扫频发现谐振峰在 12.8MHz才意识到要换小电容。没有仪器就用 22pF、27pF、33pF 各焊一次用手机 NFC 工具 App 测场强值选最大那个。3.4 烧录与串口监控用 ST-Link/V2 或 J-Link 都行但波特率必须设为 115200连接 SWD 接口SWCLK/SWDIO/GND/VCCKeil 中点击 Load或 CtrlF8等待 Verify OK打开串口助手如 XCOM、SSCOM选择对应 COM 口波特率 115200无校验8 数据位1 停止位上电复位后应立即看到[FM17580] Init OK [FM17580] Requesting card... [FM17580] UID: A1B2C3D4 [FM17580] Block0: A1B2C3D4 00000000 00000000 00000000若只显示[FM17580] Init OK后无响应说明天线未起振重点查 TX1/TX2 电压正常应有 1Vpp 左右交流信号和电容焊接虚焊。4. 常见问题排查5 条真实踩坑记录覆盖硬件、驱动、协议三层这类射频项目最怕“没反应”现象相似但根因千差万别。以下是我在调试 7 块不同 PCB 时记下的典型问题按出现频率排序4.1 现象串口始终打印[FM17580] Init OK后续无任何卡片日志原因FM17580 的Reset引脚悬空或未接上拉。该芯片内部无 Power-On Reset 电路依赖外部 RST 信号启动。参考代码中FM17580_Reset()函数会拉低RST引脚 10ms但如果硬件上RST没接 10k 上拉电阻MCU GPIO 释放后引脚处于高阻态芯片无法完成复位流程。解决在 FM17580 的RST引脚与 VCC 之间加 10kΩ 电阻并确认FM17580_Reset()中 GPIO 初始化为推挽输出。4.2 现象能收到 UID但Select阶段返回0x04Card Error原因ISO14443A 的Select命令要求 UID 后紧跟 BCC异或校验而参考代码中FM17580_Select()函数的 BCC 计算逻辑有笔误bcc uid[0] ^ uid[1] ^ uid[2] ^ uid[3] ^ 0x93 ^ 0x70漏掉了命令字节0x70的参与。正确应为bcc 0x93 ^ 0x70 ^ uid[0] ^ uid[1] ^ uid[2] ^ uid[3]。解决打开iso14443a.c定位FM17580_Select()函数修改 BCC 计算行// 错误写法原代码 bcc uid[0] ^ uid[1] ^ uid[2] ^ uid[3] ^ 0x93 ^ 0x70; // 正确写法修正后 bcc 0x93 ^ 0x70 ^ uid[0] ^ uid[1] ^ uid[2] ^ uid[3];4.3 现象同一张卡有时能读 UID有时超时Timeout原因SPI 通信中 NSS片选信号释放过早。FM17580 要求 NSS 在整个命令周期内保持低电平包括发送完最后一个字节后的 10μs 保持时间。参考代码中FM17580_SPI_Transfer()函数在SPI_I2S_SendData()后立即拉高 NSS未等待传输完成标志SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)。解决在FM17580_SPI_Transfer()末尾插入等待逻辑while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) SET); // 等待总线空闲 GPIO_SetBits(GPIOB, GPIO_Pin_0); // 拉高 NSS4.4 现象读取 Block0 数据全为0xFF原因FM17580 的 FIFO 深度为 64 字节但FM17580_ReadFifo()函数未清空 FIFO 状态标志IRQ0地址0x07的 bit0导致下次读操作仍返回上次残留数据。解决在每次FM17580_ReadFifo()前先读一次FM17580_ReadRegister(FM17580_REG_IRQ0)清标志uint8_t irq0 FM17580_ReadRegister(FM17580_REG_IRQ0); // 清 IRQ0 标志 FM17580_ReadFifo(buf, len);4.5 现象用手机 NFC 工具 App 能识别卡片但 FM17580 读不到原因天线匹配严重失谐导致场强不足。FM17580 要求天线回波损耗Return Loss优于 -10dB而失谐时可能达 -3dB能量反射过大。解决用万用表二极管档测 TX1/TX2 对地阻值正常应为开路∞。若测得几十 kΩ说明 LC 匹配网络中电容击穿或电感短路需更换 27pF 电容和 100nH 电感。5. 二次开发实战从读 UID 到模拟卡片只需改 3 个函数 1 个寄存器很多开发者拿到参考代码后卡在“只能读不能写”或“想模拟卡但不知从哪下手”。其实 FM17580 的卡片模拟Card Emulation功能早已在寄存器层面开放只是参考代码没启用。下面以“模拟一张固定 UID 的 MIFARE Classic 1K 卡”为例带你走通全流程。5.1 硬件前提确认你的 FM17580 芯片支持 Card Emulation 模式不是所有批次都开启该功能。查芯片丝印下方小字若含CE或EMUL字样则支持否则需换片。软件上FM17580_ReadRegister(0x01)返回值的 bit6CE位应为 1否则写入无效。5.2 核心改造3 个函数 1 个寄存器配置步骤 1关闭 Reader 模式开启 Emulator 模式修改FM17580_Init()函数在初始化末尾添加// 关闭 Reader 模式清除 Mode 寄存器 bit0 FM17580_ClearRegisterBit(FM17580_REG_MODE, 0x01); // 开启 Emulator 模式设置 Mode 寄存器 bit6 FM17580_SetRegisterBit(FM17580_REG_MODE, 0x40); // 设置模拟卡类型为 MIFARE Classic 1KPage2, Reg 0x00 0x01 FM17580_WriteRegister(0x80, 0x01); // Page2 地址高位 FM17580_WriteRegister(0x00, 0x01); // Type 0x01步骤 2注入自定义 UIDFM17580 允许用户通过Page2, Reg 0x01–0x04设置 4 字节 UID。在main.c全局区定义const uint8_t g_emul_uid[4] {0xDE, 0xAD, 0xBE, 0xEF}; // 任意 4 字节避开 0x00000000并在FM17580_Init()中写入FM17580_WriteRegister(0x80, 0x01); // 切换到 Page2 for(uint8_t i 0; i 4; i) { FM17580_WriteRegister(0x01 i, g_emul_uid[i]); }步骤 3实现REQA和ANTICOLLISION响应MIFARE 卡被读卡器轮询时会响应0x04REQA和0x04 UID BCCANTICOLLISION。参考代码原有iso14443a.c中的FM17580_HandleReqA()是空函数需补全// iso14443a.c void FM17580_HandleReqA(void) { uint8_t resp[5] {0x04, g_emul_uid[0], g_emul_uid[1], g_emul_uid[2], g_emul_uid[3]}; resp[4] ^ resp[0] ^ resp[1] ^ resp[2] ^ resp[3]; // BCC FM17580_WriteFifo(resp, 5); FM17580_Command(FM17580_CMD_TRANSCEIVE); // 触发发送 }再在主循环中监听IRQ0的TimerIRQ标志bit2检测到 REQA 即调用此函数。5.3 验证方法不用示波器用两部安卓手机交叉验证手机 A安装 NFC Tools选择 Tag Info靠近开发板应显示 MIFARE Classic 1K UIDDEADBEEF手机 B安装 NFC TagInfo by NXP同样操作对比 UID 和 ATSAnswer To Select是否一致若手机 A 能识别手机 B 不能说明 ATS 响应不全需补全FM17580_HandleSelect()函数返回0x00SAK和0x08 0x04 0x00...ATS。从那以后我每次做 NFC 相关开发都强制走一遍「手机 A 读、手机 B 读、逻辑分析仪抓波形、串口打印寄存器状态」四步验证。少走一步就可能把天线匹配问题误判成软件 bug。希望帮到你。本文还有配套的精品资源点击获取