单片机菜鸟 发表于 2016-11-4 10:57:27

《博哥玩Arduino》-测试RC522模块

本帖最后由 单片机菜鸟 于 2016-11-27 21:15 编辑

大学学电子,毕业后阴差阳错跑去做了android开发。近来心血来潮,又想玩玩单片机和android app的结合了,然后就考虑了Arduino以及树莓派。最后选Arduino入门吧,毕竟是一种爱好,也相对来说门槛没那么高,所以就上某宝掏了一块UNO R3开发板以及一些外围电路模块,射频模块RC522 蓝牙模块HC05 超声波模块等等,也花了两天时间大概鲁了一遍《ARDUINO程序设计基础》,了解基本的开发流程。       买了RC522模块,发现原来引脚没有焊好,好吧,那就上网去买个电烙铁,这下子工具齐全了,貌似又回到大学那段焊板子的日子      就先从RC522模块开始吧,使用SPI总线,电路接线如下:

引脚对应关系:

RFID -> Arduino UNO
-------------------
GND -> GND
SS -> D10//片选使能
SCK -> D13 //时钟
MISI -> D11
MISO -> D12
3V# -> 3.3V
RST -> D5


因为有几年没有玩过单片机了。所以特意去查了一下SPI通信:

一开始我对接线不是很理解,因为对于串口来说,TX-->RXRX--->TX
那应该是 MISO->MOSI   MOSI->MISO
其实不然,在这里Arduino是主机RC522是从机。MISO<-->MISO,MOSI<-->MOSI,因为,SPI通信协议是全双工的,从这一点上说,似乎应该是MISO<-->MOSI,MOSI<-->MISO,有点像网络TX<-->RX,RX<-->TX。其实这样理解是不对的。主机在任何时刻只和SS为LOW的从机通信(也就是,只有SS引脚为LOW的从机才响应主机的呼唤),当主机向从机写数据时,通过MOSI(MasterOut)发送到从机,对应相连的从机上的引脚也是MOSI(Slave In); 从机向主机写入数据时,用的是MISO<-->MISO,对从机而言,是Slave Out,对主机而言是Master In.

理解了这个之后,我去网上找了一下RC522的扩展库 以及 MFRC522模块的中文手册。

MFRC522模块的中文手册大于1MB,无法上传上来。

最终在Arduino IDE用如下代码:

/**
读卡器与M1卡之间的通讯,首先要寻卡(Answer To Request),验证卡片类型
----》防冲突检查(Anticollision Loop)选择一张卡片,返回该卡序列号;
-----》选择卡片(Select Tag),选择被选中的卡的序列号,返回卡的容量代码;
-----》三次互相确认(3 Pass Authentication)通过密码校验之后,三次互相认                                             证                                                   之后可以通讯。
*/

#include <SPI.h>
#include <RFID.h>

RFID rfid(10,5);   //D10--读卡器SS引脚、D5--读卡器RST引脚

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
SPI.begin();
rfid.init();
}

void loop() {
//找卡
if (rfid.isCard()) {
    Serial.println("Find the card!");
    //读取卡序列号
    if (rfid.readCardSerial()) {
      Serial.print("The card's number is: ");
      Serial.print(rfid.serNum,HEX);
      Serial.print(rfid.serNum,HEX);
      Serial.print(rfid.serNum,HEX);
      Serial.print(rfid.serNum,HEX);
      Serial.print(rfid.serNum,HEX);
      Serial.println(" ");
    }
    //选卡,可返回卡容量(锁定卡片,防止多数读取),去掉本行将连续读卡
    rfid.selectTag(rfid.serNum);
}
rfid.halt();
}

流程:
   1.先初始化RC522模块
   2.开始轮询寻找天线内未进入休眠的卡   卡命令:PICC_REQIDL(RFID.h 里面定义)
   3.找到卡之后打印序列号
   4.锁定卡片,防止多数读卡
   5.卡进入休眠


因为用到别人的扩展库,程序员的习惯,那么就进入扩展库看看代码。


第一步看看RFID这个类的初始化

/******************************************************************************
* 构造 RFID
* int chipSelectPin RFID /ENABLE pin
******************************************************************************/
RFID::RFID(int chipSelectPin, int NRSTPD)
{
_chipSelectPin = chipSelectPin;
_NRSTPD = NRSTPD;

pinMode(_chipSelectPin,OUTPUT);   // 设置管脚_chipSelectPin为输出并连接到模块使能口
digitalWrite(_chipSelectPin, LOW);


pinMode(_NRSTPD,OUTPUT);            // 设置管脚NRSTPD为输出,非重置或掉电
digitalWrite(_NRSTPD, HIGH);
}

主要是处理片选引脚以及RST引脚。

第二步看看init这个函数:

/******************************************************************************
* 函 数 名:init
* 功能描述:初始化RC522
* 输入参数:无
* 返 回 值:无
******************************************************************************/
void RFID::init()
{
digitalWrite(_NRSTPD,HIGH);

//复位RC522
reset();

//Timer: TPrescaler*TreloadVal/6.78MHz = 15ms
writeMFRC522(TModeReg, 0x8D);   //Tauto=1; f(Timer) = 6.78MHz/TPreScaler 10001101
writeMFRC522(TPrescalerReg, 0x3E);//TModeReg + TPrescalerReg 00111110
writeMFRC522(TReloadRegL, 30);
writeMFRC522(TReloadRegH, 0);

writeMFRC522(TxAutoReg, 0x40);    //100%ASK
writeMFRC522(ModeReg, 0x3D);    // CRC valor inicial de 0x636300 11 11 01

//ClearBitMask(Status2Reg, 0x08); //MFCrypto1On=0
//writeMFRC522(RxSelReg, 0x86);   //RxWait = RxSelReg
//writeMFRC522(RFCfgReg, 0x7F);   //RxGain = 48dB

antennaOn();    //打开天线
}

/******************************************************************************
* 函 数 名:reset
* 功能描述:复位RC522
* 输入参数:无
* 返 回 值:无
******************************************************************************/
void RFID::reset()
{
//复位
writeMFRC522(CommandReg, PCD_RESETPHASE);
}

/******************************************************************************
* 函 数 名:writeMFRC522
* 功能描述:向MFRC522的某一寄存器写一个字节数据
* 输入参数:addr--寄存器地址;val--要写入的值
* 返 回 值:无
******************************************************************************/
void RFID::writeMFRC522(unsigned char addr, unsigned char val)
{
digitalWrite(_chipSelectPin, LOW);

//地址格式:0XXXXXX0
//地址字节按下面格式传输。第一个字节的MSB设置使用的模式
//MSB位为0时将数据写入MFRC522
//位6-1定义地址
//最后一位应对设置为0
SPI.transfer((addr<<1)&0x7E);
SPI.transfer(val);

digitalWrite(_chipSelectPin, HIGH);
}


主要是做一些配置,复位RC522 用到了CommandReg这个寄存器,然后设置了时钟 打开天线

第三步:开始找卡
/******************************************************************************
* 函 数 名:isCard
* 功能描述:寻卡
* 输入参数:无
* 返 回 值:成功返回ture 失败返回false
******************************************************************************/
bool RFID::isCard()
{
unsigned char status;
unsigned char str;

//寻天线区内未进入休眠状态
status = MFRC522Request(PICC_REQIDL, str);
if (status == MI_OK)
    return true;
else
    return false;
}


/******************************************************************************
* 函 数 名:MFRC522Request
* 功能描述:寻卡,读取卡类型号
* 输入参数:reqMode--寻卡方式,
*         TagType--返回卡片类型
*                  0x4400 = Mifare_UltraLight
*                  0x0400 = Mifare_One(S50)
*                  0x0200 = Mifare_One(S70)
*                  0x0800 = Mifare_Pro(X)
*                  0x4403 = Mifare_DESFire
* 返 回 值:成功返回MI_OK
******************************************************************************/
unsigned char RFID::MFRC522Request(unsigned char reqMode, unsigned char *TagType)
{
unsigned char status;
unsigned int backBits;      //接收到的数据位数

writeMFRC522(BitFramingReg, 0x07);    //TxLastBists = BitFramingReg ???

TagType = reqMode;
   //#define PCD_TRANSCEIVE      0x0C发送并接收数据
status = MFRC522ToCard(PCD_TRANSCEIVE, TagType, 1, TagType, &backBits);

if ((status != MI_OK) || (backBits != 0x10))
    status = MI_ERR;

return status;
}


/******************************************************************************
* 函 数 名:MFRC522ToCard
* 功能描述:RC522和ISO14443卡通讯
* 输入参数:command--MF522命令字,
*         sendData--通过RC522发送到卡片的数据,
*                     sendLen--发送的数据长度
*                     backData--接收到的卡片返回数据,
*                     backLen--返回数据的位长度
* 返 回 值:成功返回MI_OK
******************************************************************************/
unsigned char RFID::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);

//执行命令
writeMFRC522(CommandReg, command);
if (command == PCD_TRANSCEIVE)
    setBitMask(BitFramingReg, 0x80);    //StartSend=1,transmission of data starts

//等待接收数据完成
i = 2000; //i根据时钟频率调整,操作M1卡最大等待时间25ms
do
{
    //CommIrqReg
    //Set1 TxIRq RxIRq IdleIRq HiAlerIRq LoAlertIRq ErrIRq TimerIRq
    n = readMFRC522(CommIrqReg);
    i--;
}
while ((i!=0) && !(n&0x01) && !(n&waitIRq));

clearBitMask(BitFramingReg, 0x80);      //StartSend=0

if (i != 0)
{
    if(!(readMFRC522(ErrorReg) & 0x1B)) //BufferOvfl Collerr CRCErr ProtecolErr
    {
      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 = readMFRC522(FIFODataReg);
      }
    }
    else
      status = MI_ERR;
}

//SetBitMask(ControlReg,0x80);         //timer stops
//Write_MFRC522(CommandReg, PCD_IDLE);

return status;
}


发送一个寻卡命令,由于RC522要做收发命令,所以需要写BitFragmingReg寄存器 Startsend flag.因为找不到比较好的M1卡手册,有些命令我也不知道什么意思,暂且先放着。两者之间的数据交流是听过FIFO缓存空间

第四步:找到卡后需要打印卡的序列号

/******************************************************************************
* 函 数 名:readCardSerial
* 功能描述:返回卡的序列号 4字节
* 输入参数:无
* 返 回 值:成功返回ture 失败返回false
******************************************************************************/
bool RFID::readCardSerial(){

unsigned char status;
unsigned char str;

// 防冲撞,返回卡的序列号 4字节,存入serNum中
status = anticoll(str);
memcpy(serNum, str, 5);

if (status == MI_OK)
    return true;
else
    return false;
}

/******************************************************************************
* 函 数 名:anticoll
* 功能描述:防冲突检测,读取选中卡片的卡序列号
* 输入参数:serNum--返回4字节卡序列号,第5字节为校验字节
* 返 回 值:成功返回MI_OK
******************************************************************************/
unsigned char RFID::anticoll(unsigned char *serNum)
{
unsigned char status;
unsigned char i;
unsigned char serNumCheck=0;
unsigned int unLen;

//ClearBitMask(Status2Reg, 0x08);   //TempSensclear
//ClearBitMask(CollReg,0x80);   //ValuesAfterColl
writeMFRC522(BitFramingReg, 0x00);    //TxLastBists = BitFramingReg

serNum = PICC_ANTICOLL;//防冲撞
serNum = 0x20;
status = MFRC522ToCard(PCD_TRANSCEIVE, serNum, 2, serNum, &unLen);

if (status == MI_OK)
{
    //校验卡序列号
    for (i=0; i<4; i++)
      serNumCheck ^= serNum;
    if (serNumCheck != serNum)//第5字节为校验字节
      status = MI_ERR;
}

//SetBitMask(CollReg, 0x80);    //ValuesAfterColl=1

return status;
}


卡片需要运行防冲撞,具体是怎么做我也暂时不明白

今天暂且写到这里,毕竟看了一天这个RC522模块的各种资料,记录一下过程。后面等熟悉之后再重新修正

《博哥玩Arduino》-蓝牙小车-基础篇
   http://www.geek-workshop.com/thread-27767-1-1.html (出处: 极客工坊)
《博哥玩Arduino》-蓝牙小车-实操篇
   http://www.geek-workshop.com/thread-27777-1-1.html (出处: 极客工坊)
《博哥玩Arduino》-Arduino语法手册
   http://www.geek-workshop.com/thread-27843-1-1.html (出处: 极客工坊)
《博哥玩Arduino》-WIFI小车-基础篇
   http://www.geek-workshop.com/thread-27850-1-1.html (出处: 极客工坊)
《博哥玩Arduino》-WIFI小车-调试实操篇
   http://www.geek-workshop.com/thread-27853-1-1.html (出处: 极客工坊)
《博哥玩Arduino》- 蓝牙模块HC06 重命名
   http://www.geek-workshop.com/thread-27897-1-1.html (出处: 极客工坊)
《博哥玩Arduino》- 蓝牙RGB灯
    http://www.geek-workshop.com/thread-27892-1-1.html(出处: 极客工坊)




P.919HY 发表于 2016-11-4 14:59:02

不错的教程,谢谢!

单片机菜鸟 发表于 2016-11-4 15:37:09

P.919HY 发表于 2016-11-4 14:59
不错的教程,谢谢!

:lol 兴趣爱好自己踩过的坑

yaanlmc 发表于 2016-11-5 10:40:01

学习了 感谢楼主

单片机菜鸟 发表于 2016-11-5 18:52:53

yaanlmc 发表于 2016-11-5 10:40
学习了 感谢楼主

哈哈哈 客气

yaanlmc 发表于 2016-11-12 14:58:18

单片机菜鸟 发表于 2016-11-5 18:52
哈哈哈 客气

你好 我是一个爱好者 但是我对编程基本是白痴 自己也看了一些书,希望能加你QQ 以后能向你多请教

单片机菜鸟 发表于 2016-11-13 14:29:44

yaanlmc 发表于 2016-11-12 14:58
你好 我是一个爱好者 但是我对编程基本是白痴 自己也看了一些书,希望能加你QQ 以后能向你多请教

2421818708
页: [1]
查看完整版本: 《博哥玩Arduino》-测试RC522模块