动手实操丨RC522射频卡模块与IC卡完成充值消费查询的技术实现思路

x33g5p2x  于2022-05-06 转载在 其他  
字(9.2k)|赞(0)|评价(0)|浏览(429)

本文分享自华为云社区《​​​​​​​RC522射频卡模块与IC卡完成充值消费查询的技术实现思路》,作者:DS小龙哥。

一、IC卡介绍

常用的IC卡一般是M1卡,也称为S50卡,购买RC522刷卡模块送的白卡,蓝色钥匙扣、公交卡、地铁卡都是S50卡。S50卡内部有16个分区,每分区有AB两组密码,总容量为8Kbit。

第0个扇区第0块用于存放厂商代码,意见固话,不可更改。

每个扇区的块0、块1、块2为数据块,可以用于存储数据。数据块可以进行读写操作。

每个扇区的块3为控制块,包括了密码A、存储控制、密码B。具体结构如下:

每个扇区的密码和控制位都是独立的,可以根据实际需求设定各自的密码及存取控制。存取控制为4个字节,共32位,扇区中的每个块(包括数据和控制块)存取条件是由密码和存取控制共同决定的,在存取控制中每个块都有一个相应的三个控制位。

重点总结:

(1)M1卡分为16个扇区,每个扇区由4块(0、1、2、3)组成。在实际操作时,将16个扇区分为64个块,按绝对地址编号为0-63进行访问,也就是程序里需要填块的位置时,范围是0~63。

(2)每个块的大小是16字节,每个扇区里有3个数据块,数据块可以存放自己的自定义数据。

二、一卡通消费机实现原理

2.1 封装核心函数

(1)主要的硬件:单片机选择STM32,刷卡模块采用RC522。

(2)实现核心思路:为了方便存储数据,对数据进行管理,保证程序的通用性,将IC卡的所有信息都存放在IC卡上。包括:激活状态、卡所属人信息,金额等。

所以在程序里定义了一个结构体:

  1. #pragma pack(1)
  2. //这个结构体大小为16个字节,刚好存放到 IC卡的一个块里面
  3. typedef struct CARD_INFO
  4. {
  5. u8 stat; //卡状态. 66表示此卡已经激活 其他值表示此卡未激活
  6. // 88表示此卡挂失,无法再进行消费
  7. u32 money; //金额. 第一次激活卡,就将金额清0
  8. u8 phone[11];//可以存放电话号码,ID,标识符之类的数据
  9. }CARD;
  10. extern u8 IC_Card_uid[4];

并封装了两个底层函数: 接下来的所有对卡的操作只需要调用下面函数即可。​

  1. //读取卡号
  2. u8 IC_Card_uid[4];
  3. /*
  4. card_uid :卡的id号外部5字节数组
  5. data : 读出来的一个块,16字节数据
  6. addr : 块号,从4开始
  7. 数据存放的地址。每个扇区的0、1、2块是存放数据。3是存放密码。
  8. 一般填:0、1、2 、4、5、6、8、9、10
  9. 数据一般格式:u8 SJ[16]={255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255}; //写入的金额;
  10. */
  11. u8 IC_Card_Read(CARD *rdata)
  12. {
  13. u8 KEY[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; //白卡的出厂密码
  14. u8 status;
  15. /*1. 寻卡*/
  16. status = search_card(IC_Card_uid);
  17. /*2. 验证卡密码*/
  18. if(MI_OK==status)
  19. {
  20. print_CardNnmber(IC_Card_uid);
  21. status = RC522_PcdAuthState(PICC_AUTHENT1A, 3, KEY, IC_Card_uid);
  22. //验证卡片密码 形参参数:验证方式,块地址,密码,卡序列号
  23. }
  24. /*3. 读出数据*/
  25. if(MI_OK==status)
  26. {
  27. status = RC522_PcdRead(1,(u8*)rdata); //从第addr块读出数据值。
  28. }
  29. return status;
  30. }
  31. /*
  32. 功能:写数据到指定块
  33. 参数:
  34. u8 addr :数据存放的地址。每个扇区的0、1、2块是存放数据。3是存放密码。
  35. 一般填:0、1、2 、4、5、6、8、9、10
  36. 数据一般格式:u8 SJ[16]={255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255}; //写入的金额;
  37. */
  38. u8 IC_Card_Write(CARD *wdata)
  39. {
  40. u8 KEY[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; //白卡的出厂密码
  41. u8 status;
  42. /*1. 寻卡*/
  43. status = search_card(IC_Card_uid);
  44. /*2. 验证卡密码*/
  45. if(MI_OK==status)
  46. {
  47. status = RC522_PcdAuthState(PICC_AUTHENT1A, 3, KEY, IC_Card_uid);
  48. //验证卡片密码 形参参数:验证方式,块地址,密码,卡序列号
  49. }
  50. /*3. 写数据到卡*/
  51. if(MI_OK==status)
  52. {
  53. status = RC522_PcdWrite(1, (u8*)wdata); //写数据到第addr块,data入的数据值。
  54. }
  55. return status;
  56. }

2.2 编写案例接口

为了方便理解整体的设计思路,下面针对几个常见的操作编写了函数接口测试Demo。

  1. void Activation_CardInformation(void); //对卡激活-将卡状态设置为66
  2. void Unlock_CardInformation(void); //对卡解锁--去除挂失状态。将卡状态设置为66
  3. void locking_CardInformation(void); //对卡挂失。将卡状态设置为88
  4. void Consumption_CardInformation(void); //消费. 消费就是减少金额.
  5. void Recharge_CardInformation(void); //对卡进行充值. 充值就是累加金额
  6. void Query_CardInformation(void); //查询卡的详细信息,通过串口打印

源代码如下:

  1. #include "app.h"
  2. /*
  3. 函数功能: 查询卡的详细信息,通过串口打印
  4. */
  5. void Query_CardInformation()
  6. {
  7. CARD data;
  8. if(IC_Card_Read(&data)==MI_OK)
  9. {
  10. //判断卡是否已经激活
  11. if(data.stat==66)
  12. {
  13. printf("用户信息:%s\r\n",data.phone);
  14. printf("余额:%d\r\n",data.money);
  15. }
  16. else if(data.stat==88)
  17. {
  18. printf("此卡已挂失.请先解锁.\r\n");
  19. }
  20. //卡没有激活
  21. else
  22. {
  23. printf("此卡没有激活.\r\n");
  24. }
  25. //复位--释放选中的卡片
  26. RC522_PcdReset();
  27. }
  28. }
  29. /*
  30. 函数功能: 对卡进行充值. 充值就是累加金额
  31. */
  32. void Recharge_CardInformation()
  33. {
  34. CARD data;
  35. if(IC_Card_Read(&data)==MI_OK)
  36. {
  37. //判断卡是否已经激活
  38. if(data.stat==66)
  39. {
  40. printf("用户信息:%s\r\n",data.phone);
  41. printf("充值前的余额:%d\r\n",data.money);
  42. //累加金额
  43. data.money+=100; //充值100块
  44. //重新写入到卡里
  45. RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。;
  46. printf("充值后的余额:%d\r\n",data.money);
  47. }
  48. //卡已经挂失
  49. else if(data.stat==88)
  50. {
  51. printf("此卡已挂失.请先解锁后再充值.\r\n");
  52. }
  53. //卡没有激活
  54. else
  55. {
  56. printf("此卡没有激活.请先激活后再充值.\r\n");
  57. }
  58. //复位--释放选中的卡片
  59. RC522_PcdReset();
  60. }
  61. }
  62. /*
  63. 函数功能: 消费. 消费就是减少金额.
  64. */
  65. void Consumption_CardInformation()
  66. {
  67. CARD data;
  68. if(IC_Card_Read(&data)==MI_OK)
  69. {
  70. //判断卡是否已经激活
  71. if(data.stat==66)
  72. {
  73. printf("用户信息:%s\r\n",data.phone);
  74. printf("消费前的余额:%d\r\n",data.money);
  75. //消费金额,假如:我要消费10元,先判断卡里有没有10元,没有就不能消费.
  76. printf("即将消费10元...\r\n");
  77. //余额足够才能消费
  78. if(data.money>=10)
  79. {
  80. data.money-=10; //减去10块
  81. //重新写入到卡里
  82. RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。;
  83. printf("消费后的余额:%d\r\n",data.money);
  84. }
  85. else
  86. {
  87. printf("余额不足,消费失败...\r\n");
  88. }
  89. }
  90. //卡已经挂失
  91. else if(data.stat==88)
  92. {
  93. printf("此卡已挂失.请先解锁后再进行消费流程.\r\n");
  94. }
  95. //卡没有激活
  96. else
  97. {
  98. printf("此卡没有激活.请先激活后再进行消费流程.\r\n");
  99. }
  100. //复位--释放选中的卡片
  101. RC522_PcdReset();
  102. }
  103. }
  104. /*
  105. 函数功能: 对卡挂失。将卡状态设置为88
  106. */
  107. void locking_CardInformation()
  108. {
  109. CARD data;
  110. if(IC_Card_Read(&data)==MI_OK)
  111. {
  112. //判断卡是否已经激活
  113. if(data.stat==66)
  114. {
  115. printf("用户信息:%s\r\n",data.phone);
  116. //设置挂失状态
  117. data.stat=88;
  118. //重新写入到卡里
  119. RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。;
  120. printf("此卡已成功挂失.\r\n");
  121. }
  122. //卡已经挂失
  123. else if(data.stat==88)
  124. {
  125. printf("此卡已挂失.\r\n");
  126. }
  127. //卡没有激活
  128. else
  129. {
  130. printf("此卡没有激活.请先激活.\r\n");
  131. }
  132. //复位--释放选中的卡片
  133. RC522_PcdReset();
  134. }
  135. }
  136. /*
  137. 函数功能: 对卡解锁--去除挂失状态。将卡状态设置为66
  138. */
  139. void Unlock_CardInformation()
  140. {
  141. CARD data;
  142. if(IC_Card_Read(&data)==MI_OK)
  143. {
  144. //判断卡是否已经激活
  145. if(data.stat==66)
  146. {
  147. printf("此卡已解锁.\r\n");
  148. }
  149. //卡已经挂失
  150. else if(data.stat==88)
  151. {
  152. //设置解锁状态
  153. data.stat=66;
  154. //重新写入到卡里
  155. RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。;
  156. printf("此卡已成功解锁.\r\n");
  157. }
  158. //卡没有激活
  159. else
  160. {
  161. printf("此卡没有激活.请先激活.\r\n");
  162. }
  163. //复位--释放选中的卡片
  164. RC522_PcdReset();
  165. }
  166. }
  167. /*
  168. 函数功能: 对卡激活-将卡状态设置为66
  169. 激活卡也叫注册卡。可以写入一些用户信息到卡里.
  170. */
  171. void Activation_CardInformation()
  172. {
  173. CARD data;
  174. if(IC_Card_Read(&data)==MI_OK)
  175. {
  176. //判断卡是否已经激活
  177. if(data.stat==66)
  178. {
  179. printf("此卡已激活,不需要重复激活.\r\n");
  180. }
  181. //卡已经挂失
  182. else if(data.stat==88)
  183. {
  184. printf("此卡已激活,并挂失,锁定,请先解锁...\r\n");
  185. }
  186. //卡没有激活
  187. else
  188. {
  189. //设置解锁状态
  190. data.stat=66;
  191. strncpy((char*)data.phone,"473608901",sizeof(data.phone)-1);
  192. //重新写入到卡里
  193. // IC_Card_Write(&data);
  194. /*3. 写数据到卡*/
  195. RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。
  196. printf("此卡已成功激活.用户信息:%s\r\n",data.phone);
  197. }
  198. //复位--释放选中的卡片
  199. RC522_PcdReset();
  200. }
  201. }

2.3 编写操作界面

为了方便测试功能,在LCD屏上绘制了几个矩形,触摸屏点击分别执行对应的功能。

  1. #include "app.h"
  2. /*
  3. RC522射频模块外部的接口:
  4. *1--SDA <----->PB5--片选脚
  5. *2--SCK <----->PB4--时钟线
  6. *3--MOSI<----->PA12--输出
  7. *4--MISO<----->PA11--输入
  8. *5--悬空
  9. *6--GND <----->GND
  10. *7--RST <----->PA8--复位脚
  11. *8--VCC <----->VCC
  12. */
  13. int main()
  14. {
  15. USARTx_Init(USART1,72,115200);
  16. LCD_Init();
  17. LCD_Clear(BLACK);
  18. XPT2046_TouchInit();
  19. RC522_Init();
  20. // DisplayString(0,0,16,"12345jkdbdfvdfvdfv7364837340hdxsmsks3743934ndvdfv",BLACK,WHITE);
  21. //
  22. // POINT_COLOR=0x00FF; //设置画笔颜色
  23. // LCD_DrawLine(0,0,200,50); //画线
  24. //
  25. //颜色填充
  26. LCD_Fill(0,0,120,105,RED);
  27. //颜色填充
  28. LCD_Fill(120,0,239,105,RED);
  29. //颜色填充
  30. LCD_Fill(0,105,120,210,RED);
  31. //颜色填充
  32. LCD_Fill(120,105,239,210,RED);
  33. //颜色填充
  34. LCD_Fill(0,210,120,320,RED);
  35. //颜色填充
  36. LCD_Fill(120,210,239,320,RED);
  37. DisplayString(0,0,16,"Activation",BLACK,WHITE);
  38. DisplayString(120,0,16,"Query",BLACK,WHITE);
  39. DisplayString(0,105,16,"Recharge",BLACK,WHITE);
  40. DisplayString(120,105,16,"Consumption",BLACK,WHITE);
  41. DisplayString(0,210,16,"locking",BLACK,WHITE);
  42. DisplayString(120,210,16,"Unlock",BLACK,WHITE);
  43. while(1)
  44. {
  45. //扫描触摸屏坐标
  46. if(XPT2046_ReadXY())
  47. {
  48. printf("x=%d,y=%d\r\n",xpt2046_touch.x,xpt2046_touch.y);
  49. printf("x0=%d,y0=%d\r\n",xpt2046_touch.x0,xpt2046_touch.y0);
  50. // 对卡激活-
  51. if(xpt2046_touch.x>0&&xpt2046_touch.x<120&&
  52. xpt2046_touch.y>0&&xpt2046_touch.y<105)
  53. {
  54. printf("---- 对卡激活-Demo----\r\n");
  55. //充值Demo
  56. Activation_CardInformation();
  57. //颜色填充
  58. LCD_Fill(0,0,120,105,WHITE);
  59. DisplayString(0,0,16,"Activation",BLACK,WHITE);
  60. //等待触摸屏松开
  61. while(XPT2046_PEN==0){}
  62. //颜色填充
  63. LCD_Fill(0,0,120,105,RED);
  64. DisplayString(0,0,16,"Activation",BLACK,WHITE);
  65. }
  66. //查询Demo
  67. else if(xpt2046_touch.x>120&&xpt2046_touch.x<240&&
  68. xpt2046_touch.y>0&&xpt2046_touch.y<105)
  69. {
  70. printf("----运行查询Demo----\r\n");
  71. //查询Demo
  72. Query_CardInformation();
  73. //颜色填充
  74. LCD_Fill(120,0,239,105,WHITE);
  75. DisplayString(120,0,16,"Query",BLACK,WHITE);
  76. //等待触摸屏松开
  77. while(XPT2046_PEN==0){}
  78. //颜色填充
  79. LCD_Fill(120,0,239,105,RED);
  80. DisplayString(120,0,16,"Query",BLACK,WHITE);
  81. }
  82. //充值Demo
  83. else if(xpt2046_touch.x>0&&xpt2046_touch.x<120&&
  84. xpt2046_touch.y>105&&xpt2046_touch.y<210)
  85. {
  86. printf("----运行充值Demo----\r\n");
  87. //充值Demo
  88. Recharge_CardInformation();
  89. //颜色填充
  90. LCD_Fill(0,105,120,210,WHITE);
  91. DisplayString(0,105,16,"Recharge",BLACK,WHITE);
  92. //等待触摸屏松开
  93. while(XPT2046_PEN==0){}
  94. //颜色填充
  95. LCD_Fill(0,105,120,210,RED);
  96. DisplayString(0,105,16,"Recharge",BLACK,WHITE);
  97. }
  98. //消费Demo
  99. else if(xpt2046_touch.x>120&&xpt2046_touch.x<240&&
  100. xpt2046_touch.y>105&&xpt2046_touch.y<210)
  101. {
  102. printf("----运行消费Demo----\r\n");
  103. //消费Demo
  104. Consumption_CardInformation();
  105. //颜色填充
  106. LCD_Fill(120,105,239,210,WHITE);
  107. DisplayString(120,105,16,"Consumption",BLACK,WHITE);
  108. //等待触摸屏松开
  109. while(XPT2046_PEN==0){}
  110. //颜色填充
  111. LCD_Fill(120,105,239,210,RED);
  112. DisplayString(120,105,16,"Consumption",BLACK,WHITE);
  113. //等待触摸屏松开
  114. }
  115. //挂失Demo
  116. else if(xpt2046_touch.x>0&&xpt2046_touch.x<120&&
  117. xpt2046_touch.y>210&&xpt2046_touch.y<320)
  118. {
  119. printf("----运行挂失Demo----\r\n");
  120. //挂失Demo
  121. locking_CardInformation();
  122. //颜色填充
  123. LCD_Fill(0,210,120,320,WHITE);
  124. DisplayString(0,210,16,"locking",BLACK,WHITE);
  125. //等待触摸屏松开
  126. while(XPT2046_PEN==0){}
  127. //颜色填充
  128. LCD_Fill(0,210,120,320,RED);
  129. DisplayString(0,210,16,"locking",BLACK,WHITE);
  130. }
  131. //解锁Demo
  132. else if(xpt2046_touch.x>120&&xpt2046_touch.x<240&&
  133. xpt2046_touch.y>210&&xpt2046_touch.y<320)
  134. {
  135. printf("----运行解锁Demo----\r\n");
  136. //解锁Demo
  137. Unlock_CardInformation();
  138. //颜色填充
  139. LCD_Fill(120,210,239,320,WHITE);
  140. DisplayString(120,210,16,"Unlock",BLACK,WHITE);
  141. //等待触摸屏松开
  142. while(XPT2046_PEN==0){}
  143. //颜色填充
  144. LCD_Fill(120,210,239,320,RED);
  145. DisplayString(120,210,16,"Unlock",BLACK,WHITE);
  146. }
  147. }
  148. delay_ms(10);
  149. }
  150. }

2.4 运行效果

点击关注,第一时间了解华为云新鲜技术~

相关文章