范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

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卡上。包括:激活状态、卡所属人信息,金额等。
  所以在程序里定义了一个结构体: #pragma pack(1)  //这个结构体大小为16个字节,刚好存放到 IC卡的一个块里面  typedef struct CARD_INFO  {      u8  stat;     //卡状态. 66表示此卡已经激活 其他值表示此卡未激活                    //        88表示此卡挂失,无法再进行消费      u32 money;    //金额. 第一次激活卡,就将金额清0      u8  phone[11];//可以存放电话号码,ID,标识符之类的数据  }CARD;  extern u8 IC_Card_uid[4];
  并封装了两个底层函数: 接下来的所有对卡的操作只需要调用下面函数即可。   //读取卡号  u8 IC_Card_uid[4];    /*  card_uid :卡的id号外部5字节数组  data     : 读出来的一个块,16字节数据  addr     : 块号,从4开始  数据存放的地址。每个扇区的0、1、2块是存放数据。3是存放密码。                 一般填:0、1、2 、4、5、6、8、9、10  数据一般格式:u8 SJ[16]={255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255}; //写入的金额;    */  u8 IC_Card_Read(CARD *rdata)  {      u8 KEY[6] = {0xff,0xff,0xff,0xff,0xff,0xff};    //白卡的出厂密码      u8 status;        /*1. 寻卡*/      status = search_card(IC_Card_uid);        /*2. 验证卡密码*/      if(MI_OK==status)      {           print_CardNnmber(IC_Card_uid);          status = RC522_PcdAuthState(PICC_AUTHENT1A, 3, KEY, IC_Card_uid);          //验证卡片密码       形参参数:验证方式,块地址,密码,卡序列号      }        /*3. 读出数据*/      if(MI_OK==status)      {          status = RC522_PcdRead(1,(u8*)rdata);   //从第addr块读出数据值。      }      return status;  }      /*  功能:写数据到指定块  参数:  u8   addr      :数据存放的地址。每个扇区的0、1、2块是存放数据。3是存放密码。                  一般填:0、1、2 、4、5、6、8、9、10  数据一般格式:u8 SJ[16]={255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255}; //写入的金额;    */  u8 IC_Card_Write(CARD *wdata)  {      u8 KEY[6] = {0xff,0xff,0xff,0xff,0xff,0xff};    //白卡的出厂密码      u8 status;        /*1. 寻卡*/      status = search_card(IC_Card_uid);        /*2. 验证卡密码*/      if(MI_OK==status)      {          status = RC522_PcdAuthState(PICC_AUTHENT1A, 3, KEY, IC_Card_uid);          //验证卡片密码       形参参数:验证方式,块地址,密码,卡序列号      }        /*3. 写数据到卡*/      if(MI_OK==status)      {          status = RC522_PcdWrite(1, (u8*)wdata); //写数据到第addr块,data入的数据值。      }      return status;  }2.2 编写案例接口
  为了方便理解整体的设计思路,下面针对几个常见的操作编写了函数接口测试Demo。 void Activation_CardInformation(void); //对卡激活-将卡状态设置为66  void Unlock_CardInformation(void);    //对卡解锁--去除挂失状态。将卡状态设置为66  void locking_CardInformation(void);  //对卡挂失。将卡状态设置为88  void Consumption_CardInformation(void); //消费. 消费就是减少金额.  void Recharge_CardInformation(void); //对卡进行充值. 充值就是累加金额  void Query_CardInformation(void); //查询卡的详细信息,通过串口打印
  源代码如下: #include "app.h"  /*  函数功能: 查询卡的详细信息,通过串口打印  */  void Query_CardInformation()  {      CARD data;      if(IC_Card_Read(&data)==MI_OK)      {          //判断卡是否已经激活          if(data.stat==66)          {              printf("用户信息:%sr ",data.phone);              printf("余额:%dr ",data.money);          }          else if(data.stat==88)          {               printf("此卡已挂失.请先解锁.r ");          }          //卡没有激活          else           {               printf("此卡没有激活.r ");          }             //复位--释放选中的卡片          RC522_PcdReset();      }  }      /*  函数功能: 对卡进行充值. 充值就是累加金额  */  void Recharge_CardInformation()  {      CARD data;      if(IC_Card_Read(&data)==MI_OK)      {          //判断卡是否已经激活          if(data.stat==66)          {              printf("用户信息:%sr ",data.phone);              printf("充值前的余额:%dr ",data.money);                                          //累加金额              data.money+=100; //充值100块                            //重新写入到卡里              RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。;                            printf("充值后的余额:%dr ",data.money);          }          //卡已经挂失          else if(data.stat==88)          {               printf("此卡已挂失.请先解锁后再充值.r ");          }          //卡没有激活          else           {               printf("此卡没有激活.请先激活后再充值.r ");          }             //复位--释放选中的卡片          RC522_PcdReset();      }  }      /*  函数功能: 消费. 消费就是减少金额.  */  void Consumption_CardInformation()  {      CARD data;      if(IC_Card_Read(&data)==MI_OK)      {          //判断卡是否已经激活          if(data.stat==66)          {              printf("用户信息:%sr ",data.phone);              printf("消费前的余额:%dr ",data.money);                            //消费金额,假如:我要消费10元,先判断卡里有没有10元,没有就不能消费.              printf("即将消费10元...r ");                            //余额足够才能消费              if(data.money>=10)              {                  data.money-=10; //减去10块                                                //重新写入到卡里                  RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。;                                    printf("消费后的余额:%dr ",data.money);              }              else              {                  printf("余额不足,消费失败...r ");              }          }          //卡已经挂失          else if(data.stat==88)          {               printf("此卡已挂失.请先解锁后再进行消费流程.r ");          }          //卡没有激活          else           {               printf("此卡没有激活.请先激活后再进行消费流程.r ");          }             //复位--释放选中的卡片          RC522_PcdReset();      }  }        /*  函数功能: 对卡挂失。将卡状态设置为88  */  void locking_CardInformation()  {      CARD data;      if(IC_Card_Read(&data)==MI_OK)      {          //判断卡是否已经激活          if(data.stat==66)          {              printf("用户信息:%sr ",data.phone);                          //设置挂失状态             data.stat=88;                           //重新写入到卡里             RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。;                           printf("此卡已成功挂失.r ");                        }          //卡已经挂失          else if(data.stat==88)          {               printf("此卡已挂失.r ");          }          //卡没有激活          else           {               printf("此卡没有激活.请先激活.r ");          }             //复位--释放选中的卡片          RC522_PcdReset();      }  }        /*  函数功能: 对卡解锁--去除挂失状态。将卡状态设置为66  */  void Unlock_CardInformation()  {      CARD data;      if(IC_Card_Read(&data)==MI_OK)      {          //判断卡是否已经激活          if(data.stat==66)          {             printf("此卡已解锁.r ");          }          //卡已经挂失          else if(data.stat==88)          {              //设置解锁状态             data.stat=66;                           //重新写入到卡里            RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。;                           printf("此卡已成功解锁.r ");          }          //卡没有激活          else           {               printf("此卡没有激活.请先激活.r ");          }             //复位--释放选中的卡片          RC522_PcdReset();      }  }    /*  函数功能: 对卡激活-将卡状态设置为66    激活卡也叫注册卡。可以写入一些用户信息到卡里.  */  void Activation_CardInformation()  {      CARD data;      if(IC_Card_Read(&data)==MI_OK)      {          //判断卡是否已经激活          if(data.stat==66)          {             printf("此卡已激活,不需要重复激活.r ");          }          //卡已经挂失          else if(data.stat==88)          {             printf("此卡已激活,并挂失,锁定,请先解锁...r ");          }          //卡没有激活          else           {              //设置解锁状态              data.stat=66;              strncpy((char*)data.phone,"473608901",sizeof(data.phone)-1);              //重新写入到卡里             // IC_Card_Write(&data);              /*3. 写数据到卡*/              RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。              printf("此卡已成功激活.用户信息:%sr ",data.phone);          }                    //复位--释放选中的卡片          RC522_PcdReset();      }  }2.3 编写操作界面
  为了方便测试功能,在LCD屏上绘制了几个矩形,触摸屏点击分别执行对应的功能。
  #include "app.h"    /*  RC522射频模块外部的接口:      *1--SDA <----->PB5--片选脚  *2--SCK <----->PB4--时钟线  *3--MOSI<----->PA12--输出  *4--MISO<----->PA11--输入  *5--悬空  *6--GND <----->GND  *7--RST <----->PA8--复位脚  *8--VCC <----->VCC  */      int main()  {      USARTx_Init(USART1,72,115200);      LCD_Init();      LCD_Clear(BLACK);      XPT2046_TouchInit();      RC522_Init();  //    DisplayString(0,0,16,"12345jkdbdfvdfvdfv7364837340hdxsmsks3743934ndvdfv",BLACK,WHITE);  //      //    POINT_COLOR=0x00FF; //设置画笔颜色  //    LCD_DrawLine(0,0,200,50); //画线  //         //颜色填充      LCD_Fill(0,0,120,105,RED);      //颜色填充      LCD_Fill(120,0,239,105,RED);      //颜色填充      LCD_Fill(0,105,120,210,RED);      //颜色填充      LCD_Fill(120,105,239,210,RED);      //颜色填充      LCD_Fill(0,210,120,320,RED);      //颜色填充      LCD_Fill(120,210,239,320,RED);      DisplayString(0,0,16,"Activation",BLACK,WHITE);      DisplayString(120,0,16,"Query",BLACK,WHITE);      DisplayString(0,105,16,"Recharge",BLACK,WHITE);      DisplayString(120,105,16,"Consumption",BLACK,WHITE);      DisplayString(0,210,16,"locking",BLACK,WHITE);      DisplayString(120,210,16,"Unlock",BLACK,WHITE);             while(1)      {             //扫描触摸屏坐标          if(XPT2046_ReadXY())          {                  printf("x=%d,y=%dr ",xpt2046_touch.x,xpt2046_touch.y);              printf("x0=%d,y0=%dr ",xpt2046_touch.x0,xpt2046_touch.y0);                              // 对卡激活-              if(xpt2046_touch.x>0&&xpt2046_touch.x<120&&                 xpt2046_touch.y>0&&xpt2046_touch.y<105)              {                  printf("---- 对卡激活-Demo----r ");                                  //充值Demo                  Activation_CardInformation();                  //颜色填充                  LCD_Fill(0,0,120,105,WHITE);                  DisplayString(0,0,16,"Activation",BLACK,WHITE);                  //等待触摸屏松开                  while(XPT2046_PEN==0){}                  //颜色填充                  LCD_Fill(0,0,120,105,RED);                  DisplayString(0,0,16,"Activation",BLACK,WHITE);              }                            //查询Demo              else if(xpt2046_touch.x>120&&xpt2046_touch.x<240&&                 xpt2046_touch.y>0&&xpt2046_touch.y<105)              {                  printf("----运行查询Demo----r ");                  //查询Demo                  Query_CardInformation();                  //颜色填充                  LCD_Fill(120,0,239,105,WHITE);                  DisplayString(120,0,16,"Query",BLACK,WHITE);                  //等待触摸屏松开                  while(XPT2046_PEN==0){}                     //颜色填充                  LCD_Fill(120,0,239,105,RED);                  DisplayString(120,0,16,"Query",BLACK,WHITE);              }                            //充值Demo              else if(xpt2046_touch.x>0&&xpt2046_touch.x<120&&                 xpt2046_touch.y>105&&xpt2046_touch.y<210)              {                  printf("----运行充值Demo----r ");                  //充值Demo                  Recharge_CardInformation();                  //颜色填充                  LCD_Fill(0,105,120,210,WHITE);                  DisplayString(0,105,16,"Recharge",BLACK,WHITE);                  //等待触摸屏松开                  while(XPT2046_PEN==0){}                     //颜色填充                  LCD_Fill(0,105,120,210,RED);                  DisplayString(0,105,16,"Recharge",BLACK,WHITE);              }                            //消费Demo              else if(xpt2046_touch.x>120&&xpt2046_touch.x<240&&                 xpt2046_touch.y>105&&xpt2046_touch.y<210)              {                  printf("----运行消费Demo----r ");                  //消费Demo                  Consumption_CardInformation();                  //颜色填充                  LCD_Fill(120,105,239,210,WHITE);                  DisplayString(120,105,16,"Consumption",BLACK,WHITE);                  //等待触摸屏松开                  while(XPT2046_PEN==0){}                  //颜色填充                  LCD_Fill(120,105,239,210,RED);                  DisplayString(120,105,16,"Consumption",BLACK,WHITE);                  //等待触摸屏松开              }                            //挂失Demo              else if(xpt2046_touch.x>0&&xpt2046_touch.x<120&&                 xpt2046_touch.y>210&&xpt2046_touch.y<320)              {                  printf("----运行挂失Demo----r ");                  //挂失Demo                  locking_CardInformation();                  //颜色填充                  LCD_Fill(0,210,120,320,WHITE);                  DisplayString(0,210,16,"locking",BLACK,WHITE);                  //等待触摸屏松开                  while(XPT2046_PEN==0){}                    //颜色填充                  LCD_Fill(0,210,120,320,RED);                  DisplayString(0,210,16,"locking",BLACK,WHITE);              }                               //解锁Demo              else if(xpt2046_touch.x>120&&xpt2046_touch.x<240&&                 xpt2046_touch.y>210&&xpt2046_touch.y<320)              {                  printf("----运行解锁Demo----r ");                  //解锁Demo                  Unlock_CardInformation();                  //颜色填充                  LCD_Fill(120,210,239,320,WHITE);                  DisplayString(120,210,16,"Unlock",BLACK,WHITE);                  //等待触摸屏松开                  while(XPT2046_PEN==0){}                      //颜色填充                  LCD_Fill(120,210,239,320,RED);                  DisplayString(120,210,16,"Unlock",BLACK,WHITE);              }              }                          delay_ms(10);      }  }2.4 运行效果
  点击下方,第一时间了解华为云新鲜技术~
  华为云博客_大数据博客_AI博客_云计算博客_开发者中心-华为云

新研究格陵兰岛冰如果全融化,海平面或升7。4米,影响有多大?全球变暖对大家来说都不陌生了,近些年来,几乎年年都在说。并且从才过去的2021年全球气候变化数据来看,也再次证明了全球变暖还在持续发展之中。根据美国宇航局(NASA)发布的2021被严重高估的三款旗舰机,配置高但缺点很明显,只怪太多人不了解旗舰手机价格贵,买到有缺点的手机大家心里一定不好受,小编给大家介绍三款被严重高估的旗舰机,虽然配置高但缺点真的很明显!第一款华为P50pro华为P50pro作为深受大家喜爱的华为旗被严重高估的三款旗舰机,配置高但缺点很明显,只怪太多人不了解旗舰手机价格贵,买到有缺点的手机大家心里一定不好受,小编给大家介绍三款被严重高估的旗舰机,虽然配置高但缺点真的很明显!第一款华为P50pro华为P50pro作为深受大家喜爱的华为旗喝白粥不养胃,当心反而伤胃!养胃这6招才有用天一冷,胃就格外爱闹毛病。常常胃胀胃痛胃酸于是,为了让自己的胃更强大,很多人会吃些养胃食物,比如白粥烂面条但长期这样吃,对胃反而不好。真相白粥里面主要是碳水化合物和水,确实好消化,几个睡眠好习惯今天和大家来聊聊几个好的睡眠习惯的问题1hr正常人睡多长时间?新生儿睡眠时间最长,可以达到16个小时。大多数成年人78小时足够了。随着年纪增长,睡眠时间逐步减少。2hr保持好的睡眠喝白粥不养胃,当心反而伤胃!养胃这6招才有用天一冷,胃就格外爱闹毛病。常常胃胀胃痛胃酸于是,为了让自己的胃更强大,很多人会吃些养胃食物,比如白粥烂面条但长期这样吃,对胃反而不好。真相白粥里面主要是碳水化合物和水,确实好消化,几个睡眠好习惯今天和大家来聊聊几个好的睡眠习惯的问题1hr正常人睡多长时间?新生儿睡眠时间最长,可以达到16个小时。大多数成年人78小时足够了。随着年纪增长,睡眠时间逐步减少。2hr保持好的睡眠刘玉翠跳广场舞,古灵精怪,网友我们的建宁公主回来了说起来刘玉翠这个名字,大家可能对这个名字不大熟悉,但是对于他饰演的角色,大家应该印象都非常的深刻。其实他曾经出演过天龙八部和陈小春版本的鹿鼎记。当时他凭借着自己古灵精怪的表演方式,刘玉翠跳广场舞,古灵精怪,网友我们的建宁公主回来了说起来刘玉翠这个名字,大家可能对这个名字不大熟悉,但是对于他饰演的角色,大家应该印象都非常的深刻。其实他曾经出演过天龙八部和陈小春版本的鹿鼎记。当时他凭借着自己古灵精怪的表演方式,编程语言将走入怎样的2022本文是2021InfoQ年度技术盘点与展望系列文章之一,由直播内容整理而成,重点聚焦编程语言领域在2022年的核心趋势展望,希望能帮助你准确把握2022年编程语言领域的核心发展脉络编程语言将走入怎样的2022本文是2021InfoQ年度技术盘点与展望系列文章之一,由直播内容整理而成,重点聚焦编程语言领域在2022年的核心趋势展望,希望能帮助你准确把握2022年编程语言领域的核心发展脉络
为什么彩超医生都不告诉是男孩还是女孩呢?禁止胎儿性别,这不仅仅是无数生命换来的法律规定。还有很多凄凉的故事。一人性重男轻女的社会现象依然存在,这样的家庭为了生儿子可以说是不择手段。生育本来是件好事,但在这类人群中却是迫害杨幂的高颜值原来是遗传了妈妈的好基因,网友妈妈更漂亮杨幂这几年可以说是火的一塌糊涂,对杨幂印象深刻的是,杨幂拍摄的仙剑奇侠传三,里面古灵精怪的雪见,真的是很漂亮。杨幂那时候应该还没有拔牙,所以脸看起来有一点方。杨幂因为这部电视剧名声看头条的男人们,一般用什么价位的手机?前些天,我有个小爆款,5万多的阅读,从作品详细数据可以见到一二。我这个微头条说的是小米12和红米K50电竞版对比,总共34万8的展现量,阅读量为5万2,性别分布男性比例达到了99。为什么压力大时会长白头发?这些习惯可能会催生白发白头发被人们当作是变老的特征之一随着人的年纪越来越大脸上长了皱纹头发也变得花白但有一部分人在年轻的时候也长出了白发我们称之为少白头也有些人生活中遇到重大变故时有过一夜愁白头的经历黑女,32岁,脚后跟干裂,裂口子的地方还有硬皮,怎么办?提问女,32岁,脚后跟干裂,按下去会有点疼,裂口子的地方还有硬皮,我天天洗脚的,应该不是卫生问题,最近才发现这样的,之前也没注意,不知道什么时候开始有的,有什么好办法?回答李芳郑州独家对话四川冬奥技术官员看到苏翊鸣登上领奖台,情不自禁热泪盈眶四川体育职业学院冬季运动管理中心单双板滑雪队坡面障碍技巧教练李云峰封面新闻记者陈甘露陈羽啸张家口报道图由受访者提供2月20日,2022年北京冬奥会即将在依依不舍中落下帷幕,中国代表冬奥记忆瓦利耶娃与谷爱凌,两大美少女,以不同方式被世人牢记瞩目为期17天的202北京冬奥闭幕,2800多名运动员用努力和汗水,留下不少动人回忆和纪录,偏偏最令人难以忘怀的一位,却是只得15岁的俄罗斯奥委会花样滑冰代表瓦利耶娃。独领二金一银这届冬奥会,短视频的正确打开方式站在2022北京冬奥会的尾巴上,大概很多人已经开始想念。无论是行云流水的开幕式,于细节之处见中国式浪漫还是不断拼搏的运动员们,或激动夺冠或挑战极限,都释放着热血与奥运精神顶流冰墩墩追梦格林住所被盗百万美元财物!詹皇有不在场证明,二球有点惨据TMZ报道,勇士前锋德雷蒙德格林在洛杉矶的家在超级碗期间遭小偷光顾,包括手表珠宝在内超过价值100万美元的物品被盗。执法部门的消息人士告知,31岁的格林当晚看完超级碗回到家里,发看了王馥荔发福后的老年穿搭,才发现胖妈妈穿得得体比显瘦更美文小润在生活中,很多妈妈一到老年就不可避免地陷入身材发福的境遇,从而导致穿衣显胖显壮,就连女明星也是一样。比如72岁的天下第一嫂王馥荔老师。但不同的是,相比起我们普通妈妈而言,王馥还未绽放就已落幕!K宝输得令人心疼,生涯恐无缘奥运女单自由滑决赛在争议中落下帷幕,三套娃的盛宴大餐如期上演,只可惜最终的结果并不完美,除了K宝频繁失误无缘领奖台之外,二娃莎莎特鲁索娃赛后更是暴怒,直言恨这项运动并一度拒绝登台领奖。