基于STM32HAL库,RC522(RFID)模块读写驱动,无线IC卡读写

一、模块简短介绍

        有关RC522模块的背景知识以及工作原理,本次实验暂时不做分享,这里贴上两位写得很好的博主,大家可自行查看。 

(5条消息) RC522(RFID)读写驱动_桃成蹊2.0的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_51220742/article/details/123683745(5条消息) STM32--RFID无线射频技术(RC522刷卡模块)_rc522射频模块详细资料_y黎好好的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_45771489/article/details/124079134

b829df1211c843b49139edf393f1aa42.jpeg

         该模块价格普遍实惠,所以我买了一个自己玩玩。经过一段时间的摸爬滚打目前能够正常读写,后续会继续完善该模块的使用,并加入到其他嵌入式设计中。

二、开发资料的使用及经验分享

        该模块的API函数淘宝有很多资源,数据手册也有,不过是英文版的。建议大家有时间还是要过一数据手册的重要部分,比如寄存器和指令集,通信协议等,可以使用知云文献查看,我习惯用它来看论文,适合翻译长段大段。也可以用WPS的翻译,小部分翻译,看个人使用习惯。要是有能力直接撸原文那就更好,节约时间。

                            2e92c08f81ca46af8c0d2361b9d2e130.png

        找过很多资料,提供的API无非就是一套没有章法,毫无可读性的杂乱代码,十分影响学习效果。这里建议大家不要去死磕,可以看本次实验的代码逻辑和上文提到的两位博主,每一个底层函数都有注释,在一些底层函数的逻辑上可以结合这些API函数进行反推或者校验,可以帮助读者理解实现原理。

       一开始是通过数据手册,硬着头皮啃无良店铺给的代码,没有注释,没有逻辑,没有章法。虽然没报错,但是能不能用完全不知道,就是这一步就花了好几天时间,所以建议大家不要像我一样埋头苦读,可以先看每一个函数的中文注释,整理出一个框架。

三、源码解析

         代码总体逻辑没有变化,参考了两位博主的思路,我用官方的API函数进行修改。两位博主一位使用的软件实现SPI协议,一位使用板载SPI。两种方法我都试过,但是软件SPI协议总是通信失败,不知道是我协议写的不对还是通信速度和模块不匹配,暂时还未解决。  

软件模拟SPI

/*
 * 函数名:SOFT_SPI_RC522_SendByte
 * 描述  :向RC522发送1 Byte 数据
 * 输入  :write_dat,要发送的数据
 * 返回  : RC522返回的数据
 */
void SOFT_SPI_RC522_SendByte(uint8_t write_dat)
{
    uint8_t i;
	
    for(i=0;i<8;i++)
    {     
		RC522_SCK(0);
		RC522_DELAY();
        if (write_dat & 0x80)
                RC522_MOSI(1);
        else 
                RC522_MOSI(0);                    		
		write_dat <<= 1;
        RC522_DELAY();     //delay_ms(1)       
        RC522_SCK(1);
        RC522_DELAY();
        					
    } 
	RC522_SCK(0);//释放时钟线
}
 
 


/*
 * 函数名:SOFT_SPI_RC522_ReadByte
 * 描述  :从RC522发送1 Byte 数据
 * 输入  :无
 * 返回  : RC522返回的数据
 */
uint8_t SOFT_SPI_RC522_ReadByte(void)
{
	uint8_t i;
	uint8_t SPI_Data=0;
	for(i=0;i<8;i++)
	{
        SPI_Data <<= 1;
        RC522_SCK(0);        
        RC522_DELAY();//SCK low pulse width 最小延时
        RC522_SCK(1);
        if (RC522_MISO_GET==1)
                SPI_Data++;
        RC522_DELAY();//SCK high pulse width 最小延时	
	}
    RC522_SCK(0);
	return SPI_Data;
}




/*
 * 函数名:SOFT_SPI_RC522_Read_Write
 * 描述  :RC522同时读写,数据在上升沿读取
 * 输入  :byte,要发送的数据
 * 返回  : RC522返回的数据
 */
uint8_t SOFT_SPI_RC522_Read_Write(uint8_t write_dat)
{
    uint8_t i,read_dat;
    for(i=0;i<8;i++)
    {
        RC522_SCK(0);
        if(write_dat&0x80)
            RC522_MOSI(1);
        else                    
            RC522_MOSI(0); 
        write_dat <<= 1;
        RC522_DELAY(); //data changes to SCK high 最小延时	
        RC522_SCK(1); 
        read_dat <<= 1;  
        if(RC522_MISO_GET==1) 
            read_dat++; 
		RC522_DELAY();
                
    }
	RC522_SCK(0);
    return read_dat;
}

         有大佬如果发现问题,还请给我留言,也不知道是什么原因导致通信失败。

板载SPI通信

/*
 * 函数名:SPI_WriteNBytes
 * 描述  :向RC522发送n Byte 数据
 * 输入  :SPIx : 要发送数据的SPI
 * 输入  :p_TxData : 要发送的数据
 * 输入  :sendDataNum : 要发送的数据量(Byte)
 * 返回  : 0
 */
int SPI_WriteNBytes(SPI_TypeDef* SPIx, uint8_t *p_TxData,uint32_t sendDataNum)
{
	int retry=0;
	while(sendDataNum--){
		while((SPIx->SR&SPI_FLAG_TXE)==0)//等待发送区空
		{
			retry++;
			if(retry>20000)return -1;
		}
		SPIx->DR=*p_TxData++;//发送一个byte
		retry=0;
		while((SPIx->SR&SPI_FLAG_RXNE)==0)//等待接收完一个byte
		{
			SPIx->SR = SPIx->SR;
			retry++;
			if(retry>20000)return -1;
		}
		SPIx->DR;
	}
    return 0;
}



/*
 * 函数名:SPI_ReadNBytes
 * 描述  :读取RC522 n Byte 数据
 * 输入  :SPIx : 要读取数据的SPI
 * 输入  :p_RxData : 要读取的数据
 * 输入  :readDataNum : 要读取的数据量(Byte)
 * 返回  : 0
 */
int SPI_ReadNBytes(SPI_TypeDef* SPIx, uint8_t *p_RxData,uint32_t readDataNum)
{
	int retry=0;
	while(readDataNum--){
		SPIx->DR = 0xFF;
		while(!(SPIx->SR&SPI_FLAG_TXE)){
			retry++;
			if(retry>20000)return -1;
		}
		retry = 0;
		while(!(SPIx->SR&SPI_FLAG_RXNE)){
			retry++;
			if(retry>20000)return -1;
		}
		*p_RxData++ = SPIx->DR;
	}
    return 0;
}

        本实验使用读取寄存器状态来完成SPI通信,未使用HAL_SPI_TransmitReceive库函数。

源码部分

        主函数比较简略,调用一个操作函数,用按键触发的方式启动该函数实现读写。main.c 中,加入以下代码:

main.c

    RC522_Init();
	uint8_t key;

    uint8_t Write_Card_Data[16]={0};//要写入的数据

	while(1)
	{
		key = key_scan();//按键扫描函数,自己定义修改
		if(key==1)
		{
			RC522_Start(5,readID,Write_Card_Data);//readID  读命令
		}
		else if(key==2)
		{
			RC522_Start(5,writeID,Write_Card_Data);//writeID  写命令
		}
		delay_ms(100);
		LED0=!LED0;
	}

RC522部分

RC522.c

        其中RC522_Start是自定义函数,只要操作流程正确,可自行编写想要的操作方式,本实验只完成简单的单块读写操作。蓝卡和白卡的卡号根据自己的修改,可以通过手机NFC功能查看卡号,同时也能查看扇区数据,APP可以使用上文提到的博主推荐的NFC Writer。

#include "RC522.h"
#include "stdio.h"
#include "delay.h"
#include "spi.h"
#include "oled.h"
#include "string.h"



// M1卡分为16个扇区,每个扇区由四个块(块0、块1、块2、块3)组成
// 将16个扇区的64个块按绝对地址编号为:0~63,每块16个字节
// 第0个扇区的块0(即绝对地址0块),用于存放厂商代码,已经固化不可更改 
// 每个扇区的块0、块1、块2为数据块,可用于存放数据
// 每个扇区的块3为控制块(绝对地址为:块3、块7、块11.....)包括密码A,存取控制、密码B等
void RC522_Init(void)
{

	RC522_SPI_GPIO_Init();//初始化RC522的复位引脚和片选引脚

	SPI1_Init();//初始化SPI	

    delay_ms(50);
	PcdReset();//复位RC522读卡器

	delay_ms(10);
	PcdAntennaOff();//关闭天线发射
	
	delay_ms(10);
    PcdAntennaOn();//开启天线发射

	printf("RFID-MFRC522 初始化完成\nPress KEY0 Or KEY1 To Start...\r\n");  //初始化完成
}




uint8_t IC_UID[4]; //UID卡片序列号,4字节
uint8_t IC_Type[2];//卡类型代码,2字节,0x0400,Mifare_One(S50)
uint8_t card_1[4]={0xC3,0xB2,0x37,0xC5};//蓝卡1卡号
uint8_t card_2[4]={0xA3,0x09,0x3C,0xFB};//白卡2卡号
uint8_t ID_num=0;//当前操作的序号
uint8_t Card_KEY[6]={0xff,0xff,0xff,0xff,0xff,0xff};//验证密码
uint8_t Card_Data[16];//读取出的块数据
/*
 * 函数名:RC522_Start
 * 输入  block:要操作的块地址
 * 输入  option:读操作或写操作
 *              readID  1//读
 *				writeID 2//写
 * 输入  block:要操作的块地址
 * 描述  :RC522操作主函数,可完成对卡的读写操作
 *  * 返回  : 状态值
 *         = 1,成功
 */
void RC522_Start(uint8_t block,uint8_t option,uint8_t *Write_Card_Data)
{
	
	if(PcdRequest(PICC_REQALL,IC_Type) == MI_OK)//寻卡
	{		
		uint16_t cardType = (IC_Type[0] << 8) | IC_Type[1];
        switch (cardType)
        {
            case 0x4400:
                printf("\r\nMifare UltraLight\r\n");
                break;
            case 0x0400:
                printf("\r\nMifare One(S50)\r\n");
                break;
            case 0x0200:
                printf("\r\nMifare One(S70)\r\n");
                break;
            case 0x0800:
                printf("\r\nMifare Pro(X)\r\n");
                break;
            case 0x4403:
                printf("\r\nMifare DESFire\r\n");
                break;
            default:
                printf("\r\nUnknown Card\r\n");
                break;
        }
		if(PcdAnticoll(IC_UID)==MI_OK)//防冲撞
		{
			
			if((IC_UID[0]==card_1[0])&&(IC_UID[1]==card_1[1])&&(IC_UID[2]==card_1[2])&&(IC_UID[3]==card_1[3]))
            {
                ID_num=1; 
                printf("The User is: %d, Blue card",ID_num);                      
            }
            else if((IC_UID[0]==card_2[0])&&(IC_UID[1]==card_2[1])&&(IC_UID[2]==card_2[2])&&(IC_UID[3]==card_2[3]))
            {
                ID_num=2;
                printf("The User is: %d, White card",ID_num);                           
            }
            printf("\r\ncard_ID: %02X:%02X:%02X:%02X\r\n",IC_UID[0],IC_UID[1],IC_UID[2],IC_UID[3]); //打印卡的UID号
			
			if(PcdSelect(IC_UID)==MI_OK)//选卡
			{
				if(PcdAuthState(PICC_AUTHENT1A,block,Card_KEY,IC_UID)==MI_OK)//验证A密钥,对应块
				{
//					memset(Card_Data,1,16);										
					if(option==readID)//读操作
					{
						if(PcdRead(block,Card_Data)==MI_OK)
						{
							printf("\n读取结果: \n");
							printf("block %d date:\r\n",block);
							for(int i=0;i<16;i++)
							{
								printf("%02X ",Card_Data[i]);
							} 
							printf("\r\n");                     
							
						}
					}
					else if(option==writeID)//写操作
					{						
						if(PcdWrite(block,Write_Card_Data)==MI_OK)
						{
							delay_ms(8);
							if(PcdRead(block,Card_Data)==MI_OK)
							{
								printf("\n写入完成\n");
								printf("block %d date:\r\n",block);
								for(int i=0;i<16;i++)
								{
									printf("%02X ",Card_Data[i]);
								} 
								printf("\r\n");                     								
							}
						}
					}	
				}
				else
				{
					printf("\r\n验证失败\r\n");
				}
			}
			else
			{
				printf("\r\n选卡失败\r\n");
			}
		}
		else
		{
			printf("\r\n防冲撞失败\r\n");
		}	
	}
	else
	{
		printf("\r\n寻卡失败\r\n");
	}

//	PcdHalt();
	PcdAntennaOff();//关闭天线发射
			
	delay_ms(10);
	PcdAntennaOn();//开启天线发射	
}



/*
 * 函数名:RC522_SPI_GPIO_Init
 * 描述  :初始化RC522的复位引脚和片选引脚
 *         片选引脚采用软件管理模式,自定义片选引脚
 */
void RC522_SPI_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_Initure;
    
    RC522_GPIO_Reset_CLK_ENABLE();
    RC522_GPIO_CS_CLK_ENABLE();   
    
    GPIO_Initure.Pin=RC522_RST_Pin;
    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;             
    GPIO_Initure.Pull=GPIO_PULLUP;     //复位和片选引脚低电平有效,默认上拉             
    GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;                    
    HAL_GPIO_Init(RC522_RST_GPIO_Port,&GPIO_Initure);

    GPIO_Initure.Pin=RC522_GPIO_CS_PIN;
    HAL_GPIO_Init(RC522_GPIO_CS_PORT,&GPIO_Initure);
}


/*
 * 函数名:SPI_WriteNBytes
 * 描述  :向RC522发送n Byte 数据
 * 输入  :SPIx : 要发送数据的SPI
 * 输入  :p_TxData : 要发送的数据
 * 输入  :sendDataNum : 要发送的数据量(Byte)
 * 返回  : 0
 */
int SPI_WriteNBytes(SPI_TypeDef* SPIx, uint8_t *p_TxData,uint32_t sendDataNum)
{
	int retry=0;
	while(sendDataNum--){
		while((SPIx->SR&SPI_FLAG_TXE)==0)//等待发送区空
		{
			retry++;
			if(retry>20000)return -1;
		}
		SPIx->DR=*p_TxData++;//发送一个byte
		retry=0;
		while((SPIx->SR&SPI_FLAG_RXNE)==0)//等待接收完一个byte
		{
			SPIx->SR = SPIx->SR;
			retry++;
			if(retry>20000)return -1;
		}
		SPIx->DR;
	}
    return 0;
}



/*
 * 函数名:SPI_ReadNBytes
 * 描述  :读取RC522 n Byte 数据
 * 输入  :SPIx : 要读取数据的SPI
 * 输入  :p_RxData : 要读取的数据
 * 输入  :readDataNum : 要读取的数据量(Byte)
 * 返回  : 0
 */
int SPI_ReadNBytes(SPI_TypeDef* SPIx, uint8_t *p_RxData,uint32_t readDataNum)
{
	int retry=0;
	while(readDataNum--){
		SPIx->DR = 0xFF;
		while(!(SPIx->SR&SPI_FLAG_TXE)){
			retry++;
			if(retry>20000)return -1;
		}
		retry = 0;
		while(!(SPIx->SR&SPI_FLAG_RXNE)){
			retry++;
			if(retry>20000)return -1;
		}
		*p_RxData++ = SPIx->DR;
	}
    return 0;
}



/*
 * 函数名:PcdComMF522
 * 描述  :通过RC522和ISO14443卡通讯
 * 输入  :ucCommand,RC522命令字
 *         pInData,通过RC522发送到卡片的数据
 *         ucInLenByte,发送数据的字节长度
 *         pOutData,接收到的卡片返回数据
 *         pOutLenBit,返回数据的位长度
 * 返回  : 状态值
 *         = MI_OK,成功
 * 调用  :内部调用
 */
char PcdComMF522(uint8_t Command,uint8_t *pInData,uint8_t InLenByte,uint8_t *pOutData,uint32_t *pOutLenBit)
{
    char status = MI_ERR;
    uint8_t irqEn = 0x00;
    uint8_t waitFor = 0x00;
    uint8_t lastBits;
    uint8_t n;
    uint32_t i;
    switch (Command)
    {
    case PCD_AUTHENT:   //Mifare认证
        irqEn = 0x12;   //允许错误中断请求ErrIEn  允许空闲中断IdleIEn
        waitFor = 0x10; //认证寻卡等待时候 查询空闲中断标志位
        break;
    case PCD_TRANSCEIVE://接收发送 发送接收
        irqEn = 0x77;   //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
        waitFor = 0x30; //寻卡等待时候 查询接收中断标志位与 空闲中断标志位
        break;
    default:
        break;
    }
    WriteRawRC(ComIEnReg, irqEn | 0x80); //IRqInv置位管脚IRQ与Status1Reg的IRq位的值相反
    ClearBitMask(ComIrqReg, 0x80);       //Set1该位清零时,CommIRqReg的屏蔽位清零
    WriteRawRC(CommandReg, PCD_IDLE);    //写空闲命令
    SetBitMask(FIFOLevelReg, 0x80);      //置位FlushBuffer清除内部FIFO的读和写指针以及ErrReg的BufferOvfl标志位被清除
    for (i = 0; i < InLenByte; i++)
    {
        WriteRawRC(FIFODataReg, pInData[i]);//写数据进FIFOdata
    }
    WriteRawRC(CommandReg, Command);//写命令 
    if (Command == PCD_TRANSCEIVE)
    {
        SetBitMask(BitFramingReg, 0x80);//StartSend置位启动数据发送 该位与收发命令使用时才有效
    }
    i = 800;//根据时钟频率调整,操作M1卡最大等待时间25ms
    do
    {
        n = ReadRawRC(ComIrqReg);//查询事件中断
        i--;
    } while ((i != 0) && !(n & 0x01) && !(n & waitFor));//退出条件i=0,定时器中断,与写空闲命令
    ClearBitMask(BitFramingReg, 0x80);//清理允许StartSend位
    if (i != 0)
    {
        if (!(ReadRawRC(ErrorReg) & 0x1B))//读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
        {
            status = MI_OK;
            if (n & irqEn & 0x01)//是否发生定时器中断
            {
                status = MI_NOTAGERR;
            }
            if (Command == PCD_TRANSCEIVE)
            {
                n = ReadRawRC(FIFOLevelReg);//读FIFO中保存的字节数
                lastBits = ReadRawRC(ControlReg) & 0x07;//最后接收到得字节的有效位数
                if (lastBits)
                {
                    *pOutLenBit = (n - 1) * 8 + lastBits;//N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
                }
                else
                {
                    *pOutLenBit = n * 8;//最后接收到的字节整个字节有效
                }
                if (n == 0)
                {
                    n = 1;
                }
                if (n > MAXRLEN)
                {
                    n = MAXRLEN;
                }
                for (i = 0; i < n; i++)
                {
                    pOutData[i] = ReadRawRC(FIFODataReg);
                }
            }
        }
        else
        {
            status = MI_ERR;
        }
    }
    SetBitMask(ControlReg, 0x80); // stop timer now
    WriteRawRC(CommandReg, PCD_IDLE);
    return status;
}

/*
 * 函数名:PcdRequest
 * 描述  :寻卡
 * 输入  :ucReq_code,寻卡方式
 *                     = 0x52,寻感应区内所有符合14443A标准的卡
 *                     = 0x26,寻未进入休眠状态的卡
 *         pTagType,卡片类型代码
 *                   = 0x4400,Mifare_UltraLight
 *                   = 0x0400,Mifare_One(S50)
 *                   = 0x0200,Mifare_One(S70)
 *                   = 0x0800,Mifare_Pro(X))
 *                   = 0x4403,Mifare_DESFire
 * 返回  : 状态值
 *         = MI_OK,成功
 */
char PcdRequest(uint8_t req_code, uint8_t *pTagType)
{
    char status;
    uint32_t unLen;
    uint8_t ucComMF522Buf[MAXRLEN];

    ClearBitMask(Status2Reg, 0x08);  //清除RC522寄存位,清理指示MIFARECyptol单元接通以及所有卡的数据通信被加密的情况
    WriteRawRC(BitFramingReg, 0x07); //写RC522寄存器,发送的最后一个字节的七位
    SetBitMask(TxControlReg, 0x03);  写RC522寄存位,TX1,TX2管脚的输出信号传递经发送调制的13.56的能量载波信号

    ucComMF522Buf[0] = req_code;//存入寻卡方式

    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, &unLen);
    if ((status == MI_OK) && (unLen == 0x10))//寻卡成功返回卡类型
    {
        *pTagType = ucComMF522Buf[0];
        *(pTagType + 1) = ucComMF522Buf[1];
    }
    else
    {
        status = MI_ERR;
    }
    return status;
}
 

 
/*
 * 函数名:PcdAnticoll
 * 描述  :防冲撞
 * 输入  :pSnr,卡片序列号,4字节
 * 返回  : 状态值
 *         = MI_OK,成功
 */
char PcdAnticoll(uint8_t *pSnr)
{
    char status;
    uint8_t i, snr_check = 0;
    uint32_t unLen;
    uint8_t ucComMF522Buf[MAXRLEN];
    ClearBitMask(Status2Reg, 0x08);//清MFCryptol On位,只有成功执行MFAuthent命令后,该位才能置位
    WriteRawRC(BitFramingReg, 0x00);//清理寄存器,停止收发
    ClearBitMask(CollReg, 0x80);//清ValuesAfterColl所有接收的位在冲突后被清除
    ucComMF522Buf[0] = PICC_ANTICOLL1;//卡片防冲突命令
    ucComMF522Buf[1] = 0x20;

    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, &unLen);
    if (status == MI_OK)
    {
        for (i = 0; i < 4; i++)
        {
            *(pSnr + i) = ucComMF522Buf[i];
            snr_check ^= ucComMF522Buf[i];
        }
        if (snr_check != ucComMF522Buf[i])
        {
            status = MI_ERR;
        }
    }
    SetBitMask(CollReg, 0x80);
    return status;
}
 

  
/*
 * 函数名:PcdSelect
 * 描述  :选定卡片
 * 输入  :pSnr,卡片序列号,4字节
 * 返回  : 状态值
 *         = MI_OK,成功
 */
char PcdSelect(uint8_t *pSnr)
{
    char status;
    uint8_t i;
    uint32_t unLen;
    uint8_t ucComMF522Buf[MAXRLEN];
    ucComMF522Buf[0] = PICC_ANTICOLL1;//防冲撞
    ucComMF522Buf[1] = 0x70;
    ucComMF522Buf[6] = 0;
    for (i = 0; i < 4; i++)
    {
        ucComMF522Buf[i + 2] = *(pSnr + i);
        ucComMF522Buf[6] ^= *(pSnr + i);
    }
    CalulateCRC(ucComMF522Buf, 7, &ucComMF522Buf[7]);
    ClearBitMask(Status2Reg, 0x08);
    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, &unLen);
    if ((status == MI_OK) && (unLen == 0x18))
    {
        status = MI_OK;
    }
    else
    {
        status = MI_ERR;
    }
    return status;
}






/*
 * 函数名:PcdAuthState
 * 描述  :验证卡片密码
 * 输入  :ucAuth_mode,密码验证模式
 *                     = 0x60,验证A密钥
 *                     = 0x61,验证B密钥
 *         uint8_t ucAddr,块地址
 *         pKey,密码
 *         pSnr,卡片序列号,4字节
 * 返回  : 状态值
 *         = MI_OK,成功
 */
char PcdAuthState(uint8_t auth_mode, uint8_t addr, uint8_t *pKey, uint8_t *pSnr)
{
    char status;
    uint32_t unLen;
    uint8_t i, ucComMF522Buf[MAXRLEN];
    ucComMF522Buf[0] = auth_mode;
    ucComMF522Buf[1] = addr;
    for (i = 0; i < 6; i++)
    {
        ucComMF522Buf[i + 2] = *(pKey + i);
    }
    for (i = 0; i < 6; i++)
    {
        ucComMF522Buf[i + 8] = *(pSnr + i);
    }
    status = PcdComMF522(PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, &unLen);
    if ((status != MI_OK) || (!(ReadRawRC(Status2Reg) & 0x08)))
    {
        status = MI_ERR;
    }
    return status;
}




/*
 * 函数名:PcdRead
 * 描述  :读取M1卡一块数据
 * 输入  :uint8_t ucAddr,块地址
 *         pData,读出的数据,16字节
 * 返回  : 状态值
 *         = MI_OK,成功
 * 调用  :外部调用
 */
char PcdRead(uint8_t addr, uint8_t *pData)
{
    char status;
    uint32_t unLen;
    uint8_t i, ucComMF522Buf[MAXRLEN];
    ucComMF522Buf[0] = PICC_READ;
    ucComMF522Buf[1] = addr;
    CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);

    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
    if ((status == MI_OK) && (unLen == 0x90))
    {
        for (i = 0; i < 16; i++)
        {
            *(pData + i) = ucComMF522Buf[i];
        }
    }
    else
    {
        status = MI_ERR;
    }
    return status;
}





/*
 * 函数名:PcdWrite
 * 描述  :写数据到M1卡一块
 * 输入  :uint8_t ucAddr,块地址
 *         pData,写入的数据,16字节
 * 返回  : 状态值
 *         = MI_OK,成功
 * 调用  :外部调用
 */
char PcdWrite(uint8_t addr, uint8_t *pData)
{
    char status;
    uint32_t unLen;
    uint8_t i, ucComMF522Buf[MAXRLEN];
    ucComMF522Buf[0] = PICC_WRITE;
    ucComMF522Buf[1] = addr;
    CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);
    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {
        status = MI_ERR;
    }
    if (status == MI_OK)
    {
        for (i = 0; i < 16; i++)
        {
            ucComMF522Buf[i] = *(pData + i);
        }
        CalulateCRC(ucComMF522Buf, 16, &ucComMF522Buf[16]);

        status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, &unLen);
        if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
        {
            status = MI_ERR;
        }
    }
    return status;
}





/*
 * 函数名:PcdValue
 * 描述  :扣款和充值
 * 输入  :dd_mode[IN]:命令字
 *              0xC0 = 扣款
 *              0xC1 = 充值
 *         addr[IN]:钱包地址
 *         pValue[IN]:4字节增(减)值,低位在前
 * 返回  : 状态值
 *         = MI_OK,成功
 */
char PcdValue(uint8_t dd_mode, uint8_t addr, uint8_t *pValue)
{
    char status;
    uint32_t unLen;
    uint8_t i, ucComMF522Buf[MAXRLEN];
    ucComMF522Buf[0] = dd_mode;
    ucComMF522Buf[1] = addr;
    CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);
    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);

    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {
        status = MI_ERR;
    }
    if (status == MI_OK)
    {
        for (i = 0; i < 16; i++)
        {
            ucComMF522Buf[i] = *(pValue + i);
        }
        CalulateCRC(ucComMF522Buf, 4, &ucComMF522Buf[4]);
        unLen = 0;
        status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 6, ucComMF522Buf, &unLen);
        if (status != MI_ERR)
        {
            status = MI_OK;
        }
    }
    if (status == MI_OK)
    {
        ucComMF522Buf[0] = PICC_TRANSFER;
        ucComMF522Buf[1] = addr;
        CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);

        status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);

        if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
        {
            status = MI_ERR;
        }
    }
    return status;
}





/*
 * 函数名:PcdBakValue
 * 描述  :备份钱包
 * 输入  :sourceaddr[IN]:源地址
 *         goaladdr[IN]:目标地址
 * 返回  : 状态值
 *         = MI_OK,成功
 */
char PcdBakValue(uint8_t sourceaddr, uint8_t goaladdr)
{
    char status;
    uint32_t unLen;
    uint8_t ucComMF522Buf[MAXRLEN];
    ucComMF522Buf[0] = PICC_RESTORE;
    ucComMF522Buf[1] = sourceaddr;
    CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);
    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {
        status = MI_ERR;
    }
    if (status == MI_OK)
    {
        ucComMF522Buf[0] = 0;
        ucComMF522Buf[1] = 0;
        ucComMF522Buf[2] = 0;
        ucComMF522Buf[3] = 0;
        CalulateCRC(ucComMF522Buf, 4, &ucComMF522Buf[4]);
        status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 6, ucComMF522Buf, &unLen);
        if (status != MI_ERR)
        {
            status = MI_OK;
        }
    }
    if (status != MI_OK)
    {
        return MI_ERR;
    }
    ucComMF522Buf[0] = PICC_TRANSFER;
    ucComMF522Buf[1] = goaladdr;
    CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);
    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {
        status = MI_ERR;
    }
    return status;
}




/*
 * 函数名:PcdHalt
 * 描述  :命令卡片进入休眠状态
 * 返回  : 状态值
 *         = MI_OK,成功
 */
char PcdHalt(void)
{
    uint32_t unLen;
    uint8_t ucComMF522Buf[MAXRLEN];

    ucComMF522Buf[0] = PICC_HALT;
    ucComMF522Buf[1] = 0;
    CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);

    PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);

    return MI_OK;
}




/*
 * 函数名:CalulateCRC
 * 描述  :用RC522计算CRC16
 * 输入  :pIndata,计算CRC16的数组
 *         ucLen,计算CRC16的数组字节长度
 *         pOutData,存放计算结果存放的首地址
 * 返回  : 无
 * 调用  :内部调用
 */
void CalulateCRC(uint8_t *pIndata, uint8_t len, uint8_t *pOutData)
{
    uint8_t i, n;
    ClearBitMask(DivIrqReg, 0x04);
    WriteRawRC(CommandReg, PCD_IDLE);
    SetBitMask(FIFOLevelReg, 0x80);
    for (i = 0; i < len; i++)
    {
        WriteRawRC(FIFODataReg, *(pIndata + i));
    }
    WriteRawRC(CommandReg, PCD_CALCCRC);
    i = 0xFF;
    do
    {
        n = ReadRawRC(DivIrqReg);
        i--;
    } while ((i != 0) && !(n & 0x04));
    pOutData[0] = ReadRawRC(CRCResultRegL);
    pOutData[1] = ReadRawRC(CRCResultRegM);
}



/*
 * 函数名:PcdRese
 * 描述  :复位RC522 
 * 返回  : 状态值
 *         = MI_OK,成功
 */
char PcdReset(void)
{
	RC522_Reset_Disable();
    delay_ms(10);
    RC522_Reset_Enable();
    delay_ms(60);
    RC522_Reset_Disable();
    delay_ms(500);
    WriteRawRC(CommandReg, PCD_RESETPHASE);//复位指令
    delay_ms(2);

    WriteRawRC(ModeReg, 0x3D);//定义发送和接收常用模式,和Mifare卡通讯,CRC初始值0x6363
    WriteRawRC(TReloadRegL, 30);//16位定时器低位
    WriteRawRC(TReloadRegH, 0);//16位定时器高位
    WriteRawRC(TModeReg, 0x8D);//定义内部定时器的设置
    WriteRawRC(TPrescalerReg, 0x3E);//设置定时器分频系数
    WriteRawRC(TxAutoReg, 0x40);//调制发送信号为100%ASK

    ClearBitMask(TestPinEnReg, 0x80);
    WriteRawRC(TxAutoReg, 0x40);

    return MI_OK;
}

 
/*
 * 函数名:ReadRawRC
 * 描述  :读RC522寄存器
 * 输入  :ucAddress,寄存器地址
 * 返回  : 寄存器的当前值
 */
uint8_t ReadRawRC(uint8_t Address)
{
    uint8_t ucAddr;
    uint8_t ucResult = 0;
    ucAddr = ((Address << 1) & 0x7E) | 0x80;//最高位置1,读操作;最低为默认0
    delay_ms(1);
    RC522_CS_Enable();
    SPI_WriteNBytes(SPI1_SPI, &ucAddr, 1);  //向总线写多个数据
    SPI_ReadNBytes(SPI1_SPI, &ucResult, 1); //向总线读多个数据
    RC522_CS_Disable();
    return ucResult;
}
 
 
/*
 * 函数名:WriteRawRC
 * 描述  :写RC522寄存器
 * 输入  :ucAddress,寄存器地址
 *         ucValue,写入寄存器的值
 */
void WriteRawRC(uint8_t Address, uint8_t value)
{
    uint8_t ucAddr;
    uint8_t write_buffer[2] = {0};
    ucAddr = ((Address << 1) & 0x7E);//最高位置0,写操作;最低为默认0
    write_buffer[0] = ucAddr;
    write_buffer[1] = value;
    delay_ms(1);
    RC522_CS_Enable();
    SPI_WriteNBytes(SPI1_SPI, write_buffer, 2);
    RC522_CS_Disable();
}
 
 
/*
 * 函数名:SetBitMask
 * 描述  :对RC522寄存器置位,可对多个位同时操作
 * 输入  :ucReg,寄存器地址
 *         ucMask,置位值,例0x13,0001 0011 
 */
void SetBitMask(uint8_t reg, uint8_t mask)
{
    uint8_t temp = 0x00;

    temp = ReadRawRC(reg);        //读寄存器
    WriteRawRC(reg, temp | mask); //将目标位置1 
}
 
 
/*
 * 函数名:ClearBitMask
 * 描述  :对RC522寄存器清位
 * 输入  :ucReg,寄存器地址
 *         ucMask,清位值
 */
void ClearBitMask(uint8_t reg, uint8_t mask)
{
    uint8_t temp = 0x00;

    temp = ReadRawRC(reg);
    WriteRawRC(reg, temp & ~mask);
}




/*
 * 函数名:PcdAntennaOn
 * 描述  :开启天线 
 * TxControlReg寄存器位0置1,引脚TX1上的输出信号将传输由传输数据调制的13.56 MHz能量载波。
 * TxControlReg寄存器位1置1,引脚TX2上的输出信号将发送由传输数据调制的13.56 MHz能量载波。
 */
void PcdAntennaOn(void)
{
    uint8_t i;
    i = ReadRawRC(TxControlReg);
    if (!(i & 0x03))
    {
        SetBitMask(TxControlReg, 0x03);
    }
}
 
 
/*
 * 函数名:PcdAntennaOff
 * 描述  :关闭天线 
 */
void PcdAntennaOff(void)
{
    ClearBitMask(TxControlReg, 0x03);
}
 



/*
 * 函数名:RC522_PcdConfig_Type
 * 描述  :设置RC522的工作方式
 * 工作方式ISO14443_A
 * 复位中已经设置过,该函数暂时未调用
 */
void RC522_Config(uint8_t Card_Type)
{
    ClearBitMask(Status2Reg, 0x08);
    WriteRawRC(ModeReg, 0x3D);
    WriteRawRC(RxSelReg, 0x86);
    WriteRawRC(RFCfgReg, 0x7F);
    WriteRawRC(TReloadRegL, 30);
    WriteRawRC(TReloadRegH, 0);
    WriteRawRC(TModeReg, 0x8D);
    WriteRawRC(TPrescalerReg, 0x3E);
    delay_ms(5);
    PcdAntennaOn();
}


RC522.h

        头文件内容与API大致相同,定义了指令集和寄存器。

        这里的sys.h是正点原子的F1系列头文件。

        加入了引脚定义和自定义函数。

#ifndef __RC522_H
#define __RC522_H			  	 
#include "sys.h"




/***************************引脚定义****************************/
/*
RC522SPI引脚,RST和CS可随意更改
RST-PC5
CS-PA4
SCK-PA5
MISO-PA6
MOSI-PA7
*/

//Reset
#define RC522_RST_Pin      GPIO_PIN_5
#define RC522_RST_GPIO_Port     GPIOC
#define RC522_GPIO_Reset_CLK_ENABLE() \
    do { __HAL_RCC_GPIOA_CLK_ENABLE(); } while(0)

//CS片选,模块SDA引脚,SPI通信时用作NSS
#define RC522_GPIO_CS_PIN      GPIO_PIN_4
#define RC522_GPIO_CS_PORT     GPIOA
#define RC522_GPIO_CS_CLK_ENABLE() \
    do { __HAL_RCC_GPIOA_CLK_ENABLE(); } while(0)


	
/***********************RC522 函数宏定义**********************/	
	
#define RC522_Reset_Disable() HAL_GPIO_WritePin(RC522_RST_GPIO_Port, RC522_RST_Pin, GPIO_PIN_SET);
#define RC522_Reset_Enable() HAL_GPIO_WritePin(RC522_RST_GPIO_Port, RC522_RST_Pin, GPIO_PIN_RESET);

#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 readID  1//读
#define writeID 2//写	
	
	
/***************************自定义函数****************************/
void RC522_Init(void);
void RC522_Start(uint8_t block,uint8_t option,uint8_t *Write_Card_Data);
void RC522_SPI_GPIO_Init(void);
	
	
	
	
	
	
	
/***************************RC522指令集****************************/
//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


//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                          0
#define MI_NOTAGERR                    1 
#define MI_ERR                         2 

//缓冲数组大小
#define MAXRLEN                       18


/*******************************内部调用函数**************************************/	
char PcdRequest(uint8_t req_code,uint8_t *pTagType);
char PcdAnticoll(uint8_t *pSnr);
char PcdSelect(uint8_t *pSnr);
char PcdAuthState(uint8_t auth_mode,uint8_t addr,uint8_t *pKey,uint8_t *pSnr);
char PcdRead(uint8_t addr,uint8_t *pData);
char PcdWrite(uint8_t addr,uint8_t *pData);
char PcdValue(uint8_t dd_mode,uint8_t addr,uint8_t *pValue);
char PcdBakValue(uint8_t sourceaddr, uint8_t goaladdr);
char PcdHalt(void);
void CalulateCRC(uint8_t *pIndata,uint8_t len,uint8_t *pOutData);
char PcdReset(void);
uint8_t ReadRawRC(uint8_t Address);
void WriteRawRC(uint8_t Address, uint8_t value);
void SetBitMask(uint8_t reg,uint8_t mask);
void ClearBitMask(uint8_t reg,uint8_t mask);
char PcdComMF522(uint8_t Command,uint8_t *pInData,uint8_t InLenByte,uint8_t *pOutData,unsigned int *pOutLenBit);
void PcdAntennaOn(void);
void PcdAntennaOff(void);
void RC522_Config(uint8_t Card_Type);

#endif 

SPI定义

spi的初始化默认即可,注意以下两项的设置,才能保持和模块的正常通信:

CLKPolarity=SPI_POLARITY_LOW;    //串行同步时钟的空闲状态为低电平

CLKPhase=SPI_PHASE_1EDGE;         //串行同步时钟的第1个跳变沿(上升或下降)数据被采样

片选引脚改为软件管理,这样方便更改引脚,

NSS=SPI_NSS_SOFT;                 //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理

SPI.c

#include "spi.h"

SPI_HandleTypeDef SPI1_Handler;  //SPI句柄

//以下是SPI模块的初始化代码,配置成主机模式 						  
//SPI口初始化
//这里针是对SPI1的初始化
void SPI1_Init(void)
{
    SPI1_Handler.Instance=SPI1_SPI;                         //SPI1
    SPI1_Handler.Init.Mode=SPI_MODE_MASTER;             //设置SPI工作模式,设置为主模式
    SPI1_Handler.Init.Direction=SPI_DIRECTION_2LINES;   //设置SPI单向或者双向的数据模式:SPI设置为双线模式
    SPI1_Handler.Init.DataSize=SPI_DATASIZE_8BIT;       //设置SPI的数据大小:SPI发送接收8位帧结构
    SPI1_Handler.Init.CLKPolarity=SPI_POLARITY_LOW;    //串行同步时钟的空闲状态为低电平
    SPI1_Handler.Init.CLKPhase=SPI_PHASE_1EDGE;         //串行同步时钟的第1个跳变沿(上升或下降)数据被采样
    SPI1_Handler.Init.NSS=SPI_NSS_SOFT;                 //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
    SPI1_Handler.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;//定义波特率预分频的值:波特率预分频值为256
    SPI1_Handler.Init.FirstBit=SPI_FIRSTBIT_MSB;        //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
    SPI1_Handler.Init.TIMode=SPI_TIMODE_DISABLE;        //关闭TI模式
    SPI1_Handler.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;//关闭硬件CRC校验
    SPI1_Handler.Init.CRCPolynomial=7;                  //CRC值计算的多项式,默认值为7
    HAL_SPI_Init(&SPI1_Handler);//初始化
    
    __HAL_SPI_ENABLE(&SPI1_Handler);                    //使能SPI1
	
    SPI1_ReadWriteByte(0Xff);                           //启动传输,产生8个时钟脉冲, 达到清空DR的作用, 非必需
}

//SPI底层驱动,时钟使能,引脚配置
//此函数会被HAL_SPI_Init()调用
//hspi:SPI句柄
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
    GPIO_InitTypeDef GPIO_Initure;
    
    SPI1_SPI_CLK_ENABLE();
    SPI1_SCK_GPIO_CLK_ENABLE();
    SPI1_MISO_GPIO_CLK_ENABLE();
    SPI1_MOSI_GPIO_CLK_ENABLE();    
    
    GPIO_Initure.Pin=SPI1_SCK_GPIO_PIN;
    GPIO_Initure.Mode=GPIO_MODE_AF_PP;             
    GPIO_Initure.Pull=GPIO_PULLDOWN;                  
    GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;                    
    HAL_GPIO_Init(SPI1_SCK_GPIO_PORT,&GPIO_Initure);

    GPIO_Initure.Pin=SPI1_MISO_GPIO_PIN;
    HAL_GPIO_Init(SPI1_MISO_GPIO_PORT,&GPIO_Initure);

    GPIO_Initure.Pin=SPI1_MOSI_GPIO_PIN;
    HAL_GPIO_Init(SPI1_MOSI_GPIO_PORT,&GPIO_Initure);
}

SPI.h

#ifndef __SPI_H
#define __SPI_H
#include "sys.h"

extern SPI_HandleTypeDef SPI1_Handler;  //SPI句柄

/* SPI1 引脚 定义 */

#define SPI1_SCK_GPIO_PORT              GPIOA
#define SPI1_SCK_GPIO_PIN               GPIO_PIN_5
#define SPI1_SCK_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   

#define SPI1_MISO_GPIO_PORT             GPIOA
#define SPI1_MISO_GPIO_PIN              GPIO_PIN_6
#define SPI1_MISO_GPIO_CLK_ENABLE()     do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   

#define SPI1_MOSI_GPIO_PORT             GPIOA
#define SPI1_MOSI_GPIO_PIN              GPIO_PIN_7
#define SPI1_MOSI_GPIO_CLK_ENABLE()     do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   

/* SPI1相关定义 */
#define SPI1_SPI                        SPI1
#define SPI1_SPI_CLK_ENABLE()           do{ __HAL_RCC_SPI1_CLK_ENABLE(); }while(0)    


void SPI1_Init(void);

#endif

四、实验结果

53941b94abc34bf5a897740cede1942a.png

本实验在于经验分享和学习记录,有不正确的地方请读者指正。

五、更新源码:

源码已上传至gitee:

stm32: 一些stm32模块使用经验记录 - Gitee.comicon-default.png?t=N7T8https://gitee.com/lrf1125962926/stm32/tree/RC522_SPI/

  • 16
    点赞
  • 116
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

plmm烟酒僧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值