Chinaunix首页 | 论坛 | 博客
  • 博客访问: 165595
  • 博文数量: 73
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 235
  • 用 户 组: 普通用户
  • 注册时间: 2014-06-27 09:43
个人简介

为兴趣挑灯夜战

文章分类
文章存档

2018年(4)

2017年(7)

2016年(9)

2015年(4)

2014年(49)

分类: 嵌入式

2017-03-12 04:27:40


一、简介:
      
首先看看RFID标签长什么样:
                                                                   
        这张图片叫做晶圆,上面秘密麻麻的刻好的RFID芯片的裸片,还未封装,就图中这一块晶圆上就有2万颗RFID芯片,把每一块芯片取下来固定到胶片上,加上天线焊盘的后的芯片叫绑定片,如下图中第一张图中线圈中心的那个黑块。再加上最外层的封装后做成卡片状、米粒状、扣状等等,但不论其外形如何,内部都是一个线圈加一个RFID芯片,如左边第一张图一样,芯片中间黑色的就是芯片,芯片通过线圈从RFID设备获取能量,原理和变压器是一会事,RFID产生电场,耦合到RFID芯片的线圈,给RFID芯片提供能量;同时,线圈也用于通信作用,RFID芯片或RFID设备通开启和关闭线圈上的电场来发送信号,这里并不是关闭电场就代表0,开启电场就代秒1,而是有一定的编码方式,比如说曼切斯特编码(一下简称曼码)。
  

二、编码简介
下面就先来看看曼码的描述,此部分内容来源于RFID世界网。     
曼彻斯特编码也被称为分相编码(Split-Phase Coding)。在曼彻斯特编码中,某位的值是由该位长度内半个位周期时电平的变化(上升/下降)来表示的,在半个位周期时的负跳变表示二进制“1”,半个位周期时的正跳变表示二进制“0″,如下图所示。曼彻斯特编码在采用负载波的负载调制或者反向散射调制时,通常用于从电子标签到读写器的数据传输,因为这有利于发现数据传输的错误。这是因为在位长度内,“没有变化”的状态是不允许的。当多个电子标签同时发送的数据位有不同值时,接收的上升边和下降边互相抵消,导致在整个位长度内是不间断的副载波信号,由于该状态不允许,所以读写器利用该错误就可以判定碰撞发生的具体位置。
                                                   
                                      
上面的文字描述要慢慢理解,简单的来说就是RFID线圈的电场在一个周期的中间时关闭代表1,在一个周期的中间开启代表0,要注意的一点是RFID标签线圈和RFID设备的线圈上的0和1恰好是相反的。在应用中,我们只关心RFID设备上线圈的信号。

在低频RFID中,有一个词叫ID64,是什么意思呢,其中ID就是identity,身份的意思,64就是说用于表示这个身份的号码是64个字节,也就是一个ID号而已,比如市面上现在很多低端的门禁锁,用的就是ID64.下面就以EM4100为例来介绍。从EM4100的手册中,可知其操作频率范围是100k-150Hz,具体使用的频率是要看我们给他匹配的线圈,但这是RFID制卡商的事了,一般是125HZ或134KHz.其他不多说,先来看看EM4100的数据结构:
                                             ID64的数据结构是,9位的头+8位版本号或簇ID+32位数据,最右边的一列的P0-P9每一位对应左边Dx0-Dx3四位数据的奇校验,最后以行的PC0-PC3对应上方D0X-D9X位的奇校验。最后一位S0固定为0.
这款RFID芯片的编码模式有三种:Manchester、Biphase Code和PSK Code,有生产时配置好,配置编码模式的指令,这些配置的指令一般都是保密的,只有制卡厂家有修改这些配置的设备,所以具体怎么配置我们就不用管了,这里来介绍一下编码模式,Manchester(曼码)前面已介绍过了。
                
                               biphase(双相码)编码的特点就是信号1在周期结束时电场跳转(开启或关闭)在一个周期的中间不跳转,而信号0的电场在周期中间和结束都有跳转。

                           

PSK(相移键控)就是用相位的改变表示信号从0到1的改变。

三、RFID设备硬件设计

为了方便,之后称RFID设备为读写器,设计低频读卡器,可以用用三极管、二极管、电容、电阻、线圈设计成发射电路和检波电路,发射电路很简单,其原理就是LC振荡器,一个电容C和一个电感 L的串联,再加一个三极管来驱动,检波电路绕为复杂一点,但也不难,就是滤波+信号转换废话少说,直接上图:

  图中的C1在实际中要在理论值附近调整时期达到最优,      如果不想麻烦,直接用专用解码IC就行了,电路简单易调,比如EM4095.
        EM4100卡工作在134KHz,所以直接用单片机的IO输出134KHz的方波来去动TX,电容C1和L在驱动节拍下充电放电下,只要L和C的谐振频率和驱动信号的频率相近,LC电路就会振荡起来,电感上的电压能达到上百伏,但不用当心安全问题,因为功耗很小,不会伤到人。当EM4100卡进入电感 L也就是天线的电场内时,EM4100通过其天线和读卡器的天线耦合获取到足够能量后,就开始向读卡器发送自己64位的信号。在调制信号的作用下下,输入二极管端的是包络波形,经过二极管和R2、C2和C3组成的检波电路后就得到我们先要的有用信号。
 读卡器天线上发送信号时的波形如下:

继续放大后是这样的:

天线端的信号时这样的,图中蓝色的那个。

通过检波电路后的波形是上图图中黄色波形,再来看一张检波后的波形


四、解码程序
  解码就是要将标签的信号按曼码的格式逐位解出来,然后再验证校验位,校验通过后,说明数据接收正确,解码的方式有两种,一种是先判断头部,是否为9个1,如果判断头部通过,再继续接收后面的数据,如果不通过,则重新从下一次头部判断,另一种是按64位数据帧长的时间抓取两贝帧长时间的数据,然后再从在一段数据中查找帧头(9个1),查到帧头后就就继续读枕头的数据,如果查不到,则重新下一次数据抓取。


通过位速率,比如64时钟周期,就是64个时钟周期才传送一个bit,那在125kHz时,一个比特的时间512us,半个位的时间是256us,在上图中,宽的用红色数字标记一个0或1的正脉宽或负脉宽就是半个周期的时间没也就是256us,但在实际中这个值是有偏差的,所以要设定一个容限,若在接收的过程中那个比特的时间超出容限值,就结束本次接收数据,重新再接收,接收数据时,如记录到一个正脉宽的时间在大于一个周期小于一个周期的容限内,则在接收缓冲区里记录下11,若是负脉宽则记录00,如记录到半个周期时间容限的正脉宽,则记录一个1,若是负脉宽在记录一个0,如上图中标记所示,这样做是为了方便解码。这个码就叫它为曼码。
曼码接收函数:这是用EM4095解码芯片设计的解码电路,接收时不判断头部,直接抓取数据存缓冲区。
uint8 ReceiveMan(uint8 *mandata,uint16 datanum,uint8 maxfullT,uint8 minfullT,uint8 maxhalfT,uint8 minhalfT)
{
    uint8 i=0,j=0,clkcount=0;
    uint16 bitcount=0;
    TH0=0;TL0=0;TR0=1;
    while(bitcount  
   {
             modout=!modout;//mudout j接EM4095de的DEMOD_OUT脚,用51单片机计时器计算此脚的电平翻转时间来确定接受到的数据。
             while(DEMOD_OUT==modout)
             {
                if (TL0>maxfullT)
                 {
                     TR0 = 0 ; clkcount=TL0;
                    if(bitcount==(datanum-3))//the end number is 01
                    {
                        mandata[i]<<=1;
                        mandata[i]|=modout;  
                        mandata[i]<<=1;
                        mandata[i]|=modout;
                         bitcount=bitcount+3;
                         return ok;
                    }
                    else if(bitcount>(datanum-3))//the end number is 11
                    {
                        mandata[i]<<=1;
                        mandata[i]|=modout;  
                         bitcount=bitcount+3;
                         return ok;
                    }
                     return fail ;
                 }
             }
              TR0=0;
              clkcount=TL0;
              TL0=0;TR0=1;
             
              if((clkcount>=minfullT)&&(clkcount<=maxfullT))  //received 1 bit
                 {
                     mandata[i]<<=1;
                     mandata[i]|=modout;     
                     j++;
                     if (j>=8)
                     {
                         i++;
                         j=0;
                     }
                   mandata[i]<<=1;
                   mandata[i]|=modout;     
                   j++;
                   if (j>=8)
                   {
                       i++;
                       j=0;
                   }
                   bitcount=bitcount+2;
                 }
             else if((clkcount>=minhalfT)&&(clkcount<=maxhalfT)) //receive half bit
             {
                  mandata[i]<<=1; //input the bit into allhex;
                  mandata[i]|=modout;
                  j++;
                  if (j==8)
                  {i++; j=0; }
                  bitcount=bitcount+1;

             }    
             else
             {
                  TR0=0;
                 return fail ;
             }
     }
    return ok;
}

然后我们要将曼码转换成二进制bit码,我们就直接看转换函数了。

//sourcebuf为曼码缓存区
uint8 ManTohex(uint8 num,uint8 *sourcebuf,uint8 *destbuf)//manchester to hex
{
    uint8 i=0,j=0,m=0,manchk=0;
    uint8 bitcount=0;
    for (i=0;i     {
        for (j=0;j<4;j++)//一次检查2个bit,一个字节4次检查完,故循环4次
         {
                 manchk=sourcebuf[i]&0xc0;//1100 0000b
                 if(manchk==0x40)         //被检测的两位为01b,则该曼码为0
                    {
                        destbuf[m]>>=1;         
                        bitcount++;
                        if (bitcount==8)
                        {m++; bitcount=0;}
                        sourcebuf[i]<<=2;
                    }
                 else if (manchk==0x80)         //被检测的两位为10b,则改为曼码为1
                    {
                        destbuf[m]>>=1;
                        destbuf[m]|=0x80;
                        bitcount++;
                        if (bitcount==8)
                                {m++; bitcount=0;}
                        sourcebuf[i]<<=2;
                    }
                 else//若被检测的两位为00或11,说明是错误的位,结束转换。
                 {
                    return fail ;
                 }
         }
     }
    return ok;
}

  通过简单的注释不难理解代码的逻辑,在此就不多讲了。接下来就是查找头部9个1,然后去调头部的九个1,如果接收时是先判断头部再接收,此步可以跳过。
//delay_50us(10);
//*******************************//
// push "111111111" to first byte//
//*******************************//
for (i=0;i<64;i++)
 {
   emchk=emdata[0]&0x80;
   byte0=0;
   if (emchk==0x80)
   {byte0=1;}

   for(j=0;j<8;j++)
    {
    emdata[j]<<=1;
    emchk=emdata[j+1]&0x80;
    if (emchk==0x80 && j<7)
      {emdata[j]|=0x01;}
    }
    emdata[7]|=byte0;
    if (emdata[0]==0xff && emdata[1]>=0x80)
     {i=69;}
 }
//delay_50us(10);
//   LED_GREEN = 0;

if (i!=70)
{TR0 = 0 ; return Fail ;}//{goto r_round;}
//---check the chksum if the last bit not 0,error.      //20020610
     chksum=emdata[7];
     emchk=chksum&0x01;
      if (emchk==1)
         {TR0 = 0 ; return Fail ;}//{goto r_round;}
      chksum>>=1;
//*******************************//
// take away "111111111"         //
//*******************************//
for (i=0;i<9;i++)
 {
   emchk=emdata[0]&0x80;
   byte0=0;
    if (emchk==0x80)
   {byte0=1;}

   for(j=0;j<8;j++)
    {
    emdata[j]<<=1;
    emchk=emdata[j+1]&0x80;
    if (emchk==0x80 && j<7)
      {emdata[j]|=0x01;}
    }
    emdata[7]|=byte0;
 }
然后执行行奇校验和列奇校验,若果校验通过,则接收数据正确。下面是别人进行校验的函数,我当初直接就用了,也没有自己写这一部分:
//********************************//
//four bit data and one bit parity//
//********************************//
if(emdata[0]!=0x96)
   i=i;
byte1=0;
for (i=0;i<5;i++)
    {
        for (j=0;j<10;j++)
         {
              byte0=0;
              if ((emdata[0]&0x80)==0x80)
              {byte0=1;}
               for (m=0;m<8;m++)
               {
                 emdata[m]<<=1;
                 emchk=emdata[m+1]&0x80;
                 if (emchk==0x80 && m<7)
                 {emdata[m]|=0x01;}
               }

               if((j!=4) && (j!=9))
               {allhex[i]<<=1;
                allhex[i]|=byte0;
                byte1=byte1^byte0;
               }
               if((j==4) || (j==9)) //check row parity
               {
                if(byte1!=byte0)
                 {TR0 = 0 ; return Fail ;}//{goto r_round;}
                byte1=0;
               }
        }
   }
 emdata[0]=allhex[0];
 for(i=1;i<5;i++)
     emdata[0]=emdata[0]^allhex[i];
     emdata[0]=(emdata[0]>>4)^emdata[0];
     emdata[0]=emdata[0]&0x0f;
     chksum=chksum&0x0f;
      if(emdata[0]==chksum)
        {
         for(i=0;i<5;i++)
           { hcode[i]=allhex[i];}
        
         return Ok;
        }
       else
        {TR0 = 0 ; return Fail ;}// goto r_round;

                       

阅读(2065) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~