mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
1833 字
5 分钟
基于STM32与YMC1501的RFID电子钱包系统设计与实现
2026-05-07

项目概述#

本项目设计并实现了一款基于STM32F103RC的RFID电子钱包系统,使用YMC1501 S50 Mifare高频读写模块完成对IC卡的电子钱包操作,并通过128×128 SPI LCD屏幕与3个物理按键提供完整的人机交互界面。

核心功能: 初始化钱包(余额归零)、充值、扣款、余额查询——完全对标真实电子钱包的增、减、查、改四项基本操作。

系统架构#

┌──────────────────────────────────────────┐
│ STM32F103RC │
│ │
│ ┌──────────┐ ┌──────────┐ ┌───────┐ │
│ │ UI状态机 │ │ 协议栈 │ │ I2C │ │
│ │ (ui.c) │ │ (rfid.c) │ │(LDC) │ │
│ └────┬─────┘ └────┬─────┘ └───┬───┘ │
│ │ │ │ │
│ ┌────┴─────┐ ┌────┴─────┐ ┌──┴───┐ │
│ │LCD 128px │ │ USART2 │ │PB10/ │ │
│ │SPI (PB4- │ │9600 8N1 │ │ PB11 │ │
│ │ PB9) │ │ PA2/PA3 │ │ │ │
│ └──────────┘ └────┬─────┘ └──┬───┘ │
│ │ │ │
└─────────────────────┼────────────┼──────┘
│ │
┌───────┴──┐ ┌─────┴─────┐
│ YMC1501 │ │ LDC1614 │
│ S50 读写器│ │ 电感传感器 │
└─────┬────┘ └───────────┘
┌─────┴────┐
│ M1 S50 │
│ IC卡 │
└──────────┘

硬件选型与接线#

设备型号/引脚说明
主控芯片STM32F103RCARM Cortex-M3, 72MHz
RFID读写器YMC1501 (S50 Mifare)UART接口, 13.56MHz
LCD屏幕128×128 SPI驱动IC ST7735, 16位色
电感传感器LDC16144通道, I2C接口
按键 ×3KEY1=PA0, KEY2=PC8, KEY3=PC9下拉/上拉输入
指示灯LED=PA8操作指示

关键接线:

外设STM32引脚接口类型
YMC1501 TXPA3 (USART2 RX)UART 9600 8N1
YMC1501 RXPA2 (USART2 TX)UART 9600 8N1
LCD SCL/CS/BLKPB4~PB9软件SPI
LDC1614 SCLPB10软件I2C
LDC1614 SDAPB11软件I2C
LDC1614 ADDRPA11地址选择(低=0x2A)
LDC1614 SDPC13关断控制(低有效)

PB10/PB11 用于软件I2C而非硬件I2C的原因是:原硬件I2C引脚PB8/PB9与LCD的CS/BLK冲突,因此改用GPIO模拟。

YMC1501 通信协议驱动#

协议帧格式#

YMC1501 采用自定义的UART二进制帧协议(文档版本 V1.0.5),帧格式如下:

上位机发送帧:
[CMD_TYPE][LEN][CMD][ADDR][...DATA...][CS]
读写器应答帧:
[CMD_TYPE][LEN][CMD][ADDR][STATUS][...DATA...][CS]
  • CMD_TYPE: 命令类型,固定为 0x01(IC卡操作)
  • LEN: 整帧长度(含自身)
  • CMD: 命令码
  • ADDR: 读写器地址,默认 0x20
  • STATUS: 0x00=成功, 0x01=失败, 0x03=操作成功但读余额失败
  • CS: 校验和 = ~(Buf[0] ^ Buf[1] ^ ... ^ Buf[LEN-2])

校验算法实现#

每个字节依次异或,最终取反得到校验码:

void RFID_TxCheckSum(u8 *buf, u8 len)
{
u8 i, cs = 0;
for(i = 0; i < (u8)(len - 1); i++) cs ^= buf[i];
buf[len - 1] = ~cs;
}
u8 RFID_RxVerify(u8 *buf, u8 len)
{
u8 i, cs = 0;
for(i = 0; i < (u8)(len - 1); i++) cs ^= buf[i];
cs = ~cs;
return (cs == buf[len - 1]) ? RFID_OK : RFID_ERR;
}

接收端重新计算校验并与帧中校验字节比对,一致则校验通过。这种异或取反方案虽然是简单校验,但对串口常规误码有较好的检测效果。

命令码一览#

命令码名称功能是否需要密钥
0xA1读卡号读取IC卡4字节UID
0xA3读数据块读取指定块16字节数据是(KEYA)
0xA4写数据块写入指定块16字节数据是(KEYA)
0xA6初始化钱包将指定块初始化为钱包,设置初始值是(KEYA)
0xA7钱包减值从钱包扣除指定金额是(KEYA)
0xA8钱包加值向钱包充值指定金额是(KEYA)
0xA9余额查询读取钱包当前余额是(KEYA)

核心命令详解#

读卡号 (0xA1) — 探测是否有卡:

发送: 01 08 A1 20 00 01 00 [CS] (8字节)
成功: 01 0C A1 20 00 04 00 [UID4] [CS] (12字节)
失败: 01 08 A1 20 01 00 00 76 (8字节)

第5字节 0x01 表示操作时蜂鸣提示,0x00 表示静默。

钱包操作 (0xA6/A7/A8) — 初始化/增值/减值,使用统一的11字节命令帧:

发送: 01 0B [CMD] 20 [BLOCK] [BEEP] [VALUE_LE4] [CS]
| | | | | | | |
0 1 2 3 4 5 [6..9] 10
应答: 01 0A [CMD] 20 00 [VALUE_LE4] [CS] (成功, 10字节)
01 08 [CMD] 20 01 00 00 [CS] (失败, 8字节)

金额采用4字节小端序存储(S50钱包块原生格式),以”分”为单位避免浮点运算:

static void s32ToLE(s32 val, u8 *out)
{
u32 v = (u32)val;
out[0] = (u8)(v & 0xFF); // 最低字节
out[1] = (u8)((v >> 8) & 0xFF);
out[2] = (u8)((v >> 16) & 0xFF);
out[3] = (u8)((v >> 24) & 0xFF); // 最高字节
}

USART2 中断接收#

void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
u8 d = (u8)USART_ReceiveData(USART2);
if(rfid_rx_cnt < RFID_RX_BUF_LEN)
rfid_rx_buf[rfid_rx_cnt++] = d;
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
}
}

接收缓冲为32字节的环形缓冲区,每次收到数据触发中断逐字节存入。等待应答时先清空计数器,然后轮询超时检测:

u8 RFID_WaitResponse(u32 timeout_ms)
{
u32 t;
rfid_rx_cnt = 0;
rfid_rx_flag = 0;
for(t = 0; t < timeout_ms; t++)
{
delay_ms(1);
if(rfid_rx_cnt > 0)
{
delay_ms(50); /* 等整帧收完 */
rfid_rx_flag = 1;
return RFID_OK;
}
}
return RFID_TIMEOUT;
}

检测到第一个字节后延时50ms确保整帧收完,再返回给上层处理。这是基于9600bps下11字节约需11ms的保守估计。

用户界面与交互设计#

状态机设计#

界面采用二层状态机架构:

第一层 (UI_State_t):
MAIN ──→ INIT ──→ RESULT ──→ MAIN
│ │ ↑
│ │ │
├──→ WALLET ────→ RESULT
│ ↑
│ │
└──→ QUERY ────→ RESULT
第二层 (Wallet_SubState_t):
SELECT ──→ AMOUNT ──→ SWIPE ──→ RESULT

所有页面按任意键返回主菜单,操作有5秒超时机制。

按键映射#

按键主菜单增/减选择金额选择结果页
K1初始化钱包选择”增值”上移选项返回
K2增/减值选择”减值”下移选项返回
K3查询余额确认进入确认刷卡返回

金额档位#

预设4个金额档位(单位为分):

索引金额(分)显示
01001.00 Y
15005.00 Y
2100010.00 Y
3500050.00 Y

金额显示#

采用 “分 → X.XX Y” 格式化,避免浮点数运算:

static void fmt_money(s32 fen, u8 *buf)
{
if(fen < 0)
sprintf((char*)buf, "-%d.%02dY", (int)(-fen/100), (int)(-fen%100));
else
sprintf((char*)buf, "%d.%02dY", (int)(fen/100), (int)(fen%100));
}

配色方案#

LCD界面采用白底色系配色方案,通过色块区分不同类型的UI元素:

用途颜色值效果
全局背景WHITE白色
标题栏0x4A49深灰蓝
选中项0x04FF蓝色高亮
成功提示0x0660绿色
失败/减值0xC000红色
增值金额0x0400深绿
减值金额0xC000红色

非阻塞超时轮询机制#

刷卡等待采用非阻塞倒计时方案,避免死等导致界面无响应:

#define WAIT_TICKS 500u /* 500×10ms = 5秒 */
static u8 wait_poll(void)
{
u8 r;
if(g_tick == 0) return RFID_TIMEOUT;
g_tick--;
r = RFID_ReadUID(g_uid);
if(r == RFID_OK) return RFID_OK;
return RFID_NONE; /* 本次未读到,继续等 */
}

主循环每10ms调用一次,500次(5秒)后自动超时。期间可以检测按键、处理中断,不会被阻塞。YMC1501的读卡号命令0xA1发送/应答约需50ms,在500次循环中足以轮询多次。

LDC1614 电感传感器驱动#

项目预留了LDC1614电感传感器的驱动支持,用于测量物体距离。该芯片是TI的4通道电感数字转换器,通过I2C接口通信。

软件I2C实现#

由于硬件I2C引脚冲突,采用GPIO模拟I2C(位带操作):

// PB10=SCL, PB11=SDA 开漏输出
SDA_IN() // 切换为输入模式
SDA_OUT() // 切换为输出模式

SMBus写操作(写寄存器):

START → 设备地址+W → ACK → 寄存器地址 → ACK → 数据高字节 → ACK → 数据低字节 → ACK → STOP

SMBus读操作(读寄存器):

START → 设备地址+W → ACK → 寄存器地址 → ACK
RESTART → 设备地址+R → ACK → 数据高字节 → ACK → 数据低字节 → NACK → STOP

LDC1614初始化流程#

  1. 软件复位:写入 RESET_DEVICE 寄存器 0x8000,延时20ms
  2. 读取设备ID寄存器(0x7F)验证芯片型号(兼容 0x3055 / 0x3054)
  3. 配置4个通道的参考计数(0xFFFF)、偏移量(0x0000)、稳定计数(0x0400)、时钟分频器(0x1001)
  4. 配置MUX选择、系统时钟、驱动电流(0xF000)
  5. 延时100ms等待传感器稳定

S50卡电子钱包原理#

Mifare S50卡的每个扇区由4个数据块组成(每块16字节),第4块为密钥/访问控制块。YMC1501在后台完成所有S50底层操作(认证、读写块、值块格式转换),上位机只需要发送高层钱包命令。

钱包块(值块) 是S50卡的特殊数据格式:

  • 4字节值(小端)+ 4字节反值 + 4字节值(冗余)+ 1字节地址
  • 钱包操作(增/减/恢复)在卡内由芯片硬件执行,保证原子性

本系统使用扇区1的块0(绝对块号4)作为钱包块,密钥为默认KEYA(0xFFFFFFFFFFFF)。

编译信息#

  • 编译器: Keil5 ARMCC
  • 启动文件: startup_stm32f10x_hd.s (高密度Flash)
  • 外设库: STM32F10x Standard Peripheral Library
  • 优化级别: 默认

调试顺序建议#

  1. 先烧录程序,检查LCD启动画面是否正常显示
  2. 用串口助手连接USART1 (115200bps),观察printf调试输出
  3. 放一张M1 S50卡在读写器上,按K3测试读卡号功能
  4. 按K1初始化钱包(会提示放卡,5秒内放卡即写余额为0)
  5. 按K2测试充值/扣款(选择增/减 → 选金额 → 刷卡确认)
  6. 按K3查询余额验证操作结果

完整代码和工程文件位于 GitHub Repository

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

基于STM32与YMC1501的RFID电子钱包系统设计与实现
https://qianban.online/posts/project/rfid_wallet/
作者
千板な
发布于
2026-05-07
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录