FEATURED · 精选文章

基于STM32与RC522的S50卡ETC收费系统毕业设计实战

发布时间 / 2026/9/17 16:29:39
来源 / 创域科博编辑部
栏目 / 资讯中心
基于STM32与RC522的S50卡ETC收费系统毕业设计实战 简介这份单片机方向毕业设计文档围绕基于RFID与STM32的高速ETC智能收费系统展开适合电子信息、自动化、物联网等专业学生作为课程设计、毕设参考也可供嵌入式初学者练习外设驱动与系统集成。全文以STM32F103ZET6为主控串联RC522射频识别、L298闸门电机驱动、TFTLCD液晶与触摸屏、供电及在线调试等硬件模块并给出软件界面与后台充值、单价设定等管理功能完整呈现从车辆感应、LCD扣费显示到自动抬杆放行的流程。压缩包内仅1个doc文件约1.31MB为整篇论文正文结构含概述、硬件设计、软件设计、测试与总结硬件部分涵盖射频通信、主控制器、系统时钟、按键输入、闸门电机、显示触摸与供电等模块目录层级清晰便于按模块查阅原理、电路与程序思路。目前已有89人学习可作为ETC类课题的方案模板与写作参考。1. 从一张 S50 卡贴上天线到栏杆抬起这套 STM32RC522 的 ETC 毕设究竟在做什么一张 S50 卡靠近 RC522 天线几百毫秒内要完成寻卡、防冲突、密钥认证、读余额、比对单价、写回余额、TFTLCD 刷新、步进电机抬杆这一整串动作中间任何一环超时或校验失败车道就得回到空闲态等下一辆车。这就是基于 STM32F103ZET6 的单片机 ETC 收费系统也是电子毕设里少有的能把 SPI、IIC、FSMC、外部中断、SPI Flash 字库、电机节拍驱动全部串成一条业务链的题目。它适合两类人一类是电子信息、自动化、物联网专业正在选题、想找一个硬件不贵又能讲清楚闭环的同学另一类是会点灯、会串口打印但没独立做过读卡器扣费执行机构完整业务的嵌入式新手。下面按硬件骨架、读卡驱动、人机界面与掉电保存、联调排错的顺序把它拆开。2. ETC 车道机的硬件骨架主控、射频前端与执行机构的选型理由2.1 STM32F103ZET6 为什么能撑住这个车道机选 STM32F103ZET6 的核心原因不是主频而是引脚和外设的余量。它是 Cortex-M3 内核的 32 位单片机144 个引脚PA 到 PG 共 7 组端口、每组 16 个 IO论文里提到的256K 字节系统可编程 Flash对这套业务是够用的——字库那 64Mbit8MB是外挂在 W25Q64 上的不占内部 Flash。真正吃资源的是外设并行SPI1 给 RC522SPI2 给 W25Q64 字库 FlashSDIO 给 SD 卡IIC 给 AT24C02FSMC 给 TFTLCD再加一组 IO 驱动四相步进电机、一组外部中断接待机按键。常见的踩坑是 IO 口分配时忘记复用功能。RC522 走 SPI1 就要占 PA5/PA6/PA7如果同时想把 PA5 当普通 IO 用就必须在初始化时明确设置 GPIO_Mode_AF_PP而不是 Out_PP。配置模式上上拉输入、下拉输入、推挽输出、开漏输出这四种在本系统里都会用到MISO 用上拉输入电机四个相位用推挽输出IIC 的 SDA/SCL 用开漏输出加外部上拉。2.2 RC522 模块与 S50 卡的接口与时序约束RC522 是 3.3V 供电的非接触读写芯片支持 ISO14443A 协议内部集成调制解调、CRC 校验和奇偶校验SPI 接口速率可达 10Mbit/s带 64 字节收发缓冲。它的信号线定义和接线关系如下表这张表几乎是调不通时的第一张排查清单。RC522 引脚作用接 STM32F103ZET6备注SDA / NSS片选低有效PA4用软件控制片选方便多从机SCKSPI 时钟同步PA572MHz PCLK2 八分频约 9MHzMOSI主到从数据PA7单向MISO从到主数据PA6必须上拉输入空闲时模块会悬空RST硬复位PB0初始化失败或模块无响应时拉低复位3.3V / GND供电与参考地3.3V / GND不兼容 5V直连 5V 会烧模块注意RST 引脚不能省。模块上电后如果 SPI 通信读到的一直是 0x00 或 0xFF先做一次硬复位再软复位写 CommandReg 的 0x0F比反复查代码有效得多。天线部分的匹配电容和电感是模块厂家调好的不要自己改改了读卡距离会从 3cm 掉到贴不上。2.3 时钟、供电与调试通道的工程细节系统时钟用 8MHz 外部晶振配 22pF 负载电容晶振引脚内部是数字反相器与外接晶体构成皮尔斯振荡器只有在反相器处于线性区时才能起振所以串联反馈电阻CMOS 芯片通常在数 MΩ 量级不能省。HSE 起振后经 PLL 九倍频得到 72MHz 系统时钟SPI、FSMC、SDIO 的时序都建立在这个时钟上晶振不稳会导致读卡偶发失败、液晶花屏这类玄学现象。供电分三级12V/2A 开关电源输入7805 线性降压到 5V再用 AMS1117-3.3 降到 3.3V。论文里提到 3.3V 这一级用两片 AMS1117 并联扩流原因是 TFT 液晶背光和 SD 卡瞬时电流较大单片的余量不够。输入输出端各挂 10610µF和 1040.1µF电容做滤波大小电容并联是为了同时压住低频纹波和高频毛刺。/* sysclk.c —— HSE 8MHz 经 PLL 九倍频到 72MHz */ void System_Clock_Init(void) { RCC-CR | 1 16; /* 使能 HSEON */ while (!(RCC-CR (1 17))); /* 等待 HSERDY起振失败会死在这里 */ RCC-CFGR ~0x03; /* SW[1:0]00HSE 作为 PLL 输入 */ RCC-CFGR | 0x04 8; /* PLLMUL0x07 时为 9 倍频注意低 4 位编码 */ RCC-CFGR | 0x02 0; /* AHB 不分频APB1 二分频 36MHz */ RCC-CR | 1 24; /* 使能 PLLON */ while (!(RCC-CR (1 25))); /* 等待 PLLRDY */ RCC-CFGR | 0x02; /* SW10切换系统时钟到 PLL */ while ((RCC-CFGR 0x0C) ! 0x08); /* 确认 SWS 已切换 */ }这段代码里最容易被忽略的是 RCC-CFGR 的 PLLMUL 位域不同芯片手册的编码不同写错会得到 4 倍频或 16 倍频表现为串口波特率全乱、RC522 读不出卡。调试通道上论文用的是 ST-LINK V2 的 SWD 模式而不是 JTAG 全引脚理由是 RC522 指令多、状态机复杂单步执行比串口打印更快定位问题。2.4 闸门执行机构ULN2003 与 L298 的取舍原料里有个不一致点摘要写的是 L298 模块驱动闸门电机硬件章节实际落的是 ULN2003 达林顿管加四相步进电机。两版方案的共性要求是一致的——低电压控制高电压大电流负载且必须做隔离。ULN2003 是七路达林顿阵列驱动板上 INT1 到 INT4 接单片机 IO四颗 LED 指示各相位导通状态调试驱动节拍时肉眼就能看出时序对不对。步进电机必须按规定节拍驱动四相单四拍、双四拍、八拍三种节拍方式对应的步距角不同。闸门只需要开合两个位置用四相单节拍足够如果要求开合角度精确就要上细分驱动。另外论文特别提到驱动板加装光耦隔离这不是可选项——电机换向时产生的反向电动势和共地串扰会直接干扰 SPI 总线表现就是一抬杆就读卡失败。3. RC522 驱动 S50 卡的 SPI 时序与寻卡、认证、扣费链路3.1 RC522 寄存器访问模型地址左移一位这件事RC522 的 SPI 通信有一套自己的地址约定搞不清这个约定读回来的数据永远是错的。规则是写操作时寄存器地址左移一位、最低位清零读操作时寄存器地址左移一位、最低位置一。所以代码里通常把读写封装成两个宏而不是靠调用者自己移位。寄存器地址用途常用值CommandReg0x01启动/停止命令0x0F 软复位0x0C 收发ComIEnReg0x02中断使能0x77 常用组合ComIrqReg0x04中断标志轮询 RxIRq 判断收到数据ErrorReg0x06错误标志位 3 为协议错误位 2 为 CRC 错误FIFODataReg0x09收发缓冲64 字节FIFOLevelReg0x0A缓冲内字节数写 0x80 清空 FIFOBitFramingReg0x0D位对齐寻卡时写 0x07ModeReg0x11发送调制方式0x3D 对应 106kbit/sVersionReg0x37版本号读 0x92/0x91 判断芯片在线提示上电后先读 VersionReg能读出 0x91 或 0x92 才说明 SPI 通路上电正常。读不到就直接查片选、上拉和供电不要往下调读卡逻辑。3.2 SPI1 主机模式初始化与片选控制/* rc522_hw.c —— SPI1 主机软件片选SCK 约 9MHz */ void RC522_SPI_Init(void) { GPIO_InitTypeDef gpio; SPI_InitTypeDef spi; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); gpio.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_7; /* SCK / MOSI */ gpio.GPIO_Mode GPIO_Mode_AF_PP; /* 复用推挽 */ gpio.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, gpio); gpio.GPIO_Pin GPIO_Pin_6; /* MISO */ gpio.GPIO_Mode GPIO_Mode_IPU; /* 上拉输入模块空闲时会悬空 */ GPIO_Init(GPIOA, gpio); gpio.GPIO_Pin GPIO_Pin_4; /* NSS 软件片选 */ gpio.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(GPIOA, gpio); GPIO_SetBits(GPIOA, GPIO_Pin_4); /* 空闲时片选拉高 */ 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; SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_8; /* 72/89MHz */ SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial 7; SPI_Init(SPI1, spi); SPI_Cmd(SPI1, ENABLE); }CPOL 和 CPHA 的组合必须和 RC522 的时序要求一致这里取空闲低电平、第一个边沿采样也就是常说的 SPI 模式 0。分频系数选 8 而不是 2是因为杜邦线飞线长度超过 10cm 时9MHz 已经是稳定读卡的上限线再长就把分频改成 16用速度换稳定。3.3 一次完整的扣费事务寻卡、防冲突、认证、读改写/* etc_charge.c —— 返回值 0 成功非 0 为错误码 */ uint8_t ETC_Charge(uint8_t *uid, uint16_t unit_price) { uint8_t status; uint8_t block[16]; uint16_t balance; status PcdRequest(PICC_REQALL, g_card_type); /* 0x52一次寻所有卡 */ if (status ! MI_OK) return 1; /* 车道内无卡 */ status PcdAnticoll(uid); /* 取 4 字节 UID解开冲突 */ if (status ! MI_OK) return 2; /* 多卡同时进入未解开 */ status PcdSelect(uid); /* 选定卡片进入 ACTIVE 态 */ if (status ! MI_OK) return 3; status PcdAuthState(PICC_AUTHENT1A, 4, g_key_a, uid); /* 块 4密钥 A 认证 */ if (status ! MI_OK) return 4; /* 密钥错或卡已离场 */ status PcdRead(4, block); if (status ! MI_OK) return 5; balance ((uint16_t)block[0] 8) | block[1]; /* 余额按 2 字节大端存放 */ if (balance unit_price) return 6; /* 余额不足不抬杆 */ balance - unit_price; block[0] (uint8_t)(balance 8); block[1] (uint8_t)(balance 0xFF); status PcdWrite(4, block); /* 写回扣费后余额 */ if (status ! MI_OK) return 7; PcdHalt(); /* 必须停机否则卡停在 ACTIVE 态 */ return 0; }逻辑上分四段PcdRequest 只负责有没有卡参数 0x52 表示寻全部卡、0x26 表示只寻未进入休眠的卡PcdAnticoll 拿 UID 并解开多卡冲突这一步在车道场景几乎不会冲突但两张卡贴一起时就会返回错误码 2PcdAuthState 用扇区密钥认证S50 的扇区 1块 4 到块 7默认密钥 A 是六个 0xFF出厂状态可用最后一段是典型的读—比较—写回事务余额不写成浮点而写成 2 字节整数是为了避开单片机上浮点运算和格式化显示的额外开销。注意PcdHalt 不能漏。少了这一句卡会停在 ACTIVE 态同一张卡在几秒内被反复选中实测表现就是过一次车扣好几次费。4. 触摸充值界面、字库方案与 AT24C02 参数掉电保存4.1 两级菜单的状态机设计软件流程分两条主线系统初始化完成后询问用户选择进入充值系统就按触摸屏输入的金额给卡片充值进入收费系统就循环检测是否有车。这种开机二选一的结构用状态机写比用嵌套 if 清晰得多页面与按键映射关系可以列成一张表来对齐需求。页面触发条件触摸/按键响应写卡动作主菜单上电初始化完成左半屏进收费右半屏进充值无充值页主菜单选充值数字键盘输入金额确认键提交读余额→加金额→写回收费页主菜单选收费无需操作自动循环寻卡读余额→减单价→写回单价设置页长按按键 2 秒上下键调整单价写入 AT24C02不写卡单价这类全局参数不写进射频卡只写进 EEPROM理由是单价属于运营方配置卡里存一份会带来改价时的数据不一致。4.2 XPT2046 触摸坐标与校准参数的存储触摸控制器 XPT2046 集成在液晶电路上输出的是原始 ADC 值必须经过两点或四点校准转换成屏幕坐标。校准系数放在 AT24C02 里掉电不丢。AT24C02 是 256 字节的串行 CMOS EEPROMIIC 接口页写缓冲 16 字节官方标称擦写寿命一百万次按每次开机校准一次算完全够用。/* at24c02.c —— 单字节写注意写周期延时 */ #define AT24C02_ADDR 0xA0 void AT24C02_WriteByte(uint8_t addr, uint8_t dat) { IIC_Start(); IIC_SendByte(AT24C02_ADDR); /* 器件地址 写方向 */ IIC_WaitAck(); IIC_SendByte(addr); /* 片内字节地址0x00~0xFF */ IIC_WaitAck(); IIC_SendByte(dat); IIC_WaitAck(); IIC_Stop(); Delay_ms(5); /* 内部写周期典型 5ms期间不响应总线 */ } uint8_t AT24C02_ReadByte(uint8_t addr) { uint8_t dat; IIC_Start(); IIC_SendByte(AT24C02_ADDR); IIC_WaitAck(); IIC_SendByte(addr); IIC_WaitAck(); IIC_Start(); /* 重复起始条件 */ IIC_SendByte(AT24C02_ADDR | 0x01); /* 读方向 */ IIC_WaitAck(); dat IIC_ReadByte(); IIC_NoAck(); /* 最后一字节回 NACK */ IIC_Stop(); return dat; }IIC 的时序要点是SDA 上数据必须在 SCL 高电平期间保持稳定只有在 SCL 低电平时才允许变化。总线速率不要超过 400kHz速率和误码率是此消彼长的关系。如果校准参数读出来是 0xFF先检查上拉电阻是否装了SDA/SCL 开漏输出没有上拉就是这种表现。4.3 TFTLCD 字库从 SD 卡烧到 W25Q64 的完整路径TFT 液晶本身不存字库论文的方案是在 64Mbit 的 W25Q64 SPI Flash 上建一个 FAT 文件系统存放字库数据。首次使用时的流程是SD 卡里预先放好字库文件STM32 用 SDIO 协议从 SD 卡读取数据再通过 SPI 写进 W25Q64之后运行时只查 Flash 里的字库不再依赖 SD 卡。这里有个容易被忽略的性能取舍SD 卡走 SDIO 比走 SPI 快所以读取阶段用 SDIOW25Q64 挂在 SPI2 上写入阶段受 SPI 速率限制。整片字库烧写会花几十秒建议做成一次性动作并在串口打印进度而不是每次上电都烧。SDIO 的四根数据线和一根命令线都要 47kΩ 上拉缺了上拉会出现SD 卡初始化偶尔成功偶尔失败的现象。FSMC 驱动液晶这部分需要配好时序参数读 ID 是第一步验证能正确读出控制器 ID说明地址线、数据线和读写时序都对再去调显存写入。5. 两张 S50 卡模拟过闸防重复扣费与联调排错的实操技巧系统软件写完之后用两张射频卡模拟汽车经过是最小可行的验证方案。测试目标有三个能不能精确区分不同卡片对应的车辆、扣费金额是否与单价一致、同一张卡在一次通过中会不会被扣多次。第三个问题在论文的测试结论里被特别提到不存在一次经过多次扣费的错误情况说明它在实现中确实容易踩。排查它的第一步是看串口打印的 UID 和余额流水。每完成一次寻卡就把 4 字节 UID 打出来配合时间戳能立刻分辨是卡没离场被重复选中还是状态机逻辑进入了两遍。常见做法是把车道逻辑写成显式状态机只有检测到卡离开射频场才允许进入下一次扣费判断。/* lane_fsm.c —— 车道状态机卡离场才开下一次扣费窗口 */ typedef enum { LANE_IDLE, LANE_CARD_ON, LANE_CHARGED, LANE_GATE_OPEN } lane_state_t; void ETC_Lane_Task(void) { static lane_state_t st LANE_IDLE; static uint32_t t_gate 0; switch (st) { case LANE_IDLE: if (PcdRequest(PICC_REQALL, g_card_type) MI_OK) st LANE_CARD_ON; break; case LANE_CARD_ON: if (ETC_Charge(g_card_sn, g_unit_price) 0) { /* 只有成功才扣 */ LCD_ShowCharge(g_unit_price, g_balance); /* 刷新扣费标准与费用 */ Gate_Open(90); /* 90 个节拍推开闸门 */ t_gate GetTick(); st LANE_CHARGED; } else { st LANE_IDLE; /* 余额不足/认证失败直接回空闲 */ } break; case LANE_CHARGED: /* 卡离开射频场才认为本次通行结束 */ if (PcdRequest(PICC_REQALL, g_card_type) ! MI_OK) st LANE_GATE_OPEN; break; case LANE_GATE_OPEN: if (GetTick() - t_gate 3000) { /* 3 秒后自动落杆 */ Gate_Close(); st LANE_IDLE; } break; } }LANE_CHARGED 这个中间态是关键它把已扣费和卡还在天线上这两件事绑定在一起卡不离开就永远不回到 IDLE重复扣费在结构上就不可能出现。闸门落下用毫秒计数而不是阻塞延时是为了让主循环继续响应触摸屏。其余高频故障可以用下面这张表对照定位比盲改代码快得多。现象高概率原因定位手段VersionReg 读回 0x00/0xFF片选未拉低、MISO 未上拉、供电非 3.3V示波器看 SCK 与 MISO 波形寻卡时好时坏SPI 分频过高、杜邦线过长分频改 16 或 32 复测认证始终失败扇区密钥被改、块号填错换一张出厂态新卡对比抬杆瞬间读卡失败电机干扰 SPI 总线加大光耦隔离、电机单独供电触摸坐标偏移校准参数丢失或写入未延时读 AT24C02 对应地址核对运行数小时后死机电源纹波大或未开看门狗加 104 电容、启用 IWDG调试方式上RFID 指令多、状态转换频繁用串口打印加 SWD 单步的组合最省时间串口负责看长周期的业务流水ST-LINK 单步负责抓某一帧命令的时序错误。另外软件层面开启独立看门狗是低成本的兜底手段电机启停带来的电源毛刺偶尔会让程序跑飞看门狗能把系统拉回来代价只是要记得在主循环里定时喂狗。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻