开发环境
STM32F103C8T6 STM32f103c8t6最小系统板(typec接口)
STM32CUBEMX
PN532模块
一、NFC简介
NFC(Near Field Communication)近场通信,是一种短距高频的无线电技术,在13.56MHz频率运行于20厘米距离内。其传输速度有106Kbit/秒、212Kbit/秒或者424Kbit/秒三种。目前近场通信已通过成为ISO/IEC IS 18092国际标准、ECMA-340标准与ETSI TS 102 190标准。
NFC近场通信技术是由非接触式射频识别(RFID)及互联互通技术整合演变而来,在单一芯片上结合感应式读卡器、感应式卡片和点对点的功能,能在短距离内与兼容设备进行识别和数据交换。工作频率为13.56MHz.但是使用这种手机支付方案的用户必须更换特制的手机。目前这项技术在日韩被广泛应用。手机用户凭着配置了支付功能的手机就可以行遍全国:他们的手机可以用作机场登机验证、大厦的门禁钥匙、交通一卡通、信用卡、支付卡等等。
二、PN532概述
PN532是一个高度集成的非接触读写芯片,它包含80C51微控制器内核,集成了13.56MHz下的各种主动/被动式非接触通信方法和协议。
PN532传输模块支持6种不同的工作模式:
读写器模式,支持ISO/IEC 14443A / MIFARE®机制
读写器模式,支持 FeliCa机制
读写器模式,支持ISO/IEC 14443B机制
卡操作模式,支持ISO 14443A / MIFARE®机制
卡操作模式,FeliCa机制
ISO/IEC18092,ECM340点对点
支持主机接口:
-SPI接口
-I2C接口
-串行UART
卡操作模式
读写器模式
三,读取卡的信息
1.简单用上位机读一下空白卡的种类
PN532工作模式:106 kbps type A (ISO/IEC14443 Type A)
2.寻找NFC设备,读整卡,保存为dump文件
3.利用DUMP转txt工具提取测试卡的信息
0 扇区
0 区块: C2 99 4A 1B 0A 08 04 00 62 63 64 65 66 67 68 69
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
1 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
2 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
3 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
4 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
5 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
6 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 06 07 08 09 10 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
7 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
8 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
9 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
10 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
11 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
12 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
13 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
14 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
15 扇区
0 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2 区块: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 区块: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF
可以看出卡的UID号为:C2 99 4A 1B,不懂怎么来的,可以在这里转到另一个博客
S50非接触式IC卡性能简介(M1)
四,PN532的一些指令
打开串口助手,
1.向PN532模块发送以下数据,唤醒PN532模块
55 55 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF 03 FD D4 14 01 17 00
如果连接没有问题的话,成功唤醒会返回以下数据
00 00 FF 00 FF 00 00 00 FF 02 FE D5 15 16 00
2.接下来开始寻卡,发送下列数据
00 00 FF 04 FC D4 4A 01 00 E1 00
00 00 FF----帧头,与卡相关的所有命令都要包含该头;
04----包长度,即“D4 4A 01 00”的长度;
FC----包长度校验:0x100-包长度;此处0xFC=0x100-0x04;
D4----方向标识码:数据方向是stm32(主机)至PN532
4A----寻卡命令标识码
01----卡数量,最大是2,PN532一次只能同时处理两张卡
00----PN532工作模式:106 kbps type A (ISO/IEC14443 Type A)
E1----发送的数据校验:等于:0x100-(0xff&(0xD4+0x4A+0x01+0x00))
00----;
卡片被识别,返回下列数据
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----ACK(应答)指令码:千万别以为收到应答码就以为卡片正常读写了,因为不正常读写也会收到应答码哟。原因是你操作PN532芯片,只要芯片工作正常,都会有应答码的啦。所以还得往下看,看后边的命令码是否符合规则。
00 00 FF----帧头----与卡相关的所有命令都要包含该头,所以无论是发送还是接收,都要包含帧头的。
0C----包长度,长度12(从F4到1B正好是12)
F4----包长度校验=0x100减去包长度
D5----方向标识码:PN532至主控
4B 01 01 00 04 08 04--;
C2 99 4A 1B----卡号;卡号为8字节BCD编码,最大卡号支持16位数字。卡号小于16位数字时填充F。
0E 00----;
此时可以看出卡的UID在这段数据的19-22位(0开始)
五,STM32串口配置
串口1:TX PA9/RX PA10(连接USB转TTL模块做上位机使用)
串口2:TX PA2/RX PA3(连接PN532模块)
那么根据上面命令码,很容易就写出下面命令码
#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
最终实现该UID号的卡被PN532识别,并和数据库中的UID相比对,如果符合,执行test函数内的命令(本代码效果为识别成功,会向上位机发送卡号,以及测试成功字符串,LED闪烁)。
关于PN532模块的一些思考
PN532模块成本高昂(10元),后期做RFID硬件电路会考虑RC522芯片,立省80%。
同时考虑换一种单片机,大概ESP系列,配合TFT屏幕,连上阿里云做智能家居控制之类的。
又或者做一个类似学校饮水机部分,设计对卡的读写,并采用一些算法进行校验
例如
十元:99 F2 E803 07 0D 008E 0400 BA00 00BA 0008
八元:68 28 2003 05 D7 00B2 0300 BF00 00BF 00A5
第一位是第二位到第十位异或
第三位和第四位是金额
第二位是第三位, 第四位, 第五位的和
第六位是第二位取反
第十一、十二、十三、十四位是刷卡次数
最后一位CRC校验或者其他的算法