• 基础篇010.2 STM32驱动RC522 RFID模块之二:STM32硬件SPI驱动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. //具体实现思想,参考<>第五章(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//这里注意一下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;idelay_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
    3. #include
    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
    70. #include
    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
    2. #include
    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

     

     

  • 相关阅读:
    浅聊一下Nginx
    『德不孤』Pytest框架 — 6、Mark分组执行测试用例
    分布式事务
    构建 Go 应用 docker 镜像的十八种姿势
    angular:html2canvas对ion-avatar节点渲染不正确
    SASS/SCSS精华干货教程
    TCP协议的三次握手和四次挥手
    力扣124. 二叉树中的最大路径和
    面向6G的编码调制和波形技术
    福云玩具销售系统
  • 原文地址:https://blog.csdn.net/qcmyqcmy/article/details/130913990