一、硬件设计
硬件接线:
SPI_NSS(CS) -- PA4
SPI_SCK-- PA5
SPI_MISO -- PA6
SPI_MOSI -- PA7
SPI_RST -- PD2
二、软件设计
本项目用到的是IO口模拟SPI,STM32Cubemx直接将PA4、PA5、PA7、PD2配置为推挽输出,PA6配置为输入即可。
RC522.c
#include "rc522.h"
#define RC522_DELAY() delay_us ( 2 )
#define fac_us 72 //时钟频率,单位MHZ
uint32_t RC522_ID1 = 0xED0B562D; //小圆球ID
void Get_RC522(void)
{
uint32_t writeValue = 100;
uint32_t readValue;
char cStr [ 30 ];
uint8_t ucArray_ID [ 4 ]; /*先后存放IC卡的类型和UID(IC卡序列号)*/
uint8_t ucStatusReturn; /*返回状态*/
static uint8_t Rc_Timeout;
uint8_t status;
uint8_t Card_ID[4];
HAL_Delay(100);
/*寻卡*/
while( (ucStatusReturn = PcdRequest ( PICC_REQIDL, ucArray_ID )) != MI_OK )
{
/*若失败再次寻卡*/
Rc_Timeout++;
HAL_Delay(100);
//printf ( "寻卡失败\n" );
ucStatusReturn = PcdRequest ( PICC_REQIDL, ucArray_ID ); //PICC_REQALL PICC_REQIDL
if(Rc_Timeout >= 10)
{
Rc_Timeout = 0;
break;
}
}
if ( ucStatusReturn == MI_OK )
{
printf ( "寻卡成功\n" );
status = PcdAnticoll(Card_ID);
//判断读取的ID是否为的小圆球ID,是的话再做操作。
if(status)
{
if(Card_ID[0] == ((RC522_ID1 & 0xFF000000) >> 24) )
if(Card_ID[1] == ((RC522_ID1 & 0x00FF0000) >> 16))
if(Card_ID[2] == ((RC522_ID1 & 0x0000FF00) >> 8))
if(Card_ID[3] == ((RC522_ID1 & 0x000000FF) >>0 ))
printf("小圆球\n");
}
}
}
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
uint32_t reload=SysTick->LOAD; //LOAD的值
ticks=nus*fac_us; //需要的节拍数
told=SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
}
}
}
/**
* @brief RC522_Init
* @param 无
* @retval 无
*/
void RC522_Init ( void )
{
// RC522_Reset_Disable();
// RC522_CS_Disable();
PcdReset ();/*RC522相关*/
HAL_Delay(50);
PcdReset();//复位RC522读卡器
HAL_Delay(10);
PcdReset();//复位RC522读卡器
HAL_Delay(10);
PcdAntennaOff();//关闭天线发射
HAL_Delay(10);
PcdAntennaOn();//开启天线发射
// M500PcdConfigISOType ( 'A' );/*RC52射频模 设置工作方式*/
printf("RFID-MFRC522 初始化完成\r\nFindCard Starting ...\r\n");
}
/**
* @brief IC测试函数
* @param 无
* @retval 无
*/
extern uint8_t KeyValue[]; // 卡A密钥
void IC_test ( void )
{
uint32_t writeValue = 100;
uint32_t readValue;
char cStr [ 30 ];
uint8_t ucArray_ID [ 4 ]; /*先后存放IC卡的类型和UID(IC卡序列号)*/
uint8_t ucStatusReturn; /*返回状态*/
static uint8_t Rc_Timeout;
while ( 1 )
{
/*寻卡*/
if ( ( ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID ) ) != MI_OK )
{
/*若失败再次寻卡*/
Rc_Timeout++;
HAL_Delay(1000);
printf ( "寻卡失败\n" );
ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID ); //PICC_REQALL PICC_REQIDL
if(Rc_Timeout >= 10)
{
Rc_Timeout = 0;
break;
}
}
if ( ucStatusReturn == MI_OK )
{
printf ( "寻卡成功\n" );
//Voice_Send("RC522 OK");
//delay_ms(2000);
HAL_Delay(2000);
break;
// /*防冲撞(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)*/
// if ( PcdAnticoll ( ucArray_ID ) == MI_OK )
// {
// PcdSelect(ucArray_ID);
//
// PcdAuthState( PICC_AUTHENT1A, 0x11, KeyValue, ucArray_ID );//校验密码
// WriteAmount(0x11,writeValue); //写入金额
// if(ReadAmount(0x11,&readValue) == MI_OK) //读取金额
// {
// writeValue +=100;
// 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 ); //打印卡片ID
//
// printf ("余额为:%d\r\n",readValue);
// sprintf ( cStr, "TThe residual amount: %d", readValue);
// PcdHalt();
// }
// }
}
}
}
/**
* @brief 向RC522发送1 Byte 数据
* @param byte,要发送的数据
* @retval RC522返回的数据
*/
void SPI_RC522_SendByte ( uint8_t byte )
{
uint8_t counter;
for(counter=0;counter<8;counter++)
{
if ( byte & 0x80 )
RC522_MOSI_1 ();
else
RC522_MOSI_0 ();
RC522_DELAY();
RC522_SCK_0 ();
RC522_DELAY();
RC522_SCK_1();
RC522_DELAY();
byte <<= 1;
}
}
/**
* @brief 从RC522发送1 Byte 数据
* @param 无
* @retval RC522返回的数据
*/
uint8_t SPI_RC522_ReadByte ( void )
{
uint8_t counter;
uint8_t SPI_Data;
for(counter=0;counter<8;counter++)
{
SPI_Data <<= 1;
RC522_SCK_0 ();
RC522_DELAY();
if ( RC522_MISO_GET() == 1)
SPI_Data |= 0x01;
RC522_DELAY();
RC522_SCK_1 ();
RC522_DELAY();
}
return SPI_Data;
}
/**
* @brief 读RC522寄存器
* @param ucAddress,寄存器地址
* @retval 寄存器的当前值
*/
uint8_t ReadRawRC ( uint8_t ucAddress )
{
uint8_t ucAddr, ucReturn;
ucAddr = ( ( ucAddress << 1 ) & 0x7E ) | 0x80;
RC522_CS_Enable();
SPI_RC522_SendByte ( ucAddr );
ucReturn = SPI_RC522_ReadByte ();
RC522_CS_Disable();
return ucReturn;
}
/**
* @brief 写RC522寄存器
* @param ucAddress,寄存器地址
* @param ucValue,写入寄存器的值
* @retval 无
*/
void WriteRawRC ( uint8_t ucAddress, uint8_t ucValue )
{
uint8_t ucAddr;
ucAddr = ( ucAddress << 1 ) & 0x7E;
RC522_CS_Enable();
SPI_RC522_SendByte ( ucAddr );
SPI_RC522_SendByte ( ucValue );
RC522_CS_Disable();
}
/**
* @brief 对RC522寄存器置位
* @param ucReg,寄存器地址
* @param ucMask,置位值
* @retval 无
*/
void SetBitMask ( uint8_t ucReg, uint8_t ucMask )
{
uint8_t ucTemp;
ucTemp = ReadRawRC ( ucReg );
WriteRawRC ( ucReg, ucTemp | ucMask ); // set bit mask
}
/**
* @brief 对RC522寄存器清位
* @param ucReg,寄存器地址
* @param ucMask,清位值
* @retval 无
*/
void ClearBitMask ( uint8_t ucReg, uint8_t ucMask )
{
uint8_t ucTemp;
ucTemp = ReadRawRC ( ucReg );
WriteRawRC ( ucReg, ucTemp & ( ~ ucMask) ); // clear bit mask
}
/**
* @brief 开启天线
* @param 无
* @retval 无
*/
void PcdAntennaOn ( void )
{
uint8_t uc;
uc = ReadRawRC ( TxControlReg );
if ( ! ( uc & 0x03 ) )
SetBitMask(TxControlReg, 0x03);
}
/**
* @brief 关闭天线
* @param 无
* @retval 无
*/
void PcdAntennaOff ( void )
{
ClearBitMask ( TxControlReg, 0x03 );
}
/**
* @brief 复位RC522
* @param 无
* @retval 无
*/
void PcdReset ( void )
{
RC522_Reset_Disable();
delay_us ( 1 );
RC522_Reset_Enable();
delay_us ( 1 );
RC522_Reset_Disable();
delay_us ( 1 );
WriteRawRC ( CommandReg, 0x0f );
while ( ReadRawRC ( CommandReg ) & 0x10 );
delay_us ( 1 );
//定义发送和接收常用模式 和Mifare卡通讯,CRC初始值0x6363
WriteRawRC ( ModeReg, 0x3D );
WriteRawRC ( TReloadRegL, 30 ); //16位定时器低位
WriteRawRC ( TReloadRegH, 0 ); //16位定时器高位
WriteRawRC ( TModeReg, 0x8D ); //定义内部定时器的设置
WriteRawRC ( TPrescalerReg, 0x3E ); //设置定时器分频系数
WriteRawRC ( TxAutoReg, 0x40 ); //调制发送信号为100%ASK
}
/**
* @brief 设置RC522的工作方式
* @param ucType,工作方式
* @retval 无
*/
void M500PcdConfigISOType ( uint8_t ucType )
{
if ( ucType == 'A') //ISO14443_A
{
ClearBitMask ( Status2Reg, 0x08 );
WriteRawRC ( ModeReg, 0x3D ); //3F
WriteRawRC ( RxSelReg, 0x86 ); //84
WriteRawRC( RFCfgReg, 0x7F ); //4F
WriteRawRC( TReloadRegL, 30 );
WriteRawRC ( TReloadRegH, 0 );
WriteRawRC ( TModeReg, 0x8D );
WriteRawRC ( TPrescalerReg, 0x3E );
delay_us ( 2 );
PcdAntennaOn ();//开天线
}
}
/**
* @brief 通过RC522和ISO14443卡通讯
* @param ucCommand,RC522命令字
* @param pInData,通过RC522发送到卡片的数据
* @param ucInLenByte,发送数据的字节长度
* @param pOutData,接收到的卡片返回数据
* @param 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 允许空闲中断IdleIEn
ucWaitFor = 0x10; //认证寻卡等待时候 查询空闲中断标志位
break;
case PCD_TRANSCEIVE: //接收发送 发送接收
ucIrqEn = 0x77; //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
ucWaitFor = 0x30; //寻卡等待时候 查询接收中断标志位与 空闲中断标志位
break;
default:
break;
}
//IRqInv置位管脚IRQ与Status1Reg的IRq位的值相反
WriteRawRC ( ComIEnReg, ucIrqEn | 0x80 );
//Set1该位清零时,CommIRqReg的屏蔽位清零
ClearBitMask ( ComIrqReg, 0x80 );
//写空闲命令
WriteRawRC ( CommandReg, PCD_IDLE );
//置位FlushBuffer清除内部FIFO的读和写指针以及ErrReg的BufferOvfl标志位被清除
SetBitMask ( FIFOLevelReg, 0x80 );
for ( ul = 0; ul < ucInLenByte; ul ++ )
WriteRawRC ( FIFODataReg, pInData [ ul ] ); //写数据进FIFOdata
WriteRawRC ( CommandReg, ucCommand ); //写命令
if ( ucCommand == PCD_TRANSCEIVE )
//StartSend置位启动数据发送 该位与收发命令使用时才有效
SetBitMask(BitFramingReg,0x80);
ul = 1000; //根据时钟频率调整,操作M1卡最大等待时间25ms
do //认证 与寻卡等待时间
{
ucN = ReadRawRC ( ComIrqReg ); //查询事件中断
ul --;
} while ( ( ul != 0 ) && ( ! ( ucN & 0x01 ) ) && ( ! ( ucN & ucWaitFor ) ) );
ClearBitMask ( BitFramingReg, 0x80 ); //清理允许StartSend位
if ( ul != 0 )
{
//读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
if ( ! ( ReadRawRC ( ErrorReg ) & 0x1B ) )
{
cStatus = MI_OK;
if ( ucN & ucIrqEn & 0x01 ) //是否发生定时器中断
cStatus = MI_NOTAGERR;
if ( ucCommand == PCD_TRANSCEIVE )
{
//读FIFO中保存的字节数
ucN = ReadRawRC ( FIFOLevelReg );
//最后接收到得字节的有效位数
ucLastBits = ReadRawRC ( ControlReg ) & 0x07;
if ( ucLastBits )
//N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
* pOutLenBit = ( ucN - 1 ) * 8 + ucLastBits;
else
* pOutLenBit = ucN * 8; //最后接收到的字节整个字节有效
if ( ucN == 0 )
ucN = 1;
if ( ucN > MAXRLEN )
ucN = MAXRLEN;
for ( ul = 0; ul < ucN; ul ++ )
pOutData [ ul ] = ReadRawRC ( FIFODataReg );
}
}
else
cStatus = MI_ERR;
}
SetBitMask ( ControlReg, 0x80 ); // stop timer now
WriteRawRC ( CommandReg, PCD_IDLE );
return cStatus;
}
/**
* @brief 寻卡
* @param ucReq_code,寻卡方式 = 0x52,寻感应区内所有符合14443A标准的卡;
寻卡方式= 0x26,寻未进入休眠状态的卡
* @param 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;
//清理指示MIFARECyptol单元接通以及所有卡的数据通信被加密的情况
ClearBitMask ( Status2Reg, 0x08 );
//发送的最后一个字节的 七位
WriteRawRC ( BitFramingReg, 0x07 );
//TX1,TX2管脚的输出信号传递经发送调制的13.56的能量载波信号
SetBitMask ( TxControlReg, 0x03 );
ucComMF522Buf [ 0 ] = ucReq_code; //存入 卡片命令字
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 pSnr,卡片序列号,4字节
* @retval 状态值= MI_OK,成功
*/
char PcdAnticoll ( uint8_t * pSnr )
{
char cStatus;
uint8_t uc, ucSnr_check = 0;
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
//清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
ClearBitMask ( Status2Reg, 0x08 );
//清理寄存器 停止收发
WriteRawRC ( BitFramingReg, 0x00);
//清ValuesAfterColl所有接收的位在冲突后被清除
ClearBitMask ( CollReg, 0x80 );
ucComMF522Buf [ 0 ] = 0x93; //卡片防冲突命令
ucComMF522Buf [ 1 ] = 0x20;
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;
}
SetBitMask ( CollReg, 0x80 );
return cStatus;
}
/**
* @brief 用RC522计算CRC16
* @param pIndata,计算CRC16的数组
* @param ucLen,计算CRC16的数组字节长度
* @param pOutData,存放计算结果存放的首地址
* @retval 无
*/
void CalulateCRC ( uint8_t * pIndata,
uint8_t ucLen,
uint8_t * pOutData )
{
uint8_t uc, ucN;
ClearBitMask(DivIrqReg,0x04);
WriteRawRC(CommandReg,PCD_IDLE);
SetBitMask(FIFOLevelReg,0x80);
for ( uc = 0; uc < ucLen; uc ++)
WriteRawRC ( FIFODataReg, * ( pIndata + uc ) );
WriteRawRC ( CommandReg, PCD_CALCCRC );
uc = 0xFF;
do
{
ucN = ReadRawRC ( DivIrqReg );
uc --;
} while ( ( uc != 0 ) && ! ( ucN & 0x04 ) );
pOutData [ 0 ] = ReadRawRC ( CRCResultRegL );
pOutData [ 1 ] = ReadRawRC ( 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;
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 ] );
ClearBitMask ( 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密钥
* @param uint8_t ucAddr,块地址
* @param pKey,密码
* @param 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;
for ( uc = 0; uc < 6; uc ++ )
ucComMF522Buf [ uc + 2 ] = * ( pKey + uc );
for ( uc = 0; uc < 6; uc ++ )
ucComMF522Buf [ uc + 8 ] = * ( pSnr + uc );
cStatus = PcdComMF522 ( PCD_AUTHENT,
ucComMF522Buf,
12,
ucComMF522Buf,
& ulLen );
if ( ( cStatus != MI_OK ) || ( ! ( ReadRawRC ( Status2Reg ) & 0x08 ) ) )
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief 写数据到M1卡一块
* @param ucAddr,块地址(0-63)。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
* @param 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;
CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
cStatus = PcdComMF522 ( PCD_TRANSCEIVE,
ucComMF522Buf,
4,
ucComMF522Buf,
& ulLen );
if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) ||
( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
cStatus = MI_ERR;
if ( cStatus == MI_OK )
{
//memcpy(ucComMF522Buf, pData, 16);
for ( uc = 0; uc < 16; uc ++ )
ucComMF522Buf [ uc ] = * ( pData + uc );
CalulateCRC ( ucComMF522Buf, 16, & ucComMF522Buf [ 16 ] );
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,块地址(0-63)。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
* @param 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 ] );
cStatus = PcdComMF522 ( PCD_TRANSCEIVE,
ucComMF522Buf,
4,
ucComMF522Buf,
& ulLen );
if ( ( cStatus == MI_OK ) && ( ulLen == 0x90 ) )
{
for ( uc = 0; uc < 16; uc ++ )
* ( pData + uc ) = ucComMF522Buf [ uc ];
}
else
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief 判断 ucAddr 是否数据块
* @param ucAddr,块绝对地址(0-63)
* @retval 返回值 1:是数据块;0:不是数据块
*/
char IsDataBlock( uint8_t ucAddr )
{
if(ucAddr == 0)
{
printf("第0扇区的块0不可更改,不应对其进行操作\r\n");
return 0;
}
/* 如果是数据块(不包含数据块0) */
if( (ucAddr<64) && (((ucAddr+1)%4) != 0) )
{
return 1;
}
printf("块地址不是指向数据块\r\n");
return 0;
}
/**
* @brief 写 pData 字符串到M1卡中的数据块
* @param ucAddr,数据块地址(不能写入控制块)
* @param pData,写入的数据,16字节
* @retval 状态值= MI_OK,成功
*/
char PcdWriteString ( uint8_t ucAddr, uint8_t * pData )
{
/* 如果是数据块(不包含数据块0),则写入 */
if( IsDataBlock(ucAddr) )
{
return PcdWrite(ucAddr, pData);
}
return MI_ERR;
}
/**
* @brief 读取M1卡中的一块数据到 pData
* @param ucAddr,数据块地址(不读取控制块)
* @param pData,读出的数据,16字节
* @retval 状态值= MI_OK,成功
*/
char PcdReadString ( uint8_t ucAddr, uint8_t * pData )
{
/* 如果是数据块(不包含数据块0),则读取 */
if( IsDataBlock(ucAddr) )
{
return PcdRead(ucAddr, pData);
}
return MI_ERR;
}
/**
* @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;
}
/
//功 能:写入钱包金额
//参数说明: ucAddr[IN]:块地址
// pData:写入的金额
//返 回: 成功返回MI_OK
/
char WriteAmount( uint8_t ucAddr, uint32_t pData )
{
char status;
uint8_t ucComMF522Buf[16];
ucComMF522Buf[0] = (pData&((uint32_t)0x000000ff));
ucComMF522Buf[1] = (pData&((uint32_t)0x0000ff00))>>8;
ucComMF522Buf[2] = (pData&((uint32_t)0x00ff0000))>>16;
ucComMF522Buf[3] = (pData&((uint32_t)0xff000000))>>24;
ucComMF522Buf[4] = ~(pData&((uint32_t)0x000000ff));
ucComMF522Buf[5] = ~(pData&((uint32_t)0x0000ff00))>>8;
ucComMF522Buf[6] = ~(pData&((uint32_t)0x00ff0000))>>16;
ucComMF522Buf[7] = ~(pData&((uint32_t)0xff000000))>>24;
ucComMF522Buf[8] = (pData&((uint32_t)0x000000ff));
ucComMF522Buf[9] = (pData&((uint32_t)0x0000ff00))>>8;
ucComMF522Buf[10] = (pData&((uint32_t)0x00ff0000))>>16;
ucComMF522Buf[11] = (pData&((uint32_t)0xff000000))>>24;
ucComMF522Buf[12] = ucAddr;
ucComMF522Buf[13] = ~ucAddr;
ucComMF522Buf[14] = ucAddr;
ucComMF522Buf[15] = ~ucAddr;
status = PcdWrite(ucAddr,ucComMF522Buf);
return status;
}
/
//功 能:读取钱包金额
//参数说明: ucAddr[IN]:块地址
// *pData:读出的金额
//返 回: 成功返回MI_OK
/
char ReadAmount( uint8_t ucAddr, uint32_t *pData )
{
char status = MI_ERR;
uint8_t j;
uint8_t ucComMF522Buf[16];
status = PcdRead(ucAddr,ucComMF522Buf);
if(status != MI_OK)
return status;
for(j=0;j<4;j++)
{
if((ucComMF522Buf[j] != ucComMF522Buf[j+8]) && (ucComMF522Buf[j] != ~ucComMF522Buf[j+4]))//验证一下是不是钱包的数据
break;
}
if(j == 4)
{
status = MI_OK;
*pData = ucComMF522Buf[0] + (ucComMF522Buf[1]<<8) + (ucComMF522Buf[2]<<16) + (ucComMF522Buf[3]<<24);
}
else
{
status = MI_ERR;
*pData = 0;
}
return status;
}
/**
* @brief 修改控制块 ucAddr 的密码A。注意 ucAddr 指的是控制块的地址。
* 必须要校验密码B,密码B默认为6个0xFF,如果密码B也忘记了,那就改不了密码A了
* @note 注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
* @param ucAddr:[控制块]所在的地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
* @param pKeyA:指向新的密码A字符串,六个字符,比如 "123456"
* @retval 成功返回 MI_OK
*/
char ChangeKeyA( uint8_t ucAddr, uint8_t *pKeyA )
{
uint8_t KeyBValue[]={0xFF ,0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
uint8_t ucArray_ID [ 4 ]; /*先后存放IC卡的类型和UID(IC卡序列号)*/
uint8_t ucComMF522Buf[16];
uint8_t j;
/*寻卡*/
while ( PcdRequest ( PICC_REQALL, ucArray_ID ) != MI_OK )
{
printf( "寻卡失败\r\n" );
delay_us(1000000);
}
printf ( "寻卡成功\n" );
/* 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)*/
if ( PcdAnticoll ( ucArray_ID ) == MI_OK )
{
/* 选中卡 */
PcdSelect(ucArray_ID);
/* 校验 B 密码 */
if( PcdAuthState( PICC_AUTHENT1B, ucAddr, KeyBValue, ucArray_ID ) != MI_OK )
{
printf( "检验密码B失败\r\n" );
}
// 读取控制块里原本的数据(只要修改密码A,其他数据不改)
if( PcdRead(ucAddr,ucComMF522Buf) != MI_OK)
{
printf( "读取控制块数据失败\r\n" );
return MI_ERR;
}
/* 修改密码A */
for(j=0; j<6; j++)
ucComMF522Buf[j] = pKeyA[j];
if( PcdWrite(ucAddr,ucComMF522Buf) != MI_OK)
{
printf( "写入数据到控制块失败\r\n" );
return MI_ERR;
}
printf( "密码A修改成功!\r\n" );
PcdHalt();
return MI_OK;
}
return MI_ERR;
}
/**
* @brief 按照RC522操作流程写入16字节数据到块 ucAddr
* 函数里校验的是密码B,密码B默认为6个0xFF,也可以校验密码A
* 用法:WriteDataBlock( 1, "123456789\n", 10); //字符串不够16个字节的后面补零写入
* @note 注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
* 注意:使用该函数要注意 ucAddr 是块0、数据块还是控制块,该函数内部不对此做判断
* @param ucAddr:任意块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
* @param pData:指向要写入的数据,最大16个字符
* @param Len:要写入数据的字节数
* @retval 成功返回 MI_OK
*/
char WriteDataBlock( uint8_t ucAddr, uint8_t *pData, uint8_t Len)
{
uint8_t KeyBValue[]={0xFF ,0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
uint8_t ucArray_ID [ 4 ]; /*先后存放IC卡的类型和UID(IC卡序列号)*/
uint8_t ucComMF522Buf[16];
uint8_t j;
/*寻卡*/
while ( PcdRequest ( PICC_REQALL, ucArray_ID ) != MI_OK )
{
printf( "寻卡失败\r\n" );
delay_us(1000000);
}
printf ( "寻卡成功\n" );
/* 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)*/
if ( PcdAnticoll ( ucArray_ID ) == MI_OK )
{
/* 选中卡 */
PcdSelect(ucArray_ID);
/* 校验 B 密码 */
if( PcdAuthState( PICC_AUTHENT1B, ucAddr, KeyBValue, ucArray_ID ) != MI_OK )
{
printf( "检验密码B失败\r\n" );
}
/* 拷贝 pData 里的 Len 个字符到 ucComMF522Buf */
for(j=0; j<16; j++)
{
if( j < Len ) ucComMF522Buf[j] = pData[j];
else ucComMF522Buf[j] = 0; //16个字节若是未填满的字节置0
}
/* 写入字符串 */
if( PcdWrite(ucAddr,ucComMF522Buf) != MI_OK)
{
printf( "写入数据到数据块失败\r\n" );
return MI_ERR;
}
printf( "写入数据成功!\r\n" );
PcdHalt();
return MI_OK;
}
return MI_ERR;
}
/**
* @brief 按照RC522操作流程读取块 ucAddr
* 函数里校验的是密码B,密码B默认为6个0xFF,也可以校验密码A
* 用法:ReadDataBlock( 1, databuf); // databuf 至少为16字节:uint8_t databuf[16];
* @note 注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
* 注意:使用该函数要注意 ucAddr 是块0、数据块还是控制块,该函数内部不对此做判断
* @param ucAddr:任意块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
* @param pData:指向读取到的数据,包含16个字符
* @retval 成功返回 MI_OK
*/
char ReadDataBlock( uint8_t ucAddr, uint8_t *pData)
{
uint8_t KeyBValue[]={0xFF ,0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
uint8_t ucArray_ID [ 4 ]; /*先后存放IC卡的类型和UID(IC卡序列号)*/
/*寻卡*/
while ( PcdRequest ( PICC_REQALL, ucArray_ID ) != MI_OK )
{
printf( "寻卡失败\r\n" );
delay_us(1000000);
}
printf ( "寻卡成功\n" );
/* 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)*/
if ( PcdAnticoll ( ucArray_ID ) == MI_OK )
{
/* 选中卡 */
PcdSelect(ucArray_ID);
/* 校验 B 密码 */
if( PcdAuthState( PICC_AUTHENT1B, ucAddr, KeyBValue, ucArray_ID ) != MI_OK )
{
printf( "检验密码B失败\r\n" );
}
// 读取数据块里的数据到 pData
if( PcdRead(ucAddr, pData) != MI_OK)
{
printf( "读取数据块失败\r\n" );
return MI_ERR;
}
printf( "读取数据成功!\r\n" );
PcdHalt();
return MI_OK;
}
return MI_ERR;
}
RC522.h
#ifndef _RC522_H_
#define _RC522_H_
#include "main.h"
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define macDummy_Data 0x00
/
//MF522命令字
/
#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 //休眠
/
//MF522 FIFO长度定义
/
#define DEF_FIFO_LENGTH 64 //FIFO size=64byte
#define MAXRLEN 18
/
//MF522寄存器定义
/
// PAGE 0
#define RFU00 0x00
#define CommandReg 0x01
#define ComIEnReg 0x02
#define DivlEnReg 0x03
#define ComIrqReg 0x04
#define DivIrqReg 0x05
#define ErrorReg 0x06
#define Status1Reg 0x07
#define Status2Reg 0x08
#define FIFODataReg 0x09
#define FIFOLevelReg 0x0A
#define WaterLevelReg 0x0B
#define ControlReg 0x0C
#define BitFramingReg 0x0D
#define CollReg 0x0E
#define RFU0F 0x0F
// PAGE 1
#define RFU10 0x10
#define ModeReg 0x11
#define TxModeReg 0x12
#define RxModeReg 0x13
#define TxControlReg 0x14
#define TxAutoReg 0x15
#define TxSelReg 0x16
#define RxSelReg 0x17
#define RxThresholdReg 0x18
#define DemodReg 0x19
#define RFU1A 0x1A
#define RFU1B 0x1B
#define MifareReg 0x1C
#define RFU1D 0x1D
#define RFU1E 0x1E
#define SerialSpeedReg 0x1F
// PAGE 2
#define RFU20 0x20
#define CRCResultRegM 0x21
#define CRCResultRegL 0x22
#define RFU23 0x23
#define ModWidthReg 0x24
#define RFU25 0x25
#define RFCfgReg 0x26
#define GsNReg 0x27
#define CWGsCfgReg 0x28
#define ModGsCfgReg 0x29
#define TModeReg 0x2A
#define TPrescalerReg 0x2B
#define TReloadRegH 0x2C
#define TReloadRegL 0x2D
#define TCounterValueRegH 0x2E
#define TCounterValueRegL 0x2F
// PAGE 3
#define RFU30 0x30
#define TestSel1Reg 0x31
#define TestSel2Reg 0x32
#define TestPinEnReg 0x33
#define TestPinValueReg 0x34
#define TestBusReg 0x35
#define AutoTestReg 0x36
#define VersionReg 0x37
#define AnalogTestReg 0x38
#define TestDAC1Reg 0x39
#define TestDAC2Reg 0x3A
#define TestADCReg 0x3B
#define RFU3C 0x3C
#define RFU3D 0x3D
#define RFU3E 0x3E
#define RFU3F 0x3F
/
//和MF522通讯时返回的错误代码
/
#define MI_OK 0x26
#define MI_NOTAGERR 0xcc
#define MI_ERR 0xbb
/*********************************** RC522 引脚定义 *********************************************/
//RC522模块有除了电源还有6个数据引脚,其中IRQ不需要使用,悬空即可,剩下的5个数据引脚连接如下:
//如果RC522需要修改与STM32的连接,则修改这些IO即可,但必须连接到STM32的SPI引脚
//片选,即RC522模块的SDA引脚
#define RC522_GPIO_CS_CLK_FUN RCC_APB2PeriphClockCmd
#define RC522_GPIO_CS_CLK RCC_APB2Periph_GPIOA
#define RC522_GPIO_CS_PORT GPIOA
#define RC522_GPIO_CS_PIN GPIO_PIN_4
#define RC522_GPIO_CS_Mode GPIO_Mode_Out_PP
//时钟,即RC522模块的SCK引脚,接STM32的SPI的SCK引脚
#define RC522_GPIO_SCK_CLK_FUN RCC_APB2PeriphClockCmd
#define RC522_GPIO_SCK_CLK RCC_APB2Periph_GPIOA
#define RC522_GPIO_SCK_PORT GPIOA
#define RC522_GPIO_SCK_PIN GPIO_PIN_5
#define RC522_GPIO_SCK_Mode GPIO_Mode_Out_PP
// 数据输入,即即RC522模块的MOSI引脚,接STM32的SPI的MOSI引脚
#define RC522_GPIO_MOSI_CLK_FUN RCC_APB2PeriphClockCmd
#define RC522_GPIO_MOSI_CLK RCC_APB2Periph_GPIOA
#define RC522_GPIO_MOSI_PORT GPIOA
#define RC522_GPIO_MOSI_PIN GPIO_PIN_7
#define RC522_GPIO_MOSI_Mode GPIO_Mode_Out_PP
// 数据输出,即即RC522模块的MISO引脚,接STM32的SPI的MISO引脚
#define RC522_GPIO_MISO_CLK_FUN RCC_APB2PeriphClockCmd
#define RC522_GPIO_MISO_CLK RCC_APB2Periph_GPIOA
#define RC522_GPIO_MISO_PORT GPIOA
#define RC522_GPIO_MISO_PIN GPIO_PIN_6
#define RC522_GPIO_MISO_Mode GPIO_Mode_IN_FLOATING
//复位,即即RC522模块的RST引脚,接STM32的普通IO即可
#define RC522_GPIO_RST_CLK_FUN RCC_APB2PeriphClockCmd
#define RC522_GPIO_RST_CLK RCC_APB2Periph_GPIOD
#define RC522_GPIO_RST_PORT GPIOD
#define RC522_GPIO_RST_PIN GPIO_PIN_2
#define RC522_GPIO_RST_Mode GPIO_Mode_Out_PP
/*********************************** RC522 函数宏定义*********************************************/
#define RC522_CS_Enable() HAL_GPIO_WritePin(RC522_GPIO_CS_PORT, RC522_GPIO_CS_PIN, GPIO_PIN_RESET)
#define RC522_CS_Disable() HAL_GPIO_WritePin(RC522_GPIO_CS_PORT, RC522_GPIO_CS_PIN, GPIO_PIN_SET)
#define RC522_Reset_Enable() HAL_GPIO_WritePin(RC522_GPIO_RST_PORT, RC522_GPIO_RST_PIN, GPIO_PIN_RESET)
#define RC522_Reset_Disable() HAL_GPIO_WritePin(RC522_GPIO_RST_PORT, RC522_GPIO_RST_PIN, GPIO_PIN_SET)
#define RC522_SCK_0() HAL_GPIO_WritePin( RC522_GPIO_SCK_PORT, RC522_GPIO_SCK_PIN , GPIO_PIN_RESET)
#define RC522_SCK_1() HAL_GPIO_WritePin ( RC522_GPIO_SCK_PORT, RC522_GPIO_SCK_PIN, GPIO_PIN_SET )
#define RC522_MOSI_0() HAL_GPIO_WritePin( RC522_GPIO_MOSI_PORT, RC522_GPIO_MOSI_PIN, GPIO_PIN_RESET )
#define RC522_MOSI_1() HAL_GPIO_WritePin ( RC522_GPIO_MOSI_PORT, RC522_GPIO_MOSI_PIN, GPIO_PIN_SET )
#define RC522_MISO_GET() HAL_GPIO_ReadPin( RC522_GPIO_MISO_PORT, RC522_GPIO_MISO_PIN )
/*********************************** 函数 *********************************************/
void RC522_Init ( void );
void IC_test ( void );
void PcdReset ( void ); //复位
void M500PcdConfigISOType ( u8 type ); //工作方式
char PcdRequest ( u8 req_code, u8 * pTagType ); //寻卡
char PcdAnticoll ( u8 * pSnr); //读卡号
char PcdHalt ( void );
char PcdSelect ( uint8_t * pSnr );
char PcdAuthState ( uint8_t ucAuth_mode, uint8_t ucAddr, uint8_t * pKey, uint8_t * pSnr );
char WriteAmount ( uint8_t ucAddr, uint32_t pData );
char ReadAmount ( uint8_t ucAddr, uint32_t *pData );
void PcdAntennaOn ( void );
void PcdAntennaOff ( void );
char IsDataBlock( uint8_t ucAddr );
char PcdWriteString( uint8_t ucAddr, uint8_t *pData);
char PcdReadString( uint8_t ucAddr, uint8_t *pData);
char ChangeKeyA( uint8_t ucAddr, uint8_t *pKeyA );
char WriteDataBlock( uint8_t ucAddr, uint8_t *pData, uint8_t Len);
char ReadDataBlock( uint8_t ucAddr, uint8_t *pData);
void Get_RC522(void);
#endif
main函数
/* USER CODE BEGIN WHILE */
while (1)
{
Get_RC522();
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
三、实验效果
stm32 RC522
结尾:
对这个项目感兴趣的小伙伴,完全可以根据本项目的文章教程,一步步自己搭建属于自己的STM32-FreeRTOS智能家居项目,如果需要完整资料和完整硬件的可私信我(有偿):原理图、STM32源码、Android app源码、操作教程。