基於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。

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

 

以上。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM