(一)、RFID-RC522用的是典型的SPI通信,比较方便,我在本篇文章采用的是软件模拟的方式,相比于硬件SPI的优势是方便大家的移植,引脚用普通的引脚即可。
(二)、卡和读卡器之间的通信流程如下:
复位应答(Request)
M1卡的通信协议和通信波特率是定义好的,当有卡片进入读卡器的工作范围时,读卡器要以特定的协议与卡片通信,从而确定卡片的卡型。
防冲突机制(Anticollision Loop)
当有多张卡片进入读写器操作范围时,会从中选择一张卡片进行操作,并返回选中卡片的序列号。
选择卡片(Select Tag)
选择被选中的卡的序列号,并同时返回卡的容量代码。
三次相互确认(3 Pass Authentication)
选定要处理的卡片后,读写器就要确定访问的扇区号,并且对扇区密码进行密码校验。在三次互相认证后就可以通过加密流进行通信。每次在选择扇区的时候都要进行扇区的密码校验。
对数据块的操作
读(Read):读一个块的数据;
写(Write):在一个块中写数据;
加(Increment):对数据块中的数值进行加值;
减(Decrement):对数据块中的数值进行减值;
传输(Transfer):将数据寄存器中的内容写入数据块中;
中止(Halt):暂停卡片的工作;
(三)、核心代码及解释
相关寄存器的定义比较多,网上也有很多,我在这就不再详细的叙述了。
1、相关引脚初始化
void RC522_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE); //使能PF端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_0|GPIO_Pin_2|GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOF, &GPIO_InitStructure);
GPIO_SetBits(GPIOF,GPIO_Pin_1|GPIO_Pin_0|GPIO_Pin_2|GPIO_Pin_4); //拉高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOF, &GPIO_InitStructure);
}
在RC522中,IRQ 不需要接,MISO引脚与单片机相连的引脚配置成上拉输入或者浮空输入都可,其他除了VCC ,GND引脚都配置成推挽输出。
2、寻卡
char PcdRequest(unsigned char req_code,unsigned char *pTagType)
{
char status;
unsigned int unLen;
unsigned char ucComMF522Buf[MAXRLEN];
ClearBitMask(Status2Reg,0x08);
WriteRawRC(BitFramingReg,0x07);
SetBitMask(TxControlReg,0x03);
ucComMF522Buf[0] = req_code;
status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,1,ucComMF522Buf,&unLen);
if ((status == MI_OK) && (unLen == 0x10))
{
*pTagType = ucComMF522Buf[0];
*(pTagType+1) = ucComMF522Buf[1];
}
else
{ status = MI_ERR;
}
return status;
}
其中 PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,1,ucComMF522Buf,&unLen);是M1卡与RFID-RC522进行通信的函数,后面会介绍。
ucComMF522Buf数组里面保存的是卡片的类型,相关类型对应如下:
// 0x4400 = Mifare_UltraLight
// 0x0400 = Mifare_One(S50)
// 0x0200 = Mifare_One(S70)
// 0x0800 = Mifare_Pro(X)
// 0x4403 = Mifare_DESFire
3、防冲撞
char PcdAnticoll(unsigned char *pSnr)
{
char status;
unsigned char i,snr_check=0;
unsigned int unLen;
unsigned char ucComMF522Buf[MAXRLEN];
ClearBitMask(Status2Reg,0x08);
WriteRawRC(BitFramingReg,0x00);
ClearBitMask(CollReg,0x80);
ucComMF522Buf[0] = PICC_ANTICOLL1;
ucComMF522Buf[1] = 0x20;
status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,2,ucComMF522Buf,&unLen);
if (status == MI_OK)
{
for (i=0; i<4; i++)
{
*(pSnr+i) = ucComMF522Buf[i]; //读出卡片的UID
snr_check ^= ucComMF522Buf[i];
}
if (snr_check != ucComMF522Buf[i])
{ status = MI_ERR; }
}
SetBitMask(CollReg,0x80);
return status;
}
以上代码中具有将卡片防冲突命令通过RC522传到卡片中,返回的是被选中卡片的32位的卡号
4、选择卡片
char PcdSelect(unsigned char *pSnr)
{
char status;
unsigned char i;
unsigned int unLen;
unsigned char ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0] = PICC_ANTICOLL1;
ucComMF522Buf[1] = 0x70;
ucComMF522Buf[6] = 0;
for (i=0; i<4; i++)
{
ucComMF522Buf[i+2] = *(pSnr+i);
ucComMF522Buf[6] ^= *(pSnr+i);
}
CalulateCRC(ucComMF522Buf,7,&ucComMF522Buf[7]);
ClearBitMask(Status2Reg,0x08);
status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,9,ucComMF522Buf,&unLen);
if ((status == MI_OK) && (unLen == 0x18))
{ status = MI_OK; }
else
{ status = MI_ERR; }
return status;
}
参数传入的是32位的卡号
5、计算CRC
//用MF522计算CRC16函数
void CalulateCRC(unsigned char *pIndata,unsigned char len,unsigned char *pOutData)
{
unsigned char i,n;
ClearBitMask(DivIrqReg,0x04);
WriteRawRC(CommandReg,PCD_IDLE);
SetBitMask(FIFOLevelReg,0x80);
for (i=0; i<len; i++)
{ WriteRawRC(FIFODataReg, *(pIndata+i)); }
WriteRawRC(CommandReg, PCD_CALCCRC);
i = 0xFF;
do
{
n = ReadRawRC(DivIrqReg);
i--;
}
while ((i!=0) && !(n&0x04));
pOutData[0] = ReadRawRC(CRCResultReg
pOutData[1] = ReadRawRC(CRCResultRegM);
}
大家有没有发现以上代码大部分都有用到
unsigned char ReadRawRC(unsigned char Address);
void WriteRawRC(unsigned char Address,unsigned char value);
这2个函数呢,分别是读写相应寄存器,一个方法就是也是用到这2个函数,但是它还调用了底层的SPI读写一个字节;还有一个方法就是直接在这2个函数里面编写读写SPI的函数,我在这里就采用这个方法一步到位了比较简单:代码如下:/
//功 能:读RC632寄存器
//参数说明:Address[IN]:寄存器地址
//返 回:读出的值
/
unsigned char ReadRawRC(unsigned char Address)
{
unsigned char i, ucAddr;
unsigned char ucResult=0;
MF522_SCK = 0;
MF522_NSS = 0;
ucAddr = ((Address<<1)&0x7E)|0x80;
for(i=8;i>0;i--)
{
MF522_SI = ((ucAddr&0x80)==0x80);
MF522_SCK = 1;
ucAddr <<= 1;
MF522_SCK = 0;
delay_us(10); //STM32需要多加的延时时间。51的不需要加
}
for(i=8;i>0;i--)
{
MF522_SCK = 1;
ucResult <<= 1;
ucResult|=MF522_SO;
MF522_SCK = 0;
delay_us(10); //STM32需要多加的延时时间。51的不需要加
}
MF522_NSS = 1;
MF522_SCK = 1;
return ucResult;
}
/
//功 能:写RC632寄存器
//参数说明:Address[IN]:寄存器地址
// value[IN]:写入的值
/
void WriteRawRC(unsigned char Address, unsigned char value)
{
unsigned char i, ucAddr;
MF522_SCK = 0;
MF522_NSS = 0;
ucAddr = ((Address<<1)&0x7E);
for(i=8;i>0;i--)
{
MF522_SI = ((ucAddr&0x80)==0x80);
MF522_SCK = 1;
ucAddr <<= 1;
MF522_SCK = 0;
delay_us(10); //STM32需要多加的延时时间。51的不需要加
}
for(i=8;i>0;i--)
{
MF522_SI = ((value&0x80)==0x80);
MF522_SCK = 1;
value <<= 1;
MF522_SCK = 0;
delay_us(10); //STM32需要多加的延时时间。51的不需要加
}
MF522_NSS = 1;
MF522_SCK = 1;
}
以上就是本次的全部介绍,欢迎各位大佬和我一起交流!