51单片机驱动FM1702实现读写卡的资料包分享:
FM1702天线设计与电路设计及编程指南等:
程序使用说明:
1.程序用到的数码管、FM1702、按键的所有引脚均有定义,如果用户按照这个定义,程序上电后应该是数 码管无显示内容。
2.当有卡片靠近后,数码管先显示卡号序列号的高两位,显示大约2秒,然后读取卡片的块8的数值并显示
显示时间大约为6秒
3.按键是双功能键。按键2和3是充值和刷卡键,4是确认键,只有先按2或者3然后按4,否则直接按4程序 不会反应。当按了4后,此时按键2和3就变成增值或者减值按键,同时短按一下是增1或者减1,长按是 增10或者减10.确认了数值后,再次按4键,程序会根据你按得是充值键还是刷卡键进行响应的加或减运 算,并将运算后得值写入块8,然后再次读取,并显示在数码管上。在你按键确认数值之前,数码管会 显示当前你的按键值是多少。
4.总结使用流程就是:放卡到读卡器—按动2或3键—按4—再按2或3(长按短按效果不一样,数码管会显 示)—再按4—卡拿开
不足之处:
1.没有用到芯片自带的E2PROM,所以程序没有它的读写程序。
2.没有用到卡的自增自减运算函数,这个涉及卡的控制字符,卡的资料有详细,所以也没写
3.防衝突没有实现那种公平的防衝突,此程序实现的是随机选卡
注意事项:
网上关于射频卡的资料很多关键的地方都没有说明,类似SPI时序,寄存器读写指令,FIFO读写时间等等,所以下列几点一定要注意,这是我在做这个时遇到的问题。
1.首先,spi时序,注意读时和写时的sck,以及左移函数的位置,很重要。也可以把SPI的读和写分开写 ,会的自己试一下。
2.1702的寄存器读写并不是单纯的把寄存器地址写进去,而是有一定规则的。不论读写,寄存器都是先左 移一位,即有效的六位地址(做的时候你就明白为什麽是六位)处于一个字节的中间,最低位不论读写 都为0,高位当读时是1,写时是0,这点非常重要,查遍所有资料,都没有这点。
3.1702初始化时,按照pdf所给的启动步骤来,当时我觉得最后一条转换线性寻址好像没用到,所以没写 ,结果怎麽也不好使,后来一加,什麽都好使了。这也是很关键的一点。
4.延时问题,当FIFO有数据要发送或者接受时,启动0x1e命令后,根据FIFO字节数要加适当延时,这是很 关键的点,具体参考程序。
大致就这些了,程序实现的功能比较简单,后续我也许会加上12864,做成一个界面化的带密码修改,以及卡的识别的小型一卡通系统,类似于学校那种什麽饭卡、交通、图书馆、水卡的东西,如果做的话,我会把后续的传上来。敬请期待....
51单片机源程序如下:
- #include<reg52.h>
- #include<intrins.h>
- #include<FM1702.h>
- #define uchar unsigned char
- #define uint unsigned int
- uchar Fbuff[16]; //发送FIFO缓存
- uchar Jbuff[16]; //接收FIFO缓存
- uchar UID[7]; //卡型及卡号
- uchar Data[4]; //按键值存储区
- uchar code seg[16]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E}; //数码管段选对应0~f
- uchar count=0 ; //按键回传的值
- uchar shuaka,chongzhi,keyflag=0;
- sbit k2=P3^5; //充值‘加值键(实现加1或者加10)
- sbit k3=P3^2; //刷卡’减值键(实现减1或者减10)
- sbit k4=P3^3; //确认键
- sbit nWR=P2^4; //74hc373片选
- /*****************1702函数声明**************************/
- uchar spi(uchar m);
- void fifo_clear();
- void resig_write(uchar reg,uchar da);
- uchar resig_read(uchar reg);
- uchar fifo_read(uchar count,uchar *s);
- void fifo_write(uchar count,uchar *s);
- uchar inti_1702();
- void card_halt();
- uchar request();
- uchar card_anticoll();
- uchar card_select();
- uchar card_authtication(uchar m) ;
- uchar card_read(uchar m);
- uchar card_write(uchar m);
- uchar loadkey();
- void delay(uchar m);
- /********************按键显示函数声明***************/
- void display();
- void keyscan() ;
- void data1deal() ;
- void data2deal(uchar m);
- void lcdclear();
- /***********************子函数解释******************/
- /*********************************************************/
- //解释: 这是spi总线的读写时序,所有的寄存器操作基于此时序,非常重要
- //
- //
- //输入:要写入的16进制参数
- //
- //
- //输出:内部传回的16进制参数
- //
- /********************************************************/
- uchar spi(uchar m)
- {
- uchar i,temp=0;
- for(i=0;i<8;i++)
- {
- sck=0;
- if(m&0x80)
- mosi=1;
- else
- mosi=0;
- m<<=1;
- sck=1;
- temp<<=1;
- if(miso)
- temp|=0x01;
- }
- sck=0;
- mosi=0;
- return temp;
- }
- /*********************************************************/
- //解释:写寄存器函数
- //
- //
- //输入:寄存器地址 以及要写入的参数
- //
- //
- //输出:
- //
- //注意!!在所有的资料中都没有给出寄存器寻址时的格式,下边的有,看仔细了,如果连寄存器都找不到,后边的就不用看了
- /********************************************************/
- void resig_write(uchar reg,uchar da)
- {
- sck=0;
- reg<<=1;
- cs=0;
- reg=reg&0x7e;
- spi(reg);
- spi(da);
- cs=1;
- }
- /*********************************************************/
- //解释:读寄存器函数
- //
- //
- //输入:寄存器地址
- //
- //
- //输出:该寄存器目前的值
- //
- //注意!!读写寄存器时指令不一样,仔细看。
- /********************************************************/
- uchar resig_read(uchar reg)
- {
- uchar temp;
- sck=0;
- _nop_();
- _nop_();
- cs=0;
- reg<<=1;
- reg|=0x80;
- spi(reg);
- temp=spi(0x00);
- cs=1;
- return temp;
-
- }
- /*********************************************************/
- //解释:FIFO缓衝器的读函数
- //
- //
- //输入:读取的字节个数 ,返回的值存放首地址
- //
- //
- //输出:读成功的话会返回真值,否则返回0
- //
- //
- /********************************************************/
- uchar fifo_read(uchar count,uchar *s)
- {
- uchar i,temp;
- temp=resig_read(FIFOLength);
- if(temp<count)
- return 0;
- else
- {
- for(i=0;i<count;i++)
- {
- temp=resig_read(FIFODaTa);
- *(s+i)=temp;
- }
- }
- return 1;
- }
- /*********************************************************/
- //解释:FIFO缓衝器的写函数
- //
- //
- //输入:写入的字节个数 ,要写入的值的存放首地址
- //
- //
- //输出:
- //
- //
- /********************************************************/
- void fifo_write(uchar count,uchar *s)
- {
- uchar i,temp;
- for(i=0;i<count;i++)
- {
- temp=*(s+i);
- resig_write(FIFODaTa,temp);
- }
- }
- /*********************************************************/
- //解释:芯片FM1702初始化函数
- //
- //
- //输入:
- //
- //
- //输出:初始化成功的话返回真值,否则返回0
- //
- //
- //注意:初始化步骤非常重要,一定要按照使用手册的启动步骤来,参考所给pdf
- /********************************************************/
- uchar inti_1702()
- {
- uchar temp,i;
- FM1702rst=1;
- mosi=1;
- sck=1;
- delay(20);
- FM1702rst=0;
- delay(20);
- while(resig_read(Command));
- resig_write(0x00,0x80);
- temp=resig_read(Command);
- while(temp);
- resig_write(0x00,0x00); //切换到线性寻址(此命令非常重要,否则无法启动发送 )
- resig_write(TimerClock,0x0b); //address 2AH /* 定时器週期设置寄存器 */
- resig_write(TimerControl,0x02); //address 2BH /* 定时器控制寄存器 */
- resig_write(TimerReload,0x42); //address 2CH /* 定时器初值寄存器 */
- resig_write(InterruptEn,0x7f); //address 06H /* 中断使能/禁止寄存器 */
- resig_write(InterruptRq,0x7f); //address 07H /* 中断请求标识寄存器 */
- resig_write(MFOUTSelect,0x02); //address 26H /* mf OUT 选择配置寄存器 */
- resig_write(TxControl,0x5b); //address 11H /* 发送控制寄存器 */
- resig_write(RxControl2,0x01);
- resig_write(RxWait,0x07);
- for(i=0;i<16;i++)
- {
- Fbuff[i]=0;
- Jbuff[i]=0;
- }
- lcdclear() ;
- if(temp==0x00)
- return 1;
- else
- return 0;
- }
- /*********************************************************/
- //解释:延时函数,单次延时为5ms
- //
- //
- //输入:要延时的次数
- //
- //
- //输出:
- //
- //
- /********************************************************/
- void delay(uchar m) //单次定时为10ms
- {
- TMOD=0x01;
- while(m--)
- {
- TH0=0xfe;
- TL0=0x33 ;
- TR0=1;
- while(!TF0);
- TF0=0;
- TR0=0;
- }
-
- }
- /*********************************************************/
- //解释:卡的回应函数,判断是否有卡在读写器跟前
- //
- //
- //输入:
- //
- //
- //输出:如果有卡在的话返回真值,否则返回0
- //
- //
- /********************************************************/
- uchar request()
- {
- uchar temp;
- resig_write(CRCResultLSB,0x63);
- resig_write(CWConductance,0x3f);
- resig_write(BitFraming,0x07);
- resig_write(ChannelRedundancy,0x03);
- temp=resig_read(Control);
- temp=temp&0x7f;
- resig_write(Control,temp);
- resig_read(FIFOLength);
- temp=resig_read(Control);
- temp=temp|0x01;
- resig_write(Control,temp);
- Fbuff[0]=0x52;
- fifo_write(1,Fbuff);
- resig_write(Command,0x1e);
- delay(1);
- temp=resig_read(FIFOLength);
- if(temp==0x02)
- return 1;
- else
- return 0;
- }
- /*********************************************************/
- //解释:防衝突函数 (我所做的此函数没有手册的那种功能,只是实现了单卡交易功能)
- //
- //
- //输入:
- //
- //
- //输出:防衝突成功的话返回真值,否则返回0
- //
- //实现功能:如果有多张卡在读卡器区域,只选中一张,并读取验证这张卡的ID,然后存储
- /********************************************************/
- uchar card_anticoll() //防衝突函数
- {
- uchar temp,i;
- resig_write(DecoderControl,0x28);
- resig_write(Control,0x08);
- resig_write(ChannelRedundancy,0x03);
- temp=resig_read(Control);
- temp=temp|0x01;
- resig_write(Control,temp);
- Fbuff[0]=0x93;
- Fbuff[1]=0x20;
- fifo_write(2,Fbuff);
- resig_write(Command,0x1e);
- delay(2);
- temp=resig_read(FIFOLength);
- if(temp==0x05)
- {
- temp=0;
- fifo_read(5,UID);
- delay(1);
- for(i=0;i<5;i++)
- {
- temp=temp^UID[i];
- }
- if(temp==0)
- return 1;
- else
- return 0;
- }
- else
- return 0;
- }
- /*********************************************************/
- //解释:选卡函数
- //
- //
- //输入:
- //
- //
- //输出:选卡成功的话返回真值,否则返回0
- //
- //实现功能:将要交易的卡号发给卡,如果回应正确则选卡成功
- /********************************************************/
- uchar card_select()
- {
- uchar i,temp;
- resig_write(ChannelRedundancy,0x0f);
- resig_write(Control,0x08);
- temp=resig_read(Control);
- temp=temp|0x01;
- resig_write(Control,temp);
- Fbuff[0]=0x93;
- Fbuff[1]=0x70;
- for(i=0;i<5;i++)
- {
- Fbuff[i+2]=UID[i];
- }
- fifo_write(7,Fbuff);
- resig_write(Command,0x1e);
- delay(2);
- temp=resig_read(FIFOLength);
- if(temp==0x01)
- return 1 ;
- else
- return 0 ;
- }
- /*********************************************************/
- //解释:三重认证函数
- //
- //
- //输入:要操作的块号
- //
- //
- //输出:如果三重认证的话返回真值,否则返回0
- //
- //实现功能:这是卡片与读卡器之间的默认通信协议,次程序执行成功后才可以与卡进行交易
- /********************************************************/
- uchar card_authtication(uchar m)
- {
- uchar i,temp;
- temp=resig_read(Control);
- temp=temp|0x01;
- resig_write(Control,temp);
- Fbuff[0]=0x60;
- Fbuff[1]=m;
- for(i=0;i<4;i++)
- {
- Fbuff[i+2]=UID[i];
- }
- fifo_write(6,Fbuff);
- resig_write(InterruptEn,0xa5);
- resig_write(Command,0x0c);
- delay(2);
- temp=resig_read(SecondaryStatus);
- temp=temp&0x07;
- if(temp==0)
- {
- temp=resig_read(Control);
- temp=temp|0x01;
- resig_write(Control,temp);
- resig_write(InterruptEn,0xa4);
- resig_write(Command,0x14);
- delay(2);
- temp=temp=resig_read(Control);
- temp&=0x08;
- if(temp==0x08)
- return 1;
- else
- return 0;
- }
- return 0;
- }
- /*********************************************************/
- //解释:读卡函数
- //
- //
- //输入:要读的考号
- //
- //
- //输出:读卡成功的话返回真值,否则返回0
- //
- //实现功能:读取指定卡号的内容,并传送给FIFO
- /********************************************************/
- uchar card_read(uchar m)
- {
- uchar temp;
- temp=resig_read(Control);
- temp=temp|0x01;
- resig_write(Control,temp);
- resig_write(ChannelRedundancy,0x0f);
- temp=resig_read(Control);
- temp=temp|0x01;
- resig_write(Control,temp);
- Fbuff[0]=0x30;
- Fbuff[1]=m;
- fifo_write(2,Fbuff);
- resig_write(Command,0x1e);
- delay(0x04);
- temp=resig_read(FIFOLength);
- if(temp==16)
- {
-
- fifo_read(16,Jbuff);
- return 1;
- }
- else
- return 0;
- }
- /*********************************************************/
- //解释:写卡函数
- //
- //
- //输入:要写入的卡号
- //
- //
- //输出:写卡成功的话返回真值,否则返回0
- //
- //实现功能:将发送缓衝区的数据写入指定的块号
- /********************************************************/
- uchar card_write(uchar m)
- {
- uchar temp;
- temp=resig_read(Control);
- temp=temp|0x01;
- resig_write(Control,temp);
- Fbuff[0]=0xA0;
- Fbuff[1]=m;
- fifo_write(2,Fbuff);
- resig_write(Command,0x1e);
- delay(2);
- temp=resig_read(FIFOLength);
- if(temp==1)
- {
- temp=resig_read(Control);
- temp=temp|0x01;
- resig_write(Control,temp);
- Fbuff[0]=Jbuff[0];
- fifo_write(16,Fbuff);
- resig_write(Command,0x1e);
- delay(10);
- temp=resig_read(FIFOLength);
- if(temp==1)
- return 1;
- else
- return 0;
- }
- else
- return 0;
- }
- /*********************************************************/
- //解释:加载密匙函数
- //
- //
- //输入:
- //
- //
- //输出:成功的话返回真值,否则返回0
- //
- //实现功能:建立与卡通信协议的第一步,将密匙发送给卡,验证密匙是否成功,成功的话执行三重认证
- /********************************************************/
- uchar loadkey()
- {
- uchar temp;
- temp=resig_read(Control);
- temp=temp|0x01;
- resig_write(Control,temp);
- fifo_write(12,changekey);
- resig_write(Command,0x19);
- delay(3);
- temp=resig_read(ErrorFlag);
- if(temp==0)
- return 1;
- else
- return 0;
- }
- /*********************************************************/
- //解释:设置卡的暂停态函数
- //
- //
- //输入:
- //
- //
- //输出:执行成功的话返回真值,否则返回0
- //
- //实现功能:将卡设置为终止交易态,若要再次交易必须从头重新开始认证
- /********************************************************/
- void card_halt()
- {
- uchar temp;
- resig_write(InterruptEn,0x7f);
- resig_write(InterruptRq,0x7f);
- resig_write(Command,0x00);
- temp=resig_read(Control);
- temp=temp|0x01;
- resig_write(Control,temp);
- Fbuff[0]=0x50;
- Fbuff[1]=0x00;
- fifo_write(2,Fbuff);
- resig_write(InterruptEn,0xbd);
- resig_write(Command,0x1e);
- delay(2);
- }
- /****************按键显示子函数解释********************/
- /*********************************************************/
- //解释:卡的高两位序列号函数
- //
- //
- //输入:
- //
- //
- //输出:
- //
- //实现功能:将卡的高两位序列号处理为16进制后存放在相应显示区
- /********************************************************/
- void data1deal() //序列号处理
- {
- uchar temp,i;
- for(i=0;i<4;i++)
- Data[i]=0;
- temp=UID[3];
- Data[1]=temp/16;
- Data[0]=temp%16;
- temp=UID[2];
- Data[3]=temp/16;
- Data[2]=temp%16;
- }
- /*********************************************************/
- //解释:显示数据处理函数
- //
- //
- //输入:
- //
- //
- //输出:
- //
- //实现功能:将要现实的数据处理为10进制后存放在相应显示区
- /********************************************************/
- void data2deal(uchar m) //显示数据及按键处理
- {
- uchar i,temp;
- for(i=0;i<4;i++)
- Data[i]=0;
- temp=m;
- Data[0]=temp%10;
- Data[1]=temp%100/10;
- Data[2]=temp/100;
- }
- /*********************************************************/
- //解释:按键扫描函数
- //
- //
- //输入:
- //
- //
- //输出:输出相应的交易种类以及交易数额(会在说明裡做详细说明)
- //
- //
- /********************************************************/
- void keyscan()
- {
- if(k2==0||k3==0)
- {
- if(k2==0)
- {
- delay(100);
- if(k2==0)
- delay(250);
- if(keyflag==1)
- {
- if(k2==1)
- {
- if(count==255)
- count=0 ;
- else
- count++;
- }
- if(k2==0)
- {
- while(!k2)
- display();
- if(count>=246)
- count=count+11;
- else
- count=count+10;
- }
- }
- else
- {
- chongzhi=1;
- shuaka=0;
- }
- }
- if(k3==0)
- {
- delay(100);
- if(k3==0)
- delay(250);
- if(keyflag==1)
- {
- if(k3==1)
- {
- if(count==0)
- count=255;
- else
- count--;
- }
- if(k3==0)
- {
- while(!k3)
- display();
- if(count<=9)
- count=245+count;
- else
- count=count-10;
- }
- }
- else
- {
- shuaka=1;
- chongzhi=0;
- }
-
- }
- }
- if(k4==0)
- {
- delay(180);
- if(k4==0)
- keyflag=keyflag+1;
- }
- }
- /*********************************************************/
- //解释:显示函数
- //
- //
- //输入:
- //
- //
- //输出:
- //
- //
- /********************************************************/
- void display()
- {
- unsigned char time=0;
- nWR=0;
- P2=0x0f;
- WR=0;
- P0=seg[Data[3]];
- WR=1;
- P2=0X0e;
- time=5000;
- while(time--);
-
- nWR=0;
- P2=0x0f;
- WR=0;
- P0=seg[Data[2]];
- WR=1;
- P2=0X0d;
- time=5000;
- while(time--);
-
- nWR=0;
- P2=0x0f;
- WR=0;
- P0=seg[Data[1]];
- WR=1;
- P2=0X0b;
- time=5000;
- while(time--);
-
- nWR=0;
- P2=0x0f;
- WR=0;
- P0=seg[Data[0]];
- WR=1;
- P2=0X07;
- time=5000;
- while(time--);
-
- }
- /*********************************************************/
- //解释:lcd清屏函数
- //
- //
- //输入:
- //
- //
- //输出:
- //
- /********************************************************/
- void lcdclear()
- {
- nWR=0;
- P2=0x0f;
- WR=0;
- P0=0xff;
- WR=1;
- P2=0X0e;
-
- nWR=0;
- P2=0x0f;
- WR=0;
- P0=0xff;
- WR=1;
- P2=0X0d;
- nWR=0;
- P2=0x0f;
- WR=0;
- P0=0xff;
- WR=1;
- P2=0X0b;
-
- nWR=0;
- P2=0x0f;
- WR=0;
- P0=0xff;
- WR=1;
- P2=0X07;
- }
- /*****************************主函数****************************/
- void main()
- {
- uchar temp,p,i,j;
- temp=inti_1702();
- while(1)
- {
- switch(temp)
- {
- case 0: p=inti_1702();if(p) temp++;else temp=0;break;
- case 1: p=request(); if(p) {temp++;led5=~led5;}else temp=1;break;
- case 2: p=card_anticoll(); if(p){ temp++;led6=~led6;}else temp=1;break;
- case 3: p=card_select(); if(p) temp++; else temp=1;break;
- case 4: p=loadkey();if(p){temp++;led7=~led7;} else temp=1;break;
- case 5: p=card_authtication(PICC_BLOCK);if(p){ temp++;led8=~led8;}else temp=1;break;
- case 6: p=card_read(PICC_BLOCK);
- if(p)
- {
- temp++;
- data1deal();
- for(i=0;i<2;i++)
- for(j=0;j<150;j++)
- display();
- data2deal(Jbuff[0]);
- i=10;
- j=200;
- while(i--)
- {
- while(j--)
- { display();
- keyscan();
- if(shuaka||chongzhi)
- { data2deal(count);
- display();
- if(keyflag==0x02)
- {
- if(shuaka)
- Jbuff[0]=Jbuff[0]-count ;
- if(chongzhi)
- Jbuff[0]=Jbuff[0]+count;
- keyflag=0;
- shuaka=0;
- chongzhi=0;
- count=0;
- j=0;
- i=0;
- }
- else
- j=100;
- }
-
- }
- }
-
- }
- else
- temp=0;break;
- case 7: p=card_write(PICC_BLOCK);if(p){temp++;led9=~led9;} else temp=0;break;
- case 8: p=card_read(PICC_BLOCK);
- if(p)
- {
- temp++;
- data1deal();
- for(i=0;i<2;i++)
- for(j=0;j<150;j++)
- display();
- data2deal(Jbuff[0]);
- i=5;
- j=200;
- while(i--)
- {
- while(j--)
- display();
- }
- lcdclear() ;
- }
- else
- temp=0; break;
- case 9: card_halt();temp=0;break;
- default: break;
-
- }
- }
- }
复制代码
所有资料51hei提供下载:
基於51的FM1702射頻卡.rar
(3.26 MB, 下载次数: 94)
|