赞
踩
目录
篇前说明:本文代码已经调试通过,因采用硬件SPI,你可使用不同的STM32实现,实现时请修改为与你的STM32单片机对应的SPI接口。
本实验的RFID信息显示是通过串口实现的,关于串行通信请参考博文:
关于RFID基础知识,请参考博文:
如果您需用软件模拟SPI方式实现RC522驱动,请参考:
本实验使用的RFID-RC522模块采用MFRC522芯片,SPI通讯方式,支持Mifarel S50、S70 、Pro、Desfire等类型的卡,附带的白卡和钥匙扣是S50卡,每张卡都有自己的标识(UID)。
图1
(1)MC522原理图
图2
(2)STM32F446RE Nucleo-64开发板原理图
核心板原理图:
图3
核心板自带的ST-Link原理图:
图4
核心板接口:
图5
底板原理图:
图6
(3)STM32与RC522模块的接口连接
//! Nucleo-F446RE与RC522接口定义
//SPI2_SCK PB10---(接Arduino D6)
//SPI2_MISO PC2----(接CN7左下2)
//SPI2_MOSI PC1----(接Arduino A4)
//RCC522_RST(CE) PC7----(接Arduino D9)
//RCC522_NSS(SDA) PB6----(接Arduino D10)
//RCC522_IRQ 悬空
可实现各种不同主机接口的功能:
(1)SPI接口
(2)串行UART(类似 RS232,电压电平取决于提供的管脚电压)
(3)I2C接口
RC522是一款高度集成的非接触式(13.56MHz)读写卡芯片。它采用了NXP公司的MFRC522为核心的处理芯片,此发送模块利用调制和解调的原理,支持各种非接触式的通信协议。RC522是采用的一种先进的RFID(Radio Fequency Identification,中文为无线射频识别)通信技术。其工作原理其实很简单:IC/ID磁卡进入到磁场后,接受读写器发出的射频信号,凭借感应电流所获得的能量发送出存储在芯片中的产品信息,读写器读取到信息并解码后,送至处理单元进行数据处理。
RC522模块引脚说明:
编号 | 名称 | 说明 |
1 | VCC | 电源正 |
2 | RST | 复位 |
3 | IRQ | 中断信号 |
4 | GND | 地线 |
5 | MISO | 主进从出数据引脚 |
6 | MOSI | 从进主出数据引脚 |
7 | SCK | 时钟 |
8 | SDA | 片选 |
分析上面图2和图3可知,Nucleo-446RE开发板中的核心芯片与板载ST-Link芯片U2的串行通信,连接的是串口2。ST-Link与电脑采用的是ST USB虚拟串口通信。在windows中,串口驱动可以自行加载。本实验可以采用SPI方式实现单片机与RC522模块的通信。
STM32采用硬件SPI2时,使用的接口是PB10、PC2、PC1。
选择File下的New Project:
选择芯片类型(本文为STM32F446RET6),选择下边的item,然后Start Project:
点击左侧的System Core下的SYS,将Debug设置为Serial Wire:
配置时钟:将RCC下的HSE设置为Crystal/Ceramic Resonator
Nucleo-446RE开发板:
请结合开发版的硬件电路,从下面两种方式中二选一,选择第二种方式时,开发板中需要焊接相应元件(X3、C33、C34、R35、R37),或者你不能确定振荡电路,直接选第一种方式吧。
在STM32CubeMX中,做如下配置:
(1)采用内部8MHz时钟时选择Clock Configuration,做如下配置:
(2)使用外部时钟时,开发板需焊接的X3(8MHz)、C33、C34(20PF)、R36、R37),选择Clock Configuration,做如下配置:
结合开发版的硬件电路,进行GPIO设置。RC522板有六个接口:SCK、MOSI、MISO、SDA、RST,前三项为SPI接口,后两项SDA(片选)、RST(复位)。SPI口可采用ARM芯片自带的硬件资源控制,也可以用软件模拟;
在左侧选择System Core/GPIO,依次将RST、SDA与LED连接的IO设置为GPIO_Output,将按键设置为GPIO_Input,按键对应的IO口设置为输入。电路图参考图6。
各IO口设置后的参数放大图如下:
核心板STM32采用内部时钟时,硬件SPI2的配置如下:
实验调试中的系统运行信息,可以通过串口输出。根据开发板的硬件电路,选中串口2。
USART2参数配置:
在 Connectivity 中选择 USART2 设置,并选择 Asynchronous 异步通信。
波特率为 115200 Bits/s。传输数据长度为8Bit。奇偶检验 None,停止位 1 ,接收和发送都使能。
本文的串口采用阻塞方式收发信息,无需设置中断。
在Project Manager下的Project中设置工程名称和工程路径,并选择编译软件。
代码生成设置:
在Code Generate中选择第二个,然后Generate Code,即生成代码:
可以打开MDK工程编辑了。
在工程文件夹内部新建“BSP”文件夹:
在BSP文件夹内建立自定义驱动的新文件夹:
本部分的代码从项目基础篇005. 按键控制中修改而来,程序与硬件必须匹配,为培养同学们在不同STM32芯片间移植程序的灵活性,本专栏中的课程采用几种不同的STM32芯片,请结合硬件电路修改代码。
Global文件夹内建立文件(红框内的文件用于一些全局变量函数,本文暂时不用)user.c和user.h:
- #ifndef __USER_H
- #define __USER_H
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- #include "main.h"
-
- //#define uchar unsigned char
- typedef unsigned char uchar;
-
- // ! --定义位带操作-->>>
- //位带操作,实现51类似的GPIO控制功能
- //具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).M4同M3类似,只是寄存器地址变了.
- //IO口操作宏定义
- #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
- #define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
- #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
- //IO口地址映射
- #define GPIOA_ODR_Addr (GPIOA_BASE+20) //0x40020014
- #define GPIOB_ODR_Addr (GPIOB_BASE+20) //0x40020414
- #define GPIOC_ODR_Addr (GPIOC_BASE+20) //0x40020814
- #define GPIOD_ODR_Addr (GPIOD_BASE+20) //0x40020C14
- #define GPIOE_ODR_Addr (GPIOE_BASE+20) //0x40021014
- #define GPIOF_ODR_Addr (GPIOF_BASE+20) //0x40021414
- #define GPIOG_ODR_Addr (GPIOG_BASE+20) //0x40021814
- #define GPIOH_ODR_Addr (GPIOH_BASE+20) //0x40021C14
- #define GPIOI_ODR_Addr (GPIOI_BASE+20) //0x40022014
- #define GPIOJ_ODR_ADDr (GPIOJ_BASE+20) //0x40022414
- #define GPIOK_ODR_ADDr (GPIOK_BASE+20) //0x40022814
-
- #define GPIOA_IDR_Addr (GPIOA_BASE+16) //0x40020010
- #define GPIOB_IDR_Addr (GPIOB_BASE+16) //0x40020410
- #define GPIOC_IDR_Addr (GPIOC_BASE+16) //0x40020810
- #define GPIOD_IDR_Addr (GPIOD_BASE+16) //0x40020C10
- #define GPIOE_IDR_Addr (GPIOE_BASE+16) //0x40021010
- #define GPIOF_IDR_Addr (GPIOF_BASE+16) //0x40021410
- #define GPIOG_IDR_Addr (GPIOG_BASE+16) //0x40021810
- #define GPIOH_IDR_Addr (GPIOH_BASE+16) //0x40021C10
- #define GPIOI_IDR_Addr (GPIOI_BASE+16) //0x40022010
- #define GPIOJ_IDR_Addr (GPIOJ_BASE+16) //0x40022410
- #define GPIOK_IDR_Addr (GPIOK_BASE+16) //0x40022810
-
- //IO口操作,只对单一的IO口!
- //确保n的值小于16!
- #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
- #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
-
- #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
- #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
-
- #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
- #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
-
- #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
- #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
-
- #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
- #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入
-
- #define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
- #define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
-
- #define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
- #define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
-
- #define PHout(n) BIT_ADDR(GPIOH_ODR_Addr,n) //输出
- #define PHin(n) BIT_ADDR(GPIOH_IDR_Addr,n) //输入
-
- #define PIout(n) BIT_ADDR(GPIOI_ODR_Addr,n) //输出
- #define PIin(n) BIT_ADDR(GPIOI_IDR_Addr,n) //输入
-
- #define PJout(n) BIT_ADDR(GPIOJ_ODR_Addr,n) //输出
- #define PJin(n) BIT_ADDR(GPIOJ_IDR_Addr,n) //输入
-
- #define PKout(n) BIT_ADDR(GPIOK_ODR_Addr,n) //输出
- #define PKin(n) BIT_ADDR(GPIOK_IDR_Addr,n) //输入
-
- // ! --汇编函数声明-->>>
- void WFI_SET(void); //执行WFI指令
- void INTX_DISABLE(void);//关闭所有中断
- void INTX_ENABLE(void); //开启所有中断
- void MSR_MSP(uint32_t addr); //设置堆栈地址
-
- // ! --延时函数声明-->>>
- void delay_init(uint8_t SYSCLK);
- void delay_ms(uint16_t nms);
- void delay_us(uint32_t nus);
-
- void delaySoft_ns(uint32_t t_ns); //ns级纯软件延时函数,不使用定时器,延时不准,需要调试
- void delaySoft_us(uint32_t t_us);
-
- #ifdef __cplusplus
- }
- #endif
-
- #endif /*__ USER_H__ */
- #include "global/user.h"
-
-
- #ifdef USE_FULL_ASSERT
- //当编译提示出错的时候此函数用来报告错误的文件和所在行
- //file:指向源文件
- //line:指向在文件中的行数
- void assert_failed(uint8_t* file, uint32_t line)
- {
- while (1)
- {
- }
- }
- #endif
-
- // ! ------延时函数------->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
- //使用SysTick的普通计数模式对延迟进行管理(支持ucosii/ucosiii)
- //包括delay_us,delay_ms
- //********************************************************************************
-
- static uint32_t fac_us=0; //us延时倍乘数
-
- /**
- * @DESCRIPTION: 初始化延迟函数
- * @INPUT ARGS: 系统时钟频率SYSCLK=主PLL时钟,即:SYSCLK= (外部晶振*PLLN)/(PLLM*PLLP)
- * @OUTPUT ARGS: none
- * @NOTE : SYSTICK的时钟固定为AHB时钟
- * @param {uint8_t} SYSCLK
- * @return {*}
- */
- void delay_init(uint8_t SYSCLK)
- {
- HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); //SysTick频率为HCLK
- fac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用
- }
-
- //延时nus
- //nus为要延时的us数.
- //nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)
- 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; //时间超过/等于要延迟的时间,则退出.
- }
- };
- }
-
- //延时nms
- //nms:要延时的ms数
- void delay_ms(uint16_t nms)
- {
- uint32_t i;
- for(i=0;i<nms;i++) delay_us(1000);
- }
-
- // ! ------软件延时函数------->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
- /**
- * @DESCRIPTION: us级纯软件延时函数,不使用定时器
- * @INPUT ARGS : none
- * @OUTPUT ARGS: none
- * @RETURNS : none
- * @NOTES : F407内部时钟为168MHz时,每个指令周期约6ns。
- * @param {uint32_t} t_us
- */
- #define INS_CPU_CYCLES 8 //一条自增减指令所需的CPU周期数
- #define ADJ_CPU_CYCLES 62 //延时函数自身需要的CPU周期数(根据需要调整)
- void delaySoft_us(uint32_t t_us)
- {
- uint32_t count;
- count = (HAL_RCC_GetHCLKFreq()/1000000*t_us - ADJ_CPU_CYCLES)/INS_CPU_CYCLES;
- while(count--);
- }
-
- /**
- * @DESCRIPTION: ns级纯软件延时函数,不使用定时器,延时不准,需要调试
- * @INPUT ARGS : none
- * @OUTPUT ARGS: none
- * @RETURNS : none
- * @NOTES : F407内部时钟为168MHz时,每个指令周期约6ns。
- * @param {uint32_t} t_ns
- */
- void delaySoft_ns(uint32_t t_ns)
- {
- do
- {
- ;
- }
- while(t_ns--);
- }
-
- // ! ------汇编指令------->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
- //THUMB指令不支持汇编内联
- //采用如下方法实现执行汇编指令WFI
- #if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) //AC6编译器
- //以下为汇编函数(AC6)
- void WFI_SET(void) //执行WFI指令
- {
- __ASM volatile("WFI");
- }
- void INTX_DISABLE(void) //关闭所有中断
- {
- __ASM volatile("CPSID I");
- __ASM volatile("BX LR");
- }
- void INTX_ENABLE(void) //开启所有中断
- {
- __ASM volatile("CPSIE I");
- __ASM volatile("BX LR");
- }
- void MSR_MSP(uint32_t addr) //设置堆栈地址
- {
- __ASM volatile("MSR MSP, r0");
- __ASM volatile("BX r14");
- }
- #elif defined ( __CC_ARM ) //AC5编译器
- __asm void WFI_SET(void)
- {
- WFI;
- }
- //关闭所有中断(但是不包括fault和NMI中断)
- __asm void INTX_DISABLE(void)
- {
- CPSID I
- BX LR
- }
- //开启所有中断
- __asm void INTX_ENABLE(void)
- {
- CPSIE I
- BX LR
- }
- //设置栈顶地址
- //addr:栈顶地址
- __asm void MSR_MSP(uint32_t addr)
- {
- MSR MSP, r0 //set Main Stack value
- BX r14
- }
- #endif
Key文件夹截图
key.h文件的代码如下:
- #ifndef _KEY_H
- #define _KEY_H
- #include "main.h"
-
- #define KEY_ON 0
- #define KEY_OFF 1
-
- void key_Init(void);
- uint8_t Key_Scan(GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin);
-
- #endif
key.c文件的代码如下:
- #include "main.h"
- #include <stdio.h>
- #include <string.h>
- #include "global/user.h"
- #include "key\key.h"
-
- /**
- * @DESCRIPTION: 初始化SPI端口
- * @INPUT ARGS : none
- * @OUTPUT ARGS: none
- * @RETURNS : none
- * @NOTES : none
- */
- void key_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct = {0};
-
- /* GPIO Ports Clock Enable */
- __HAL_RCC_GPIOC_CLK_ENABLE();
- __HAL_RCC_GPIOA_CLK_ENABLE();
- __HAL_RCC_GPIOB_CLK_ENABLE();
-
-
- /*Configure GPIO pins : PCPin PCPin */
- GPIO_InitStruct.Pin = KEY2_Pin;
- GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
- GPIO_InitStruct.Pull = GPIO_PULLUP;
- HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
-
- /*Configure GPIO pin : PtPin */
- GPIO_InitStruct.Pin = KEY1_Pin;
- GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
- GPIO_InitStruct.Pull = GPIO_PULLDOWN;
- HAL_GPIO_Init(KEY1_GPIO_Port, &GPIO_InitStruct);
-
-
- /*Configure GPIO pins : PBPin PBPin */
- GPIO_InitStruct.Pin = KEY3_Pin|KEY4_Pin;
- GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
- GPIO_InitStruct.Pull = GPIO_PULLUP;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
- }
-
- /**
- * @brief 检测是否有按键按下
- * @param GPIOx:具体的端口, x可以是(A...K)
- * @param GPIO_PIN:具体的端口位, 可以是GPIO_PIN_x(x可以是0...15)
- * @retval 按键的状态
- * @arg KEY_ON:按键按下(注意,key_up按键的电平定义时相反的)
- * @arg KEY_OFF:按键没按下
- */
- uint8_t Key_Scan(GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin)
- {
- /*检测是否有按键按下 */
- if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == KEY_ON )
- {
- /*等待按键释放 */
- while(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == KEY_ON);
- return KEY_ON;
- }
- else
- return KEY_OFF;
- }
-
在工程的“BSP”文件夹内,分别建立如下文件:
其中,RC522.c及RC522.h为MFRC522芯片的通用驱动程序,这部分程序可以移植到任何单片机上。RFID.h和RFID.c是针对Mifare 1卡的应用代码。
- #ifndef _RC522_H
- #define _RC522_H
- #include "main.h"
-
- /***********************************************************************************
- * MFRC522驱动程序 *
- ************************************************************************************/
- /*MFRC522寄存器定义*/
- //PAGE0
- #define MFRC_RFU00 0x00
- #define MFRC_CommandReg 0x01
- #define MFRC_ComIEnReg 0x02
- #define MFRC_DivlEnReg 0x03
- #define MFRC_ComIrqReg 0x04
- #define MFRC_DivIrqReg 0x05
- #define MFRC_ErrorReg 0x06
- #define MFRC_Status1Reg 0x07
- #define MFRC_Status2Reg 0x08
- #define MFRC_FIFODataReg 0x09
- #define MFRC_FIFOLevelReg 0x0A
- #define MFRC_WaterLevelReg 0x0B
- #define MFRC_ControlReg 0x0C
- #define MFRC_BitFramingReg 0x0D
- #define MFRC_CollReg 0x0E
- #define MFRC_RFU0F 0x0F
- //PAGE1
- #define MFRC_RFU10 0x10
- #define MFRC_ModeReg 0x11
- #define MFRC_TxModeReg 0x12
- #define MFRC_RxModeReg 0x13
- #define MFRC_TxControlReg 0x14
- #define MFRC_TxAutoReg 0x15 //中文手册有误
- #define MFRC_TxSelReg 0x16
- #define MFRC_RxSelReg 0x17
- #define MFRC_RxThresholdReg 0x18
- #define MFRC_DemodReg 0x19
- #define MFRC_RFU1A 0x1A
- #define MFRC_RFU1B 0x1B
- #define MFRC_MifareReg 0x1C
- #define MFRC_RFU1D 0x1D
- #define MFRC_RFU1E 0x1E
- #define MFRC_SerialSpeedReg 0x1F
- //PAGE2
- #define MFRC_RFU20 0x20
- #define MFRC_CRCResultRegM 0x21
- #define MFRC_CRCResultRegL 0x22
- #define MFRC_RFU23 0x23
- #define MFRC_ModWidthReg 0x24
- #define MFRC_RFU25 0x25
- #define MFRC_RFCfgReg 0x26
- #define MFRC_GsNReg 0x27
- #define MFRC_CWGsCfgReg 0x28
- #define MFRC_ModGsCfgReg 0x29
- #define MFRC_TModeReg 0x2A
- #define MFRC_TPrescalerReg 0x2B
- #define MFRC_TReloadRegH 0x2C
- #define MFRC_TReloadRegL 0x2D
- #define MFRC_TCounterValueRegH 0x2E
- #define MFRC_TCounterValueRegL 0x2F
- //PAGE3
- #define MFRC_RFU30 0x30
- #define MFRC_TestSel1Reg 0x31
- #define MFRC_TestSel2Reg 0x32
- #define MFRC_TestPinEnReg 0x33
- #define MFRC_TestPinValueReg 0x34
- #define MFRC_TestBusReg 0x35
- #define MFRC_AutoTestReg 0x36
- #define MFRC_VersionReg 0x37
- #define MFRC_AnalogTestReg 0x38
- #define MFRC_TestDAC1Reg 0x39
- #define MFRC_TestDAC2Reg 0x3A
- #define MFRC_TestADCReg 0x3B
- #define MFRC_RFU3C 0x3C
- #define MFRC_RFU3D 0x3D
- #define MFRC_RFU3E 0x3E
- #define MFRC_RFU3F 0x3F
-
- /*MFRC522的FIFO长度定义*/
- #define MFRC_FIFO_LENGTH 64
-
- /*MFRC522传输的帧长定义*/
- #define MFRC_MAXRLEN 18
-
- /*MFRC522命令集,中文手册P59*/
- #define MFRC_IDLE 0x00 //取消当前命令的执行
- #define MFRC_CALCCRC 0x03 //激活CRC计算
- #define MFRC_TRANSMIT 0x04 //发送FIFO缓冲区内容
- #define MFRC_NOCMDCHANGE 0x07 //无命令改变
- #define MFRC_RECEIVE 0x08 //激活接收器接收数据
- #define MFRC_TRANSCEIVE 0x0C //发送并接收数据
- #define MFRC_AUTHENT 0x0E //执行Mifare认证(验证密钥)
- #define MFRC_RESETPHASE 0x0F //复位MFRC522
-
- /*MFRC522通讯时返回的错误代码*/
- #define MFRC_OK (char)0
- #define MFRC_NOTAGERR (char)(-1)
- #define MFRC_ERR (char)(-2)
-
- /*MFRC522函数声明*/
- void MFRC_Init(void);
- void MFRC_WriteReg(uint8_t addr, uint8_t data);
- uint8_t MFRC_ReadReg(uint8_t addr);
- void MFRC_SetBitMask(uint8_t addr, uint8_t mask);
- void MFRC_ClrBitMask(uint8_t addr, uint8_t mask);
- void MFRC_CalulateCRC(uint8_t *pInData, uint8_t len, uint8_t *pOutData);
- char MFRC_CmdFrame(uint8_t cmd, uint8_t *pInData, uint8_t InLenByte, uint8_t *pOutData, uint16_t *pOutLenBit);
-
-
- /***********************************************************************************
- * MFRC552与MF1卡通讯接口程序 *
- ************************************************************************************/
- /*Mifare1卡片命令字*/
- #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_TRANSFER 0xB0 //转存(传送)
- #define PICC_RESTORE 0xC2 //恢复(重储)
- #define PICC_HALT 0x50 //休眠
-
- /*PCD通讯时返回的错误代码*/
- #define PCD_OK (char)0 //成功
- #define PCD_NOTAGERR (char)(-1) //无卡
- #define PCD_ERR (char)(-2) //出错
-
- /*PCD函数声明*/
- void PCD_Init(void);
- void PCD_Reset(void);
- void PCD_AntennaOn(void);
- void PCD_AntennaOff(void);
- char PCD_Request(uint8_t RequestMode, uint8_t *pCardType); //寻卡,并返回卡的类型
- char PCD_Anticoll(uint8_t *pSnr); //防冲突,返回卡号
- char PCD_Select(uint8_t *pSnr); //选卡
- char PCD_AuthState(uint8_t AuthMode, uint8_t BlockAddr, uint8_t *pKey, uint8_t *pSnr); //验证密码(密码A和密码B)
- char PCD_WriteBlock(uint8_t BlockAddr, uint8_t *pData); //写数据
- char PCD_ReadBlock(uint8_t BlockAddr, uint8_t *pData); //读数据
- char PCD_Value(uint8_t mode, uint8_t BlockAddr, uint8_t *pValue);
- char PCD_BakValue(uint8_t sourceBlockAddr, uint8_t goalBlockAddr);
- char PCD_Halt(void);
- void StartIDcardTask(void const * argument);
-
- #endif
- /**
- MFRC522-AN模块采用Philips MFRC522芯片设计读卡电路,使用方便,成本低廉,适用
- 于设备开发、读卡器开发等高级应用的用户、需要进行射频卡终端设计/生产的用户。
- 模块参数:
- ①工作电压:3.3v
- ②工作频率:13.56MHz
- ③支持卡类型:mifare1 s50、mifare1s70、 mifareUltraLight、mifare Pro, mifare Desfire
- ④通信方式:SPI协议
- ⑤环境工作温度:-20°C——80°C
- M1卡分为16个扇区,每个扇区由四个块(块0、块1、块2、块3)组成
- 将16个扇区的64个块按绝对地址编号为:0~63
- 第0个扇区的块0(即绝对地址0块),用于存放厂商代码,已经固化不可更改
- 每个扇区的块0、块1、块2为数据块,可用于存放数据
- 每个扇区的块3为控制块(绝对地址为:块3、块7、块11.....)包括密码A,存取控制、密码B等
- 1、CPU选择
- STM32F446RE,内部时钟180MHz
- 2、STM32CubeMX 定义任意两个引脚,作为复位脚和片选脚,并对引脚作出如下配置:
- GPlO output level --High
- GPIO mode --Output Push Pull
- GPIO Pull-up/Pull-down --No pull-up and no pull-down
- Maximum output speed --LOW
- User label --RC522_RST/RC522_SDA
- ---------------------------------------------------------
- 开启SPI功能,模式选择-->Full-Duplex Master(全双工),其他配置如下:
- Basic Parameters
- Frame format-->Motorola
- Data size -->8 Bits
- First bit -->MSB First
- Clock Parameters
- Prescaler(for Baud Rate)-->8
- Baud rate -->5.625MBits/s【RC522中的SPI最高速率为10MHz/s】
- Clock Polarity(CPOL) -->LOW
- Clock Phase(CPHA) -->1 Edge
- Advanced Parameters
- CRC Calculation -->Disabled
- NSS Signal Type -->Software
- 3、接线方式:
- SPI_MISO(MUC)--> MISO(器件)
- SPI_MOSI(MUC)--> MOSI(器件)
- 其他引脚一一对应
- //! Nucleo-F446RE接口
- //SPI2_SCK PB10---(接Arduino D6)
- //SPI2_MISO PC2----(接CN7左下2)
- //SPI2_MOSI PC1----(接Arduino A4)
- //RCC522_RST(CE) PC7----(接Arduino D9)
- //RCC522_NSS(SDA) PB6----(接Arduino D10)
- //RCC522_IRQ 悬空
- 4、SPI模式说明:SPI总线传输的四种模式:
- * SPI传输的模式由CPOL:clock polarity 时钟的极性,和CPHA:clock phase 时钟的相位控制。
- * RC522采用的是CPOL=0,CPHA=0的工作模式。在CubeMX中,SPI_CPHA设置为1Edge。
- * ┌─────────┬───────┬───────┬─────────────────┬─────────────────┐
- * │ SPI模式 │ CPOL │ CPHA │ 空闲时间SCLK状态 │ 采样时刻 │
- * │ 0 │ 0 │ 0 │ 低电平 │ 奇数边沿(上升沿) │
- * │ 1 │ 0 │ 1 │ 低电平 │ 偶数边沿(下降沿) │
- * │ 2 │ 1 │ 0 │ 高电平 │ 奇数边沿(下降沿) │
- * │ 3 │ 1 │ 1 │ 高电平 │ 偶数边沿(上升沿) │
- * └─────────┴───────┴───────┴─────────────────┴─────────────────┘
- 5、应用函数
- MFRC_Init();//初始化
- PCD_Reset();//器件复位
- PCD_Request(PICC_REQALL, RxBuffer);//返回值为0,代表寻卡成功;并把卡类型存入RxBuffer中
- PCD_Anticoll(RxBuffer); //把(十六进制)的4个字节卡号存储在数组RxBuffer中
- ***********************************/
-
- // #define RC522_SDA GPIO_Port GPIOB
- // #define RC522_SDA Pin GPIO_PIN_6 //cs、nss、SDA指同一个口
- // #define RC522_RST GPIO_Port GPIOC
- // #define RC522_RST Pin GPIO_PIN_7
-
- #include "main.h"
- #include <stdio.h>
- #include <string.h>
- #include "global/user.h"
- #include "usart.h"
- #include "RC522\RC522.h"
-
- extern SPI_HandleTypeDef hspi2;
-
- // #define osDelay HAL_Delay
- #define osDelay delay_ms
-
- #define RS522_RST(N) HAL_GPIO_WritePin(RC522_RST_GPIO_Port, RC522_RST_Pin, N == 1 ? GPIO_PIN_SET : GPIO_PIN_RESET)
- #define RC522_SDA(N) HAL_GPIO_WritePin(RC522_SDA_GPIO_Port, RC522_SDA_Pin, N == 1 ? GPIO_PIN_SET : GPIO_PIN_RESET)
-
- /**************************************************************************************
- * 函数名称:MFRC_Init
- * 功能描述:MFRC初始化
- * 入口参数:无
- * 出口参数:无
- * 返 回 值:无
- * 说 明:MFRC的SPI接口速率为0~10Mbps
- ***************************************************************************************/
- void MFRC_Init(void)
- {
- RC522_SDA(1);
- RS522_RST(1);
- }
-
- /**************************************************************************************
- * 函数名称: SPI_RW_Byte
- * 功能描述: 模拟SPI读写一个字节
- * 入口参数: -byte:要发送的数据
- * 出口参数: -byte:接收到的数据
- ***************************************************************************************/
- static uint8_t ret; // 这些函数是HAL与标准库不同的地方【读写函数】
- uint8_t SPI2_RW_Byte(uint8_t byte)
- {
- //硬件SPI
- HAL_SPI_TransmitReceive(&hspi2, &byte, &ret, 1, 10); // 把byte 写入,并读出一个值,把它存入ret
- return ret; // 入口是byte 的地址,读取时用的也是ret地址,一次只写入一个值10
- //下面是模拟SPI
- // if (byte == 0x00) // 读数据时
- // {
- // ret = RC522_SPI_ReadByte();
- // }
- // RC522_SPI_SendByte(byte);
- // return ret;
- }
-
- /**************************************************************************************
- * 函数名称:MFRC_WriteReg
- * 功能描述:写一个寄存器
- * 入口参数:-addr:待写的寄存器地址
- * -data:待写的寄存器数据
- * 出口参数:无
- * 返 回 值:无
- * 说 明:无
- ***************************************************************************************/
- void MFRC_WriteReg(uint8_t addr, uint8_t data)
- {
- uint8_t AddrByte;
- AddrByte = (addr << 1) & 0x7E; // 求出地址字节
- RC522_SDA(0); // NSS拉低
- SPI2_RW_Byte(AddrByte); // 写地址字节
- SPI2_RW_Byte(data); // 写数据
- RC522_SDA(1); // NSS拉高
- }
-
- /**************************************************************************************
- * 函数名称:MFRC_ReadReg
- * 功能描述:读一个寄存器
- * 入口参数:-addr:待读的寄存器地址
- * 出口参数:无
- * 返 回 值:-data:读到寄存器的数据
- * 说 明:无
- ***************************************************************************************/
- uint8_t MFRC_ReadReg(uint8_t addr)
- {
- uint8_t AddrByte, data;
- AddrByte = ((addr << 1) & 0x7E) | 0x80; // 求出地址字节
- RC522_SDA(0); // NSS拉低
- SPI2_RW_Byte(AddrByte); // 写地址字节
- data = SPI2_RW_Byte(0x00); // 读数据
- RC522_SDA(1); // NSS拉高
- return data;
- }
-
- /**************************************************************************************
- * 函数名称:MFRC_SetBitMask
- * 功能描述:设置寄存器的位
- * 入口参数:-addr:待设置的寄存器地址
- * -mask:待设置寄存器的位(可同时设置多个bit)
- * 出口参数:无
- * 返 回 值:无
- * 说 明:无
- ***************************************************************************************/
- void MFRC_SetBitMask(uint8_t addr, uint8_t mask)
- {
- uint8_t temp;
- temp = MFRC_ReadReg(addr); // 先读回寄存器的值
- MFRC_WriteReg(addr, temp | mask); // 处理过的数据再写入寄存器
- }
-
- /**************************************************************************************
- * 函数名称:MFRC_ClrBitMask
- * 功能描述:清除寄存器的位
- * 入口参数:-addr:待清除的寄存器地址
- * -mask:待清除寄存器的位(可同时清除多个bit)
- * 出口参数:无
- * 返 回 值:无
- * 说 明:无
- ***************************************************************************************/
- void MFRC_ClrBitMask(uint8_t addr, uint8_t mask)
- {
- uint8_t temp;
- temp = MFRC_ReadReg(addr); // 先读回寄存器的值
- MFRC_WriteReg(addr, temp & ~mask); // 处理过的数据再写入寄存器
- }
-
- /**************************************************************************************
- * 函数名称:MFRC_CalulateCRC
- * 功能描述:用MFRC计算CRC结果
- * 入口参数:-pInData:带进行CRC计算的数据
- * -len:带进行CRC计算的数据长度
- * -pOutData:CRC计算结果
- * 出口参数:-pOutData:CRC计算结果
- * 返 回 值:无
- * 说 明:无
- ***************************************************************************************/
- void MFRC_CalulateCRC(uint8_t *pInData, uint8_t len, uint8_t *pOutData)
- {
- // 0xc1 1 2 pInData[2]
- uint8_t temp;
- uint32_t i;
- MFRC_ClrBitMask(MFRC_DivIrqReg, 0x04); // 使能CRC中断
- MFRC_WriteReg(MFRC_CommandReg, MFRC_IDLE); // 取消当前命令的执行
- MFRC_SetBitMask(MFRC_FIFOLevelReg, 0x80); // 清除FIFO及其标志位
- for (i = 0; i < len; i++) // 将待CRC计算的数据写入FIFO
- {
- MFRC_WriteReg(MFRC_FIFODataReg, *(pInData + i));
- }
- MFRC_WriteReg(MFRC_CommandReg, MFRC_CALCCRC); // 执行CRC计算
- i = 100000;
- do
- {
- temp = MFRC_ReadReg(MFRC_DivIrqReg); // 读取DivIrqReg寄存器的值
- i--;
- } while ((i != 0) && !(temp & 0x04)); // 等待CRC计算完成
- pOutData[0] = MFRC_ReadReg(MFRC_CRCResultRegL); // 读取CRC计算结果
- pOutData[1] = MFRC_ReadReg(MFRC_CRCResultRegM);
- }
-
- /**************************************************************************************
- * 函数名称:MFRC_CmdFrame
- * 功能描述:MFRC522和ISO14443A卡通讯的命令帧函数
- * 入口参数:-cmd:MFRC522命令字
- * -pIndata:MFRC522发送给MF1卡的数据的缓冲区首地址
- * -InLenByte:发送数据的字节长度
- * -pOutdata:用于接收MF1卡片返回数据的缓冲区首地址
- * -pOutLenBit:MF1卡返回数据的位长度
- * 出口参数:-pOutdata:用于接收MF1卡片返回数据的缓冲区首地址
- * -pOutLenBit:用于MF1卡返回数据位长度的首地址
- * 返 回 值:-status:错误代码(MFRC_OK、MFRC_NOTAGERR、MFRC_ERR)
- * 说 明:无
- ***************************************************************************************/
- char MFRC_CmdFrame(uint8_t cmd, uint8_t *pInData, uint8_t InLenByte, uint8_t *pOutData, uint16_t *pOutLenBit)
- {
- uint8_t lastBits;
- uint8_t n;
- uint32_t i;
- char status = MFRC_ERR;
- uint8_t irqEn = 0x00;
- uint8_t waitFor = 0x00;
-
- /*根据命令设置标志位*/
- switch (cmd)
- {
- case MFRC_AUTHENT: // Mifare认证
- irqEn = 0x12;
- waitFor = 0x10; // idleIRq中断标志
- break;
- case MFRC_TRANSCEIVE: // 发送并接收数据
- irqEn = 0x77;
- waitFor = 0x30; // RxIRq和idleIRq中断标志
- break;
- }
-
- /*发送命令帧前准备*/
- MFRC_WriteReg(MFRC_ComIEnReg, irqEn | 0x80); // 开中断
- MFRC_ClrBitMask(MFRC_ComIrqReg, 0x80); // 清除中断标志位SET1
- MFRC_WriteReg(MFRC_CommandReg, MFRC_IDLE); // 取消当前命令的执行
- MFRC_SetBitMask(MFRC_FIFOLevelReg, 0x80); // 清除FIFO缓冲区及其标志位
-
- /*发送命令帧*/
- for (i = 0; i < InLenByte; i++) // 写入命令参数
- {
- MFRC_WriteReg(MFRC_FIFODataReg, pInData[i]); // 写数据进 FIFODataReg
- }
- MFRC_WriteReg(MFRC_CommandReg, cmd); // 执行命令
- if (cmd == MFRC_TRANSCEIVE)
- {
- MFRC_SetBitMask(MFRC_BitFramingReg, 0x80); // 启动发送
- }
- i = 300000; // 根据时钟频率调整,操作M1卡最大等待时间25ms
- do // 认证 与寻卡等待时间
- {
- n = MFRC_ReadReg(MFRC_ComIrqReg); // 查询事件中断
- i--;
- } while ((i != 0) && !(n & 0x01) && !(n & waitFor)); // 等待命令完成
- MFRC_ClrBitMask(MFRC_BitFramingReg, 0x80); // 停止发送
-
- /*处理接收的数据*/
- if (i != 0)
- {
- // 读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
- if (!(MFRC_ReadReg(MFRC_ErrorReg) & 0x1B))
- {
- status = MFRC_OK;
- if (n & irqEn & 0x01) // 是否发生定时器中断
- {
- status = MFRC_NOTAGERR;
- }
- if (cmd == MFRC_TRANSCEIVE)
- {
- // 读FIFO中保存的字节数
- n = MFRC_ReadReg(MFRC_FIFOLevelReg);
- lastBits = MFRC_ReadReg(MFRC_ControlReg) & 0x07; // 最后接收到得字节的有效位数
- if (lastBits)
- {
- *pOutLenBit = (n - 1) * 8 + lastBits; // N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
- }
- else
- {
- *pOutLenBit = n * 8; // 最后接收到的字节整个字节有效
- }
- if (n == 0)
- {
- n = 1;
- }
- if (n > MFRC_MAXRLEN)
- {
- n = MFRC_MAXRLEN;
- }
- for (i = 0; i < n; i++)
- {
- pOutData[i] = MFRC_ReadReg(MFRC_FIFODataReg);
- }
- }
- }
- else
- {
- status = MFRC_ERR;
- }
- }
-
- MFRC_SetBitMask(MFRC_ControlReg, 0x80); // 停止定时器运行
- MFRC_WriteReg(MFRC_CommandReg, MFRC_IDLE); // 取消当前命令的执行
-
- return status;
- }
-
- /**************************************************************************************
- * 函数名称:PCD_Reset
- * 功能描述:PCD复位
- * 入口参数:无
- * 出口参数:无
- * 返 回 值:无
- * 说 明:无
- ***************************************************************************************/
- void PCD_Reset(void)
- {
- /*硬复位*/
- RS522_RST(1); // 用到复位引脚
- osDelay(2);
- RS522_RST(0);
- osDelay(2);
- RS522_RST(1);
- osDelay(2);
-
- /*软复位*/
- MFRC_WriteReg(MFRC_CommandReg, MFRC_RESETPHASE);
- osDelay(2);
-
- /*复位后的初始化配置*/
- MFRC_WriteReg(MFRC_ModeReg, 0x3D); // CRC初始值0x6363
- MFRC_WriteReg(MFRC_TReloadRegL, 30); // 定时器重装值
- MFRC_WriteReg(MFRC_TReloadRegH, 0);
- MFRC_WriteReg(MFRC_TModeReg, 0x8D); // 定义内部定时器的设置
- MFRC_WriteReg(MFRC_TPrescalerReg, 0x3E); // 设置定时器预分频值
- MFRC_WriteReg(MFRC_TxAutoReg, 0x40); // 调制发送信号为100%ASK
-
- PCD_AntennaOff(); // 关天线
- osDelay(2);
- PCD_AntennaOn(); // 开天线
- }
-
- /**************************************************************************************
- * 函数名称:PCD_AntennaOn
- * 功能描述:开启天线,使能PCD发送能量载波信号
- * 入口参数:无
- * 出口参数:无
- * 返 回 值:无
- * 说 明:每次开启或关闭天线之间应至少有1ms的间隔
- ***************************************************************************************/
- void PCD_AntennaOn(void)
- {
- uint8_t temp;
- temp = MFRC_ReadReg(MFRC_TxControlReg);
- if (!(temp & 0x03))
- {
- MFRC_SetBitMask(MFRC_TxControlReg, 0x03);
- }
- }
-
- /**************************************************************************************
- * 函数名称:PCD_AntennaOff
- * 功能描述:关闭天线,失能PCD发送能量载波信号
- * 入口参数:无
- * 出口参数:无
- * 返 回 值:无
- * 说 明:每次开启或关闭天线之间应至少有1ms的间隔
- ***************************************************************************************/
- void PCD_AntennaOff(void)
- {
- MFRC_ClrBitMask(MFRC_TxControlReg, 0x03);
- }
-
- /***************************************************************************************
- * 函数名称:PCD_Init
- * 功能描述:读写器初始化
- * 入口参数:无
- * 出口参数:无
- * 返 回 值:无
- * 说 明:无
- ***************************************************************************************/
- void PCD_Init(void)
- {
- MFRC_Init(); // MFRC管脚配置
- PCD_Reset(); // PCD复位 并初始化配置
- PCD_AntennaOff(); // 关闭天线
- PCD_AntennaOn(); // 开启天线
- }
-
- /***************************************************************************************
- * 函数名称:PCD_Request
- * 功能描述:寻卡
- * 入口参数: -RequestMode:寻卡方式
- * PICC_REQIDL:寻天线区内未进入休眠状态
- * PICC_REQALL:寻天线区内全部卡
- * -pCardType: 用于保存卡片类型
- * 出口参数:-pCardType:卡片类型
- * 0x4400:Mifare_UltraLight
- * 0x0400:Mifare_One(S50)
- * 0x0200:Mifare_One(S70)
- * 0x0800:Mifare_Pro(X)
- * 0x4403:Mifare_DESFire
- * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
- * 说 明:无
- ***************************************************************************************/
- char PCD_Request(uint8_t RequestMode, uint8_t *pCardType)
- {
- int status;
- uint16_t unLen;
- uint8_t CmdFrameBuf[MFRC_MAXRLEN];
-
- MFRC_ClrBitMask(MFRC_Status2Reg, 0x08); // 关内部温度传感器
- MFRC_WriteReg(MFRC_BitFramingReg, 0x07); // 存储模式,发送模式,是否启动发送等
- MFRC_SetBitMask(MFRC_TxControlReg, 0x03); // 配置调制信号13.56MHZ
-
- CmdFrameBuf[0] = RequestMode; // 存入 卡片命令字
-
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 1, CmdFrameBuf, &unLen);
-
- if ((status == PCD_OK) && (unLen == 0x10)) // 寻卡成功返回卡类型
- {
- *pCardType = CmdFrameBuf[0];
- *(pCardType + 1) = CmdFrameBuf[1];
- }
-
- return status;
- }
-
- /***************************************************************************************
- * 函数名称:PCD_Anticoll
- * 功能描述:防冲撞,获取卡号
- * 入口参数:-pSnr:用于保存卡片序列号,4字节
- * 出口参数:-pSnr:卡片序列号,4字节
- * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
- * 说 明:无
- ***************************************************************************************/
- char PCD_Anticoll(uint8_t *pSnr)
- {
- char status;
- uint8_t i, snr_check = 0;
- uint16_t unLen;
- uint8_t CmdFrameBuf[MFRC_MAXRLEN];
-
- MFRC_ClrBitMask(MFRC_Status2Reg, 0x08); // 清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
- MFRC_WriteReg(MFRC_BitFramingReg, 0x00); // 清理寄存器 停止收发
- MFRC_ClrBitMask(MFRC_CollReg, 0x80); // 清ValuesAfterColl所有接收的位在冲突后被清除
-
- CmdFrameBuf[0] = PICC_ANTICOLL1; // 卡片防冲突命令
- CmdFrameBuf[1] = 0x20;
-
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 2, CmdFrameBuf, &unLen); // 与卡片通信
-
- if (status == PCD_OK) // 通信成功
- {
- for (i = 0; i < 4; i++)
- {
- *(pSnr + i) = CmdFrameBuf[i]; // 读出UID
- snr_check ^= CmdFrameBuf[i];
- }
- if (snr_check != CmdFrameBuf[i])
- {
- status = PCD_ERR;
- }
- }
-
- MFRC_SetBitMask(MFRC_CollReg, 0x80);
- return status;
- }
-
- /***************************************************************************************
- * 函数名称:PCD_Select
- * 功能描述:选定卡片
- * 入口参数:-pSnr:卡片序列号,4字节
- * 出口参数:无
- * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
- * 说 明:无
- ***************************************************************************************/
- char PCD_Select(uint8_t *pSnr)
- {
- char status;
- uint8_t i;
- uint16_t unLen;
- uint8_t CmdFrameBuf[MFRC_MAXRLEN];
-
- CmdFrameBuf[0] = PICC_ANTICOLL1;
- CmdFrameBuf[1] = 0x70;
- CmdFrameBuf[6] = 0;
- for (i = 0; i < 4; i++)
- {
- CmdFrameBuf[i + 2] = *(pSnr + i);
- CmdFrameBuf[6] ^= *(pSnr + i);
- }
- MFRC_CalulateCRC(CmdFrameBuf, 7, &CmdFrameBuf[7]);
-
- MFRC_ClrBitMask(MFRC_Status2Reg, 0x08);
-
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 9, CmdFrameBuf, &unLen);
-
- if ((status == PCD_OK) && (unLen == 0x18))
- {
- status = PCD_OK;
- }
- else
- {
- status = PCD_ERR;
- }
- return status;
- }
-
- /***************************************************************************************
- * 函数名称:PCD_AuthState
- * 功能描述:验证卡片密码
- * 入口参数:-AuthMode:验证模式
- * PICC_AUTHENT1A:验证A密码
- * PICC_AUTHENT1B:验证B密码
- * -BlockAddr:块地址(0~63)
- * -pKey:密码
- * -pSnr:卡片序列号,4字节
- * 出口参数:无
- * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
- * 说 明:验证密码时,以扇区为单位,BlockAddr参数可以是同一个扇区的任意块
- ***************************************************************************************/
- char PCD_AuthState(uint8_t AuthMode, uint8_t BlockAddr, uint8_t *pKey, uint8_t *pSnr)
- {
- char status;
- uint16_t unLen;
- uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
- CmdFrameBuf[0] = AuthMode;
- CmdFrameBuf[1] = BlockAddr;
- for (i = 0; i < 6; i++)
- {
- CmdFrameBuf[i + 2] = *(pKey + i);
- }
- for (i = 0; i < 4; i++)
- {
- CmdFrameBuf[i + 8] = *(pSnr + i);
- }
-
- status = MFRC_CmdFrame(MFRC_AUTHENT, CmdFrameBuf, 12, CmdFrameBuf, &unLen);
- if ((status != PCD_OK) || (!(MFRC_ReadReg(MFRC_Status2Reg) & 0x08)))
- {
- status = PCD_ERR;
- }
-
- return status;
- }
-
- /***************************************************************************************
- * 函数名称:PCD_WriteBlock
- * 功能描述:写MF1卡数据块
- * 入口参数:-BlockAddr:块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
- * -pData: 用于保存待写入的数据,16字节
- * 出口参数:无
- * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
- * 说 明:无
- ***************************************************************************************/
- char PCD_WriteBlock(uint8_t BlockAddr, uint8_t *pData)
- {
- char status;
- uint16_t unLen;
- uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
-
- CmdFrameBuf[0] = PICC_WRITE;
- CmdFrameBuf[1] = BlockAddr;
- MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
-
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
-
- if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
- {
- status = PCD_ERR;
- }
-
- if (status == PCD_OK)
- {
- for (i = 0; i < 16; i++)
- {
- CmdFrameBuf[i] = *(pData + i);
- }
- MFRC_CalulateCRC(CmdFrameBuf, 16, &CmdFrameBuf[16]);
-
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 18, CmdFrameBuf, &unLen);
-
- if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
- {
- status = PCD_ERR;
- }
- }
-
- return status;
- }
-
- /***************************************************************************************
- * 函数名称:PCD_ReadBlock
- * 功能描述:读MF1卡数据块
- * 入口参数:-BlockAddr:块地址
- * -pData: 用于保存读出的数据,16字节
- * 出口参数:-pData: 用于保存读出的数据,16字节
- * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
- * 说 明:无
- ***************************************************************************************/
- char PCD_ReadBlock(uint8_t BlockAddr, uint8_t *pData)
- {
- char status;
- uint16_t unLen;
- uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
-
- CmdFrameBuf[0] = PICC_READ;
- CmdFrameBuf[1] = BlockAddr;
- MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
-
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
- if ((status == PCD_OK) && (unLen == 0x90))
- {
- for (i = 0; i < 16; i++)
- {
- *(pData + i) = CmdFrameBuf[i];
- }
- }
- else
- {
- status = PCD_ERR;
- }
-
- return status;
- }
-
- /***************************************************************************************
- * 函数名称:PCD_Value
- * 功能描述:对MF1卡数据块增减值操作
- * 入口参数:
- * -BlockAddr:块地址
- * -pValue:四字节增值的值,低位在前
- * -mode:数值块操作模式
- * PICC_INCREMENT:增值
- * PICC_DECREMENT:减值
- * 出口参数:无
- * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
- * 说 明:无
- ***************************************************************************************/
- char PCD_Value(uint8_t mode, uint8_t BlockAddr, uint8_t *pValue)
- {
- // 0XC1 1 Increment[4]={0x03, 0x01, 0x01, 0x01};
- char status;
- uint16_t unLen;
- uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
-
- CmdFrameBuf[0] = mode;
- CmdFrameBuf[1] = BlockAddr;
- MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
-
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
-
- if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
- {
- status = PCD_ERR;
- }
-
- if (status == PCD_OK)
- {
- for (i = 0; i < 16; i++)
- {
- CmdFrameBuf[i] = *(pValue + i);
- }
- MFRC_CalulateCRC(CmdFrameBuf, 4, &CmdFrameBuf[4]);
- unLen = 0;
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 6, CmdFrameBuf, &unLen);
- if (status != PCD_ERR)
- {
- status = PCD_OK;
- }
- }
-
- if (status == PCD_OK)
- {
- CmdFrameBuf[0] = PICC_TRANSFER;
- CmdFrameBuf[1] = BlockAddr;
- MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
-
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
-
- if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
- {
- status = PCD_ERR;
- }
- }
- return status;
- }
-
- /***************************************************************************************
- * 函数名称:PCD_BakValue
- * 功能描述:备份钱包(块转存)
- * 入口参数:-sourceBlockAddr:源块地址
- * -goalBlockAddr :目标块地址
- * 出口参数:无
- * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
- * 说 明:只能在同一个扇区内转存
- ***************************************************************************************/
- char PCD_BakValue(uint8_t sourceBlockAddr, uint8_t goalBlockAddr)
- {
- char status;
- uint16_t unLen;
- uint8_t CmdFrameBuf[MFRC_MAXRLEN];
-
- CmdFrameBuf[0] = PICC_RESTORE;
- CmdFrameBuf[1] = sourceBlockAddr;
- MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
- if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
- {
- status = PCD_ERR;
- }
-
- if (status == PCD_OK)
- {
- CmdFrameBuf[0] = 0;
- CmdFrameBuf[1] = 0;
- CmdFrameBuf[2] = 0;
- CmdFrameBuf[3] = 0;
- MFRC_CalulateCRC(CmdFrameBuf, 4, &CmdFrameBuf[4]);
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 6, CmdFrameBuf, &unLen);
- if (status != PCD_ERR)
- {
- status = PCD_OK;
- }
- }
-
- if (status != PCD_OK)
- {
- return PCD_ERR;
- }
-
- CmdFrameBuf[0] = PICC_TRANSFER;
- CmdFrameBuf[1] = goalBlockAddr;
- MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
- if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
- {
- status = PCD_ERR;
- }
-
- return status;
- }
-
- /***************************************************************************************
- * 函数名称:PCD_Halt
- * 功能描述:命令卡片进入休眠状态
- * 入口参数:无
- * 出口参数:无
- * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
- * 说 明:无
- ***************************************************************************************/
- char PCD_Halt(void)
- {
- char status;
- uint16_t unLen;
- uint8_t CmdFrameBuf[MFRC_MAXRLEN];
-
- CmdFrameBuf[0] = PICC_HALT;
- CmdFrameBuf[1] = 0;
- MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
-
- status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
-
- return status;
- }
RC522.c中的函数说明:
IO口定义:
- #define RS522_RST(N) HAL_GPIO_WritePin(RC522_RST_GPIO_Port, RC522_RST_Pin, N == 1 ? GPIO_PIN_SET : GPIO_PIN_RESET)
-
- #define RC522_SDA(N) HAL_GPIO_WritePin(RC522_SDA_GPIO_Port, RC522_SDA_Pin, N == 1 ? GPIO_PIN_SET : GPIO_PIN_RESET)
通过SPI总线读写RC522模块函数:
uint8_t SPI2_RW_Byte(uint8_t byte)
- #ifndef _RFID_H
- #define _RFID_H
- #include "main.h"
-
- extern uint8_t readUid[5];
- extern uint8_t UID[5]; //定义一张已知卡号,可以通过串口打印通过下面读取到的打印到上位机,再把那个读取的卡号填入数组
- extern uint8_t DefaultKey[6]; // 默认秘钥
-
- /*函数声明*/
- void RC522_Init(void);
- uint8_t EntranceGuard(uint8_t *readUid,void(*funCallBack)(void));
- void DoorSensor(void);
- void RfidIndicator(void);
- //void notarize_type1(void);
-
- char WriteAmount(uint8_t addr, uint32_t pData);
- char ReadAmount(uint8_t addr, uint32_t *pData);
- char ReadAmount(uint8_t addr, uint32_t *pData);
- char WriteDataBlock(uint8_t addr, uint8_t *pData, uint8_t Len);
- char ReadDataBlock(uint8_t addr, uint8_t *pData);
-
- #endif
- #include <stdio.h>
- #include <string.h>
- #include "global/user.h"
- #include "RC522\RC522.h"
- #include "RC522\RFID.h"
-
- uint8_t readUid[5];
- uint8_t UID[5] = {0x37, 0x7e, 0xbc, 0xfd}; // 自定义的卡号,用于比较
- uint8_t DefaultKey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 默认秘钥
-
- /***************************************************************************************
- * 函数名称:RC522_Init
- * 功能描述:初始化
- * 入口参数:无
- * 出口参数:无
- * 返 回 值:无
- * 说 明:无
- ***************************************************************************************/
- void RC522_Init(void)
- {
- MFRC_Init();
- PCD_Reset();
- printf("RC522初始化完成\n");
- }
-
- /***************************************************************************************
- * 函数名称:门禁开门
- * 功能描述:只读取并显示卡号,成功读取到卡号就退出,并调用回调功能函数
- * 入口参数:-readUid:用于保存卡片序列号,4字节
- -funCallBack:函数传参,无需会掉功能函数时填NULL即可
- * 出口参数:
- * 返 回 值:读到卡号返回0,失败返回1
- * 说 明:无
- ***************************************************************************************/
- uint8_t EntranceGuard(uint8_t *readUid, void (*funCallBack)(void))
- {
- uint8_t Temp[5]; // 存放IC卡的类型和UID(IC卡序列号)
- if (PCD_Request(PICC_REQALL, Temp) == PCD_OK) // 寻卡
- {//成功
- if (Temp[0] == 0x04 && Temp[1] == 0x00)
- printf("Mifare1-S50\n");
- else if (Temp[0] == 0x02 && Temp[1] == 0x00)
- printf("Mifare1-S70");
- else if (Temp[0] == 0x44 && Temp[1] == 0x00)
- printf("Mifare-UltraLight(MF0)");
- else if (Temp[0] == 0x08 && Temp[1] == 0x00)
- printf("Mifare-Pro(MF2)");
- else if (Temp[0] == 0x44 && Temp[1] == 0x03)
- printf("Mifare Desire(MF3)");
- else
- printf("Unknown");
- if (PCD_Anticoll(readUid) == PCD_OK) // 防冲撞,获取卡号,存入readUid
- { // 防冲撞成功
- if (funCallBack != NULL)
- funCallBack(); // 调用功能执行函数,如指示灯信号
- return 0;
- }
- }
- return 1;
- }
-
- /***************************************************************************************
- * 函数名称:DoorSensor
- * 功能描述:门磁控制信号
- * 入口参数:无
- * 出口参数:无
- * 返 回 值:无
- * 说 明:无
- ***************************************************************************************/
- void DoorSensor(void)
- {
- // 【STM32F446,NUCLEO-F446RE板】使用STM32CubeMX创建MDK工程,实现流水灯
- HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); // LED亮
- HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET); // LED灭
- HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); // LED灭
- HAL_Delay(500); // 延时 500ms
- HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); // LED灭
- HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); // LED亮
- HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); // LED灭
- }
-
- /***************************************************************************************
- * 函数名称:RfidIndicator
- * 功能描述:指示灯信号
- * 入口参数:无
- * 出口参数:无
- * 返 回 值:无
- * 说 明:无
- ***************************************************************************************/
- void RfidIndicator(void)
- {
- HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_RESET); // LED1亮
- HAL_Delay(1000); // 延时 500ms
- HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_SET); // LED1灭
- }
-
- /**
- * @brief 判断 addr 是否数据块
- * @param addr,块绝对地址(0-63)
- * @retval 返回值 1:是数据块;0:不是数据块
- */
- char IsDataBlock(uint8_t addr)
- {
- if (addr == 0)
- {
- printf("第0扇区的块0不可更改,不应对其进行操作\n");
- return 0;
- }
- // 如果是数据块(不包含数据块0)
- if ((addr < 64) && (((addr + 1) % 4) != 0))
- {
- return 1;
- }
-
- printf("块地址不是指向数据块\n");
- return 0;
- }
-
- /**
- * @brief 写 pData 字符串到M1卡中的数据块
- * @param addr,数据块地址(不能写入控制块)
- * @param pData,写入的数据,16字节
- * @retval 状态值= PCD_OK,成功
- */
- char PCD_WriteString(uint8_t addr, uint8_t *pData)
- {
- /* 如果是数据块(不包含数据块0),则写入 */
- if (IsDataBlock(addr))
- {
- return PCD_WriteBlock(addr, pData);
- }
-
- return PCD_ERR;
- }
-
- /**
- * @brief 读取M1卡中的一块数据到 pData
- * @param addr,数据块地址(不读取控制块)
- * @param pData,读出的数据,16字节
- * @retval 状态值= PCD_OK,成功
- */
- char PCD_ReadString(uint8_t addr, uint8_t *pData)
- {
- /* 如果是数据块(不包含数据块0),则读取 */
- if (IsDataBlock(addr))
- {
- return PCD_ReadBlock(addr, pData);
- }
-
- return PCD_ERR;
- }
-
- /**
- * @DESCRIPTION: 写入钱包金额
- * @INPUT ARGS: none
- * @OUTPUT ARGS: none
- * @NOTE : none
- * @param {uint8_t} addr:块地址
- * @param {uint32_t} pData:写入的金额
- * @return {*} 成功返回PCD_OK
- */
- char WriteAmount(uint8_t addr, 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] = addr;
- ucComMF522Buf[13] = ~addr;
- ucComMF522Buf[14] = addr;
- ucComMF522Buf[15] = ~addr;
- status = PCD_WriteBlock(addr, ucComMF522Buf);
- return status;
- }
-
- /**
- * @DESCRIPTION: 读取钱包金额
- * @INPUT ARGS: none
- * @OUTPUT ARGS: none
- * @NOTE : none
- * @param {uint8_t} addr:块地址
- * @param {uint32_t} *pData:读出的金额
- * @return {*}: 成功返回PCD_OK
- */
- char ReadAmount(uint8_t addr, uint32_t *pData)
- {
-
- char status = PCD_ERR;
- uint8_t j;
- uint8_t ucComMF522Buf[16];
- status = PCD_ReadBlock(addr, ucComMF522Buf);
- if (status != PCD_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 = PCD_OK;
- *pData = ucComMF522Buf[0] + (ucComMF522Buf[1] << 8) + (ucComMF522Buf[2] << 16) + (ucComMF522Buf[3] << 24);
- }
- else
- {
- status = PCD_ERR;
- *pData = 0;
- }
- return status;
- }
-
- /**
- * @brief 修改控制块 addr 的密码A。注意 addr 指的是控制块的地址。
- * 必须要校验密码B,密码B默认为6个0xFF,如果密码B也忘记了,那就改不了密码A了
- * @note 注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
- * @param addr:[控制块]所在的地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
- * @param pKeyA:指向新的密码A字符串,六个字符,比如 "123456"
- * @retval 成功返回 PCD_OK
- */
- char ChangeKeyA(uint8_t addr, uint8_t *pKeyA)
- {
- uint8_t KeyBValue[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
- uint8_t ucArrayID[4]; // 先后存放IC卡的类型和UID(IC卡序列号)
- uint8_t ucComMF522Buf[16];
- uint8_t j;
-
- // 寻卡
- while (PCD_Request(PICC_REQALL, ucArrayID) != PCD_OK)
- {
- printf("寻卡失败\n");
- delay_ms(1000);
- }
-
- printf("寻卡成功\n");
-
- // 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
- if (PCD_Anticoll(ucArrayID) == PCD_OK)
- {
- // 选中卡
- PCD_Select(ucArrayID);
-
- // 校验 B 密码
- if (PCD_AuthState(PICC_AUTHENT1B, addr, KeyBValue, ucArrayID) != PCD_OK)
- {
- printf("检验密码B失败\n");
- }
-
- // 读取控制块里原本的数据(只要修改密码A,其他数据不改)
- if (PCD_ReadBlock(addr, ucComMF522Buf) != PCD_OK)
- {
- printf("读取控制块数据失败\n");
- return PCD_ERR;
- }
-
- // 修改密码A
- for (j = 0; j < 6; j++)
- ucComMF522Buf[j] = pKeyA[j];
-
- if (PCD_WriteBlock(addr, ucComMF522Buf) != PCD_OK)
- {
- printf("写入数据到控制块失败\n");
- return PCD_ERR;
- }
-
- printf("密码A修改成功!\n");
- PCD_Halt();
-
- return PCD_OK;
- }
-
- return PCD_ERR;
- }
-
- /**
- * @brief 按照RC522操作流程写入16字节数据到块 addr
- * 函数里校验的是密码B,密码B默认为6个0xFF,也可以校验密码A
- * 用法:WriteDataBlock( 1, "123456789\n", 10); //字符串不够16个字节的后面补零写入
- * @note 注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
- * 注意:使用该函数要注意 addr 是块0、数据块还是控制块,该函数内部不对此做判断
- * @param addr:任意块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
- * @param pData:指向要写入的数据,最大16个字符
- * @param Len:要写入数据的字节数
- * @retval 成功返回 PCD_OK
- */
- char WriteDataBlock(uint8_t addr, uint8_t *pData, uint8_t Len)
- {
- uint8_t KeyBValue[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
- uint8_t ucArrayID[4]; // 先后存放IC卡的类型和UID(IC卡序列号)
- uint8_t ucComMF522Buf[16];
- uint8_t j;
-
- // 寻卡
- while (PCD_Request(PICC_REQALL, ucArrayID) != PCD_OK)
- {
- printf("寻卡失败\n");
- delay_ms(1000);
- }
-
- printf("寻卡成功\n");
-
- // 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
- if (PCD_Anticoll(ucArrayID) == PCD_OK)
- {
- // 选中卡
- PCD_Select(ucArrayID);
-
- // 校验 B 密码
- if (PCD_AuthState(PICC_AUTHENT1B, addr, KeyBValue, ucArrayID) != PCD_OK)
- {
- printf("检验密码B失败\n");
- }
-
- // 拷贝 pData 里的 Len 个字符到 ucComMF522Buf
- for (j = 0; j < 16; j++)
- {
- if (j < Len)
- ucComMF522Buf[j] = pData[j];
- else
- ucComMF522Buf[j] = 0; // 16个字节若是未填满的字节置0
- }
-
- // 写入字符串
- if (PCD_WriteBlock(addr, ucComMF522Buf) != PCD_OK)
- {
- printf("写入数据到数据块失败\n");
- return PCD_ERR;
- }
-
- printf("写入数据成功!\n");
- PCD_Halt();
-
- return PCD_OK;
- }
-
- return PCD_ERR;
- }
-
- /**
- * @brief 按照RC522操作流程读取块 addr
- * 函数里校验的是密码B,密码B默认为6个0xFF,也可以校验密码A
- * 用法:ReadDataBlock( 1, databuf); // databuf 至少为16字节:uint8_t databuf[16];
- * @note 注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
- * 注意:使用该函数要注意 addr 是块0、数据块还是控制块,该函数内部不对此做判断
- * @param addr:任意块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
- * @param pData:指向读取到的数据,包含16个字符
- * @retval 成功返回 PCD_OK
- */
- char ReadDataBlock(uint8_t addr, uint8_t *pData)
- {
- uint8_t KeyBValue[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
- uint8_t ucArrayID[4]; // 先后存放IC卡的类型和UID(IC卡序列号)
-
- // 寻卡
- while (PCD_Request(PICC_REQALL, ucArrayID) != PCD_OK)
- {
- printf("寻卡失败\n");
- delay_ms(1000);
- }
-
- printf("寻卡成功\n");
-
- // 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
- if (PCD_Anticoll(ucArrayID) == PCD_OK)
- {
- // 选中卡
- PCD_Select(ucArrayID);
-
- // 校验 B 密码
- if (PCD_AuthState(PICC_AUTHENT1B, addr, KeyBValue, ucArrayID) != PCD_OK)
- {
- printf("检验密码B失败\n");
- }
- // 读取数据块里的数据到 pData
- if (PCD_ReadBlock(addr, pData) != PCD_OK)
- {
- printf("读取数据块失败\n");
- return PCD_ERR;
- }
- printf("读取数据成功!\n");
- PCD_Halt();
- return PCD_OK;
- }
- return PCD_ERR;
- }
因本实验中的调试信息需要通过串口输出,Nucleo-446RE提供了利用USB的虚拟串口(串口2)。
这部分内容与基础篇007. 串行通信(一)--阻塞方式发送接收基本相同,只是把UART1换成UART2。以下仅提供代码截图,请大家参考前文实验。
在usart.c文件的user code 0 区域内:
输入如下内容:
要让RC522工作起来,需对RC522模块及Mifare的原理与协议有较深入的了解,请参考博文:
基础篇010.1 STM32驱动RC522 RFID模块之一:基础知识https://blog.csdn.net/qcmyqcmy/article/details/130876866
驱动程序设计时,必须的顺序是:寻卡--->防冲撞--->选卡--->开天线--->读/写卡。主函数的编写必须要按照这样的顺序,否者设备不会工作。
本实验是由门禁项目修改而来,为方便分析RC522读取到的数据,利用串口通信助手来检查是否读取到正确的数据。
修改Main.c函数头文件:
在下图红框区域添加代码:
- HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
- delay_ms(1000);
-
- // if(!EntranceGuard(readUid, RfidIndicator))
- if (!EntranceGuard(readUid, NULL))
- {
- printf("当前卡号:%x-%x-%x-%x\n", readUid[0], readUid[1], readUid[2], readUid[3]);
- if (!strncmp((char *)readUid, (char *)UID, 4))
- {
- // TODO
- // 插入比对卡号正确时的处理程序,如打开门禁
- printf("已认证的卡\n");
- DoorSensor();
- }
- else
- {
- // TODO
- // 插入比对卡号错误时的处理程序
- printf("未认证卡\n");
- }
- HAL_Delay(2000);
- }
关于工程配置方法,请参考本专栏博文中的第4部分“4.在MDK中自建驱动库的工程设置”:
基础篇005. 按键控制https://blog.csdn.net/qcmyqcmy/article/details/129407317此处不赘述,需要交流课上当面交流,网友可私信或留言。
如果你需要AC5编译器,请参考如下博文安装设置:
Keil MDK5.37以上版本自行添加AC5(ARMCC)编译器的方法_armcc下载
程序编译通过后,可将其下载到开发板进行验证:
实验需要使用串口调试助手验证。
打开串口调试助手,用RFID卡在读卡器上测试,实验结果如下:
文中代码已经调试通过,如果在你的板子上运行有问题,可以尝试调整SPI的速率。
本实验是STM32驱动RFID模块的第二部分,基础知识已在上一篇讲述:
关于RFID基础知识,请参考博文:
如果您需用软件模拟SPI方式实现RC522驱动,请参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。