一、什么是NFC

NFC(Near Field Communication)是一种非接触式的近场通信协议,可以理解为无线通信的一种,在我们的日常生活中有广泛的应用场景,允许设备在彼此靠近时进行数据交换和通信。其基于基于RFID技术,工作频率为13.56 MHz,允许设备在靠近时进行数据交换。主要功能包括读取、写入数据,实现点对点通信和设备控制等。

二、PN532模块

PN532是一款高性能的NFC/RFID控制器,支持多种通信协议,如ISO14443 A/B、Mifare、FeliCa等。它具备读写功能,可与多种IC卡通信,广泛应用于门禁系统、交通卡支付、智能海报等领域。具备低功耗、高集成度特点,适用于便携设备。可嵌入手机、平板等,实现移动支付等功能。

在我们的stm32开发场景中,我们通常购买相应的模块来进行开发。

该模块通过开关的调节来进行不同通信协议之间的转换,具体转换关系如图(不同厂商的板子都应有相应的标注):

图一:背面标注

图二:正面标注

不同协议的接口在图一中均有标注。

三、命令码书写及其解释

代码书写:

#include "pn532.h"
 
 
 
uint8_t aRxBuffer[15];          //接收唤醒数据
uint8_t bRxBuffer[25];          //接收寻卡数据
 
uint8_t Enter[] = "\r\n"; //回车换行
 
uint8_t hello0[]="Your PN532 has woken up successfully";
uint8_t hello1[]="Your PN532 has been successfully found";
uint8_t hello2[]="UID";
 
uint8_t UID[4]; //存储 UID
uint8_t UID_HOST[4]={0XC2,0X99,0X4A,0X1B}; //存储 UID
 
 
/*******************************************************************************
  * @函数名称   nfc_WakeUp
  * @函数说明   PN532自带一个休眠功能,要使用PN532对NFC卡片进行读写的时候要激活一下(唤醒),一般放在程序的开头,调用一次即可。
                55 55 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF 03 FD D4 14 01 17 00   (24)
                成功唤醒 返回给STM32   00 00 FF 00 FF 00 00 00 FF 02 FE D5 15 16 00        (15)                    
  * @输入参数   无
  * @输出参数   无
  * @返回参数   无
*******************************************************************************/
void nfc_WakeUp(void)//唤醒
{
    u8 adata[24]={0x55,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x03,0xFD,0xD4,0x14,0x01,0x17,0x00,};
    HAL_UART_Transmit(&huart2,(uint8_t *)&adata,sizeof(adata),0xFFFF); 
 
        
        
    HAL_UART_Receive(&huart2,(uint8_t *)&aRxBuffer,15,0xFFFF);
    //UART2接收PN532返回给单片机的数据:       00 00 FF 00 FF 00 00 00 FF 02 FE D5 15 16 00
    
        
        
    if (HAL_UART_GetState(&huart2) != HAL_UART_STATE_BUSY_TX)
    {
//        HAL_UART_Transmit(&huart1, (uint8_t*)hello0,sizeof(hello0), 0xFFFF);//Your PN532 has woken up successfully
//      HAL_UART_Transmit(&huart1,(uint8_t *)&aRxBuffer,sizeof(aRxBuffer),0xFFFF);
//        HAL_UART_Transmit(&huart1, (uint8_t*)Enter,2, 0xFFFF);
    }       
}
 
 
 
 
 
/*******************************************************************************
  * @函数名称   nfc_look
  * @函数说明   寻卡是涉及到卡片的第一个步骤,nfc对卡的操作都是先寻找卡的,
                  若寻不到卡,则后续的读写操作将无法进行;
                                反之,若寻到卡,则后续的读写操作过程中将不再对卡片进行身份确认。
                                
                                寻卡命令:00 00 FF 04 FC D4 4A 01 00 E1 00 (11)
                成功找到 返回给STM32  00 00 FF 00 FF 00 00 00 FF 0C F4 D5 4B 01 01 00 04 08 04 C2 99 4A 1B 0E 00 (25)                      
  * @输入参数   无
  * @输出参数   无
  * @返回参数   无
*******************************************************************************/
void nfc_look(void)//寻卡
{
    u8 i;
    u8 bdata[11]={0x00,0x00,0xFF,0x04,0xFC,0xD4,0x4A,0x01,0x00,0xE1,0x00};
    HAL_UART_Transmit(&huart2,(uint8_t *)&bdata,sizeof(bdata),0xFFFF); 
 
        
        
    HAL_UART_Receive(&huart2,(uint8_t *)&bRxBuffer,25,0xFFFF);
    
    
    
    if (HAL_UART_GetState(&huart2) != HAL_UART_STATE_BUSY_TX)
    {
//        HAL_UART_Transmit(&huart1, (uint8_t*)hello1,sizeof(hello1), 0xFFFF);//"Your PN532 has been successfully found";
//      HAL_UART_Transmit(&huart1,(uint8_t *)&bRxBuffer,sizeof(bRxBuffer),0xFFFF);
          HAL_UART_Transmit(&huart1, (uint8_t*)Enter,2, 0xFFFF);
//      
//      
 
                                    UID[0]=bRxBuffer[19];
                                    UID[1]=bRxBuffer[20];
                                    UID[2]=bRxBuffer[21];
                                    UID[3]=bRxBuffer[22]; 
 
//          
//          
//          HAL_UART_Transmit(&huart1, (uint8_t *)hello2,sizeof(hello2), 0xFFFF);
 
                    HAL_UART_Transmit(&huart1,(uint8_t *)&UID,4,0xFFFF);
 
//          HAL_UART_Transmit(&huart1, (uint8_t*)Enter,2, 0xFFFF);
 
    }       
}
 
 
 
 
 
int uid_check(void)//UID核验
{
    
        for(int i = 0; i < sizeof(UID_HOST); i++)
        {
            if(UID[i]!=UID_HOST[i])
            {
         break;
            }
      } 
        return 1;
        
}
 
void control(void)
{
    if(uid_check()==1)
    {
    test();
    }
}
 
 
void test(void)//方便测试
{
    HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
    HAL_Delay(500);
    HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
    HAL_Delay(500);
    printf("test成功");
}
 
 
/**
  * 函数功能: 重定向c库函数printf到DEBUG_USARTx
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}
 
/**
  * 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
int fgetc(FILE *f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
  return ch;
}
 

#ifndef  __PN532_H__    
#define  __PN532_H__
 
#include "stm32f1xx_hal.h"
#include "gpio.h"
#include "usart.h"
#include <stdio.h>
#include <string.h>
 
typedef   uint32_t   u32;///32位
typedef   uint16_t   u16;///16位
typedef   uint8_t     u8;///8位
 
void nfc_WakeUp(void);//唤醒
void nfc_look(void);//寻卡
int uid_check(void);
void control(void);
void test(void);//方便测试
 
 
 
 
 
int fputc(int ch, FILE *f);
int fgetc(FILE *f);
 
 
 
#endif

关键代码段解释:

这里先补充一下NFC通信的原理方便看懂源码。

首先NFC所包含的信息是一个数据帧(是数据传输中的基本单位,由若干字节组成,包含帧头、数据、校验码和帧尾等部分。它规定了数据的传输格式)。我们这里假设数据帧存成一个数组

u8 bdata[11]={0x00,0x00,0xFF,0x04,0xFC,0xD4,0x4A,0x01,0x00,0xE1,0x00};

可以看到该数组由11个十六进制的数字组成下面是各个数字的具体含义:

  • 0x00, 0x00:帧头,用于标识数据帧的开始。

  • 0xFF:帧头结束标志。

  • 0x04:数据长度,表示后面数据的字节数(不包括校验和)。

  • 0xFC:长度的补码,0x04的补码。

  • 0xD4:指令类型,表示这是一个发送给PN532的命令。

  • 0x4A:具体命令码,表示寻卡命令。

  • 0x01:参数,表示寻卡模式(0x01表示寻卡模式为ISO14443A)。

  • 0x00:保留字节。

  • 0xE1:校验和,用于数据完整性校验。

  • 0x00:帧尾。

其中的指令类型可以有很多种,也是其中应该变化的部分。

我们看寻卡函数中如下代码

HAL_UART_Transmit(&huart2,(uint8_t *)&bdata,sizeof(bdata),0xFFFF);

通过HAL_UART_Transmit函数将这个数据帧发送给PN532模块。

这里我们的pn532模块会返回一个数值给我们,调用相关的函数进行接收操作:

HAL_UART_Receive(&huart2,(uint8_t *)&bRxBuffer,25,0xFFFF);

假设接收的信息如下:

00 00 FF 00 FF 00 00 00 FF 0C F4 D5 4B 01 01 00 04 08 04 C2 99 4A 1B 0E 00

具体含义如下:

  • 00 00 FF 00 FF:帧头和帧头结束标志。

  • 00 00 00:保留字节。

  • FF:帧头结束标志。

  • 0C:返回数据长度。

  • F4:长度的补码。

  • D5:指令类型,表示这是PN532返回的数据。

  • 4B:返回的命令码,与发送的0x4A对应。

  • 01:返回状态码,表示操作成功。

  • 01:返回的卡片数量。

  • 00:保留字节。

  • 04:卡片UID长度。

  • 08 04 C2 99 4A 1B:卡片的UID(唯一标识符)。

  • 0E:校验和。

  • 00:帧尾。

    该信息的关键就在于卡片的UID,卡片中存储的不同的UID可以赋予卡片不同的身份,是我们NFC能够行使其功能的关键。

    寻卡函数再对UID信息进行存储:

    UID[0]=bRxBuffer[19];
    UID[1]=bRxBuffer[20];
    UID[2]=bRxBuffer[21];
    UID[3]=bRxBuffer[22];

    bRxBuffer[19]bRxBuffer[22]是返回数据中的卡片UID部分。

寻卡函数会通过检查返回数据中的状态码01来判断是否成功找到卡片。如果状态码为01,表示寻卡成功,卡片的UID会被存储到UID数组中。

寻卡成功后,函数发送UID信息。

HAL_UART_Transmit(&huart1,(uint8_t *)&UID,4,0xFFFF);
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐