一、项目介绍
本项目是我本科毕业设计的硬件部分,主要功能为使用RC522一次读取多个内嵌在餐盘中的M1卡片中的价格,然后学生刷卡或者刷手机或者穿戴设备进行支付。
涉及到的设备有:51开发板、RC522芯片、5v-3.3v降压芯片(有可能不需要)、M1卡(非接触式IC卡的一种,我用的是S50方形卡和异形卡)
二、RC522模块套件介绍
简单来说RC522能够读取或写入数据到单片机和IC卡,并且能够对值域进行增值和减值。
M1卡的结构如下图:
M1卡总共有16个扇区编号0~15,每个扇区有4块,其中第四块(编号为3)为控制块,限制前3块的访问权限,每张卡有唯一的4字节的id编号。具体如何操作参考:
其中第1号扇区 不知怎地 被我给写坏了 现在已经不能够读写了。
三、项目代码解读
1. 管脚接线
其中IRQ管脚不接。 根据自己的排线合理选择管脚,以避免冲突。
2.项目目录结构
3.核心代码讲解
void writePlatePrice()
{
unsigned char status;
while(1){
status = PcdRequest(PICC_REQIDL, g_ucTempbuf1);//*PICC_REQALL=0x52:寻找所有符合规定卡 PICC_REQIDL=0x26:只寻找未休眠的卡
if (status == MI_OK)
{
status = PcdAnticoll(g_ucTempbuf1); // 防冲撞,g_ucTempbuf1获得卡号
}
if (status == MI_OK)
{
status = PcdSelect(g_ucTempbuf1); //选定卡
}
if (status == MI_OK)
{
status = PcdAuthState(PICC_AUTHENT1A, 8, DefaultKey, g_ucTempbuf1);//打卡天线,验证密码,这里验证的块与下面读取的块一定要一致
}
if (status == MI_OK)
{
status = PcdWrite(8, w_ucTempbuf);//在8号块写入数据
}
if (status == MI_OK)
{
status = PcdRead(8, g_ucTempbuf2);//读取8号块的数据
}
if(status == MI_OK)
{
CALL_isr_UART();
Buzzer_Time(500);
PcdHalt();
mode = readPlate_mode; //切换模式
P2_1 = 1; //
break; //
}
}
}
RC522的读写机制:
python脚本获取串口数据:
# TODO 串口读取数据
# Auther wjw
import logging
import serial # 导入串口包
import time # 导入时间包
import threading
plate_list = ['c3cc260c', '038af109'] # 餐盘id列表
campusCard_list = ['31340380'] # 校园卡列表
subtract_list = [] # 点的菜品数据
'''
初始化串口
'''
def InitSerial():
ser = serial.Serial("COM3", 4800, timeout=5) # 开启com3口,波特率4800,超时5
ser.flushInput() # 清空缓冲区
return ser
'''
16进制显示数据
'''
def HexShow(argv):
try:
result = ''
hLen = len(argv)
for i in range(hLen):
hvol = argv[i]
hhex = '%02x' % hvol
result += hhex+' '
return result
except Exception as e:
print("---异常---:", e)
'''
获得卡片id
'''
def GetUID(recv):
return '%02x' % recv[0] + '%02x' % recv[1] + '%02x' % recv[2] + '%02x' % recv[3]
'''
包装浮点数金额
'''
def PackFloat(f):
f = str(round(f, 2)) # 保留两位小数转成字符串
if len(f) > 16:
raise Exception("金额数据过长")
f = f.zfill(16) # 右对齐 做填充0
return f
'''
接受到的串口数据转浮点并加入到待扣列表
'''
def DePackFloat(f):
tempStr = ''
for i in range(4, len(f)):
if f[i] == 46:
tempStr += '.'
else:
tempStr += str(f[i]-48)
if tempStr != '':
subtract_list.append(float(tempStr))
else:
subtract_list.append(0)
'''
结算模式
'''
def SettlementMode(recv):
totalMoney = 0
for t in subtract_list:
totalMoney += t
subtract_list.clear() # 清空扣款列表
print('成功结算,总计价格:', totalMoney)
print('结算对象:', GetUID(recv))
'''
读取串口数据
'''
def ReadSerialData(ser):
while True:
count = ser.inWaiting() # 获取串口缓冲区数据
if count != 0:
recv = ser.read(count) # 读出串口数据,数据采用16进制存储
print("串口接受到数据:", recv)
print("16进制ascii码存储:", HexShow(recv)) # 打印一下子
if len(recv) == 1:
print('模式选择')
elif GetUID(recv) in plate_list:
DePackFloat(recv)
print("已读入该餐盘价格")
elif GetUID(recv) in campusCard_list and len(recv) == 5 and recv[4] == 255:
print("进入结算模式")
SettlementMode(recv)
time.sleep(0.1) # 延时0.1秒,免得CPU出问题
def writeMoney(ser):
while True:
if input("请输入指令:") == 'w':
try:
money = float(input("请输入要设定的金额:"))
t = PackFloat(money).encode()
ser.write(PackFloat(money).encode())
except:
print("输入的金额有误")
time.sleep(1)
if __name__ == '__main__':
ser = InitSerial() # 获取初始化串口对象
threading.Thread(target=ReadSerialData, args=(ser,)).start() # 读串口数据子线程
threading.Thread(target=writeMoney, args=(ser,)).start() # 写金额子线程
这里使用了两个线程,一个用于读取数据,向卡中写入数据。
四、项目遇到的坑及难点
- 了解到RC522只能用3.3v供电,我这个开发板上没有3.3v的引脚,最后在淘宝买了个稳压芯片解决。
- 中断定时器问题,才开始uart通信的代码是在网上找的,好像是晶振不同,导致中断定时器的初值不对,以及一些寄存器配置错误,导致uart通信不了,最后改改就行了。
- 如何存值的问题,因为我要存浮点数,但是卡片只能存16进制的,才开始想着用计算机组成原理中的IEEE754方法把浮点数存起来,然后发现太麻烦,而且我只存小数点后两位,完全没必要用IEEE754那么高的精度,最后to通过把浮点数凑成16字节(包含小数点,高低位补0,刚好一块),然后转成ascii码的16进制形式进行的存储,同样读取只需要逆着来就行。
- 卡片休眠问题,我想一次读取多张卡,我才开始用的方法是一张一张的读,读完一张然后令这张卡休眠(开始只寻找未休眠的卡)然后读取下一张,不知怎地,后面竟然读取不了了,我找了大概两三天的时间,问题是那个扇区给读坏了,我读取8号块就是第2号扇区就可以了。
- 我当时修改了代码中的函数名,后面发现怎么都调用不了了,最后发现原来.h文件中的函数声明没有修改。
- uart的中断处理函数中不能在调用其他执行时间长的函数,也不能咋函数中进行延时操作。