英创Linux工控主板对韦根信号的支持

 2016-6-1         

  Wiegand(韦根)协议是由摩托罗拉公司定制的一种通讯协议,它适用于涉及门禁控制系统的读卡器和卡片的许多特性。韦根数据输出由两条数据线DATA0和DATA1,和公共的信号地GND组成。在没有数据输出时,DATA0和DATA1都保持高电平(典型为+5V电平),若输出'0'时,DATA0输出低脉冲而DATA1保持为高电平,输出'1'时,DATA1输出低脉冲而DATA0保持为高。典型的低脉冲宽度为50us,输出每一bit之前的间隔为1ms(如下图,实际的信号电平和时序由实际的韦根读卡器决定)。


Wiegand.gif


  韦根协议包含很多种格式来传输串行数据,英创公司工控主板支持最常用的韦根26 bit和韦根34 bit格式。韦根26是已经广泛使用的通用工业校准,一个“韦根包”有26位数据,第1位为第1到第13位的偶校验,最后1位为第14到第26位的奇校验,中间24位为数据位。


Wiegand.gif


  对于韦根34格式,即一个“韦根包”有34位数据,常见的格式为第1位为第1到第17位的偶校验,最后1位为第18到第34位的奇校验,中间32位为数据位。


  英创公司为Linux工控主板提供了支持韦根协议的设备驱动模块,在命令行输入insmod wiegand-gpio.ko即完成驱动程序的加载。linux韦根驱动支持应用程序通过非阻塞的轮询操作(select)和异步通知的方式读取韦根数据,这里我们建议使用非阻塞轮询方式。以非阻塞方式O_NONBLOCK打开设备文件后,使用num = read(fd,buffer,len)读取韦根数据,只读取Site Code和User Code,不包含奇偶校验位,各参数意义如下:


  输入参数:

  fd:int, 韦根设备文件描述符;

  len:buffer长度,固定4字节,小于4字节提示错误;


  输出参数:

  buffer:char *, read结果缓存,固定4字节,根据不同返回值num,具有不同意义;


  返回值:

  num:int, 读取成功时,表示读取的字节数,具体意义如下:

    num = 4,读取成功,数据格式为wiegand 34,buffer [0-1]为Site Code,buffer [2-3]为User Code;

    num = 3,读取成功,数据格式为wiegand 26,buffer [0]=0,buffer [1]为Site Code,buffer [2-3]为User Code;

    num = 1,读取成功,数据格式为键盘按键(部分读卡器有此功能),buffer [0]=0, buffer [1]为按键值;

    num = -1,读取失败,buffer [0]存错误码:

      -1  //格式错误,长度不匹配

      -2  //偶校验错误

      -3  //奇校验错误

      -4  //用户传入buffer空间太小


  英创各个主板连接韦根信号的定义如下:


 Wiegand_DATA0Wiegand_DATA1
ESM928x / ESM335x / EM335xGPIO14GPIO15
EM9280 / EM9281 / EM9287GPIO26GPIO27


  韦根读卡器通常输出5V TTL电平,而英创工控主板的GPIO要求输入电平不能超过3.3V,所以韦根读卡器输出的信号需要经过转换后才能与英创主板的GPIO相连。下图是一个简单的5V转3.3V的电平转的电路,WG_DATA0和WG_DATA1为韦根读卡器输出信号,注意要将韦根读卡器与英创工控主板共地。


Wiegand.gif


  以下是读取韦根数据的应用程序示例代码:


  #include <stdio.h>

  #include <unistd.h>

  #include <signal.h>

  #include <pthread.h>

  #include <stdlib.h>

  #include <fcntl.h>

 

  #define WIEGAND_ERROR_FORMAT -1               //格式错误,长度不匹配

  #define WIEGAND_ERROR_EVEN_PARITY -2        //偶校验错误

  #define WIEGAND_ERROR_ODD_PARITY -3         //奇校验错误

  #define WIEGAND_ERROR_LESS_BUF -4            //用户传入buf空间太小

 

  int exitflag;   //退出标志

  char buffer[4]; //read结果

 

  int readWiegandThreadFunc(void* lparam)

  {

      int fd = * (int*)lparam;

      fd_set fdRead;

      struct timeval aTime;

      int ret;

      while(1)

      {

          FD_ZERO(&fdRead);

          FD_SET(fd,&fdRead);

          aTime.tv_sec = 2; //s

          aTime.tv_usec = 0; //us

          ret = select ( fd+1, &fdRead, NULL, NULL, &aTime );

          if ( ret>0 )

          {

              if ( FD_ISSET(fd, &fdRead) )

              {

                  //用户可以在此加入处理操作

                  int num = 0;

                  num = read(fd,buffer,4);

                  if(num < 0)

                  {

                      int errorCode = (signed char)buffer[0];

                      printf("ERROR:read failed! num: %d\n",num);

                      switch(errorCode)

                      {

                          case WIEGAND_ERROR_FORMAT:

                              printf("ERROR CODE: %d, wiegand data format did not match wiegand-26,weigand-34 or 4 bits for keyboard!\n",errorCode);

                              break;

                          case WIEGAND_ERROR_EVEN_PARITY:

                              printf("ERROR CODE: %d, wiegand data even parity was wrong!\n",errorCode);

                              break;

                          case WIEGAND_ERROR_ODD_PARITY:

                              printf("ERROR CODE: %d, wiegand data odd parity was wrong!\n",errorCode);

                              break;

                          case WIEGAND_ERROR_LESS_BUF:

                              printf("ERROR CODE: %d, buffer was too small, please set it to 4 bytes!\n",errorCode);

                              break;

                          default:

                              printf("ERROR CODE: %d, Unknown Error!\n",errorCode);

                              break;

                      }

                  }

                  else

                  {

             printf("result: %x,%x,%x,%x\n",buffer[0],buffer[1], buffer[2], buffer[3]);

                  }

              }

          }

          if ( exitflag==1 )//exitflag在程序其他线程中改变

          {

              printf("exit!\n");

              break;

          }

      }

      pthread_exit( NULL );

      return 0;

  }

 

  int main(int argc, char* argv[])

  {

      int fd;

      int i = 0;

      exitflag = 0;

 

      fd = open("/dev/em9280_wiegand",O_RDONLY | O_NONBLOCK);

      if (fd  < 0)

      {

          printf("ERROR:open failed!\n");

          return -1;

      }

 

      pthread_attr_t      attr;

      pthread_t           m_thread;

      int                 res;

  

      res = pthread_attr_init(&attr);

      if( res!=0 )

      {

          printf("Create attribute failed\n" );

      }

      // 设置线程绑定属性

      res = pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );

      // 设置线程分离属性

      res += pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );

      if( res!=0 )

      {

          printf( "Setting attribute failed\n" );

      }

      // 创建select线程,在此线程中读韦根数据

      res = pthread_create( &m_thread, &attr, (void *(*) (void *))&readWiegandThreadFunc, &fd );

      if( res!=0 )

      {

          return -1;

      }

 

      pthread_attr_destroy( &attr );

      while(1)

      {

          //执行其他任务

          printf("%d\n",i++);

          sleep(1);

          if (i==50)

          {

              exitflag = 1;

              printf("over\n");

              break;

          }

      }

      close(fd);

      return 0;

  }