🎀 文章作者:二土电子
🌸 关注公众号获取完整程序工程!
🐸 期待大家一起学习交流!
文章目录
一、RFID是什么
首先我们先来简单了解一下什么是RFID,射频识别技术(Radio Frequency Identification, RFID)是一种利用无线电波实现非接触式自动识别的技术。RFID系统通过无线电波与电子标签(Tag)进行通信,从而完成对目标对象的识别和数据交换。一个完整的RFID系统由三部分组成,分别是标签(ID卡)、读写器、天线。像我们生活中常见的校饭卡水卡这种都是RFID技术的实际应用。
插播一段笔者的小理解,学生时代,我们偶尔会听说某某某同学自己能充饭卡水卡,首先这肯定是可以的,但是了解了ID卡之后可能或更加清楚为什么,因为ID卡通常有三个分区,每一个分区都会有对应的密码,密码对了才可以对某一个分区进行读写,密码肯定是有手段可以破解的,所以可以做到自己充饭卡水卡,但是一般这种用到RFID技术做支付使用的场景时会有专人进行资金核对,还是建议大家不要做这种事情。
关于其他RFID的更加细节的内容大家可以自行搜索一下,这不是本文重点,这里就只是有一个简单的认识和理解,就不再做赘述了。
二、MF RC522模块
我们这里使用MF RC522模块来做读写器,搭配IC卡来组成一个RFID系统,学习一下RFID的使用方法。
MF RC522是高度集成的非接触式(13.56MHz)读写卡芯片。此发送模块利用调制和解调的原理,并将它们完全集成到各种非接触式通信方法和协议中(13.56MHz)。MF RC522支持SPI通信和IIC通信模式。
MF RC522的操作由可执行一系列命令的内部状态机来决定。通过向命令寄存器写入相应的命令代码来启动命令。关于MF RC522模块更多细节可以到MFRC522资料手册中查看,有需要的小伙伴可以私信获取。
三、程序设计
3.1 SPI程序设计
首先要实现和模块的通信,SPI通信必备的一些函数基本都是一样的,只要注意模块SPI模式即可,关于SPI四种模式的内容可以移步STM32速成笔记专栏查看,里面有对SPI的详细介绍。
我们这里使用的是软件模拟SPI,引脚分别是
SPI引脚 | 对应IO |
---|---|
CS | PA4 |
SCK | PA5 |
MOSI | PA7 |
MISO | PA6 |
3.1.1 SPI读/写字节
RC522的SPI空闲状态下时钟线是低电平。
/*
*==============================================================================
*函数名称:Med_Rc522_WriteByte
*函数功能:RC522写入数据
*输入参数:data:写入的数据
*返回值:无
*备 注:无
*==============================================================================
*/
void Med_Rc522_WriteByte (u8 data)
{
u8 tempVar = 0;
for (tempVar = 0;tempVar < 8;tempVar ++)
{
if (data & 0x80)
RC522_MOSI = 1;
else
RC522_MOSI = 0;
delay_us(200);
RC522_SCLK = 0;
delay_us(200);
RC522_SCLK = 1;
delay_us(200);
data <<= 1;
}
}
/*
*==============================================================================
*函数名称:Med_Rc522_ReadByte
*函数功能:RC522读取数据
*输入参数:data:写入的数据
*返回值:读取到的数据
*备 注:无
*==============================================================================
*/
u8 Med_Rc522_ReadByte (void)
{
u8 tempVar = 0;
u8 data = 0;
for (tempVar = 0;tempVar < 8;tempVar ++)
{
data <<= 1;
RC522_SCLK = 0;
delay_us(200);
if (RC522_MISO == 1)
data |= 0x01;
delay_us(200);
RC522_SCLK = 1;
delay_us(200);
}
return data;
}
3.1.2 SPI读/写寄存器
/*
*==============================================================================
*函数名称:Med_Rc522_Write_Reg
*函数功能:RC522写入寄存器
*输入参数:addr:寄存器地址;data:数据
*返回值:无
*备 注:无
*==============================================================================
*/
void Med_Rc522_Write_Reg (u8 addr,u8 data)
{
addr = ( addr << 1 ) & 0x7E;
RC522_CS = 0; // 使能片选
Med_Rc522_WriteByte(addr); // 发送地址
Med_Rc522_WriteByte(data); // 发送数据
RC522_CS = 1; // 取消片选
}
/*
*==============================================================================
*函数名称:Med_Rc522_Read_Reg
*函数功能:RC522读取寄存器
*输入参数:addr:寄存器地址;
*返回值:读取到的数据
*备 注:无
*==============================================================================
*/
u8 Med_Rc522_Read_Reg (u8 addr)
{
u8 data = 0;
addr = ((addr << 1) & 0x7E) | 0x80;
RC522_CS = 0; // 使能片选
Med_Rc522_WriteByte(addr); // 发送地址
data = Med_Rc522_ReadByte(); // 读取数据
RC522_CS = 1; // 取消片选
return data;
}
3.2 RC522功能函数
下面是一些RC522的功能函数实现
3.2.1 RC522开启天线
void Med_Rc522_OpenAntenna (void)
{
u8 tempVar = 0;
tempVar = Med_Rc522_Read_Reg(TxControlReg);
if (!(tempVar & 0x30))
Med_Rc522_SetBit(TxControlReg,0x30);
}
3.2.2 RC522工作类型设置
/*
*==============================================================================
*函数名称:Med_Rc522_WorkType
*函数功能:RC522工作类型设置
*输入参数:无
*返回值:无
*备 注:无
*==============================================================================
*/
void Med_Rc522_WorkType (u8 type)
{
if (type == 'A')
{
Med_Rc522_ClearBit(Status2Reg,0x80);
Med_Rc522_Write_Reg(ModeReg,0x3D);
Med_Rc522_Write_Reg(RxSelReg,0x86);
Med_Rc522_Write_Reg(RFCfgReg,0x7F);
Med_Rc522_Write_Reg(TReloadRegL,0x1E);
Med_Rc522_Write_Reg(TReloadRegH,0x00);
Med_Rc522_Write_Reg(TModeReg,0x8D);
Med_Rc522_Write_Reg(TPrescalerReg,0x3E);
delay_us(2);
Med_Rc522_OpenAntenna();
}
}
3.2.3 RC522寻卡
/*
*==============================================================================
*函数名称:Med_Rc522_FindCards
*函数功能:RC522寻卡
*输入参数:method:寻卡方式;cardType:卡片类型
*返回值:MI_OK,成功
*备 注:无
*==============================================================================
*/
u8 Med_Rc522_FindCards (u8 method,u8 *cardType)
{
char cStatus;
u8 ucComMF522Buf [ MAXRLEN ];
u32 ulLen;
Med_Rc522_ClearBit(Status2Reg,0x08);
Med_Rc522_Write_Reg(BitFramingReg,0x07);
Med_Rc522_SetBit(TxControlReg,0x03);
ucComMF522Buf[0] = method; // 存入卡片命令字
// 寻卡
cStatus = Med_Rc522_CommWithCard(PCD_TRANSCEIVE,ucComMF522Buf,1,ucComMF522Buf,& ulLen );
if ( (cStatus == MI_OK) && (ulLen == 0x10)) //寻卡成功返回卡类型
{
*cardType = ucComMF522Buf [0];
*(cardType + 1) = ucComMF522Buf[1];
}
else
cStatus = MI_ERR;
return cStatus;
}
3.2.4 RC522防冲撞
/*
*==============================================================================
*函数名称:Med_Rc522_Anticoll
*函数功能:RC522防冲撞
*输入参数:*pSnr:四字节卡片序列号
*返回值:MI_OK,成功
*备 注:无
*==============================================================================
*/
u8 Med_Rc522_Anticoll (u8 *pSnr)
{
char cStatus;
u8 uc, ucSnr_check = 0;
u8 ucComMF522Buf [MAXRLEN];
u32 ulLen;
Med_Rc522_ClearBit(Status2Reg,0x08); // 清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
Med_Rc522_Write_Reg(BitFramingReg,0x00); // 清理寄存器 停止收发
Med_Rc522_ClearBit(CollReg,0x80); // 清ValuesAfterColl所有接收的位在冲突后被清除
ucComMF522Buf[0] = 0x93; // 卡片防冲突命令
ucComMF522Buf[1] = 0x20;
// 与卡片通信
cStatus = Med_Rc522_CommWithCard(PCD_TRANSCEIVE,ucComMF522Buf,2,ucComMF522Buf,&ulLen);
if (cStatus == MI_OK) // 通信成功
{
for (uc = 0;uc < 4;uc ++)
{
*(pSnr + uc) = ucComMF522Buf[uc]; // 读出UID
ucSnr_check ^= ucComMF522Buf[uc];
}
if (ucSnr_check != ucComMF522Buf[uc])
cStatus = MI_ERR;
}
Med_Rc522_SetBit (CollReg, 0x80);
return cStatus;
}
3.3 获取卡片ID并通过串口输出
最后我们编写一个业务函数用于在主函数的轮询中运行,寻卡成功后会输出卡片ID。
/*
*==============================================================================
*函数名称:App_Rc522_GetId
*函数功能:RC522获取卡片ID并通过串口输出
*输入参数:无
*返回值:无
*备 注:无
*==============================================================================
*/
void App_Rc522_GetId (void)
{
char cStr [ 30 ];
u8 ucArray_ID [ 4 ]; // 先后存放IC卡的类型和UID(IC卡序列号)
u8 ucStatusReturn; // 返回状态
static u8 ucLineCount = 0;
// 寻卡
if ((ucStatusReturn = Med_Rc522_FindCards(PICC_REQALL,ucArray_ID)) != MI_OK)
{
// 失败再次寻卡
ucStatusReturn = Med_Rc522_FindCards(PICC_REQALL,ucArray_ID);
}
if (ucStatusReturn == MI_OK)
{
// 防冲撞(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
if (Med_Rc522_Anticoll(ucArray_ID) == MI_OK)
{
sprintf(cStr,"The Card ID is: %02X%02X%02X%02X",ucArray_ID[0],ucArray_ID[1],ucArray_ID[2],ucArray_ID[3]);
printf("%s\r\n",cStr);
if(ucLineCount == 0)
ucLineCount ++;
if(ucLineCount == 17)
ucLineCount = 0;
}
}
}