# 51单片机连接RC522芯片实现刷卡扣费并通过python脚本获取串口数据 **Repository Path**: No10/rc522 ## Basic Information - **Project Name**: 51单片机连接RC522芯片实现刷卡扣费并通过python脚本获取串口数据 - **Description**: 51单片机连接RC522芯片实现刷卡扣费并通过python脚本获取串口数据 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-02-14 - **Last Updated**: 2024-04-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README @[TOC](目录) # 一、项目介绍 本项目是我本科毕业设计的硬件部分,主要功能为使用RC522一次读取多个内嵌在餐盘中的M1卡片中的价格,然后学生刷卡或者刷手机或者穿戴设备进行支付。 涉及到的设备有:51开发板、RC522芯片、5v-3.3v降压芯片(有可能不需要)、M1卡(非接触式IC卡的一种,我用的是S50方形卡和异形卡) ![请添加图片描述](https://img-blog.csdnimg.cn/72cc7c4262004a019072872c3ebe4608.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56mGX-a4hQ==,size_20,color_FFFFFF,t_70,g_se,x_16) ![请添加图片描述](https://img-blog.csdnimg.cn/f73624790fc94013840f0ba4a74ceb9d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56mGX-a4hQ==,size_20,color_FFFFFF,t_70,g_se,x_16) # 二、RC522模块套件介绍 简单来说RC522能够读取或写入数据到单片机和IC卡,并且能够对值域进行增值和减值。 M1卡的结构如下图:![请添加图片描述](https://img-blog.csdnimg.cn/142a01a359284e27b1c085c1e7d71547.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56mGX-a4hQ==,size_20,color_FFFFFF,t_70,g_se,x_16) M1卡总共有16个扇区编号0~15,每个扇区有4块,其中第四块(编号为3)为控制块,限制前3块的访问权限,每张卡有唯一的4字节的id编号。具体如何操作参考: ![在这里插入图片描述](https://img-blog.csdnimg.cn/c104c4e0626a4dc7b492177686b7b34a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56mGX-a4hQ==,size_20,color_FFFFFF,t_70,g_se,x_16) 其中第1号扇区 不知怎地 被我给写坏了 现在已经不能够读写了。 # 三、项目代码解读 ## 1. 管脚接线 ![管脚接线图](https://img-blog.csdnimg.cn/2f958499d07146b1a7a29d4e2ad3f00c.png) 其中IRQ管脚不接。 根据自己的排线合理选择管脚,以避免冲突。 ## 2.项目目录结构 ![在这里插入图片描述](https://img-blog.csdnimg.cn/f8dc4586ccd1472f8e75e90ad19c825d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56mGX-a4hQ==,size_12,color_FFFFFF,t_70,g_se,x_16) ## 3.核心代码讲解 ```c 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的读写机制:![在这里插入图片描述](https://img-blog.csdnimg.cn/986166ecedc8420bae98a9fd3b52c7be.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56mGX-a4hQ==,size_20,color_FFFFFF,t_70,g_se,x_16) python脚本获取串口数据: ```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() # 写金额子线程 ``` 这里使用了两个线程,一个用于读取数据,向卡中写入数据。 # 四、项目遇到的坑及难点 1. 了解到RC522只能用3.3v供电,我这个开发板上没有3.3v的引脚,最后在淘宝买了个稳压芯片解决。 2. 中断定时器问题,才开始uart通信的代码是在网上找的,好像是晶振不同,导致中断定时器的初值不对,以及一些寄存器配置错误,导致uart通信不了,最后改改就行了。 3. 如何存值的问题,因为我要存浮点数,但是卡片只能存16进制的,才开始想着用计算机组成原理中的IEEE754方法把浮点数存起来,然后发现太麻烦,而且我只存小数点后两位,完全没必要用IEEE754那么高的精度,最后to通过把浮点数凑成16字节(包含小数点,高低位补0,刚好一块),然后转成ascii码的16进制形式进行的存储,同样读取只需要逆着来就行。 4. 卡片休眠问题,我想一次读取多张卡,我才开始用的方法是一张一张的读,读完一张然后令这张卡休眠(开始只寻找未休眠的卡)然后读取下一张,不知怎地,后面竟然读取不了了,我找了大概两三天的时间,问题是那个扇区给读坏了,我读取8号块就是第2号扇区就可以了。 5. 我当时修改了代码中的函数名,后面发现怎么都调用不了了,最后发现原来.h文件中的函数声明没有修改。 6. uart的中断处理函数中不能在调用其他执行时间长的函数,也不能咋函数中进行延时操作。