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协议
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