开发者学堂课程【嵌入式之 RFID 开发与应用2020版:RFID 寄存器初始化】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/665/detail/11120
RFID 寄存器初始化
针对 A 类卡的基本流程
对于 B 类卡在这次的内容里面是不去讨论的,只讨论 A 类卡。就是的专门针对 A 类卡的操作。基本流程看一下,首先,有关串口的操作不再重复了,就是针对串口的初始化以及卡片的硬件的复位的脉冲。
unsigned char version = 0;
if(access(" /sys/class/gpio/gpio30/value",F_OK))
system( "echo 30 > l sys/ class/gpio/export" );
system( "echo out > /sys/class/gpio/gpio30/direction");
//外部复位 RFID 模块,直到成功读回正确版本号确定卡的存在 do{
rfid_uart_init();
//如果版本读取失败还要重置串口 do_rset_set(e);
usleep(50*1000 ) ;
do_rset_set(1);
usleep(50*1000);
version = reg_get(VersionReg);
printf( "version=%x\n", version) ;
if(version != ex9二 && version != 0x88){
usleep ( 100*1000) ;
uart_ciose();
usleep(10*1000);
}while(version != 0x9二 && version != 0x88);
再接下来需要对卡片进行一个初始化,而且主要是针对 A 类卡(cardA)的初始化。其实整个初始化的过程就是对一堆寄存器进行操作的过程,过程简单的说一下,因为寄存区域不应该过多去关注。
void rfid_carda_init(void)
/*复位*/
eg_set( CommandReg,PCD_RESETPHASE);
usleep(10*1000);
//复位需要时间
/*大部分保持芯片软复位后的默认值即可*/
reg_clr_bit(Status2Reg,exe8) ;
//关闭加密传输
reg_set(ModeReg,0x3D); //add new
reg_set(RxSelReg,0x86);//add new
/*发送部分*/
reg_set( TxASKReg, 0x40);
/ / TypeA 卡需要 100%ASK 调制
/*接收部分*/
reg_set(RFCfgReg, 0x7F);
//提高接收器的增益和灵敏度,主要用于增加通信
reg_set(TReloadRegL,30); //tmoLength); // TReloadVal = 'h6a =tmoLength(dec)
reg_set(TReloadRegH,e);
首先第一个是软件复位,在这里大概提到了常用的几个命令,一个是 reg_set,第二个是 reg_get,然后还有 reg_set_bit 以及 reg_clr_bit。
1.reg_set
reg_set 的作用,建立一个文本,首先是有几个接口,接口的作用是 fm17550,寄存器写入值,与其相对应的还有一个从 FM17550 寄存器中读取值,以及 set_bit,set_bit 和上面是类似的,唯一的区别是给 1750 寄存器的指定位置1写入值,它遵循的原则是读改写原则,可以看到要操作的寄存器和要修改的寄存器是 mask 里面所提到的那些为 1 的位。
unsigned char reg_set_bit(unsigned char reg_addr,unsigned char mask)
unsigned char status = 0;
status = reg_set( reg_addr, reg_get(reg_addr) / mask);
if(status == wR_succEss)
return SET_BIT_SuccEss;
else
return SET_BIT_FAIL;
首先是先读取里面的地址,再进行改的操作,最后再写进去,即读改写操作。同理 clear 也是一样的道理,它也是类似的,就是给寄存器指定的位怎么样了,那就是置顶的依然遵循读改写的原则,寄存器人指定位置 0。也就是先给值读出来,把原来表示为一的值全部取反让其变成 0,然后再与上读出来的结果,再把它写回去,以上就是所谓的读改写。
Reg_set 的作用是给寄存器写入一个值,一共有 4 页寄存器,分别为第一页是有关命令和状态的寄存器,第二页通讯的寄存器,第 3 页是跟配置有关的寄存器。第 4页是跟测试相关的寄存器。
这些所有的寄存器都变成红色了,红也是官方给的,不是自己去定义的,直接拿过来用就可以。
结合前面设计的内容,要去查找 CommandReg 寄存器的作用
位 |
符号 |
描述 |
7-6 |
- |
预留后用。 |
5 |
RcvOff |
置1,射频模拟接收器关闭。 |
4 |
PowerDown |
置1,进入 Soft power-down 模式。 置.0,FM17550 开始唤醒过程。过程中该位仍保持 1。0 表示FM17550 已为后续操作准备就绪。 注:当指令 SoftReset 被激活时,PowerDown 位不能置 1。 |
3-0 |
Command |
指令寄存器,根据主控芯片写入的指令码激活一条指令。读该寄存器反馈正在执行的指令。 |
比如可能用的最多的就是 3-0 位,低次位决定了 pcb,也就是 FM17550 的芯片的一个配置的命令,就它芯片的配置命令,所以它的命令显然最多就是十几个命令,因为它总共就只有 4 位。刚刚传递的是 PCD-RESETPHASE 命令,所以这里要记住一点,只要是 pcd 开头的命令都是操作 FM17550,其实就是操作 pcd 阅读器的命令。与之对应的是只要是 picc 开头的,都是操作 picc(卡片)。PCD 支持的命令如下
/读写命令
#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_CALCCRE
(0x03) /CRC 计算
再回去看一下,刚才是叫做复位命令,之前有一个上电的时候的负脉冲,它是属于硬复位,现在去让它成为一个软复位
reg_set( CommandReg,PCD_RESETPHASE);
usleep( 10*1000) ;
等待 10 毫秒之后软件复位结束,然后依然是通过设置,寄存器将其它的为 8 一位清零,就表示关闭了加密通道。并且将模式设置成 3D,然后接收器设置为 86,然后发送支持 100%,Ask 调幅的方式,然后接收部分设置成 7F,然后接下来的定时器的高位和低位以及模式分屏分别设置成以下字段:
reg_set(TReloadRegL,30); //tmoLength); // TReloadVal = 'h6a =tmoLength(dec)
reg_set( TReloadRegH,0);
reg_set(TModeReg,0x8D);
reg set(TPrescalerReg,0x3E);
然后把它的天线先关闭,延时之后再打开
/*天线*
antenna_set(ANTENNA__OFF);
usleep(10*1000);
antenna_set(ANTENNA_ON ) ;
void antenna_set(unsigned char mode)
if( mode == ANTENNA_ON){
if ( ! (reg_get( (TxControlReg) & 0x03 ))
reg_set_bit( TxControlReg, 0x03);
//天线开
}
else if(mode == ANTENNA_OFF)
reg_clr_bit(TxControlReg,0x03);
//天线关
}
AntennA 的 site 接口其实本质上也是通过调用 reg_set 的就先读取它的最低两位,然后再将它的最低两位设成 1 或者是 0,设置为 1 就是开启,设置为 0 就是关闭。有关天线的寄存器就是 14 号寄存器,
相对来说重要一点,关于 TxControlReg 寄存器如下
位 |
符号 |
描述 |
7 |
lnvTx2RFOn |
置1,如果TX2驱动使能,引脚TX2的输出信号取反。 |
6 |
lnvTx1RfOn |
置1,如果TX1驱动使能,引脚TX1的输出信号取反。 |
5 |
lnvTx2RFOff |
置1,如果TX2驱动关闭,引脚TX2的输出信号取反。 |
4 |
InvTx1RFOff |
置1,如果TX1驱动关闭,引脚T×1的输出信号取反。 |
3 |
Tx2Cw |
置1,引脚TX2将持续输出未调制的13.56 MHz的能量载波。 置0,Tx2CW 使能调制13.56 MHz 载波。 |
2 |
RFU |
预留后用。 |
1 |
Tx2RFEn |
置1,引脚 TX2 输出经由发送数据调制的 13.56 MHz 能量载波。 |
0 |
Tx1RFEn |
置 1,引脚 TX1 输出经由发送数据调制的 13.56 MHz 能量载波。 |
两位决定了天线的它有两个引脚,它可以发射 13.56 兆的载波的频率,0 代表了静止。把它设置成 set1 就是开启,clear70 就是关闭,这就是 AntennA 的天线的配置。天线配置完打开之后,初始化就认为完成。
2.寻卡
初始化完成之后,紧接着是去读就是去读取它的卡号以及来进行寻卡,寻卡指令如下:
ret = rfid_carda_request(PICCEQALL,type);
if(ret == OK){
printf( "type = Ox%02x%02x\n",type[e],type[1]);
//waitCardOff();
}else continue;
ret = rfid_anticol1( card_id);
if(ret == OK){
printf( "card ID: ");
for(i=0;i<4;i++)
printf( "Ox%02x ",card_id[i]);
puts("");
waitcardOff();
寻卡指令是 PCC_REQALL,前面提到过,只要是 PIcC_开关的命令,都是操作 PICc (卡后)的命令。之前操作的全都是阅读器 PCD。接下来要真正的操作卡片,卡片的指令是 picc 0x52,卡片的操作指令是由中国金融集成电路IC卡规范,它规范它是通用的,不管是什么阅读器,只要操作卡片的都要去遵循规范,所以说是通用的。无论是什么类型的操作卡,操作指令都是 52。要把指令下达到卡片里面去,前面已经把天线打开了,天线打开就代表阅读器已经可以发射电磁波。如果在它的辐射范围之内有卡片接近,就可以去读卡。代码操作如下:
reg_clr_bit(status2Reg,0x08);
//清除加密位
reg_set(BitFrafingReg, 0x07);
//请求帧格式
reg_set_bit(TxContro1Reg,0x03);
//设置载波方式
buf[0] = req_code;
status = rfid_cmd(PCD_TRANSCEIVE, buf,1, buf, &len);
if ( ( status == CMD_succESS) &&( len == 16))
//2字节,16bit
*card_type= buf[];*( card_type+1) = buf[1];status = OK ;
详细了解要看数据手册,首先把指令封装到 buf 的第 0 个字节里面,然后接下来通过 RFID_cmd 实现对卡片的操作,就是直接对卡片的操作,比如说是阅读器可以完成发送并接收数据,status = rfid_cmd(PCD_TRANSCEIVE, buf,1, buf, &len);
指的是发送的数据在 bug 里面长度为一个字节,这后面的是接收的数据和接收的数据,就是希望接收的数据放在哪里,那把接收的数据放到和发射在同一个buf 里面。0 表示不想接收,长度是已经给出的,是它得到了多少个数据,它就会填到 len 里面,让用户知道得到了多少数据。
3.rfid_cmd
switch (Command){
case PCD_AUTHENT:
irqEn=0x12;
waitFor = 0x10;
break;
case PCD_TRANSCEIVE:
irqEn= 0x77;
waitFor = Ox30;
break;
default:
break;
它的整个的工作过程如下,首先判断一下的阅读器的操作的指令,如果说是这两种指令它这里会做一个简单的处理。
接下来就是主要是针对阅读器的寄存器的配置,包括对 FIFOLevelReg 的一些整理
for (i=0; i<InLenByte; i++)
reg_set( FIFODataReg, pInData[i]);
reg_set( CommandReg, Command);
Plndata 就是要发送的 buf,buf 传到 Plndata 里面,所以说把 buf 里面的指令也就是 picc 的指令写在了 64 字节的 FIFOLevelReg 里面。写进去之后通过 reg_set( CommandReg, Command);
命令即启动停止命令,去启动对卡片的写入操作,那指令最终就写进去了。
if (command == PCD_TRANSCEIVE)
reg_set_bit(BitFramingReg,ex80);
//开启数据发射
timerIset(5);
//本来要 25ms 但实测 5ms 就可以,且下面 2000 的软件延时也要加
i = 2000;
/根据时钟频率调整,操作 M1 卡最大等待时间 25ms
do{
n = reg_get(ComIrqReg);
i--;
}while ((il=0)&& ! (n&0xe1) && ! (n&waitFor));
//判断是否接收到数据或指令执行完成
reg_clr_bit(BitFramingReg,ex80);
//关闭数据发射
每一次操作它都有一个反应的时间,所以会有一个等待时间,大概是 20 多毫秒。它可以用web定时器也可以用内部定时器来实现一个等待。
if (i!=){
/I 条件成立表示可能成功执行指令或收到数据
rif(!(reg_get(ErrorReg)&0x1B)){
//如果错误寄存器有错误标志,表示未能成功执行
status =CMD_succEss;
if (n & irqEn & 0x01)
//一般表示超时了 status = COMMAND_ERR;
if (Command == PCD_TRANSCEIVE){
//如果是传输数据的指令,就开始接收数据
n = reg_get(FIFOLevelReg);
/ 读取 FIFO 中收到的数据长度
lastBits = reg_get(ControlReg) & 0x07;
/读取低于 8bit 的不完整位数 if ( lastBits)/ 计算接收总 bit 数
*poutLenBit = (n-1)*8 + lastBits;else
*poutLenBit = n*8;
if(n > MAXRLEN)
n = MAXRLEN;
for(i=; i<n; i++)
pOutData[i] = reg_get(FIFODataReg);
以上数据就是要为接收数据做准备的,接收数据也是从 FIFOLevelReg 里面去获取,和前面的是一样的,是同一个寄存器,收和发都是寄存器,收到的数据存到的outdata 里面,并且把长度记录到 poutLenbit 里面。因为它接受的长度有可能是不完整的,需要要做一些处理。接收完了数据之后,rfid_cmd 就会返回,它返回的时候就带回了它得到的数据以及它得到了多少的数据。
需要判断首先命令下达是不是正确的,如果是正确的,长度读到了多少,如果全部都正确,那可能会把读到的数据重新再赋值给 card_type,也就是可以获取卡的类型。
4.card_type
Card_type 是的最后一个参数,ret =rfid_carda_request(PICC_REQALL,type ;
通过寻卡的操作就可以得到卡的类型,卡的类型到后面的时候再去进行学习,包括怎么样读取 ID。