在ARM Linux应用层下驱动MFRC522

打印 上一主题 下一主题

主题 1611|帖子 1611|积分 4833

1、媒介

本文将介绍怎样在ARM Linux应用层下,利用SPI驱动MFRC522读写IC-S50。本人也是第一次接触MFRC522,所以本文将记录概念扫盲到最终的驱动实现。
2、IC卡 和 IC卡读卡器

IC卡一般分为接触式IC卡和非接触式IC卡。所以实际上我们将讨论的是非接触式IC卡和非接触式IC卡读卡器。IC卡里是有一个芯片的,IC卡读卡器也是有一个芯片的,很多半导体公司都有生产用于IC卡和IC卡读卡器的芯片。所以再细分下来,我们将讨论的是NXP公司生产的MIFARE Classic系列的S50型号的非接触式卡IC芯片(后文将称为IC-S50)和NXP公司生产的MFRC522非接触式读卡器芯片。它们都工作于13.56MHz。
例如下图是一个利用了MFRC522芯片的读卡器模块,本次实验也是利用这个模块:

下图是利用了IC-S50的非接触式IC卡,右边白色IC卡的物理外观遵循国际标准ISO/IEC 7816,和我们通常利用的银行卡、校园卡的外观是一样的(也可以称M1卡)。其它外观的另有像左边蓝色这种的,常见于门禁等多种场合。

非接触式IC卡通过无线射频(RF)与读卡器通信。它们通常遵循ISO/IEC 14443标准,工作频率为13.56MHz。非接触式IC卡不必要外部电源。它们通过感应天线从读卡器的射频信号中获取能量,这种能量足以驱动卡片内的芯片举行数据处置惩罚和通信。如下图为MFRC522和IC卡通信示意框图:

下面我们将进一步相识IC-S50和MFRC522的根本特性。
IC-S50根本特性:


  • 8KBit大小的EEPROM存储
  • 分为16个扇区,每个扇区为4块,每块16字节,以块为存取单位
  • 每个扇区有独立的一组密码及访问控制
  • 每张卡有唯一序列号,为 32 位
  • 工作频率:13.56MHZ
  • 通信速率:106 KBPS
  • 读写距离:10 cm 以内(与读写器有关)
MFRC522根本特性:


  • 支持的主机接口:SPI、UART、I2C
  • 支持ISO 14443A / MIFARE
3、MFRC522

这里实际必要相识两个芯片的利用,分别是MFRC522和IC-S50。MFRC522重要相识的内容有两部分,分别是MFRC522寄存器集 和 MFRC522命令集。
3.1、寄存器集

MFRC522有4组寄存器,分别如下所示。
Page 0(命令和状态寄存器):

Page 1(命令寄存器):

Page 2(配置寄存器):

Page 3(测试寄存器):

3.2、命令集

MFRC522的利用由可执行一系列命令的内部状态机来决定。通过向寄存器集中的命令寄存器写入相应的命令来启动命令。下图是MFRC522的命令集:

3.3、数据利用

这里利用的MFRC522模块是SPI接口。
对于读数据(MSB先行):

对于写数据(MSB先行):

其中对于地址字节有如下要求:地址字节的最高位bit7为1时表示从MFRC522读取数据,为0时表示向MFRC522写入数据。bit6-bit1表示地址。bit0为0。

3.4、基础函数编写

关于MFRC522的利用教程博客有很多。这里保举直接参考野火基于stm32的MFRC522驱动。我们先以读取到IC卡的ID为阶段性目的,以此测试spi和mfrc522相关的基础函数是正常的。
如今还没介绍IC-S50,又怎样知道怎么读ID?没关系,先复制粘贴。由于关于mfrc522的利用函数太多了,先移植一部分。
3.4.1、MFRC522接线

MFRC522Linux板卡SDA(片选脚)任意gpio口MOSISPI_MISOMISOSPI_MOSISCK(时钟脚)SPI_CLKRST(复位脚)任意gpio口3.3V3.3VGNDGND 3.4.2、编写SPI利用函数

下面将直接提供在Linux应用层的spi利用函数,支持多线程下利用。也可以用在其它项目。
spi.h:
  1. /* spi.h */
  2. #ifndef _SPI_H
  3. #define _SPI_H
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <sys/ioctl.h>
  7. #include <stdlib.h>
  8. #include <fcntl.h>
  9. #include <unistd.h>
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include <gpiod.h>
  13. #include <stdint.h>
  14. #include <linux/spi/spidev.h>
  15. #include <pthread.h>
  16. typedef struct spi_handle
  17. {
  18.         char *dev_name;
  19.         int fd;       
  20.         pthread_mutex_t mutex;
  21.         struct gpiod_chip *cs_gpiochip;        
  22.         struct gpiod_line *cs_gpioline;
  23. }spi_handle_t;
  24. typedef enum
  25. {
  26.         SPIMODE0 = SPI_MODE_0,
  27.         SPIMODE1 = SPI_MODE_1,
  28.         SPIMODE2 = SPI_MODE_2,
  29.         SPIMODE3 = SPI_MODE_3,
  30. }SPI_MODE;
  31. typedef enum
  32. {
  33.     S_1M    = 1000000,
  34.         S_6_75M = 6750000,
  35.         S_8M    = 8000000,
  36.         S_13_5M = 13500000,
  37.         S_27M   = 27000000,
  38. }SPI_SPEED;
  39. spi_handle_t *spi_handle_alloc(const char *spi_dev, SPI_MODE spi_mode, SPI_SPEED spi_speed, const char *cs_chip, unsigned int cs_num);
  40. void spi_write_and_read(spi_handle_t *spi, unsigned char *send_buf, unsigned int send_buf_len, unsigned char *recv_buf);
  41. void spi_write_then_read(spi_handle_t *spi, unsigned char *send_buf, unsigned int send_buf_len, unsigned char *recv_buf, unsigned int recv_buf_len);
  42. void spi_write_byte_data(spi_handle_t *spi, unsigned char data);
  43. void spi_write_nbyte_data(spi_handle_t *spi, unsigned char *send_buf, unsigned int send_buf_len);
  44. void spi_handle_free(spi_handle_t *spi);
  45. #endif
复制代码
spi.c:
  1. /* spi.c */
  2. #include "spi.h"
  3. /*******************************
  4. * @brief : SPI同时发送数据和接收数据
  5. * @param : spi - SPI设备句柄
  6. *          send_buf - 发送缓冲区
  7. *          send_buf_len - 发送缓冲区长度
  8. *          recv_buf - 接收缓冲区
  9. * @return: 无
  10. *******************************/
  11. void spi_write_and_read(spi_handle_t *spi, unsigned char *send_buf, unsigned int send_buf_len, unsigned char *recv_buf)
  12. {
  13.         struct spi_ioc_transfer        xfer[1];
  14.         int status;
  15.        
  16.     if (spi == NULL)
  17.         return;
  18.     if (send_buf == NULL || recv_buf == NULL)
  19.         return;
  20.     memset(xfer, 0, sizeof(xfer));
  21.         xfer[0].tx_buf = (unsigned long)send_buf;
  22.         xfer[0].rx_buf = (unsigned long)recv_buf;
  23.         xfer[0].len = send_buf_len;
  24.        
  25.     pthread_mutex_lock(&(spi->mutex));
  26.     gpiod_line_set_value(spi->cs_gpioline, 0);
  27.         status = ioctl(spi->fd, SPI_IOC_MESSAGE(1), xfer);
  28.         if (status < 0)
  29.         printf("SPI_IOC_MESSAGE failed!\n");
  30.     gpiod_line_set_value(spi->cs_gpioline, 1);
  31.     pthread_mutex_unlock(&(spi->mutex));
  32. }
  33. /*******************************
  34. * @brief : SPI先发送数据后接收数据
  35. * @param : spi - SPI设备句柄
  36. *          send_buf - 发送缓冲区
  37. *          send_buf_len - 发送缓冲区长度
  38. *          recv_buf - 接收缓冲区
  39. *          recv_buf_len - 接收缓冲区长度
  40. * @return: 无
  41. *******************************/
  42. void spi_write_then_read(spi_handle_t *spi, unsigned char *send_buf, unsigned int send_buf_len, unsigned char *recv_buf, unsigned int recv_buf_len)
  43. {
  44.     struct spi_ioc_transfer        xfer[2];
  45.         int status;
  46.     if (spi == NULL)
  47.         return;
  48.     if (send_buf == NULL || recv_buf == NULL)
  49.         return;
  50.     memset(xfer, 0, sizeof(xfer));
  51.         xfer[0].tx_buf = (unsigned long)send_buf;
  52.         xfer[0].len = send_buf_len;
  53.         xfer[1].rx_buf = (unsigned long)recv_buf;
  54.         xfer[1].len = recv_buf_len;
  55.     pthread_mutex_lock(&(spi->mutex));
  56.     gpiod_line_set_value(spi->cs_gpioline, 0);
  57.         status = ioctl(spi->fd, SPI_IOC_MESSAGE(2), xfer);
  58.         if (status < 0)
  59.         printf("SPI_IOC_MESSAGE failed!\n");
  60.     gpiod_line_set_value(spi->cs_gpioline, 1);
  61.     pthread_mutex_unlock(&(spi->mutex));
  62. }
  63. /*******************************
  64. * @brief : SPI发送一个字节
  65. * @param : spi - SPI设备句柄
  66. *          data - 待发送的字节数据
  67. * @return: 无
  68. *******************************/
  69. void spi_write_byte_data(spi_handle_t *spi, unsigned char data)
  70. {
  71.     unsigned char buff[1] = {data};
  72.     if (spi == NULL)
  73.         return;
  74.     pthread_mutex_lock(&(spi->mutex));
  75.     gpiod_line_set_value(spi->cs_gpioline, 0);
  76.     write(spi->fd, &buff, 1);
  77.     gpiod_line_set_value(spi->cs_gpioline, 1);
  78.     pthread_mutex_unlock(&(spi->mutex));
  79. }
  80. /*******************************
  81. * @brief : 通过SPI发送多个字节数据
  82. * @param : spi - SPI设备句柄
  83. *          send_buf - 发送缓冲区
  84. *          send_buf_len - 发送缓冲区长度
  85. * @return: 无
  86. *******************************/
  87. void spi_write_nbyte_data(spi_handle_t *spi, unsigned char *send_buf, unsigned int send_buf_len)
  88. {
  89.     struct spi_ioc_transfer        xfer[2];
  90.     unsigned char recv_buf[send_buf_len];
  91.         int status;
  92.     if (spi == NULL)
  93.         return;
  94.     if (send_buf == NULL)
  95.         return;
  96.     memset(xfer, 0, sizeof(xfer));
  97.     memset(recv_buf, 0, sizeof(send_buf_len));
  98.         xfer[0].tx_buf = (unsigned long)send_buf;
  99.     xfer[0].rx_buf = (unsigned long)recv_buf;
  100.         xfer[0].len = send_buf_len;
  101.     pthread_mutex_lock(&(spi->mutex));
  102.     gpiod_line_set_value(spi->cs_gpioline, 0);
  103.         status = ioctl(spi->fd, SPI_IOC_MESSAGE(1), xfer);
  104.         if (status < 0)
  105.         printf("SPI_IOC_MESSAGE failed!\n");
  106.     gpiod_line_set_value(spi->cs_gpioline, 1);
  107.     pthread_mutex_unlock(&(spi->mutex));
  108. }
  109. /*******************************
  110. * @brief : 分配并初始化SPI设备句柄
  111. * @param : spi_dev - SPI设备文件路径
  112. *          spi_mode - SPI模式
  113. *          spi_speed - SPI通信速度
  114. *          cs_chip - 片选GPIO芯片      
  115. *          cs_line - 片选GPIO引脚
  116. * @return: 成功返回SPI设备句柄,失败返回NULL
  117. *******************************/
  118. spi_handle_t *spi_handle_alloc(const char *spi_dev, SPI_MODE spi_mode, SPI_SPEED spi_speed, const char *cs_chip, unsigned int cs_line)
  119. {
  120.     int ret;
  121.     int fd;
  122.     char spi_bits = 8;
  123.     SPI_SPEED speed = (uint32_t)spi_speed;
  124.     if (!spi_dev)
  125.         return NULL;
  126.     if (!cs_chip)
  127.         return NULL;
  128.    
  129.     fd = open(spi_dev, O_RDWR);
  130.         if (fd < 0)
  131.     {
  132.                 printf("open %s failed!\n", spi_dev);
  133.                 return NULL;
  134.         }
  135.     /* alloc spi_handle_t */
  136.     spi_handle_t *spi = (spi_handle_t *)malloc(sizeof(spi_handle_t));
  137.     if (!spi)
  138.     {
  139.         printf("spi_handle_t allocation failed!\n");
  140.         return NULL;
  141.     }
  142.     spi->fd = fd;
  143.     /* spi mode setting */
  144.     ret = ioctl(spi->fd, SPI_IOC_WR_MODE, &spi_mode);               
  145.     if (ret < 0)
  146.     {
  147.                 printf("SPI_IOC_WR_MODE failed!\n");
  148.                 free(spi);
  149.                 return NULL;
  150.         }
  151.     /* bits per word */
  152.     ret = ioctl(spi->fd, SPI_IOC_WR_BITS_PER_WORD, &spi_bits);   
  153.     if (ret < 0)
  154.     {
  155.                 printf("SPI_IOC_WR_BITS_PER_WORD failed!\n");
  156.                 free(spi);
  157.                 return NULL;
  158.         }
  159.     /* spi speed setting */
  160.     ret = ioctl(spi->fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);        
  161.     if (ret < 0)
  162.     {
  163.                 printf("SPI_IOC_WR_MAX_SPEED_HZ failed!\n");
  164.                 free(spi);
  165.                 return NULL;
  166.         }
  167.     spi->dev_name = (char *)malloc(strlen(spi_dev) + 1);
  168.     if (!(spi->dev_name))
  169.     {
  170.         printf("dev_name allocation failed!\n");
  171.         free(spi);
  172.         return NULL;
  173.     }
  174.     strcpy(spi->dev_name, spi_dev);
  175.     ret = pthread_mutex_init(&spi->mutex, NULL);
  176.     if (ret != 0)
  177.     {
  178.         printf("pthread_mutex_init failed!\n");
  179.         free(spi->dev_name);
  180.         free(spi);
  181.         return NULL;
  182.     }
  183.     /* cs pin init */
  184.     spi->cs_gpiochip = gpiod_chip_open(cs_chip);
  185.     if (spi->cs_gpiochip == NULL)
  186.     {
  187.         printf("gpiod_chip_open failed!\n");
  188.         free(spi->dev_name);
  189.         free(spi);
  190.         return NULL;
  191.     }
  192.     spi->cs_gpioline = gpiod_chip_get_line(spi->cs_gpiochip, cs_line);
  193.     if (spi->cs_gpioline == NULL)
  194.     {
  195.         printf("gpiod_chip_get_line failed!\n");
  196.         free(spi->dev_name);
  197.         free(spi);
  198.         return NULL;
  199.     }
  200.     ret = gpiod_line_request_output(spi->cs_gpioline, "cs_gpioline", 1);
  201.     if (ret < 0)
  202.     {
  203.         printf("gpiod_line_request_output failed!\n");
  204.         free(spi->dev_name);
  205.         free(spi);
  206.         return NULL;
  207.     }
  208.     return spi;
  209. }
  210. /*******************************
  211. * @brief : 释放SPI设备句柄
  212. * @param : spi - SPI设备句柄
  213. * @return: 无
  214. *******************************/
  215. void spi_handle_free(spi_handle_t *spi)
  216. {
  217.     if (!spi)
  218.         return;
  219.     gpiod_line_release(spi->cs_gpioline);
  220.     gpiod_chip_close(spi->cs_gpiochip);
  221.     if (spi->dev_name)
  222.     {
  223.         free(spi->dev_name);
  224.     }
  225.     pthread_mutex_destroy(&spi->mutex);
  226.     free(spi);
  227. }
复制代码
3.4.3、编写MFRC522基础函数

3.4.3.1、完备的mfrc522.h

本次mfrc522驱动步伐涉及两个文件,分别是mfrc522.h和mfrc522.c。如下是mfrc522.h,重要是寄存器、命令集的宏界说。注意了,这里mfrc522寄存器的宏界说命名利用驼峰命名是为了和数据手册保持同等,方便查阅:
  1. /* mfrc522.h */
  2. #ifndef MFRC522_H
  3. #define MFRC522_H
  4. #include <gpiod.h>
  5. #include "spi.h"
  6. #define MI_OK                                        0
  7. #define MI_NOTAGERR                                1
  8. #define MI_ERR                                        2
  9. #define MFRC522_MAX_LEN                 18
  10. /* MFRC522 REG */
  11. // Page 0 - Command and Status
  12. #define        RFU00                                         0x00   
  13. #define        CommandReg                                    0x01   
  14. #define        ComIEnReg                                     0x02   
  15. #define        DivlEnReg                                     0x03   
  16. #define        ComIrqReg                                     0x04   
  17. #define        DivIrqReg                                     0x05
  18. #define        ErrorReg                                      0x06   
  19. #define        Status1Reg                                    0x07   
  20. #define        Status2Reg                                    0x08   
  21. #define        FIFODataReg                                   0x09
  22. #define        FIFOLevelReg                                  0x0A
  23. #define        WaterLevelReg                                 0x0B
  24. #define        ControlReg                                    0x0C
  25. #define        BitFramingReg                                 0x0D
  26. #define        CollReg                                       0x0E
  27. #define        RFU0F                                         0x0F
  28. // Page 1 - Command
  29. #define        RFU10                                         0x10
  30. #define        ModeReg                                       0x11
  31. #define        TxModeReg                                     0x12
  32. #define        RxModeReg                                     0x13
  33. #define        TxControlReg                                  0x14
  34. #define        TxAutoReg                                     0x15
  35. #define        TxSelReg                                      0x16
  36. #define        RxSelReg                                      0x17
  37. #define        RxThresholdReg                                0x18
  38. #define        DemodReg                                      0x19
  39. #define        RFU1A                                         0x1A
  40. #define        RFU1B                                         0x1B
  41. #define        MifareReg                                     0x1C
  42. #define        RFU1D                                         0x1D
  43. #define        RFU1E                                         0x1E
  44. #define        SerialSpeedReg                                0x1F
  45. // Page 2 - CFG
  46. #define        RFU20                                         0x20  
  47. #define        CRCResultRegM                                 0x21
  48. #define        CRCResultRegL                                 0x22
  49. #define        RFU23                                         0x23
  50. #define        ModWidthReg                                   0x24
  51. #define        RFU25                                         0x25
  52. #define        RFCfgReg                                      0x26
  53. #define        GsNReg                                        0x27
  54. #define        CWGsCfgReg                                    0x28
  55. #define        ModGsCfgReg                                   0x29
  56. #define        TModeReg                                      0x2A
  57. #define        TPrescalerReg                                 0x2B
  58. #define        TReloadRegH                                   0x2C
  59. #define        TReloadRegL                                   0x2D
  60. #define        TCounterValueRegH                             0x2E
  61. #define        TCounterValueRegL                             0x2F
  62. // Page 3 - TestRegister
  63. #define        RFU30                                         0x30
  64. #define        TestSel1Reg                                   0x31
  65. #define        TestSel2Reg                                   0x32
  66. #define        TestPinEnReg                                  0x33
  67. #define        TestPinValueReg                               0x34
  68. #define        TestBusReg                                    0x35
  69. #define        AutoTestReg                                   0x36
  70. #define        VersionReg                                    0x37
  71. #define        AnalogTestReg                                 0x38
  72. #define        TestDAC1Reg                                   0x39  
  73. #define        TestDAC2Reg                                   0x3A   
  74. #define        TestADCReg                                    0x3B   
  75. #define        RFU3C                                         0x3C   
  76. #define        RFU3D                                         0x3D   
  77. #define        RFU3E                                         0x3E   
  78. #define        RFU3F                                                            0x3F
  79. /* MFRC522 CMD */
  80. #define PCD_IDLE                        0x00               // 取消当前命令
  81. #define PCD_AUTHENT                     0x0E               // 验证密钥
  82. #define PCD_RECEIVE                     0x08               // 接收数据
  83. #define PCD_TRANSMIT                    0x04               // 发送数据
  84. #define PCD_TRANSCEIVE                  0x0C               // 发送并接收数据
  85. #define PCD_RESETPHASE                  0x0F               // 复位
  86. #define PCD_CALCCRC                     0x03               // CRC计算
  87. /* IC-S50 CMD */
  88. #define PICC_REQIDL                     0x26               // 寻天线区内未进入休眠状态
  89. #define PICC_REQALL                     0x52               // 寻天线区内全部卡
  90. #define PICC_ANTICOLL1                  0x93               // 防冲撞
  91. #define PICC_ANTICOLL2                  0x95               // 防冲撞
  92. #define PICC_AUTHENT1A                  0x60               // 验证A密钥
  93. #define PICC_AUTHENT1B                  0x61               // 验证B密钥
  94. #define PICC_READ                       0x30               // 读块
  95. #define PICC_WRITE                      0xA0               // 写块
  96. #define PICC_DECREMENT                  0xC0               // 扣款
  97. #define PICC_INCREMENT                  0xC1               // 充值
  98. #define PICC_RESTORE                    0xC2               // 调块数据到缓冲区
  99. #define PICC_TRANSFER                   0xB0               // 保存缓冲区中数据
  100. #define PICC_HALT                       0x50               // 休眠
  101. int mfrc522_init(const char *spi_dev,
  102.     const char *cs_chip, unsigned char cs_line,
  103.     const char *rst_chip, unsigned char rst_line);
  104. void mfrc522_reset(void);
  105. void mfrc522_antenna_on(void);
  106. void mfrc522_antenna_off(void);
  107. void mfrc522_config_iso_type(unsigned char type);
  108. unsigned char mfrc522_to_card(unsigned char command, unsigned char *send_buf, unsigned char send_buf_len, unsigned char *recv_buf, unsigned int *recv_buf_len);
  109. unsigned char mfrc522_anticoll(unsigned char* ser_num);
  110. char mfrc522_pcd_request(unsigned char req_code, unsigned char *tag_type);
  111. void mfrc522_exit(void);
  112. #endif
复制代码
3.4.3.2、写寄存器和读寄存器

关于mfrc522基础利用函数,先实现写寄存器和读寄存器,如下:
  1. /* mfrc522.c */
  2. static void mfrc522_write_reg(unsigned char reg, unsigned char value)
  3. {
  4.     unsigned char send_buf[2];
  5.     // 根据上面对地址字节的要求:
  6.     // bit7为1代表读数据,为0代表写数据
  7.     // bit6-bit1表示寄存器地址(寄存器只用6个bit来表示)
  8.     // bit0默认为0
  9.     send_buf[0] = (reg << 1) & 0x7E;
  10.    
  11.     send_buf[1] = value;
  12.     spi_write_nbyte_data(mfrc522_spi, send_buf, sizeof(send_buf));
  13. }
  14. static unsigned char mfrc522_read_reg(unsigned char reg)
  15. {
  16.     unsigned char send_buf[2];
  17.     unsigned char recv_buf[2] = {0};
  18.     // 根据上面对地址字节的要求:
  19.     // bit7为1代表读数据,为0代表写数据
  20.     // bit6-bit1表示寄存器地址(寄存器只用6个bit来表示)
  21.     // bit0默认为0
  22.     send_buf[0] = ((reg << 1) & 0x7E ) | 0x80;
  23.     send_buf[1] = 0x00;
  24.     spi_write_and_read(mfrc522_spi, send_buf, sizeof(send_buf), recv_buf);
  25.     return recv_buf[1];
  26. }
复制代码
对上面两个函数再封装一下,得到如下:
  1. /* mfrc522.c */
  2. // 将指定寄存器中的指定位置1,其它位不变
  3. static void mfrc522_set_bit_mask(unsigned char reg, unsigned char mask)
  4. {
  5.     unsigned char temp;
  6.         temp = mfrc522_read_reg(reg);
  7.     mfrc522_write_reg(reg, temp | mask);
  8. }
  9. // 将指定寄存器中的指定位清0,其它位不变
  10. static void mfrc522_clr_bit_mask(unsigned char reg, unsigned char mask)
  11. {
  12.     unsigned char temp;
  13.         temp = mfrc522_read_reg(reg);
  14.     mfrc522_write_reg(reg, temp & (~mask));
  15. }
复制代码
3.4.3.3、复位引脚利用

下面编写mfrc522复位脚的利用函数。关于复位引脚,数据手册对其的解释如下:

  1. /* mfrc522.c */
  2. static void mfrc522_rst_enabled(void)
  3. {
  4.     gpiod_line_set_value(rst_gpioline, 0);
  5. }
  6. static void mfrc522_rst_disabled(void)
  7. {
  8.     gpiod_line_set_value(rst_gpioline, 1);
  9. }
复制代码
3.4.3.4、天线利用

下面是mfrc522打开天线与关闭天线的利用函数:
  1. /* mfrc522.c */
  2. // mfrc522打开天线
  3. void mfrc522_antenna_on(void)
  4. {
  5.     unsigned char temp;
  6.     temp = mfrc522_read_reg(TxControlReg);
  7.     if (!(temp & 0x03))
  8.         mfrc522_set_bit_mask(TxControlReg, 0x03);
  9. }
  10. // mfrc522关闭天线
  11. void mfrc522_antenna_off(void)
  12. {
  13.     mfrc522_clr_bit_mask(TxControlReg, 0x03);
  14. }
复制代码
3.4.3.5、初始化利用

下面是MFRC522的初始化和反初始化函数。分别先初始化spi、复位引脚:
  1. /* mfrc522.c */
  2. int mfrc522_init(const char *spi_dev,
  3.                     const char *cs_chip, unsigned char cs_line,
  4.                     const char *rst_chip, unsigned char rst_line)
  5. {
  6.     int ret;
  7.     if (!spi_dev)
  8.         return -1;
  9.     if (!cs_chip || !rst_chip)
  10.         return -1;
  11.     /* spi初始化 */
  12.     mfrc522_spi = spi_handle_alloc(spi_dev, SPIMODE0, S_1M, cs_chip, cs_line);
  13.     if (!mfrc522_spi)
  14.         return -1;
  15.     /* 复位脚初始化 */
  16.     rst_gpiochip = gpiod_chip_open(rst_chip);
  17.     if (rst_gpiochip == NULL)
  18.     {
  19.         printf("gpiod_chip_open failed!\n");
  20.         return -1;
  21.     }
  22.     rst_gpioline = gpiod_chip_get_line(rst_gpiochip, rst_line);
  23.     if (rst_gpioline == NULL)
  24.     {
  25.         printf("gpiod_chip_get_line failed!\n");
  26.         return -1;
  27.     }
  28.     ret = gpiod_line_request_output(rst_gpioline, "rst_gpioline", 1);
  29.     if (ret < 0)
  30.     {
  31.         printf("gpiod_line_request_output failed!\n");
  32.         return -1;
  33.     }
  34.     mfrc522_rst_disabled();
  35.     return 0;
  36. }
  37. // mfrc522反初始化
  38. void mfrc522_exit(void)
  39. {
  40.     if (mfrc522_spi)
  41.         spi_handle_free(mfrc522_spi);
  42.     if (rst_gpioline)
  43.         gpiod_line_release(rst_gpioline);
  44.     if (rst_gpiochip)
  45.         gpiod_chip_close(rst_gpiochip);
  46. }
复制代码
3.4.3.6、复位利用

下面是mfrc522的复位函数:
  1. /* mfrc522.c */
  2. void mfrc522_reset(void)
  3. {
  4.     mfrc522_rst_disabled();
  5.     usleep(1);
  6.     mfrc522_rst_enabled();                                  // 切断内部电流吸收,关闭振荡器,断开输入管脚与外部电路的连接
  7.     usleep(1);
  8.     mfrc522_rst_disabled();                                 // 上升沿启动内部复位阶段
  9.     usleep(1);
  10.     mfrc522_write_reg(CommandReg, PCD_RESETPHASE);          // 软复位
  11.    
  12.     while (mfrc522_read_reg(CommandReg) & 0x10)             // 等待mfrc522唤醒结束
  13.         ;
  14.         usleep(1);
  15.    
  16.     mfrc522_write_reg(ModeReg, 0x3D);                       // 定义发送和接收常用模式:和Mifare卡通讯,CRC初始值0x6363
  17.    
  18.     mfrc522_write_reg(TReloadRegL, 30);                     // 16位定时器低位   
  19.         mfrc522_write_reg(TReloadRegH, 0);                                // 16位定时器高位
  20.        
  21.     mfrc522_write_reg(TModeReg, 0x8D);                                    // 定义内部定时器的设置
  22.        
  23.     mfrc522_write_reg(TPrescalerReg, 0x3E);                        // 设置定时器分频系数
  24.        
  25.         mfrc522_write_reg(TxAutoReg, 0x40);                            // 调制发送信号为100%ASK
  26. }
复制代码
3.4.3.7、工作模式设置

下面是mfrc522设置工作模式的利用函数:
  1. /* mfrc522.c */
  2. // 设置工作模式
  3. void mfrc522_config_iso_type(unsigned char type)
  4. {
  5.     if (type == 'A')               // ISO14443_A
  6.     {
  7.         mfrc522_clr_bit_mask(Status2Reg, 0x08);
  8.         mfrc522_write_reg(ModeReg, 0x3D);         
  9.         mfrc522_write_reg(RxSelReg, 0x86);        
  10.         mfrc522_write_reg(RFCfgReg, 0x7F);         
  11.         mfrc522_write_reg(TReloadRegL, 30);        
  12.         mfrc522_write_reg(TReloadRegH, 0);
  13.         mfrc522_write_reg(TModeReg, 0x8D);
  14.         mfrc522_write_reg(TPrescalerReg, 0x3E);
  15.                
  16.                 usleep(2);
  17.                
  18.                 mfrc522_antenna_on();       //开天线
  19.    }         
  20. }
复制代码
3.4.3.8、和M1卡通信利用

下面是mfrc522和iso14443卡的通信函数:
  1. /* mfrc522.c */
  2. unsigned char mfrc522_to_card(unsigned char command, unsigned char *send_buf, unsigned char send_buf_len, unsigned char *recv_buf, unsigned int *recv_buf_len)
  3. {
  4.     unsigned char status = MI_ERR;
  5.         unsigned char irq_en = 0x00;
  6.         unsigned char wait_irq = 0x00;
  7.         unsigned char last_bits;
  8.         unsigned char n;
  9.         unsigned int i = 0;
  10.         switch (command)
  11.     {
  12.                 case PCD_AUTHENT:                                       // Mifare认证
  13.         {
  14.                         irq_en = 0x12;                                      // 允许错误中断请求ErrIEn  允许空闲中断IdleIEn
  15.                         wait_irq = 0x10;                                    // 认证寻卡等待时候 查询空闲中断标志位
  16.                         break;
  17.                 }
  18.                 case PCD_TRANSCEIVE:                                    // 接收发送 发送接收
  19.         {
  20.                         irq_en = 0x77;                                      // 允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
  21.                         wait_irq = 0x30;                                    // 寻卡等待时候 查询接收中断标志位与 空闲中断标志位
  22.                         break;
  23.                 }
  24.                 default:
  25.                     break;
  26.         }
  27.         mfrc522_write_reg(ComIEnReg, irq_en | 0x80);                // IRqInv置位管脚IRQ与Status1Reg的IRq位的值相反
  28.         mfrc522_clr_bit_mask(ComIrqReg, 0x80);                      // Set1该位清零时,CommIRqReg的屏蔽位清零
  29.         mfrc522_write_reg(CommandReg, PCD_IDLE);                    // 写空闲命令
  30.    
  31.     mfrc522_set_bit_mask(FIFOLevelReg, 0x80);                   // 置位FlushBuffer清除内部FIFO的读和写指针以及ErrReg的BufferOvfl标志位被清除
  32.         for (i = 0; i < send_buf_len; i++)
  33.         mfrc522_write_reg(FIFODataReg, send_buf[i]);            // 写数据进FIFOdata
  34.         mfrc522_write_reg(CommandReg, command);                     // 写命令
  35.         if (command == PCD_TRANSCEIVE)
  36.         mfrc522_set_bit_mask(BitFramingReg, 0x80);              // StartSend置位启动数据发送 该位与收发命令使用时才有效
  37.         do                                                          // 认证与寻卡等待时间
  38.     {
  39.                 n = mfrc522_read_reg(ComIrqReg);                        // 查询事件中断
  40.         usleep(1000);
  41.                 i++;
  42.         } while ((i != 25) && !(n & 0x01) && !(n & wait_irq));      
  43.         mfrc522_clr_bit_mask(BitFramingReg, 0x80);                            // 清理允许StartSend位                                                                                                               
  44.         if (i != 25)  
  45.     {
  46.                 if (!(mfrc522_read_reg(ErrorReg) & 0x1B))               // 读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
  47.         {
  48.                         status = MI_OK;
  49.                         if (n & irq_en & 0x01)                              // 是否发生定时器中断
  50.                 status = MI_NOTAGERR;
  51.                         if (command == PCD_TRANSCEIVE)                     
  52.             {
  53.                                 n = mfrc522_read_reg(FIFOLevelReg);             // 读FIFO中保存的字节数
  54.                                 last_bits = mfrc522_read_reg(ControlReg) & 0x07;// 最后接收到得字节的有效位数
  55.                                 if (last_bits)
  56.                     *recv_buf_len = (n-1) * 8 + last_bits;      // N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
  57.                 else
  58.                     *recv_buf_len = n*8;                        // 最后接收到的字节整个字节有效
  59.                                
  60.                 if (n == 0)
  61.                     n = 1;
  62.                                 if (n > MFRC522_MAX_LEN)
  63.                     n = MFRC522_MAX_LEN;
  64.                                 for (i = 0; i < n; i++)
  65.                     recv_buf[i] = mfrc522_read_reg(FIFODataReg); // 从FIFOdata读数据
  66.                         }
  67.                 }
  68.         else
  69.             status = MI_ERR;
  70.         }
  71.     mfrc522_set_bit_mask(ControlReg, 0x80);         
  72.     mfrc522_write_reg(CommandReg, PCD_IDLE);
  73.         return status;
  74. }
复制代码
3.4.3.9、寻卡利用

下面是mfrc522的寻卡函数:
  1. /* mfrc522.c */
  2. char mfrc522_pcd_request(unsigned char req_code, unsigned char *tag_type)
  3. {
  4.     char status;  
  5.     unsigned char com_buf[MFRC522_MAX_LEN];
  6.     unsigned int len;
  7.     mfrc522_clr_bit_mask(Status2Reg, 0x08);                 // 清理指示MIFARECyptol单元接通以及所有卡的数据通信被加密的情况
  8.     mfrc522_write_reg(BitFramingReg, 0x07);                 // 发送的最后一个字节的 七位
  9.     mfrc522_set_bit_mask(TxControlReg, 0x03);                    // TX1,TX2管脚的输出信号传递经发送调制的13.56的能量载波信号
  10.     com_buf[0] = req_code;                                            // 存入卡片命令字
  11.     status = mfrc522_to_card(PCD_TRANSCEIVE, com_buf, 1, com_buf, &len);        // 寻卡  
  12.     if ((status == MI_OK) && (len == 0x10))                        // 寻卡成功返回卡类型
  13.     {   
  14.         *tag_type = com_buf[0];
  15.         *(tag_type + 1) = com_buf[1];
  16.     }
  17.     else
  18.         status = MI_ERR;
  19.     return status;         
  20. }
复制代码
3.4.3.10、防冲撞利用

下面是mfrc522的防冲撞函数:
  1. /* mfrc522.c */
  2. // 防冲撞
  3. unsigned char mfrc522_anticoll(unsigned char *snr)
  4. {
  5.     char status;
  6.     uint8_t i, snr_check = 0;
  7.     uint8_t com_mfrc522_buf[MFRC522_MAX_LEN];
  8.     uint32_t len;
  9.    
  10.     mfrc522_clr_bit_mask(Status2Reg, 0x08);             // 清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
  11.     mfrc522_write_reg(BitFramingReg, 0x00);                    // 清理寄存器 停止收发
  12.     mfrc522_clr_bit_mask(CollReg, 0x80);                        // 清ValuesAfterColl所有接收的位在冲突后被清除          
  13.    
  14.     com_mfrc522_buf[0] = 0x93;                                // 卡片防冲突命令
  15.     com_mfrc522_buf[1] = 0x20;
  16.    
  17.     status = mfrc522_to_card(PCD_TRANSCEIVE, com_mfrc522_buf, 2, com_mfrc522_buf, &len);      // 与卡片通信
  18.     if (status == MI_OK)                                        // 通信成功
  19.     {
  20.         for (i = 0; i < 4; i++)
  21.         {
  22.             *(snr + i) = com_mfrc522_buf[i];            // 读出UID
  23.             snr_check ^= com_mfrc522_buf[i];
  24.         }
  25.         
  26.         if (snr_check != com_mfrc522_buf[i])
  27.             status = MI_ERR;                                     
  28.     }
  29.    
  30.     mfrc522_set_bit_mask(CollReg, 0x80);
  31.         
  32.     return status;
  33. }
复制代码
3.4.3.11、完备的mfrc522.c

好了,到目前为止,终于完成了mfrc522读IC卡 ID所必要的基础利用函数,目前完备的mfrc522.c文件如下:
  1. /* mfrc522.c */
  2. #include "mfrc522.h"
  3. static spi_handle_t *mfrc522_spi;
  4. static struct gpiod_chip *rst_gpiochip;        
  5. static struct gpiod_line *rst_gpioline;
  6. static void mfrc522_write_reg(unsigned char reg, unsigned char value)
  7. {
  8.     unsigned char send_buf[2];
  9.     send_buf[0] = (reg << 1) & 0x7E;
  10.     send_buf[1] = value;
  11.     spi_write_nbyte_data(mfrc522_spi, send_buf, sizeof(send_buf));
  12. }
  13. static unsigned char mfrc522_read_reg(unsigned char reg)
  14. {
  15.     unsigned char send_buf[2];
  16.     unsigned char recv_buf[2] = {0};
  17.     send_buf[0] = ((reg << 1) & 0x7E ) | 0x80;
  18.     send_buf[1] = 0x00;
  19.     spi_write_and_read(mfrc522_spi, send_buf, sizeof(send_buf), recv_buf);
  20.     return recv_buf[1];
  21. }
  22. // 将寄存器中指定的位置1
  23. static void mfrc522_set_bit_mask(unsigned char reg, unsigned char mask)
  24. {
  25.     unsigned char temp;
  26.         temp = mfrc522_read_reg(reg);
  27.     mfrc522_write_reg(reg, temp | mask);
  28. }
  29. // 将寄存器中指定的位清0
  30. static void mfrc522_clr_bit_mask(unsigned char reg, unsigned char mask)
  31. {
  32.     unsigned char temp;
  33.         temp = mfrc522_read_reg(reg);
  34.     mfrc522_write_reg(reg, temp & (~mask));
  35. }
  36. //
  37. static void mfrc522_rst_enabled(void)
  38. {
  39.     gpiod_line_set_value(rst_gpioline, 0);
  40. }
  41. static void mfrc522_rst_disabled(void)
  42. {
  43.     gpiod_line_set_value(rst_gpioline, 1);
  44. }
  45. // mfrc522复位
  46. void mfrc522_reset(void)
  47. {
  48.     mfrc522_rst_disabled();
  49.     usleep(1);
  50.     mfrc522_rst_enabled();                                  // 切断内部电流吸收,关闭振荡器,断开输入管脚与外部电路的连接
  51.     usleep(1);
  52.     mfrc522_rst_disabled();                                 // 上升沿启动内部复位阶段
  53.     usleep(1);
  54.     mfrc522_write_reg(CommandReg, PCD_RESETPHASE);          // 软复位
  55.    
  56.     while (mfrc522_read_reg(CommandReg) & 0x10)             // 等待mfrc522唤醒结束
  57.         ;
  58.         usleep(1);
  59.    
  60.     mfrc522_write_reg(ModeReg, 0x3D);                       // 定义发送和接收常用模式:和Mifare卡通讯,CRC初始值0x6363
  61.    
  62.     mfrc522_write_reg(TReloadRegL, 30);                     // 16位定时器低位   
  63.         mfrc522_write_reg(TReloadRegH, 0);                                // 16位定时器高位
  64.        
  65.     mfrc522_write_reg(TModeReg, 0x8D);                                    // 定义内部定时器的设置
  66.        
  67.     mfrc522_write_reg(TPrescalerReg, 0x3E);                        // 设置定时器分频系数
  68.        
  69.         mfrc522_write_reg(TxAutoReg, 0x40);                            // 调制发送信号为100%ASK
  70. }
  71. // mfrc522打开天线
  72. void mfrc522_antenna_on(void)
  73. {
  74.     unsigned char temp;
  75.     temp = mfrc522_read_reg(TxControlReg);
  76.     if (!(temp & 0x03))
  77.         mfrc522_set_bit_mask(TxControlReg, 0x03);
  78. }
  79. // mfrc522关闭天线
  80. void mfrc522_antenna_off(void)
  81. {
  82.     mfrc522_clr_bit_mask(TxControlReg, 0x03);
  83. }
  84. // 设置工作模式
  85. void mfrc522_config_iso_type(unsigned char type)
  86. {
  87.     if (type == 'A')               // ISO14443_A
  88.     {
  89.         mfrc522_clr_bit_mask(Status2Reg, 0x08);
  90.         mfrc522_write_reg(ModeReg, 0x3D);         
  91.         mfrc522_write_reg(RxSelReg, 0x86);        
  92.         mfrc522_write_reg(RFCfgReg, 0x7F);         
  93.         mfrc522_write_reg(TReloadRegL, 30);        
  94.         mfrc522_write_reg(TReloadRegH, 0);
  95.         mfrc522_write_reg(TModeReg, 0x8D);
  96.         mfrc522_write_reg(TPrescalerReg, 0x3E);
  97.                
  98.                 usleep(2);
  99.                
  100.                 mfrc522_antenna_on();       //开天线
  101.    }         
  102. }
  103. // 通过RC522和ISO14443卡通讯
  104. unsigned char mfrc522_to_card(unsigned char command, unsigned char *send_buf, unsigned char send_buf_len, unsigned char *recv_buf, unsigned int *recv_buf_len)
  105. {
  106.     unsigned char status = MI_ERR;
  107.         unsigned char irq_en = 0x00;
  108.         unsigned char wait_irq = 0x00;
  109.         unsigned char last_bits;
  110.         unsigned char n;
  111.         unsigned int i = 0;
  112.         switch (command)
  113.     {
  114.                 case PCD_AUTHENT:                                       // Mifare认证
  115.         {
  116.                         irq_en = 0x12;                                      // 允许错误中断请求ErrIEn  允许空闲中断IdleIEn
  117.                         wait_irq = 0x10;                                    // 认证寻卡等待时候 查询空闲中断标志位
  118.                         break;
  119.                 }
  120.                 case PCD_TRANSCEIVE:                                    // 接收发送 发送接收
  121.         {
  122.                         irq_en = 0x77;                                      // 允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
  123.                         wait_irq = 0x30;                                    // 寻卡等待时候 查询接收中断标志位与 空闲中断标志位
  124.                         break;
  125.                 }
  126.                 default:
  127.                     break;
  128.         }
  129.         mfrc522_write_reg(ComIEnReg, irq_en | 0x80);                // IRqInv置位管脚IRQ与Status1Reg的IRq位的值相反
  130.         mfrc522_clr_bit_mask(ComIrqReg, 0x80);                      // Set1该位清零时,CommIRqReg的屏蔽位清零
  131.         mfrc522_write_reg(CommandReg, PCD_IDLE);                    // 写空闲命令
  132.    
  133.     mfrc522_set_bit_mask(FIFOLevelReg, 0x80);                   // 置位FlushBuffer清除内部FIFO的读和写指针以及ErrReg的BufferOvfl标志位被清除
  134.         for (i = 0; i < send_buf_len; i++)
  135.         mfrc522_write_reg(FIFODataReg, send_buf[i]);            // 写数据进FIFOdata
  136.         mfrc522_write_reg(CommandReg, command);                     // 写命令
  137.        
  138.     if (command == PCD_TRANSCEIVE)
  139.         mfrc522_set_bit_mask(BitFramingReg, 0x80);              // StartSend置位启动数据发送 该位与收发命令使用时才有效
  140.         do                                                          // 认证与寻卡等待时间
  141.     {
  142.                 n = mfrc522_read_reg(ComIrqReg);                        // 查询事件中断
  143.         usleep(1000);
  144.                 i++;
  145.         } while ((i != 50) && !(n & 0x01) && !(n & wait_irq));      
  146.         mfrc522_clr_bit_mask(BitFramingReg, 0x80);                            // 清理允许StartSend位                                                                                                               
  147.         if (i != 50)  
  148.     {
  149.                 if (!(mfrc522_read_reg(ErrorReg) & 0x1B))               // 读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
  150.         {
  151.                         status = MI_OK;
  152.                         if (n & irq_en & 0x01)                              // 是否发生定时器中断
  153.                 status = MI_NOTAGERR;
  154.                         if (command == PCD_TRANSCEIVE)                     
  155.             {
  156.                                 n = mfrc522_read_reg(FIFOLevelReg);             // 读FIFO中保存的字节数
  157.                                 last_bits = mfrc522_read_reg(ControlReg) & 0x07;// 最后接收到得字节的有效位数
  158.                                 if (last_bits)
  159.                     *recv_buf_len = (n - 1) * 8 + last_bits;    // N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
  160.                 else
  161.                     *recv_buf_len = n * 8;                      // 最后接收到的字节整个字节有效
  162.                                
  163.                 if (n == 0)
  164.                     n = 1;
  165.                                 if (n > MFRC522_MAX_LEN)
  166.                     n = MFRC522_MAX_LEN;
  167.                                 for (i = 0; i < n; i++)
  168.                     recv_buf[i] = mfrc522_read_reg(FIFODataReg); // 从FIFOdata读数据
  169.                         }
  170.                 }
  171.         else
  172.             status = MI_ERR;
  173.         }
  174.     mfrc522_set_bit_mask(ControlReg, 0x80);         
  175.     mfrc522_write_reg(CommandReg, PCD_IDLE);
  176.         return status;
  177. }
  178. // 防冲撞
  179. unsigned char mfrc522_anticoll(unsigned char *snr)
  180. {
  181.     char status;
  182.     uint8_t i, snr_check = 0;
  183.     uint8_t com_mfrc522_buf[MFRC522_MAX_LEN];
  184.     uint32_t len;
  185.    
  186.     mfrc522_clr_bit_mask(Status2Reg, 0x08);             // 清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
  187.     mfrc522_write_reg(BitFramingReg, 0x00);                    // 清理寄存器 停止收发
  188.     mfrc522_clr_bit_mask(CollReg, 0x80);                        // 清ValuesAfterColl所有接收的位在冲突后被清除          
  189.    
  190.     com_mfrc522_buf[0] = 0x93;                                // 卡片防冲突命令
  191.     com_mfrc522_buf[1] = 0x20;
  192.    
  193.     status = mfrc522_to_card(PCD_TRANSCEIVE, com_mfrc522_buf, 2, com_mfrc522_buf, &len);      // 与卡片通信
  194.     if (status == MI_OK)                                        // 通信成功
  195.     {
  196.         for (i = 0; i < 4; i++)
  197.         {
  198.             *(snr + i) = com_mfrc522_buf[i];            // 读出UID
  199.             snr_check ^= com_mfrc522_buf[i];
  200.         }
  201.         
  202.         if (snr_check != com_mfrc522_buf[i])
  203.             status = MI_ERR;                                     
  204.     }
  205.    
  206.     mfrc522_set_bit_mask(CollReg, 0x80);
  207.         
  208.     return status;
  209. }
  210. // 寻卡
  211. char mfrc522_pcd_request(unsigned char req_code, unsigned char *tag_type)
  212. {
  213.     char status;  
  214.     unsigned char com_buf[MFRC522_MAX_LEN];
  215.     unsigned int len;
  216.     mfrc522_clr_bit_mask(Status2Reg, 0x08);                 // 清理指示MIFARECyptol单元接通以及所有卡的数据通信被加密的情况
  217.     mfrc522_write_reg(BitFramingReg, 0x07);                 // 发送的最后一个字节的 七位
  218.     mfrc522_set_bit_mask(TxControlReg, 0x03);                    // TX1,TX2管脚的输出信号传递经发送调制的13.56的能量载波信号
  219.     com_buf[0] = req_code;                                            // 存入卡片命令字
  220.     status = mfrc522_to_card(PCD_TRANSCEIVE, com_buf, 1, com_buf, &len);        // 寻卡  
  221.     if ((status == MI_OK) && (len == 0x10))                        // 寻卡成功返回卡类型
  222.     {   
  223.         *tag_type = com_buf[0];
  224.         *(tag_type + 1) = com_buf[1];
  225.     }
  226.     else
  227.         status = MI_ERR;
  228.     return status;         
  229. }
  230. // mfrc522初始化
  231. int mfrc522_init(const char *spi_dev,
  232.                     const char *cs_chip, unsigned char cs_line,
  233.                     const char *rst_chip, unsigned char rst_line)
  234. {
  235.     int ret;
  236.     if (!spi_dev)
  237.         return -1;
  238.     if (!cs_chip || !rst_chip)
  239.         return -1;
  240.     /* spi初始化 */
  241.     mfrc522_spi = spi_handle_alloc(spi_dev, SPIMODE0, S_1M, cs_chip, cs_line);
  242.     if (!mfrc522_spi)
  243.         return -1;
  244.     /* 复位脚初始化 */
  245.     rst_gpiochip = gpiod_chip_open(rst_chip);
  246.     if (rst_gpiochip == NULL)
  247.     {
  248.         printf("gpiod_chip_open failed!\n");
  249.         return -1;
  250.     }
  251.     rst_gpioline = gpiod_chip_get_line(rst_gpiochip, rst_line);
  252.     if (rst_gpioline == NULL)
  253.     {
  254.         printf("gpiod_chip_get_line failed!\n");
  255.         return -1;
  256.     }
  257.     ret = gpiod_line_request_output(rst_gpioline, "rst_gpioline", 1);
  258.     if (ret < 0)
  259.     {
  260.         printf("gpiod_line_request_output failed!\n");
  261.         return -1;
  262.     }
  263.     mfrc522_rst_disabled();
  264.     return 0;
  265. }
  266. // mfrc522反初始化
  267. void mfrc522_exit(void)
  268. {
  269.     if (mfrc522_spi)
  270.         spi_handle_free(mfrc522_spi);
  271.     if (rst_gpioline)
  272.         gpiod_line_release(rst_gpioline);
  273.     if (rst_gpiochip)
  274.         gpiod_chip_close(rst_gpiochip);
  275. }
复制代码
3.5、测试

下面编写一个main.c用于读取ic卡的id:
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <fcntl.h>
  4. #include <stdlib.h>
  5. #include <pthread.h>
  6. #include <gpiod.h>
  7. #include <math.h>
  8. #include "mfrc522.h"
  9. int main()
  10. {
  11.     unsigned char id[4];
  12.     unsigned char status;
  13.     int ret;
  14.     ret = mfrc522_init("/dev/spidev3.0",
  15.                        "/dev/gpiochip6", 11,        // cs片选脚
  16.                        "/dev/gpiochip6", 10);        // rst复位脚
  17.     if (ret != 0)
  18.     {
  19.         printf("mfrc522_init fialed!\n");
  20.         return -1;
  21.     }
  22.     mfrc522_reset();                                        // mfrc522复位
  23.     mfrc522_config_iso_type('A');                // 设置模式
  24.     while(1)
  25.     {
  26.         status = mfrc522_pcd_request(PICC_REQALL, id);
  27.         if (status != MI_OK)
  28.             printf("request card fialed!\n");
  29.         else
  30.             break;
  31.         sleep(0.1);   
  32.     }
  33.     if (status == MI_OK)
  34.     {
  35.         printf("request card successfully!\n");
  36.         if (mfrc522_anticoll(id) == MI_OK)
  37.             printf("card uid = %02X%02X%02X%02X\n", id[0], id[1], id[2], id[3]);
  38.     }
  39.     mfrc522_exit();
  40.    
  41.     return 0;
  42. }
复制代码
3.5.1、编译

假如利用buildroot系统,必要交叉编译。我这利用ubuntu系统,直接gcc编译。执行如下命令举行编译:
  1. gcc -o build main.c spi.c mfrc522.c -lgpiod
复制代码
3.5.2、测试

执行如下命令测试:
  1. sudo ./build
复制代码

和手机“NFC工具”APP读取的同等:

4、IC-S50

如今开始介绍S50芯片。
4.1、存储结构

上面介绍过,S50芯片内部有一个eeprom是用来存储数据的。容量为8Kbit,即1024Byte。分为16个扇区,即每个扇区占64Byte。每个扇区又由4个块(块0、块1、块2、块3)组成,即每个块占16Byte。如下图所示:



  • 第0扇区的块0,它用于存放厂商代码,已经固化,不可更改。
  • 每个扇区的块0、块1、块2为数据块。可以用来存放数据。数据块有两种应用:

    • 用作一般的数据保存,可以举行读、写利用。
    • 用作数据值,可以举行初始化、加值、减值、读值利用。

  • 每个扇区的块3为尾块,存储了该扇区的访问控制信息和密钥,具体结构如下:

尾块的16字节分配如下:


  • 字节0-5:存储Key A(密钥A),用于本扇区的访问控制。
  • 字节6-9:存储访问控制位(Access Bits),用于界说该扇区中各块的访问权限。
  • 字节10-15:存储Key B(密钥B),用于本扇区的访问控制。假如Key B未利用,这些字节可以作为普通数据存储。
这里先小结一下,总的来说就是,M1卡有16个扇区,每个扇区有4个块。我们可以通过mfrc522读卡器来访问M1卡的任意一个扇区中的任意一个块。但每个块的访问是有限制条件的,好比这个块不能读也不能写,这个块可以读但不能写等等。所以我们得清晰的知道每个块有怎样的限制。
假设如今想访问扇区1的尾块(即块3)。上面介绍过,每个扇区的尾块是用来存储KeyA、KeyB和访问控制位的,那如今想访问尾块,分析我们是想要读取或修改密码与访问控制位。所以,得看看访问尾块时,有着什么样的权限:

条件真值表由C1X3、C2X3、C3X3构成(X为某个扇区的编号。X后面的数字为块号,这里是3,代表块3),它们的值可以在尾块的Byte6-Byte9找到:

如C1X3在byte7的bit7,C2X3在byte8的bit3,C3X3在byte8的bit7。假设C1X3、C2X3、C3X3的值分别为100时,会有如下意思:


  • Key A:不可读;验证Key B后可写。
  • 控制信息:验证Key A或Key B后可读;不可写。
  • Key B:不可读;验证Key B后可写。

上面介绍的是尾块的利用权限。如今再看块0~块2的利用权限:

假设如今要访问扇区2的块1。那么就要知道C121、C221、C321的值:

如C121在byte7的bit5,C221在byte8的bit1,C321在byte8的bit5。假设C121、C221、C321的值分别为100时,会有如下意思:


  • 验证Key A或Key B后可读。
  • 验证Key B后可写。
  • 不可举行加值、减值利用。

4.2、M1卡与读卡器的通信流程

下图展示了M1卡与读卡器的通信流程:



  • 复位应答:M1射频卡的通讯协媾和通讯波特率是界说好的,当有卡片进入读写器的利用范围时读写器以特定的协议与它通讯,从而确定该卡是否为M1射频卡,即验证卡片的卡型。
  • 防冲突机制:当有多张卡进入读写器利用范围时,防冲突机制会从其中选择一张举行利用,未选中的则处于空闲模式等待下一次选卡,该过程会返回被选卡的序列号。
  • 选择卡片:选择被选中的卡的序列号,并同时返回卡的容量代码。
  • 三次相互验证:选定要处置惩罚的卡片之后,读写器就确定要访问的扇区号,并对该扇区密码举行密码校验,在三次相互认证之后就可以通过加密流举行通讯。(在选择另一扇区时,则必须举行另一扇区密码校验。)
其中复位应答和防冲突机制这两部分在上面已经移植完了。后续会继续移植剩下两部分。
4.3、对数据块的利用

我们将继续完成如下利用函数:


  • 读(Read):读一个块;
  • 写(Write):写一个块;
  • 加(Increment):对数值块举行加值;
  • 减(Decrement):对数值块举行减值;
  • 存储(Restore):将块中的内容存到数据寄存器中;
  • 传输(Transfer):将数据寄存器中的内容写入块中;
  • 中断(Halt):将卡置于暂停工作状态;
5、美满剩余的利用函数

下面将直接给出完备的mfrc522.h和mfrc522.c。
5.1、mfrc522.h

  1. #ifndef MFRC522_H
  2. #define MFRC522_H
  3. #include <gpiod.h>
  4. #include "spi.h"
  5. #define MI_OK                                        0
  6. #define MI_NOTAGERR                                1
  7. #define MI_ERR                                        2
  8. #define MFRC522_MAX_LEN                 18
  9. /* MFRC522 REG */
  10. // Page 0 - Command and Status
  11. #define        RFU00                                         0x00   
  12. #define        CommandReg                                    0x01   
  13. #define        ComIEnReg                                     0x02   
  14. #define        DivlEnReg                                     0x03   
  15. #define        ComIrqReg                                     0x04   
  16. #define        DivIrqReg                                     0x05
  17. #define        ErrorReg                                      0x06   
  18. #define        Status1Reg                                    0x07   
  19. #define        Status2Reg                                    0x08   
  20. #define        FIFODataReg                                   0x09
  21. #define        FIFOLevelReg                                  0x0A
  22. #define        WaterLevelReg                                 0x0B
  23. #define        ControlReg                                    0x0C
  24. #define        BitFramingReg                                 0x0D
  25. #define        CollReg                                       0x0E
  26. #define        RFU0F                                         0x0F
  27. // Page 1 - Command
  28. #define        RFU10                                         0x10
  29. #define        ModeReg                                       0x11
  30. #define        TxModeReg                                     0x12
  31. #define        RxModeReg                                     0x13
  32. #define        TxControlReg                                  0x14
  33. #define        TxAutoReg                                     0x15
  34. #define        TxSelReg                                      0x16
  35. #define        RxSelReg                                      0x17
  36. #define        RxThresholdReg                                0x18
  37. #define        DemodReg                                      0x19
  38. #define        RFU1A                                         0x1A
  39. #define        RFU1B                                         0x1B
  40. #define        MifareReg                                     0x1C
  41. #define        RFU1D                                         0x1D
  42. #define        RFU1E                                         0x1E
  43. #define        SerialSpeedReg                                0x1F
  44. // Page 2 - CFG
  45. #define        RFU20                                         0x20  
  46. #define        CRCResultRegM                                 0x21
  47. #define        CRCResultRegL                                 0x22
  48. #define        RFU23                                         0x23
  49. #define        ModWidthReg                                   0x24
  50. #define        RFU25                                         0x25
  51. #define        RFCfgReg                                      0x26
  52. #define        GsNReg                                        0x27
  53. #define        CWGsCfgReg                                    0x28
  54. #define        ModGsCfgReg                                   0x29
  55. #define        TModeReg                                      0x2A
  56. #define        TPrescalerReg                                 0x2B
  57. #define        TReloadRegH                                   0x2C
  58. #define        TReloadRegL                                   0x2D
  59. #define        TCounterValueRegH                             0x2E
  60. #define        TCounterValueRegL                             0x2F
  61. // Page 3 - TestRegister
  62. #define        RFU30                                         0x30
  63. #define        TestSel1Reg                                   0x31
  64. #define        TestSel2Reg                                   0x32
  65. #define        TestPinEnReg                                  0x33
  66. #define        TestPinValueReg                               0x34
  67. #define        TestBusReg                                    0x35
  68. #define        AutoTestReg                                   0x36
  69. #define        VersionReg                                    0x37
  70. #define        AnalogTestReg                                 0x38
  71. #define        TestDAC1Reg                                   0x39  
  72. #define        TestDAC2Reg                                   0x3A   
  73. #define        TestADCReg                                    0x3B   
  74. #define        RFU3C                                         0x3C   
  75. #define        RFU3D                                         0x3D   
  76. #define        RFU3E                                         0x3E   
  77. #define        RFU3F                                                            0x3F
  78. /* MFRC522 CMD */
  79. #define PCD_IDLE                        0x00               // 取消当前命令
  80. #define PCD_AUTHENT                     0x0E               // 验证密钥
  81. #define PCD_RECEIVE                     0x08               // 接收数据
  82. #define PCD_TRANSMIT                    0x04               // 发送数据
  83. #define PCD_TRANSCEIVE                  0x0C               // 发送并接收数据
  84. #define PCD_RESETPHASE                  0x0F               // 复位
  85. #define PCD_CALCCRC                     0x03               // CRC计算
  86. /* IC-S50 CMD */
  87. #define PICC_REQIDL                     0x26               // 寻天线区内未进入休眠状态
  88. #define PICC_REQALL                     0x52               // 寻天线区内全部卡
  89. #define PICC_ANTICOLL1                  0x93               // 防冲撞
  90. #define PICC_ANTICOLL2                  0x95               // 防冲撞
  91. #define PICC_AUTHENT1A                  0x60               // 验证A密钥
  92. #define PICC_AUTHENT1B                  0x61               // 验证B密钥
  93. #define PICC_READ                       0x30               // 读块
  94. #define PICC_WRITE                      0xA0               // 写块
  95. #define PICC_DECREMENT                  0xC0               // 扣款
  96. #define PICC_INCREMENT                  0xC1               // 充值
  97. #define PICC_RESTORE                    0xC2               // 调块数据到缓冲区
  98. #define PICC_TRANSFER                   0xB0               // 保存缓冲区中数据
  99. #define PICC_HALT                       0x50               // 休眠
  100. void mfrc522_reset(void);
  101. void mfrc522_config_iso_type(uint8_t type);
  102. uint8_t mfrc522_request(uint8_t req_code, uint8_t *tag_type);
  103. uint8_t mfrc522_anticoll(uint8_t *snr);
  104. uint8_t mfrc522_select(uint8_t *snr);
  105. uint8_t mfrc522_auth_state(uint8_t auth_mode, uint8_t addr, uint8_t *key, uint8_t *snr);
  106. uint8_t mfrc522_read(uint8_t addr, uint8_t *data);
  107. uint8_t mfrc522_write(uint8_t addr, uint8_t *data);
  108. uint8_t mfrc522_write_string(uint8_t addr, uint8_t *data);
  109. uint8_t mfrc522_read_string(uint8_t addr, uint8_t *data);
  110. uint8_t mfrc522_halt(void);
  111. uint8_t mfrc522_change_keya(uint8_t addr, uint8_t *keya);
  112. uint8_t mfrc522_write_data_block(uint8_t addr, uint8_t *data, uint8_t len);
  113. uint8_t mfrc522_read_data_block(uint8_t addr, uint8_t *data);
  114. uint8_t mfrc522_write_amount(uint8_t addr, uint32_t data);
  115. uint8_t mfrc522_read_amount(uint8_t addr, uint32_t *data);
  116. int mfrc522_init(const char *spi_dev, const char *cs_chip, uint8_t cs_line, const char *rst_chip, uint8_t rst_line);
  117. void mfrc522_exit(void);
  118. #endif
复制代码
5.2、mfrc522.c

  1. /* mfrc522.c */
  2. #include "mfrc522.h"
  3. static spi_handle_t *mfrc522_spi;
  4. static struct gpiod_chip *rst_gpiochip;
  5. static struct gpiod_line *rst_gpioline;
  6. /**
  7. * @brief 写入MFRC522寄存器
  8. * @param reg 寄存器地址
  9. * @param value 写入的值
  10. */
  11. static void mfrc522_write_reg(uint8_t reg, uint8_t value)
  12. {
  13.     uint8_t send_buf[2];
  14.     send_buf[0] = (reg << 1) & 0x7E;
  15.     send_buf[1] = value;
  16.     spi_write_nbyte_data(mfrc522_spi, send_buf, sizeof(send_buf));
  17. }
  18. /**
  19. * @brief 读取MFRC522寄存器
  20. * @param reg 寄存器地址
  21. * @return 读取的值
  22. */
  23. static uint8_t mfrc522_read_reg(uint8_t reg)
  24. {
  25.     uint8_t send_buf[2];
  26.     uint8_t recv_buf[2] = {0};
  27.     send_buf[0] = ((reg << 1) & 0x7E) | 0x80;
  28.     send_buf[1] = 0x00;
  29.     spi_write_and_read(mfrc522_spi, send_buf, sizeof(send_buf), recv_buf);
  30.     return recv_buf[1];
  31. }
  32. /**
  33. * @brief 将寄存器中指定的位置1
  34. * @param reg 寄存器地址
  35. * @param mask 要设置的位掩码
  36. */
  37. static void mfrc522_set_bit_mask(uint8_t reg, uint8_t mask)
  38. {
  39.     uint8_t temp;
  40.     temp = mfrc522_read_reg(reg);
  41.     mfrc522_write_reg(reg, temp | mask);
  42. }
  43. /**
  44. * @brief 将寄存器中指定的位清0
  45. * @param reg 寄存器地址
  46. * @param mask 要清除的位掩码
  47. */
  48. static void mfrc522_clr_bit_mask(uint8_t reg, uint8_t mask)
  49. {
  50.     uint8_t temp;
  51.     temp = mfrc522_read_reg(reg);
  52.     mfrc522_write_reg(reg, temp & (~mask));
  53. }
  54. /**
  55. * @brief 使能复位引脚
  56. */
  57. static void mfrc522_rst_enabled(void)
  58. {
  59.     gpiod_line_set_value(rst_gpioline, 0);
  60. }
  61. /**
  62. * @brief 禁用复位引脚
  63. */
  64. static void mfrc522_rst_disabled(void)
  65. {
  66.     gpiod_line_set_value(rst_gpioline, 1);
  67. }
  68. /**
  69. * @brief 复位MFRC522
  70. */
  71. void mfrc522_reset(void)
  72. {
  73.     mfrc522_rst_disabled();
  74.     usleep(1);
  75.     mfrc522_rst_enabled(); // 切断内部电流吸收,关闭振荡器,断开输入管脚与外部电路的连接
  76.     usleep(1);
  77.     mfrc522_rst_disabled(); // 上升沿启动内部复位阶段
  78.     usleep(1);
  79.     mfrc522_write_reg(CommandReg, PCD_RESETPHASE); // 软复位
  80.     while (mfrc522_read_reg(CommandReg) & 0x10) // 等待MFRC522唤醒结束
  81.         ;
  82.     usleep(1);
  83.     mfrc522_write_reg(ModeReg, 0x3D); // 定义发送和接收常用模式:和Mifare卡通讯,CRC初始值0x6363
  84.     mfrc522_write_reg(TReloadRegL, 30); // 16位定时器低位
  85.     mfrc522_write_reg(TReloadRegH, 0);  // 16位定时器高位
  86.     mfrc522_write_reg(TModeReg, 0x8D); // 定义内部定时器的设置
  87.     mfrc522_write_reg(TPrescalerReg, 0x3E); // 设置定时器分频系数
  88.     mfrc522_write_reg(TxAutoReg, 0x40); // 调制发送信号为100%ASK
  89. }
  90. /**
  91. * @brief 打开天线
  92. */
  93. static void mfrc522_antenna_on(void)
  94. {
  95.     uint8_t temp;
  96.     temp = mfrc522_read_reg(TxControlReg);
  97.     if (!(temp & 0x03))
  98.         mfrc522_set_bit_mask(TxControlReg, 0x03);
  99. }
  100. /**
  101. * @brief 关闭天线
  102. */
  103. static void mfrc522_antenna_off(void)
  104. {
  105.     mfrc522_clr_bit_mask(TxControlReg, 0x03);
  106. }
  107. /**
  108. * @brief 配置ISO类型
  109. * @param type ISO类型('A' 或 'B')
  110. */
  111. void mfrc522_config_iso_type(uint8_t type)
  112. {
  113.     if (type == 'A') // ISO14443_A
  114.     {
  115.         mfrc522_clr_bit_mask(Status2Reg, 0x08);
  116.         mfrc522_write_reg(ModeReg, 0x3D);
  117.         mfrc522_write_reg(RxSelReg, 0x86);
  118.         mfrc522_write_reg(RFCfgReg, 0x7F);
  119.         mfrc522_write_reg(TReloadRegL, 30);
  120.         mfrc522_write_reg(TReloadRegH, 0);
  121.         mfrc522_write_reg(TModeReg, 0x8D);
  122.         mfrc522_write_reg(TPrescalerReg, 0x3E);
  123.         usleep(2);
  124.         mfrc522_antenna_on(); // 开天线
  125.     }
  126. }
  127. /**
  128. * @brief 通过MFRC522与ISO14443卡通信
  129. * @param command 命令
  130. * @param send_buf 发送缓冲区
  131. * @param send_buf_len 发送缓冲区长度
  132. * @param recv_buf 接收缓冲区
  133. * @param recv_buf_len 接收缓冲区长度
  134. * @return 状态码
  135. */
  136. static uint8_t mfrc522_to_card(uint8_t command, uint8_t *send_buf, uint8_t send_buf_len, uint8_t *recv_buf, uint32_t *recv_buf_len)
  137. {
  138.     uint8_t status = MI_ERR;
  139.     uint8_t irq_en = 0x00;
  140.     uint8_t wait_irq = 0x00;
  141.     uint8_t last_bits;
  142.     uint8_t n;
  143.     uint32_t i = 0;
  144.     switch (command)
  145.     {
  146.     case PCD_AUTHENT: // Mifare认证
  147.     {
  148.         irq_en = 0x12;   // 允许错误中断请求ErrIEn  允许空闲中断IdleIEn
  149.         wait_irq = 0x10; // 认证寻卡等待时候 查询空闲中断标志位
  150.         break;
  151.     }
  152.     case PCD_TRANSCEIVE: // 接收发送 发送接收
  153.     {
  154.         irq_en = 0x77;   // 允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
  155.         wait_irq = 0x30; // 寻卡等待时候 查询接收中断标志位与 空闲中断标志位
  156.         break;
  157.     }
  158.     default:
  159.         break;
  160.     }
  161.     mfrc522_write_reg(ComIEnReg, irq_en | 0x80); // IRqInv置位管脚IRQ与Status1Reg的IRq位的值相反
  162.     mfrc522_clr_bit_mask(ComIrqReg, 0x80);       // Set1该位清零时,CommIRqReg的屏蔽位清零
  163.     mfrc522_write_reg(CommandReg, PCD_IDLE);     // 写空闲命令
  164.     mfrc522_set_bit_mask(FIFOLevelReg, 0x80); // 置位FlushBuffer清除内部FIFO的读和写指针以及ErrReg的BufferOvfl标志位被清除
  165.     for (i = 0; i < send_buf_len; i++)
  166.         mfrc522_write_reg(FIFODataReg, send_buf[i]); // 写数据进FIFOdata
  167.     mfrc522_write_reg(CommandReg, command); // 写命令
  168.     if (command == PCD_TRANSCEIVE)
  169.         mfrc522_set_bit_mask(BitFramingReg, 0x80); // StartSend置位启动数据发送 该位与收发命令使用时才有效
  170.     do // 认证与寻卡等待时间
  171.     {
  172.         n = mfrc522_read_reg(ComIrqReg); // 查询事件中断
  173.         usleep(1000);
  174.         i++;
  175.     } while ((i != 25) && !(n & 0x01) && !(n & wait_irq));
  176.     mfrc522_clr_bit_mask(BitFramingReg, 0x80); // 清理允许StartSend位
  177.     if (i != 25)
  178.     {
  179.         if (!(mfrc522_read_reg(ErrorReg) & 0x1B)) // 读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
  180.         {
  181.             status = MI_OK;
  182.             if (n & irq_en & 0x01) // 是否发生定时器中断
  183.                 status = MI_NOTAGERR;
  184.             if (command == PCD_TRANSCEIVE)
  185.             {
  186.                 n = mfrc522_read_reg(FIFOLevelReg);              // 读FIFO中保存的字节数
  187.                 last_bits = mfrc522_read_reg(ControlReg) & 0x07; // 最后接收到得字节的有效位数
  188.                 if (last_bits)
  189.                     *recv_buf_len = (n - 1) * 8 + last_bits; // N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
  190.                 else
  191.                     *recv_buf_len = n * 8; // 最后接收到的字节整个字节有效
  192.                 if (n == 0)
  193.                     n = 1;
  194.                 if (n > MFRC522_MAX_LEN)
  195.                     n = MFRC522_MAX_LEN;
  196.                 for (i = 0; i < n; i++)
  197.                     recv_buf[i] = mfrc522_read_reg(FIFODataReg); // 从FIFOdata读数据
  198.             }
  199.         }
  200.         else
  201.             status = MI_ERR;
  202.     }
  203.     mfrc522_set_bit_mask(ControlReg, 0x80);
  204.     mfrc522_write_reg(CommandReg, PCD_IDLE);
  205.     return status;
  206. }
  207. /**
  208. * @brief 防冲撞
  209. * @param snr 卡片序列号
  210. * @return 状态码
  211. */
  212. uint8_t mfrc522_anticoll(uint8_t *snr)
  213. {
  214.     uint8_t status;
  215.     uint8_t i, snr_check = 0;
  216.     uint8_t com_buf[MFRC522_MAX_LEN];
  217.     uint32_t len;
  218.     mfrc522_clr_bit_mask(Status2Reg, 0x08); // 清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
  219.     mfrc522_write_reg(BitFramingReg, 0x00); // 清理寄存器 停止收发
  220.     mfrc522_clr_bit_mask(CollReg, 0x80);    // 清ValuesAfterColl所有接收的位在冲突后被清除
  221.     com_buf[0] = 0x93; // 卡片防冲突命令
  222.     com_buf[1] = 0x20;
  223.     status = mfrc522_to_card(PCD_TRANSCEIVE, com_buf, 2, com_buf, &len); // 与卡片通信
  224.     if (status == MI_OK) // 通信成功
  225.     {
  226.         for (i = 0; i < 4; i++)
  227.         {
  228.             *(snr + i) = com_buf[i]; // 读出UID
  229.             snr_check ^= com_buf[i];
  230.         }
  231.         if (snr_check != com_buf[i])
  232.             status = MI_ERR;
  233.     }
  234.     mfrc522_set_bit_mask(CollReg, 0x80);
  235.     return status;
  236. }
  237. /**
  238. * @brief 寻卡
  239. * @param req_code 寻卡命令
  240. * @param tag_type 卡片类型
  241. * @return 状态码
  242. */
  243. uint8_t mfrc522_request(uint8_t req_code, uint8_t *tag_type)
  244. {
  245.     uint8_t status;
  246.     uint8_t com_buf[MFRC522_MAX_LEN];
  247.     uint32_t len;
  248.     mfrc522_clr_bit_mask(Status2Reg, 0x08);   // 清理指示MIFARECyptol单元接通以及所有卡的数据通信被加密的情况
  249.     mfrc522_write_reg(BitFramingReg, 0x07);   // 发送的最后一个字节的 七位
  250.     mfrc522_set_bit_mask(TxControlReg, 0x03); // TX1,TX2管脚的输出信号传递经发送调制的13.56的能量载波信号
  251.     com_buf[0] = req_code; // 存入卡片命令字
  252.     status = mfrc522_to_card(PCD_TRANSCEIVE, com_buf, 1, com_buf, &len); // 寻卡
  253.     if ((status == MI_OK) && (len == 0x10)) // 寻卡成功返回卡类型
  254.     {
  255.         *tag_type = com_buf[0];
  256.         *(tag_type + 1) = com_buf[1];
  257.     }
  258.     else
  259.         status = MI_ERR;
  260.     return status;
  261. }
  262. /**
  263. * @brief CRC校验
  264. * @param in_data 输入数据
  265. * @param len 数据长度
  266. * @param out_data 输出数据
  267. */
  268. static void mfrc522_calculate_crc(uint8_t *in_data, uint8_t len, uint8_t *out_data)
  269. {
  270.     uint8_t i, n;
  271.     mfrc522_clr_bit_mask(DivIrqReg, 0x04); // CRCIrq = 0
  272.     mfrc522_write_reg(CommandReg, PCD_IDLE);
  273.     mfrc522_set_bit_mask(FIFOLevelReg, 0x80); // Clear the FIFO pointer
  274.     // Writing data to the FIFO
  275.     for (i = 0; i < len; i++)
  276.         mfrc522_write_reg(FIFODataReg, *(in_data + i));
  277.     mfrc522_write_reg(CommandReg, PCD_CALCCRC);
  278.     // Wait CRC calculation is complete
  279.     i = 0xFF;
  280.     do
  281.     {
  282.         n = mfrc522_read_reg(DivIrqReg);
  283.         i--;
  284.     } while ((i != 0) && !(n & 0x04)); // CRCIrq = 1
  285.     // Read CRC calculation result
  286.     out_data[0] = mfrc522_read_reg(CRCResultRegL);
  287.     out_data[1] = mfrc522_read_reg(CRCResultRegM);
  288. }
  289. /**
  290. * @brief 选定卡片
  291. * @param snr 卡片序列号
  292. * @return 状态码
  293. */
  294. uint8_t mfrc522_select(uint8_t *snr)
  295. {
  296.     uint8_t status;
  297.     uint8_t i;
  298.     uint8_t com_buf[MFRC522_MAX_LEN];
  299.     uint32_t len;
  300.     com_buf[0] = PICC_ANTICOLL1;
  301.     com_buf[1] = 0x70;
  302.     com_buf[6] = 0;
  303.     for (i = 0; i < 4; i++)
  304.     {
  305.         com_buf[i + 2] = *(snr + i);
  306.         com_buf[6] ^= *(snr + i);
  307.     }
  308.     mfrc522_calculate_crc(com_buf, 7, &com_buf[7]);
  309.     mfrc522_clr_bit_mask(Status2Reg, 0x08);
  310.     status = mfrc522_to_card(PCD_TRANSCEIVE, com_buf, 9, com_buf, &len);
  311.     if ((status == MI_OK) && (len == 0x18))
  312.         status = MI_OK;
  313.     else
  314.         status = MI_ERR;
  315.     return status;
  316. }
  317. /**
  318. * @brief 验证卡片密码
  319. * @param auth_mode 认证模式
  320. * @param addr 块地址
  321. * @param key 密钥
  322. * @param snr 卡片序列号
  323. * @return 状态码
  324. */
  325. uint8_t mfrc522_auth_state(uint8_t auth_mode, uint8_t addr, uint8_t *key, uint8_t *snr)
  326. {
  327.     uint8_t status;
  328.     uint8_t i, com_buf[MFRC522_MAX_LEN];
  329.     uint32_t len;
  330.     com_buf[0] = auth_mode;
  331.     com_buf[1] = addr;
  332.     for (i = 0; i < 6; i++)
  333.         com_buf[i + 2] = *(key + i);
  334.     for (i = 0; i < 6; i++)
  335.         com_buf[i + 8] = *(snr + i);
  336.     status = mfrc522_to_card(PCD_AUTHENT, com_buf, 12, com_buf, &len);
  337.     if ((status != MI_OK) || (!(mfrc522_read_reg(Status2Reg) & 0x08)))
  338.         status = MI_ERR;
  339.     return status;
  340. }
  341. /**
  342. * @brief 写数据到M1卡一块
  343. * @param addr 块地址
  344. * @param data 写入的数据
  345. * @return 状态码
  346. */
  347. uint8_t mfrc522_write(uint8_t addr, uint8_t *data)
  348. {
  349.     uint8_t status;
  350.     uint8_t i, com_buf[MFRC522_MAX_LEN];
  351.     uint32_t len;
  352.     com_buf[0] = PICC_WRITE;
  353.     com_buf[1] = addr;
  354.     mfrc522_calculate_crc(com_buf, 2, &com_buf[2]);
  355.     status = mfrc522_to_card(PCD_TRANSCEIVE, com_buf, 4, com_buf, &len);
  356.     if ((status != MI_OK) || (len != 4) || ((com_buf[0] & 0x0F) != 0x0A))
  357.         status = MI_ERR;
  358.     if (status == MI_OK)
  359.     {
  360.         for (i = 0; i < 16; i++)
  361.             com_buf[i] = *(data + i);
  362.         mfrc522_calculate_crc(com_buf, 16, &com_buf[16]);
  363.         status = mfrc522_to_card(PCD_TRANSCEIVE, com_buf, 18, com_buf, &len);
  364.         if ((status != MI_OK) || (len != 4) || ((com_buf[0] & 0x0F) != 0x0A))
  365.             status = MI_ERR;
  366.     }
  367.     return status;
  368. }
  369. /**
  370. * @brief 读取M1卡一块数据
  371. * @param addr 块地址
  372. * @param data 读出的数据
  373. * @return 状态码
  374. */
  375. uint8_t mfrc522_read(uint8_t addr, uint8_t *data)
  376. {
  377.     uint8_t status;
  378.     uint8_t i, com_buf[MFRC522_MAX_LEN];
  379.     uint32_t len;
  380.     com_buf[0] = PICC_READ;
  381.     com_buf[1] = addr;
  382.     mfrc522_calculate_crc(com_buf, 2, &com_buf[2]);
  383.     status = mfrc522_to_card(PCD_TRANSCEIVE, com_buf, 4, com_buf, &len);
  384.     if ((status == MI_OK) && (len == 0x90))
  385.     {
  386.         for (i = 0; i < 16; i++)
  387.             *(data + i) = com_buf[i];
  388.     }
  389.     else
  390.         status = MI_ERR;
  391.     return status;
  392. }
  393. /**
  394. * @brief 判断 addr 是否数据块
  395. * @param addr 块地址
  396. * @return 返回值 1:是数据块;0:不是数据块
  397. */
  398. static uint8_t mfrc522_is_data_block(uint8_t addr)
  399. {
  400.     if (addr == 0)
  401.     {
  402.         printf("第0扇区的块0不可更改,不应对其进行操作\r\n");
  403.         return 0;
  404.     }
  405.     /* 如果是数据块(不包含数据块0) */
  406.     if ((addr < 64) && (((addr + 1) % 4) != 0))
  407.     {
  408.         return 1;
  409.     }
  410.     printf("块地址不是指向数据块\r\n");
  411.     return 0;
  412. }
  413. /**
  414. * @brief 写 data 字符串到M1卡中的数据块
  415. * @param addr 数据块地址
  416. * @param data 写入的数据
  417. * @return 状态码
  418. */
  419. uint8_t mfrc522_write_string(uint8_t addr, uint8_t *data)
  420. {
  421.     /* 如果是数据块(不包含数据块0),则写入 */
  422.     if (mfrc522_is_data_block(addr))
  423.     {
  424.         return mfrc522_write(addr, data);
  425.     }
  426.     return MI_ERR;
  427. }
  428. /**
  429. * @brief 读取M1卡中的一块数据到 data
  430. * @param addr 数据块地址
  431. * @param data 读出的数据
  432. * @return 状态码
  433. */
  434. uint8_t mfrc522_read_string(uint8_t addr, uint8_t *data)
  435. {
  436.     /* 如果是数据块(不包含数据块0),则读取 */
  437.     if (mfrc522_is_data_block(addr))
  438.     {
  439.         return mfrc522_read(addr, data);
  440.     }
  441.     return MI_ERR;
  442. }
  443. /**
  444. * @brief 命令卡片进入休眠状态
  445. * @return 状态码
  446. */
  447. uint8_t mfrc522_halt(void)
  448. {
  449.     uint8_t com_buf[MFRC522_MAX_LEN];
  450.     uint32_t len;
  451.     com_buf[0] = PICC_HALT;
  452.     com_buf[1] = 0;
  453.     mfrc522_calculate_crc(com_buf, 2, &com_buf[2]);
  454.     mfrc522_to_card(PCD_TRANSCEIVE, com_buf, 4, com_buf, &len);
  455.     return MI_OK;
  456. }
  457. /**
  458. * @brief 写入钱包金额
  459. * @param addr 块地址
  460. * @param data 写入的金额
  461. * @return 状态码
  462. */
  463. uint8_t mfrc522_write_amount(uint8_t addr, uint32_t data)
  464. {
  465.     uint8_t status;
  466.     uint8_t com_buf[16];
  467.     com_buf[0] = (data & ((uint32_t)0x000000ff));
  468.     com_buf[1] = (data & ((uint32_t)0x0000ff00)) >> 8;
  469.     com_buf[2] = (data & ((uint32_t)0x00ff0000)) >> 16;
  470.     com_buf[3] = (data & ((uint32_t)0xff000000)) >> 24;
  471.     com_buf[4] = ~(data & ((uint32_t)0x000000ff));
  472.     com_buf[5] = ~(data & ((uint32_t)0x0000ff00)) >> 8;
  473.     com_buf[6] = ~(data & ((uint32_t)0x00ff0000)) >> 16;
  474.     com_buf[7] = ~(data & ((uint32_t)0xff000000)) >> 24;
  475.     com_buf[8] = (data & ((uint32_t)0x000000ff));
  476.     com_buf[9] = (data & ((uint32_t)0x0000ff00)) >> 8;
  477.     com_buf[10] = (data & ((uint32_t)0x00ff0000)) >> 16;
  478.     com_buf[11] = (data & ((uint32_t)0xff000000)) >> 24;
  479.     com_buf[12] = addr;
  480.     com_buf[13] = ~addr;
  481.     com_buf[14] = addr;
  482.     com_buf[15] = ~addr;
  483.     status = mfrc522_write(addr, com_buf);
  484.     return status;
  485. }
  486. /**
  487. * @brief 读取钱包金额
  488. * @param addr 块地址
  489. * @param data 读出的金额
  490. * @return 状态码
  491. */
  492. uint8_t mfrc522_read_amount(uint8_t addr, uint32_t *data)
  493. {
  494.     uint8_t status = MI_ERR;
  495.     uint8_t j;
  496.     uint8_t com_buf[16];
  497.     status = mfrc522_read(addr, com_buf);
  498.     if (status != MI_OK)
  499.         return status;
  500.     for (j = 0; j < 4; j++)
  501.     {
  502.         if ((com_buf[j] != com_buf[j + 8]) && (com_buf[j] != ~com_buf[j + 4])) // 验证一下是不是钱包的数据
  503.             break;
  504.     }
  505.     if (j == 4)
  506.     {
  507.         status = MI_OK;
  508.         *data = com_buf[0] + (com_buf[1] << 8) + (com_buf[2] << 16) + (com_buf[3] << 24);
  509.     }
  510.     else
  511.     {
  512.         status = MI_ERR;
  513.         *data = 0;
  514.     }
  515.     return status;
  516. }
  517. /**
  518. * @brief 修改控制块 addr 的密码A。注意 addr 指的是控制块的地址。
  519. *        必须要校验密码B,密码B默认为6个0xFF,如果密码B也忘记了,那就改不了密码A了
  520. * @note  注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
  521. * @param addr 控制块地址
  522. * @param keya 新的密码A,六个字符,比如 "123456"
  523. * @return 状态码
  524. */
  525. uint8_t mfrc522_change_keya(uint8_t addr, uint8_t *keya)
  526. {
  527.     uint8_t keyb_value[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
  528.     uint8_t array_id[4];                                         /*先后存放IC卡的类型和UID(IC卡序列号)*/
  529.     uint8_t com_buf[16];
  530.     uint8_t j;
  531.     /*寻卡*/
  532.     while (mfrc522_request(PICC_REQALL, array_id) != MI_OK)
  533.     {
  534.         printf("寻卡失败\r\n");
  535.         usleep(1000000);
  536.     }
  537.     printf("寻卡成功\n");
  538.     /* 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)*/
  539.     if (mfrc522_anticoll(array_id) == MI_OK)
  540.     {
  541.         /* 选中卡 */
  542.         mfrc522_select(array_id);
  543.         /* 校验 B 密码 */
  544.         if (mfrc522_auth_state(PICC_AUTHENT1B, addr, keyb_value, array_id) != MI_OK)
  545.         {
  546.             printf("检验密码B失败\r\n");
  547.         }
  548.         // 读取控制块里原本的数据(只要修改密码A,其他数据不改)
  549.         if (mfrc522_read(addr, com_buf) != MI_OK)
  550.         {
  551.             printf("读取控制块数据失败\r\n");
  552.             return MI_ERR;
  553.         }
  554.         /* 修改密码A */
  555.         for (j = 0; j < 6; j++)
  556.             com_buf[j] = keya[j];
  557.         if (mfrc522_write(addr, com_buf) != MI_OK)
  558.         {
  559.             printf("写入数据到控制块失败\r\n");
  560.             return MI_ERR;
  561.         }
  562.         printf("密码A修改成功!\r\n");
  563.         mfrc522_halt();
  564.         return MI_OK;
  565.     }
  566.     return MI_ERR;
  567. }
  568. /**
  569. * @brief 按照RC522操作流程写入16字节数据到块 addr
  570. *        函数里校验的是密码B,密码B默认为6个0xFF,也可以校验密码A
  571. *        mfrc522_write_data_block(1, "123456789\n", 10); //字符串不够16个字节的后面补零写入
  572. * @note  注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
  573. *        注意:使用该函数要注意 addr 是块0、数据块还是控制块,该函数内部不对此做判断
  574. * @param addr 块地址
  575. * @param data 写入的数据
  576. * @param len 数据长度
  577. * @return 状态码
  578. */
  579. uint8_t mfrc522_write_data_block(uint8_t addr, uint8_t *data, uint8_t len)
  580. {
  581.     uint8_t keyb_value[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
  582.     uint8_t array_id[4];                                         /*先后存放IC卡的类型和UID(IC卡序列号)*/
  583.     uint8_t com_buf[16];
  584.     uint8_t j;
  585.     /*寻卡*/
  586.     while (mfrc522_request(PICC_REQALL, array_id) != MI_OK)
  587.     {
  588.         printf("寻卡失败\r\n");
  589.         usleep(1000000);
  590.     }
  591.     printf("寻卡成功\n");
  592.     /* 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)*/
  593.     if (mfrc522_anticoll(array_id) == MI_OK)
  594.     {
  595.         /* 选中卡 */
  596.         mfrc522_select(array_id);
  597.         /* 校验 B 密码 */
  598.         if (mfrc522_auth_state(PICC_AUTHENT1B, addr, keyb_value, array_id) != MI_OK)
  599.         {
  600.             printf("检验密码B失败\r\n");
  601.         }
  602.         /* 拷贝 data 里的 len 个字符到 com_buf */
  603.         for (j = 0; j < 16; j++)
  604.         {
  605.             if (j < len)
  606.                 com_buf[j] = data[j];
  607.             else
  608.                 com_buf[j] = 0; // 16个字节若是未填满的字节置0
  609.         }
  610.         /* 写入字符串 */
  611.         if (mfrc522_write(addr, com_buf) != MI_OK)
  612.         {
  613.             printf("写入数据到数据块失败\r\n");
  614.             return MI_ERR;
  615.         }
  616.         printf("写入数据成功!\r\n");
  617.         mfrc522_halt();
  618.         return MI_OK;
  619.     }
  620.     return MI_ERR;
  621. }
  622. /**
  623. * @brief 读取M1卡数据
  624. * @note  注意:该函数仅适用于默认的存储控制模式,若是其他的话可能出现问题
  625. *        注意:使用该函数要注意 addr 是块0、数据块还是控制块,该函数内部不对此做判断
  626. * @param addr 块地址
  627. * @param data 读出的数据
  628. * @return 状态码
  629. */
  630. uint8_t mfrc522_read_data_block(uint8_t addr, uint8_t *data)
  631. {
  632.     uint8_t keyb_value[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // B密钥
  633.     uint8_t array_id[4];                                         /*先后存放IC卡的类型和UID(IC卡序列号)*/
  634.     /*寻卡*/
  635.     while (mfrc522_request(PICC_REQALL, array_id) != MI_OK)
  636.     {
  637.         printf("寻卡失败\r\n");
  638.         usleep(1000000);
  639.     }
  640.     printf("寻卡成功\n");
  641.     /* 防冲突(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)*/
  642.     if (mfrc522_anticoll(array_id) == MI_OK)
  643.     {
  644.         /* 选中卡 */
  645.         mfrc522_select(array_id);
  646.         /* 校验 B 密码 */
  647.         if (mfrc522_auth_state(PICC_AUTHENT1B, addr, keyb_value, array_id) != MI_OK)
  648.         {
  649.             printf("检验密码B失败\r\n");
  650.         }
  651.         // 读取数据块里的数据到 data
  652.         if (mfrc522_read(addr, data) != MI_OK)
  653.         {
  654.             printf("读取数据块失败\r\n");
  655.             return MI_ERR;
  656.         }
  657.         printf("读取数据成功!\r\n");
  658.         mfrc522_halt();
  659.         return MI_OK;
  660.     }
  661.     return MI_ERR;
  662. }
  663. /**
  664. * @brief 初始化MFRC522
  665. * @param spi_dev SPI设备文件路径
  666. * @param cs_chip CS芯片名称
  667. * @param cs_line CS引脚编号
  668. * @param rst_chip 复位芯片名称
  669. * @param rst_line 复位引脚编号
  670. * @return 初始化结果
  671. */
  672. int mfrc522_init(const char *spi_dev,
  673.                  const char *cs_chip, uint8_t cs_line,
  674.                  const char *rst_chip, uint8_t rst_line)
  675. {
  676.     int ret;
  677.     if (!spi_dev)
  678.         return -1;
  679.     if (!cs_chip || !rst_chip)
  680.         return -1;
  681.     /* SPI初始化 */
  682.     mfrc522_spi = spi_handle_alloc(spi_dev, SPIMODE0, S_1M, cs_chip, cs_line);
  683.     if (!mfrc522_spi)
  684.         return -1;
  685.     /* 复位脚初始化 */
  686.     rst_gpiochip = gpiod_chip_open(rst_chip);
  687.     if (rst_gpiochip == NULL)
  688.     {
  689.         printf("gpiod_chip_open failed!\n");
  690.         return -1;
  691.     }
  692.     rst_gpioline = gpiod_chip_get_line(rst_gpiochip, rst_line);
  693.     if (rst_gpioline == NULL)
  694.     {
  695.         printf("gpiod_chip_get_line failed!\n");
  696.         return -1;
  697.     }
  698.     ret = gpiod_line_request_output(rst_gpioline, "rst_gpioline", 1);
  699.     if (ret < 0)
  700.     {
  701.         printf("gpiod_line_request_output failed!\n");
  702.         return -1;
  703.     }
  704.     mfrc522_rst_disabled();
  705.     return 0;
  706. }
  707. /**
  708. * @brief 反初始化MFRC522
  709. */
  710. void mfrc522_exit(void)
  711. {
  712.     if (mfrc522_spi)
  713.         spi_handle_free(mfrc522_spi);
  714.     if (rst_gpioline)
  715.         gpiod_line_release(rst_gpioline);
  716.     if (rst_gpiochip)
  717.         gpiod_chip_close(rst_gpiochip);
  718. }
复制代码
6、测试

下面编写一个测试步伐main.c。完成写入金额和读取金额的利用。
  1. /* main.c */
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>
  5. #include <stdlib.h>
  6. #include <pthread.h>
  7. #include <gpiod.h>
  8. #include <math.h>
  9. #include "mfrc522.h"
  10. // 通常新卡的密码A和密码B都是0xff。可以通过访问每个扇区的尾块来修改密码。
  11. // 修改完密码后,要自行记录。因为密码A和密码B都是不可读的。
  12. uint8_t key_value[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 卡A密钥
  13. int main()
  14. {
  15.     unsigned char id[4];
  16.     unsigned char status;
  17.     int ret;
  18.     uint32_t write_value = 100;
  19.     uint32_t read_value;
  20.        
  21.         /*
  22.     * 块地址的计算:
  23.     * 块地址   是从0开始的连续编号,范围是 0x00 到 0x3F(总共64个块)
  24.     * 扇区地址 是从0开始的连续编号,范围是 0x00 到 0x0F(总共16个扇区)
  25.     *
  26.     * 具体计算公式:
  27.     * 扇区号 = 块地址 / 4
  28.     * 块号 = 块地址 % 4
  29.     *
  30.     * 假设块地址为0x11
  31.     * 扇区号 = 0x11 / 4 = 0x04(第5个扇区)
  32.     * 块号 = 0x11 % 4 = 0x01(第2个块,即块1)
  33.     * 因此,0x11 对应 扇区4的块1。
  34.     */
  35.     unsigned char addr = 0x11;
  36.    
  37.     ret = mfrc522_init("/dev/spidev3.0",
  38.                        "/dev/gpiochip6", 11,
  39.                        "/dev/gpiochip6", 10);
  40.     if (ret != 0)
  41.     {
  42.         printf("mfrc522_init fialed!\n");
  43.         return -1;
  44.     }
  45.     mfrc522_reset();
  46.     mfrc522_config_iso_type('A');
  47.     while (1)
  48.     {
  49.         status = mfrc522_request(PICC_REQALL, id);
  50.         if (status != MI_OK)
  51.             printf("request card fialed!\n");
  52.         if (status == MI_OK)
  53.         {
  54.             printf("request card successfully!\n");
  55.             if (mfrc522_anticoll(id) != MI_OK)
  56.             {
  57.                 printf("anticoll failed, continue!\n");
  58.                 continue;
  59.             }
  60.             status = mfrc522_select(id); // 选定卡片
  61.             if (status != MI_OK)
  62.             {
  63.                 printf("select card failed, continue!\n");
  64.                 continue;
  65.             }
  66.             status = mfrc522_auth_state(PICC_AUTHENT1A, addr, key_value, id); // 校验密码
  67.             if (status != MI_OK)
  68.             {
  69.                 printf("autu failed, continue!\n");
  70.                 continue;
  71.             }
  72.             status =  mfrc522_write_amount(addr, write_value); // 写入金额
  73.             if (status != MI_OK)
  74.             {
  75.                 printf("write amount failed, continue!\n");
  76.                 continue;
  77.             }
  78.             
  79.             status =  mfrc522_read_amount(addr, &read_value);
  80.             if (status != MI_OK)
  81.             {
  82.                 printf("read amount failed, continue!\n");
  83.                 continue;
  84.             }
  85.             printf("card uid = %02X%02X%02X%02X\n", id[0], id[1], id[2], id[3]);
  86.                         printf("read value = %d\r\n", read_value);
  87.                        
  88.             mfrc522_halt();
  89.             
  90.             break;
  91.         }
  92.         sleep(0.1);
  93.     }
  94.     mfrc522_exit();
  95.     return 0;
  96. }
复制代码
目前总共涉及的文件有:

执行如下命令编译步伐:
  1. gcc -o build main.c mfrc522.c spi.c -lgpiod
复制代码
执行如下命令运行步伐:
  1. sudo ./build
复制代码

利用手机nfc工具检察,扇区4的块1被修改。0x64转成十进制就是100。

7、总结

参考文章:基于STM32的RC522门禁系统步伐解读_mfrc522-CSDN博客
依稀记得小时间拿家里的门禁卡去外貌找人复制一个新的,那已是10几年前的事。在查阅MFRC522手册时,发现这款芯片诞生于2007年。其实用的产物功能我们不谈。只是如今2025年,在嵌入式教育的视野中另有它的存在。固然另有很多外设都经典传播至今。那对于嵌入式入门学习来说,到底什么是核心?我想更多是培养学者独立阅读数据手册的技能,熟悉常见的外设通讯协议,加强步伐编码或阅读能力等等。但初学时的这种习惯容易遗留到以后的工作中,一字一句的扣代码只会降低开发效率,也不会让你大富大贵。我只想表达,不必花太多心思在雷同于这种寄存器或外设驱动的研究上,更应该侧重于上层应用或框架性的东西。包括Linux驱动的学习,重点在理解装备驱动框架,各种子系统框架。
以上也只是我入行一年左右带给我的眼界而作出的讨论。不必理会。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

十念

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表