基于STM32的RFID射频识别技术 韦根协议C语言驱动解码程序

RFID工作原理

RFID技术的基本工作原理并不复杂:标签进入磁场后,接收解读器发出的射频信号,凭借感应电流所获得的能量发送出存储在芯片中的产品信息(无源标签或被动标签),或者由标签主动发送某一频率的信号(Active Tag,有源标签或主动标签),解读器读取信息并解码后,送至中央信息系统进行有关数据处理。

一套完整的RFID系统, 是由阅读器与电子标签也就是所谓的应答器及应用软件系统三个部份所组成,其工作原理是Reader发射一特定频率的无线电波能量,用以驱动电路将内部的数据送出,此时Reader便依序接收解读数据, 送给应用程序做相应的处理。

以RFID 卡片阅读器及电子标签之间的通讯及能量感应方式来看大致上可以分成:感应耦合及后向散射耦合两种。一般低频的RFID大都采用第一种式,而较高频大多采用第二种方式。

阅读器根据使用的结构和技术不同可以是读或读/写装置,是RFID系统信息控制和处理中心。阅读器通常由耦合模块、收发模块、控制模块和接口单元组成。阅读器和应答器之间一般采用半双工通信方式进行信息交换,同时阅读器通过耦合给无源应答器提供能量和时序。在实际应用中,可进一步通过Ethernet或WLAN等实现对物体识别信息的采集、处理及远程传送等管理功能。应答器是RFID系统的信息载体,应答器大多是由耦合原件(线圈、微带天线等)和微芯片组成无源单元。

韦根协议

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

-------------------------------------------------------------------------------------------------------------------------------------------------------------

以上是RFID技术协议和韦根的简略说明,不久之前,本人在某个项目中使用了RFID设备,在做解码的时候感觉很有趣,现在分享出来,以供各位同僚参考,如果记述有错误或者疏漏,那完全是因为本人能力有限,还请各位勇士不吝指出,本人会感激不尽。

本篇文章的重点在于韦根协议的解码,以及基于STM32单片机的C语言的具体实现。

首先看看韦根协议到底是什么。

韦根接口

  Wiegand接口通常由3根线组成,它们是:数据0(Data0),数据1(Data1)和 Data return。这3条线负责传输Wiegand信号。D0,D1在没有数据输出时都保持+5V高电平。若输出为0,则D0拉低一段时间,若输出为1,则D1拉低一段时间。

基本概念

  由上面可以知道,韦根数据输出由二根线组成,分别是DATA0 和 DATA1 ;二根线分别为‘0’或‘1’输出。

  输出‘0’时:DATA0线上出现负脉冲;

  输出‘1’时:DATA1线上出现负脉冲;

  负脉冲宽度TP=100微秒;周期TW=1600微秒;

  

  韦根协议常用的是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。

——————————————————————————————————————————————————————————————————————————————

以上。

时间: 2024-10-08 20:23:05

基于STM32的RFID射频识别技术 韦根协议C语言驱动解码程序的相关文章

什么是RFID? 射频识别技术的特点及工作原理!

RFID即Radio Frequency Identifcation,就是射频识别技术,这篇给大家讲述的就是这个射频识别技术.这里就涉及到射频,电磁学等等知识.看完这篇,你应该会对这些知识有些了解,大家匆必要看完! 原文地址:https://www.cnblogs.com/ct20150811/p/9946890.html

射频识别技术漫谈(20)——RC系列射频接口芯片

目前基于13.56MHz的射频识别技术主要有ISO14443A.ISO14443B.ISO15693和FELICA技术.针对13.56MHz的射频识别技术,NXP开发了一系列名字以RC(Radio Chip)开头的射频接口芯片,包括RC500.RC400.RC530.RC531.RC632等,这些芯片的设计架构.引脚排列.内部寄存器阵列.天线设计等方面基本相同,不同之处主要是与微控制器的接口界面.支持的协议种类等不一样.后来的RC522.PN512也大体继承了同样的设计思路,只是在供电电压和封装

射频识别技术漫谈(6)——通讯协议概述

通讯协议是通讯的双方或多方在交流时遵守的规矩,包括谁先发起通讯,先交流什么,后交流什么,一方如何问,另一方如何答等.在这里通迅的双方指的是读写器和卡片. 首先是谁先发起通讯,很显然有两种,读写器先发言和卡片先发言.前一种叫Reader Talk First,简称为RTF;后一种叫Tag Talk First,简称为TTF. 我们知道,卡片从读写器产生的射频场获得工作时需要的能量,一般认为应该是读写器主动,卡片被动才对,这也是人之常情.所以多数功能稍复杂的卡片都是采用RTF模式,即读写器问,卡片回

射频识别技术漫谈(28)——基于MF1射频卡的酒店门锁设计

电子门锁是现代星级酒店管理电子化.智能化的重要电子设备.相较于传统的机械锁,基于RFID技术的电子门锁使用方便,易于管理,安全性高,可实现对开锁用户的分优先级自动管理,对房间入住信息实现自动统计与报表输出. 1  系统整体分析 MF1 S50和S70卡是遵守ISO14443A国际标准的非接触式逻辑加密卡,S50卡内共有1024字节非易失性存储空间,分为16个扇区,每个扇区包含4个数据块,每个扇区都有一组独立的密码A和B,扇区内的每个数据块都可单独设置存取条件.S70存储结构与S50类似,存储空间

射频识别技术漫谈(24)——ISO15693的防冲突与传输协议

遵守ISO15693协议的电子标签都有一个8字节共64bit的全球唯一序列号(UID),这个UID一方面可以使全球范围内的标签互相区别,更重要的是可以在多标签同时读写时用于防冲突.8字节UID按权重从高到低标记为UID7--UID0,其中UID7固定为16进制的E0H,UID6是标签制造商的代码,例如NXP的代码为04H,TI的代码为07H;UID5为产品类别代码,比如ICODE SL2 ICS20是01H,Tag-it HF-I Plus Chip为80H,Tag-it HF-I Plus I

(转)基于即时通信和LBS技术的位置感知服务(二):XMPP协议总结以及开源解决方案

在<基于即时通信和LBS技术的位置感知服务(一):提出问题及解决方案>一文中,提到尝试使用XMPP协议来实现即时通信.本文将对XMPP协议框架以及相关的C/S架构进行介绍,协议的底层实现不再本文的讨论范围. 一.什么是XMPP? 介 绍XMPP之前,我们先来聊聊GTalk.GTalk是Google推出的IM(Instant Messaging,即时通讯)软件,类似于QQ和MSN.从技术角度来说,GTalk与QQ和MSN的差异是使用了不同的通讯协议,QQ使用了自己的私 有协议(未公开),MSN也

射频识别技术漫谈(29)——射频接口芯片TRF7960

TRF7960系列是TI推出的载波为13.56MHz.支持ISO15693.ISO14443A/B和FeliCa协议的射频接口芯片.许多人更关心它能不能读写MF1卡片,就我的理解及实际验证,由于MF1在卡选择之前的操作是遵守ISO14443A协议的,之后的卡验证和卡数据读写都是NXP自己的保密协议,所以TRF7960可以对MF1卡执行到卡选择操作,或者通俗的说可以读MF1的卡片序列号,但不能对MF1卡读写数据,除非开发者自己知道NXP的加密协议并自己编写代码实现该协议. 在TI官方公开的TRF7

射频识别技术漫谈(25)——Felica简介

Felica是SONY公司开发的射频识别技术,该技术使用的载波频率与ISO14443A和ISO14443B一样,都是13.56MHz,所以有人把它称为ISO14443C,但SONY并没有正式接受这样的称谓. Felica技术并不是一个完全公开的协议,尤其是该技术涉及安全认证和加密的部分,SONY的保密做的很好.本文介绍的也仅是其公开的部分信息. Felica技术的通讯协议包括三层:物理层描述数据传输的物理和电气特性; 数据链路层描述数据传输和错误检测的格式;应用层描述命令的功能和规范. Feli

射频识别技术漫谈(1)——概念、分类

现代社会智能卡已经渗透到生活的方方面面,公交卡.考勤卡.身份证.手机卡等等数不胜数. 智能卡按使用时是否和读卡器接触可分为接触式智能卡和非接触式智能卡,接触式智能卡上有6-8个触点,使用时插在卡座上,典型的如手机卡以及以前的公用电话卡.非接触式智能卡没有触点,卡上也没有电源,通过读卡器产生的电磁场获得能量并与读写器交换信息.由于非接触式智能卡的无源和免接触特性,卡的使用寿命和安全性大大提高,目前应用越来越广泛. 根据安全等级,智能卡可分为存储器卡.逻辑加密卡和CPU卡.存储器卡就像一个无人看守且