1、写在前面
韦根(Wiegand)协议是国际上统一的标准,是由摩托罗拉公司制定的一种通讯协议。它适用于涉及门禁控制系统的读卡器和卡片的许多特性。 它有很多格式,标准的26-bit 应该是最常用的格式。此外,还有34-bit 、37-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协议
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,测试过可用!)
- #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
#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; //当前接收数据位数
- 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)
- {
- <span style="white-space: pre;"> </span>if(EXTI_GetITStatus(EXTI_Line12) != RESET)
- <span style="white-space: pre;"> </span>{//Data0-> 低电平表示1位0
- <span style="white-space: pre;"> </span>if (u_DataBits == 0)
- {//偶校验
- u_EvenCheck = 0;
- }
- else if (u_DataBits == (WG_DATA_BITS -1))
- {//奇校验
- u_OddCheck = 0;
- }
- else
- {//数据,4字节、高位在前
- <span style="white-space: pre;"> </span>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)
- <span style="white-space: pre;"> </span>evencheck = 0; //偶数个1
- else
- <span style="white-space: pre;"> </span>evencheck = 1; //奇数个1
- if(u_OddNums % 2 == 0)
- oddcheck = 1; //偶数个1
- else
- oddcheck = 0; //奇数个1
- u_EvenNums = 0;
- u_OddNums = 0; <span style="white-space: pre;"> </span>//清零
- 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
- //串口打印
- void IDDataPrintf(void)
- {
- u8 CheckFlag = 0;
- u8 Buff[6],i;
- if(u_DataBits >= WG_DATA_BITS)
- {
- CheckFlag = DataCheck();
- if(CheckFlag == 0)
- {
- #if 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
- for (i = 0;i < 4;i++)
- Buff[i] = IDData[i];
- Buff[i++] = u_DataBits;
- Buff[i] = CheckFlag;
- g_Driver.OnSendCanData(Buff,6,0);
- #endif
- }
- else if(CheckFlag == 1)
- {
- #if 0
- USART_send_string("Read Data Failed\n");
- #else
- for (i = 0;i < 4;i++)
- Buff[i] = IDData[i];
- Buff[i++] = u_DataBits;
- Buff[i] = CheckFlag;
- g_Driver.OnSendCanData(Buff,6,0);
- #endif
- }
- u_DataBits = 0;
- }
- }
#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; //当前接收数据位数
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
//串口打印
void IDDataPrintf(void)
{
u8 CheckFlag = 0;
u8 Buff[6],i;
if(u_DataBits >= WG_DATA_BITS)
{
CheckFlag = DataCheck();
if(CheckFlag == 0)
{
#if 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
for (i = 0;i < 4;i++)
Buff[i] = IDData[i];
Buff[i++] = u_DataBits;
Buff[i] = CheckFlag;
g_Driver.OnSendCanData(Buff,6,0);
#endif
}
else if(CheckFlag == 1)
{
#if 0
USART_send_string("Read Data Failed\n");
#else
for (i = 0;i < 4;i++)
Buff[i] = IDData[i];
Buff[i++] = u_DataBits;
Buff[i] = CheckFlag;
g_Driver.OnSendCanData(Buff,6,0);
#endif
}
u_DataBits = 0;
}
}
1)采用IO中断的方式接收,查询方式可能响应不过来;
2)IO中断方式尽可能设为最高,防止被其他中断打断,导致数据错误;
3)IO中断中处理事务尽可能少,减少耗时;
4)IO输入口必须加RC滤波,从硬件上增加抗干扰能力。
6、参考
https://baike.baidu.com/item/韦根协议/9914978?fr=aladdin