基于STM32设计的校园一卡通项目-详解原理

Posted DS小龙哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于STM32设计的校园一卡通项目-详解原理相关的知识,希望对你有一定的参考价值。

一、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("用户信息:%s\\r\\n",data.phone);
            printf("余额:%d\\r\\n",data.money);
        
        else if(data.stat==88)
        
             printf("此卡已挂失.请先解锁.\\r\\n");
        
        //卡没有激活
        else 
        
             printf("此卡没有激活.\\r\\n");
        
           //复位--释放选中的卡片
        RC522_PcdReset();
    



/*
函数功能: 对卡进行充值. 充值就是累加金额
*/
void Recharge_CardInformation()

    CARD data;
    if(IC_Card_Read(&data)==MI_OK)
    
        //判断卡是否已经激活
        if(data.stat==66)
        
            printf("用户信息:%s\\r\\n",data.phone);
            printf("充值前的余额:%d\\r\\n",data.money);
            
            
            //累加金额
            data.money+=100; //充值100块
            
            //重新写入到卡里
            RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。;
            
            printf("充值后的余额:%d\\r\\n",data.money);
        
        //卡已经挂失
        else if(data.stat==88)
        
             printf("此卡已挂失.请先解锁后再充值.\\r\\n");
        
        //卡没有激活
        else 
        
             printf("此卡没有激活.请先激活后再充值.\\r\\n");
        
           //复位--释放选中的卡片
        RC522_PcdReset();
    



/*
函数功能: 消费. 消费就是减少金额.
*/
void Consumption_CardInformation()

    CARD data;
    if(IC_Card_Read(&data)==MI_OK)
    
        //判断卡是否已经激活
        if(data.stat==66)
        
            printf("用户信息:%s\\r\\n",data.phone);
            printf("消费前的余额:%d\\r\\n",data.money);
            
            //消费金额,假如:我要消费10元,先判断卡里有没有10元,没有就不能消费.
            printf("即将消费10元...\\r\\n");
            
            //余额足够才能消费
            if(data.money>=10)
            
                data.money-=10; //减去10块
                            
                //重新写入到卡里
                RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。;
                
                printf("消费后的余额:%d\\r\\n",data.money);
            
            else
            
                printf("余额不足,消费失败...\\r\\n");
            
        
        //卡已经挂失
        else if(data.stat==88)
        
             printf("此卡已挂失.请先解锁后再进行消费流程.\\r\\n");
        
        //卡没有激活
        else 
        
             printf("此卡没有激活.请先激活后再进行消费流程.\\r\\n");
        
           //复位--释放选中的卡片
        RC522_PcdReset();
    




/*
函数功能: 对卡挂失。将卡状态设置为88
*/
void locking_CardInformation()

    CARD data;
    if(IC_Card_Read(&data)==MI_OK)
    
        //判断卡是否已经激活
        if(data.stat==66)
        
            printf("用户信息:%s\\r\\n",data.phone);
           
           //设置挂失状态
           data.stat=88;
            
           //重新写入到卡里
           RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。;
            
           printf("此卡已成功挂失.\\r\\n");
            
        
        //卡已经挂失
        else if(data.stat==88)
        
             printf("此卡已挂失.\\r\\n");
        
        //卡没有激活
        else 
        
             printf("此卡没有激活.请先激活.\\r\\n");
        
           //复位--释放选中的卡片
        RC522_PcdReset();
    




/*
函数功能: 对卡解锁--去除挂失状态。将卡状态设置为66
*/
void Unlock_CardInformation()

    CARD data;
    if(IC_Card_Read(&data)==MI_OK)
    
        //判断卡是否已经激活
        if(data.stat==66)
        
           printf("此卡已解锁.\\r\\n");
        
        //卡已经挂失
        else if(data.stat==88)
        
            //设置解锁状态
           data.stat=66;
            
           //重新写入到卡里
          RC522_PcdWrite(1, (u8*)&data); //写数据到第addr块,data入的数据值。;
            
           printf("此卡已成功解锁.\\r\\n");
        
        //卡没有激活
        else 
        
             printf("此卡没有激活.请先激活.\\r\\n");
        
           //复位--释放选中的卡片
        RC522_PcdReset();
    


/*
函数功能: 对卡激活-将卡状态设置为66

激活卡也叫注册卡。可以写入一些用户信息到卡里.
*/
void Activation_CardInformation()

    CARD data;
    if(IC_Card_Read(&data)==MI_OK)
    
        //判断卡是否已经激活
        if(data.stat==66)
        
           printf("此卡已激活,不需要重复激活.\\r\\n");
        
        //卡已经挂失
        else if(data.stat==88)
        
           printf("此卡已激活,并挂失,锁定,请先解锁...\\r\\n");
        
        //卡没有激活
        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("此卡已成功激活.用户信息:%s\\r\\n",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=%d\\r\\n",xpt2046_touch.x,xpt2046_touch.y);
            printf("x0=%d,y0=%d\\r\\n",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\\n");
              
                //充值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\\n");
                //查询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\\n");
                //充值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\\n");
                //消费Demo
                Consumption_CardInformation(以上是关于基于STM32设计的校园一卡通项目-详解原理的主要内容,如果未能解决你的问题,请参考以下文章

基于STM32设计的校园一卡通项目

STM32毕业设计项目

基于jsp校园一卡通系统

Java精品项目源码第102期嘟嘟校园一卡通系统

基于STM32的高精度温度测控系统-原理图设计

基于STM32单片机项目设计目录-加油吧√