极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 20005|回复: 7

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

[复制链接]
发表于 2016-11-4 10:57:27 | 显示全部楼层 |阅读模式
本帖最后由 单片机菜鸟 于 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-->RX  RX--->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(Master  Out)发送到从机,对应相连的从机上的引脚也是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[0],HEX);
      Serial.print(rfid.serNum[1],HEX);
      Serial.print(rfid.serNum[2],HEX);
      Serial.print(rfid.serNum[3],HEX);
      Serial.print(rfid.serNum[4],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[3..0] + TPrescalerReg 00111110
  writeMFRC522(TReloadRegL, 30);
  writeMFRC522(TReloadRegH, 0);

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

  //ClearBitMask(Status2Reg, 0x08); //MFCrypto1On=0
  //writeMFRC522(RxSelReg, 0x86);   //RxWait = RxSelReg[5..0]
  //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[MAX_LEN];

  //寻天线区内未进入休眠状态
  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[2..0] ???

  TagType[0] = 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[7..0]
    //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[MAX_LEN];

  // 防冲撞,返回卡的序列号 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[2..0]

  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++)
      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  (出处: 极客工坊)





本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2016-11-4 14:59:02 | 显示全部楼层
不错的教程,谢谢!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-11-4 15:37:09 | 显示全部楼层
P.919HY 发表于 2016-11-4 14:59
不错的教程,谢谢!

兴趣爱好  自己踩过的坑
回复 支持 反对

使用道具 举报

发表于 2016-11-5 10:40:01 | 显示全部楼层
学习了 感谢楼主
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-11-5 18:52:53 | 显示全部楼层
yaanlmc 发表于 2016-11-5 10:40
学习了 感谢楼主

哈哈哈 客气
回复 支持 反对

使用道具 举报

发表于 2016-11-12 14:58:18 | 显示全部楼层

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

使用道具 举报

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

2421818708
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|联系我们|极客工坊

GMT+8, 2026-6-10 00:14 , Processed in 0.064404 second(s), 21 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表