MFRC522 RFID 模块
连接树莓派读取卡片信息。本文涉及到MFRC522的驱动程序。
0x00 预备知识
MifareS50卡相关数据参数
C语言基础
树莓派
英文缩写
重点记住 PICC 是卡片的意思
PCD 是读卡器的意思
后面有完整的缩写列表
0x01 连接 MFRC522 和树莓派
rc522 RFID模块
GPIO 接法
GPIO | RC522接口 |
---|---|
1 | 3.3v |
6 | GND |
19 | MOSI |
21 | MISO |
22 | RST |
23 | SCK |
24 | SDA |
GPIO 图
接好后是这样子的 IRQ 不连
0x02 驱动文件(向 rc522 寄存器写命令,读取相关寄存器的值)和上层函数(选卡、防冲突、认证、读写)
mfrc522.c
驱动封装,包含基本函数和上层函数
void writeMFRC522(unsigned char Address, unsigned char value);
void antennaOn(void);
//选卡函数
unsigned char selectCard(unsigned char *pSnr);
//读数据块函数,要提供块地址,和一个容器
unsigned char read(unsigned char blockAddr,char* strData);
//写数据块函数,要提供块地址和要写入的数据,注意,数组中每个元素是数据的 16 进制数。
//例如,strData[3] = 0x66,当然你愿意转化成十进制也可以 strData[3] = 102。反正就是一个数多少进制无所谓。
unsigned char write(unsigned char blockAddr, unsigned char *writeData);
//认证函数,要提供认证模式(用 keyA 还是 keyB 认证),要认证的块地址,密钥,uid
unsigned char auth(unsigned char auth_mode,unsigned char addr,unsigned char *pKey,unsigned char *pSnr);
void RFID_init()// rc522 射频读卡器的初始化,不深究了
{
bcm2835_spi_begin();
bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST);
bcm2835_spi_setDataMode(BCM2835_SPI_MODE0);
bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_65536);
bcm2835_spi_chipSelect(BCM2835_SPI_CS0);
bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW);
writeMFRC522(CommandReg, PCD_RESETPHASE);
writeMFRC522(TModeReg, 0x8D);
writeMFRC522(TPrescalerReg, 0x3E);
writeMFRC522(TReloadRegL, 30);
writeMFRC522(TReloadRegH, 0);
writeMFRC522(TxAutoReg, 0x40);
writeMFRC522(ModeReg, 0x3D);
antennaOn();
}
//向 rc522 的寄存器中写数(可以是命令码等)
//接受寄存器的地址和要写入的值
void writeMFRC522(unsigned char Address, unsigned char value)
{
char buff[2];
buff[0] = (char)((Address<<1)&0x7E);
buff[1] = (char)value;
bcm2835_spi_transfern(buff,2);
}
//读取rc522 的寄存器中的数
//接受要读的数据块的地址
unsigned char readMFRC522(unsigned char Address)
{
char buff[2];
buff[0] = ((Address<<1)&0x7E)|0x80;
bcm2835_spi_transfern(buff,2);
return (unsigned char)buff[1];
}
void setBitMask(unsigned char reg, unsigned char mask)
{
unsigned char tmp;
tmp = readMFRC522(reg);
writeMFRC522(reg, tmp | mask);
}
void clearBitMask(unsigned char reg, unsigned char mask)
{
unsigned char tmp;
tmp = readMFRC522(reg);
writeMFRC522(reg, tmp & (~mask));
}
void antennaOn(void)//打开天线
{
unsigned char temp;
temp = readMFRC522(TxControlReg);
if(!(temp & 0x03))
{
setBitMask(TxControlReg, 0x03);
}
}
void antennaOff(void)//关闭天线
{
unsigned char temp;
temp = readMFRC522(TxControlReg);
if(!(temp & 0x03))
{
clearBitMask(TxControlReg, 0x03);
}
}
//计算 CRC, CRC 占两个字节
//要计算的序列。长度。输出 CRC 的位置,一般就是追加在原数组后面
void calculateCRC(unsigned char *pIndata, unsigned char len, unsigned char *pOutData)
{
unsigned char i, n;
clearBitMask(DivIrqReg, 0x04);
setBitMask(FIFOLevelReg, 0x80);
for (i=0; i<len;i++)
{
writeMFRC522(FIFODataReg, *(pIndata+i));
}
writeMFRC522(CommandReg, PCD_CALCCRC);
i = 0xFF;
do
{
n = readMFRC522(DivIrqReg);
i--;
}
while ((i!=0) && !(n&0x04));
pOutData[0] = readMFRC522(CRCResultRegL);//CRC占两个字节
pOutData[1] = readMFRC522(CRCResultRegM);//CRC占两个字节!
}
//这个函数比较关键,是MFRC522对卡片的操作,认证读取写入等都通过它实现。
//本质是操作 FIFO(First In First Out) 寄存器。
//要发送给卡片的命令码。要发送给 FIFO 的内容,是一个特定数据结构的 char 数组,
//比如写入就是 写入的命令码 + 16 字节数据 + 2 字节 CRC。
//发送的字节数,一个 char 一个字节。返回给的 FIFO 的数据,需要一个容器。返回数据的**位**数(8 位一字节)
unsigned char MFRC522ToCard(unsigned char command, unsigned char *sendData, unsigned char sendLen, unsigned char *backData, unsigned int *backLen)
{
unsigned char status = MI_ERR;
unsigned char irqEn = 0x00;
unsigned char waitIRq = 0x00;
unsigned char lastBits;
unsigned char n;
unsigned int i;
switch(command)
{
case PCD_AUTHENT: //认证卡密
{
irqEn = 0x12;
waitIRq = 0x10;
break;
}
case PCD_TRANSCEIVE: //发送FIFO中数据
{
irqEn = 0x77;
waitIRq = 0x30;
break;
}
default:
break;
}
writeMFRC522(CommIEnReg, irqEn|0x80); //允许中断请求
clearBitMask(CommIrqReg, 0x80); //清除所有中断请求位
setBitMask(FIFOLevelReg, 0x80); //FlushBuffer=1, FIFO初始化
writeMFRC522(CommandReg, PCD_IDLE); //无动作,取消当前命令
//向FIFO中写入数据
for (i=0; i<sendLen;i++)
{
writeMFRC522(FIFODataReg, sendData[i]);
}
//执行命令
writeMFRC522(CommandReg, command);
if(command == PCD_TRANSCEIVE)
setBitMask(BitFramingReg, 0x80);
//等待接收数据完成
i = 2000; //i根据时钟频率调整,操作M1卡最大等待时间25ms
do
{
n = readMFRC522(CommIrqReg);
i--;
}
while ((i!=0) && !(n&0x01) && !(n&waitIRq));
clearBitMask(BitFramingReg, 0x80);
if(i != 0)
{
if(!(readMFRC522(ErrorReg) & 0x1B))
{
status = MI_OK;
if(n & irqEn & 0x01)
status = MI_NOTAGERR;
if(command == PCD_TRANSCEIVE)
{
n = readMFRC522(FIFOLevelReg);
lastBits = readMFRC522(ControlReg) & 0x07;
if(lastBits)
*backLen = (n-1)*8 + lastBits;
else
*backLen = n*8;
if(n == 0)
n = 1;
if(n > MAX_LEN)
n = MAX_LEN;
//读取FIFO中接收到的数据
for (i=0; i<n;i++)
backData[i] = readMFRC522(FIFODataReg);
}
}
else
status = MI_ERR;
}
return status;
}
//寻卡函数。
//寻卡模式,0x52 是寻天线范围内所有卡。TagType用来存放卡片类型,如果寻到则放入到容器。
unsigned char findCard(unsigned char reqMode, unsigned char *TagType)
{
unsigned char status;
unsigned int backBits;
writeMFRC522(BitFramingReg, 0x07);
TagType[0] = reqMode;
status = MFRC522ToCard(PCD_TRANSCEIVE, TagType, 1, TagType, &backBits);
if((status != MI_OK) || (backBits != 0x10))
status = MI_ERR;
return status;
}
//防冲撞函数,这个过程可以获取到卡片 uid 并存入容器
unsigned char anticoll(unsigned char *serNum)
{
unsigned char status;
unsigned char i;
unsigned char serNumCheck=0;
unsigned int unLen;
clearBitMask(Status2Reg, 0x08);
clearBitMask(CollReg,0x80);
writeMFRC522(BitFramingReg, 0x00);
serNum[0] = PICC_ANTICOLL;
serNum[1] = 0x20;
status = MFRC522ToCard(PCD_TRANSCEIVE, serNum, 2, serNum, &unLen);
if(status == MI_OK)
{
//校验卡序列号
for (i=0; i<4; i++)
{
*(serNum+i) = serNum[i];
serNumCheck ^= serNum[i];
}
if(serNumCheck != serNum[i])
{
status = MI_ERR;
}
}
setBitMask(CollReg, 0x80);
return status;
}
//让卡片休眠
void RFID_halt()
{
unsigned char status;
unsigned int unLen;
unsigned char buff[4];
buff[0] = PICC_HALT;
buff[1] = 0;
calculateCRC(buff, 2, &buff[2]);
status = MFRC522ToCard(PCD_TRANSCEIVE, buff, 4, buff,&unLen);
}
/***************************上层建筑*****************************************/
//写块函数,块地址0-63,要写的数据的数组指针为函数
unsigned char write(unsigned char blockAddr, unsigned char *writeData)
{
unsigned char status;
unsigned int recvBits;
unsigned char i;
unsigned char buff[18];
buff[0] = PICC_WRITE;
buff[1] = blockAddr;//块地址0-63
calculateCRC(buff, 2, &buff[2]);
/*发送指令*/
status = MFRC522ToCard(PCD_TRANSCEIVE, buff, 4, buff, &recvBits);
/*这里判断返回状态*/
if ((status != MI_OK) || (recvBits != 4) || ((buff[0] & 0x0F) != 0x0A))
status = MI_ERR;
/*准备16byte数据*/
if (status == MI_OK){
for (i=0; i<16; i++)
buff[i] = *(writeData+i);
/*计算校验位*/
calculateCRC(buff, 16, &buff[16]);
/*发送数据*/
status = MFRC522ToCard(PCD_TRANSCEIVE, buff, 18, buff, &recvBits);
if ((status != MI_OK) || (recvBits != 4) || ((buff[0] & 0x0F) != 0x0A))
status = MI_ERR;
}
return status;
}
//读块函数
//块地址0-63,信息被读取后存放到的数组的指针为参数
unsigned char read(unsigned char blockAddr,char* strData) {
unsigned char status;
unsigned int recvBits;
unsigned char i;
unsigned char buff[18];
buff[0]=PICC_READ;
buff[1]=blockAddr;
calculateCRC(buff,2,&buff[2]);
status=MFRC522ToCard(PCD_TRANSCEIVE,buff,4,buff,&recvBits);
if ((status != MI_OK))// || (recvBits!=0x90))
//是否返回18字节数据
// 0x90转化为10进制是144,144/8=18
status=MI_ERR;
if(status==MI_OK){
for(i=0;i<16;i++)
*(strData+i)=buff[i];
}
return status;
}
//选卡
//uid为参数
unsigned char selectCard(unsigned char *pSnr)
{
unsigned char status;
unsigned char i;
unsigned int unLen;
unsigned char buff[18];
buff[0] = PICC_ANTICOLL1;
buff[1] = 0x70;
buff[6] = 0;
for (i=0; i<4; i++)
{
buff[i+2] = *(pSnr+i);
buff[6] ^= *(pSnr+i);
}
calculateCRC(buff,7,&buff[7]);
// buff中的数据情况如下图
// |PICC_ANTICOLL1(0x93)|0x70|UID bit|UID bit|UID bit|UID bit|0和每位UID异或的结果|CRC|CRC|
clearBitMask(Status2Reg,0x08);
status = MFRC522ToCard(PCD_TRANSCEIVE,buff,9,buff,&unLen);
if ((status == MI_OK) && (unLen == 0x18))
{ status = MI_OK; }
else
{ status = MI_ERR; }
return status;
}
//认证函数
//要提供认证模式(用 keyA 还是 keyB 认证),要认证的块地址,密钥,uid
unsigned char auth(unsigned char auth_mode,unsigned char addr,unsigned char *pKey,unsigned char *pSnr)
{
unsigned char status;
unsigned int unLen;
unsigned char i,buff[18];
buff[0] = auth_mode;
buff[1] = addr;
for (i=0; i<6; i++)
{
buff[i+2] = *(pKey+i);
}
for (i=0; i<6; i++)
{
buff[i+8] = *(pSnr+i);
}
status = MFRC522ToCard(PCD_AUTHENT,buff,12,buff,&unLen);
if ((status != MI_OK) || (!(readMFRC522(Status2Reg) & 0x08)))
{
status = MI_ERR;
}
return status;
}
RFID.h
发送给 rc522 命令码的定义和 rc522 发送给卡片命令码的定义,每个常量对应一个命令码
#define MAX_LEN 18
//PCD 是读卡器的意思
//PICC 是卡片的意思
//参见预备知识里英文缩写表格
/////////////////////////////////////////////////////////////////////
//MF522命令字 //跟 rc522 通信时要发送的命令码,
//比如想让 rc522 发送 FIFO 里的数据给卡片就要用 PCD_TRANSCEIVE 作为参数。
//怎么给 FIFO 发送数据?好吧,这其实是函数层层调用的关系,我们并不需要特定函数给 FIFO 发送数据,
//一个函数就完成了给 FIFO 发送数据又把 FIFO 中数据发送给卡片的功能。对着相关函数源码强撸吧,少年。
/////////////////////////////////////////////////////////////////////
#define PCD_IDLE 0x00 //取消当前命令
#define PCD_AUTHENT 0x0E //验证密钥
#define PCD_RECEIVE 0x08 //接收数据
#define PCD_TRANSMIT 0x04 //发送数据
#define PCD_TRANSCEIVE 0x0C //发送并接收数据
#define PCD_RESETPHASE 0x0F //复位
#define PCD_CALCCRC 0x03 //CRC计算
/////////////////////////////////////////////////////////////////////
//Mifare_One卡片命令字
//读卡器跟卡片通信时发给卡片的命令码。比如想验证A密钥。
/////////////////////////////////////////////////////////////////////
#define PICC_REQIDL 0x26 //寻天线区内未进入休眠状态
#define PICC_REQALL 0x52 //寻天线区内全部卡
#define PICC_ANTICOLL 0x93
#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_RESTORE 0xC2 //调块数据到缓冲区
#define PICC_TRANSFER 0xB0 //保存缓冲区中数据
#define PICC_HALT 0x50 //休眠
/////////////////////////////////////////////////////////////////////
//MF522 FIFO长度定义
/////////////////////////////////////////////////////////////////////
#define DEF_FIFO_LENGTH 64 //FIFO size=64byte
/////////////////////////////////////////////////////////////////////
//MF522寄存器定义
/////////////////////////////////////////////////////////////////////
// PAGE 0
#define RFU00 0x00
#define CommandReg 0x01
#define CommIEnReg 0x02
#define DivlEnReg 0x03
#define CommIrqReg 0x04
#define DivIrqReg 0x05
#define ErrorReg 0x06
#define Status1Reg 0x07
#define Status2Reg 0x08
#define FIFODataReg 0x09
#define FIFOLevelReg 0x0A
#define WaterLevelReg 0x0B
#define ControlReg 0x0C
#define BitFramingReg 0x0D
#define CollReg 0x0E
#define RFU0F 0x0F
// PAGE 1
#define RFU10 0x10
#define ModeReg 0x11
#define TxModeReg 0x12
#define RxModeReg 0x13
#define TxControlReg 0x14
#define TxAutoReg 0x15
#define TxSelReg 0x16
#define RxSelReg 0x17
#define RxThresholdReg 0x18
#define DemodReg 0x19
#define RFU1A 0x1A
#define RFU1B 0x1B
#define MifareReg 0x1C
#define RFU1D 0x1D
#define RFU1E 0x1E
#define SerialSpeedReg 0x1F
// PAGE 2
#define RFU20 0x20
#define CRCResultRegM 0x21
#define CRCResultRegL 0x22
#define RFU23 0x23
#define ModWidthReg 0x24
#define RFU25 0x25
#define RFCfgReg 0x26
#define GsNReg 0x27
#define CWGsCfgReg 0x28
#define ModGsCfgReg 0x29
#define TModeReg 0x2A
#define TPrescalerReg 0x2B
#define TReloadRegH 0x2C
#define TReloadRegL 0x2D
#define TCounterValueRegH 0x2E
#define TCounterValueRegL 0x2F
// PAGE 3
#define RFU30 0x30
#define TestSel1Reg 0x31
#define TestSel2Reg 0x32
#define TestPinEnReg 0x33
#define TestPinValueReg 0x34
#define TestBusReg 0x35
#define AutoTestReg 0x36
#define VersionReg 0x37
#define AnalogTestReg 0x38
#define TestDAC1Reg 0x39
#define TestDAC2Reg 0x3A
#define TestADCReg 0x3B
#define RFU3C 0x3C
#define RFU3D 0x3D
#define RFU3E 0x3E
#define RFU3F 0x3F
/////////////////////////////////////////////////////////////////////
//和MF522通讯时返回的错误代码
/////////////////////////////////////////////////////////////////////
#define MI_OK 0
#define MI_NOTAGERR (-1)
#define MI_ERR (-2)
read.c
main 函数
//如果对流程还有问题,可以看看 Data sheet 里面读卡器和卡片交互的过程,main 程序里的流程与之一致。
#include <stdio.h>
#include <bcm2835.h>
#include <stdlib.h>
#include <string.h>
#include "RFID.h"
#include "mfrc522.c"
int main(){
int i,j,count;
unsigned char command;
unsigned char sendData;
unsigned char sendLen;
unsigned char backData;
unsigned int backLen;
unsigned char s;
unsigned char id[10];
unsigned char key[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
unsigned char uid[5]; //4字节卡序列号,第5字节为校验字节
unsigned char str[MAX_LEN];
unsigned char wData[16] = {'h','a','c','k','e','d',' ','b','y',' ','r','u','o'};
int isTrue = 1;
if (!bcm2835_init()) return -1;
RFID_init();
while(isTrue){
if (findCard(0x52,&s) == MI_OK){
if ( anticoll(id) == MI_OK){
memcpy(uid,id,5);
printf("CARD UID:");
for(i = 0;i < 5;i++)
printf("%x",uid[i]);
printf("\n");
}else {
printf("FindCard ERR.\n");
}
//select Card
selectCard(uid);
//auth
for(j=0;j<64;j++) {
if(auth(0x61,j,key,uid) == MI_OK){
//read data
if(read(j,str) == MI_OK){
printf("Hex:");
for(i = 0;i < 16;i++)
printf("%02x",str[i]);
printf("\n");
// printf("Data:%s\n",str);
}
}
else
printf("Auth faild.\n");
}
}
RFID_halt();
}
bcm2835_spi_end();
bcm2835_close();
return 0;
}
0x03 测试读卡
终端里编译执行
读卡成功!
除非注明,嗯VIEW文章均为原创,转载请以链接形式标明本文地址
本文地址:https://www.umview.com/rfid-mfrc522
老師你好,想請問RC522模組腳位接線問題,為何您的SDA 以及 SCK是接到23 與 24接腳?
而不是 腳為3 以及 5呢?
RC522的模組如果用在msp430f5529的話該如何接呢?