近場(chǎng)通信(NEAR FIELD COMMUNICATION, NFC),又稱(chēng)近距離無(wú)線(xiàn)通信,是一種短距離的高頻無(wú)線(xiàn)通信技術(shù),允許電子設(shè)備之間進(jìn)行非接觸式點(diǎn)對(duì)點(diǎn)數(shù)據(jù)傳輸(在十厘米內(nèi))交換數(shù)據(jù)。這個(gè)技術(shù)由免接觸式射頻識(shí)別(RFID)演變而來(lái),并向下兼容RFID,最早由SONY和PHILIPS各自開(kāi)發(fā)成功,主要用于手機(jī)等手持設(shè)備中提供M2M(MACHINE TO MACHINE)的通信。由于近場(chǎng)通訊具有天然的安全性,因此,NFC技術(shù)被認(rèn)為在手機(jī)支付等領(lǐng)域具有很大的應(yīng)用前景。同時(shí),NFC也因?yàn)槠湎啾扔谄渌麩o(wú)線(xiàn)通訊技術(shù)較好的安全性被中國(guó)物聯(lián)網(wǎng)校企聯(lián)盟比作機(jī)器之間的“安全對(duì)話(huà)”。
一、模塊來(lái)源
模塊實(shí)物展示:

資料鏈接:https://pan.baidu.com/s/1pGSaohXnOi8tu6M3i3KFcQ
資料提取碼:suah
二、規(guī)格參數(shù)
工作電壓:3.3V
工作電流:10-26mA
模塊尺寸:40mm×60mm
支持的卡類(lèi)型:mifare1 S50、mifare1 S70、mifare UltraLight、mifare Pro、mifare Desfire
控制方式:SPI
以上信息見(jiàn)廠家資料文件
三、移植過(guò)程
我們的目標(biāo)是將例程移植至CW32F030C8T6開(kāi)發(fā)板上【能夠識(shí)別IC卡的ID并進(jìn)行讀寫(xiě)的功能】。首先要獲取資料,查看數(shù)據(jù)手冊(cè)應(yīng)如何實(shí)現(xiàn)讀取數(shù)據(jù),再移植至我們的工程。
3.1查看資料
S50非接觸式IC卡,分為16個(gè)扇區(qū),每個(gè)扇區(qū)由4塊(塊0、塊1、塊2、塊3)組成,(我們也將16個(gè)扇區(qū)的64個(gè)塊按絕對(duì)地址編號(hào)為0~63,存貯結(jié)構(gòu)如下圖所示:

第0扇區(qū)的塊0(即絕對(duì)地址0塊),它用于存放廠商代碼,已經(jīng)固化,不可更改。
每個(gè)扇區(qū)的塊0、塊1、塊2為數(shù)據(jù)塊,可用于存貯數(shù)據(jù)。數(shù)據(jù)塊可作兩種應(yīng)用:
用作一般的數(shù)據(jù)保存,可以進(jìn)行讀、寫(xiě)操作。
用作數(shù)據(jù)值,可以進(jìn)行初始化值、加值、減值、讀值操作。
每個(gè)扇區(qū)的塊3為控制塊,包括了密碼A、存取控制、密碼B。具體結(jié)構(gòu)如下:

每個(gè)扇區(qū)的密碼和存取控制都是獨(dú)立的,可以根據(jù)實(shí)際需要設(shè)定各自的密碼及存取控制。存取控制為4個(gè)字節(jié),共32位,扇區(qū)中的每個(gè)塊(包括數(shù)據(jù)塊和控制塊)的存取條件是由密碼和存取控制共同決定的,在存取控制中每個(gè)塊都有相應(yīng)的三個(gè)控制位,定義如下:
塊0:C10 C20 C30 塊1:C11 C21 C31 塊2:C12 C22 C32 塊3:C13 C23 C33
三個(gè)控制位以正和反兩種形式存在于存取控制字節(jié)中,決定了該塊的訪問(wèn)權(quán)限(如進(jìn)行減值操作必須驗(yàn)證KEY A,進(jìn)行加值操作必須驗(yàn)證KEY B,等等)。
三個(gè)控制位在存取控制字節(jié)中的位置,以塊0為例(對(duì)塊0的控制):


數(shù)據(jù)塊(塊0、塊1、塊2)的存取控制如下:

(KeyA|B 表示密碼A或密碼B,Never表示任何條件下不能實(shí)現(xiàn))
例如:當(dāng)塊0的存取控制位C10 C20 C30=1 0 0時(shí),驗(yàn)證密碼A或密碼B正確后可讀;驗(yàn)證密碼B正確后可寫(xiě);不能進(jìn)行加值、減值操作。
控制塊塊3的存取控制與數(shù)據(jù)塊(塊0、1、2)不同,它的存取控制如下:

例如:當(dāng)塊3的存取控制位C13 C23 C33=1 0 0時(shí),表示:
密碼A:不可讀,驗(yàn)證KEYA或KEYB正確后,可寫(xiě)(更改)。
存取控制:驗(yàn)證KEYA或KEYB正確后,可讀、可寫(xiě)。
密碼B:驗(yàn)證KEYA或KEYB正確后,可讀、可寫(xiě)。
M1射頻卡與讀寫(xiě)器的通訊

3.2引腳選擇

模塊接線(xiàn)圖
3.3移植至工程
移植步驟中的導(dǎo)入.c和.h文件與【CW32模塊使用】DHT11溫濕度傳感器相同,只是將.c和.h文件更改為bsp_rc522.c與bsp_rc522.h。這里不再過(guò)多講述,移植完成后面修改相關(guān)代碼。
在文件bsp_rc522.c中,編寫(xiě)如下代碼。
/* * Change Logs: * Date Author Notes * 2024-06-21 LCKFB-LP first version */ #include "bsp_rc522.h" /****************************************************************** * 函 數(shù) 名 稱(chēng):RC522_Init * 函 數(shù) 說(shuō) 明:IC卡感應(yīng)模塊配置 * 函 數(shù) 形 參:無(wú) * 函 數(shù) 返 回:無(wú) * 作 者:LC * 備 注: ******************************************************************/ void RC522_Init(void) { //開(kāi)啟GPIO時(shí)鐘 RCC_GPIO_ENABLE(); GPIO_InitTypeDef GPIO_InitStructure; // SDA SCK MOSI RST GPIO_InitStructure.Pins = GPIO_CS|GPIO_SCK|GPIO_MOSI|GPIO_RST; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; GPIO_Init(PORT_GPIO, &GPIO_InitStructure); GPIO_WritePin(PORT_GPIO, GPIO_CS|GPIO_SCK|GPIO_MOSI|GPIO_RST, GPIO_Pin_SET); // MISO GPIO_InitStructure.Pins = GPIO_MISO; GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP; GPIO_Init(PORT_GPIO, &GPIO_InitStructure); } ////////////////軟件模擬SPI與RC522通信/////////////////////////////////////////// /* 軟件模擬SPI發(fā)送一個(gè)字節(jié)數(shù)據(jù),高位先行 */ void RC522_SPI_SendByte( uint8_t byte ) { uint8_t n; for( n=0;n8;n++ ) { if( byte&0x80 ) RC522_MOSI_1(); else RC522_MOSI_0(); delay_us(200); RC522_SCK_0(); delay_us(200); RC522_SCK_1(); delay_us(200); byte<=1; } } /* 軟件模擬SPI讀取一個(gè)字節(jié)數(shù)據(jù),先讀高位 */ uint8_t RC522_SPI_ReadByte( void ) { uint8_t n,data; for( n=0;n8;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; } //////////////////////////STM32對(duì)RC522寄存器的操作////////////////////////////////// /* 讀取RC522指定寄存器的值 向RC522指定寄存器中寫(xiě)入指定的數(shù)據(jù) 置位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指定寄存器中寫(xiě)入指定的數(shù)據(jù) * @param :Address:寄存器地址 data:要寫(xiě)入寄存器的數(shù)據(jù) * @retval :無(wú) */ 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 :無(wú) */ void RC522_SetBit_Register( uint8_t Address, uint8_t mask ) { uint8_t temp; /* 獲取寄存器當(dāng)前值 */ temp = RC522_Read_Register( Address ); /* 對(duì)指定位進(jìn)行置位操作后,再將值寫(xiě)入寄存器 */ RC522_Write_Register( Address, temp|mask ); } /** * @brief :清位RC522指定寄存器的指定位 * @param :Address:寄存器地址 mask:清位值 * @retval :無(wú) */ void RC522_ClearBit_Register( uint8_t Address, uint8_t mask ) { uint8_t temp; /* 獲取寄存器當(dāng)前值 */ temp = RC522_Read_Register( Address ); /* 對(duì)指定位進(jìn)行清位操作后,再將值寫(xiě)入寄存器 */ RC522_Write_Register( Address, temp&(~mask) ); } ///////////////////STM32對(duì)RC522的基礎(chǔ)通信/////////////////////////////////// /* 開(kāi)啟天線(xiàn) 關(guān)閉天線(xiàn) 復(fù)位RC522 設(shè)置RC522工作方式 */ /** * @brief :開(kāi)啟天線(xiàn) * @param :無(wú) * @retval :無(wú) */ void RC522_Antenna_On( void ) { uint8_t k; k = RC522_Read_Register( TxControlReg ); /* 判斷天線(xiàn)是否開(kāi)啟 */ if( !( k&0x03 ) ) RC522_SetBit_Register( TxControlReg, 0x03 ); } /** * @brief :關(guān)閉天線(xiàn) * @param :無(wú) * @retval :無(wú) */ void RC522_Antenna_Off( void ) { /* 直接對(duì)相應(yīng)位清零 */ RC522_ClearBit_Register( TxControlReg, 0x03 ); } /** * @brief :復(fù)位RC522 * @param :無(wú) * @retval :無(wú) */ 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 ); //定義發(fā)送和接收常用模式 RC522_Write_Register( TReloadRegL, 30 ); //16位定時(shí)器低位 RC522_Write_Register( TReloadRegH, 0 ); //16位定時(shí)器高位 RC522_Write_Register( TModeReg, 0x8D ); //內(nèi)部定時(shí)器的設(shè)置 RC522_Write_Register( TPrescalerReg, 0x3E ); //設(shè)置定時(shí)器分頻系數(shù) RC522_Write_Register( TxAutoReg, 0x40 ); //調(diào)制發(fā)送信號(hào)為100%ASK } /** * @brief :設(shè)置RC522的工作方式 * @param :Type:工作方式 * @retval :無(wú) 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); /* 開(kāi)天線(xiàn) */ RC522_Antenna_On(); } } /////////////////////////STM32控制RC522與M1卡的通信/////////////////////////////////////// /* 通過(guò)RC522和M1卡通訊(數(shù)據(jù)的雙向傳輸) 尋卡 防沖突 用RC522計(jì)算CRC16(循環(huán)冗余校驗(yàn)) 選定卡片 校驗(yàn)卡片密碼 在M1卡的指定塊地址寫(xiě)入指定數(shù)據(jù) 讀取M1卡的指定塊地址的數(shù)據(jù) 讓卡片進(jìn)入休眠模式 */ /** * @brief :通過(guò)RC522和ISO14443卡通訊 * @param :ucCommand:RC522命令字 * pInData:通過(guò)RC522發(fā)送到卡片的數(shù)據(jù) * ucInLenByte:發(fā)送數(shù)據(jù)的字節(jié)長(zhǎng)度 * pOutData:接收到的卡片返回?cái)?shù)據(jù) * pOutLenBit:返回?cái)?shù)據(jù)的位長(zhǎng)度 * @retval :狀態(tài)值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認(rèn)證 ucIrqEn = 0x12; //允許錯(cuò)誤中斷請(qǐng)求ErrIEn 允許空閑中斷IdleIEn ucWaitFor = 0x10; //認(rèn)證尋卡等待時(shí)候 查詢(xún)空閑中斷標(biāo)志位 break; case PCD_TRANSCEIVE: //接收發(fā)送 發(fā)送接收 ucIrqEn = 0x77; //允許TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn ucWaitFor = 0x30; //尋卡等待時(shí)候 查詢(xún)接收中斷標(biāo)志位與 空閑中斷標(biāo)志位 break; default: break; } RC522_Write_Register ( ComIEnReg, ucIrqEn | 0x80 ); //IRqInv置位管腳IRQ與Status1Reg的IRq位的值相反 RC522_ClearBit_Register ( ComIrqReg, 0x80 ); //Set1該位清零時(shí),CommIRqReg的屏蔽位清零 RC522_Write_Register ( CommandReg, PCD_IDLE ); //寫(xiě)空閑命令 RC522_SetBit_Register ( FIFOLevelReg, 0x80 ); //置位FlushBuffer清除內(nèi)部FIFO的讀和寫(xiě)指針以及ErrReg的BufferOvfl標(biāo)志位被清除 for ( ul = 0; ul < ucInLenByte; ul ++ ) RC522_Write_Register ( FIFODataReg, pInData [ ul ] ); //寫(xiě)數(shù)據(jù)進(jìn)FIFOdata RC522_Write_Register ( CommandReg, ucCommand ); //寫(xiě)命令 if ( ucCommand == PCD_TRANSCEIVE ) RC522_SetBit_Register(BitFramingReg,0x80); //StartSend置位啟動(dòng)數(shù)據(jù)發(fā)送 該位與收發(fā)命令使用時(shí)才有效 ul = 1000;//根據(jù)時(shí)鐘頻率調(diào)整,操作M1卡最大等待時(shí)間25ms do //認(rèn)證 與尋卡等待時(shí)間 { ucN = RC522_Read_Register ( ComIrqReg ); //查詢(xún)事件中斷 ul --; } while ( ( ul != 0 ) && ( ! ( ucN & 0x01 ) ) && ( ! ( ucN & ucWaitFor ) ) ); //退出條件i=0,定時(shí)器中斷,與寫(xiě)空閑命令 RC522_ClearBit_Register ( BitFramingReg, 0x80 ); //清理允許StartSend位 if ( ul != 0 ) { if ( ! ( RC522_Read_Register ( ErrorReg ) & 0x1B ) ) //讀錯(cuò)誤標(biāo)志寄存器BufferOfI CollErr ParityErr ProtocolErr { cStatus = MI_OK; if ( ucN & ucIrqEn & 0x01 ) //是否發(fā)生定時(shí)器中斷 cStatus = MI_NOTAGERR; if ( ucCommand == PCD_TRANSCEIVE ) { ucN = RC522_Read_Register ( FIFOLevelReg ); //讀FIFO中保存的字節(jié)數(shù) ucLastBits = RC522_Read_Register ( ControlReg ) & 0x07; //最后接收到得字節(jié)的有效位數(shù) if ( ucLastBits ) * pOutLenBit = ( ucN - 1 ) * 8 + ucLastBits; //N個(gè)字節(jié)數(shù)減去1(最后一個(gè)字節(jié))+最后一位的位數(shù) 讀取到的數(shù)據(jù)總位數(shù) else * pOutLenBit = ucN * 8; //最后接收到的字節(jié)整個(gè)字節(jié)有效 if ( ucN == 0 ) ucN = 1; if ( ucN > MAXRLEN ) ucN = MAXRLEN; for ( ul = 0; ul < ucN; ul ++ ) pOutData [ ul ] = RC522_Read_Register ( FIFODataReg ); } } else cStatus = MI_ERR; } RC522_SetBit_Register ( ControlReg, 0x80 ); // stop timer now RC522_Write_Register ( CommandReg, PCD_IDLE ); return cStatus; } /** * @brief :尋卡 * @param ucReq_code,尋卡方式 * = 0x52:尋感應(yīng)區(qū)內(nèi)所有符合14443A標(biāo)準(zhǔn)的卡 * = 0x26:尋未進(jìn)入休眠狀態(tài)的卡 * pTagType,卡片類(lèi)型代碼 * = 0x4400:Mifare_UltraLight * = 0x0400:Mifare_One(S50) * = 0x0200:Mifare_One(S70) * = 0x0800:Mifare_Pro(X)) * = 0x4403:Mifare_DESFire * @retval :狀態(tài)值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單元接通以及所有卡的數(shù)據(jù)通信被加密的情況 RC522_Write_Register ( BitFramingReg, 0x07 ); // 發(fā)送的最后一個(gè)字節(jié)的 七位 RC522_SetBit_Register ( TxControlReg, 0x03 ); //TX1,TX2管腳的輸出信號(hào)傳遞經(jīng)發(fā)送調(diào)制的13.46的能量載波信號(hào) ucComMF522Buf [ 0 ] = ucReq_code; //存入尋卡方式 /* PCD_TRANSCEIVE:發(fā)送并接收數(shù)據(jù)的命令,RC522向卡片發(fā)送尋卡命令,卡片返回卡的型號(hào)代碼到ucComMF522Buf中 */ cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, & ulLen ); //尋卡 if ( ( cStatus == MI_OK ) && ( ulLen == 0x10 ) ) //尋卡成功返回卡類(lèi)型 { /* 接收卡片的型號(hào)代碼 */ * pTagType = ucComMF522Buf [ 0 ]; * ( pTagType + 1 ) = ucComMF522Buf [ 1 ]; } else cStatus = MI_ERR; return cStatus; } /** * @brief :防沖突 * @param :Snr:卡片序列,4字節(jié),會(huì)返回選中卡片的序列 * @retval :狀態(tài)值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位 只有成功執(zhí)行MFAuthent命令后,該位才能置位 RC522_Write_Register ( BitFramingReg, 0x00); //清理寄存器 停止收發(fā) RC522_ClearBit_Register ( CollReg, 0x80 ); //清ValuesAfterColl所有接收的位在沖突后被清除 ucComMF522Buf [ 0 ] = 0x93; //卡片防沖突命令 ucComMF522Buf [ 1 ] = 0x20; /* 將卡片防沖突命令通過(guò)RC522傳到卡片中,返回的是被選中卡片的序列 */ cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, & ulLen);//與卡片通信 if ( cStatus == MI_OK) //通信成功 { for ( uc = 0; uc < 4; uc ++ ) { * ( pSnr + uc ) = ucComMF522Buf [ uc ]; //讀出UID ucSnr_check ^= ucComMF522Buf [ uc ]; } if ( ucSnr_check != ucComMF522Buf [ uc ] ) cStatus = MI_ERR; } RC522_SetBit_Register ( CollReg, 0x80 ); return cStatus; } /** * @brief :用RC522計(jì)算CRC16(循環(huán)冗余校驗(yàn)) * @param :pIndata:計(jì)算CRC16的數(shù)組 * ucLen:計(jì)算CRC16的數(shù)組字節(jié)長(zhǎng)度 * pOutData:存放計(jì)算結(jié)果存放的首地址 * @retval :狀態(tài)值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:卡片序列號(hào),4字節(jié) * @retval :狀態(tài)值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; else ucN = MI_ERR; return ucN; } /** * @brief :校驗(yàn)卡片密碼 * @param :ucAuth_mode:密碼驗(yàn)證模式 * = 0x60,驗(yàn)證A密鑰 * = 0x61,驗(yàn)證B密鑰 * ucAddr:塊地址 * pKey:密碼 * pSnr:卡片序列號(hào),4字節(jié) * @retval :狀態(tài)值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; /* 前倆字節(jié)存儲(chǔ)驗(yàn)證模式和塊地址,2~8字節(jié)存儲(chǔ)密碼(6個(gè)字節(jié)),8~14字節(jié)存儲(chǔ)序列號(hào) */ for ( uc = 0; uc < 6; uc ++ ) ucComMF522Buf [ uc + 2 ] = * ( pKey + uc ); for ( uc = 0; uc < 6; uc ++ ) ucComMF522Buf [ uc + 8 ] = * ( pSnr + uc ); /* 進(jìn)行冗余校驗(yàn),14~16倆個(gè)字節(jié)存儲(chǔ)校驗(yàn)結(jié)果 */ cStatus = PcdComMF522 ( PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, & ulLen ); /* 判斷驗(yàn)證是否成功 */ if ( ( cStatus != MI_OK ) || ( ! ( RC522_Read_Register ( Status2Reg ) & 0x08 ) ) ) cStatus = MI_ERR; return cStatus; } /** * @brief :在M1卡的指定塊地址寫(xiě)入指定數(shù)據(jù) * @param :ucAddr:塊地址 * pData:寫(xiě)入的數(shù)據(jù),16字節(jié) * @retval :狀態(tài)值MI_OK,成功 */ char PcdWrite ( uint8_t ucAddr, uint8_t * pData ) { char cStatus; uint8_t uc, ucComMF522Buf [ MAXRLEN ]; uint32_t ulLen; ucComMF522Buf [ 0 ] = PICC_WRITE;//寫(xiě)塊命令 ucComMF522Buf [ 1 ] = ucAddr;//寫(xiě)塊地址 /* 進(jìn)行循環(huán)冗余校驗(yàn),將結(jié)果存儲(chǔ)在& ucComMF522Buf [ 2 ] */ CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] ); /* PCD_TRANSCEIVE:發(fā)送并接收數(shù)據(jù)命令,通過(guò)RC522向卡片發(fā)送寫(xiě)塊命令 */ cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen ); /* 通過(guò)卡片返回的信息判斷,RC522是否與卡片正常通信 */ if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) ) cStatus = MI_ERR; if ( cStatus == MI_OK ) { //memcpy(ucComMF522Buf, pData, 16); /* 將要寫(xiě)入的16字節(jié)的數(shù)據(jù),傳入ucComMF522Buf數(shù)組中 */ for ( uc = 0; uc < 16; uc ++ ) ucComMF522Buf [ uc ] = * ( pData + uc ); /* 冗余校驗(yàn) */ CalulateCRC ( ucComMF522Buf, 16, & ucComMF522Buf [ 16 ] ); /* 通過(guò)RC522,將16字節(jié)數(shù)據(jù)包括2字節(jié)校驗(yàn)結(jié)果寫(xiě)入卡片中 */ cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, & ulLen ); /* 判斷寫(xiě)地址是否成功 */ if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) ) cStatus = MI_ERR; } return cStatus; } /** * @brief :讀取M1卡的指定塊地址的數(shù)據(jù) * @param :ucAddr:塊地址 * pData:讀出的數(shù)據(jù),16字節(jié) * @retval :狀態(tài)值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; /* 冗余校驗(yàn) */ CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] ); /* 通過(guò)RC522將命令傳給卡片 */ cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen ); /* 如果傳輸正常,將讀取到的數(shù)據(jù)傳入pData中 */ if ( ( cStatus == MI_OK ) && ( ulLen == 0x90 ) ) { for ( uc = 0; uc < 16; uc ++ ) * ( pData + uc ) = ucComMF522Buf [ uc ]; } else cStatus = MI_ERR; return cStatus; } /** * @brief :讓卡片進(jìn)入休眠模式 * @param :無(wú) * @retval :狀態(tài)值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; }
在文件bsp_rc522.h中,編寫(xiě)如下代碼。
/* * Change Logs: * Date Author Notes * 2024-06-21 LCKFB-LP first version */ #ifndef _BSP_RC522_H #define _BSP_RC522_H #include "board.h" #ifndef u8 #define u8 uint8_t #endif #ifndef u16 #define u16 uint16_t #endif #ifndef u32 #define u32 uint32_t #endif #define RCC_GPIO_ENABLE() __RCC_GPIOA_CLK_ENABLE() #define PORT_GPIO CW_GPIOA //SDA #define GPIO_CS GPIO_PIN_1 //SCK #define GPIO_SCK GPIO_PIN_2 //MOSI #define GPIO_MOSI GPIO_PIN_3 //RST #define GPIO_RST GPIO_PIN_5 //MISO #define GPIO_MISO GPIO_PIN_4 /* IO口操作函數(shù) */ #define RC522_CS_Enable() GPIO_WritePin(PORT_GPIO, GPIO_CS,GPIO_Pin_RESET) #define RC522_CS_Disable() GPIO_WritePin(PORT_GPIO, GPIO_CS,GPIO_Pin_SET) #define RC522_Reset_Enable() GPIO_WritePin(PORT_GPIO, GPIO_RST,GPIO_Pin_RESET) #define RC522_Reset_Disable() GPIO_WritePin(PORT_GPIO, GPIO_RST,GPIO_Pin_SET) #define RC522_SCK_0() GPIO_WritePin(PORT_GPIO, GPIO_SCK,GPIO_Pin_RESET) #define RC522_SCK_1() GPIO_WritePin(PORT_GPIO, GPIO_SCK,GPIO_Pin_SET) #define RC522_MOSI_0() GPIO_WritePin(PORT_GPIO, GPIO_MOSI,GPIO_Pin_RESET) #define RC522_MOSI_1() GPIO_WritePin(PORT_GPIO, GPIO_MOSI,GPIO_Pin_SET) #define RC522_MISO_GET() GPIO_ReadPin(PORT_GPIO, GPIO_MISO) //RC522命令字 #define PCD_IDLE 0x00 //取消當(dāng)前命令 #define PCD_AUTHENT 0x0E //驗(yàn)證密鑰 #define PCD_RECEIVE 0x08 //接收數(shù)據(jù) #define PCD_TRANSMIT 0x04 //發(fā)送數(shù)據(jù) #define PCD_TRANSCEIVE 0x0C //發(fā)送并接收數(shù)據(jù) #define PCD_RESETPHASE 0x0F //復(fù)位 #define PCD_CALCCRC 0x03 //CRC計(jì)算 //Mifare_One卡片命令字 #define PICC_REQIDL 0x26 //尋天線(xiàn)區(qū)內(nèi)未進(jìn)入休眠狀態(tài) #define PICC_REQALL 0x52 //尋天線(xiàn)區(qū)內(nèi)全部卡 #define PICC_ANTICOLL1 0x93 //防沖撞 #define PICC_ANTICOLL2 0x95 //防沖撞 #define PICC_AUTHENT1A 0x60 //驗(yàn)證A密鑰 #define PICC_AUTHENT1B 0x61 //驗(yàn)證B密鑰 #define PICC_READ 0x30 //讀塊 #define PICC_WRITE 0xA0 //寫(xiě)塊 #define PICC_DECREMENT 0xC0 //扣款 #define PICC_INCREMENT 0xC1 //充值 #define PICC_RESTORE 0xC2 //調(diào)塊數(shù)據(jù)到緩沖區(qū) #define PICC_TRANSFER 0xB0 //保存緩沖區(qū)中數(shù)據(jù) #define PICC_HALT 0x50 //休眠 /* RC522 FIFO長(zhǎng)度定義 */ #define DEF_FIFO_LENGTH 64 //FIFO size=64byte #define MAXRLEN 18 /* RC522寄存器定義 */ // PAGE 0 #define RFU00 0x00 //保留 #define CommandReg 0x01 //啟動(dòng)和停止命令的執(zhí)行 #define ComIEnReg 0x02 //中斷請(qǐng)求傳遞的使能(Enable/Disable) #define DivlEnReg 0x03 //中斷請(qǐng)求傳遞的使能 #define ComIrqReg 0x04 //包含中斷請(qǐng)求標(biāo)志 #define DivIrqReg 0x05 //包含中斷請(qǐng)求標(biāo)志 #define ErrorReg 0x06 //錯(cuò)誤標(biāo)志,指示執(zhí)行的上個(gè)命令的錯(cuò)誤狀態(tài) #define Status1Reg 0x07 //包含通信的狀態(tài)標(biāo)識(shí) #define Status2Reg 0x08 //包含接收器和發(fā)送器的狀態(tài)標(biāo)志 #define FIFODataReg 0x09 //64字節(jié)FIFO緩沖區(qū)的輸入和輸出 #define FIFOLevelReg 0x0A //指示FIFO中存儲(chǔ)的字節(jié)數(shù) #define WaterLevelReg 0x0B //定義FIFO下溢和上溢報(bào)警的FIFO深度 #define ControlReg 0x0C //不同的控制寄存器 #define BitFramingReg 0x0D //面向位的幀的調(diào)節(jié) #define CollReg 0x0E //RF接口上檢測(cè)到的第一個(gè)位沖突的位的位置 #define RFU0F 0x0F //保留 // PAGE 1 #define RFU10 0x10 //保留 #define ModeReg 0x11 //定義發(fā)送和接收的常用模式 #define TxModeReg 0x12 //定義發(fā)送過(guò)程的數(shù)據(jù)傳輸速率 #define RxModeReg 0x13 //定義接收過(guò)程中的數(shù)據(jù)傳輸速率 #define TxControlReg 0x14 //控制天線(xiàn)驅(qū)動(dòng)器管教TX1和TX2的邏輯特性 #define TxAutoReg 0x15 //控制天線(xiàn)驅(qū)動(dòng)器的設(shè)置 #define TxSelReg 0x16 //選擇天線(xiàn)驅(qū)動(dòng)器的內(nèi)部源 #define RxSelReg 0x17 //選擇內(nèi)部的接收器設(shè)置 #define RxThresholdReg 0x18 //選擇位譯碼器的閾值 #define DemodReg 0x19 //定義解調(diào)器的設(shè)置 #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計(jì)算的實(shí)際MSB值 #define CRCResultRegL 0x22 //顯示CRC計(jì)算的實(shí)際LSB值 #define RFU23 0x23 //保留 #define ModWidthReg 0x24 //控制ModWidth的設(shè)置 #define RFU25 0x25 //保留 #define RFCfgReg 0x26 //配置接收器增益 #define GsNReg 0x27 //選擇天線(xiàn)驅(qū)動(dòng)器管腳(TX1和TX2)的調(diào)制電導(dǎo) #define CWGsCfgReg 0x28 //選擇天線(xiàn)驅(qū)動(dòng)器管腳的調(diào)制電導(dǎo) #define ModGsCfgReg 0x29 //選擇天線(xiàn)驅(qū)動(dòng)器管腳的調(diào)制電導(dǎo) #define TModeReg 0x2A //定義內(nèi)部定時(shí)器的設(shè)置 #define TPrescalerReg 0x2B //定義內(nèi)部定時(shí)器的設(shè)置 #define TReloadRegH 0x2C //描述16位長(zhǎng)的定時(shí)器重裝值 #define TReloadRegL 0x2D //描述16位長(zhǎng)的定時(shí)器重裝值 #define TCounterValueRegH 0x2E #define TCounterValueRegL 0x2F //顯示16位長(zhǎng)的實(shí)際定時(shí)器值 // PAGE 3 #define RFU30 0x30 //保留 #define TestSel1Reg 0x31 //常用測(cè)試信號(hào)配置 #define TestSel2Reg 0x32 //常用測(cè)試信號(hào)配置和PRBS控制 #define TestPinEnReg 0x33 //D1-D7輸出驅(qū)動(dòng)器的使能管腳(僅用于串行接口) #define TestPinValueReg 0x34 //定義D1-D7用作I/O總線(xiàn)時(shí)的值 #define TestBusReg 0x35 //顯示內(nèi)部測(cè)試總線(xiàn)的狀態(tài) #define AutoTestReg 0x36 //控制數(shù)字自測(cè)試 #define VersionReg 0x37 //顯示版本 #define AnalogTestReg 0x38 //控制管腳AUX1和AUX2 #define TestDAC1Reg 0x39 //定義TestDAC1的測(cè)試值 #define TestDAC2Reg 0x3A //定義TestDAC2的測(cè)試值 #define TestADCReg 0x3B //顯示ADCI和Q通道的實(shí)際值 #define RFU3C 0x3C //保留 #define RFU3D 0x3D //保留 #define RFU3E 0x3E //保留 #define RFU3F 0x3F //保留 /* 和RC522通信時(shí)返回的錯(cuò)誤代碼 */ #define MI_OK 0x26 #define MI_NOTAGERR 0xcc #define MI_ERR 0xbb /**********************************************************************/ void RC522_Init(void);/* IO口初始化 */ ////////////////軟件模擬SPI與RC522通信/////////////////////////////////////////// void RC522_SPI_SendByte( uint8_t byte );/* 軟件模擬SPI發(fā)送一個(gè)字節(jié)數(shù)據(jù),高位先行 */ uint8_t RC522_SPI_ReadByte( void );/* 軟件模擬SPI讀取一個(gè)字節(jié)數(shù)據(jù),先讀高位 */ uint8_t RC522_Read_Register( uint8_t Address );//讀取RC522指定寄存器的值 void RC522_Write_Register( uint8_t Address, uint8_t data );//向RC522指定寄存器中寫(xiě)入指定的數(shù)據(jù) void RC522_SetBit_Register( uint8_t Address, uint8_t mask );//置位RC522指定寄存器的指定位 void RC522_ClearBit_Register( uint8_t Address, uint8_t mask );//清位RC522指定寄存器的指定位 /////////////////////STM32對(duì)RC522的基礎(chǔ)通信/////////////////////////////////// void RC522_Antenna_On( void );//開(kāi)啟天線(xiàn) void RC522_Antenna_Off( void );//關(guān)閉天線(xiàn) void RC522_Rese( void );//復(fù)位RC522 void RC522_Config_Type( char Type );//設(shè)置RC522的工作方式 /////////////////////////STM32控制RC522與M1的通信/////////////////////////////////////// char PcdComMF522 ( uint8_t ucCommand, uint8_t * pInData, uint8_t ucInLenByte, uint8_t * pOutData, uint32_t * pOutLenBit );//通過(guò)RC522和ISO14443卡通訊 char PcdRequest ( uint8_t ucReq_code, uint8_t * pTagType );//尋卡 char PcdAnticoll ( uint8_t * pSnr );//防沖突 void CalulateCRC ( uint8_t * pIndata, u8 ucLen, uint8_t * pOutData );//用RC522計(jì)算CRC16(循環(huán)冗余校驗(yàn)) char PcdSelect ( uint8_t * pSnr );//選定卡片 char PcdAuthState ( uint8_t ucAuth_mode, uint8_t ucAddr, uint8_t * pKey, uint8_t * pSnr );//校驗(yàn)卡片密碼 char PcdWrite ( uint8_t ucAddr, uint8_t * pData );//在M1卡的指定塊地址寫(xiě)入指定數(shù)據(jù) char PcdRead ( uint8_t ucAddr, uint8_t * pData );//讀取M1卡的指定塊地址的數(shù)據(jù) char PcdHalt( void );//讓卡片進(jìn)入休眠模式 #endif
四、移植驗(yàn)證
在自己工程中的main主函數(shù)中,編寫(xiě)如下。
/*
* Change Logs:
* Date Author Notes
* 2024-06-21 LCKFB-LP first version
*/
#include "board.h"
#include "stdio.h"
#include "bsp_uart.h"
#include "bsp_rc522.h"
/* 卡的ID存儲(chǔ),32位,4字節(jié) */
u8 ucArray_ID [ 4 ];
uint8_t ucStatusReturn; //返回狀態(tài)
int32_t main(void)
{
int i = 0;
uint8_t read_write_data[16]={0};//讀寫(xiě)數(shù)據(jù)緩存
uint8_t card_KEY[6] ={0xff,0xff,0xff,0xff,0xff,0xff};//默認(rèn)密碼
board_init();
uart1_init(115200U);
printf ("Init....rn");
RC522_Init( );//IC卡IO口初始化
RC522_Rese( );//復(fù)位RC522
printf ("Start!rn");
while(1)
{
/* 尋卡(方式:范圍內(nèi)全部),第一次尋卡失敗后再進(jìn)行一次,尋卡成功時(shí)卡片序列傳入數(shù)組ucArray_ID中 */
if ( ( ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID ) ) != MI_OK )
{
ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID );
}
if ( ucStatusReturn == MI_OK )
{
/* 防沖突操作,被選中的卡片序列傳入數(shù)組ucArray_ID中 */
if ( PcdAnticoll ( ucArray_ID ) == MI_OK )
{
//輸出卡ID
printf("ID: %X %X %X %Xrn", ucArray_ID [ 0 ], ucArray_ID [ 1 ], ucArray_ID [ 2 ], ucArray_ID [ 3 ]);
//選卡
if( PcdSelect(ucArray_ID) != MI_OK )
{ printf("PcdSelect failurern"); }
//校驗(yàn)卡片密碼
//數(shù)據(jù)塊6的密碼A進(jìn)行校驗(yàn)(所有密碼默認(rèn)為16個(gè)字節(jié)的0xff)
if( PcdAuthState(PICC_AUTHENT1B, 6, card_KEY, ucArray_ID) != MI_OK )
{ printf("PcdAuthState failurern"); }
//往數(shù)據(jù)塊4寫(xiě)入數(shù)據(jù)read_write_data
read_write_data[0] = 0xaa;//將read_write_data的第一位數(shù)據(jù)改為0xaa
if( PcdWrite(4,read_write_data) != MI_OK )
{ printf("PcdWrite failurern"); }
//將read_write_data的16位數(shù)據(jù),填充為0(清除數(shù)據(jù)的意思)
memset(read_write_data,0,16);
delay_us(8);
//讀取數(shù)據(jù)塊4的數(shù)據(jù)
if( PcdRead(4,read_write_data) != MI_OK )
{ printf("PcdRead failurern"); }
//輸出讀出的數(shù)據(jù)
for( i = 0; i < 16; i++ )
{
printf("%x ",read_write_data[i]);
}
printf("rn");
}
}
}
}
移植現(xiàn)象:串口輸出讀取到的卡ID,然后寫(xiě)入0xaa,之后將數(shù)據(jù)讀出發(fā)送至串口。

模塊移植成功案例代碼:
鏈接:https://pan.baidu.com/s/1G2mJkPs4eGR7D1eKMDLAPw?pwd=LCKF
提取碼:LCKF
審核編輯 黃宇
-
射頻
+關(guān)注
關(guān)注
106文章
5952瀏覽量
172931 -
IC卡
+關(guān)注
關(guān)注
2文章
175瀏覽量
36392 -
nfc
+關(guān)注
關(guān)注
62文章
1719瀏覽量
184976 -
RC522
+關(guān)注
關(guān)注
7文章
57瀏覽量
24727 -
CW32
+關(guān)注
關(guān)注
1文章
281瀏覽量
1689
發(fā)布評(píng)論請(qǐng)先 登錄
請(qǐng)問(wèn)CW32系列微控制器是否有擴(kuò)展模塊或外設(shè)板卡可供選擇?
CW32 MCU溫度監(jiān)測(cè)應(yīng)用
SI522與恩智浦 RC522 13.56MHZ的刷卡問(wèn)題
CW32 MCU用什么IDE開(kāi)發(fā)?
CW32 MCU有哪些系列?
CW32 MCU的工作電壓、工作溫度是多少?
CW32模塊使用 0.91寸彩屏
單片機(jī)實(shí)例:RC522無(wú)線(xiàn)射頻項(xiàng)目資料
ESP32驅(qū)動(dòng)MFRC522 RFID模塊讀寫(xiě)IC卡數(shù)據(jù)
SI522A 低功耗尋卡與多款13.56MHZ 兼容開(kāi)發(fā)資料
CW32模塊使用 紅外接收模塊
CW32模塊使用 紅外解碼編碼模塊

CW32模塊使用 RC522射頻IC卡識(shí)別模塊
評(píng)論