Linux驱动开发-编写RFID-RC522射频刷卡模块驱动

【摘要】 当前文章介绍如果在linux系统下编写mf-rc522模块驱动,配合应用层,完成ic卡号读取,扇区读写,密码验证等等。当前开发板采用友善之臂tiny4412,芯片是三星的exynos4412,驱动代码没有采用spi子系统,直接控制io口模拟spi时序完成与mf-rc522之间通讯。
1. mf-rc522模块介绍 mfrc522是应用于13.56mhz非接触式通信中高集成度的读写卡芯片,针对“三表”应用推出的一款低电压、低成本、体积小的非接触式读写卡芯片,是智能仪表和便携式手持设备研发的较好选择。便携式手持设备研发的较好选择。mfrc522利用了先进的调制和解调概念,集成了在13.56mhz下所有类型的被动非接触式通信方式和协议。支持14443a兼容应答器信号。数字部分处理iso14443a帧和错误检测。此外,还支持快速crypto1加密算法,用语验证mifare系列产品。mfrc522支持mi fare系列更高速的非接触式通信,双向数据传输速率高达424kbit/s。作为13.56mhz高集成度读写卡系列芯片族的新成员,mfrc522与mf rc500和mfrc530有不少相似之处,同时也具备许多特点和差异。它与主机间通信采用spi模式,有利于减少连线,缩小pcb板体积,降低成本。
淘宝上mfrc522的成品模块非常多,购买都会送几张白卡(ic卡),完成读写实验。
  淘宝上购买的mf-rc522模块基本是引出的spi接口,实际上mf-rc522本身还支持iic,uart协议,spi相比来讲,协议更加简单,速度也快。
当前我采用的就是淘宝购买一个封装好的成品模块,采用mfrc522原装芯片设计读卡电路,使用方便,成本低廉,适用于设备开发、读卡器开发等高应用的用户,需要进行射频卡终端设计/生产的用户。本模块可直接装入各种读卡器模具。模块采用电压为3.3v,通过spi接口简单的几条线就可以直接与用户任何cpu主板相连接通信,可以保证模块稳定可靠的工作、读卡距离远。
当前文章介绍如果在linux系统下编写mf-rc522模块驱动,配合应用层,完成ic卡号读取,扇区读写,密码验证等等。当前开发板采用友善之臂tiny4412,芯片是三星的exynos4412,驱动代码没有采用spi子系统,直接控制io口模拟spi时序完成与mf-rc522之间通讯。
购买模块时,会送一张ic白卡和一个钥匙扣,虽然形状不一样,内部芯片型号都是属于s50卡,常用的公交车卡、地铁卡、超市会员卡等等,都是属于这种s50卡。这个洗头还有一个s70类型的卡,空间比s50大4倍。s50卡内部就是一个eeprom空间,可以存放任何数据,空间一共分为16个扇区,每个扇区由4块(0、1、2、3)组成。实际操作时,将16个扇区分为64个块,按绝对地址编号为0-63。
ic卡没有电源的,它是由ic芯片、感应天线组成,封装在一个标准的pvc卡片内,芯片及天线无任何外露部分。是世界上最近几年发展起来的一项新技术,它成功的将射频识别技术和ic卡技术结合起来,结束了无源(卡中无电源)和免接触这一难题,是电子器件领域的一大突破。卡片在一定距离范围(通常为5—10cm)靠近读写器表面,通过无线电波的传递来完成数据的读写操作。
2. 硬件原理连线   3. 驱动代码示例 3.1 rc522.c 源代码#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include rfid_rc522.h#include #include /*--------------------------------rc522相关操作代码---------------------------------------------*//*函数功能:rc522初始化tiny4412硬件连接: do--miso :gpb_2 di--mosi :gpb_3 clk-sclk :gpb_0 cs--cs :gpb_1 rst-- :gpb_4*/void rc522_io_init(void){ /*1. 注册gpio*/ gpio_request(exynos4_gpb(0), rc522_clk-sclk); gpio_request(exynos4_gpb(1), rc522_cs); gpio_request(exynos4_gpb(2), mosi); gpio_request(exynos4_gpb(3), rc522_mosi); gpio_request(exynos4_gpb(4), rst); /*2. 配置gpio口模式*/ s3c_gpio_cfgpin(exynos4_gpb(0), s3c_gpio_output); //时钟 s3c_gpio_cfgpin(exynos4_gpb(1), s3c_gpio_output); //片选 s3c_gpio_cfgpin(exynos4_gpb(2), s3c_gpio_input); //输入模式 s3c_gpio_cfgpin(exynos4_gpb(3), s3c_gpio_output); //输出模式 s3c_gpio_cfgpin(exynos4_gpb(4), s3c_gpio_output); //输出模式 /*3. 上拉gpio口*/ gpio_set_value(exynos4_gpb(0), 1); gpio_set_value(exynos4_gpb(1), 1); gpio_set_value(exynos4_gpb(3), 1); gpio_set_value(exynos4_gpb(4), 1);}/*函数功能:spi时序读写一个字节说 明:spi底层时序,程序的移植接口*/u8 rc522_spi_readwriteonebyte(u8 data_tx){ u8 data_rx=0; u8 i; for(i=0;i<8;i++) { gpio_set_value(exynos4_gpb(0), 0); if(data_tx&0x80)gpio_set_value(exynos4_gpb(3), 1); else gpio_set_value(exynos4_gpb(3), 0); data_tx<<=1; //继续发送下一个数据 gpio_set_value(exynos4_gpb(0), 1); data_rx<<=1; if(gpio_get_value(exynos4_gpb(2)))data_rx|=0x01; } return data_rx;}/*功能描述:选卡读取卡存储器容量输入参数:sernum 传入卡序列号返 回 值:成功返回卡容量*/u8 rc522_mfrc522_selecttag(u8 *sernum) //读取卡存储器容量{ u8 i; u8 status; u8 size; u8 recvbits; u8 buffer[9]; buffer[0]=picc_anticoll1; //防撞码1 buffer[1]=0x70; buffer[6]=0x00; for(i=0;i<4;i++) { buffer[i+2]=*(sernum+i); //buffer[2]-buffer[5]为卡序列号 buffer[6]^=*(sernum+i); //卡校验码 } rc522_calulatecrc(buffer,7,&buffer[7]); //buffer[7]-buffer[8]为rcr校验码 rc522_clearbitmask(status2reg,0x08); status=rc522_pcdcommf522(pcd_transceive,buffer,9,buffer,&recvbits); if((status==mi_ok)&&(recvbits==0x18)) size=buffer[0]; else size=0; return size; }/*延时函数,纳秒级*/void rc522_delay(u32 ns){ ndelay(ns);}/*函数功能:rc522芯片初始化*/void rc522_init(void){ rc522_io_init(); //rc522初始化 rc522_pcdreset(); //复位rc522 rc522_pcdantennaoff(); //关闭天线 msleep(2); //延时2毫秒 rc522_pcdantennaon(); //开启天线 m500pcdconfigisotype('a'); //设置rc632的工作方式}/*函数功能:复位rc522*/void rc522_reset(void){ rc522_pcdreset(); //复位rc522 rc522_pcdantennaoff(); //关闭天线 msleep(2); //延时2毫秒 rc522_pcdantennaon(); //开启天线 }/*功 能: 寻卡参数说明: req_code[in]:寻卡方式 0x52 = 寻感应区内所有符合14443a标准的卡 0x26 = 寻未进入休眠状态的卡 ptagtype[out]:卡片类型代码 0x4400 = mifare_ultralight 0x0400 = mifare_one(s50) 0x0200 = mifare_one(s70) 0x0800 = mifare_pro(x) 0x4403 = mifare_desfire返 回 值: 成功返回mi_ok*/char rc522_pcdrequest(u8 req_code,u8 *ptagtype){ char status; u8 unlen; u8 uccommf522buf[maxrlen]; // maxrlen 18 rc522_clearbitmask(status2reg,0x08); //清rc522寄存器位,/接收数据命令 rc522_writerawrc(bitframingreg,0x07); //写rc632寄存器 rc522_setbitmask(txcontrolreg,0x03); //置rc522寄存器位 uccommf522buf[0]=req_code; //寻卡方式 status=rc522_pcdcommf522(pcd_transceive,uccommf522buf,1,uccommf522buf,&unlen); //通过rc522和iso14443卡通讯 if((status==mi_ok)&&(unlen==0x10)) { *ptagtype=uccommf522buf[0]; *(ptagtype+1)=uccommf522buf[1]; } else { status = mi_err; } return status;}/*功 能: 防冲撞参数说明: psnr[out]:卡片序列号,4字节返 回: 成功返回mi_ok*/char rc522_pcdanticoll(u8 *psnr){ char status; u8 i,snr_check=0; u8 unlen; u8 uccommf522buf[maxrlen]; rc522_clearbitmask(status2reg,0x08); //清rc522寄存器位 rc522_writerawrc(bitframingreg,0x00); //写 rc522_clearbitmask(collreg,0x80); //清 uccommf522buf[0]=picc_anticoll1; //picc_anticoll1 = 0x93 uccommf522buf[1]=0x20; status=rc522_pcdcommf522(pcd_transceive,uccommf522buf,2,uccommf522buf,&unlen); //0x0c,通过rc522和iso14443卡通讯 //pcd_transceive =发送并接收数据 //2:写入卡里的数据字节长度 //uccommf522buf:存放数据的地址 //unlen:从卡里读出的数据长度 if(status==mi_ok) { for(i=0;i<4;i++) { *(psnr+i)=uccommf522buf[i]; //把读到的卡号赋值给psnr snr_check^=uccommf522buf[i]; } if(snr_check!=uccommf522buf[i]) { status = mi_err; } } rc522_setbitmask(collreg,0x80); return status;}/*功 能:选定卡片参数说明:psnr[in]:卡片序列号,4字节返 回:成功返回mi_ok*/char rc522_pcdselect(u8 *psnr){ char status; u8 i; u8 unlen; u8 uccommf522buf[maxrlen]; uccommf522buf[0]=picc_anticoll1; uccommf522buf[1]=0x70; uccommf522buf[6]=0; for(i=0;i<4;i++) { uccommf522buf[i+2]=*(psnr+i); uccommf522buf[6]^=*(psnr+i); } rc522_calulatecrc(uccommf522buf,7,&uccommf522buf[7]); //用mf522计算crc16函数,校验数据 rc522_clearbitmask(status2reg,0x08); //清rc522寄存器位 status=rc522_pcdcommf522(pcd_transceive,uccommf522buf,9,uccommf522buf,&unlen); if((status==mi_ok)&&(unlen==0x18))status=mi_ok; else status=mi_err; return status;}/*功 能:验证卡片密码参数说明:auth_mode[in]: 密码验证模式 0x60 = 验证a密钥 0x61 = 验证b密钥 addr[in]:块地址 pkey[in]:扇区密码 psnr[in]:卡片序列号,4字节返 回:成功返回mi_ok*/ char rc522_pcdauthstate(u8 auth_mode,u8 addr,u8 *pkey,u8 *psnr){ char status; u8 unlen; u8 uccommf522buf[maxrlen]; //maxrlen 18(数组的大小) //验证模式+块地址+扇区密码+卡序列号 uccommf522buf[0]=auth_mode; uccommf522buf[1]=addr; memcpy(&uccommf522buf[2],pkey,6); //拷贝,复制 memcpy(&uccommf522buf[8],psnr,4); status=rc522_pcdcommf522(pcd_authent,uccommf522buf,12,uccommf522buf,&unlen); if((status!= mi_ok)||(!(rc522_readrawrc(status2reg)&0x08)))status = mi_err; return status;}/*功 能:读取m1卡一块数据参数说明: addr:块地址p :读出的块数据,16字节返 回:成功返回mi_ok*/ char rc522_pcdread(u8 addr,u8 *p){ char status; u8 unlen; u8 i,uccommf522buf[maxrlen]; //18 uccommf522buf[0]=picc_read; uccommf522buf[1]=addr; rc522_calulatecrc(uccommf522buf,2,&uccommf522buf[2]); status=rc522_pcdcommf522(pcd_transceive,uccommf522buf,4,uccommf522buf,&unlen);//通过rc522和iso14443卡通讯 if((status==mi_ok&&(unlen==0x90))) { for(i=0;i<16;i++) { *(p +i)=uccommf522buf[i]; } } else { status=mi_err; } return status;}/*功 能:写数据到m1卡指定块参数说明:addr:块地址 p :向块写入的数据,16字节返 回:成功返回mi_ok*/ char rc522_pcdwrite(u8 addr,u8 *p){ char status; u8 unlen; u8 i,uccommf522buf[maxrlen]; uccommf522buf[0]=picc_write;// 0xa0 //写块 uccommf522buf[1]=addr; //块地址 rc522_calulatecrc(uccommf522buf,2,&uccommf522buf[2]); status=rc522_pcdcommf522(pcd_transceive,uccommf522buf,4,uccommf522buf,&unlen); if((status!= mi_ok)||(unlen != 4)||((uccommf522buf[0]&0x0f)!=0x0a)) { status = mi_err; } if(status==mi_ok) { for(i=0;i<16;i++)//向fifo写16byte数据 { uccommf522buf[i]=*(p +i); } rc522_calulatecrc(uccommf522buf,16,&uccommf522buf[16]); status = rc522_pcdcommf522(pcd_transceive,uccommf522buf,18,uccommf522buf,&unlen); if((status != mi_ok)||(unlen != 4)||((uccommf522buf[0]&0x0f)!=0x0a)) { status = mi_err; } } return status;}/*功 能:命令卡片进入休眠状态返 回:成功返回mi_ok*/char rc522_pcdhalt(void){ u8 status; u8 unlen; u8 uccommf522buf[maxrlen]; //maxrlen==18 status=status; uccommf522buf[0]=picc_halt; uccommf522buf[1]=0; rc522_calulatecrc(uccommf522buf,2,&uccommf522buf[2]); status=rc522_pcdcommf522(pcd_transceive,uccommf522buf,4,uccommf522buf,&unlen); return mi_ok;}/*功 能:用mf522计算crc16函数参 数: *pin :要读数crc的数据 len:-数据长度 *pout:计算的crc结果*/void rc522_calulatecrc(u8 *pin ,u8 len,u8 *pout ){ u8 i,n; rc522_clearbitmask(divirqreg,0x04); //crcirq = 0 rc522_writerawrc(commandreg,pcd_idle); rc522_setbitmask(fifolevelreg,0x80); //清fifo指针 //向fifo中写入数据 for(i=0;i
传感器的7大感应方式介绍
复旦微自研存储芯片通过AEC-Q100 Grade 1车规级验证
基于正交频分复用技术的自动PLC/AMR方案
曝红米9与小米10将于3月份在印度同台发布 前者或搭载联发科Helio G70处理器
新基建下的5G产业,如何保障基站的无线传输质量
Linux驱动开发-编写RFID-RC522射频刷卡模块驱动
卡塔尔和特斯拉联手合作共建电站的首个储能系统
低压电器用电范围_低压电器的工作制
射频芯片:创新不一定是出路
RC无源滤波电路介绍_RC无源滤波电路及其原理
配电变压器损坏的原因分析及解决方法
高性能SiC JFET SJEP120R100A的操作和性能
华为平板M6曝光将有8.4英寸和10.1英寸两种版本均搭载了麒麟980处理器
海力士不排除与Rambus和解
安全光幕的光轴间距如何区分
F-OP300倾斜模块上线——支持免像控倾斜模式
孢子捕捉仪的作用原理及其配置的介绍
ADI精密窄带宽信号链平台的详细介绍
II在Microblaze上的移植与使用专题(续3)
高通领头,抱团合作让5G照明全球