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是通過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模块的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS开发之SEL用法
- 下一篇: Unity InputField光标位置