韦根协议及IO模拟韦根34接口

1.写在前面

韦根(Wiegand)协议是国际上统一的标准,是由摩托罗拉公司制定的一种通讯协议。它适用于涉及门禁控制系统的读卡器和卡片的许多特性。 韦根有许多种类格式,常见有26-bit 、34-bit、37-bit格式,其中26-bit格式是最常用的格式。而标准26-bit 格式是一个开放式的格式,这就意味着任何人都可以购买某一特定格式的HID卡,并且这些特定格式的种类是公开可选的。26-Bit格式就是一个广泛使用的工业标准,并且对所有HID的用户开放。几乎所有的门禁控制系统都接受标准的26-Bit格式。

2.韦根接口

Wiegand接口通常由3根线组成,它们分别是:数据0(Data0),数据1(Data1)和 Data return。这3条线负责传输Wiegand信号。D0,D1在没有数据输出时都保持+5V高电平。若输出为0,则D0持续输出一段低电平,若输出为1,则D1持续输出一段高电平。两个电子卡韦根输出之间的最小间隔为0.25秒。

3.韦根34协议

韦根协议及IO模拟韦根34接口_第1张图片

Wiegand 34各数据位的含义如下:

第 1 位: 为输出第2—17位的偶校验位

第 2-17 位: ID卡的HID码

第18-33位: ID卡的PID号码

第 34 位: 为输出第18-33位的奇校验位

数据输出顺序:HID码和PID码均为高位在前,低位在后。

 

例:一张ID卡内容为:

HID:32769   PID:34953  (假设卡面印字为:2147584137   001   34953 )

相应的二进制为:

HID:1000 0000 0000 0001

PID:1000 1000 1000 1001

输出如下:

12                     1718                     33  34

0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 10 0 0 1 0 0 0 1 0 0 1   0

|       HID_L            |          PID            |

 

4.韦根34接收程序(stm32)

一般情况下,很少MCU提供硬件韦根接口,但可以根据上述韦根协议的标准,结合时序图,通过MCU IO口读取电平变化解析韦根数据。 IO口解析时序电平,有IO中断方式和循环查询读取方式,推荐使用IO中断的方式,保证数据接收实时性和准确性。以下代码为中断方式实现。

头文件

#ifndef _WIEGAND_H_
#define	_WIEGAND_H_

// Wiegand 数据格式
#define		WG_DATA_BITS			34	//韦根34格式

// Wiegand 数据线接口
#define 	WIEGAND_PORT   			GPIOB
#define 	WIEGAND_DATA1_GPIO		GPIO_Pin_13
#define 	WIEGAND_DATA0_GPIO		GPIO_Pin_12
#define 	WIEGAND_RCC_PORT  		RCC_APB2Periph_GPIOB
#define		WIEGAND_DATA1			PBin(13)
#define		WIEGAND_DATA0			PBin(12)

// 外部函数
extern void WiegandInit(void);
extern void IDDataPrintf(void);

#endif

源文件


#include "stm32f10x.h"
#include "wiegand.h"

#define USEING_INTERR				//使能中断方式

static u8 u_EvenCheck = 2;			//偶检验
static u8 u_OddCheck  = 2;			//奇校验
static u8 u_EvenNums  = 0;			//偶校验软件比较
static u8 u_OddNums   = 0;			//奇校验软件比较
static u8 u_DataBits  = 0;			//当前接收数据位数
static u8 IDData[4];				//4字节ID号

#ifdef USEING_INTERR				
/***********************************************************
 * 函数名:WiegandInit
 * 功能  :外部中断引脚初始化 
 * 输入  : 无
 * 输出  :无
**********************************************************/
void WiegandInit(void)
{
  	GPIO_InitTypeDef GPIO_InitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

  	RCC_APB2PeriphClockCmd(WIEGAND_RCC_PORT | RCC_APB2Periph_AFIO,ENABLE);

  	GPIO_InitStructure.GPIO_Pin  = WIEGAND_DATA0_GPIO | WIEGAND_DATA1_GPIO;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;		  		//下拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_Init(WIEGAND_PORT, &GPIO_InitStructure);

	//Data0 中断线	PB12
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource12); 
  	EXTI_InitStructure.EXTI_Line	= EXTI_Line12;
  	EXTI_InitStructure.EXTI_Mode 	= EXTI_Mode_Interrupt;	
  	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//下降沿触发
  	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  	EXTI_Init(&EXTI_InitStructure);	 
	
	//Data1 中断线  PB13
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource13);
	EXTI_InitStructure.EXTI_Line	= EXTI_Line13;
  	EXTI_InitStructure.EXTI_Mode 	= EXTI_Mode_Interrupt;	
  	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;	  	//下降沿触发
  	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  	EXTI_Init(&EXTI_InitStructure);		

   	//中断优先级,尽可能设为最高优先级
  	NVIC_InitStructure.NVIC_IRQChannel 				= EXTI15_10_IRQn;
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;	//抢占优先级 
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority 	= 0x00;			//子优先级
  	NVIC_InitStructure.NVIC_IRQChannelCmd 			= ENABLE;		
  	NVIC_Init(&NVIC_InitStructure); 
}

/***********************************************************
 * 函数名:EXTI15_10_IRQHandler
 * 功能  :两数据线中断函数
 * 输入  : 无
 * 输出  :无
**********************************************************/
void EXTI15_10_IRQHandler(void)
{		 
    if(EXTI_GetITStatus(EXTI_Line12) != RESET)
	{//Data0-> 低电平表示1位0
		if (u_DataBits == 0)
		{//偶校验
			u_EvenCheck	= 0;
		}
		else if (u_DataBits == (WG_DATA_BITS -1))
		{//奇校验
			u_OddCheck = 0;
		}
		else
		{//数据,4字节、高位在前
			IDData[(WG_DATA_BITS - 2 - u_DataBits) / 8] &= ~(0x1 << ((WG_DATA_BITS - 2 - u_DataBits) % 8));	
		}
		u_DataBits++; 
		EXTI_ClearITPendingBit(EXTI_Line12); 
	}
	else if(EXTI_GetITStatus(EXTI_Line13) != RESET)
	{//Data1 -> 低电平表示1位1
		if (u_DataBits == 0)
		{//偶校验 
			u_EvenCheck = 1;
		}
		else if (u_DataBits == (WG_DATA_BITS -1))
		{ //奇校验
			u_OddCheck = 1;
		}
		else 
		{//数据,4字节,高位在前
			IDData[(WG_DATA_BITS - 2 - u_DataBits) / 8] |= (0x1 << ((WG_DATA_BITS - 2 - u_DataBits) % 8));
			if(u_DataBits < WG_DATA_BITS / 2)
				u_EvenNums++;					//计算1的个数来作偶校验
			else if(u_DataBits < WG_DATA_BITS - 1)								  	
				u_OddNums++;					//计算1的个数来作奇校验
		}
		u_DataBits++; 
		EXTI_ClearITPendingBit(EXTI_Line13);	
	}	 
}

/***********************************************************
 * 函数名:Check
 * 功能  :奇偶校验,确定读出数据书否正确 
 * 输入  : 无
 * 输出  :0->校验成功,数据有效    1->校验失败,数据无效
**********************************************************/
u8 DataCheck(void)
{
	u8 oddcheck,evencheck;

	if(u_DataBits >= WG_DATA_BITS)
	{//数据接收完才校验
		//u_DataBits = 0;		
		if(u_EvenNums % 2 == 0)
			evencheck = 0;					//偶数个1
		else							  	
			evencheck = 1;					//奇数个1
		if(u_OddNums % 2 == 0)
			oddcheck = 1;					//偶数个1
		else 
			oddcheck = 0;					//奇数个1 
		u_EvenNums = 0;
		u_OddNums  = 0;				    	//清零
		if((u_EvenCheck == evencheck) && (u_OddCheck == oddcheck))	
		{//校验成功
			u_EvenCheck = 2;
			u_OddCheck  = 2;
			return 0;						//和实际的校验码一起返回成功
		}
		else
		{	//校验失败
			u_EvenCheck = 2;
			u_OddCheck  = 2;
			return 1;
		}	
	}
	else
		return 2;	
}
#else  //查询方式
/***********************************************************
 * 函数名:WiegandInit
 * 功能  :Wiegan数据线引脚初始化, 
 * 输入  : 无
 * 输出  :无
**********************************************************/
void WiegandInit(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

  	RCC_APB2PeriphClockCmd(WIEGAND_RCC_PORT | RCC_APB2Periph_AFIO,ENABLE);
  	GPIO_InitStructure.GPIO_Pin  = WIEGAND_DATA0_GPIO | WIEGAND_DATA1_GPIO;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  	GPIO_Init(WIEGAND_PORT, &GPIO_InitStructure);
}
/***********************************************************
 * 函数名:GetWiegandData
 * 功能  :查询方式获取Wiegand数据
 * 输入  : 无
 * 输出  :无
**********************************************************/
u8 GetWiegandData(void)
{	


}
#endif

/***********************************************************
 * 函数名:IDDataPrintf
 * 功能  :串口打印结果测试
 * 输入  : 无
 * 输出  :无
**********************************************************/
void IDDataPrintf(void)
{
	u8 CheckFlag = 0;
	u8 Buff[6],i;

	if(u_DataBits >= WG_DATA_BITS)
	{
		CheckFlag = DataCheck();
		if(CheckFlag == 0)
		{	
			USART_printf(USART1,"%s " "%d " "%d " "%d " "%d ","ID:", IDData[0],IDData[1],IDData[2],IDData[3]);
			USART_send_string("\n");
			USART_send_string("DataBits/Check: ");
			USART_printf(USART1, "%d " "%d\n ",u_DataBits,CheckFlag);	
		}	 
		else if(CheckFlag == 1)
		{
			USART_send_string("Read Data Failed\n");
		}
		u_DataBits = 0;
	}		
}

代码简要分析:

1)只实现“IO中断”方式解析韦根数据;

2)数据解析在“EXTI15_10_IRQHandler”中断函数中,主要将电平解析成实际数据,这部分执行时间不长,在中断处理可以接受;

3)“DataCheck”为数据校验函数,主要验证解析数据的正确性;

4)“IDDataPrintf”为测试用,连接串口,将结果打印至电脑串口助手;自行实现串口打印代码,或者直接接仿真器在线查看查看解析到的数据。

 

5.注意事项

1)采用IO中断的方式接收解析,查询方式可能响应不过来;

2)采用IO中断方式时,尽可能将中断优先级设为最高,防止被其他中断打断,导致数据出错;

3)IO中断中处理事务尽可能少,减少耗时;

4)连接韦根数据线的IO口必须加RC滤波,从硬件上增加抗干扰能力。

6.参考

[1]  https://baike.baidu.com/item/韦根协议/9914978?fr=aladdin

 

 

你可能感兴趣的:(C,算法,MCU开发,物联网)