近場通信(NEAR FIELD COMMUNICATION, NFC),又稱近距離無線通信,是一種短距離的高頻無線通信技術,允許電子設備之間進行非接觸式點對點數(shù)據(jù)傳輸(在十厘米內(nèi))交換數(shù)據(jù)。這個技術由免接觸式射頻識別(RFID)演變而來,并向下兼容RFID,最早由SONY和PHILIPS各自開發(fā)成功,主要用于手機等手持設備中提供M2M(MACHINE TO MACHINE)的通信。
由于近場通訊具有天然的安全性,因此,NFC技術被認為在手機支付等領域具有很大的應用前景。同時,NFC也因為其相比于其他無線通訊技術較好的安全性被中國物聯(lián)網(wǎng)校企聯(lián)盟比作機器之間的“安全對話”。
一模塊來源
模塊實物展示:
資料鏈接:https://pan.baidu.com/s/1pGSaohXnOi8tu6M3i3KFcQ
資料提取碼:suah
二規(guī)格參數(shù)
工作電壓:3.3V
工作電流:10-26mA
模塊尺寸:40mm×60mm
支持的卡類型:mifare1 S50、mifare1 S70、mifare UltraLight、mifare Pro、mifare Desfire
控制方式:SPI
以上信息見廠家資料文件
三移植過程
我們的目標是將例程移植至CW32F030C8T6開發(fā)板上【能夠識別IC卡的ID并進行讀寫的功能】。首先要獲取資料,查看數(shù)據(jù)手冊應如何實現(xiàn)讀取數(shù)據(jù),再移植至我們的工程。
3.1查看資料
S50非接觸式IC卡,分為16個扇區(qū),每個扇區(qū)由4塊(塊0、塊1、塊2、塊3)組成,(我們也將16個扇區(qū)的64個塊按絕對地址編號為0~63,存貯結(jié)構如下圖所示:
第0扇區(qū)的塊0(即絕對地址0塊),它用于存放廠商代碼,已經(jīng)固化,不可更改。
每個扇區(qū)的塊0、塊1、塊2為數(shù)據(jù)塊,可用于存貯數(shù)據(jù)。數(shù)據(jù)塊可作兩種應用:
用作一般的數(shù)據(jù)保存,可以進行讀、寫操作。
用作數(shù)據(jù)值,可以進行初始化值、加值、減值、讀值操作。
每個扇區(qū)的塊3為控制塊,包括了密碼A、存取控制、密碼B。具體結(jié)構如下:
每個扇區(qū)的密碼和存取控制都是獨立的,可以根據(jù)實際需要設定各自的密碼及存取控制。存取控制為4個字節(jié),共32位,扇區(qū)中的每個塊(包括數(shù)據(jù)塊和控制塊)的存取條件是由密碼和存取控制共同決定的,在存取控制中每個塊都有相應的三個控制位,定義如下:
塊0:C10 C20 C30
塊1:C11 C21 C31
塊2:C12 C22 C32
塊3:C13 C23 C33
三個控制位以正和反兩種形式存在于存取控制字節(jié)中,決定了該塊的訪問權限(如進行減值操作必須驗證KEY A,進行加值操作必須驗證KEY B,等等)。
三個控制位在存取控制字節(jié)中的位置,以塊0為例(對塊0的控制):
數(shù)據(jù)塊(塊0、塊1、塊2)的存取控制如下:
(KeyA|B 表示密碼A或密碼B,Never表示任何條件下不能實現(xiàn))
例如:當塊0的存取控制位C10 C20 C30=1 0 0時,驗證密碼A或密碼B正確后可讀;驗證密碼B正確后可寫;不能進行加值、減值操作。
控制塊塊3的存取控制與數(shù)據(jù)塊(塊0、1、2)不同,它的存取控制如下:
例如:當塊3的存取控制位C13 C23 C33=1 0 0時,表示:
密碼A:不可讀,驗證KEYA或KEYB正確后,可寫(更改)。
存取控制:驗證KEYA或KEYB正確后,可讀、可寫。
密碼B:驗證KEYA或KEYB正確后,可讀、可寫。
M1射頻卡與讀寫器的通訊
3.2引腳選擇
模塊接線圖
3.3移植至工程
移植步驟中的導入.c和.h文件與【CW32模塊使用】DHT11溫濕度傳感器相同,只是將.c和.h文件更改為bsp_rc522.c與bsp_rc522.h。這里不再過多講述,移植完成后面修改相關代碼。
在文件bsp_rc522.c中,編寫如下代碼。
/*
* Change Logs:
* Date Author Notes
* 2024-06-21 LCKFB-LP first version
*/
#include "bsp_rc522.h"
/******************************************************************
* 函 數(shù) 名 稱:RC522_Init
* 函 數(shù) 說 明:IC卡感應模塊配置
* 函 數(shù) 形 參:無
* 函 數(shù) 返 回:無
* 作 者:LC
* 備 注:
******************************************************************/
void RC522_Init(void)
{
//開啟GPIO時鐘
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ā)送一個字節(jié)數(shù)據(jù),高位先行 */
void RC522_SPI_SendByte( uint8_t byte )
{
uint8_t n;
for( n=0;n<8;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讀取一個字節(jié)數(shù)據(jù),先讀高位 */
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;
}
//////////////////////////STM32對RC522寄存器的操作//////////////////////////////////
/* 讀取RC522指定寄存器的值
向RC522指定寄存器中寫入指定的數(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指定寄存器中寫入指定的數(shù)據(jù)
* @param :Address:寄存器地址
data:要寫入寄存器的數(shù)據(jù)
* @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) );
}
///////////////////STM32對RC522的基礎通信///////////////////////////////////
/*
開啟天線
關閉天線
復位RC522
設置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 ); //定義發(fā)送和接收常用模式
RC522_Write_Register( TReloadRegL, 30 ); //16位定時器低位
RC522_Write_Register( TReloadRegH, 0 ); //16位定時器高位
RC522_Write_Register( TModeReg, 0x8D ); //內(nèi)部定時器的設置
RC522_Write_Register( TPrescalerReg, 0x3E ); //設置定時器分頻系數(shù)
RC522_Write_Register( TxAutoReg, 0x40 ); //調(diào)制發(fā)送信號為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();
}
}
/////////////////////////STM32控制RC522與M1卡的通信///////////////////////////////////////
/*
通過RC522和M1卡通訊(數(shù)據(jù)的雙向傳輸)
尋卡
防沖突
用RC522計算CRC16(循環(huán)冗余校驗)
選定卡片
校驗卡片密碼
在M1卡的指定塊地址寫入指定數(shù)據(jù)
讀取M1卡的指定塊地址的數(shù)據(jù)
讓卡片進入休眠模式
*/
/**
* @brief :通過RC522和ISO14443卡通訊
* @param :ucCommand:RC522命令字
* pInData:通過RC522發(fā)送到卡片的數(shù)據(jù)
* ucInLenByte:發(fā)送數(shù)據(jù)的字節(jié)長度
* pOutData:接收到的卡片返回數(shù)據(jù)
* pOutLenBit:返回數(shù)據(jù)的位長度
* @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認證
ucIrqEn = 0x12; //允許錯誤中斷請求ErrIEn 允許空閑中斷IdleIEn
ucWaitFor = 0x10; //認證尋卡等待時候 查詢空閑中斷標志位
break;
case PCD_TRANSCEIVE: //接收發(fā)送 發(fā)送接收
ucIrqEn = 0x77; //允許TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
ucWaitFor = 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清除內(nèi)部FIFO的讀和寫指針以及ErrReg的BufferOvfl標志位被清除
for ( ul = 0; ul < ucInLenByte; ul ++ )
RC522_Write_Register ( FIFODataReg, pInData [ ul ] ); //寫數(shù)據(jù)進FIFOdata
RC522_Write_Register ( CommandReg, ucCommand ); //寫命令
if ( ucCommand == PCD_TRANSCEIVE )
RC522_SetBit_Register(BitFramingReg,0x80); //StartSend置位啟動數(shù)據(jù)發(fā)送 該位與收發(fā)命令使用時才有效
ul = 1000;//根據(jù)時鐘頻率調(diào)整,操作M1卡最大等待時間25ms
do //認證 與尋卡等待時間
{
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 ) //是否發(fā)生定時器中斷
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個字節(jié)數(shù)減去1(最后一個字節(jié))+最后一位的位數(shù) 讀取到的數(shù)據(jù)總位數(shù)
else
* pOutLenBit = ucN * 8; //最后接收到的字節(jié)整個字節(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:尋感應區(qū)內(nèi)所有符合14443A標準的卡
* = 0x26:尋未進入休眠狀態(tài)的卡
* pTagType,卡片類型代碼
* = 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ā)送的最后一個字節(jié)的 七位
RC522_SetBit_Register ( TxControlReg, 0x03 ); //TX1,TX2管腳的輸出信號傳遞經(jīng)發(fā)送調(diào)制的13.46的能量載波信號
ucComMF522Buf [ 0 ] = ucReq_code; //存入尋卡方式
/* PCD_TRANSCEIVE:發(fā)送并接收數(shù)據(jù)的命令,RC522向卡片發(fā)送尋卡命令,卡片返回卡的型號代碼到ucComMF522Buf中 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, & ulLen ); //尋卡
if ( ( cStatus == MI_OK ) && ( ulLen == 0x10 ) ) //尋卡成功返回卡類型
{
/* 接收卡片的型號代碼 */
* pTagType = ucComMF522Buf [ 0 ];
* ( pTagType + 1 ) = ucComMF522Buf [ 1 ];
}
else
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief :防沖突
* @param :Snr:卡片序列,4字節(jié),會返回選中卡片的序列
* @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;
/* 將卡片防沖突命令通過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計算CRC16(循環(huán)冗余校驗)
* @param :pIndata:計算CRC16的數(shù)組
* ucLen:計算CRC16的數(shù)組字節(jié)長度
* pOutData:存放計算結(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:卡片序列號,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 :校驗卡片密碼
* @param :ucAuth_mode:密碼驗證模式
* = 0x60,驗證A密鑰
* = 0x61,驗證B密鑰
* ucAddr:塊地址
* pKey:密碼
* pSnr:卡片序列號,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é)存儲驗證模式和塊地址,2~8字節(jié)存儲密碼(6個字節(jié)),8~14字節(jié)存儲序列號 */
for ( uc = 0; uc < 6; uc ++ )
ucComMF522Buf [ uc + 2 ] = * ( pKey + uc );
for ( uc = 0; uc < 6; uc ++ )
ucComMF522Buf [ uc + 8 ] = * ( pSnr + uc );
/* 進行冗余校驗,14~16倆個字節(jié)存儲校驗結(jié)果 */
cStatus = PcdComMF522 ( PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, & ulLen );
/* 判斷驗證是否成功 */
if ( ( cStatus != MI_OK ) || ( ! ( RC522_Read_Register ( Status2Reg ) & 0x08 ) ) )
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief :在M1卡的指定塊地址寫入指定數(shù)據(jù)
* @param :ucAddr:塊地址
* pData:寫入的數(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;//寫塊命令
ucComMF522Buf [ 1 ] = ucAddr;//寫塊地址
/* 進行循環(huán)冗余校驗,將結(jié)果存儲在& ucComMF522Buf [ 2 ] */
CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
/* PCD_TRANSCEIVE:發(fā)送并接收數(shù)據(jù)命令,通過RC522向卡片發(fā)送寫塊命令 */
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字節(jié)的數(shù)據(jù),傳入ucComMF522Buf數(shù)組中 */
for ( uc = 0; uc < 16; uc ++ )
ucComMF522Buf [ uc ] = * ( pData + uc );
/* 冗余校驗 */
CalulateCRC ( ucComMF522Buf, 16, & ucComMF522Buf [ 16 ] );
/* 通過RC522,將16字節(jié)數(shù)據(jù)包括2字節(jié)校驗結(jié)果寫入卡片中 */
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卡的指定塊地址的數(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;
/* 冗余校驗 */
CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
/* 通過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 :讓卡片進入休眠模式
* @param :無
* @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中,編寫如下代碼。
/*
* 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 //取消當前命令
#define PCD_AUTHENT 0x0E //驗證密鑰
#define PCD_RECEIVE 0x08 //接收數(shù)據(jù)
#define PCD_TRANSMIT 0x04 //發(fā)送數(shù)據(jù)
#define PCD_TRANSCEIVE 0x0C //發(fā)送并接收數(shù)據(jù)
#define PCD_RESETPHASE 0x0F //復位
#define PCD_CALCCRC 0x03 //CRC計算
//Mifare_One卡片命令字
#define PICC_REQIDL 0x26 //尋天線區(qū)內(nèi)未進入休眠狀態(tài)
#define PICC_REQALL 0x52 //尋天線區(qū)內(nèi)全部卡
#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 //調(diào)塊數(shù)據(jù)到緩沖區(qū)
#define PICC_TRANSFER 0xB0 //保存緩沖區(qū)中數(shù)據(jù)
#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 //啟動和停止命令的執(zhí)行
#define ComIEnReg 0x02 //中斷請求傳遞的使能(Enable/Disable)
#define DivlEnReg 0x03 //中斷請求傳遞的使能
#define ComIrqReg 0x04 //包含中斷請求標志
#define DivIrqReg 0x05 //包含中斷請求標志
#define ErrorReg 0x06 //錯誤標志,指示執(zhí)行的上個命令的錯誤狀態(tài)
#define Status1Reg 0x07 //包含通信的狀態(tài)標識
#define Status2Reg 0x08 //包含接收器和發(fā)送器的狀態(tài)標志
#define FIFODataReg 0x09 //64字節(jié)FIFO緩沖區(qū)的輸入和輸出
#define FIFOLevelReg 0x0A //指示FIFO中存儲的字節(jié)數(shù)
#define WaterLevelReg 0x0B //定義FIFO下溢和上溢報警的FIFO深度
#define ControlReg 0x0C //不同的控制寄存器
#define BitFramingReg 0x0D //面向位的幀的調(diào)節(jié)
#define CollReg 0x0E //RF接口上檢測到的第一個位沖突的位的位置
#define RFU0F 0x0F //保留
// PAGE 1
#define RFU10 0x10 //保留
#define ModeReg 0x11 //定義發(fā)送和接收的常用模式
#define TxModeReg 0x12 //定義發(fā)送過程的數(shù)據(jù)傳輸速率
#define RxModeReg 0x13 //定義接收過程中的數(shù)據(jù)傳輸速率
#define TxControlReg 0x14 //控制天線驅(qū)動器管教TX1和TX2的邏輯特性
#define TxAutoReg 0x15 //控制天線驅(qū)動器的設置
#define TxSelReg 0x16 //選擇天線驅(qū)動器的內(nèi)部源
#define RxSelReg 0x17 //選擇內(nèi)部的接收器設置
#define RxThresholdReg 0x18 //選擇位譯碼器的閾值
#define DemodReg 0x19 //定義解調(diào)器的設置
#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 //選擇天線驅(qū)動器管腳(TX1和TX2)的調(diào)制電導
#define CWGsCfgReg 0x28 //選擇天線驅(qū)動器管腳的調(diào)制電導
#define ModGsCfgReg 0x29 //選擇天線驅(qū)動器管腳的調(diào)制電導
#define TModeReg 0x2A //定義內(nèi)部定時器的設置
#define TPrescalerReg 0x2B //定義內(nèi)部定時器的設置
#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輸出驅(qū)動器的使能管腳(僅用于串行接口)
#define TestPinValueReg 0x34 //定義D1-D7用作I/O總線時的值
#define TestBusReg 0x35 //顯示內(nèi)部測試總線的狀態(tài)
#define AutoTestReg 0x36 //控制數(shù)字自測試
#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
/**********************************************************************/
void RC522_Init(void);/* IO口初始化 */
////////////////軟件模擬SPI與RC522通信///////////////////////////////////////////
void RC522_SPI_SendByte( uint8_t byte );/* 軟件模擬SPI發(fā)送一個字節(jié)數(shù)據(jù),高位先行 */
uint8_t RC522_SPI_ReadByte( void );/* 軟件模擬SPI讀取一個字節(jié)數(shù)據(jù),先讀高位 */
uint8_t RC522_Read_Register( uint8_t Address );//讀取RC522指定寄存器的值
void RC522_Write_Register( uint8_t Address, uint8_t data );//向RC522指定寄存器中寫入指定的數(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對RC522的基礎通信///////////////////////////////////
void RC522_Antenna_On( void );//開啟天線
void RC522_Antenna_Off( void );//關閉天線
void RC522_Rese( void );//復位RC522
void RC522_Config_Type( char Type );//設置RC522的工作方式
/////////////////////////STM32控制RC522與M1的通信///////////////////////////////////////
char PcdComMF522 ( uint8_t ucCommand, uint8_t * pInData, uint8_t ucInLenByte, uint8_t * pOutData, uint32_t * pOutLenBit );//通過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計算CRC16(循環(huán)冗余校驗)
char PcdSelect ( uint8_t * pSnr );//選定卡片
char PcdAuthState ( uint8_t ucAuth_mode, uint8_t ucAddr, uint8_t * pKey, uint8_t * pSnr );//校驗卡片密碼
char PcdWrite ( uint8_t ucAddr, uint8_t * pData );//在M1卡的指定塊地址寫入指定數(shù)據(jù)
char PcdRead ( uint8_t ucAddr, uint8_t * pData );//讀取M1卡的指定塊地址的數(shù)據(jù)
char PcdHalt( void );//讓卡片進入休眠模式
#endif
四移植驗證
在自己工程中的main主函數(shù)中,編寫如下。
/*
* 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存儲,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};//讀寫數(shù)據(jù)緩存
uint8_t card_KEY[6] ={0xff,0xff,0xff,0xff,0xff,0xff};//默認密碼
board_init();
uart1_init(115200U);
printf ("Init....rn");
RC522_Init( );//IC卡IO口初始化
RC522_Rese( );//復位RC522
printf ("Start!rn");
while(1)
{
/* 尋卡(方式:范圍內(nèi)全部),第一次尋卡失敗后再進行一次,尋卡成功時卡片序列傳入數(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"); }
//校驗卡片密碼
//數(shù)據(jù)塊6的密碼A進行校驗(所有密碼默認為16個字節(jié)的0xff)
if( PcdAuthState(PICC_AUTHENT1B, 6, card_KEY, ucArray_ID) != MI_OK )
{ printf("PcdAuthState failurern"); }
//往數(shù)據(jù)塊4寫入數(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,然后寫入0xaa,之后將數(shù)據(jù)讀出發(fā)送至串口。
模塊移植成功案例代碼:
鏈接:https://pan.baidu.com/s/1G2mJkPs4eGR7D1eKMDLAPw?pwd=LCKF
提取碼:LCKF