当前位置:   article > 正文

基础篇010.2 STM32驱动RC522 RFID模块之二:STM32硬件SPI驱动RC522_stm32 rc522

stm32 rc522

目录

1. 实验硬件及原理图

1.1 RFID硬件

1.2 硬件原理图

2. 单片机与RFID硬件模块分析

3. 利用STM32CubeMX创建MDK工程

3.1 STM32CubeMX工程创建

3.2 配置调试方式

3.3 配置时钟电路

3.4 配置时钟

3.5 配置GPIO

3.6 配置SPI

3.7 配置串口

3.8 项目配置

4. MDK工程驱动代码调试

4.1 按键、LED程序

4.1.1 User.h文件的代码

4.1.2 User.c文件的代码

4.1.3 键盘程序key.c和key.h

4.2 RC522驱动程序

4.2.1 RC522.h文件

4.2.2 RC522.c文件

4.2.3 RFID.h文件

4.2.4 RFID.c文件

4.3 UART串口printf,scanf函数串口重定向

4.4 main()函数修改

4.5 工程配置

5.调试与验证

6.总结


 

篇前说明:本文代码已经调试通过,因采用硬件SPI,你可使用不同的STM32实现,实现时请修改为与你的STM32单片机对应的SPI接口。

本实验的RFID信息显示是通过串口实现的,关于串行通信请参考博文:

基础篇007. 串行通信(一)--阻塞方式发送接收

关于RFID基础知识,请参考博文:

基础篇010.1 STM32驱动RC522 RFID模块之一:基础知识https://blog.csdn.net/qcmyqcmy/article/details/130876866

如果您需用软件模拟SPI方式实现RC522驱动,请参考:

基础篇010.3 STM32驱动RC522 RFID模块之三:STM32软件模拟SPI驱动RC522https://blog.csdn.net/qcmyqcmy/article/details/131018784

1. 实验硬件及原理图

1.1 RFID硬件

本实验使用的RFID-RC522模块采用MFRC522芯片,SPI通讯方式,支持Mifarel S50、S70 、Pro、Desfire等类型的卡,附带的白卡和钥匙扣是S50卡,每张卡都有自己的标识(UID)。

c59de434dc31451ea043d195b3789cc3.png

 

图1

1.2 硬件原理图

(1)MC522原理图

 

61cf08d83f894757925929e0ff0a7c2f.png

 

图2

(2)STM32F446RE Nucleo-64开发板原理图

核心板原理图:

4c9a1dbd134a438b9324d6ddd0cf67c2.png

图3

核心板自带的ST-Link原理图:

ae45a4be23624489b00b4b9e2a7817bc.png

 

图4

核心板接口:

096426ed24564a2a8e0b9e30e6bbdccb.png

 

图5

底板原理图:

cbb92d69fc1742c9a56ca17b059770fd.png

图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            悬空

2. 单片机与RFID硬件模块分析

可实现各种不同主机接口的功能:

(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模块的通信。

80ad9a7c347846ea8bfa013b6f8f0f3a.png

 

STM32采用硬件SPI2时,使用的接口是PB10、PC2、PC1。

00449a7aece14fd1987af5e1ac0f139a.png

 

3. 利用STM32CubeMX创建MDK工程

3.1 STM32CubeMX工程创建

选择File下的New Project:

efc1b4021bdf41868a4cac2e97c0df9c.png

 

选择芯片类型(本文为STM32F446RET6),选择下边的item,然后Start Project:

ff5e5aefbbad481596e00618964cf060.png

 

3.2 配置调试方式

点击左侧的System Core下的SYS,将Debug设置为Serial Wire:

61f70de22a124a4a85b7ff342932f638.png

3.3 配置时钟电路

配置时钟:将RCC下的HSE设置为Crystal/Ceramic Resonator

1658592a216147a4b38011c73bcf61a6.png

3.4 配置时钟

Nucleo-446RE开发板:

94e11cb3a2c74e4e95940054983fd856.png

 

请结合开发版的硬件电路,从下面两种方式中二选一,选择第二种方式时,开发板中需要焊接相应元件(X3、C33、C34、R35、R37),或者你不能确定振荡电路,直接选第一种方式吧。

在STM32CubeMX中,做如下配置:

(1)采用内部8MHz时钟时选择Clock Configuration,做如下配置:

26a8e855a27d434d891e081655e5db0f.png

 

(2)使用外部时钟时,开发板需焊接的X3(8MHz)、C33、C34(20PF)、R36、R37),选择Clock Configuration,做如下配置:

8e97497f0e0147a2b13dd4b420d116e7.png

 

3.5 配置GPIO

结合开发版的硬件电路,进行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。

1e074043bd0144c584ab68c0d7546c15.png

 

各IO口设置后的参数放大图如下:

721e6b58fdb645adaecc5f3280daf7a6.png

 

3.6 配置SPI

核心板STM32采用内部时钟时,硬件SPI2的配置如下:

c2109740d31d48688ea7f6bef277cf2b.png

3.7 配置串口

实验调试中的系统运行信息,可以通过串口输出。根据开发板的硬件电路,选中串口2。

USART2参数配置:

在 Connectivity 中选择 USART2 设置,并选择 Asynchronous 异步通信。

波特率为 115200 Bits/s。传输数据长度为8Bit。奇偶检验 None,停止位 1 ,接收和发送都使能。

24e1f7f36e12435ba7b8e3901881a4f2.png

本文的串口采用阻塞方式收发信息,无需设置中断。

 

3.8 项目配置

在Project Manager下的Project中设置工程名称和工程路径,并选择编译软件。

fcde986398db4ff39610efb7e5497de6.png

 

代码生成设置:

b2179ac52cb044ee9591ff6e4f1a7827.png

 

在Code Generate中选择第二个,然后Generate Code,即生成代码:

11d52cb3e9a34512ba300ed0dfea21c5.png

可以打开MDK工程编辑了。

 

4. MDK工程驱动代码调试

4.1 按键、LED程序

在工程文件夹内部新建“BSP”文件夹:

037ed0cc231d47a39d3a35a7a9760c2e.png

 

在BSP文件夹内建立自定义驱动的新文件夹:

本部分的代码从项目基础篇005. 按键控制中修改而来,程序与硬件必须匹配,为培养同学们在不同STM32芯片间移植程序的灵活性,本专栏中的课程采用几种不同的STM32芯片,请结合硬件电路修改代码。

41eed4983a294dc4ba6f514bdd062a3d.png

 

Global文件夹内建立文件(红框内的文件用于一些全局变量函数,本文暂时不用)user.c和user.h:

2486c313916e40919cb895c97f9396da.png

 

4.1.1 User.h文件的代码

  1. #ifndef __USER_H
  2. #define __USER_H
  3. #ifdef __cplusplus
  4. extern "C" {
  5. #endif
  6. #include "main.h"
  7. //#define uchar unsigned char
  8. typedef unsigned char uchar;
  9. // ! --定义位带操作-->>>
  10. //位带操作,实现51类似的GPIO控制功能
  11. //具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).M4同M3类似,只是寄存器地址变了.
  12. //IO口操作宏定义
  13. #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
  14. #define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
  15. #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
  16. //IO口地址映射
  17. #define GPIOA_ODR_Addr (GPIOA_BASE+20) //0x40020014
  18. #define GPIOB_ODR_Addr (GPIOB_BASE+20) //0x40020414
  19. #define GPIOC_ODR_Addr (GPIOC_BASE+20) //0x40020814
  20. #define GPIOD_ODR_Addr (GPIOD_BASE+20) //0x40020C14
  21. #define GPIOE_ODR_Addr (GPIOE_BASE+20) //0x40021014
  22. #define GPIOF_ODR_Addr (GPIOF_BASE+20) //0x40021414
  23. #define GPIOG_ODR_Addr (GPIOG_BASE+20) //0x40021814
  24. #define GPIOH_ODR_Addr (GPIOH_BASE+20) //0x40021C14
  25. #define GPIOI_ODR_Addr (GPIOI_BASE+20) //0x40022014
  26. #define GPIOJ_ODR_ADDr (GPIOJ_BASE+20) //0x40022414
  27. #define GPIOK_ODR_ADDr (GPIOK_BASE+20) //0x40022814
  28. #define GPIOA_IDR_Addr (GPIOA_BASE+16) //0x40020010
  29. #define GPIOB_IDR_Addr (GPIOB_BASE+16) //0x40020410
  30. #define GPIOC_IDR_Addr (GPIOC_BASE+16) //0x40020810
  31. #define GPIOD_IDR_Addr (GPIOD_BASE+16) //0x40020C10
  32. #define GPIOE_IDR_Addr (GPIOE_BASE+16) //0x40021010
  33. #define GPIOF_IDR_Addr (GPIOF_BASE+16) //0x40021410
  34. #define GPIOG_IDR_Addr (GPIOG_BASE+16) //0x40021810
  35. #define GPIOH_IDR_Addr (GPIOH_BASE+16) //0x40021C10
  36. #define GPIOI_IDR_Addr (GPIOI_BASE+16) //0x40022010
  37. #define GPIOJ_IDR_Addr (GPIOJ_BASE+16) //0x40022410
  38. #define GPIOK_IDR_Addr (GPIOK_BASE+16) //0x40022810
  39. //IO口操作,只对单一的IO口!
  40. //确保n的值小于16!
  41. #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
  42. #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
  43. #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
  44. #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
  45. #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
  46. #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
  47. #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
  48. #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
  49. #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
  50. #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入
  51. #define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
  52. #define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
  53. #define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
  54. #define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
  55. #define PHout(n) BIT_ADDR(GPIOH_ODR_Addr,n) //输出
  56. #define PHin(n) BIT_ADDR(GPIOH_IDR_Addr,n) //输入
  57. #define PIout(n) BIT_ADDR(GPIOI_ODR_Addr,n) //输出
  58. #define PIin(n) BIT_ADDR(GPIOI_IDR_Addr,n) //输入
  59. #define PJout(n) BIT_ADDR(GPIOJ_ODR_Addr,n) //输出
  60. #define PJin(n) BIT_ADDR(GPIOJ_IDR_Addr,n) //输入
  61. #define PKout(n) BIT_ADDR(GPIOK_ODR_Addr,n) //输出
  62. #define PKin(n) BIT_ADDR(GPIOK_IDR_Addr,n) //输入
  63. // ! --汇编函数声明-->>>
  64. void WFI_SET(void); //执行WFI指令
  65. void INTX_DISABLE(void);//关闭所有中断
  66. void INTX_ENABLE(void); //开启所有中断
  67. void MSR_MSP(uint32_t addr); //设置堆栈地址
  68. // ! --延时函数声明-->>>
  69. void delay_init(uint8_t SYSCLK);
  70. void delay_ms(uint16_t nms);
  71. void delay_us(uint32_t nus);
  72. void delaySoft_ns(uint32_t t_ns); //ns级纯软件延时函数,不使用定时器,延时不准,需要调试
  73. void delaySoft_us(uint32_t t_us);
  74. #ifdef __cplusplus
  75. }
  76. #endif
  77. #endif /*__ USER_H__ */

4.1.2 User.c文件的代码

  1. #include "global/user.h"
  2. #ifdef USE_FULL_ASSERT
  3. //当编译提示出错的时候此函数用来报告错误的文件和所在行
  4. //file:指向源文件
  5. //line:指向在文件中的行数
  6. void assert_failed(uint8_t* file, uint32_t line)
  7. {
  8. while (1)
  9. {
  10. }
  11. }
  12. #endif
  13. // ! ------延时函数------->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  14. //使用SysTick的普通计数模式对延迟进行管理(支持ucosii/ucosiii)
  15. //包括delay_us,delay_ms
  16. //********************************************************************************
  17. static uint32_t fac_us=0; //us延时倍乘数
  18. /**
  19. * @DESCRIPTION: 初始化延迟函数
  20. * @INPUT ARGS: 系统时钟频率SYSCLK=主PLL时钟,即:SYSCLK= (外部晶振*PLLN)/(PLLM*PLLP)
  21. * @OUTPUT ARGS: none
  22. * @NOTE : SYSTICK的时钟固定为AHB时钟
  23. * @param {uint8_t} SYSCLK
  24. * @return {*}
  25. */
  26. void delay_init(uint8_t SYSCLK)
  27. {
  28. HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); //SysTick频率为HCLK
  29. fac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用
  30. }
  31. //延时nus
  32. //nus为要延时的us数.
  33. //nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)
  34. void delay_us(uint32_t nus)
  35. {
  36. uint32_t ticks;
  37. uint32_t told,tnow,tcnt=0;
  38. uint32_t reload=SysTick->LOAD; //LOAD的值
  39. ticks=nus*fac_us; //需要的节拍数
  40. told=SysTick->VAL; //刚进入时的计数器值
  41. while(1)
  42. {
  43. tnow=SysTick->VAL;
  44. if(tnow!=told)
  45. {
  46. if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
  47. else tcnt+=reload-tnow+told;
  48. told=tnow;
  49. if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
  50. }
  51. };
  52. }
  53. //延时nms
  54. //nms:要延时的ms数
  55. void delay_ms(uint16_t nms)
  56. {
  57. uint32_t i;
  58. for(i=0;i<nms;i++) delay_us(1000);
  59. }
  60. // ! ------软件延时函数------->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  61. /**
  62. * @DESCRIPTION: us级纯软件延时函数,不使用定时器
  63. * @INPUT ARGS : none
  64. * @OUTPUT ARGS: none
  65. * @RETURNS : none
  66. * @NOTES : F407内部时钟为168MHz时,每个指令周期约6ns。
  67. * @param {uint32_t} t_us
  68. */
  69. #define INS_CPU_CYCLES 8 //一条自增减指令所需的CPU周期数
  70. #define ADJ_CPU_CYCLES 62 //延时函数自身需要的CPU周期数(根据需要调整)
  71. void delaySoft_us(uint32_t t_us)
  72. {
  73. uint32_t count;
  74. count = (HAL_RCC_GetHCLKFreq()/1000000*t_us - ADJ_CPU_CYCLES)/INS_CPU_CYCLES;
  75. while(count--);
  76. }
  77. /**
  78. * @DESCRIPTION: ns级纯软件延时函数,不使用定时器,延时不准,需要调试
  79. * @INPUT ARGS : none
  80. * @OUTPUT ARGS: none
  81. * @RETURNS : none
  82. * @NOTES : F407内部时钟为168MHz时,每个指令周期约6ns。
  83. * @param {uint32_t} t_ns
  84. */
  85. void delaySoft_ns(uint32_t t_ns)
  86. {
  87. do
  88. {
  89. ;
  90. }
  91. while(t_ns--);
  92. }
  93. // ! ------汇编指令------->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  94. //THUMB指令不支持汇编内联
  95. //采用如下方法实现执行汇编指令WFI
  96. #if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) //AC6编译器
  97. //以下为汇编函数(AC6)
  98. void WFI_SET(void) //执行WFI指令
  99. {
  100. __ASM volatile("WFI");
  101. }
  102. void INTX_DISABLE(void) //关闭所有中断
  103. {
  104. __ASM volatile("CPSID I");
  105. __ASM volatile("BX LR");
  106. }
  107. void INTX_ENABLE(void) //开启所有中断
  108. {
  109. __ASM volatile("CPSIE I");
  110. __ASM volatile("BX LR");
  111. }
  112. void MSR_MSP(uint32_t addr) //设置堆栈地址
  113. {
  114. __ASM volatile("MSR MSP, r0");
  115. __ASM volatile("BX r14");
  116. }
  117. #elif defined ( __CC_ARM ) //AC5编译器
  118. __asm void WFI_SET(void)
  119. {
  120. WFI;
  121. }
  122. //关闭所有中断(但是不包括fault和NMI中断)
  123. __asm void INTX_DISABLE(void)
  124. {
  125. CPSID I
  126. BX LR
  127. }
  128. //开启所有中断
  129. __asm void INTX_ENABLE(void)
  130. {
  131. CPSIE I
  132. BX LR
  133. }
  134. //设置栈顶地址
  135. //addr:栈顶地址
  136. __asm void MSR_MSP(uint32_t addr)
  137. {
  138. MSR MSP, r0 //set Main Stack value
  139. BX r14
  140. }
  141. #endif

4.1.3 键盘程序key.c和key.h

Key文件夹截图 

f60277b032c349d3bbf8034e21e29efa.png

 

key.h文件的代码如下:

  1. #ifndef _KEY_H
  2. #define _KEY_H
  3. #include "main.h"
  4. #define KEY_ON 0
  5. #define KEY_OFF 1
  6. void key_Init(void);
  7. uint8_t Key_Scan(GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin);
  8. #endif

key.c文件的代码如下:

  1. #include "main.h"
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include "global/user.h"
  5. #include "key\key.h"
  6. /**
  7. * @DESCRIPTION: 初始化SPI端口
  8. * @INPUT ARGS : none
  9. * @OUTPUT ARGS: none
  10. * @RETURNS : none
  11. * @NOTES : none
  12. */
  13. void key_Init(void)
  14. {
  15. GPIO_InitTypeDef GPIO_InitStruct = {0};
  16. /* GPIO Ports Clock Enable */
  17. __HAL_RCC_GPIOC_CLK_ENABLE();
  18. __HAL_RCC_GPIOA_CLK_ENABLE();
  19. __HAL_RCC_GPIOB_CLK_ENABLE();
  20. /*Configure GPIO pins : PCPin PCPin */
  21. GPIO_InitStruct.Pin = KEY2_Pin;
  22. GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  23. GPIO_InitStruct.Pull = GPIO_PULLUP;
  24. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  25. /*Configure GPIO pin : PtPin */
  26. GPIO_InitStruct.Pin = KEY1_Pin;
  27. GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  28. GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  29. HAL_GPIO_Init(KEY1_GPIO_Port, &GPIO_InitStruct);
  30. /*Configure GPIO pins : PBPin PBPin */
  31. GPIO_InitStruct.Pin = KEY3_Pin|KEY4_Pin;
  32. GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  33. GPIO_InitStruct.Pull = GPIO_PULLUP;
  34. HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  35. }
  36. /**
  37. * @brief 检测是否有按键按下
  38. * @param GPIOx:具体的端口, x可以是(A...K)
  39. * @param GPIO_PIN:具体的端口位, 可以是GPIO_PIN_x(x可以是0...15)
  40. * @retval 按键的状态
  41. * @arg KEY_ON:按键按下(注意,key_up按键的电平定义时相反的)
  42. * @arg KEY_OFF:按键没按下
  43. */
  44. uint8_t Key_Scan(GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin)
  45. {
  46. /*检测是否有按键按下 */
  47. if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == KEY_ON )
  48. {
  49. /*等待按键释放 */
  50. while(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == KEY_ON);
  51. return KEY_ON;
  52. }
  53. else
  54. return KEY_OFF;
  55. }

4.2 RC522驱动程序

在工程的“BSP”文件夹内,分别建立如下文件:

5c9d8ee397b24840a8e6329b895336f7.png

 

其中,RC522.c及RC522.h为MFRC522芯片的通用驱动程序,这部分程序可以移植到任何单片机上。RFID.h和RFID.c是针对Mifare 1卡的应用代码。

4.2.1 RC522.h文件

  1. #ifndef _RC522_H
  2. #define _RC522_H
  3. #include "main.h"
  4. /***********************************************************************************
  5. * MFRC522驱动程序 *
  6. ************************************************************************************/
  7. /*MFRC522寄存器定义*/
  8. //PAGE0
  9. #define MFRC_RFU00 0x00
  10. #define MFRC_CommandReg 0x01
  11. #define MFRC_ComIEnReg 0x02
  12. #define MFRC_DivlEnReg 0x03
  13. #define MFRC_ComIrqReg 0x04
  14. #define MFRC_DivIrqReg 0x05
  15. #define MFRC_ErrorReg 0x06
  16. #define MFRC_Status1Reg 0x07
  17. #define MFRC_Status2Reg 0x08
  18. #define MFRC_FIFODataReg 0x09
  19. #define MFRC_FIFOLevelReg 0x0A
  20. #define MFRC_WaterLevelReg 0x0B
  21. #define MFRC_ControlReg 0x0C
  22. #define MFRC_BitFramingReg 0x0D
  23. #define MFRC_CollReg 0x0E
  24. #define MFRC_RFU0F 0x0F
  25. //PAGE1
  26. #define MFRC_RFU10 0x10
  27. #define MFRC_ModeReg 0x11
  28. #define MFRC_TxModeReg 0x12
  29. #define MFRC_RxModeReg 0x13
  30. #define MFRC_TxControlReg 0x14
  31. #define MFRC_TxAutoReg 0x15 //中文手册有误
  32. #define MFRC_TxSelReg 0x16
  33. #define MFRC_RxSelReg 0x17
  34. #define MFRC_RxThresholdReg 0x18
  35. #define MFRC_DemodReg 0x19
  36. #define MFRC_RFU1A 0x1A
  37. #define MFRC_RFU1B 0x1B
  38. #define MFRC_MifareReg 0x1C
  39. #define MFRC_RFU1D 0x1D
  40. #define MFRC_RFU1E 0x1E
  41. #define MFRC_SerialSpeedReg 0x1F
  42. //PAGE2
  43. #define MFRC_RFU20 0x20
  44. #define MFRC_CRCResultRegM 0x21
  45. #define MFRC_CRCResultRegL 0x22
  46. #define MFRC_RFU23 0x23
  47. #define MFRC_ModWidthReg 0x24
  48. #define MFRC_RFU25 0x25
  49. #define MFRC_RFCfgReg 0x26
  50. #define MFRC_GsNReg 0x27
  51. #define MFRC_CWGsCfgReg 0x28
  52. #define MFRC_ModGsCfgReg 0x29
  53. #define MFRC_TModeReg 0x2A
  54. #define MFRC_TPrescalerReg 0x2B
  55. #define MFRC_TReloadRegH 0x2C
  56. #define MFRC_TReloadRegL 0x2D
  57. #define MFRC_TCounterValueRegH 0x2E
  58. #define MFRC_TCounterValueRegL 0x2F
  59. //PAGE3
  60. #define MFRC_RFU30 0x30
  61. #define MFRC_TestSel1Reg 0x31
  62. #define MFRC_TestSel2Reg 0x32
  63. #define MFRC_TestPinEnReg 0x33
  64. #define MFRC_TestPinValueReg 0x34
  65. #define MFRC_TestBusReg 0x35
  66. #define MFRC_AutoTestReg 0x36
  67. #define MFRC_VersionReg 0x37
  68. #define MFRC_AnalogTestReg 0x38
  69. #define MFRC_TestDAC1Reg 0x39
  70. #define MFRC_TestDAC2Reg 0x3A
  71. #define MFRC_TestADCReg 0x3B
  72. #define MFRC_RFU3C 0x3C
  73. #define MFRC_RFU3D 0x3D
  74. #define MFRC_RFU3E 0x3E
  75. #define MFRC_RFU3F 0x3F
  76. /*MFRC522的FIFO长度定义*/
  77. #define MFRC_FIFO_LENGTH 64
  78. /*MFRC522传输的帧长定义*/
  79. #define MFRC_MAXRLEN 18
  80. /*MFRC522命令集,中文手册P59*/
  81. #define MFRC_IDLE 0x00 //取消当前命令的执行
  82. #define MFRC_CALCCRC 0x03 //激活CRC计算
  83. #define MFRC_TRANSMIT 0x04 //发送FIFO缓冲区内容
  84. #define MFRC_NOCMDCHANGE 0x07 //无命令改变
  85. #define MFRC_RECEIVE 0x08 //激活接收器接收数据
  86. #define MFRC_TRANSCEIVE 0x0C //发送并接收数据
  87. #define MFRC_AUTHENT 0x0E //执行Mifare认证(验证密钥)
  88. #define MFRC_RESETPHASE 0x0F //复位MFRC522
  89. /*MFRC522通讯时返回的错误代码*/
  90. #define MFRC_OK (char)0
  91. #define MFRC_NOTAGERR (char)(-1)
  92. #define MFRC_ERR (char)(-2)
  93. /*MFRC522函数声明*/
  94. void MFRC_Init(void);
  95. void MFRC_WriteReg(uint8_t addr, uint8_t data);
  96. uint8_t MFRC_ReadReg(uint8_t addr);
  97. void MFRC_SetBitMask(uint8_t addr, uint8_t mask);
  98. void MFRC_ClrBitMask(uint8_t addr, uint8_t mask);
  99. void MFRC_CalulateCRC(uint8_t *pInData, uint8_t len, uint8_t *pOutData);
  100. char MFRC_CmdFrame(uint8_t cmd, uint8_t *pInData, uint8_t InLenByte, uint8_t *pOutData, uint16_t *pOutLenBit);
  101. /***********************************************************************************
  102. * MFRC552与MF1卡通讯接口程序 *
  103. ************************************************************************************/
  104. /*Mifare1卡片命令字*/
  105. #define PICC_REQIDL 0x26 //寻天线区内未进入休眠状态的卡
  106. #define PICC_REQALL 0x52 //寻天线区内全部卡
  107. #define PICC_ANTICOLL1 0x93 //防冲撞
  108. #define PICC_ANTICOLL2 0x95 //防冲撞
  109. #define PICC_AUTHENT1A 0x60 //验证A密钥
  110. #define PICC_AUTHENT1B 0x61 //验证B密钥
  111. #define PICC_READ 0x30 //读块
  112. #define PICC_WRITE 0xA0 //写块
  113. #define PICC_DECREMENT 0xC0 //减值(扣除)
  114. #define PICC_INCREMENT 0xC1 //增值(充值)
  115. #define PICC_TRANSFER 0xB0 //转存(传送)
  116. #define PICC_RESTORE 0xC2 //恢复(重储)
  117. #define PICC_HALT 0x50 //休眠
  118. /*PCD通讯时返回的错误代码*/
  119. #define PCD_OK (char)0 //成功
  120. #define PCD_NOTAGERR (char)(-1) //无卡
  121. #define PCD_ERR (char)(-2) //出错
  122. /*PCD函数声明*/
  123. void PCD_Init(void);
  124. void PCD_Reset(void);
  125. void PCD_AntennaOn(void);
  126. void PCD_AntennaOff(void);
  127. char PCD_Request(uint8_t RequestMode, uint8_t *pCardType); //寻卡,并返回卡的类型
  128. char PCD_Anticoll(uint8_t *pSnr); //防冲突,返回卡号
  129. char PCD_Select(uint8_t *pSnr); //选卡
  130. char PCD_AuthState(uint8_t AuthMode, uint8_t BlockAddr, uint8_t *pKey, uint8_t *pSnr); //验证密码(密码A和密码B)
  131. char PCD_WriteBlock(uint8_t BlockAddr, uint8_t *pData); //写数据
  132. char PCD_ReadBlock(uint8_t BlockAddr, uint8_t *pData); //读数据
  133. char PCD_Value(uint8_t mode, uint8_t BlockAddr, uint8_t *pValue);
  134. char PCD_BakValue(uint8_t sourceBlockAddr, uint8_t goalBlockAddr);
  135. char PCD_Halt(void);
  136. void StartIDcardTask(void const * argument);
  137. #endif

4.2.2 RC522.c文件

  1. /**
  2. MFRC522-AN模块采用Philips MFRC522芯片设计读卡电路,使用方便,成本低廉,适用
  3. 于设备开发、读卡器开发等高级应用的用户、需要进行射频卡终端设计/生产的用户。
  4. 模块参数:
  5. ①工作电压:3.3v
  6. ②工作频率:13.56MHz
  7. ③支持卡类型:mifare1 s50、mifare1s70、 mifareUltraLight、mifare Pro, mifare Desfire
  8. ④通信方式:SPI协议
  9. ⑤环境工作温度:-20°C——80°C
  10. M1卡分为16个扇区,每个扇区由四个块(块0、块1、块2、块3)组成
  11. 将16个扇区的64个块按绝对地址编号为:0~63
  12. 第0个扇区的块0(即绝对地址0块),用于存放厂商代码,已经固化不可更改
  13. 每个扇区的块0、块1、块2为数据块,可用于存放数据
  14. 每个扇区的块3为控制块(绝对地址为:块3、块7、块11.....)包括密码A,存取控制、密码B等
  15. 1、CPU选择
  16. STM32F446RE,内部时钟180MHz
  17. 2、STM32CubeMX 定义任意两个引脚,作为复位脚和片选脚,并对引脚作出如下配置:
  18. GPlO output level --High
  19. GPIO mode --Output Push Pull
  20. GPIO Pull-up/Pull-down --No pull-up and no pull-down
  21. Maximum output speed --LOW
  22. User label --RC522_RST/RC522_SDA
  23. ---------------------------------------------------------
  24. 开启SPI功能,模式选择-->Full-Duplex Master(全双工),其他配置如下:
  25. Basic Parameters
  26.   Frame format-->Motorola
  27.   Data size -->8 Bits
  28.   First bit -->MSB First
  29. Clock Parameters
  30. Prescaler(for Baud Rate)-->8
  31. Baud rate -->5.625MBits/s【RC522中的SPI最高速率为10MHz/s】
  32. Clock Polarity(CPOL) -->LOW
  33. Clock Phase(CPHA) -->1 Edge
  34. Advanced Parameters
  35. CRC Calculation -->Disabled
  36. NSS Signal Type -->Software
  37. 3、接线方式:
  38. SPI_MISO(MUC)--> MISO(器件)
  39. SPI_MOSI(MUC)--> MOSI(器件)
  40. 其他引脚一一对应
  41. //! Nucleo-F446RE接口
  42. //SPI2_SCK PB10---(接Arduino D6)
  43. //SPI2_MISO PC2----(接CN7左下2)
  44. //SPI2_MOSI PC1----(接Arduino A4)
  45. //RCC522_RST(CE) PC7----(接Arduino D9)
  46. //RCC522_NSS(SDA) PB6----(接Arduino D10)
  47. //RCC522_IRQ 悬空
  48. 4、SPI模式说明:SPI总线传输的四种模式:
  49. * SPI传输的模式由CPOL:clock polarity 时钟的极性,和CPHA:clock phase 时钟的相位控制。
  50. * RC522采用的是CPOL=0,CPHA=0的工作模式。在CubeMX中,SPI_CPHA设置为1Edge。
  51. * ┌─────────┬───────┬───────┬─────────────────┬─────────────────┐
  52. * │ SPI模式 │ CPOL │ CPHA │ 空闲时间SCLK状态 │ 采样时刻 │
  53. * │ 0 │ 0 │ 0 │ 低电平 │ 奇数边沿(上升沿) │
  54. * │ 1 │ 0 │ 1 │ 低电平 │ 偶数边沿(下降沿) │
  55. * │ 2 │ 1 │ 0 │ 高电平 │ 奇数边沿(下降沿) │
  56. * │ 3 │ 1 │ 1 │ 高电平 │ 偶数边沿(上升沿) │
  57. * └─────────┴───────┴───────┴─────────────────┴─────────────────┘
  58. 5、应用函数
  59. MFRC_Init();//初始化
  60. PCD_Reset();//器件复位
  61. PCD_Request(PICC_REQALL, RxBuffer);//返回值为0,代表寻卡成功;并把卡类型存入RxBuffer中
  62. PCD_Anticoll(RxBuffer); //把(十六进制)的4个字节卡号存储在数组RxBuffer中
  63. ***********************************/
  64. // #define RC522_SDA GPIO_Port GPIOB
  65. // #define RC522_SDA Pin GPIO_PIN_6 //cs、nss、SDA指同一个口
  66. // #define RC522_RST GPIO_Port GPIOC
  67. // #define RC522_RST Pin GPIO_PIN_7
  68. #include "main.h"
  69. #include <stdio.h>
  70. #include <string.h>
  71. #include "global/user.h"
  72. #include "usart.h"
  73. #include "RC522\RC522.h"
  74. extern SPI_HandleTypeDef hspi2;
  75. // #define osDelay HAL_Delay
  76. #define osDelay delay_ms
  77. #define RS522_RST(N) HAL_GPIO_WritePin(RC522_RST_GPIO_Port, RC522_RST_Pin, N == 1 ? GPIO_PIN_SET : GPIO_PIN_RESET)
  78. #define RC522_SDA(N) HAL_GPIO_WritePin(RC522_SDA_GPIO_Port, RC522_SDA_Pin, N == 1 ? GPIO_PIN_SET : GPIO_PIN_RESET)
  79. /**************************************************************************************
  80. * 函数名称:MFRC_Init
  81. * 功能描述:MFRC初始化
  82. * 入口参数:无
  83. * 出口参数:无
  84. * 返 回 值:无
  85. * 说 明:MFRC的SPI接口速率为0~10Mbps
  86. ***************************************************************************************/
  87. void MFRC_Init(void)
  88. {
  89. RC522_SDA(1);
  90. RS522_RST(1);
  91. }
  92. /**************************************************************************************
  93. * 函数名称: SPI_RW_Byte
  94. * 功能描述: 模拟SPI读写一个字节
  95. * 入口参数: -byte:要发送的数据
  96. * 出口参数: -byte:接收到的数据
  97. ***************************************************************************************/
  98. static uint8_t ret; // 这些函数是HAL与标准库不同的地方【读写函数】
  99. uint8_t SPI2_RW_Byte(uint8_t byte)
  100. {
  101. //硬件SPI
  102. HAL_SPI_TransmitReceive(&hspi2, &byte, &ret, 1, 10); // 把byte 写入,并读出一个值,把它存入ret
  103. return ret; // 入口是byte 的地址,读取时用的也是ret地址,一次只写入一个值10
  104. //下面是模拟SPI
  105. // if (byte == 0x00) // 读数据时
  106. // {
  107. // ret = RC522_SPI_ReadByte();
  108. // }
  109. // RC522_SPI_SendByte(byte);
  110. // return ret;
  111. }
  112. /**************************************************************************************
  113. * 函数名称:MFRC_WriteReg
  114. * 功能描述:写一个寄存器
  115. * 入口参数:-addr:待写的寄存器地址
  116. * -data:待写的寄存器数据
  117. * 出口参数:无
  118. * 返 回 值:无
  119. * 说 明:无
  120. ***************************************************************************************/
  121. void MFRC_WriteReg(uint8_t addr, uint8_t data)
  122. {
  123. uint8_t AddrByte;
  124. AddrByte = (addr << 1) & 0x7E; // 求出地址字节
  125. RC522_SDA(0); // NSS拉低
  126. SPI2_RW_Byte(AddrByte); // 写地址字节
  127. SPI2_RW_Byte(data); // 写数据
  128. RC522_SDA(1); // NSS拉高
  129. }
  130. /**************************************************************************************
  131. * 函数名称:MFRC_ReadReg
  132. * 功能描述:读一个寄存器
  133. * 入口参数:-addr:待读的寄存器地址
  134. * 出口参数:无
  135. * 返 回 值:-data:读到寄存器的数据
  136. * 说 明:无
  137. ***************************************************************************************/
  138. uint8_t MFRC_ReadReg(uint8_t addr)
  139. {
  140. uint8_t AddrByte, data;
  141. AddrByte = ((addr << 1) & 0x7E) | 0x80; // 求出地址字节
  142. RC522_SDA(0); // NSS拉低
  143. SPI2_RW_Byte(AddrByte); // 写地址字节
  144. data = SPI2_RW_Byte(0x00); // 读数据
  145. RC522_SDA(1); // NSS拉高
  146. return data;
  147. }
  148. /**************************************************************************************
  149. * 函数名称:MFRC_SetBitMask
  150. * 功能描述:设置寄存器的位
  151. * 入口参数:-addr:待设置的寄存器地址
  152. * -mask:待设置寄存器的位(可同时设置多个bit)
  153. * 出口参数:无
  154. * 返 回 值:无
  155. * 说 明:无
  156. ***************************************************************************************/
  157. void MFRC_SetBitMask(uint8_t addr, uint8_t mask)
  158. {
  159. uint8_t temp;
  160. temp = MFRC_ReadReg(addr); // 先读回寄存器的值
  161. MFRC_WriteReg(addr, temp | mask); // 处理过的数据再写入寄存器
  162. }
  163. /**************************************************************************************
  164. * 函数名称:MFRC_ClrBitMask
  165. * 功能描述:清除寄存器的位
  166. * 入口参数:-addr:待清除的寄存器地址
  167. * -mask:待清除寄存器的位(可同时清除多个bit)
  168. * 出口参数:无
  169. * 返 回 值:无
  170. * 说 明:无
  171. ***************************************************************************************/
  172. void MFRC_ClrBitMask(uint8_t addr, uint8_t mask)
  173. {
  174. uint8_t temp;
  175. temp = MFRC_ReadReg(addr); // 先读回寄存器的值
  176. MFRC_WriteReg(addr, temp & ~mask); // 处理过的数据再写入寄存器
  177. }
  178. /**************************************************************************************
  179. * 函数名称:MFRC_CalulateCRC
  180. * 功能描述:用MFRC计算CRC结果
  181. * 入口参数:-pInData:带进行CRC计算的数据
  182. * -len:带进行CRC计算的数据长度
  183. * -pOutData:CRC计算结果
  184. * 出口参数:-pOutData:CRC计算结果
  185. * 返 回 值:无
  186. * 说 明:无
  187. ***************************************************************************************/
  188. void MFRC_CalulateCRC(uint8_t *pInData, uint8_t len, uint8_t *pOutData)
  189. {
  190. // 0xc1 1 2 pInData[2]
  191. uint8_t temp;
  192. uint32_t i;
  193. MFRC_ClrBitMask(MFRC_DivIrqReg, 0x04); // 使能CRC中断
  194. MFRC_WriteReg(MFRC_CommandReg, MFRC_IDLE); // 取消当前命令的执行
  195. MFRC_SetBitMask(MFRC_FIFOLevelReg, 0x80); // 清除FIFO及其标志位
  196. for (i = 0; i < len; i++) // 将待CRC计算的数据写入FIFO
  197. {
  198. MFRC_WriteReg(MFRC_FIFODataReg, *(pInData + i));
  199. }
  200. MFRC_WriteReg(MFRC_CommandReg, MFRC_CALCCRC); // 执行CRC计算
  201. i = 100000;
  202. do
  203. {
  204. temp = MFRC_ReadReg(MFRC_DivIrqReg); // 读取DivIrqReg寄存器的值
  205. i--;
  206. } while ((i != 0) && !(temp & 0x04)); // 等待CRC计算完成
  207. pOutData[0] = MFRC_ReadReg(MFRC_CRCResultRegL); // 读取CRC计算结果
  208. pOutData[1] = MFRC_ReadReg(MFRC_CRCResultRegM);
  209. }
  210. /**************************************************************************************
  211. * 函数名称:MFRC_CmdFrame
  212. * 功能描述:MFRC522和ISO14443A卡通讯的命令帧函数
  213. * 入口参数:-cmd:MFRC522命令字
  214. * -pIndata:MFRC522发送给MF1卡的数据的缓冲区首地址
  215. * -InLenByte:发送数据的字节长度
  216. * -pOutdata:用于接收MF1卡片返回数据的缓冲区首地址
  217. * -pOutLenBit:MF1卡返回数据的位长度
  218. * 出口参数:-pOutdata:用于接收MF1卡片返回数据的缓冲区首地址
  219. * -pOutLenBit:用于MF1卡返回数据位长度的首地址
  220. * 返 回 值:-status:错误代码(MFRC_OK、MFRC_NOTAGERR、MFRC_ERR)
  221. * 说 明:无
  222. ***************************************************************************************/
  223. char MFRC_CmdFrame(uint8_t cmd, uint8_t *pInData, uint8_t InLenByte, uint8_t *pOutData, uint16_t *pOutLenBit)
  224. {
  225. uint8_t lastBits;
  226. uint8_t n;
  227. uint32_t i;
  228. char status = MFRC_ERR;
  229. uint8_t irqEn = 0x00;
  230. uint8_t waitFor = 0x00;
  231. /*根据命令设置标志位*/
  232. switch (cmd)
  233. {
  234. case MFRC_AUTHENT: // Mifare认证
  235. irqEn = 0x12;
  236. waitFor = 0x10; // idleIRq中断标志
  237. break;
  238. case MFRC_TRANSCEIVE: // 发送并接收数据
  239. irqEn = 0x77;
  240. waitFor = 0x30; // RxIRq和idleIRq中断标志
  241. break;
  242. }
  243. /*发送命令帧前准备*/
  244. MFRC_WriteReg(MFRC_ComIEnReg, irqEn | 0x80); // 开中断
  245. MFRC_ClrBitMask(MFRC_ComIrqReg, 0x80); // 清除中断标志位SET1
  246. MFRC_WriteReg(MFRC_CommandReg, MFRC_IDLE); // 取消当前命令的执行
  247. MFRC_SetBitMask(MFRC_FIFOLevelReg, 0x80); // 清除FIFO缓冲区及其标志位
  248. /*发送命令帧*/
  249. for (i = 0; i < InLenByte; i++) // 写入命令参数
  250. {
  251. MFRC_WriteReg(MFRC_FIFODataReg, pInData[i]); // 写数据进 FIFODataReg
  252. }
  253. MFRC_WriteReg(MFRC_CommandReg, cmd); // 执行命令
  254. if (cmd == MFRC_TRANSCEIVE)
  255. {
  256. MFRC_SetBitMask(MFRC_BitFramingReg, 0x80); // 启动发送
  257. }
  258. i = 300000; // 根据时钟频率调整,操作M1卡最大等待时间25ms
  259. do // 认证 与寻卡等待时间
  260. {
  261. n = MFRC_ReadReg(MFRC_ComIrqReg); // 查询事件中断
  262. i--;
  263. } while ((i != 0) && !(n & 0x01) && !(n & waitFor)); // 等待命令完成
  264. MFRC_ClrBitMask(MFRC_BitFramingReg, 0x80); // 停止发送
  265. /*处理接收的数据*/
  266. if (i != 0)
  267. {
  268. // 读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
  269. if (!(MFRC_ReadReg(MFRC_ErrorReg) & 0x1B))
  270. {
  271. status = MFRC_OK;
  272. if (n & irqEn & 0x01) // 是否发生定时器中断
  273. {
  274. status = MFRC_NOTAGERR;
  275. }
  276. if (cmd == MFRC_TRANSCEIVE)
  277. {
  278. // 读FIFO中保存的字节数
  279. n = MFRC_ReadReg(MFRC_FIFOLevelReg);
  280. lastBits = MFRC_ReadReg(MFRC_ControlReg) & 0x07; // 最后接收到得字节的有效位数
  281. if (lastBits)
  282. {
  283. *pOutLenBit = (n - 1) * 8 + lastBits; // N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
  284. }
  285. else
  286. {
  287. *pOutLenBit = n * 8; // 最后接收到的字节整个字节有效
  288. }
  289. if (n == 0)
  290. {
  291. n = 1;
  292. }
  293. if (n > MFRC_MAXRLEN)
  294. {
  295. n = MFRC_MAXRLEN;
  296. }
  297. for (i = 0; i < n; i++)
  298. {
  299. pOutData[i] = MFRC_ReadReg(MFRC_FIFODataReg);
  300. }
  301. }
  302. }
  303. else
  304. {
  305. status = MFRC_ERR;
  306. }
  307. }
  308. MFRC_SetBitMask(MFRC_ControlReg, 0x80); // 停止定时器运行
  309. MFRC_WriteReg(MFRC_CommandReg, MFRC_IDLE); // 取消当前命令的执行
  310. return status;
  311. }
  312. /**************************************************************************************
  313. * 函数名称:PCD_Reset
  314. * 功能描述:PCD复位
  315. * 入口参数:无
  316. * 出口参数:无
  317. * 返 回 值:无
  318. * 说 明:无
  319. ***************************************************************************************/
  320. void PCD_Reset(void)
  321. {
  322. /*硬复位*/
  323. RS522_RST(1); // 用到复位引脚
  324. osDelay(2);
  325. RS522_RST(0);
  326. osDelay(2);
  327. RS522_RST(1);
  328. osDelay(2);
  329. /*软复位*/
  330. MFRC_WriteReg(MFRC_CommandReg, MFRC_RESETPHASE);
  331. osDelay(2);
  332. /*复位后的初始化配置*/
  333. MFRC_WriteReg(MFRC_ModeReg, 0x3D); // CRC初始值0x6363
  334. MFRC_WriteReg(MFRC_TReloadRegL, 30); // 定时器重装值
  335. MFRC_WriteReg(MFRC_TReloadRegH, 0);
  336. MFRC_WriteReg(MFRC_TModeReg, 0x8D); // 定义内部定时器的设置
  337. MFRC_WriteReg(MFRC_TPrescalerReg, 0x3E); // 设置定时器预分频值
  338. MFRC_WriteReg(MFRC_TxAutoReg, 0x40); // 调制发送信号为100%ASK
  339. PCD_AntennaOff(); // 关天线
  340. osDelay(2);
  341. PCD_AntennaOn(); // 开天线
  342. }
  343. /**************************************************************************************
  344. * 函数名称:PCD_AntennaOn
  345. * 功能描述:开启天线,使能PCD发送能量载波信号
  346. * 入口参数:无
  347. * 出口参数:无
  348. * 返 回 值:无
  349. * 说 明:每次开启或关闭天线之间应至少有1ms的间隔
  350. ***************************************************************************************/
  351. void PCD_AntennaOn(void)
  352. {
  353. uint8_t temp;
  354. temp = MFRC_ReadReg(MFRC_TxControlReg);
  355. if (!(temp & 0x03))
  356. {
  357. MFRC_SetBitMask(MFRC_TxControlReg, 0x03);
  358. }
  359. }
  360. /**************************************************************************************
  361. * 函数名称:PCD_AntennaOff
  362. * 功能描述:关闭天线,失能PCD发送能量载波信号
  363. * 入口参数:无
  364. * 出口参数:无
  365. * 返 回 值:无
  366. * 说 明:每次开启或关闭天线之间应至少有1ms的间隔
  367. ***************************************************************************************/
  368. void PCD_AntennaOff(void)
  369. {
  370. MFRC_ClrBitMask(MFRC_TxControlReg, 0x03);
  371. }
  372. /***************************************************************************************
  373. * 函数名称:PCD_Init
  374. * 功能描述:读写器初始化
  375. * 入口参数:无
  376. * 出口参数:无
  377. * 返 回 值:无
  378. * 说 明:无
  379. ***************************************************************************************/
  380. void PCD_Init(void)
  381. {
  382. MFRC_Init(); // MFRC管脚配置
  383. PCD_Reset(); // PCD复位 并初始化配置
  384. PCD_AntennaOff(); // 关闭天线
  385. PCD_AntennaOn(); // 开启天线
  386. }
  387. /***************************************************************************************
  388. * 函数名称:PCD_Request
  389. * 功能描述:寻卡
  390. * 入口参数: -RequestMode:寻卡方式
  391. * PICC_REQIDL:寻天线区内未进入休眠状态
  392. * PICC_REQALL:寻天线区内全部卡
  393. * -pCardType: 用于保存卡片类型
  394. * 出口参数:-pCardType:卡片类型
  395. * 0x4400:Mifare_UltraLight
  396. * 0x0400:Mifare_One(S50)
  397. * 0x0200:Mifare_One(S70)
  398. * 0x0800:Mifare_Pro(X)
  399. * 0x4403:Mifare_DESFire
  400. * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
  401. * 说 明:无
  402. ***************************************************************************************/
  403. char PCD_Request(uint8_t RequestMode, uint8_t *pCardType)
  404. {
  405. int status;
  406. uint16_t unLen;
  407. uint8_t CmdFrameBuf[MFRC_MAXRLEN];
  408. MFRC_ClrBitMask(MFRC_Status2Reg, 0x08); // 关内部温度传感器
  409. MFRC_WriteReg(MFRC_BitFramingReg, 0x07); // 存储模式,发送模式,是否启动发送等
  410. MFRC_SetBitMask(MFRC_TxControlReg, 0x03); // 配置调制信号13.56MHZ
  411. CmdFrameBuf[0] = RequestMode; // 存入 卡片命令字
  412. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 1, CmdFrameBuf, &unLen);
  413. if ((status == PCD_OK) && (unLen == 0x10)) // 寻卡成功返回卡类型
  414. {
  415. *pCardType = CmdFrameBuf[0];
  416. *(pCardType + 1) = CmdFrameBuf[1];
  417. }
  418. return status;
  419. }
  420. /***************************************************************************************
  421. * 函数名称:PCD_Anticoll
  422. * 功能描述:防冲撞,获取卡号
  423. * 入口参数:-pSnr:用于保存卡片序列号,4字节
  424. * 出口参数:-pSnr:卡片序列号,4字节
  425. * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
  426. * 说 明:无
  427. ***************************************************************************************/
  428. char PCD_Anticoll(uint8_t *pSnr)
  429. {
  430. char status;
  431. uint8_t i, snr_check = 0;
  432. uint16_t unLen;
  433. uint8_t CmdFrameBuf[MFRC_MAXRLEN];
  434. MFRC_ClrBitMask(MFRC_Status2Reg, 0x08); // 清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
  435. MFRC_WriteReg(MFRC_BitFramingReg, 0x00); // 清理寄存器 停止收发
  436. MFRC_ClrBitMask(MFRC_CollReg, 0x80); // 清ValuesAfterColl所有接收的位在冲突后被清除
  437. CmdFrameBuf[0] = PICC_ANTICOLL1; // 卡片防冲突命令
  438. CmdFrameBuf[1] = 0x20;
  439. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 2, CmdFrameBuf, &unLen); // 与卡片通信
  440. if (status == PCD_OK) // 通信成功
  441. {
  442. for (i = 0; i < 4; i++)
  443. {
  444. *(pSnr + i) = CmdFrameBuf[i]; // 读出UID
  445. snr_check ^= CmdFrameBuf[i];
  446. }
  447. if (snr_check != CmdFrameBuf[i])
  448. {
  449. status = PCD_ERR;
  450. }
  451. }
  452. MFRC_SetBitMask(MFRC_CollReg, 0x80);
  453. return status;
  454. }
  455. /***************************************************************************************
  456. * 函数名称:PCD_Select
  457. * 功能描述:选定卡片
  458. * 入口参数:-pSnr:卡片序列号,4字节
  459. * 出口参数:无
  460. * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
  461. * 说 明:无
  462. ***************************************************************************************/
  463. char PCD_Select(uint8_t *pSnr)
  464. {
  465. char status;
  466. uint8_t i;
  467. uint16_t unLen;
  468. uint8_t CmdFrameBuf[MFRC_MAXRLEN];
  469. CmdFrameBuf[0] = PICC_ANTICOLL1;
  470. CmdFrameBuf[1] = 0x70;
  471. CmdFrameBuf[6] = 0;
  472. for (i = 0; i < 4; i++)
  473. {
  474. CmdFrameBuf[i + 2] = *(pSnr + i);
  475. CmdFrameBuf[6] ^= *(pSnr + i);
  476. }
  477. MFRC_CalulateCRC(CmdFrameBuf, 7, &CmdFrameBuf[7]);
  478. MFRC_ClrBitMask(MFRC_Status2Reg, 0x08);
  479. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 9, CmdFrameBuf, &unLen);
  480. if ((status == PCD_OK) && (unLen == 0x18))
  481. {
  482. status = PCD_OK;
  483. }
  484. else
  485. {
  486. status = PCD_ERR;
  487. }
  488. return status;
  489. }
  490. /***************************************************************************************
  491. * 函数名称:PCD_AuthState
  492. * 功能描述:验证卡片密码
  493. * 入口参数:-AuthMode:验证模式
  494. * PICC_AUTHENT1A:验证A密码
  495. * PICC_AUTHENT1B:验证B密码
  496. * -BlockAddr:块地址(0~63)
  497. * -pKey:密码
  498. * -pSnr:卡片序列号,4字节
  499. * 出口参数:无
  500. * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
  501. * 说 明:验证密码时,以扇区为单位,BlockAddr参数可以是同一个扇区的任意块
  502. ***************************************************************************************/
  503. char PCD_AuthState(uint8_t AuthMode, uint8_t BlockAddr, uint8_t *pKey, uint8_t *pSnr)
  504. {
  505. char status;
  506. uint16_t unLen;
  507. uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
  508. CmdFrameBuf[0] = AuthMode;
  509. CmdFrameBuf[1] = BlockAddr;
  510. for (i = 0; i < 6; i++)
  511. {
  512. CmdFrameBuf[i + 2] = *(pKey + i);
  513. }
  514. for (i = 0; i < 4; i++)
  515. {
  516. CmdFrameBuf[i + 8] = *(pSnr + i);
  517. }
  518. status = MFRC_CmdFrame(MFRC_AUTHENT, CmdFrameBuf, 12, CmdFrameBuf, &unLen);
  519. if ((status != PCD_OK) || (!(MFRC_ReadReg(MFRC_Status2Reg) & 0x08)))
  520. {
  521. status = PCD_ERR;
  522. }
  523. return status;
  524. }
  525. /***************************************************************************************
  526. * 函数名称:PCD_WriteBlock
  527. * 功能描述:写MF1卡数据块
  528. * 入口参数:-BlockAddr:块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
  529. * -pData: 用于保存待写入的数据,16字节
  530. * 出口参数:无
  531. * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
  532. * 说 明:无
  533. ***************************************************************************************/
  534. char PCD_WriteBlock(uint8_t BlockAddr, uint8_t *pData)
  535. {
  536. char status;
  537. uint16_t unLen;
  538. uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
  539. CmdFrameBuf[0] = PICC_WRITE;
  540. CmdFrameBuf[1] = BlockAddr;
  541. MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
  542. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
  543. if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
  544. {
  545. status = PCD_ERR;
  546. }
  547. if (status == PCD_OK)
  548. {
  549. for (i = 0; i < 16; i++)
  550. {
  551. CmdFrameBuf[i] = *(pData + i);
  552. }
  553. MFRC_CalulateCRC(CmdFrameBuf, 16, &CmdFrameBuf[16]);
  554. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 18, CmdFrameBuf, &unLen);
  555. if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
  556. {
  557. status = PCD_ERR;
  558. }
  559. }
  560. return status;
  561. }
  562. /***************************************************************************************
  563. * 函数名称:PCD_ReadBlock
  564. * 功能描述:读MF1卡数据块
  565. * 入口参数:-BlockAddr:块地址
  566. * -pData: 用于保存读出的数据,16字节
  567. * 出口参数:-pData: 用于保存读出的数据,16字节
  568. * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
  569. * 说 明:无
  570. ***************************************************************************************/
  571. char PCD_ReadBlock(uint8_t BlockAddr, uint8_t *pData)
  572. {
  573. char status;
  574. uint16_t unLen;
  575. uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
  576. CmdFrameBuf[0] = PICC_READ;
  577. CmdFrameBuf[1] = BlockAddr;
  578. MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
  579. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
  580. if ((status == PCD_OK) && (unLen == 0x90))
  581. {
  582. for (i = 0; i < 16; i++)
  583. {
  584. *(pData + i) = CmdFrameBuf[i];
  585. }
  586. }
  587. else
  588. {
  589. status = PCD_ERR;
  590. }
  591. return status;
  592. }
  593. /***************************************************************************************
  594. * 函数名称:PCD_Value
  595. * 功能描述:对MF1卡数据块增减值操作
  596. * 入口参数:
  597. * -BlockAddr:块地址
  598. * -pValue:四字节增值的值,低位在前
  599. * -mode:数值块操作模式
  600. * PICC_INCREMENT:增值
  601. * PICC_DECREMENT:减值
  602. * 出口参数:无
  603. * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
  604. * 说 明:无
  605. ***************************************************************************************/
  606. char PCD_Value(uint8_t mode, uint8_t BlockAddr, uint8_t *pValue)
  607. {
  608. // 0XC1 1 Increment[4]={0x03, 0x01, 0x01, 0x01};
  609. char status;
  610. uint16_t unLen;
  611. uint8_t i, CmdFrameBuf[MFRC_MAXRLEN];
  612. CmdFrameBuf[0] = mode;
  613. CmdFrameBuf[1] = BlockAddr;
  614. MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
  615. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
  616. if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
  617. {
  618. status = PCD_ERR;
  619. }
  620. if (status == PCD_OK)
  621. {
  622. for (i = 0; i < 16; i++)
  623. {
  624. CmdFrameBuf[i] = *(pValue + i);
  625. }
  626. MFRC_CalulateCRC(CmdFrameBuf, 4, &CmdFrameBuf[4]);
  627. unLen = 0;
  628. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 6, CmdFrameBuf, &unLen);
  629. if (status != PCD_ERR)
  630. {
  631. status = PCD_OK;
  632. }
  633. }
  634. if (status == PCD_OK)
  635. {
  636. CmdFrameBuf[0] = PICC_TRANSFER;
  637. CmdFrameBuf[1] = BlockAddr;
  638. MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
  639. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
  640. if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
  641. {
  642. status = PCD_ERR;
  643. }
  644. }
  645. return status;
  646. }
  647. /***************************************************************************************
  648. * 函数名称:PCD_BakValue
  649. * 功能描述:备份钱包(块转存)
  650. * 入口参数:-sourceBlockAddr:源块地址
  651. * -goalBlockAddr :目标块地址
  652. * 出口参数:无
  653. * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
  654. * 说 明:只能在同一个扇区内转存
  655. ***************************************************************************************/
  656. char PCD_BakValue(uint8_t sourceBlockAddr, uint8_t goalBlockAddr)
  657. {
  658. char status;
  659. uint16_t unLen;
  660. uint8_t CmdFrameBuf[MFRC_MAXRLEN];
  661. CmdFrameBuf[0] = PICC_RESTORE;
  662. CmdFrameBuf[1] = sourceBlockAddr;
  663. MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
  664. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
  665. if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
  666. {
  667. status = PCD_ERR;
  668. }
  669. if (status == PCD_OK)
  670. {
  671. CmdFrameBuf[0] = 0;
  672. CmdFrameBuf[1] = 0;
  673. CmdFrameBuf[2] = 0;
  674. CmdFrameBuf[3] = 0;
  675. MFRC_CalulateCRC(CmdFrameBuf, 4, &CmdFrameBuf[4]);
  676. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 6, CmdFrameBuf, &unLen);
  677. if (status != PCD_ERR)
  678. {
  679. status = PCD_OK;
  680. }
  681. }
  682. if (status != PCD_OK)
  683. {
  684. return PCD_ERR;
  685. }
  686. CmdFrameBuf[0] = PICC_TRANSFER;
  687. CmdFrameBuf[1] = goalBlockAddr;
  688. MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
  689. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
  690. if ((status != PCD_OK) || (unLen != 4) || ((CmdFrameBuf[0] & 0x0F) != 0x0A))
  691. {
  692. status = PCD_ERR;
  693. }
  694. return status;
  695. }
  696. /***************************************************************************************
  697. * 函数名称:PCD_Halt
  698. * 功能描述:命令卡片进入休眠状态
  699. * 入口参数:无
  700. * 出口参数:无
  701. * 返 回 值:-status:错误代码(PCD_OK、PCD_NOTAGERR、PCD_ERR)
  702. * 说 明:无
  703. ***************************************************************************************/
  704. char PCD_Halt(void)
  705. {
  706. char status;
  707. uint16_t unLen;
  708. uint8_t CmdFrameBuf[MFRC_MAXRLEN];
  709. CmdFrameBuf[0] = PICC_HALT;
  710. CmdFrameBuf[1] = 0;
  711. MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]);
  712. status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen);
  713. return status;
  714. }

RC522.c中的函数说明:

IO口定义:

  1. #define RS522_RST(N) HAL_GPIO_WritePin(RC522_RST_GPIO_Port, RC522_RST_Pin, N == 1 ? GPIO_PIN_SET : GPIO_PIN_RESET)
  2. #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)

4.2.3 RFID.h文件

  1. #ifndef _RFID_H
  2. #define _RFID_H
  3. #include "main.h"
  4. extern uint8_t readUid[5];
  5. extern uint8_t UID[5]; //定义一张已知卡号,可以通过串口打印通过下面读取到的打印到上位机,再把那个读取的卡号填入数组
  6. extern uint8_t DefaultKey[6]; // 默认秘钥
  7. /*函数声明*/
  8. void RC522_Init(void);
  9. uint8_t EntranceGuard(uint8_t *readUid,void(*funCallBack)(void));
  10. void DoorSensor(void);
  11. void RfidIndicator(void);
  12. //void notarize_type1(void);
  13. char WriteAmount(uint8_t addr, uint32_t pData);
  14. char ReadAmount(uint8_t addr, uint32_t *pData);
  15. char ReadAmount(uint8_t addr, uint32_t *pData);
  16. char WriteDataBlock(uint8_t addr, uint8_t *pData, uint8_t Len);
  17. char ReadDataBlock(uint8_t addr, uint8_t *pData);
  18. #endif

4.2.4 RFID.c文件

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include "global/user.h"
  4. #include "RC522\RC522.h"
  5. #include "RC522\RFID.h"
  6. uint8_t readUid[5];
  7. uint8_t UID[5] = {0x37, 0x7e, 0xbc, 0xfd}; // 自定义的卡号,用于比较
  8. uint8_t DefaultKey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 默认秘钥
  9. /***************************************************************************************
  10. * 函数名称:RC522_Init
  11. * 功能描述:初始化
  12. * 入口参数:无
  13. * 出口参数:无
  14. * 返 回 值:无
  15. * 说 明:无
  16. ***************************************************************************************/
  17. void RC522_Init(void)
  18. {
  19. MFRC_Init();
  20. PCD_Reset();
  21. printf("RC522初始化完成\n");
  22. }
  23. /***************************************************************************************
  24. * 函数名称:门禁开门
  25. * 功能描述:只读取并显示卡号,成功读取到卡号就退出,并调用回调功能函数
  26. * 入口参数:-readUid:用于保存卡片序列号,4字节
  27. -funCallBack:函数传参,无需会掉功能函数时填NULL即可
  28. * 出口参数:
  29. * 返 回 值:读到卡号返回0,失败返回1
  30. * 说 明:无
  31. ***************************************************************************************/
  32. uint8_t EntranceGuard(uint8_t *readUid, void (*funCallBack)(void))
  33. {
  34. uint8_t Temp[5]; // 存放IC卡的类型和UID(IC卡序列号)
  35. if (PCD_Request(PICC_REQALL, Temp) == PCD_OK) // 寻卡
  36. {//成功
  37. if (Temp[0] == 0x04 && Temp[1] == 0x00)
  38. printf("Mifare1-S50\n");
  39. else if (Temp[0] == 0x02 && Temp[1] == 0x00)
  40. printf("Mifare1-S70");
  41. else if (Temp[0] == 0x44 && Temp[1] == 0x00)
  42. printf("Mifare-UltraLight(MF0)");
  43. else if (Temp[0] == 0x08 && Temp[1] == 0x00)
  44. printf("Mifare-Pro(MF2)");
  45. else if (Temp[0] == 0x44 && Temp[1] == 0x03)
  46. printf("Mifare Desire(MF3)");
  47. else
  48. printf("Unknown");
  49. if (PCD_Anticoll(readUid) == PCD_OK) // 防冲撞,获取卡号,存入readUid
  50. { // 防冲撞成功
  51. if (funCallBack != NULL)
  52. funCallBack(); // 调用功能执行函数,如指示灯信号
  53. return 0;
  54. }
  55. }
  56. return 1;
  57. }
  58. /***************************************************************************************
  59. * 函数名称:DoorSensor
  60. * 功能描述:门磁控制信号
  61. * 入口参数:无
  62. * 出口参数:无
  63. * 返 回 值:无
  64. * 说 明:无
  65. ***************************************************************************************/
  66. void DoorSensor(void)
  67. {
  68. // 【STM32F446,NUCLEO-F446RE板】使用STM32CubeMX创建MDK工程,实现流水灯
  69. HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); // LED亮
  70. HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET); // LED灭
  71. HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); // LED灭
  72. HAL_Delay(500); // 延时 500ms
  73. HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); // LED灭
  74. HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); // LED亮
  75. HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); // LED灭
  76. }
  77. /***************************************************************************************
  78. * 函数名称:RfidIndicator
  79. * 功能描述:指示灯信号
  80. * 入口参数:无
  81. * 出口参数:无
  82. * 返 回 值:无
  83. * 说 明:无
  84. ***************************************************************************************/
  85. void RfidIndicator(void)
  86. {
  87. HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_RESET); // LED1亮
  88. HAL_Delay(1000); // 延时 500ms
  89. HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_SET); // LED1灭
  90. }
  91. /**
  92. * @brief 判断 addr 是否数据块
  93. * @param addr,块绝对地址(0-63)
  94. * @retval 返回值 1:是数据块;0:不是数据块
  95. */
  96. char IsDataBlock(uint8_t addr)
  97. {
  98. if (addr == 0)
  99. {
  100. printf("第0扇区的块0不可更改,不应对其进行操作\n");
  101. return 0;
  102. }
  103. // 如果是数据块(不包含数据块0)
  104. if ((addr < 64) && (((addr + 1) % 4) != 0))
  105. {
  106. return 1;
  107. }
  108. printf("块地址不是指向数据块\n");
  109. return 0;
  110. }
  111. /**
  112. * @brief 写 pData 字符串到M1卡中的数据块
  113. * @param addr,数据块地址(不能写入控制块)
  114. * @param pData,写入的数据,16字节
  115. * @retval 状态值= PCD_OK,成功
  116. */
  117. char PCD_WriteString(uint8_t addr, uint8_t *pData)
  118. {
  119. /* 如果是数据块(不包含数据块0),则写入 */
  120. if (IsDataBlock(addr))
  121. {
  122. return PCD_WriteBlock(addr, pData);
  123. }
  124. return PCD_ERR;
  125. }
  126. /**
  127. * @brief 读取M1卡中的一块数据到 pData
  128. * @param addr,数据块地址(不读取控制块)
  129. * @param pData,读出的数据,16字节
  130. * @retval 状态值= PCD_OK,成功
  131. */
  132. char PCD_ReadString(uint8_t addr, uint8_t *pData)
  133. {
  134. /* 如果是数据块(不包含数据块0),则读取 */
  135. if (IsDataBlock(addr))
  136. {
  137. return PCD_ReadBlock(addr, pData);
  138. }
  139. return PCD_ERR;
  140. }
  141. /**
  142. * @DESCRIPTION: 写入钱包金额
  143. * @INPUT ARGS: none
  144. * @OUTPUT ARGS: none
  145. * @NOTE : none
  146. * @param {uint8_t} addr:块地址
  147. * @param {uint32_t} pData:写入的金额
  148. * @return {*} 成功返回PCD_OK
  149. */
  150. char WriteAmount(uint8_t addr, uint32_t pData)
  151. {
  152. char status;
  153. uint8_t ucComMF522Buf[16];
  154. ucComMF522Buf[0] = (pData & ((uint32_t)0x000000ff));
  155. ucComMF522Buf[1] = (pData & ((uint32_t)0x0000ff00)) >> 8;
  156. ucComMF522Buf[2] = (pData & ((uint32_t)0x00ff0000)) >> 16;
  157. ucComMF522Buf[3] = (pData & ((uint32_t)0xff000000)) >> 24;
  158. ucComMF522Buf[4] = ~(pData & ((uint32_t)0x000000ff));
  159. ucComMF522Buf[5] = ~(pData & ((uint32_t)0x0000ff00)) >> 8;
  160. ucComMF522Buf[6] = ~(pData & ((uint32_t)0x00ff0000)) >> 16;
  161. ucComMF522Buf[7] = ~(pData & ((uint32_t)0xff000000)) >> 24;
  162. ucComMF522Buf[8] = (pData & ((uint32_t)0x000000ff));
  163. ucComMF522Buf[9] = (pData & ((uint32_t)0x0000ff00)) >> 8;
  164. ucComMF522Buf[10] = (pData & ((uint32_t)0x00ff0000)) >> 16;
  165. ucComMF522Buf[11] = (pData & ((uint32_t)0xff000000)) >> 24;
  166. ucComMF522Buf[12] = addr;
  167. ucComMF522Buf[13] = ~addr;
  168. ucComMF522Buf[14] = addr;
  169. ucComMF522Buf[15] = ~addr;
  170. status = PCD_WriteBlock(addr, ucComMF522Buf);
  171. return status;
  172. }
  173. /**
  174. * @DESCRIPTION: 读取钱包金额
  175. * @INPUT ARGS: none
  176. * @OUTPUT ARGS: none
  177. * @NOTE : none
  178. * @param {uint8_t} addr:块地址
  179. * @param {uint32_t} *pData:读出的金额
  180. * @return {*}: 成功返回PCD_OK
  181. */
  182. char ReadAmount(uint8_t addr, uint32_t *pData)
  183. {
  184. char status = PCD_ERR;
  185. uint8_t j;
  186. uint8_t ucComMF522Buf[16];
  187. status = PCD_ReadBlock(addr, ucComMF522Buf);
  188. if (status != PCD_OK)
  189. return status;
  190. for (j = 0; j < 4; j++)
  191. {
  192. if ((ucComMF522Buf[j] != ucComMF522Buf[j + 8]) && (ucComMF522Buf[j] != ~ucComMF522Buf[j + 4])) // 验证一下是不是钱包的数据
  193. break;
  194. }
  195. if (j == 4)
  196. {
  197. status = PCD_OK;
  198. *pData = ucComMF522Buf[0] + (ucComMF522Buf[1] << 8) + (ucComMF522Buf[2] << 16) + (ucComMF522Buf[3] << 24);
  199. }
  200. else
  201. {
  202. status = PCD_ERR;
  203. *pData = 0;
  204. }
  205. return status;
  206. }
  207. /**
  208. * @brief 修改控制块 addr 的密码A。注意 addr 指的是控制块的地址。
  209. * 必须要校验密码B,密码B默认为6个0xFF,如果密码B也忘记了,那就改不了密码A了
  210. * @note 注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
  211. * @param addr:[控制块]所在的地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
  212. * @param pKeyA:指向新的密码A字符串,六个字符,比如 "123456"
  213. * @retval 成功返回 PCD_OK
  214. */
  215. char ChangeKeyA(uint8_t addr, uint8_t *pKeyA)
  216. {
  217. uint8_t KeyBValue[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
  218. uint8_t ucArrayID[4]; // 先后存放IC卡的类型和UID(IC卡序列号)
  219. uint8_t ucComMF522Buf[16];
  220. uint8_t j;
  221. // 寻卡
  222. while (PCD_Request(PICC_REQALL, ucArrayID) != PCD_OK)
  223. {
  224. printf("寻卡失败\n");
  225. delay_ms(1000);
  226. }
  227. printf("寻卡成功\n");
  228. // 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
  229. if (PCD_Anticoll(ucArrayID) == PCD_OK)
  230. {
  231. // 选中卡
  232. PCD_Select(ucArrayID);
  233. // 校验 B 密码
  234. if (PCD_AuthState(PICC_AUTHENT1B, addr, KeyBValue, ucArrayID) != PCD_OK)
  235. {
  236. printf("检验密码B失败\n");
  237. }
  238. // 读取控制块里原本的数据(只要修改密码A,其他数据不改)
  239. if (PCD_ReadBlock(addr, ucComMF522Buf) != PCD_OK)
  240. {
  241. printf("读取控制块数据失败\n");
  242. return PCD_ERR;
  243. }
  244. // 修改密码A
  245. for (j = 0; j < 6; j++)
  246. ucComMF522Buf[j] = pKeyA[j];
  247. if (PCD_WriteBlock(addr, ucComMF522Buf) != PCD_OK)
  248. {
  249. printf("写入数据到控制块失败\n");
  250. return PCD_ERR;
  251. }
  252. printf("密码A修改成功!\n");
  253. PCD_Halt();
  254. return PCD_OK;
  255. }
  256. return PCD_ERR;
  257. }
  258. /**
  259. * @brief 按照RC522操作流程写入16字节数据到块 addr
  260. * 函数里校验的是密码B,密码B默认为6个0xFF,也可以校验密码A
  261. * 用法:WriteDataBlock( 1, "123456789\n", 10); //字符串不够16个字节的后面补零写入
  262. * @note 注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
  263. * 注意:使用该函数要注意 addr 是块0、数据块还是控制块,该函数内部不对此做判断
  264. * @param addr:任意块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
  265. * @param pData:指向要写入的数据,最大16个字符
  266. * @param Len:要写入数据的字节数
  267. * @retval 成功返回 PCD_OK
  268. */
  269. char WriteDataBlock(uint8_t addr, uint8_t *pData, uint8_t Len)
  270. {
  271. uint8_t KeyBValue[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
  272. uint8_t ucArrayID[4]; // 先后存放IC卡的类型和UID(IC卡序列号)
  273. uint8_t ucComMF522Buf[16];
  274. uint8_t j;
  275. // 寻卡
  276. while (PCD_Request(PICC_REQALL, ucArrayID) != PCD_OK)
  277. {
  278. printf("寻卡失败\n");
  279. delay_ms(1000);
  280. }
  281. printf("寻卡成功\n");
  282. // 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
  283. if (PCD_Anticoll(ucArrayID) == PCD_OK)
  284. {
  285. // 选中卡
  286. PCD_Select(ucArrayID);
  287. // 校验 B 密码
  288. if (PCD_AuthState(PICC_AUTHENT1B, addr, KeyBValue, ucArrayID) != PCD_OK)
  289. {
  290. printf("检验密码B失败\n");
  291. }
  292. // 拷贝 pData 里的 Len 个字符到 ucComMF522Buf
  293. for (j = 0; j < 16; j++)
  294. {
  295. if (j < Len)
  296. ucComMF522Buf[j] = pData[j];
  297. else
  298. ucComMF522Buf[j] = 0; // 16个字节若是未填满的字节置0
  299. }
  300. // 写入字符串
  301. if (PCD_WriteBlock(addr, ucComMF522Buf) != PCD_OK)
  302. {
  303. printf("写入数据到数据块失败\n");
  304. return PCD_ERR;
  305. }
  306. printf("写入数据成功!\n");
  307. PCD_Halt();
  308. return PCD_OK;
  309. }
  310. return PCD_ERR;
  311. }
  312. /**
  313. * @brief 按照RC522操作流程读取块 addr
  314. * 函数里校验的是密码B,密码B默认为6个0xFF,也可以校验密码A
  315. * 用法:ReadDataBlock( 1, databuf); // databuf 至少为16字节:uint8_t databuf[16];
  316. * @note 注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
  317. * 注意:使用该函数要注意 addr 是块0、数据块还是控制块,该函数内部不对此做判断
  318. * @param addr:任意块地址。M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
  319. * @param pData:指向读取到的数据,包含16个字符
  320. * @retval 成功返回 PCD_OK
  321. */
  322. char ReadDataBlock(uint8_t addr, uint8_t *pData)
  323. {
  324. uint8_t KeyBValue[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
  325. uint8_t ucArrayID[4]; // 先后存放IC卡的类型和UID(IC卡序列号)
  326. // 寻卡
  327. while (PCD_Request(PICC_REQALL, ucArrayID) != PCD_OK)
  328. {
  329. printf("寻卡失败\n");
  330. delay_ms(1000);
  331. }
  332. printf("寻卡成功\n");
  333. // 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)
  334. if (PCD_Anticoll(ucArrayID) == PCD_OK)
  335. {
  336. // 选中卡
  337. PCD_Select(ucArrayID);
  338. // 校验 B 密码
  339. if (PCD_AuthState(PICC_AUTHENT1B, addr, KeyBValue, ucArrayID) != PCD_OK)
  340. {
  341. printf("检验密码B失败\n");
  342. }
  343. // 读取数据块里的数据到 pData
  344. if (PCD_ReadBlock(addr, pData) != PCD_OK)
  345. {
  346. printf("读取数据块失败\n");
  347. return PCD_ERR;
  348. }
  349. printf("读取数据成功!\n");
  350. PCD_Halt();
  351. return PCD_OK;
  352. }
  353. return PCD_ERR;
  354. }

4.3 UART串口printf,scanf函数串口重定向

因本实验中的调试信息需要通过串口输出,Nucleo-446RE提供了利用USB的虚拟串口(串口2)。

这部分内容与基础篇007. 串行通信(一)--阻塞方式发送接收基本相同,只是把UART1换成UART2。以下仅提供代码截图,请大家参考前文实验。

在usart.c文件的user code 0 区域内:

a85fd81860a047d192d73069717c4aeb.png

 

输入如下内容:

83b152d7ee7b448da92e3623cd8e0b21.png

4.4 main()函数修改

要让RC522工作起来,需对RC522模块及Mifare的原理与协议有较深入的了解,请参考博文:

基础篇010.1 STM32驱动RC522 RFID模块之一:基础知识https://blog.csdn.net/qcmyqcmy/article/details/130876866

驱动程序设计时,必须的顺序是:寻卡--->防冲撞--->选卡--->开天线--->读/写卡。主函数的编写必须要按照这样的顺序,否者设备不会工作。

本实验是由门禁项目修改而来,为方便分析RC522读取到的数据,利用串口通信助手来检查是否读取到正确的数据。

修改Main.c函数头文件:

9edf59105cf74f5188e6380b96fcac9c.png

在下图红框区域添加代码:

675113071afc4b8a80cd31942e6fe178.png

  1. HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
  2. delay_ms(1000);
  3. // if(!EntranceGuard(readUid, RfidIndicator))
  4. if (!EntranceGuard(readUid, NULL))
  5. {
  6. printf("当前卡号:%x-%x-%x-%x\n", readUid[0], readUid[1], readUid[2], readUid[3]);
  7. if (!strncmp((char *)readUid, (char *)UID, 4))
  8. {
  9. // TODO
  10. // 插入比对卡号正确时的处理程序,如打开门禁
  11. printf("已认证的卡\n");
  12. DoorSensor();
  13. }
  14. else
  15. {
  16. // TODO
  17. // 插入比对卡号错误时的处理程序
  18. printf("未认证卡\n");
  19. }
  20. HAL_Delay(2000);
  21. }

4.5 工程配置

关于工程配置方法,请参考本专栏博文中的第4部分“4.在MDK中自建驱动库的工程设置”

基础篇005. 按键控制https://blog.csdn.net/qcmyqcmy/article/details/129407317此处不赘述,需要交流课上当面交流,网友可私信或留言。

 

5.调试与验证

如果你需要AC5编译器,请参考如下博文安装设置:

Keil MDK5.37以上版本自行添加AC5(ARMCC)编译器的方法_armcc下载

 

程序编译通过后,可将其下载到开发板进行验证:

659d0b3ca3e34789ac6ac4aa50cab345.jpeg

 

实验需要使用串口调试助手验证。

串口调试助手下载地址

打开串口调试助手,用RFID卡在读卡器上测试,实验结果如下:

 

579c5b1163ab4266a347bd811bc3cc97.png

 

6.总结

 

文中代码已经调试通过,如果在你的板子上运行有问题,可以尝试调整SPI的速率。

 

本实验是STM32驱动RFID模块的第二部分,基础知识已在上一篇讲述:

关于RFID基础知识,请参考博文: 

基础篇010.1 STM32驱动RC522 RFID模块之一:基础知识https://blog.csdn.net/qcmyqcmy/article/details/130876866

 如果您需用软件模拟SPI方式实现RC522驱动,请参考:

基础篇010.3 STM32驱动RC522 RFID模块之三:STM32软件模拟SPI驱动RC522https://blog.csdn.net/qcmyqcmy/article/details/131018784

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/890406
推荐阅读
相关标签
  

闽ICP备14008679号