当前位置:网站首页>STM32与RC522简单公交卡系统的设计
STM32与RC522简单公交卡系统的设计
2022-06-27 19:55:00 【物联网小菜鸟一枚】
目录
前言
本篇博客旨在给大家提供一个公交卡系统的设计思路,相关硬件以及读卡写卡的流程请参考我上一篇博客。
基于STM32的RC522模块读写数据块以及电子钱包充值扣款系统的设计
系统的结构框图
目前所用到的硬件有RC522,STM32和矩阵键盘,实物图如下:
引脚连接以及相关接口设计
RC522相关的引脚在我上一篇博客已经说过,请大家点开本博客开头的链接查看,4x4的矩阵键盘相关行我们用到的引脚是PF8,9,10,11,相关列用到的引脚是PF12,13,14,15。
按键相关代码如下:
按键初始化函数
void Matrix_ssKey_Pin_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;//行按键
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOF,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;//列按键
GPIO_Init(GPIOF,&GPIO_InitStructure);
}
按键扫描函数
int Matrix_Key_Scan(void)
{
u8 temp = 0;
int key_val = -1;
GPIO_ResetBits(GPIOF,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11);
delay_us(10);
temp=(GPIO_ReadInputData(GPIOF) >> 8)&0xff;
if (temp == 0xf0)
{
delay_ms(50);
GPIO_ResetBits(GPIOF,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11);
delay_us(10);
temp=(GPIO_ReadInputData(GPIOF) >> 8)&0xff;
if (temp != 0xf0)
{
GPIO_Write(GPIOF,0);
delay_ms(5);
GPIO_Write(GPIOF,(uint16_t)(0xFE << 8));
if(((GPIO_ReadInputData(GPIOF) >> 8) & 0XF0) != 0XF0)
{
delay_ms(20);
if(((GPIO_ReadInputData(GPIOF) >> 8) & 0XF0) != 0XF0)
{
temp=((GPIO_ReadInputData(GPIOF) >> 8) & 0XFE);
switch(temp)
{
case 0xEE: key_val = 1; break;
case 0xDE: key_val = 2; break;
case 0xBE: key_val = 3; break;
case 0x7E: key_val = 4; break;
default: key_val = -1; break;
}
}
}
GPIO_Write(GPIOF,0);
delay_ms(5);
GPIO_Write(GPIOF,(uint16_t)(0xFD << 8));
if(((GPIO_ReadInputData(GPIOF) >> 8) & 0XF0)!= 0XF0)
{
delay_ms(20);
if(((GPIO_ReadInputData(GPIOF) >> 8) & 0XF0) != 0XF0)
{
temp=((GPIO_ReadInputData(GPIOF) >> 8) & 0XFD);
switch(temp)
{
case 0xED: key_val = 5; break;
case 0xDD: key_val = 6; break;
case 0xBD: key_val = 7; break;
case 0x7D: key_val = 8; break;
default: key_val = -1; break;
}
}
}
GPIO_Write(GPIOF,0);
delay_ms(5);
GPIO_Write(GPIOF,(uint16_t)(0xFB << 8));
if(((GPIO_ReadInputData(GPIOF) >> 8) & 0XF0) != 0XF0)
{
delay_ms(20);
if(((GPIO_ReadInputData(GPIOF) >> 8) & 0XF0) != 0XF0)
{
temp=((GPIO_ReadInputData(GPIOF) >> 8) & 0XFB);
switch(temp)
{
case 0xEB: key_val = 9; break;
case 0xDB: key_val = 10; break;
case 0xBB: key_val = 11; break;
case 0x7B: key_val = 12; break;
default: key_val = -1; break;
}
}
}
GPIO_Write(GPIOF,0);
delay_ms(5);
GPIO_Write(GPIOF,(uint16_t)(0xF7 << 8));
if(((GPIO_ReadInputData(GPIOF) >> 8) & 0XF0) !=0XF0)
{
delay_ms(20);
if(((GPIO_ReadInputData(GPIOF) >> 8) & 0XF0) != 0XF0)
{
temp=((GPIO_ReadInputData(GPIOF) >> 8) & 0XF7);
switch(temp)
{
case 0xE7: key_val = 13; break;
case 0xD7: key_val = 14; break;
case 0xB7: key_val = 15; break;
case 0x77: key_val = 16; break;
default: key_val = -1; break;
}
}
}
}
}
return key_val;
}
软件流程图
调试过程
我最开始设计的功能是将卡号先写好定义在数组里面,可是这样的话就没有了发卡的功能,于是我就想到设置一个空数组,读卡的时候把卡号存进这个数组里面去。随后,我在设置个人信息的功能时遇到了麻烦,我按下一个按键之后,不需要按后面三个按键他就自动跟第一个按键一样了,于是我在代码里找原因,发现原来每一次输入按键的时候都需要重新扫描,否则按键会停留在上一次的值。
在发卡和个人信息绑定设置完成后,我又要设置挂失和解除挂失功能。这个时候我又用到了一个临时数组,用来临时存放卡号。
当输入的个人信息和相应的卡匹配时,就将卡号存放在临时数组里面。
起初我只考虑了输入正确的情况,没有考虑到输入的信息错误的情况,于是我又小改了一下函数,把if改成了else if 最后如果输入的个人信息都不匹配,则打印出密码错误。
在设计卡的扣款的功能的时候,由于要扣款小数位,最开始我没有想到很好的方法怎么去设置小数位。然后我突然想到一个办法,就是用数据的第十位作为小数位,比如数据100,则实际金额为10.0。根据这个想法,我修改了代码。
将数值块的数据除以10再强制转换为double类型,这样就实现了小数的功能了。不过这样做的坏处就是整体可用金额少了十倍。
在我本来以为功能都设计的差不多的时候,我又发现了另一个问题,那就是挂失和解除挂失功能的模块,我原先把这块函数写在了读卡成功之后。实际功能为不刷卡时无法进入挂失和解除挂失模式的函数,这与实际情况不符合,我就是因为卡掉了才要进入挂失,你现在要我刷卡,我拿什么刷?于是我就把这块函数写在了读卡之前,这样,即使不刷卡,也能进入卡的挂失和解除挂失功能,更加符合实际情况。
相关代码
挂失代码
if(WK_UP==1)//进入挂失和解除挂失模式
{
printf("\r\n进入挂失和解除挂失模式\r\n");
printf("\r\n按下S1进入挂失模式,按下S2进入解除挂失模式\r\n");
while(1)
{
key_val = Matrix_Key_Scan();
if(key_val > 0 && key_val < 17)
{
if(key_val==1)//进入挂失模式
{
printf("\r\n 进入挂失模式\r\n");
printf("\r\n 请输入4位个人信息\r\n");
while(1)
{
key_val = Matrix_Key_Scan();
if(key_val > 0 && key_val < 17)//¸个人信息第一位
{
if(key_val==1)card_3[0]=1;
if(key_val==2)card_3[0]=2;
if(key_val==3)card_3[0]=3;
if(key_val==4)card_3[0]=4;
if(key_val==5)card_3[0]=5;
if(key_val==6)card_3[0]=6;
if(key_val==7)card_3[0]=7;
if(key_val==8)card_3[0]=8;
if(key_val==9)card_3[0]=9;
if(key_val==10)card_3[0]=0;
printf("\r\n %d \r\n",card_3[0]);
if(card_3[0]==0|card_3[0]==1|card_3[0]==2|card_3[0]==3|card_3[0]==4|card_3[0]==5|card_3[0]==6|card_3[0]==7|card_3[0]==8|card_3[0]==9)break;
}
}
delay_ms(10000);
while(1)
{
key_val = Matrix_Key_Scan();
if(key_val > 0 && key_val < 17)//个人信息第二位
{
if(key_val==1)card_3[1]=1;
if(key_val==2)card_3[1]=2;
if(key_val==3)card_3[1]=3;
if(key_val==4)card_3[1]=4;
if(key_val==5)card_3[1]=5;
if(key_val==6)card_3[1]=6;
if(key_val==7)card_3[1]=7;
if(key_val==8)card_3[1]=8;
if(key_val==9)card_3[1]=9;
if(key_val==10)card_3[1]=0;
printf("\r\n %d \r\n",card_3[1]);
if(card_3[1]==0|card_3[1]==1|card_3[1]==2|card_3[1]==3|card_3[1]==4|card_3[1]==5|card_3[1]==6|card_3[1]==7|card_3[1]==8|card_3[1]==9)break;
}
}
delay_ms(10000);
while(1)
{
key_val = Matrix_Key_Scan();
if(key_val > 0 && key_val < 17)//个人信息第三位
{
if(key_val==1)card_3[2]=1;
if(key_val==2)card_3[2]=2;
if(key_val==3)card_3[2]=3;
if(key_val==4)card_3[2]=4;
if(key_val==5)card_3[2]=5;
if(key_val==6)card_3[2]=6;
if(key_val==7)card_3[2]=7;
if(key_val==8)card_3[2]=8;
if(key_val==9)card_3[2]=9;
if(key_val==10)card_3[2]=0;
printf("\r\n %d \r\n",card_3[2]);
if(card_3[2]==0|card_3[2]==1|card_3[2]==2|card_3[2]==3|card_3[2]==4|card_3[2]==5|card_3[2]==6|card_3[2]==7|card_3[2]==8|card_3[2]==9)break;
}
}
delay_ms(10000);
while(1)
{
key_val = Matrix_Key_Scan();
if(key_val > 0 && key_val < 17)//个人信息第四位
{
if(key_val==1)card_3[3]=1;
if(key_val==2)card_3[3]=2;
if(key_val==3)card_3[3]=3;
if(key_val==4)card_3[3]=4;
if(key_val==5)card_3[3]=5;
if(key_val==6)card_3[3]=6;
if(key_val==7)card_3[3]=7;
if(key_val==8)card_3[3]=8;
if(key_val==9)card_3[3]=9;
if(key_val==10)card_3[3]=0;
printf("\r\n %d \r\n",card_3[3]);
if(card_3[3]==0|card_3[3]==1|card_3[3]==2|card_3[3]==3|card_3[3]==4|card_3[3]==5|card_3[3]==6|card_3[3]==7|card_3[3]==8|card_3[3]==9)break;
}
}
delay_ms(10000);
if(((card_3[0]==card[4])&&(card_3[1]==card[5])&&(card_3[2]==card[6])&&(card_3[3]==card[7])))//如果为普通卡的信息
{//创建一个临时数组来临时保存普通卡的卡号,然后将该卡卡号初始化为0
card_4[0]=card[0];card[0]=0;
card_4[1]=card[1];card[1]=0;
card_4[2]=card[2];card[2]=0;
card_4[3]=card[3];card[3]=0;
printf("\r\n 普通卡挂失成功\r\n");
printf("\r\n 按下S16退出\r\n");
}
else if(((card_3[0]==card[12])&&(card_3[1]==card[13])&&(card_3[2]==card[14])&&(card_3[3]==card[15])))//如果为学生卡的信息
{//创建一个临时数组来临时保存学生卡的卡号,然后将该卡卡号初始化为0
card_5[0]=card[8];card[8]=0;
card_5[1]=card[9];card[9]=0;
card_5[2]=card[10];card[10]=0;
card_5[3]=card[11];card[11]=0;
printf("\r\n 学生卡挂失成功\r\n");
printf("\r\n 按下S16退出\r\n");
}
else if(((card_3[0]==card[20])&&(card_3[1]==card[21])&&(card_3[2]==card[22])&&(card_3[3]==card[23])))//如果为老年卡的信息
{//创建一个临时数组来临时保存老年卡的卡号,然后将该卡卡号初始化为0
card_6[0]=card[16];card[16]=0;
card_6[1]=card[17];card[17]=0;
card_6[2]=card[18];card[18]=0;
card_6[3]=card[19];card[19]=0;
printf("\r\n 老年卡挂失成功\r\n");
printf("\r\n 按下S16退出\r\n");
}
else
{
printf("\r\n 该信息未绑定公交卡\r\n ");
}
}
}
key_val = Matrix_Key_Scan();
if(key_val > 0 && key_val < 17)
{
if(key_val==2)//进入解除挂失模式
{
printf("\r\n 进入解除挂失模式\r\n");
printf("\r\n 请输入4为个人信息\r\n");
while(1)
{
key_val = Matrix_Key_Scan();
if(key_val > 0 && key_val < 17)//个人信息第一位
{
if(key_val==1)card_3[0]=1;
if(key_val==2)card_3[0]=2;
if(key_val==3)card_3[0]=3;
if(key_val==4)card_3[0]=4;
if(key_val==5)card_3[0]=5;
if(key_val==6)card_3[0]=6;
if(key_val==7)card_3[0]=7;
if(key_val==8)card_3[0]=8;
if(key_val==9)card_3[0]=9;
if(key_val==10)card_3[0]=0;
printf("\r\n %d \r\n",card_3[0]);
if(card_3[0]==0|card_3[0]==1|card_3[0]==2|card_3[0]==3|card_3[0]==4|card_3[0]==5|card_3[0]==6|card_3[0]==7|card_3[0]==8|card_3[0]==9)break;
}
}
delay_ms(10000);
while(1)
{
key_val = Matrix_Key_Scan();
if(key_val > 0 && key_val < 17)//个人信息第二位
{
if(key_val==1)card_3[1]=1;
if(key_val==2)card_3[1]=2;
if(key_val==3)card_3[1]=3;
if(key_val==4)card_3[1]=4;
if(key_val==5)card_3[1]=5;
if(key_val==6)card_3[1]=6;
if(key_val==7)card_3[1]=7;
if(key_val==8)card_3[1]=8;
if(key_val==9)card_3[1]=9;
if(key_val==10)card_3[1]=0;
printf("\r\n %d \r\n",card_3[1]);
if(card_3[1]==0|card_3[1]==1|card_3[1]==2|card_3[1]==3|card_3[1]==4|card_3[1]==5|card_3[1]==6|card_3[1]==7|card_3[1]==8|card_3[1]==9)break;
}
}
delay_ms(10000);
while(1)
{
key_val = Matrix_Key_Scan();
if(key_val > 0 && key_val < 17)//个人信息第三位
{
if(key_val==1)card_3[2]=1;
if(key_val==2)card_3[2]=2;
if(key_val==3)card_3[2]=3;
if(key_val==4)card_3[2]=4;
if(key_val==5)card_3[2]=5;
if(key_val==6)card_3[2]=6;
if(key_val==7)card_3[2]=7;
if(key_val==8)card_3[2]=8;
if(key_val==9)card_3[2]=9;
if(key_val==10)card_3[2]=0;
printf("\r\n %d \r\n",card_3[2]);
if(card_3[2]==0|card_3[2]==1|card_3[2]==2|card_3[2]==3|card_3[2]==4|card_3[2]==5|card_3[2]==6|card_3[2]==7|card_3[2]==8|card_3[2]==9)break;
}
}
delay_ms(10000);
while(1)
{
key_val = Matrix_Key_Scan();
if(key_val > 0 && key_val < 17)//个人信息第四位
{
if(key_val==1)card_3[3]=1;
if(key_val==2)card_3[3]=2;
if(key_val==3)card_3[3]=3;
if(key_val==4)card_3[3]=4;
if(key_val==5)card_3[3]=5;
if(key_val==6)card_3[3]=6;
if(key_val==7)card_3[3]=7;
if(key_val==8)card_3[3]=8;
if(key_val==9)card_3[3]=9;
if(key_val==10)card_3[3]=0;
printf("\r\n %d \r\n",card_3[3]);
if(card_3[3]==0|card_3[3]==1|card_3[3]==2|card_3[3]==3|card_3[3]==4|card_3[3]==5|card_3[3]==6|card_3[3]==7|card_3[3]==8|card_3[3]==9)break;
}
}
delay_ms(10000);
if(((card_3[0]==card[4])&&(card_3[1]==card[5])&&(card_3[2]==card[6])&&(card_3[3]==card[7])))//如果为普通卡的个人信息
{//将挂失时临时保存的普通卡重新定义
card[0]=card_4[0];
card[1]=card_4[1];
card[2]=card_4[2];
card[3]=card_4[3];
printf("\r\n 普通卡解除挂失成功\r\n");
printf("\r\n 按下S16退出\r\n");
}
else if(((card_3[0]==card[12])&&(card_3[1]==card[13])&&(card_3[2]==card[14])&&(card_3[3]==card[15])))//如果为学生卡的个人信息
{//将挂失时临时保存的学生卡重新定义
card[8]=card_5[0];
card[9]=card_5[1];
card[10]=card_5[2];
card[11]=card_5[3];
printf("\r\n 学生卡解除挂失成功\r\n");
printf("\r\n 按下S16退出\r\n");
}
else if(((card_3[0]==card[20])&&(card_3[1]==card[21])&&(card_3[2]==card[22])&&(card_3[3]==card[23])))//如果为老年卡的个人信息
{//将挂失时临时保存的老年卡重新定义
card[16]=card_6[0];
card[17]=card_6[1];
card[18]=card_6[2];
card[19]=card_6[3];
printf("\r\n 老年卡解除挂失成功\r\n");
printf("\r\n 按下S16退出\r\n");
}
else
{
printf("\r\n 该个人信息未绑定公交卡\r\n ");
}
}
}
key_val = Matrix_Key_Scan();
if(key_val > 0 && key_val < 17)
{
if(key_val==16)//按下S16退出挂失和解除挂失功能
{
printf("\r\n 退出挂失和解除挂失功能\r\n");
break;
}
}
}
}
其他相关代码这里不再详细展示。
具体功能描述
具体实现功能为,首先进入菜单。
随机拿一张M50卡。刷卡,此卡未定义,按下KEY1进入发卡模式
此时按下S1则定义为普通卡,按下S2则定义为学生卡,按下S3则定义为老年卡。
然后输入个人信息,当卡定义完成后,按下S16按键退出发卡模式。
此时刚定义的卡是没有余额的,所以需要按下KEY0充值,每按下KEY1一次充值十元。普通卡刷卡一次扣款1.6元,学生卡刷卡一次扣款0.8元。老年卡不需要充值扣款,免费。
当用户将自己的卡遗失后,按下WK_UP键后进入卡的挂失和解除挂失功能,再按下S1进入挂失功能。
此时输入用户的个人信息后成功挂失,输入错误显示密码错误,挂失后,按下S16退出。
当用户找回自己的卡时,再次按下WK_UP键进入挂失和解除挂失功能,此时再按下S2进入解除挂失功能,此时用户输入自己的个人信息后解除挂失成功,若个人信息不匹配则显示密码错误,解除挂失成功后按下S16键退出。
可以看到,基本的功能已经演示了一遍了。(PS:串口显示有些文字有乱码,所以有些文字没显示出来。)
结论与改进
在本次设计中,我得到一个结论,想要设计一个好的系统,必须在不断的调试与改进中才能发现平时没能发现的问题,而且在写函数的时候,一定要写注释,否则到后面写多了你会发现前面写的函数你都有点忘了写的是什么了,这点我深有体会。通过这次设计,给我的收获很大,也让我成长了很多,懂得了如何自己动手去设计一款系统并且不断改进的过程。我还会不断改进这个系统,加一些能加的模块,比如显示屏和语音模块,来更加完善这个系统。 后续我会不断更新此博客,将自己的想法和改进写进此博客中,供大家学习。
有问题的小伙伴可以私信或加q1743647071,互相学习,互相进步。
边栏推荐
- 《7天学会Go并发编程》第六天 go语言Sync.cond的应用和实现 go实现多线程联合执行
- Common APIs (Methods) for scope -number and string
- Passerelle de service pour les microservices
- Go language slice vs array panic: runtime error: index out of range problem solving
- C # QR code generation and recognition, removing white edges and any color
- Flask application case
- 對話喬心昱:用戶是魏牌的產品經理,零焦慮定義豪華
- Where can I set the slides on the front page of CMS applet?
- About the SQL injection of davwa, errors are reported: analysis and verification of the causes of legal mix of settlements for operation 'Union'
- Gao fushuai in the unit testing industry, pytest framework, hands-on teaching, will do this in the future test reports~
猜你喜欢
How to participate in openharmony code contribution
Introduce you to ldbc SNB, a powerful tool for database performance and scenario testing
Codeforces Round #717 (Div. 2)
Transformation from student to engineer
资深猎头团队管理者:面试3000顾问,总结组织出8大共性(茅生)
深度学习又有新坑了!悉尼大学提出全新跨模态任务,用文本指导图像进行抠图
average-population-of-each-continent
Oracle obtains the beginning and end of the month time, and obtains the beginning and end of the previous month time
crontab定时任务常用命令
微服务之服务网关
随机推荐
Process judgment - ternary operation - for loop
gomock mockgen : unknown embedded interface
Day 7 of "learning to go concurrent programming in 7 days" go language concurrent programming atomic atomic actual operation includes ABA problem
管理系统-ITclub(下)
Is it safe to open a stock account through the account opening link given by the CICC securities manager? I want to open an account
Common APIs (Methods) for scope -number and string
爬虫笔记(2)- 解析
Is flush stock trading software reliable?? Is it safe?
Oracle obtains the beginning and end of the month time, and obtains the beginning and end of the previous month time
[cloud based co creation] what is informatization? What is digitalization? What are the connections and differences between the two?
Yarn中RMApp、RMAppAttempt、RMContainer和RMNode状态机及其状态转移
crontab定时任务常用命令
7 jours d'apprentissage de la programmation simultanée go 7 jours de programmation simultanée go Language Atomic Atomic Atomic actual Operation contains ABA Problems
01 golang environment construction
宏任务、微任务理解
Codeforces Round #722 (Div. 2)
regular expression
从学生到工程师的蜕变之路
C # QR code generation and recognition, removing white edges and any color
Management system itclub (Part 1)