标签:解码 ret 定时器 lan 信息系统 group 概念 去除 编码
Wiegand协议是国际上统一的标准,是由摩托罗拉公司制定的一种通讯协议。它适用于涉及门禁控制系统的读卡器和卡片的许多特性。 它有很多格式,标准的26-bit 应该是最常用的格式。此外,还有34-bit 、37-bit 等格式。 而标准26-bit 格式是一个开放式的格式,这就意味着任何人都可以购买某一特定格式的HID卡,并且这些特定格式的种类是公开可选的。26-Bit格式就是一个广泛使用的工业标准,并且对所有HID的用户开放。几乎所有的门禁控制系统都接受标准的26-Bit格式。
-------------------------------------------------------------------------------------------------------------------------------------------------------------
以上是RFID技术协议和韦根的简略说明,不久之前,本人在某个项目中使用了RFID设备,在做解码的时候感觉很有趣,现在分享出来,以供各位同僚参考,如果记述有错误或者疏漏,那完全是因为本人能力有限,还请各位勇士不吝指出,本人会感激不尽。
本篇文章的重点在于韦根协议的解码,以及基于STM32单片机的C语言的具体实现。
首先看看韦根协议到底是什么。
韦根协议常用的是26bit的协议,不过bit的多少对写代码的关系,完全可以做一个通用的代码。
现在我以韦根34为例子,做一下协议分析。
Wiegand 34格式:
各数据位的含义:
第 1 位: 为输出第2—17位的偶校验位
第 2-17 位: ID卡的HID码
第18-33位: ID卡的PID号码
第 34 位: 为输出第18-33位的奇校验位
数据输出顺序:
HID码和PID码均为高位在前,低位在后
由以上的信息我们可以获悉,所谓的韦根协议十分简单,完全就只有两根线,平时两根线都保持高电平,等有信号的时候就突然来一个下降沿,
DATA0的下降沿表示二进制信号0,DATA1的下降沿表示二进制信号1,我们只需要将着一些信号收集起来,然后在组合成一个完整的数,
最后做一下奇偶校验就能得到正确的结果了。(如果不懂奇偶校验,请自行百度)
韦根信号的每一bit的持续时间都极端,只有100微秒左右,如果在代码中用轮询的方式明显是不可行的,自然而然,我们想到用外部中断的方式进行捕获信号。
首先是初始化函数,
1 /******************************************************************************* 2 * 函数名 : wiegand_init 3 * 描述 : 韦根机能初期化 4 * 输出 : 无 5 * 返回 : 无 6 * 说明 : 无 7 *******************************************************************************/ 8 void wiegand_init(void) 9 { 10 EXTI_Config_B0(); // 外部中断0初始化 韦根0 11 EXTI_Config_B1(); // 外部中断1初始化 韦根1 12 Timer_Config(); // 定时器初始化 13 NVIC_Config(); // 定时器中断配置 14 15 return; 16 }
由上面的信息得知,解码韦根协议需要用到两个外部中断,分别采集两根线的信号,但是在第12,13行,我还加入了定时器的初始化,
定时器是用来判断信号的有效性,比如说,当其中的某一个引脚上发生了下降沿中断,这时我们就应该启动定时器了,在规定时间内,
第二个中断信号没有来临,那么我们就可以把第一个中断信号当做干扰,从而提高了系统的抗干扰性。
本次代码是基于ARM STM32F103 这一款单片机的,中断的内部如下:
1 /******************************************************************************* 2 * 函数名 : EXTI_Config_B0 3 * 描述 : 外部中断0初期化 4 * 输出 : 无 5 * 返回 : 无 6 * 说明 : 无 7 *******************************************************************************/ 8 static void EXTI_Config_B0(void) 9 { 10 EXTI_InitTypeDef EXTI_InitStructure; 11 NVIC_InitTypeDef NVIC_InitStructure; 12 GPIO_InitTypeDef GPIO_InitStructure; 13 14 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0); 15 16 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); 17 18 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; 19 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 20 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 21 GPIO_Init(GPIOB, &GPIO_InitStructure); 22 23 EXTI_InitStructure.EXTI_Line = EXTI_Line0; 24 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 25 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; 26 EXTI_InitStructure.EXTI_LineCmd = ENABLE; 27 EXTI_Init(&EXTI_InitStructure); 28 29 NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; 30 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 31 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 32 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 33 NVIC_Init(&NVIC_InitStructure); 34 }
1 /******************************************************************************* 2 * 函数名 : EXTI_Config_B1 3 * 描述 : 外部中断1初期化 4 * 输出 : 无 5 * 返回 : 无 6 * 说明 : 无 7 *******************************************************************************/ 8 static void EXTI_Config_B1(void) 9 { 10 EXTI_InitTypeDef EXTI_InitStructure; 11 NVIC_InitTypeDef NVIC_InitStructure; 12 GPIO_InitTypeDef GPIO_InitStructure; 13 14 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1); 15 16 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; 17 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 18 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 19 20 GPIO_Init(GPIOB, &GPIO_InitStructure); 21 EXTI_InitStructure.EXTI_Line = EXTI_Line1; 22 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 23 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; 24 EXTI_InitStructure.EXTI_LineCmd = ENABLE; 25 EXTI_Init(&EXTI_InitStructure); 26 27 NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; 28 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 29 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 30 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 31 NVIC_Init(&NVIC_InitStructure); 32 }
以上的设置就是将某两个引脚设为捕获下降沿的外部中断功能,具体讲解网上的资料很多,请自行百度。
接下来是定时器的初始化:
1 /*============================================================================== 2 *名 称: Timer_Config(); 3 *功 能: 定时器中断初始化 4 *入口 参数: 5 *说 明: 放入主函数里初始化 6 *范 例: 7 *编者 时 间: Ye.FuYao 2012-9-23 8 9 公式为: 10 Period / (72M / (Prescaler+1) )=____ 秒 11 1000 / (72 M/ (35999+1) ) = 0.5 秒 12 *============================================================================*/ 13 void Timer_Config(void) 14 { 15 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定义TIM结构体变量 16 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //使能TIM2外设 17 TIM_DeInit(TIM2); //复位时钟TIM2,恢复到初始状态 18 TIM_TimeBaseStructure.TIM_Period=200; 19 TIM_TimeBaseStructure.TIM_Prescaler=36000-1; 20 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //TIM2时钟分频 21 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //计数方式 22 // 定时时间T计算公式: 23 24 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); //初始化 25 TIM_ClearFlag(TIM2,TIM_FLAG_Update); //清除标志 26 // 中断方式下,使能中断源 27 TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //使能中断源 28 TIM_Cmd(TIM2,ENABLE); //使能TIM2 29 }
定时器中断嵌套配置函数:
1 /*============================================================================== 2 *名 称: NVIC_Config(); 3 *功 能: 定时器嵌套控制 4 *入口 参数: 5 *说 明: 6 *范 例: 7 *编者 时 间: 8 *============================================================================*/ 9 void NVIC_Config(void) 10 { 11 NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量 12 // 设置优先分级组 13 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //0组,全副优先级 14 NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; //选择中断通道,库 15 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; //抢占优先级0 16 NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; //响应优先级0 17 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //启动此通道的中断 18 NVIC_Init(&NVIC_InitStructure); //结构体初始化 19 }
接下来就是用来解码的外部中断服务函数了:
1 /******************************************************************************* 2 * 函数名 : EXTI0_IRQHandler 3 * 描述 : 外部中断服务函数 接收韦根a DATA断数据 4 * 输出 : 无 5 * 返回 : 无 6 * 说明 : 无 7 *******************************************************************************/ 8 void EXTI0_IRQHandler(void) 9 { 10 /* 外部中断 0 是韦根协议的 DATA0 */ 11 UID <<= 1; 12 /* 关闭定时器,初始化计数值为0后,再开启定时器 */ 13 TIM_Cmd(TIM2,DISABLE); 14 TIM2->CNT = 0; 15 TIM_Cmd(TIM2,ENABLE); 16 BitCount_a++; 17 EXTI_ClearITPendingBit(EXTI_Line0); 18 }
以上是韦根0的外部中断函数,当发生了外部中断下降沿信号后,数据采集变量UID左移,表示收到一个0,并且在这是重新初始化定时器,开始检测干扰。
然后韦根计数变量BitCount_a++,表示受到一个bit,当这个变量等于34的时候,就表示韦根信号接收完毕。
韦根1的外部中断也差不多,只有微小的区别:
1 /******************************************************************************* 2 * 函数名 : EXTI1_IRQHandler 3 * 描述 : 外部中断服务函数 接收韦根a DATA1中断数据 4 * 输出 : 无 5 * 返回 : 无 6 * 说明 : 无 7 *******************************************************************************/ 8 void EXTI1_IRQHandler(void) 9 { 10 /* 外部中断 1 是韦根协议的 DATA1 */ 11 UID <<= 1; 12 UID |= 1; 13 /* 关闭定时器,初始化计数值为0后,再开启定时器 */ 14 TIM_Cmd(TIM2,DISABLE); 15 TIM2->CNT = 0; 16 TIM_Cmd(TIM2,ENABLE); 17 BitCount_a++; 18 EXTI_ClearITPendingBit(EXTI_Line1); 19 }
当韦根1 发生了下降沿中断,表示接受到一个1bit,这是将数据采集变量UID加上一个1,剩下的和韦根0一毛一样。
下面是定时器服务函数,其中最重要功能是用来确定韦根信号是否接受完毕:
1 /******************************************************************************* 2 * 函数名 : TIM2_IRQHandler 3 * 描述 : 定时器中断服务函数,用于判定韦根解码失败 4 * 输出 : 无 5 * 返回 : 无 6 * 说明 : 无 7 *******************************************************************************/ 8 void TIM2_IRQHandler(void) 9 { 10 if(BitCount_a == WIEGAND34_DATA_LEN) 11 { 12 ReceiveFlag_a = 1; 13 } 14 BitCount_a = 0; 15 16 TIM_ClearFlag(TIM2,TIM_FLAG_Update); //清标志 17 }
如果韦根计数变量BitCount_a达到了我们想要接收的位数,比如34,那么表示韦根信号已经接受完毕,设定好flag,接下来就可以效验了。
如果没有到达我们想要的计数量,那么就表示我们遇见了干扰,这时将计数变量清零,什么都不做,等待下一个信号来到。
等韦根信号的flag立起来后,我们就可以尝试着去效验数据的正确性了:
1 /******************************************************************************* 2 * 函数名 : wiegand_decode 3 * 描述 : 韦根解码效验函数 4 * 输出 : 无 5 * 返回 : 无 6 * 说明 : 无 7 *******************************************************************************/ 8 void wiegand_decode(void) 9 { 10 u16 i; 11 12 if(ReceiveFlag_a == 0x01) 13 { 14 /*收到第一位偶校验 */ 15 Parity = 0; 16 for(i = 0; i < 17; i ++) 17 { 18 Parity ^= ((UID >> (33 - i)) & 0x01); 19 } 20 if(Parity == 0) 21 { 22 /* 收到最后一位奇校验 */ 23 Parity = 0; 24 for(i = 0; i < 17; i ++) 25 { 26 Parity ^= ((UID >> (16 - i)) & 0x01); 27 } 28 PID = 0; 29 HID = 0; 30 if(Parity != 0) 31 { 32 /* 分离校验位,取出HID和PID码 */ 33 UID = (UID >> 1) & 0xFFFFFFFF; 34 35 PID = UID & 0x000000FF; 36 PID = (PID << 8); 37 PID = PID | ((UID >> 8) & 0x00FF); 38 39 UID = UID >> 16; 40 HID = UID & 0x000000FF; 41 HID = (HID << 8); 42 HID = HID | ((UID >> 8) & 0x00FF); 43 44 } 45 UID = 0; 46 } 47 ReceiveFlag_a = 0; 48 } 49 50 return; 51 }
等奇偶校验成功,然后就可以取出对应的PID和HID卡号了。
当自己校对数据的时候,一定记得要将两个奇偶校验位给去除,如果解码出来的数据和卡上的编码不一样:
第一,确认一下自己的韦根0和韦根1是否接反了。
第二,确认自己在解码后是否去掉了奇偶校验bit
如果自己刷卡以后,程序无法跑进定时器中断函数里面的那个if判定,那么就要看看自己韦根卡是否是设置为相应的bit,
如果卡是26bit,你用34bit去解码,当然收不全数据,然后还有一个比较重要,看看自己是否在外部中断服务函数中处理了太多的东西,
因为两个韦根信号来的很快,如果在中断里处理了太多的东西,那等第二个中断信号来临的时候,第一个处理还没有完成,那自然就会丢掉某个bit。
中断函数中的处理一定要尽可能的少,因为我在调试的时候发现,只要在外部中断里面再加入一些中断标志位的判定,那么就会丢掉bit。
——————————————————————————————————————————————————————————————————————————————
以上。
基于STM32的RFID射频识别技术 韦根协议C语言驱动解码程序
标签:解码 ret 定时器 lan 信息系统 group 概念 去除 编码
原文地址:http://www.cnblogs.com/han-bing/p/6785632.html