日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

STM32—驱动RFID-RC522模块

發布時間:2023/12/16 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32—驱动RFID-RC522模块 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 一.S50(M1)卡介紹
    • 1.S50(M1)卡基礎知識
    • 2.內部信息
    • 3.存取控制
    • 4.數據塊的存取控制
    • 5.控制塊的存取控
    • 6.工作原理
    • 7.M1與讀卡器的通信
  • 二.RC522工程代碼詳解
    • 1.RC522與M1通信
    • 2.STM32對RC522寄存器的操作
    • 3.STM32對RC522的基礎通信
    • 4.STM32控制RC522與M1的通信
    • 5.測試函數

一.S50(M1)卡介紹

1.S50(M1)卡基礎知識

1.每張卡有唯一的序列號,32位
2.卡的容量是8Kbit的EEPROM
3.分為16個扇區,每個扇區分為4塊,每塊16個字節,以塊為存取單位
4.每個扇區都有獨立的一組密碼和訪問控制

2.內部信息

扇區0的塊0用來固化廠商代碼;
每個扇區的塊3作為控制塊,存放:密碼A(6字節)、存取控制(4字節)、密碼B(6字節)

每個扇區的塊0、1、2作為數據塊,其作用如下:
1.作為一般的數據存儲,可以對其中的數據進行讀寫操作
2.用作數據值,可以進行初始化值、加值、減值、讀值操作

3.存取控制

每個扇區的密碼和存取控制都是獨立的,存取控制是4個字節,即32位(在塊3中)。
每個塊都有存取條件,存取條件是由密碼和存取控制共同決定的。
每個塊都有相應的三個控制位,這三個控制位存在于存取控制字節中,相應的控制位決定了該塊的訪問權限,控制位如圖:

就是說,每個扇區的所有塊的存取條件控制位,都放在了該扇區的塊3中,如圖:

4.數據塊的存取控制

對數據塊,與就是塊0、1、2的存取控制是由對應塊的控制位來決定的:

從表中得知:對數據塊的存取控制,由于存取控制由三個控制位所決定,所以相應的訪問條件就產生了9種。
要想對數據塊進行操作,首先要看該數據塊的控制位是否允許對數據塊的操作,如果允許操作,再看需要驗證什么密碼,只有驗證密碼正確后才可以對該數據塊執行相應操作。
一般密碼A的初始值都是0xFF…

5.控制塊的存取控

塊3(控制塊)的存取操作與數據塊不同,如圖:

6.工作原理

電氣部分:
卡片的電氣部分由一個天線和一個ASIC組成。
天線:就是幾組繞線的線圈,體積小,已經封裝在卡片內
ASIC:ASIC即專用集成電路,是指應特定用戶要求和特定電子系統的需要而設計、制造的集成電路。 目前用CPLD(復雜可編程邏輯器件)和 FPGA(現場可編程邏輯陣列)來進行ASIC設計是最為流行的方式之一,它們的共性是都具有用戶現場可編程特性,都支持邊界掃描技術,但兩者在集成度、速度以及編程方式上具有各自的特點,這樣理解,ASIC就是卡片特點的一個集成電路。
卡片的ASIC包含了一個高速(106KB)的RF接口、一個控制單元、一個8K的EEPROM

工作過程:
讀卡器會向M1卡發送一組固定頻率的電磁波,卡片內有一個LC串聯諧振電路,其工作頻率與讀卡器發送的電磁波頻率相同,遂在電磁波的激勵下,LC串聯諧振電路會發生共振,從而使電容內產生電荷,在電容的另一端接有一個單向導電的電子泵,電子泵將產生的電荷轉移到另一個電容中存儲。當存儲電容中的電荷達到2V的時候,此時電容就作為電源為其他電路提供工作電壓,所以卡片就可以向讀卡器發送數據,或者從讀卡器接收數據,實現了讀卡器與卡片的通信。

7.M1與讀卡器的通信

通信的流程圖如示:

復位應答(Request)
M1卡的通信協議和通信波特率是定義好的,當有卡片進入讀卡器的工作范圍時,讀卡器要以特定的協議與卡片通信,從而確定卡片的卡型。

防沖突機制(Anticollision Loop)
當有多張卡片進入讀寫器操作范圍時,會從中選擇一張卡片進行操作,并返回選中卡片的序列號。

選擇卡片(Select Tag)
選擇被選中的卡的序列號,并同時返回卡的容量代碼。

三次相互確認(3 Pass Authentication)
選定要處理的卡片后,讀寫器就要確定訪問的扇區號,并且對扇區密碼進行密碼校驗。在三次互相認證后就可以通過加密流進行通信。每次在選擇扇區的時候都要進行扇區的密碼校驗。

對數據塊的操作
讀(Read):讀一個塊的數據;
寫(Write):在一個塊中寫數據;
加(Increment):對數據塊中的數值進行加值;
減(Decrement):對數據塊中的數值進行減值;
傳輸(Transfer):將數據寄存器中的內容寫入數據塊中;
中止(Halt):暫停卡片的工作;

二.RC522工程代碼詳解

1.RC522與M1通信

用戶通過單片機初始化RC522,然后通過單片機控制RC522與M1通信,那單片機是怎樣與RC522通信的呢?
RC522通過SPI接口與單片機(STM32)通信,單片機向RC522內的寄存器寫入特定的指令,RC522會根據寄存器中的值來執行相關操作,并與M1通信。所以要控制RC522,就必須了解RC522的寄存器和一些相關指令,這些東西廠家都會提供,所以我們只需要復制粘貼到我們的工程中使用即可。下面分享一下相關寄存器的地址和指令:

/ //RC522命令字 / #define PCD_IDLE 0x00 //取消當前命令 #define PCD_AUTHENT 0x0E //驗證密鑰 #define PCD_RECEIVE 0x08 //接收數據 #define PCD_TRANSMIT 0x04 //發送數據 #define PCD_TRANSCEIVE 0x0C //發送并接收數據 #define PCD_RESETPHASE 0x0F //復位 #define PCD_CALCCRC 0x03 //CRC計算/ //Mifare_One卡片命令字 / #define PICC_REQIDL 0x26 //尋天線區內未進入休眠狀態 #define PICC_REQALL 0x52 //尋天線區內全部卡 #define PICC_ANTICOLL1 0x93 //防沖撞 #define PICC_ANTICOLL2 0x95 //防沖撞 #define PICC_AUTHENT1A 0x60 //驗證A密鑰 #define PICC_AUTHENT1B 0x61 //驗證B密鑰 #define PICC_READ 0x30 //讀塊 #define PICC_WRITE 0xA0 //寫塊 #define PICC_DECREMENT 0xC0 //扣款 #define PICC_INCREMENT 0xC1 //充值 #define PICC_RESTORE 0xC2 //調塊數據到緩沖區 #define PICC_TRANSFER 0xB0 //保存緩沖區中數據 #define PICC_HALT 0x50 //休眠/* RC522 FIFO長度定義 */ #define DEF_FIFO_LENGTH 64 //FIFO size=64byte #define MAXRLEN 18/* RC522寄存器定義 */ // PAGE 0 #define RFU00 0x00 //保留 #define CommandReg 0x01 //啟動和停止命令的執行 #define ComIEnReg 0x02 //中斷請求傳遞的使能(Enable/Disable) #define DivlEnReg 0x03 //中斷請求傳遞的使能 #define ComIrqReg 0x04 //包含中斷請求標志 #define DivIrqReg 0x05 //包含中斷請求標志 #define ErrorReg 0x06 //錯誤標志,指示執行的上個命令的錯誤狀態 #define Status1Reg 0x07 //包含通信的狀態標識 #define Status2Reg 0x08 //包含接收器和發送器的狀態標志 #define FIFODataReg 0x09 //64字節FIFO緩沖區的輸入和輸出 #define FIFOLevelReg 0x0A //指示FIFO中存儲的字節數 #define WaterLevelReg 0x0B //定義FIFO下溢和上溢報警的FIFO深度 #define ControlReg 0x0C //不同的控制寄存器 #define BitFramingReg 0x0D //面向位的幀的調節 #define CollReg 0x0E //RF接口上檢測到的第一個位沖突的位的位置 #define RFU0F 0x0F //保留 // PAGE 1 #define RFU10 0x10 //保留 #define ModeReg 0x11 //定義發送和接收的常用模式 #define TxModeReg 0x12 //定義發送過程的數據傳輸速率 #define RxModeReg 0x13 //定義接收過程中的數據傳輸速率 #define TxControlReg 0x14 //控制天線驅動器管教TX1和TX2的邏輯特性 #define TxAutoReg 0x15 //控制天線驅動器的設置 #define TxSelReg 0x16 //選擇天線驅動器的內部源 #define RxSelReg 0x17 //選擇內部的接收器設置 #define RxThresholdReg 0x18 //選擇位譯碼器的閾值 #define DemodReg 0x19 //定義解調器的設置 #define RFU1A 0x1A //保留 #define RFU1B 0x1B //保留 #define MifareReg 0x1C //控制ISO 14443/MIFARE模式中106kbit/s的通信 #define RFU1D 0x1D //保留 #define RFU1E 0x1E //保留 #define SerialSpeedReg 0x1F //選擇串行UART接口的速率 // PAGE 2 #define RFU20 0x20 //保留 #define CRCResultRegM 0x21 //顯示CRC計算的實際MSB值 #define CRCResultRegL 0x22 //顯示CRC計算的實際LSB值 #define RFU23 0x23 //保留 #define ModWidthReg 0x24 //控制ModWidth的設置 #define RFU25 0x25 //保留 #define RFCfgReg 0x26 //配置接收器增益 #define GsNReg 0x27 //選擇天線驅動器管腳(TX1和TX2)的調制電導 #define CWGsCfgReg 0x28 //選擇天線驅動器管腳的調制電導 #define ModGsCfgReg 0x29 //選擇天線驅動器管腳的調制電導 #define TModeReg 0x2A //定義內部定時器的設置 #define TPrescalerReg 0x2B //定義內部定時器的設置 #define TReloadRegH 0x2C //描述16位長的定時器重裝值 #define TReloadRegL 0x2D //描述16位長的定時器重裝值 #define TCounterValueRegH 0x2E #define TCounterValueRegL 0x2F //顯示16位長的實際定時器值 // PAGE 3 #define RFU30 0x30 //保留 #define TestSel1Reg 0x31 //常用測試信號配置 #define TestSel2Reg 0x32 //常用測試信號配置和PRBS控制 #define TestPinEnReg 0x33 //D1-D7輸出驅動器的使能管腳(僅用于串行接口) #define TestPinValueReg 0x34 //定義D1-D7用作I/O總線時的值 #define TestBusReg 0x35 //顯示內部測試總線的狀態 #define AutoTestReg 0x36 //控制數字自測試 #define VersionReg 0x37 //顯示版本 #define AnalogTestReg 0x38 //控制管腳AUX1和AUX2 #define TestDAC1Reg 0x39 //定義TestDAC1的測試值 #define TestDAC2Reg 0x3A //定義TestDAC2的測試值 #define TestADCReg 0x3B //顯示ADCI和Q通道的實際值 #define RFU3C 0x3C //保留 #define RFU3D 0x3D //保留 #define RFU3E 0x3E //保留 #define RFU3F 0x3F //保留/* 和RC522通信時返回的錯誤代碼 */ #define MI_OK 0x26 #define MI_NOTAGERR 0xcc #define MI_ERR 0xbb

既然RC522是通過SPI與單片機通信的,所以就會有相應的引腳配置,下面給出相關引腳的配置和一些引腳操作宏定義:

/* RC522引腳連接說明(SPI1的引腳) : CS:PA4( 接的SDA引腳 ) SCK:PA5 MISO:PA6 MOSI:PA7 RST:PB0 */ void RC522_GPIO_Init( void ) {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE );GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_Init( GPIOA, &GPIO_InitStructure );GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;GPIO_Init( GPIOA, &GPIO_InitStructure );GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;GPIO_Init( GPIOA, &GPIO_InitStructure );GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_Init( GPIOB, &GPIO_InitStructure );GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_Init( GPIOA, &GPIO_InitStructure ); }/* IO口操作函數 */ #define RC522_CS_Enable() GPIO_ResetBits ( GPIOA, GPIO_Pin_4 ) #define RC522_CS_Disable() GPIO_SetBits ( GPIOA, GPIO_Pin_4 )#define RC522_Reset_Enable() GPIO_ResetBits( GPIOB, GPIO_Pin_0 ) #define RC522_Reset_Disable() GPIO_SetBits( GPIOB, GPIO_Pin_0 )#define RC522_SCK_0() GPIO_ResetBits( GPIOA, GPIO_Pin_5 ) #define RC522_SCK_1() GPIO_SetBits( GPIOA, GPIO_Pin_5 )#define RC522_MOSI_0() GPIO_ResetBits( GPIOA, GPIO_Pin_7 ) #define RC522_MOSI_1() GPIO_SetBits( GPIOA, GPIO_Pin_7 )#define RC522_MISO_GET() GPIO_ReadInputDataBit( GPIOA, GPIO_Pin_6 )

我是通過軟件模擬SPI與RC522通信的,SPI發送接收字節的代碼如下(高位先行):

/* 軟件模擬SPI發送一個字節數據,高位先行 */ void RC522_SPI_SendByte( uint8_t byte ) {uint8_t n;for( n=0;n<8;n++ ){if( byte&0x80 )RC522_MOSI_1();elseRC522_MOSI_0();Delay_us(200);RC522_SCK_0();Delay_us(200);RC522_SCK_1();Delay_us(200);byte<<=1;} }/* 軟件模擬SPI讀取一個字節數據,先讀高位 */ uint8_t RC522_SPI_ReadByte( void ) {uint8_t n,data;for( n=0;n<8;n++ ){data<<=1;RC522_SCK_0();Delay_us(200);if( RC522_MISO_GET()==1 )data|=0x01;Delay_us(200);RC522_SCK_1();Delay_us(200);}return data; }

單片機和RC522之間的通信基礎機制就建立起來了,下一步就是建立在通信基礎上的操作了。

2.STM32對RC522寄存器的操作

上面說了,單片機是向RC522的寄存器操作來驅動RC522的,所以會有這幾種基本操作:

  • 讀取RC522指定寄存器的值
  • 向RC522指定寄存器中寫入指定的數據
  • 置位RC522指定寄存器的指定位
  • 清位RC522指定寄存器的指定位

下面給出這些操作的函數實現:

/*** @brief :讀取RC522指定寄存器的值* @param :Address:寄存器的地址* @retval :寄存器的值 */ uint8_t RC522_Read_Register( uint8_t Address ) {uint8_t data,Addr;Addr = ( (Address<<1)&0x7E )|0x80;RC522_CS_Enable();RC522_SPI_SendByte( Addr );data = RC522_SPI_ReadByte();//讀取寄存器中的值RC522_CS_Disable();return data; }/*** @brief :向RC522指定寄存器中寫入指定的數據* @param :Address:寄存器地址data:要寫入寄存器的數據* @retval :無 */ void RC522_Write_Register( uint8_t Address, uint8_t data ) {uint8_t Addr;Addr = ( Address<<1 )&0x7E;RC522_CS_Enable();RC522_SPI_SendByte( Addr );RC522_SPI_SendByte( data );RC522_CS_Disable();}/*** @brief :置位RC522指定寄存器的指定位* @param :Address:寄存器地址mask:置位值* @retval :無 */ void RC522_SetBit_Register( uint8_t Address, uint8_t mask ) {uint8_t temp;/* 獲取寄存器當前值 */temp = RC522_Read_Register( Address );/* 對指定位進行置位操作后,再將值寫入寄存器 */RC522_Write_Register( Address, temp|mask ); }/*** @brief :清位RC522指定寄存器的指定位* @param :Address:寄存器地址mask:清位值* @retval :無 */ void RC522_ClearBit_Register( uint8_t Address, uint8_t mask ) {uint8_t temp;/* 獲取寄存器當前值 */temp = RC522_Read_Register( Address );/* 對指定位進行清位操作后,再將值寫入寄存器 */RC522_Write_Register( Address, temp&(~mask) ); }

知道了對RC522寄存器的操作,就可以結合相關的指令,對RC522寫入指令控制RC522了,下面接收一下RC522的基本操作。

3.STM32對RC522的基礎通信

上面說了寄存器、指令、對寄存器的操作,這里介紹一些對RC522的基本操作,包括:

  • 開啟天線
  • 關閉天線
  • 復位RC522
  • 設置RC522工作方式

RC522與M1通信前必須開啟天線,進行復位,然后設置RC522的工作方式!下面介紹一下相關代碼:

/*** @brief :開啟天線* @param :無* @retval :無 */ void RC522_Antenna_On( void ) {uint8_t k;k = RC522_Read_Register( TxControlReg );/* 判斷天線是否開啟 */if( !( k&0x03 ) )RC522_SetBit_Register( TxControlReg, 0x03 ); }/*** @brief :關閉天線* @param :無* @retval :無 */ void RC522_Antenna_Off( void ) {/* 直接對相應位清零 */RC522_ClearBit_Register( TxControlReg, 0x03 ); }/*** @brief :復位RC522* @param :無* @retval :無 */ void RC522_Rese( void ) {RC522_Reset_Disable();Delay_us ( 1 );RC522_Reset_Enable();Delay_us ( 1 );RC522_Reset_Disable();Delay_us ( 1 );RC522_Write_Register( CommandReg, 0x0F );while( RC522_Read_Register( CommandReg )&0x10 );/* 緩沖一下 */Delay_us ( 1 );RC522_Write_Register( ModeReg, 0x3D ); //定義發送和接收常用模式RC522_Write_Register( TReloadRegL, 30 ); //16位定時器低位RC522_Write_Register( TReloadRegH, 0 ); //16位定時器高位RC522_Write_Register( TModeReg, 0x8D ); //內部定時器的設置RC522_Write_Register( TPrescalerReg, 0x3E ); //設置定時器分頻系數RC522_Write_Register( TxAutoReg, 0x40 ); //調制發送信號為100%ASK }/*** @brief :設置RC522的工作方式* @param :Type:工作方式* @retval :無M500PcdConfigISOType */ void RC522_Config_Type( char Type ) {if( Type=='A' ){RC522_ClearBit_Register( Status2Reg, 0x08 );RC522_Write_Register( ModeReg, 0x3D );RC522_Write_Register( RxSelReg, 0x86 );RC522_Write_Register( RFCfgReg, 0x7F );RC522_Write_Register( TReloadRegL, 30 );RC522_Write_Register( TReloadRegH, 0 );RC522_Write_Register( TModeReg, 0x8D );RC522_Write_Register( TPrescalerReg, 0x3E );Delay_us(2);/* 開天線 */RC522_Antenna_On();} }

對于這些寄存器和指令的宏定義,查一下前面的說明即可。

4.STM32控制RC522與M1的通信

這部分是最重要的步驟,RC522與M1的通信是工程要實現的目的,而且要遵守前面提到的M1卡與RC522通信的步驟以及M1卡的內部構造,包括以下操作:

  • 通過RC522和M1卡通訊(數據的雙向傳輸)
  • 尋卡
  • 防沖突
  • 用RC522計算CRC16(循環冗余校驗)
  • 選定卡片
  • 校驗卡片密碼
  • 在M1卡的指定塊地址寫入指定數據
  • 讀取M1卡的指定塊地址的數據
  • 讓卡片進入休眠模式

話不多說,上代碼,代碼中都有按照我理解的一些注釋:

/*** @brief :通過RC522和ISO14443卡通訊 * @param :ucCommand:RC522命令字* pInData:通過RC522發送到卡片的數據* ucInLenByte:發送數據的字節長度* pOutData:接收到的卡片返回數據* pOutLenBit:返回數據的位長度* @retval :狀態值MI_OK,成功 */ char PcdComMF522 ( uint8_t ucCommand, uint8_t * pInData, uint8_t ucInLenByte, uint8_t * pOutData, uint32_t * pOutLenBit ) {char cStatus = MI_ERR;uint8_t ucIrqEn = 0x00;uint8_t ucWaitFor = 0x00;uint8_t ucLastBits;uint8_t ucN;uint32_t ul;switch ( ucCommand ){case PCD_AUTHENT: //Mifare認證ucIrqEn = 0x12; //允許錯誤中斷請求ErrIEn 允許空閑中斷IdleIEnucWaitFor = 0x10; //認證尋卡等待時候 查詢空閑中斷標志位break;case PCD_TRANSCEIVE: //接收發送 發送接收ucIrqEn = 0x77; //允許TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEnucWaitFor = 0x30; //尋卡等待時候 查詢接收中斷標志位與 空閑中斷標志位break;default:break;}RC522_Write_Register ( ComIEnReg, ucIrqEn | 0x80 ); //IRqInv置位管腳IRQ與Status1Reg的IRq位的值相反 RC522_ClearBit_Register ( ComIrqReg, 0x80 ); //Set1該位清零時,CommIRqReg的屏蔽位清零RC522_Write_Register ( CommandReg, PCD_IDLE ); //寫空閑命令RC522_SetBit_Register ( FIFOLevelReg, 0x80 ); //置位FlushBuffer清除內部FIFO的讀和寫指針以及ErrReg的BufferOvfl標志位被清除for ( ul = 0; ul < ucInLenByte; ul ++ )RC522_Write_Register ( FIFODataReg, pInData [ ul ] ); //寫數據進FIFOdataRC522_Write_Register ( CommandReg, ucCommand ); //寫命令if ( ucCommand == PCD_TRANSCEIVE )RC522_SetBit_Register(BitFramingReg,0x80); //StartSend置位啟動數據發送 該位與收發命令使用時才有效ul = 1000;//根據時鐘頻率調整,操作M1卡最大等待時間25msdo //認證 與尋卡等待時間 {ucN = RC522_Read_Register ( ComIrqReg ); //查詢事件中斷ul --;} while ( ( ul != 0 ) && ( ! ( ucN & 0x01 ) ) && ( ! ( ucN & ucWaitFor ) ) ); //退出條件i=0,定時器中斷,與寫空閑命令RC522_ClearBit_Register ( BitFramingReg, 0x80 ); //清理允許StartSend位if ( ul != 0 ){if ( ! ( RC522_Read_Register ( ErrorReg ) & 0x1B ) ) //讀錯誤標志寄存器BufferOfI CollErr ParityErr ProtocolErr{cStatus = MI_OK;if ( ucN & ucIrqEn & 0x01 ) //是否發生定時器中斷cStatus = MI_NOTAGERR; if ( ucCommand == PCD_TRANSCEIVE ){ucN = RC522_Read_Register ( FIFOLevelReg ); //讀FIFO中保存的字節數ucLastBits = RC522_Read_Register ( ControlReg ) & 0x07; //最后接收到得字節的有效位數if ( ucLastBits )* pOutLenBit = ( ucN - 1 ) * 8 + ucLastBits; //N個字節數減去1(最后一個字節)+最后一位的位數 讀取到的數據總位數else* pOutLenBit = ucN * 8; //最后接收到的字節整個字節有效if ( ucN == 0 ) ucN = 1; if ( ucN > MAXRLEN )ucN = MAXRLEN; for ( ul = 0; ul < ucN; ul ++ )pOutData [ ul ] = RC522_Read_Register ( FIFODataReg ); } } elsecStatus = MI_ERR; }RC522_SetBit_Register ( ControlReg, 0x80 ); // stop timer nowRC522_Write_Register ( CommandReg, PCD_IDLE ); return cStatus; }/*** @brief :尋卡 * @param ucReq_code,尋卡方式 * = 0x52:尋感應區內所有符合14443A標準的卡* = 0x26:尋未進入休眠狀態的卡* pTagType,卡片類型代碼* = 0x4400:Mifare_UltraLight* = 0x0400:Mifare_One(S50)* = 0x0200:Mifare_One(S70)* = 0x0800:Mifare_Pro(X))* = 0x4403:Mifare_DESFire* @retval :狀態值MI_OK,成功 */ char PcdRequest ( uint8_t ucReq_code, uint8_t * pTagType ) {char cStatus; uint8_t ucComMF522Buf [ MAXRLEN ]; uint32_t ulLen;RC522_ClearBit_Register ( Status2Reg, 0x08 ); //清理指示MIFARECyptol單元接通以及所有卡的數據通信被加密的情況RC522_Write_Register ( BitFramingReg, 0x07 ); // 發送的最后一個字節的 七位RC522_SetBit_Register ( TxControlReg, 0x03 ); //TX1,TX2管腳的輸出信號傳遞經發送調制的13.56的能量載波信號ucComMF522Buf [ 0 ] = ucReq_code; //存入尋卡方式/* PCD_TRANSCEIVE:發送并接收數據的命令,RC522向卡片發送尋卡命令,卡片返回卡的型號代碼到ucComMF522Buf中 */cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, & ulLen ); //尋卡 if ( ( cStatus == MI_OK ) && ( ulLen == 0x10 ) ) //尋卡成功返回卡類型 { /* 接收卡片的型號代碼 */* pTagType = ucComMF522Buf [ 0 ];* ( pTagType + 1 ) = ucComMF522Buf [ 1 ];}elsecStatus = MI_ERR; return cStatus; }/*** @brief :防沖突* @param :Snr:卡片序列,4字節,會返回選中卡片的序列* @retval :狀態值MI_OK,成功 */ char PcdAnticoll ( uint8_t * pSnr ) {char cStatus;uint8_t uc, ucSnr_check = 0;uint8_t ucComMF522Buf [ MAXRLEN ]; uint32_t ulLen;RC522_ClearBit_Register ( Status2Reg, 0x08 ); //清MFCryptol On位 只有成功執行MFAuthent命令后,該位才能置位RC522_Write_Register ( BitFramingReg, 0x00); //清理寄存器 停止收發RC522_ClearBit_Register ( CollReg, 0x80 ); //清ValuesAfterColl所有接收的位在沖突后被清除ucComMF522Buf [ 0 ] = 0x93; //卡片防沖突命令ucComMF522Buf [ 1 ] = 0x20;/* 將卡片防沖突命令通過RC522傳到卡片中,返回的是被選中卡片的序列 */cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, & ulLen);//與卡片通信if ( cStatus == MI_OK) //通信成功{for ( uc = 0; uc < 4; uc ++ ){* ( pSnr + uc ) = ucComMF522Buf [ uc ]; //讀出UIDucSnr_check ^= ucComMF522Buf [ uc ];}if ( ucSnr_check != ucComMF522Buf [ uc ] )cStatus = MI_ERR; } RC522_SetBit_Register ( CollReg, 0x80 ); return cStatus; }/*** @brief :用RC522計算CRC16(循環冗余校驗)* @param :pIndata:計算CRC16的數組* ucLen:計算CRC16的數組字節長度* pOutData:存放計算結果存放的首地址* @retval :狀態值MI_OK,成功 */ void CalulateCRC ( uint8_t * pIndata, u8 ucLen, uint8_t * pOutData ) {uint8_t uc, ucN;RC522_ClearBit_Register(DivIrqReg,0x04); RC522_Write_Register(CommandReg,PCD_IDLE); RC522_SetBit_Register(FIFOLevelReg,0x80);for ( uc = 0; uc < ucLen; uc ++)RC522_Write_Register ( FIFODataReg, * ( pIndata + uc ) ); RC522_Write_Register ( CommandReg, PCD_CALCCRC );uc = 0xFF;do {ucN = RC522_Read_Register ( DivIrqReg );uc --;} while ( ( uc != 0 ) && ! ( ucN & 0x04 ) );pOutData [ 0 ] = RC522_Read_Register ( CRCResultRegL );pOutData [ 1 ] = RC522_Read_Register ( CRCResultRegM );}/*** @brief :選定卡片* @param :pSnr:卡片序列號,4字節* @retval :狀態值MI_OK,成功 */ char PcdSelect ( uint8_t * pSnr ) {char ucN;uint8_t uc;uint8_t ucComMF522Buf [ MAXRLEN ]; uint32_t ulLen;/* PICC_ANTICOLL1:防沖突命令 */ucComMF522Buf [ 0 ] = PICC_ANTICOLL1;ucComMF522Buf [ 1 ] = 0x70;ucComMF522Buf [ 6 ] = 0;for ( uc = 0; uc < 4; uc ++ ){ucComMF522Buf [ uc + 2 ] = * ( pSnr + uc );ucComMF522Buf [ 6 ] ^= * ( pSnr + uc );}CalulateCRC ( ucComMF522Buf, 7, & ucComMF522Buf [ 7 ] );RC522_ClearBit_Register ( Status2Reg, 0x08 );ucN = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, & ulLen );if ( ( ucN == MI_OK ) && ( ulLen == 0x18 ) )ucN = MI_OK; elseucN = MI_ERR; return ucN;}/*** @brief :校驗卡片密碼* @param :ucAuth_mode:密碼驗證模式* = 0x60,驗證A密鑰* = 0x61,驗證B密鑰* ucAddr:塊地址* pKey:密碼* pSnr:卡片序列號,4字節* @retval :狀態值MI_OK,成功 */ char PcdAuthState ( uint8_t ucAuth_mode, uint8_t ucAddr, uint8_t * pKey, uint8_t * pSnr ) {char cStatus;uint8_t uc, ucComMF522Buf [ MAXRLEN ];uint32_t ulLen; ucComMF522Buf [ 0 ] = ucAuth_mode;ucComMF522Buf [ 1 ] = ucAddr;/* 前倆字節存儲驗證模式和塊地址,2~8字節存儲密碼(6個字節),8~14字節存儲序列號 */for ( uc = 0; uc < 6; uc ++ )ucComMF522Buf [ uc + 2 ] = * ( pKey + uc ); for ( uc = 0; uc < 6; uc ++ )ucComMF522Buf [ uc + 8 ] = * ( pSnr + uc ); /* 進行冗余校驗,14~16倆個字節存儲校驗結果 */cStatus = PcdComMF522 ( PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, & ulLen );/* 判斷驗證是否成功 */if ( ( cStatus != MI_OK ) || ( ! ( RC522_Read_Register ( Status2Reg ) & 0x08 ) ) )cStatus = MI_ERR; return cStatus;}/*** @brief :在M1卡的指定塊地址寫入指定數據* @param :ucAddr:塊地址* pData:寫入的數據,16字節* @retval :狀態值MI_OK,成功 */ char PcdWrite ( uint8_t ucAddr, uint8_t * pData ) {char cStatus;uint8_t uc, ucComMF522Buf [ MAXRLEN ];uint32_t ulLen;ucComMF522Buf [ 0 ] = PICC_WRITE;//寫塊命令ucComMF522Buf [ 1 ] = ucAddr;//寫塊地址/* 進行循環冗余校驗,將結果存儲在& ucComMF522Buf [ 2 ] */CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );/* PCD_TRANSCEIVE:發送并接收數據命令,通過RC522向卡片發送寫塊命令 */cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );/* 通過卡片返回的信息判斷,RC522是否與卡片正常通信 */if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )cStatus = MI_ERR; if ( cStatus == MI_OK ){//memcpy(ucComMF522Buf, pData, 16);/* 將要寫入的16字節的數據,傳入ucComMF522Buf數組中 */for ( uc = 0; uc < 16; uc ++ )ucComMF522Buf [ uc ] = * ( pData + uc ); /* 冗余校驗 */CalulateCRC ( ucComMF522Buf, 16, & ucComMF522Buf [ 16 ] );/* 通過RC522,將16字節數據包括2字節校驗結果寫入卡片中 */cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, & ulLen );/* 判斷寫地址是否成功 */if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )cStatus = MI_ERR; } return cStatus; }/*** @brief :讀取M1卡的指定塊地址的數據* @param :ucAddr:塊地址* pData:讀出的數據,16字節* @retval :狀態值MI_OK,成功 */ char PcdRead ( uint8_t ucAddr, uint8_t * pData ) {char cStatus;uint8_t uc, ucComMF522Buf [ MAXRLEN ]; uint32_t ulLen;ucComMF522Buf [ 0 ] = PICC_READ;ucComMF522Buf [ 1 ] = ucAddr;/* 冗余校驗 */CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );/* 通過RC522將命令傳給卡片 */cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );/* 如果傳輸正常,將讀取到的數據傳入pData中 */if ( ( cStatus == MI_OK ) && ( ulLen == 0x90 ) ){for ( uc = 0; uc < 16; uc ++ )* ( pData + uc ) = ucComMF522Buf [ uc ]; }elsecStatus = MI_ERR; return cStatus;}/*** @brief :讓卡片進入休眠模式* @param :無* @retval :狀態值MI_OK,成功 */ char PcdHalt( void ) {uint8_t ucComMF522Buf [ MAXRLEN ]; uint32_t ulLen;ucComMF522Buf [ 0 ] = PICC_HALT;ucComMF522Buf [ 1 ] = 0;CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );return MI_OK;}

詳情請看代碼。

5.測試函數

通過測試函數來試一下對M1卡的識別,讀取數據等。

在這里插入代碼片char cStr [ 30 ]; /* 卡的ID存儲,32位,4字節 */ u8 ucArray_ID [ 4 ]; /*** @brief : 測試代碼,讀取卡片ID* @param :無* @retval :無 */ void IC_test ( void ) { uint8_t ucStatusReturn; //返回狀態 while ( 1 ){ /* 尋卡(方式:范圍內全部),第一次尋卡失敗后再進行一次,尋卡成功時卡片序列傳入數組ucArray_ID中 */if ( ( ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID ) ) != MI_OK )ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID ); if ( ucStatusReturn == MI_OK ){/* 防沖突操作,被選中的卡片序列傳入數組ucArray_ID中 */if ( PcdAnticoll ( ucArray_ID ) == MI_OK ){sprintf ( cStr, "The Card ID is: %02X%02X%02X%02X", ucArray_ID [ 0 ], ucArray_ID [ 1 ], ucArray_ID [ 2 ], ucArray_ID [ 3 ] ); printf ("%s\r\n",cStr ); }}} }

代碼親測可以用,有什么疑問可以多交流。
q:2723808286

總結

以上是生活随笔為你收集整理的STM32—驱动RFID-RC522模块的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。