目录
一、RFID
射频识别,即RFID又称无线射频识别,由读写器和射频标签(应答器)
组成。
RC522是一种非接触式读写卡芯片,采用SPI协议,一般用于门禁卡、公交卡充值消费。
1.1 UID
在IC芯片当中(集成电路芯片中),UID即唯一识别码。在芯片制造过程中被写入芯片内部、全球唯一的数字编码,就像芯片的身份证,用于区分不同的芯片个体。通常由一串数字或数字与字母组合而成的代码,长度和格式根据芯片制造商以及具体应用场景有所不同。
1.2 IC卡的内存管理
IC卡中有1kB的存储空间,并且将存储空间分为了16个扇区,每个扇区分为4块,每块16个字节,以块为存储单位。
1kB -> 1024B ->16个扇区
1个扇区->64B->4个块
1个块->16B
将16个扇区的64个块按绝对地址编号为0x00-0x3F(0-63)
0-4B是卡片的序列号,第5字节是序列号的校验码,第6字节是卡片的容量"SIZE"字节,第7-8字节为卡片的类型号字节,其他字节由厂商另加定义。
每个扇区的块3为控制块,包括了密码A,存取控制,密码B ,空卡默认读写权限如下:
在空卡状态下,每个扇区的尾块数据(16进制)为:0x00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF
存取控制字节:
1.3 存取控制
要想对数据块进行操作,首先要看该数据块的控制位是否允许对数据块操作。如果允许操作,再看需要验证什么密码,只有验证密码正确后才可以对该数据执行相应操作。
根据上面的权限:块三:001
密码 A: 不可读,但通过密码 A 校验后,可改写密码 A。权限字节以及密码
B 的读写权限均可以使⽤密码 A 读写。
由上表⼜可以得到:密码 A 永远不可以被读,所以⼀旦设置必须记住。
1.4 IC与读卡器的通信
复位应答:复位RC522,开始检测靠近的卡片
防冲突机制:当有多张卡片进入读写器操作范围时,会从中选择一张卡片进行操作,并返回选中卡片的序列号。
三次相互确认:选定要处理的卡片后,读写器就要确定访问的扇区号,并且对扇区密码进行密码校验。在三次互相认证后就可以通过加密流进行通信。每次在选择扇区的时候都要进行扇区的密码校验。
对数据块的操作:
读:读一个块的数据
写:在一个块写
加:对数据块中的数值进行加值
减:对数据块中的数值进行减值
中止:暂停卡片的工作
二、功能介绍
使用RC522模块完成对IC卡卡号读取、卡类型区分、IC卡扇区密码修改、扇区数据读写等功能。
底层采用SPI模拟时序。并用串口打印对应信息。
三、实现
3.1 RC522射频模块外部接口
SDA <----->PA4--片选脚 -->后续用CS来表示 SCK <----->PA5--时钟线 MOSI<----->PA7--输出 MISO<----->PA6--输入
GND <----->GND RST <----->PB0--复位脚 VCC <----->VCC -->3.8v
#define MFRC522_GPIO_CS_PORT GPIOA
#define MFRC522_GPIO_CS_PIN GPIO_Pin_4
#define MFRC522_GPIO_SCK_PORT GPIOA
#define MFRC522_GPIO_SCK_PIN GPIO_Pin_5
#define MFRC522_GPIO_MOSI_PORT GPIOA
#define MFRC522_GPIO_MOSI_PIN GPIO_Pin_7
#define MFRC522_GPIO_MISO_PORT GPIOA
#define MFRC522_GPIO_MISO_PIN GPIO_Pin_6
#define MFRC522_GPIO_RST_PORT GPIOB
#define MFRC522_GPIO_RST_PIN GPIO_Pin_0
对应相应的接口拉高拉低
低电平有效,高电平空闲
//片选
#define RC522_CS_Enable() GPIO_ResetBits ( MFRC522_GPIO_CS_PORT, MFRC522_GPIO_CS_PIN )
#define RC522_CS_Disable() GPIO_SetBits ( MFRC522_GPIO_CS_PORT, MFRC522_GPIO_CS_PIN )
//复位
#define RC522_Reset_Enable() GPIO_ResetBits( MFRC522_GPIO_RST_PORT, MFRC522_GPIO_RST_PIN )
#define RC522_Reset_Disable() GPIO_SetBits ( MFRC522_GPIO_RST_PORT, MFRC522_GPIO_RST_PIN )
//时钟
#define RC522_SCK_0() GPIO_ResetBits( MFRC522_GPIO_SCK_PORT, MFRC522_GPIO_SCK_PIN )
#define RC522_SCK_1() GPIO_SetBits ( MFRC522_GPIO_SCK_PORT, MFRC522_GPIO_SCK_PIN )
//MOSI
#define RC522_MOSI_0() GPIO_ResetBits( MFRC522_GPIO_MOSI_PORT, MFRC522_GPIO_MOSI_PIN )
#define RC522_MOSI_1() GPIO_SetBits ( MFRC522_GPIO_MOSI_PORT, MFRC522_GPIO_MOSI_PIN )
//MISO
#define RC522_MISO_GET() GPIO_ReadInputDataBit ( MFRC522_GPIO_MISO_PORT, MFRC522_GPIO_MISO_PIN )
3.2 初始化
时钟:因为外部接口用到了GPIOA和GPIOB,所以需要配置对应的时钟
SPI:RC522模块通常作为从机,stm32作为主机。
时钟SCK:由主机产生。
MISO:MISO(Master In Slave Out)引脚通常配置为浮空输入模式,这是因为MISO引脚仅在从设备需要向主设备发送数据时才有效,而在其他时间应该保持高阻抗状态,以避免干扰总线。
片选:选择某个从机,低电平有效
其余配置为推挽输出
3.2.1 引脚初始化
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能时钟 */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB, ENABLE);
/* 配置 SPI_RC522_SPI 引脚:CS(SDA) */
GPIO_InitStructure.GPIO_Pin = MFRC522_GPIO_CS_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_Init(MFRC522_GPIO_CS_PORT, &GPIO_InitStructure);
/* 配置 SPI_RC522_SPI 引脚:SCK */
GPIO_InitStructure.GPIO_Pin = MFRC522_GPIO_SCK_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_Init(MFRC522_GPIO_SCK_PORT, &GPIO_InitStructure);
/* 配置 SPI_RC522_SPI 引脚:MOSI */
GPIO_InitStructure.GPIO_Pin = MFRC522_GPIO_MOSI_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_Init(MFRC522_GPIO_MOSI_PORT, &GPIO_InitStructure);
/* 配置 SPI_RC522_SPI 引脚:MISO */
GPIO_InitStructure.GPIO_Pin = MFRC522_GPIO_MISO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_Init(MFRC522_GPIO_MISO_PORT, &GPIO_InitStructure);
/* 配置 SPI_RC522_SPI 引脚:RST */
GPIO_InitStructure.GPIO_Pin = MFRC522_GPIO_RST_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_Init(MFRC522_GPIO_RST_PORT, &GPIO_InitStructure);
}
3.2.2 初始化
-
各引脚的初始化
-
复位失能
-
片选失能
-
复位
-
设置工作模式
void MFRC522_Init ( void )
{
SPI1_Init();
RC522_Reset_Disable();
RC522_CS_Disable();
RC522_Reset ();
RC522_Config_Type ( 'A' );//设置工作方式
}
3.3 发送接收信息
通过软件模拟SPI与RC522通信,SPI发送接收字节的代码如下(低位先行):
发送
void RC522_SPI_SendByte( uint8_t byte )
{
uint8_t n;
for( n=0;n<8;n++ )
{
if( byte&0x80 )
RC522_MOSI_1();//发送1
else
RC522_MOSI_0();//发送0
Delay_us();
RC522_SCK_0();//拉低,时钟开始工作
Delay_us();
RC522_SCK_1();//拉高,数据发送完毕
Delay_us();
byte<<=1;//一位数据已发送成功,移位
}
}
接收
uint8_t RC522_SPI_ReadByte( void )
{
uint8_t n,data;
for( n=0;n<8;n++ )
{
data<<=1;
RC522_SCK_0();//拉低开始接收数据
Delay_us();
if( RC522_MISO_GET()==1 )
data|=0x01;//获取数据位为1
Delay_us();
RC522_SCK_1();//拉高时钟,结束这一位数据的接收
Delay_us();
}
return data;
}
3.4 对RC522寄存器的操作
stm32是向RC522寄存器操作来驱动RC522,所以会存在以下几种基本操作:
3.4.1 读取RC522指定寄存器的值
/** * @brief :读取RC522指定寄存器的值 * @param :Address:寄存器的地址 * @retval :寄存器的值 */
uint8_t RC522_Read_Register( uint8_t Address )
{
uint8_t data,Addr;
Addr = ( (Address<<1)&0x7E )|0x80;
RC522_CS_Enable();
RC522_SPI_SendByte( Addr );
data = RC522_SPI_ReadByte();//读取寄存器中的值
RC522_CS_Disable();
return data;
}
3.4.2 向RC522指定寄存器中写入指定的数据
/** * @brief :向RC522指定寄存器中写入指定的数据 * @param :Address:寄存器地址 data:要写入寄存器的数据 * @retval :无 */
void RC522_Write_Register( uint8_t Address, uint8_t data )
{
uint8_t Addr;
Addr = ( Address<<1 )&0x7E;