本帖最后由 单片机菜鸟 于 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 (出处: 极客工坊)
|