基于stm32f103c8t6智能门锁

前言:

刚学完hal库的我,想着找一个基础项目练手,后选定了这个基础项目,由于一些资料的不齐全,还是绕了许多弯子,在此将我的版本发出来,记录一下。本项目主要是基于stm32f103c8t6进行的智能门锁设计,实现了键盘解锁、指纹解锁、RFID解锁,解锁过程主要用舵机进行模拟。

材料准备:

        stm32f103c8t6、舵机模块、4*4矩阵键盘、AS608指纹模块、0.96寸4针oled屏、RFID-RC522、CH340

1、oled模块

        oled模块我使用的江科大老师的代码,大家可以学习一下噢,网上也有许多版本的源码,这个大家自行选择。

2、4*4矩阵键盘

 矩阵键盘我采用的是最常见的逐行逐列扫描法。即行引脚设置为推挽输出,列引脚设置为上拉输入。部分配置和代码如下

uint8_t Key_Read(void)
{
	int16_t temp = 0;
	uint8_t Line = 0;//行
	uint8_t Column = 0;//列
	
	for(Line = 0; Line < 4; Line++)//行扫描
	{	//一行置RESET 其他行置SET 循环执行
		HAL_GPIO_WritePin(GPIOA, 1 << Line, GPIO_PIN_RESET);

		for(Column = 0; Column < 4; Column++)//列扫描
		{	//循环读列引脚的电平
			if(HAL_GPIO_ReadPin(GPIOA, 1 << (Column + 4)) == 0)
			{	//从1开始
				temp = Line * 4 + Column + 1;
				return temp;
			}
		}
		
		HAL_GPIO_WritePin(GPIOA, 1 << Line, GPIO_PIN_SET);		
	}		
	
	return temp;
}

需要注意一下,使用按键时需要对按键进行消抖,这边我采用的是西风up的三行消抖方法,具体实现过程大家可以看一下他的视频噢!

uint8_t Key_Val,Key_Down,Key_Old,Key_Up;//按键专用变量

void Key_Proc(void)
{
	Key_Val = Key_Read();//实时读取键码值
	Key_Down = Key_Val & (Key_Old ^ Key_Val);//捕捉按键下降沿
	Key_Up = ~Key_Val & (Key_Old ^ Key_Val);//捕捉按键上降沿
	Key_Old = Key_Val;//辅助扫描变量

	if(Key_Down == 1)
	{
	    //具体功能填这里噢
	}

	if(Key_Down == 2)//1
	{
		
	}

	if(Key_Down == 3)
	{

	}		
}

void scheduler_run(void)
{
	static uint32_t now_time, last_time;
	now_time = HAL_GetTick();
	if(now_time >= 10 + last_time)//10ms检测一次
	{
		last_time = now_time;		
		Key_Proc();		
	}
}

 3、AS608模块

 AS608模块大家可以参考网上的例程和官方的数据手册噢!

AS608模块我们只使用四个引脚,即VCC GND TX RX ,这边只要将TX与RX与stm32的RX,TX连接即可。VCC接3.3V。

这边是采用的江科大的格式噢。

#include "AS608.h"

#define CharBuffer1 0x01
#define CharBuffer2 0x02

uint8_t Serial_RxPacket[8];
uint32_t AS608Addr = 0XFFFFFFFF;

uint8_t result;
uint16_t PageID;//指纹ID
uint16_t MathScore;//匹配得分
uint16_t ValidN;//有效模板个数
//发送包头
void SendHead(void)
{
	Serial_SendByte(0xEF);
	Serial_SendByte(0x01);
}
//发送地址
void SendAddr(void)
{
	Serial_SendByte(AS608Addr>>24);
	Serial_SendByte(AS608Addr>>16);
	Serial_SendByte(AS608Addr>>8);
	Serial_SendByte(AS608Addr);
}
//发送包标识,
void SendFlag(uint8_t flag)
{
	Serial_SendByte(flag);
}
//发送包长度
void SendLength(int length)
{
	Serial_SendByte(length>>8);
	Serial_SendByte(length);
}
//发送指令码
void Sendcmd(uint8_t cmd)
{
	Serial_SendByte(cmd);
}
//发送校验和
void SendCheck(uint16_t check)
{
	Serial_SendByte(check>>8);
	Serial_SendByte(check);
}
//串口接收数据分置
void PS_Recceive(void)
{	
	if(*Serial_RxPacket)
	{
		result = Serial_RxPacket[3];
		PageID = (Serial_RxPacket[4] << 8) | Serial_RxPacket[5];
		MathScore = (Serial_RxPacket[6] << 8) | Serial_RxPacket[7];
		ValidN = PageID;
	}
	else
	{
		result=0xff;
	}
}

/*上传图像*/
void PS_UpImage(void)
{
  uint16_t temp;
	
  SendHead();
  SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x03);   //数据包长度
	Sendcmd(0x0a);      //命令包
	temp =  0x01+0x03+0x0a;
	SendCheck(temp);     //校验和
	
	HAL_Delay(500);
}

/*录入指纹*/
void PS_GetImage(void)
{
  uint16_t temp;
	
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x03);//数据包长度
	Sendcmd(0x01);	//命令包
	temp =  0x01+0x03+0x01;
	SendCheck(temp);//校验和
	
	HAL_Delay(500);
}

//生成特征 
void PS_GenChar(uint8_t BufferID)
{
	uint16_t temp;
	
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x04);
	Sendcmd(0x02);
	Serial_SendByte(BufferID);
	temp = 0x01+0x04+0x02+BufferID;
	SendCheck(temp);
	
	HAL_Delay(500);
}

//精确比对两枚指纹特征 PS_Match
void PS_Match(void)
{
	uint16_t temp;

	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x03);
	Sendcmd(0x03);
	temp = 0x01+0x03+0x03;
	SendCheck(temp);
	

	HAL_Delay(500);
}
//合并特征(生成模板)PS_RegModel
//功能:将CharBuffer1与CharBuffer2中的特征文件合并生成 模板,结果存于CharBuffer1与CharBuffer2	
//说明:  模块返回确认字
void PS_RegModel(void)
{
	uint16_t temp;
	
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x03);
	Sendcmd(0x05);
	temp = 0x01+0x03+0x05;
	SendCheck(temp);
	
	HAL_Delay(500);
}
//储存模板 PS_StoreChar
//功能:将 CharBuffer1 或 CharBuffer2 中的模板文件存到 PageID 号flash数据库位置。			

void PS_StoreChar(uint8_t BufferID,uint8_t PageID)
{
	uint16_t temp;

	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x06);
	Sendcmd(0x06);
	Serial_SendByte(BufferID);
	Serial_SendByte(PageID>>8);
	Serial_SendByte(PageID);
	temp = 0x01+0x06+0x06+BufferID + (PageID>>8) + (uint8_t)PageID;
	SendCheck(temp);

	HAL_Delay(500);
}

//删除模板 PS_DeletChar
//功能:  删除flash数据库中指定ID号开始的N个指纹模板
//参数:  PageID(指纹库模板号),N删除的模板个数。
void PS_DeletChar(uint16_t PageID,uint16_t N)
{
	uint16_t temp;
	
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x07);
	Sendcmd(0x0C);
	Serial_SendByte(PageID>>8);
	Serial_SendByte(PageID);
	Serial_SendByte(N>>8);
	Serial_SendByte(N);
	temp = 0x01 + 0x07 + 0x0C + (PageID>>8) + (uint8_t)PageID + (N>>8) + (uint8_t)N;
	SendCheck(temp);
	
	HAL_Delay(500);
}

//清空指纹库 PS_Empty
//功能:  删除flash数据库中所有指纹模板
//参数:  无
//说明:  模块返回确认字
void PS_Empty(void)
{
	uint16_t temp;
	
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x03);
	Sendcmd(0x0D);
	temp = 0x01+0x03+0x0D;
	SendCheck(temp);

	HAL_Delay(500);
}

//搜索PS_dSearch
void PS_Search(uint8_t BufferID, uint16_t StartPage, uint16_t PageNum)
{
	uint16_t temp;
	
  SendHead();
  SendAddr();
  SendFlag(0x01);//命令包标识
  SendLength(0x08);
  Sendcmd(0x04);
  Serial_SendByte(BufferID);
  Serial_SendByte(StartPage >> 8);
  Serial_SendByte(StartPage);
  Serial_SendByte(PageNum >> 8);
  Serial_SendByte(PageNum);
  temp = 0x01 + 0x08 + 0x1b + BufferID
         + (StartPage >> 8) + (uint8_t)StartPage
         + (PageNum >> 8) + (uint8_t)PageNum;
  SendCheck(temp);
	
  HAL_Delay(500);
}
//高速搜索PS_HighSpeedSearch
void PS_HighSpeedSearch(uint8_t BufferID, uint16_t StartPage, uint16_t PageNum)
{
	uint16_t temp;
	
  SendHead();
  SendAddr();
  SendFlag(0x01);//命令包标识
  SendLength(0x08);
  Sendcmd(0x1b);
  Serial_SendByte(BufferID);
  Serial_SendByte(StartPage >> 8);
  Serial_SendByte(StartPage);
  Serial_SendByte(PageNum >> 8);
  Serial_SendByte(PageNum);
  temp = 0x01 + 0x08 + 0x1b + BufferID
         + (StartPage >> 8) + (uint8_t)StartPage
         + (PageNum >> 8) + (uint8_t)PageNum;
  SendCheck(temp);
	
	HAL_Delay(500);
}

//读有效模板个数 PS_ValidTempleteNum
//功能:读有效模板个数
//参数: 无
//说明: 模块返回确认字+有效模板个数ValidN
void PS_ValidTempleteNum(void)
{
  uint16_t temp;

  SendHead();
  SendAddr();
  SendFlag(0x01);//命令包标识
  SendLength(0x03);
  Sendcmd(0x1d);
  temp = 0x01 + 0x03 + 0x1d;
  SendCheck(temp);
	
  HAL_Delay(500);
}


//写系统寄存器 PS_WriteReg
void PS_WriteReg(uint8_t RegNum, uint8_t DATA)
{
  uint16_t temp;
	
  SendHead();
  SendAddr();
  SendFlag(0x01);//命令包标识
  SendLength(0x05);
  Sendcmd(0x0E);
  Serial_SendByte(RegNum);
  Serial_SendByte(DATA);
  temp = RegNum + DATA + 0x01 + 0x05 + 0x0E;
  SendCheck(temp);
	
	HAL_Delay(500);
}

uint8_t ID_Num = 0;	

/*添加指纹*/
void Add_AS608(void)
{
	uint8_t process = 0;

	uint8_t count = 0;
	while(1)
	{
		switch(process)
		{
			case 0:
				count++;
			
				OLED_ShowString(1,6,"Finger");
			
				PS_GetImage();//录入指纹
				PS_Recceive();
			
				if(result == 0x00)
				{
					PS_GenChar(CharBuffer1);//生成特征
					if(result == 0x00)
					{
						OLED_ShowString(2,4,"Finger Normal");
						process = 1;
						count = 0;
					}
					else
					{					
						process = 0;					
					}
				}
				else
				{
					OLED_ShowString(1,4,"Finger Error");					
				}
				HAL_Delay(1000);
				OLED_Clear();
			break;
				
			case 1:
				count++;			
				OLED_ShowString(1,4,"Finger again");	
				PS_GetImage();
				PS_Recceive();
				if(result == 0x00)
				{
					PS_GenChar(CharBuffer2);//生成特征
					if(result == 0x00)
					{
						OLED_ShowString(2,4,"Finger Normal");
						process = 2;
						count = 0;
					}
					else
					{					
						process = 0;					
					}
				}
				else
				{
					OLED_ShowString(1,4,"Finger Error");					
				}
				OLED_Clear();
			break;
			
			case 2:
				OLED_ShowString(1,4,"Match");
				HAL_Delay(1000);
				PS_Match();
				PS_Recceive();		
				if(result == 0x00)
				{
					OLED_ShowString(1,4,"Match OK");//比对成功
					process = 3;
				}
				else
				{
					OLED_ShowString(1,4,"Match NO");
					process = 0;
					count = 0;
				}
				HAL_Delay(1000);				
				OLED_Clear();
			break;
				
			case 3:
				OLED_ShowString(1, 4, "Model");
				HAL_Delay(1000);			
				PS_RegModel();
				PS_Recceive();
				if(result == 0x00)
				{
					OLED_ShowString(1,4,"Model OK");//模板生成成功		
					process = 4;					
				}
				else
				{
					OLED_ShowString(1,4,"Model NO");		
					process = 0;						
				}
				HAL_Delay(1000);
				OLED_Clear();				
			break;
				
			case 4:
				OLED_ShowString(1, 4, "ID Choice");		
				ID_Num++;//每添加一个指纹 ID_Num自动加1 存储下一个指纹
				PS_StoreChar(CharBuffer2, ID_Num);
				PS_Recceive();			
				if(result == 0x00)
				{
					OLED_Clear();
					OLED_ShowString(1,4,"Store OK");//存储成功	
					return;		
				}
				else
				{
					OLED_ShowString(1,4,"Store NO");						
					OLED_Clear();
					process = 0;
				}
				HAL_Delay(1000);							
			break;
		}
		
        HAL_Delay(400);
        if(count == 5) //超过5次没有按手指则退出
        {
          break;
        }			
	}
}
/*扫描指纹*/
void Press_AS608(void)
{
	uint8_t count = 0;

	OLED_Clear();//清屏

	while(count <= 5)
	{
		count++;
		OLED_ShowString(1,6,"Finger");	
		
		PS_GetImage();//录入指纹
		PS_Recceive();
		if(result == 0x00)
		{
			PS_GenChar(CharBuffer1);//生成特征
			PS_Recceive();			
			if(result == 0x00)
			{

				PS_HighSpeedSearch(CharBuffer1, 0, 99);
				PS_Recceive();				
				if(result == 0x00)
				{
					OLED_Clear();					
					OLED_ShowString(1, 1, "Find");
					OLED_ShowNum(2, 1, PageID, 2);//显示ID
					OLED_ShowNum(3, 1, MathScore, 3);//显示得分		
					Servo();//舵机转动开门
					HAL_Delay(1000);		
					return;//检测到指纹退出循环
				}
				else
				{
					OLED_Clear();
					OLED_ShowString(1, 4, "Error");	
					HAL_Delay(1000);
					return;//检测到错误指纹退出循环
				}			
			}
			OLED_Clear();
		}
    HAL_Delay(1000);			
	}
}

	/*删除一个模板*/
void Delect_AS608(void)
{

	OLED_Clear();//清屏
			
	OLED_ShowString(1,6, "ONE");
	PS_DeletChar(ID_Num,1);
	PS_Recceive();	
	if(result == 0x00)
	{
		OLED_ShowString(2,5,"empty OK");			
	}		
}
	/*删除所有模板*/	
void Empty_AS608(void)
{

	OLED_Clear();//清屏
	
	OLED_ShowString(1,6, "ALL");		
	PS_Empty();
	PS_Recceive();
	if(result == 0x00)
	{
		OLED_ShowString(2,5,"empty OK");			
	}		
}
	/查找有效模板个数*/	
void ValidNum_AS608(void)
{
	OLED_Clear();	//清屏

	PS_ValidTempleteNum();
	PS_Recceive();	
	OLED_ShowString(1,1,"ValidN Num:");		
	if(result == 0x00)
	{		
		OLED_ShowNum(1,12,ValidN,2);		
	}		
}


#ifndef __AS608_H__
#define __AS608_H__

#include "gpio.h"
#include "usart.h"
#include "Serial.h"
#include "oled.h"
#include "delay.h"
#include "Key.h"

extern uint32_t AS608Addr;//指纹模块地址
extern uint8_t Serial_RxPacket[8];

extern uint8_t ID_Num;//指纹ID号

extern uint16_t PageID;//指纹ID
extern uint16_t MathScore;//匹配得分

/*发送包头*/
void SendHead(void);

/*发送地址*/
void SendAddr(void);

/*发送包标识*/
void SendFlag(uint8_t flag);

/*发送包长度*/
void SendLength(int length);

/*发送指令码*/
void Sendcmd(uint8_t cmd);

/*发送校验和*/
void SendCheck(uint16_t check);
/*上传图像*/
void PS_UpImage(void);

/*录入指纹*/
void PS_GetImage(void);

/*生成特征*/
void PS_GenChar(uint8_t BufferID);

/*精确比对两枚指纹特征 PS_Match*/
void PS_Match(void);

/*合并特征(生成模板)PS_RegModel*/
void PS_RegModel(void);

/*储存模板 PS_StoreChar*/
void PS_StoreChar(uint8_t BufferID,uint8_t PageID);

/*删除模板 PS_DeletChar*/
void PS_DeletChar(uint16_t PageID,uint16_t N);

/*清空指纹库 PS_Empty*/
void PS_Empty(void);

/*搜索PS_dSearch*/
void PS_Search(uint8_t BufferID, uint16_t StartPage, uint16_t PageNum);

/*高速搜索PS_HighSpeedSearch*/
void PS_HighSpeedSearch(uint8_t BufferID, uint16_t StartPage, uint16_t PageNum);

/*读有效模板个数 PS_ValidTempleteNum*/
void PS_ValidTempleteNum(void);

/*写系统寄存器 PS_WriteReg*/
void PS_WriteReg(uint8_t RegNum, uint8_t DATA);

/*添加指纹*/
void Add_AS608(void);
/*扫描指纹*/
void Press_AS608(void);
/*删除一个指纹*/
void Delect_AS608(void);
/*删除所有模板*/	
void Empty_AS608(void);
/*查找有效指纹个数*/
void ValidNum_AS608(void);
#endif

4、串口解析

uint8_t ByteReceive;
uint8_t Serial_RxData;
uint8_t Serial_RxFlag;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart == &huart1)
	{
		static uint8_t RxState = 0;
		static uint8_t pRxPacket = 0;
		uint8_t RxData = ByteReceive;
		
		if (RxState == 0)//包头
		{
			if (RxData == 0xEF)
			{
				RxState = 1;
				pRxPacket = 0;
			}
		}
		else if (RxState == 1)//包头
		{

			if (RxData == 0x01)
			{
				RxState = 2;
				pRxPacket = 0;
			}
		}
		else if (RxState == 2)//芯片地址
		{			

			if (RxData == 0xFF)
			{
				RxState = 3;
				pRxPacket = 0;
			}
		}
		else if (RxState == 3)//芯片地址
		{			
			if (RxData == 0xFF)
			{
				RxState = 4;
				pRxPacket = 0;
			}
		}	
		else if (RxState == 4)//芯片地址
		{			
			if (RxData == 0xFF)
			{
				RxState = 5;
				pRxPacket = 0;
			}
		}
		else if (RxState == 5)//芯片地址
		{			
			if (RxData == 0xFF)
			{
				RxState = 6;
				pRxPacket = 0;
			}
		}		
		else if (RxState == 6)//包标识
		{			
			Serial_RxPacket[pRxPacket] = RxData;
			pRxPacket ++;
			//Serial_RxPacket[0]-包标识		
			//Serial_RxPacket[1]Serial_RxPacket[2]-包长度
			//Serial_RxPacket[3]-确认码
			//Serial_RxPacket[4] Serial_RxPacket[5]-得分//页码	
			//Serial_RxPacket[6] Serial_RxPacket[7]-得分				
			if (pRxPacket >= 8)
			{
				RxState = 0;
				Serial_RxFlag = 1;
			}	
		}	
		
		HAL_UART_Receive_IT(&huart1, &ByteReceive, 1);
	}
}

 5、RFID-RC522模块

 RFID具体实现形式大家可以这个博主的三个文章噢!

基础篇010.1 STM32驱动RC522 RFID模块之一:基础知识-CSDN博客基础篇010.2 STM32驱动RC522 RFID模块之二:STM32硬件SPI驱动RC522_stm32 rc522-CSDN博客

基础篇010.3 STM32驱动RC522 RFID模块之三:STM32软件模拟SPI驱动RC522_rc522 模拟spi-CSDN博客 开启硬件SPI1

配置片选信号引脚和RST引脚 

将相对的引脚连接即可 

 这边的.c和.h文件直接复制即可,只需将对应的引脚名更换一下

/**
MFRC522-AN模块采用Philips MFRC522芯片设计读卡电路,使用方便,成本低廉,适用
于设备开发、读卡器开发等高级应用的用户、需要进行射频卡终端设计/生产的用户。
模块参数:
①工作电压:3.3v
②工作频率:13.56MHz
③支持卡类型:mifare1 s50、mifare1s70、 mifareUltraLight、mifare Pro, mifare Desfire
④通信方式:SPI协议
⑤环境工作温度:-20°C——80°C
  M1卡分为16个扇区,每个扇区由四个块(块0、块1、块2、块3)组成
  将16个扇区的64个块按绝对地址编号为:0~63
  第0个扇区的块0(即绝对地址0块),用于存放厂商代码,已经固化不可更改
  每个扇区的块0、块1、块2为数据块,可用于存放数据
  每个扇区的块3为控制块(绝对地址为:块3、块7、块11.....)包括密码A,存取控制、密码B等
1、CPU选择
   STM32F446RE,内部时钟180MHz
2、STM32CubeMX 定义任意两个引脚,作为复位脚和片选脚,并对引脚作出如下配置:
   GPlO output level       --High
   GPIO mode               --Output Push Pull
   GPIO Pull-up/Pull-down  --No pull-up and no pull-down
   Maximum output speed    --LOW
   User label              --RC522_RST/RC522_SDA
   ---------------------------------------------------------
   开启SPI功能,模式选择-->Full-Duplex Master(全双工),其他配置如下:
   Basic Parameters
      Frame format-->Motorola
      Data size   -->8 Bits
      First bit   -->MSB First
   Clock Parameters
       Prescaler(for Baud Rate)-->8
       Baud rate               -->5.625MBits/s【RC522中的SPI最高速率为10MHz/s】
       Clock Polarity(CPOL)    -->LOW
       Clock Phase(CPHA)       -->1 Edge
   Advanced Parameters
       CRC Calculation   -->Disabled
       NSS Signal Type   -->Software
3、接线方式:
   SPI_MISO(MUC)--> MISO(器件)
   SPI_MOSI(MUC)--> MOSI(器件)
   其他引脚一一对应
//! Nucleo-F446RE接口
//SPI2_SCK              PB10---(接Arduino D6)
//SPI2_MISO             PC2----(接CN7左下2)
//SPI2_MOSI             PC1----(接Arduino A4)
//RCC522_RST(CE)        PC7----(接Arduino D9)
//RCC522_NSS(SDA)      PB6----(接Arduino D10)
//RCC522_IRQ            悬空
4、SPI模式说明:SPI总线传输的四种模式:
 *  SPI传输的模式由CPOL:clock polarity 时钟的极性,和CPHA:clock phase 时钟的相位控制。
 *  RC522采用的是CPOL=0,CPHA=0的工作模式。在CubeMX中,SPI_CPHA设置为1Edge。
 * ┌─────────┬───────┬───────┬─────────────────┬─────────────────┐
 * │ SPI模式 │  CPOL │  CPHA │ 空闲时间SCLK状态 │  采样时刻       │
 * │    0    │   0   │  0    │    低电平       │ 奇数边沿(上升沿) │
 * │    1    │   0   │  1    │    低电平       │ 偶数边沿(下降沿) │
 * │    2    │   1   │  0    │    高电平       │ 奇数边沿(下降沿) │
 * │    3    │   1   │  1    │    高电平       │ 偶数边沿(上升沿) │
 * └─────────┴───────┴───────┴─────────────────┴─────────────────┘
5、应用函数
    MFRC_Init();//初始化
    PCD_Reset();//器件复位
    PCD_Request(PICC_REQALL, RxBuffer);//返回值为0,代表寻卡成功;并把卡类型存入RxBuffer中
    PCD_Anticoll(RxBuffer);   //把(十六进制)的4个字节卡号存储在数组RxBuffer中
***********************************/
 
// #define RC522_SDA GPIO_Port       GPIOB
// #define RC522_SDA Pin             GPIO_PIN_6	//cs、nss、SDA指同一个口
// #define RC522_RST GPIO_Port       GPIOC
// #define RC522_RST Pin             GPIO_PIN_7
 
#include "main.h"
#include <stdio.h>
#include <string.h>
#include "gpio.h"
#include "usart.h"
#include "RC522.h"
#include "Delay.h"
#include "Serial.h"
#include "oled.h"
extern SPI_HandleTypeDef hspi1;
 
// #define osDelay HAL_Delay
//#define osDelay delay_ms
 
#define RS522_RST(N) HAL_GPIO_WritePin(GPIOA, RST_Pin, N == 1 ? GPIO_PIN_SET : GPIO_PIN_RESET)
#define RC522_SDA(N) HAL_GPIO_WritePin(GPIOA, RC_SDA_Pin, N == 1 ? GPIO_PIN_SET : GPIO_PIN_RESET)
 
/**************************************************************************************
 * 函数名称:MFRC_Init
 * 功能描述:MFRC初始化
 * 入口参数:无
 * 出口参数:无
 * 返 回 值:无
 * 说    明:MFRC的SPI接口速率为0~10Mbps
 ***************************************************************************************/
void MFRC_Init(void)
{
    RC522_SDA(1);//SS
    RS522_RST(1);
}
 
/**************************************************************************************
 * 函数名称: SPI_RW_Byte
 * 功能描述: 模拟SPI读写一个字节
 * 入口参数: -byte:要发送的数据
 * 出口参数: -byte:接收到的数据
 ***************************************************************************************/
static uint8_t ret; // 这些函数是HAL与标准库不同的地方【读写函数】

uint8_t SPI2_RW_Byte(uint8_t byte)
{
//硬件SPI
    HAL_SPI_TransmitReceive(&hspi1, &byte, &ret, 1, 10); // 把byte 写入,并读出一个值,把它存入ret
    return ret;                                          // 入口是byte 的地址,读取时用的也是ret地址,一次只写入一个值10
//下面是模拟SPI
//  if (byte == 0x00) // 读数据时
//  {
//      ret = RC522_SPI_ReadByte();
//  }
//  RC522_SPI_SendByte(byte);
//  return ret;
}
 
/**************************************************************************************
 * 函数名称:MFRC_WriteReg
 * 功能描述:写一个寄存器
 * 入口参数:-addr:待写的寄存器地址
 *           -data:待写的寄存器数据
 * 出口参数:无
 * 返 回 值:无
 * 说    明:无
 ***************************************************************************************/
void MFRC_WriteReg(uint8_t addr, uint8_t data)
{
    uint8_t AddrByte;
    AddrByte = (addr << 1) & 0x7E; // 求出地址字节
    RC522_SDA(0);                  // NSS拉低
    SPI2_RW_Byte(AddrByte);        // 写地址字节
    SPI2_RW_Byte(data);            // 写数据
    RC522_SDA(1);                  // NSS拉高
}
 
/**************************************************************************************
 * 函数名称:MFRC_ReadReg
 * 功能描述:读一个寄存器
 * 入口参数:-addr:待读的寄存器地址
 * 出口参数:无
 * 返 回 值:-data:读到寄存器的数据
 * 说    明:无
 ***************************************************************************************/
uint8_t MFRC_ReadReg(uint8_t addr)
{
    uint8_t AddrByte, data;
    AddrByte = ((addr << 1) & 0x7E) | 0x80; // 求出地址字节
    RC522_SDA(0);                           // NSS拉低
    SPI2_RW_Byte(AddrByte);                 // 写地址字节
    data = SPI2_RW_Byte(0x00);              // 读数据
    RC522_SDA(1);                           // NSS拉高
    return data;
}
 
/**************************************************************************************
 * 函数名称:MFRC_SetBitMask
 * 功能描述:设置寄存器的位
 * 入口参数:-addr:待设置的寄存器地址
 *           -mask:待设置寄存器的位(可同时设置多个bit)
 * 出口参数:无
 * 返 回 值:无
 * 说    明:无
 ***************************************************************************************/
void MFRC_SetBitMask(uint8_t addr, uint8_t mask)
{
    uint8_t temp;
    temp = MFRC_ReadReg(addr);        // 先读回寄存器的值
    MFRC_WriteReg(addr, temp | mask); // 处理过的数据再写入寄存器
}
 
/**************************************************************************************
 * 函数名称:MFRC_ClrBitMask
 * 功能描述:清除寄存器的位
 * 入口参数:-addr:待清除的寄存器地址
 *           -mask:待清除寄存器的位(可同时清除多个bit)
 * 出口参数:无
 * 返 回 值:无
 * 说    明:无
 ***************************************************************************************/
void MFRC_ClrBitMask(uint8_t addr, uint8_t mask)
{
    uint8_t temp;
    temp = MFRC_ReadReg(addr);         // 先读回寄存器的值
    MFRC_WriteReg(addr, temp & ~mask); // 处理过的数据再写入寄存器
}
 
/**************************************************************************************
 * 函数名称:MFRC_CalulateCRC
 * 功能描述:用MFRC计算CRC结果
 * 入口参数:-pInData:带进行CRC计算的数据
 *           -len:带进行CRC计算的数据长度
 *           -pOutData:CRC计算结果
 * 出口参数:-pOutData:CRC计算结果
 * 返 回 值:无
 * 说    明:无
 ***************************************************************************************/
void MFRC_CalulateCRC(uint8_t *pInData, uint8_t len, uint8_t *pOutData)
{
    // 0xc1 1        2           pInData[2]
    uint8_t temp;
    uint32_t i;
    MFRC_ClrBitMask(MFRC_DivIrqReg, 0x04);     // 使能CRC中断
    MFRC_WriteReg(MFRC_CommandReg, MFRC_IDLE); // 取消当前命令的执行
    MFRC_SetBitMask(MFRC_FIFOLevelReg, 0x80);  // 清除FIFO及其标志位
    for (i = 0; i < len; i++)                  // 将待CRC计算的数据写入FIFO
    {
        MFRC_WriteReg(MFRC_FIFODataReg, *(pInData + i));
    }
    MFRC_WriteReg(MFRC_CommandReg, MFRC_CALCCRC); // 执行CRC计算
    i = 100000;
    do
    {
        temp = MFRC_ReadReg(MFRC_DivIrqReg); // 读取DivIrqReg寄存器的值
        i--;
    } while ((i != 0) && !(temp & 0x04));           // 等待CRC计算完成
    pOutData[0] = MFRC_ReadReg(MFRC_CRCResultRegL); // 读取CRC计算结果
    pOutData[1] = MFRC_ReadReg(MFRC_CRCResultRegM);
}
 
/**************************************************************************************
 * 函数名称:MFRC_CmdFrame
 * 功能描述:MFRC522和ISO14443A卡通讯的命令帧函数
 * 入口参数:-cmd:MFRC522命令字
 *           -pIndata:MFRC522发送给MF1卡的数据的缓冲区首地址
 *           -InLenByte:发送数据的字节长度
 *           -pOutdata:用于接收MF1卡片返回数据的缓冲区首地址
 *           -pOutLenBit:MF1卡返回数据的位长度
 * 出口参数:-pOutdata:用于接收MF1卡片返回数据的缓冲区首地址
 *           -pOutLenBit:用于MF1卡返回数据位长度的首地址
 * 返 回 值:-status:错误代码(MFRC_OK、MFRC_NOTAGERR、MFRC_ERR)
 * 说    明:无
 ***************************************************************************************/
char MFRC_CmdFrame(uint8_t cmd, uint8_t *pInData, uint8_t InLenByte, uint8_t *pOutData, uint16_t *pOutLenBit)
{
    uint8_t lastBits;
    uint8_t n;
    uint32_t i;
    char status = MFRC_ERR;
    uint8_t irqEn = 0x00;
    uint8_t waitFor = 0x00;
 
    /*根据命令设置标志位*/
    switch (cmd)
    {
    case MFRC_AUTHENT: // Mifare认证
        irqEn = 0x12;
        waitFor = 0x10; // idleIRq中断标志
        break;
    case MFRC_TRANSCEIVE: // 发送并接收数据
        irqEn = 0x77;
        waitFor = 0x30; // RxIRq和idleIRq中断标志
        break;
    }
 
    /*发送命令帧前准备*/
    MFRC_WriteReg(MFRC_ComIEnReg, irqEn | 0x80); // 开中断
    MFRC_ClrBitMask(MFRC_ComIrqReg, 0x80);       // 清除中断标志位SET1
    MFRC_WriteReg(MFRC_CommandReg, MFRC_IDLE);   // 取消当前命令的执行
    MFRC_SetBitMask(MFRC_FIFOLevelReg, 0x80);    // 清除FIFO缓冲区及其标志位
 
    /*发送命令帧*/
    for (i = 0; i < InLenByte; i++) // 写入命令参数
    {
        MFRC_WriteReg(MFRC_FIFODataReg, pInData[i]); // 写数据进 FIFODataReg
    }
    MFRC_WriteReg(MFRC_CommandReg, cmd); // 执行命令
    if (cmd == MFRC_TRANSCEIVE)
    {
        MFRC_SetBitMask(MFRC_BitFramingReg, 0x80); // 启动发送
    }
    i = 300000; // 根据时钟频率调整,操作M1卡最大等待时间25ms
    do          // 认证 与寻卡等待时间
    {
        n = MFRC_ReadReg(MFRC_ComIrqReg); // 查询事件中断
        i--;
    } while ((i != 0) && !(n & 0x01) && !(n & waitFor)); // 等待命令完成
    MFRC_ClrBitMask(MFRC_BitFramingReg, 0x80);           // 停止发送
 
    /*处理接收的数据*/
    if (i != 0)
    {
        // 读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
        if (!(MFRC_ReadReg(MFRC_ErrorReg) & 0x1B))
        {
            status = MFRC_OK;
            if (n & irqEn & 0x01) // 是否发生定时器中断
            {
                status = MFRC_NOTAGERR;
            }
            if (cmd == MFRC_TRANSCEIVE)
            {
                // 读FIFO中保存的字节数
                n = MFRC_ReadReg(MFRC_FIFOLevelReg);
                lastBits = MFRC_ReadReg(MFRC_ControlReg) & 0x07; // 最后接收到得字节的有效位数
                if (lastBits)
                {
                    *pOutLenBit = (n - 1) * 8 + lastBits; // N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
                }
                else
                {
                    *pOutLenBit = n * 8; // 最后接收到的字节整个字节有效
                }
                if (n == 0)
                {
                    n = 1;
                }
                if (n > MFRC_MAXRLEN)
                {
                    n = MFRC_MAXRLEN;
                }
                for (i = 0; i < n; i++)
                {
                    pOutData[i] = MFRC_ReadReg(MFRC_FIFODataReg);
                }
            }
        }
        else
        {
            status = MFRC_ERR;
        }
    }
 
    MFRC_SetBitMask(MFRC_ControlReg, 0x80);    // 停止定时器运行
    MFRC_WriteReg(MFRC_CommandReg, MFRC_IDLE); // 取消当前命令的执行
 
    return status;
}
 
/**************************************************************************************
 * 函数名称:PCD_Reset
 * 功能描述:PCD复位
 * 入口参数:无
 * 出口参数:无
 * 返 回 值:无
 * 说    明:无
 ***************************************************************************************/
void PCD_Reset(void)
{
    /*硬复位*/
    RS522_RST(1); // 用到复位引脚
    HAL_Delay(2);
    RS522_RST(0);
    HAL_Delay(2);
    RS522_RST(1);
    HAL_Delay(2);
 
    /*软复位*/
    MFRC_WriteReg(MFRC_CommandReg, MFRC_RESETPHASE);
    HAL_Delay(2);
 
    /*复位后的初始化配置*/
    MFRC_WriteReg(MFRC_ModeReg, 0x3D);   // CRC初始值0x6363
    MFRC_WriteReg(MFRC_TReloadRegL, 30); // 定时器重装值
    MFRC_WriteReg(MFRC_TReloadRegH, 0);
    MFRC_WriteReg(MFRC_TModeReg, 0x8D);      // 定义内部定时器的设置
    MFRC_WriteReg(MFRC_TPrescalerReg, 0x3E); // 设置定时器预分频值
    MFRC_WriteReg(MFRC_TxAutoReg, 0x40);     // 调制发送信号为100%ASK
 
    PCD_AntennaOff(); // 关天线
    HAL_Delay(2);
    PCD_AntennaOn(); // 开天线
}
 
/**************************************************************************************
 * 函数名称:PCD_AntennaOn
 * 功能描述:开启天线,使能PCD发送能量载波信号
 * 入口参数:无
 * 出口参数:无
 * 返 回 值:无
 * 说    明:每次开启或关闭天线之间应至少有1ms的间隔
 ***************************************************************************************/
void PCD_AntennaOn(void)
{
    uint8_t temp;
    temp = MFRC_ReadReg(MFRC_TxControlReg);
    if (!(temp & 0x03))
    {
        MFRC_SetBitMask(MFRC_TxControlReg, 0x03);
    }
}
 
/**************************************************************************************
 * 函数名称:PCD_AntennaOff
 * 功能描述:关闭天线,失能PCD发送能量载波信号
 * 入口参数:无
 * 出口参数:无
 * 返 回 值:无
 * 说    明:每次开启或关闭天线之间应至少有1ms的间隔
 ***************************************************************************************/
void PCD_AntennaOff(void)
{
    MFRC_ClrBitMask(MFRC_TxControlReg, 0x03);
}
 
/***************************************************************************************
 * 函数名称:PCD_Init
 * 功能描述:读写器初始化
 * 入口参数:无
 * 出口参数:无
 * 返 回 值:无
 * 说    明:无
 ***************************************************************************************/
void PCD_Init(void)
{
    MFRC_Init();      // MFRC管脚配置
    PCD_Reset();      // PCD复位  并初始化配置
    PCD_AntennaOff(); // 关闭天线
    PCD_AntennaOn();  // 开启天线
}
 
/***************************************************************************************
 * 函数名称:PCD_Request
 * 功能描述:寻卡
 * 入口参数: -RequestMode:寻卡方式
 *                         PICC_REQIDL:寻天线区内未进入休眠状态
 *                         PICC_REQALL:寻天线区内全部卡
 *           -pCardType:  用于保存卡片类型
 * 出口参数:-pCardType:卡片类型
 *                       0x4400:Mifare_UltraLight
 *                       0x0400:Mifare_One(S50)
 *                       0x0200:Mifare_One(S70)
 *                       0x0800:Mifare_Pro(X)
 *                       0x4403:Mifare_DESFire
 * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
 * 说    明:无
 ***************************************************************************************/
char PCD_Request(uint8_t RequestMode, uint8_t *pCardType)
{
    int status;
    uint16_t unLen;
    uint8_t CmdFrameBuf[MFRC_MAXRLEN];
 
    MFRC_ClrBitMask(MFRC_Status2Reg, 0x08);   // 关内部温度传感器
    MFRC_WriteReg(MFRC_BitFramingReg, 0x07);  // 存储模式,发送模式,是否启动发送等
    MFRC_SetBitMask(MFRC_TxControlReg, 0x03); // 配置调制信号13.56MHZ
 
    CmdFrameBuf[0] = RequestMode; // 存入 卡片命令字
 
    status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 1, CmdFrameBuf, &unLen);
 
    if ((status == PCD_OK) && (unLen == 0x10)) // 寻卡成功返回卡类型
    {
        *pCardType = CmdFrameBuf[0];
        *(pCardType + 1) = CmdFrameBuf[1];
    }
 
    return status;
}
 
/***************************************************************************************
 * 函数名称:PCD_Anticoll
 * 功能描述:防冲撞,获取卡号
 * 入口参数:-pSnr:用于保存卡片序列号,4字节
 * 出口参数:-pSnr:卡片序列号,4字节
 * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
 * 说    明:无
 ***************************************************************************************/
char PCD_Anticoll(uint8_t *pSnr)
{
    char status;
    uint8_t i, snr_check = 0;
    uint16_t unLen;
    uint8_t CmdFrameBuf[MFRC_MAXRLEN];
 
    MFRC_ClrBitMask(MFRC_Status2Reg, 0x08);  // 清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
    MFRC_WriteReg(MFRC_BitFramingReg, 0x00); // 清理寄存器 停止收发
    MFRC_ClrBitMask(MFRC_CollReg, 0x80);     // 清ValuesAfterColl所有接收的位在冲突后被清除
 
    CmdFrameBuf[0] = PICC_ANTICOLL1; // 卡片防冲突命令
    CmdFrameBuf[1] = 0x20;
 
    status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 2, CmdFrameBuf, &unLen); // 与卡片通信
 
    if (status == PCD_OK) // 通信成功
    {
        for (i = 0; i < 4; i++)
        {
            *(pSnr + i) = CmdFrameBuf[i]; // 读出UID
            snr_check ^= CmdFrameBuf[i];
        }
        if (snr_check != CmdFrameBuf[i])
        {
            status = PCD_ERR;
        }
    }
 
    MFRC_SetBitMask(MFRC_CollReg, 0x80);
    return status;
}
 
/***************************************************************************************
 * 函数名称:PCD_Select
 * 功能描述:选定卡片
 * 入口参数:-pSnr:卡片序列号,4字节
 * 出口参数:无
 * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
 * 说    明:无
 ***************************************************************************************/
char PCD_Select(uint8_t *pSnr)
{
    char status;
    uint8_t i;
    uint16_t unLen;
    uint8_t CmdFrameBuf[MFRC_MAXRLEN];
 
    CmdFrameBuf[0] = PICC_ANTICOLL1;
    CmdFrameBuf[1] = 0x70;
    CmdFrameBuf[6] = 0;
    for (i = 0; i < 4; i++)
    {
        CmdFrameBuf[i + 2] = *(pSnr + i);
        CmdFrameBuf[6] ^= *(pSnr + i);
    }
    MFRC_CalulateCRC(CmdFrameBuf, 7, &CmdFrameBuf[7]);
 
    MFRC_ClrBitMask(MFRC_Status2Reg, 0x08);
 
    status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 9, CmdFrameBuf, &unLen);
 
    if ((status == PCD_OK) && (unLen == 0x18))
    {
        status = PCD_OK;
    }
    else
    {
        status = PCD_ERR;
    }
    return status;
}
 
/***************************************************************************************
 * 函数名称:PCD_AuthState
 * 功能描述:验证卡片密码
 * 入口参数:-AuthMode:验证模式
 *                   PICC_AUTHENT1A:验证A密码
 *                   PICC_AUTHENT1B:验证B密码
 *           -BlockAddr:块地址(0~63)
 *           -pKey:密码
 *           -pSnr:卡片序列号,4字节
 * 出口参数:无
 * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
 * 说    明:验证密码时,以扇区为单位,BlockAddr参数可以是同一个扇区的任意块
 ***************************************************************************************/
char PCD_AuthState(uint8_t AuthMode, uint8_t BlockAddr, uint8_t *pKey, uint8_t *pSnr)
{
    char status;
    uint16_t unLen;
    uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
    CmdFrameBuf[0] = AuthMode;
    CmdFrameBuf[1] = BlockAddr;
    for (i = 0; i < 6; i++)
    {
        CmdFrameBuf[i + 2] = *(pKey + i);
    }
    for (i = 0; i < 4; i++)
    {
        CmdFrameBuf[i + 8] = *(pSnr + i);
    }
 
    status = MFRC_CmdFrame(MFRC_AUTHENT, CmdFrameBuf, 12, CmdFrameBuf, &unLen);
    if ((status != PCD_OK) || (!(MFRC_ReadReg(MFRC_Status2Reg) & 0x08)))
    {
        status = PCD_ERR;
    }
 
    return status;
}
 
/***************************************************************************************
 * 函数名称:PCD_WriteBlock
 * 功能描述:写MF1卡数据块
 * 入口参数:-BlockAddr:块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
 *           -pData: 用于保存待写入的数据,16字节
 * 出口参数:无
 * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
 * 说    明:无
 ***************************************************************************************/
char PCD_WriteBlock(uint8_t BlockAddr, uint8_t *pData)
{
    char status;
    uint16_t unLen;
    uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
 
    CmdFrameBuf[0] = PICC_WRITE;
    CmdFrameBuf[1] = BlockAddr;
    MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
 
    status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
 
    if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
    {
        status = PCD_ERR;
    }
 
    if (status == PCD_OK)
    {
        for (i = 0; i < 16; i++)
        {
            CmdFrameBuf[i] = *(pData + i);
        }
        MFRC_CalulateCRC(CmdFrameBuf, 16, &CmdFrameBuf[16]);
 
        status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 18, CmdFrameBuf, &unLen);
 
        if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
        {
            status = PCD_ERR;
        }
    }
 
    return status;
}
 
/***************************************************************************************
 * 函数名称:PCD_ReadBlock
 * 功能描述:读MF1卡数据块
 * 入口参数:-BlockAddr:块地址
 *           -pData: 用于保存读出的数据,16字节
 * 出口参数:-pData: 用于保存读出的数据,16字节
 * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
 * 说    明:无
 ***************************************************************************************/
char PCD_ReadBlock(uint8_t BlockAddr, uint8_t *pData)
{
    char status;
    uint16_t unLen;
    uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
 
    CmdFrameBuf[0] = PICC_READ;
    CmdFrameBuf[1] = BlockAddr;
    MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
 
    status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
    if ((status == PCD_OK) && (unLen == 0x90))
    {
        for (i = 0; i < 16; i++)
        {
            *(pData + i) = CmdFrameBuf[i];
        }
    }
    else
    {
        status = PCD_ERR;
    }
 
    return status;
}
 
/***************************************************************************************
 * 函数名称:PCD_Value
 * 功能描述:对MF1卡数据块增减值操作
 * 入口参数:
 *           -BlockAddr:块地址
 *           -pValue:四字节增值的值,低位在前
 *           -mode:数值块操作模式
 *                  PICC_INCREMENT:增值
 *                  PICC_DECREMENT:减值
 * 出口参数:无
 * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
 * 说    明:无
 ***************************************************************************************/
char PCD_Value(uint8_t mode, uint8_t BlockAddr, uint8_t *pValue)
{
    // 0XC1        1           Increment[4]={0x03, 0x01, 0x01, 0x01};
    char status;
    uint16_t unLen;
    uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
 
    CmdFrameBuf[0] = mode;
    CmdFrameBuf[1] = BlockAddr;
    MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
 
    status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
 
    if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
    {
        status = PCD_ERR;
    }
 
    if (status == PCD_OK)
    {
        for (i = 0; i < 16; i++)
        {
            CmdFrameBuf[i] = *(pValue + i);
        }
        MFRC_CalulateCRC(CmdFrameBuf, 4, &CmdFrameBuf[4]);
        unLen = 0;
        status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 6, CmdFrameBuf, &unLen);
        if (status != PCD_ERR)
        {
            status = PCD_OK;
        }
    }
 
    if (status == PCD_OK)
    {
        CmdFrameBuf[0] = PICC_TRANSFER;
        CmdFrameBuf[1] = BlockAddr;
        MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
 
        status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
 
        if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
        {
            status = PCD_ERR;
        }
    }
    return status;
}
 
/***************************************************************************************
 * 函数名称:PCD_BakValue
 * 功能描述:备份钱包(块转存)
 * 入口参数:-sourceBlockAddr:源块地址
 *                -goalBlockAddr   :目标块地址
 * 出口参数:无
 * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
 * 说    明:只能在同一个扇区内转存
 ***************************************************************************************/
char PCD_BakValue(uint8_t sourceBlockAddr, uint8_t goalBlockAddr)
{
    char status;
    uint16_t unLen;
    uint8_t CmdFrameBuf[MFRC_MAXRLEN];
 
    CmdFrameBuf[0] = PICC_RESTORE;
    CmdFrameBuf[1] = sourceBlockAddr;
    MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
    status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
    if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
    {
        status = PCD_ERR;
    }
 
    if (status == PCD_OK)
    {
        CmdFrameBuf[0] = 0;
        CmdFrameBuf[1] = 0;
        CmdFrameBuf[2] = 0;
        CmdFrameBuf[3] = 0;
        MFRC_CalulateCRC(CmdFrameBuf, 4, &CmdFrameBuf[4]);
        status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 6, CmdFrameBuf, &unLen);
        if (status != PCD_ERR)
        {
            status = PCD_OK;
        }
    }
 
    if (status != PCD_OK)
    {
        return PCD_ERR;
    }
 
    CmdFrameBuf[0] = PICC_TRANSFER;
    CmdFrameBuf[1] = goalBlockAddr;
    MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
    status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
    if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
    {
        status = PCD_ERR;
    }
 
    return status;
}
 
/***************************************************************************************
 * 函数名称:PCD_Halt
 * 功能描述:命令卡片进入休眠状态
 * 入口参数:无
 * 出口参数:无
 * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
 * 说    明:无
 ***************************************************************************************/
char PCD_Halt(void)
{
    char status;
    uint16_t unLen;
    uint8_t CmdFrameBuf[MFRC_MAXRLEN];
 
    CmdFrameBuf[0] = PICC_HALT;
    CmdFrameBuf[1] = 0;
    MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
 
    status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
 
    return status;
}
#ifndef _RC522_H
#define _RC522_H
#include "main.h"
#include "spi.h"
/***********************************************************************************
*								MFRC522驱动程序			        				   *
************************************************************************************/
/*MFRC522寄存器定义*/
//PAGE0
#define MFRC_RFU00              	0x00
#define MFRC_CommandReg         	0x01
#define MFRC_ComIEnReg             	0x02
#define MFRC_DivlEnReg             	0x03
#define MFRC_ComIrqReg             	0x04
#define MFRC_DivIrqReg             	0x05
#define MFRC_ErrorReg              	0x06
#define MFRC_Status1Reg            	0x07
#define MFRC_Status2Reg            	0x08
#define MFRC_FIFODataReg           	0x09
#define MFRC_FIFOLevelReg          	0x0A
#define MFRC_WaterLevelReg         	0x0B
#define MFRC_ControlReg            	0x0C
#define MFRC_BitFramingReg         	0x0D
#define MFRC_CollReg               	0x0E
#define MFRC_RFU0F                 	0x0F
//PAGE1
#define MFRC_RFU10                 	0x10
#define MFRC_ModeReg               	0x11
#define MFRC_TxModeReg             	0x12
#define MFRC_RxModeReg             	0x13
#define MFRC_TxControlReg          	0x14
#define MFRC_TxAutoReg             	0x15 //中文手册有误
#define MFRC_TxSelReg              	0x16
#define MFRC_RxSelReg              	0x17
#define MFRC_RxThresholdReg        	0x18
#define MFRC_DemodReg              	0x19
#define MFRC_RFU1A                 	0x1A
#define MFRC_RFU1B                 	0x1B
#define MFRC_MifareReg             	0x1C
#define MFRC_RFU1D                 	0x1D
#define MFRC_RFU1E                 	0x1E
#define MFRC_SerialSpeedReg        	0x1F
//PAGE2
#define MFRC_RFU20                 	0x20
#define MFRC_CRCResultRegM         	0x21
#define MFRC_CRCResultRegL         	0x22
#define MFRC_RFU23                 	0x23
#define MFRC_ModWidthReg           	0x24
#define MFRC_RFU25                 	0x25
#define MFRC_RFCfgReg              	0x26
#define MFRC_GsNReg                	0x27
#define MFRC_CWGsCfgReg            	0x28
#define MFRC_ModGsCfgReg           	0x29
#define MFRC_TModeReg              	0x2A
#define MFRC_TPrescalerReg         	0x2B
#define MFRC_TReloadRegH           	0x2C
#define MFRC_TReloadRegL           	0x2D
#define MFRC_TCounterValueRegH     	0x2E
#define MFRC_TCounterValueRegL     	0x2F
//PAGE3
#define MFRC_RFU30                 	0x30
#define MFRC_TestSel1Reg           	0x31
#define MFRC_TestSel2Reg           	0x32
#define MFRC_TestPinEnReg          	0x33
#define MFRC_TestPinValueReg       	0x34
#define MFRC_TestBusReg            	0x35
#define MFRC_AutoTestReg           	0x36
#define MFRC_VersionReg            	0x37
#define MFRC_AnalogTestReg         	0x38
#define MFRC_TestDAC1Reg           	0x39
#define MFRC_TestDAC2Reg           	0x3A
#define MFRC_TestADCReg            	0x3B
#define MFRC_RFU3C                 	0x3C
#define MFRC_RFU3D                 	0x3D
#define MFRC_RFU3E                 	0x3E
#define MFRC_RFU3F                 	0x3F
 
/*MFRC522的FIFO长度定义*/
#define MFRC_FIFO_LENGTH       		64
 
/*MFRC522传输的帧长定义*/
#define MFRC_MAXRLEN                18
 
/*MFRC522命令集,中文手册P59*/
#define MFRC_IDLE              		0x00	//取消当前命令的执行
#define MFRC_CALCCRC           		0x03    //激活CRC计算
#define MFRC_TRANSMIT          		0x04    //发送FIFO缓冲区内容
#define MFRC_NOCMDCHANGE            0x07	//无命令改变
#define MFRC_RECEIVE           		0x08    //激活接收器接收数据
#define MFRC_TRANSCEIVE        		0x0C    //发送并接收数据
#define MFRC_AUTHENT           		0x0E    //执行Mifare认证(验证密钥)
#define MFRC_RESETPHASE        		0x0F    //复位MFRC522
 
/*MFRC522通讯时返回的错误代码*/
#define MFRC_OK                 	(char)0
#define MFRC_NOTAGERR            	(char)(-1)
#define MFRC_ERR                	(char)(-2)
 
/*MFRC522函数声明*/
void MFRC_Init(void);
void MFRC_WriteReg(uint8_t addr, uint8_t data);
uint8_t MFRC_ReadReg(uint8_t addr);
void MFRC_SetBitMask(uint8_t addr, uint8_t mask);
void MFRC_ClrBitMask(uint8_t addr, uint8_t mask);
void MFRC_CalulateCRC(uint8_t *pInData, uint8_t len, uint8_t *pOutData);
char MFRC_CmdFrame(uint8_t cmd, uint8_t *pInData, uint8_t InLenByte, uint8_t *pOutData, uint16_t *pOutLenBit);
 
 
/***********************************************************************************
*							MFRC552与MF1卡通讯接口程序	 		     	     	   *
************************************************************************************/
/*Mifare1卡片命令字*/
#define PICC_REQIDL           	0x26               	//寻天线区内未进入休眠状态的卡
#define PICC_REQALL           	0x52               	//寻天线区内全部卡
#define PICC_ANTICOLL1        	0x93               	//防冲撞
#define PICC_ANTICOLL2        	0x95               	//防冲撞
#define PICC_AUTHENT1A        	0x60               	//验证A密钥
#define PICC_AUTHENT1B        	0x61               	//验证B密钥
#define PICC_READ             	0x30               	//读块
#define PICC_WRITE            	0xA0               	//写块
#define PICC_DECREMENT        	0xC0               	//减值(扣除)
#define PICC_INCREMENT        	0xC1               	//增值(充值)
#define PICC_TRANSFER         	0xB0               	//转存(传送)
#define PICC_RESTORE          	0xC2               	//恢复(重储)
#define PICC_HALT             	0x50               	//休眠
 
/*PCD通讯时返回的错误代码*/
#define PCD_OK                 	(char)0					//成功
#define PCD_NOTAGERR            (char)(-1)			//无卡
#define PCD_ERR                	(char)(-2)			//出错
 
/*PCD函数声明*/
void PCD_Init(void);
void PCD_Reset(void);
void PCD_AntennaOn(void);
void PCD_AntennaOff(void);
char PCD_Request(uint8_t RequestMode, uint8_t *pCardType);  //寻卡,并返回卡的类型
char PCD_Anticoll(uint8_t *pSnr);                           //防冲突,返回卡号
char PCD_Select(uint8_t *pSnr);                             //选卡
char PCD_AuthState(uint8_t AuthMode, uint8_t BlockAddr, uint8_t *pKey, uint8_t *pSnr); //验证密码(密码A和密码B)
char PCD_WriteBlock(uint8_t BlockAddr, uint8_t *pData);   //写数据
char PCD_ReadBlock(uint8_t BlockAddr, uint8_t *pData);    //读数据
char PCD_Value(uint8_t mode, uint8_t BlockAddr, uint8_t *pValue);
char PCD_BakValue(uint8_t sourceBlockAddr, uint8_t goalBlockAddr);
char PCD_Halt(void);
void StartIDcardTask(void const * argument);
#endif
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "gpio.h"
#include "RC522.h"
#include "RFID.h"
#include "delay.h"
#include "Oled.h"
uint8_t readUid[5];
uint8_t UID[5] = {0x33, 0xe2, 0x54, 0xf7};							// 自定义的卡号,用于比较
uint8_t DefaultKey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 默认秘钥
 
/***************************************************************************************
 * 函数名称:RC522_Init
 * 功能描述:初始化
 * 入口参数:无
 * 出口参数:无
 * 返 回 值:无
 * 说    明:无
 ***************************************************************************************/
void RC522_Init(void)
{
	MFRC_Init();
	PCD_Reset();
	//printf("RC522初始化完成\n");
	OLED_ShowString(1,4,"ok");
}
 
/***************************************************************************************
 * 函数名称:门禁开门
 * 功能描述:只读取并显示卡号,成功读取到卡号就退出,并调用回调功能函数
 * 入口参数:-readUid:用于保存卡片序列号,4字节
			 -funCallBack:函数传参,无需会掉功能函数时填NULL即可
 * 出口参数:
 * 返 回 值:读到卡号返回0,失败返回1
 * 说    明:无
 ***************************************************************************************/
uint8_t EntranceGuard(uint8_t *readUid, void (*funCallBack)(void))
{
	uint8_t Temp[5];												 // 存放IC卡的类型和UID(IC卡序列号)
	if (PCD_Request(PICC_REQALL, Temp) == PCD_OK) // 寻卡
	{//成功
		if (Temp[0] == 0x04 && Temp[1] == 0x00)
			printf("Mifare1-S50\n");
		else if (Temp[0] == 0x02 && Temp[1] == 0x00)
			printf("Mifare1-S70");
		else if (Temp[0] == 0x44 && Temp[1] == 0x00)
			printf("Mifare-UltraLight(MF0)");
		else if (Temp[0] == 0x08 && Temp[1] == 0x00)
			printf("Mifare-Pro(MF2)");
		else if (Temp[0] == 0x44 && Temp[1] == 0x03)
			printf("Mifare Desire(MF3)");
		else
			printf("Unknown\r\n");
		if (PCD_Anticoll(readUid) == PCD_OK) // 防冲撞,获取卡号,存入readUid
		{									 // 防冲撞成功
			if (funCallBack != NULL)
				funCallBack(); // 调用功能执行函数,如指示灯信号
			return 0;
		}
	}
	return 1;
}
 
/***************************************************************************************
 * 函数名称:DoorSensor
 * 功能描述:门磁控制信号
 * 入口参数:无
 * 出口参数:无
 * 返 回 值:无
 * 说    明:无
 ***************************************************************************************/
//void DoorSensor(void)
//{
//	// 【STM32F446,NUCLEO-F446RE板】使用STM32CubeMX创建MDK工程,实现流水灯
//	HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); // LED亮
//	HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);	 // LED灭
//	HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); // LED灭
//	HAL_Delay(500);												 // 延时 500ms
//	HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);	 // LED灭
//	HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); // LED亮
//	HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET);	 // LED灭
//}
 
/***************************************************************************************
 * 函数名称:RfidIndicator
 * 功能描述:指示灯信号
 * 入口参数:无
 * 出口参数:无
 * 返 回 值:无
 * 说    明:无
 ***************************************************************************************/
//void RfidIndicator(void)
//{
//	HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_RESET); // LED1亮
//	HAL_Delay(1000);											 // 延时 500ms
//	HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_SET);	 // LED1灭
//}
 
/**
 * @brief  判断 addr 是否数据块
 * @param  addr,块绝对地址(0-63)
 * @retval 返回值 1:是数据块;0:不是数据块
 */
char IsDataBlock(uint8_t addr)
{
	if (addr == 0)
	{
		printf("第0扇区的块0不可更改,不应对其进行操作\n");
		return 0;
	}
	// 如果是数据块(不包含数据块0)
	if ((addr < 64) && (((addr + 1) % 4) != 0))
	{
		return 1;
	}
 
	printf("块地址不是指向数据块\n");
	return 0;
}
 
/**
 * @brief  写 pData 字符串到M1卡中的数据块
 * @param  addr,数据块地址(不能写入控制块)
 * @param  pData,写入的数据,16字节
 * @retval 状态值= PCD_OK,成功
 */
char PCD_WriteString(uint8_t addr, uint8_t *pData)
{
	/* 如果是数据块(不包含数据块0),则写入 */
	if (IsDataBlock(addr))
	{
		return PCD_WriteBlock(addr, pData);
	}
 
	return PCD_ERR;
}
 
/**
 * @brief  读取M1卡中的一块数据到 pData
 * @param  addr,数据块地址(不读取控制块)
 * @param  pData,读出的数据,16字节
 * @retval 状态值= PCD_OK,成功
 */
char PCD_ReadString(uint8_t addr, uint8_t *pData)
{
	/* 如果是数据块(不包含数据块0),则读取 */
	if (IsDataBlock(addr))
	{
		return PCD_ReadBlock(addr, pData);
	}
 
	return PCD_ERR;
}
 
/**
 * @DESCRIPTION: 写入钱包金额
 * @INPUT  ARGS: none
 * @OUTPUT ARGS: none
 * @NOTE       : none
 * @param {uint8_t} addr:块地址
 * @param {uint32_t} pData:写入的金额
 * @return {*} 成功返回PCD_OK
 */
char WriteAmount(uint8_t addr, uint32_t pData)
{
	char status;
	uint8_t ucComMF522Buf[16];
	ucComMF522Buf[0] = (pData & ((uint32_t)0x000000ff));
	ucComMF522Buf[1] = (pData & ((uint32_t)0x0000ff00)) >> 8;
	ucComMF522Buf[2] = (pData & ((uint32_t)0x00ff0000)) >> 16;
	ucComMF522Buf[3] = (pData & ((uint32_t)0xff000000)) >> 24;
 
	ucComMF522Buf[4] = ~(pData & ((uint32_t)0x000000ff));
	ucComMF522Buf[5] = ~(pData & ((uint32_t)0x0000ff00)) >> 8;
	ucComMF522Buf[6] = ~(pData & ((uint32_t)0x00ff0000)) >> 16;
	ucComMF522Buf[7] = ~(pData & ((uint32_t)0xff000000)) >> 24;
 
	ucComMF522Buf[8] = (pData & ((uint32_t)0x000000ff));
	ucComMF522Buf[9] = (pData & ((uint32_t)0x0000ff00)) >> 8;
	ucComMF522Buf[10] = (pData & ((uint32_t)0x00ff0000)) >> 16;
	ucComMF522Buf[11] = (pData & ((uint32_t)0xff000000)) >> 24;
 
	ucComMF522Buf[12] = addr;
	ucComMF522Buf[13] = ~addr;
	ucComMF522Buf[14] = addr;
	ucComMF522Buf[15] = ~addr;
	status = PCD_WriteBlock(addr, ucComMF522Buf);
	return status;
}
 
/**
 * @DESCRIPTION: 读取钱包金额
 * @INPUT  ARGS: none
 * @OUTPUT ARGS: none
 * @NOTE       : none
 * @param {uint8_t} addr:块地址
 * @param {uint32_t} *pData:读出的金额
 * @return {*}: 成功返回PCD_OK
 */
char ReadAmount(uint8_t addr, uint32_t *pData)
{
 
	char status = PCD_ERR;
	uint8_t j;
	uint8_t ucComMF522Buf[16];
	status = PCD_ReadBlock(addr, ucComMF522Buf);
	if (status != PCD_OK)
		return status;
	for (j = 0; j < 4; j++)
	{
		if ((ucComMF522Buf[j] != ucComMF522Buf[j + 8]) && (ucComMF522Buf[j] != ~ucComMF522Buf[j + 4])) // 验证一下是不是钱包的数据
			break;
	}
	if (j == 4)
	{
		status = PCD_OK;
		*pData = ucComMF522Buf[0] + (ucComMF522Buf[1] << 8) + (ucComMF522Buf[2] << 16) + (ucComMF522Buf[3] << 24);
	}
	else
	{
		status = PCD_ERR;
		*pData = 0;
	}
	return status;
}
 
/**
 * @brief 修改控制块 addr 的密码A。注意 addr 指的是控制块的地址。
 *        必须要校验密码B,密码B默认为6个0xFF,如果密码B也忘记了,那就改不了密码A了
 * @note  注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
 * @param addr:[控制块]所在的地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
 * @param pKeyA:指向新的密码A字符串,六个字符,比如 "123456"
 * @retval 成功返回 PCD_OK
 */
char ChangeKeyA(uint8_t addr, uint8_t *pKeyA)
{
	uint8_t KeyBValue[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
	uint8_t ucArrayID[4];										// 先后存放IC卡的类型和UID(IC卡序列号)
	uint8_t ucComMF522Buf[16];
	uint8_t j;
 
	// 寻卡
	while (PCD_Request(PICC_REQALL, ucArrayID) != PCD_OK)
	{
		printf("寻卡失败\n");
		delay_ms(1000);
	}
 
	printf("寻卡成功\n");
 
	// 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
	if (PCD_Anticoll(ucArrayID) == PCD_OK)
	{
		// 选中卡
		PCD_Select(ucArrayID);
 
		// 校验 B 密码
		if (PCD_AuthState(PICC_AUTHENT1B, addr, KeyBValue, ucArrayID) != PCD_OK)
		{
			printf("检验密码B失败\n");
		}
 
		// 读取控制块里原本的数据(只要修改密码A,其他数据不改)
		if (PCD_ReadBlock(addr, ucComMF522Buf) != PCD_OK)
		{
			printf("读取控制块数据失败\n");
			return PCD_ERR;
		}
 
		// 修改密码A
		for (j = 0; j < 6; j++)
			ucComMF522Buf[j] = pKeyA[j];
 
		if (PCD_WriteBlock(addr, ucComMF522Buf) != PCD_OK)
		{
			printf("写入数据到控制块失败\n");
			return PCD_ERR;
		}
 
		printf("密码A修改成功!\n");
		PCD_Halt();
 
		return PCD_OK;
	}
 
	return PCD_ERR;
}
 
/**
 * @brief 按照RC522操作流程写入16字节数据到块 addr
 *        函数里校验的是密码B,密码B默认为6个0xFF,也可以校验密码A
 *        用法:WriteDataBlock( 1, "123456789\n", 10); //字符串不够16个字节的后面补零写入
 * @note  注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
 *        注意:使用该函数要注意 addr 是块0、数据块还是控制块,该函数内部不对此做判断
 * @param addr:任意块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
 * @param pData:指向要写入的数据,最大16个字符
 * @param Len:要写入数据的字节数
 * @retval 成功返回 PCD_OK
 */
char WriteDataBlock(uint8_t addr, uint8_t *pData, uint8_t Len)
{
	uint8_t KeyBValue[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
	uint8_t ucArrayID[4];										// 先后存放IC卡的类型和UID(IC卡序列号)
	uint8_t ucComMF522Buf[16];
	uint8_t j;
 
	// 寻卡
	while (PCD_Request(PICC_REQALL, ucArrayID) != PCD_OK)
	{
		printf("寻卡失败\n");
		delay_ms(1000);
	}
 
	printf("寻卡成功\n");
 
	// 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
	if (PCD_Anticoll(ucArrayID) == PCD_OK)
	{
		// 选中卡
		PCD_Select(ucArrayID);
 
		// 校验 B 密码
		if (PCD_AuthState(PICC_AUTHENT1B, addr, KeyBValue, ucArrayID) != PCD_OK)
		{
			printf("检验密码B失败\n");
		}
 
		// 拷贝 pData 里的 Len 个字符到 ucComMF522Buf
		for (j = 0; j < 16; j++)
		{
			if (j < Len)
				ucComMF522Buf[j] = pData[j];
			else
				ucComMF522Buf[j] = 0; // 16个字节若是未填满的字节置0
		}
 
		// 写入字符串
		if (PCD_WriteBlock(addr, ucComMF522Buf) != PCD_OK)
		{
			printf("写入数据到数据块失败\n");
			return PCD_ERR;
		}
 
		printf("写入数据成功!\n");
		PCD_Halt();
 
		return PCD_OK;
	}
 
	return PCD_ERR;
}
 
/**
 * @brief 按照RC522操作流程读取块 addr
 *        函数里校验的是密码B,密码B默认为6个0xFF,也可以校验密码A
 *        用法:ReadDataBlock( 1, databuf);  // databuf 至少为16字节:uint8_t databuf[16];
 * @note  注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
 *        注意:使用该函数要注意 addr 是块0、数据块还是控制块,该函数内部不对此做判断
 * @param addr:任意块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
 * @param pData:指向读取到的数据,包含16个字符
 * @retval 成功返回 PCD_OK
 */
char ReadDataBlock(uint8_t addr, uint8_t *pData)
{
	uint8_t KeyBValue[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
	uint8_t ucArrayID[4];										// 先后存放IC卡的类型和UID(IC卡序列号)
 
	// 寻卡
	while (PCD_Request(PICC_REQALL, ucArrayID) != PCD_OK)
	{
		printf("寻卡失败\n");
		delay_ms(1000);
	}
 
	printf("寻卡成功\n");
 
	// 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
	if (PCD_Anticoll(ucArrayID) == PCD_OK)
	{
		// 选中卡
		PCD_Select(ucArrayID);
 
		// 校验 B 密码
		if (PCD_AuthState(PICC_AUTHENT1B, addr, KeyBValue, ucArrayID) != PCD_OK)
		{
			printf("检验密码B失败\n");
		}
		// 读取数据块里的数据到 pData
		if (PCD_ReadBlock(addr, pData) != PCD_OK)
		{
			printf("读取数据块失败\n");
			return PCD_ERR;
		}
		printf("读取数据成功!\n");
		PCD_Halt();
		return PCD_OK;
	}
	return PCD_ERR;
}
#ifndef _RFID_H
#define _RFID_H
#include "main.h"
 
extern uint8_t readUid[5];
extern uint8_t UID[5];		//定义一张已知卡号,可以通过串口打印通过下面读取到的打印到上位机,再把那个读取的卡号填入数组
extern uint8_t DefaultKey[6]; // 默认秘钥
 
/*函数声明*/
void RC522_Init(void);
uint8_t EntranceGuard(uint8_t *readUid,void(*funCallBack)(void));
//void DoorSensor(void);
//void RfidIndicator(void);
//void notarize_type1(void);
 
char WriteAmount(uint8_t addr, uint32_t pData);
char ReadAmount(uint8_t addr, uint32_t *pData);
char ReadAmount(uint8_t addr, uint32_t *pData);
char WriteDataBlock(uint8_t addr, uint8_t *pData, uint8_t Len);
char ReadDataBlock(uint8_t addr, uint8_t *pData);
 
#endif

6、main函数

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		if (!EntranceGuard(readUid, NULL))
			 {
				printf("当前卡号:%x-%x-%x-%x\n", readUid[0], readUid[1], readUid[2], readUid[3]);
				if (!strncmp((char *)readUid, (char *)UID, 4))
				{
				  // 插入比对卡号正确时的处理程序,如打开门禁
				  printf("已认证的卡\n");
				}
				else
				{
				  // 插入比对卡号错误时的处理程序
				  printf("未认证卡\n");
				}
			HAL_Delay(2000);
			}
  }

 如果串口一直接收到unkown,需要调一下spi的Prescaler。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值