RFID

物联网之RFID三(NFC)

2020-05-31 17:28:50 许新天 802

内容提纲

1、NFC的概念-熟悉

2、NFCRFID区别-掌握

3、NFC的工作模式-掌握

nfc概述

NFC (Near Field Communication)近场通信,这个技术由非接触式射频识别(RFID)演变而来,由飞利浦半导体(现恩智浦半导体公司)、诺基亚和索尼共同研制开发,其基础是RFID及互连技术。NFC是一种短距离高频的无线电技术,在13.56Mhz频率运行于20cm距离内。其传输速度有106Kbit/s,212Kbit/s或者424Kbit/s三种。目前近场通信已通过并成为ISO/IEC IS 18092国际标准、ECMA-340标准与ETSI TS 102 190标准。NFC采用主动和被动两种读取模式。

Nfc工作模式

卡模式:这个模式其实就是相当于一张采用RFID技术的IC卡。可以替代大量的IC卡(包括信用卡)场合商场刷卡、公交卡、门禁管制,车票,门票等等。此种方式下,有一个极大的优点,那就是卡片通过非接触读卡器的RF域来供电,即便是寄主设备(如手机)没电也可以工作。

读写器模式:这个模式可以模拟读读卡器功能,读取MIFARE和FeliCa卡的信息

点对点模式:这个模式和红外线差不多,可用于数据交换,只是传输距离较短,传输创建速度较快,传输速度可快些,功耗低(蓝牙也类似)。将两个具备NFC功能的设备链接,能实现数据点对点传输,如下载音乐、交换图片或者同步设备地址簿。一次通过NFC,多个设备如数码相机、PDA、计算机和手机之间都可以交换资料或者服务

明申智能卡 物联网之RFID三(NFC)

NFC与RFID区别

• 第一、NFC将非接触读卡器、非接触卡和点对点功能整合进一块单芯片,而rfid必须有阅读器和标签组成。RFID只能实现信息的读取以及判定,而NFC技术则强调的是信息交互。通俗的说NFC就是RFID的演进版本,双方可以近距离交换信息。NFC手机内置NFC芯片,组成RFID模块的一部分,可以当作RFID无源标签使用进行支付费用;也可以当作RFID读写器,用作数据交换与采集,还可以进行NFC手机之间的数据通信。

• 第二、NFC传输范围比RFID小,RFID的传输范围可以达到几米、甚至几十米,但由于NFC采取了独特的信号衰减技术,相对于RFID来说NFC具有距离近、带宽高、能耗低等特点。

• 第三、应用方向不同。NFC看更多的是针对于消费类电子设备相互通讯,有源RFID则更擅长在长距离识别。

NFC与BLE区别

明申智能卡 物联网之RFID三(NFC)

内容提纲

1、PN532介绍-了解

2、PN532帧格式-掌握

PN532概述

• 随着互联网的普及,手机作为互联网最直接的智能终端,必将会引起一场技术上的革命,如同以前蓝牙、USB、GPS等标配,NFC将成为日后手机最重要的标配,通过NFC技术,手机支付、看电影、坐地铁都能实现,将在我们的日常生活中发挥更大的作用。

• 我们这里使用的NFC芯片为PN532,它是一款高度集成的非接触式通讯收发模块,基于8051单片机核心。它支持6个不同的操作模式:ISO/IEC14443A/MIFARE 读/写器、FeliCa 读/写器、ISO/IEC 14443B 读/写器、ISO/IEC14443A MIFARE卡模拟模式、FeliCa卡模拟模式、ISO/IEC 18092 ECMA 340点对点;这款芯片提供3中和主机通信的接口:SPII2CUSART

SPII2CUSART三种通信方式有该芯片的P16引脚和P17引脚配置:

                            P16                 P17

UART                0(GND)            0(GND)

I2C                    1(DVDD)          0(GND)

SPI                    0(GND)            1(DVDD)

PN532普通帧(P9)(普通格式最大发送255个字节,而长格式(下面有讲解)可以发送更多的字节)

明申智能卡 物联网之RFID三(NFC)

对于PN532的控制,只需要按照帧格式写入数据就可以了

0x00, 前序

0x00, 0xff, 包头

LEN, 数据长度,包含TFI和PD

LCS, 长度校验和,LEN + LCS = 0x00;

TFI, 传输方向,0xd4传到卡片,0xd5卡片返回

PD0, 数据

PD1, 数据

PDn, 数据

DCS, 数据校验和,DCS+TFI+PD0+…+PDn = 0x00;

长格式帧(P10)

明申智能卡 物联网之RFID三(NFC)

0x00, 前序

0x00, 0xff, 包头                                                 

0xff, 0xff   短格式中的长度和校验           (普通格式最大发送255个字节,而长格式可以发送更多的字节)

LENH, 数据长度高字节,包含TFI和PD

LENL, 数据长度低字节,包含TFI和PD

LCS, 长度校验和,LENH + LENL + LCS = 0x00;

TFI, 传输方向,0xd4传到卡片,0xd5卡片返回

PD0, 数据

PD1, 数据

PDn, 数据

      DCS, 数据校验和,DCS+TFI+PD0+…+PDn = 0x00;


以上帧格式可用一个数据结构表示,方便写代码:

/* PN532操作命令结构定义 */
struct Pn532Cmd{
uint8_t Preamble; //前序
uint8_t StartCode[2]; //包头
uint8_t LEN; //包长 TFI+PD0...PDn
uint8_t LCS;//长度校验 LEN + LCS = 0x00
uint8_t TFI;//命令帧识别位 (0xD4:input) (0xD5:output)
uint8_t *data;//数据域
uint8_t DCS;//数据校验 [TFI + PD0 + PD1 + ... + PDn + DCS] = 0x00
uint8_t Postamble;//后序
};

struct Pn532Cmd cmd ={
.Preamble = 0x00,
.StartCode[0] = 0x00,
.StartCode[1] = 0xff,
.TFI = 0xd4,//传输方向,0xd4传到卡片,0xd5卡片返回
.Postamble = 0x00,
};

/*******************************************************************************
* Function Name : GetCmd
* Description : 拿到命令包,在buf中存放一个完整的数据包
* Input : 命令,命令长度
* Output : none
* Return : none
* date :
* modify :
*******************************************************************************/
void GetCmd(uint8_t *data, uint8_t l)
{
uint8_t temp=0;
len = 0;
memset(buf,0,100);
cmd.LEN = l+1; //计算长度,包括数据长度和传输标志
cmd.LCS = 0-cmd.LEN; //长度校验和
cmd.data = data;
for(int i=0; i<l; i++) //拿到数据
temp += data[i];
temp += cmd.TFI;
cmd.DCS = 0-temp; //数据校验和
len = 1+2+1+1+cmd.LEN+1+1;

buf[0] = 0x00; //包前序
buf[1] = 0x00; //包头
buf[2] = 0xff;
buf[3] = cmd.LEN; //长度
buf[4] = cmd.LCS; //校验和
buf[5] = 0xd4; //输出
memcpy(&buf[6], cmd.data, l); //命令,注意这里是将l个(不是1个)字节数据从buf[6]开始往后面填写一直填写到buf[6+l-1]
buf[6+l] = cmd.DCS; //校验和
buf[7+l] = cmd.Postamble; //后序
}

PN532应答(P9)

明申智能卡 物联网之RFID三(NFC)

0x00, 前序

0x00, 0xff, 包头

00, 数据长度, 这里没有任何数据

ff, 长度校验和,LEN + LCS = 0x00;

传输方向,0xd4传到卡片,0xd5卡片返回

数据

数据

PDn, 数据

DCS, 数据校验和,DCS+TFI+PD0+…+PDn = 0x00;

00, 尾序

内容提纲

1、唤醒-掌握

2、扫卡-掌握

3、认证-掌握

4、读写-掌握

唤醒芯片(P23,P99)

0x55,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,(这些字符就是唤醒头)

0x00,0x00,0xFF,0x03,0xFD,0xD4,0x14,0x01,0x17,0x00(在唤醒头后面加上了一个普通帧)

从手册可以看出,唤醒命令要在原有的数据包之前加入唤醒头,这个比较特殊一点

0xd4代表主机向PN532写入数据

0x14,0x01代表选择了普通模式

明申智能卡 物联网之RFID三(NFC)

应答:0x00,0x00,0xff,0x02,0xfe,0xd5(方向:0xd5卡片返回数据到单片机),0x15(应答码,在发送的基础上加1,因为唤醒命令中使用的是0x14所以这里加1变成0x15),0x16,0x00

命令格式(P35)

• 扫描卡片

    0x4a, 0x02, 0x00   //扫描命令,卡片个数(2个),波特率

uint8_t SCAN[3] = {0x4a, 0x02, 0x00}; 	//扫描指令,卡片个数,波特率

• 应答

    0x4b, 应答码

    0x02, 卡片个数

    0x01, 第一个卡片

    0x04, 0x00, 卡片类型

    0x08, 卡片容量

    0x04, id长度

    0x01, 0x02, 0x03, 0x04 卡片id(该环节已经拿到了卡片ID,所以不需要同RFID一样再增加一个防冲突环节)

uint8_t WAKE[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}; //唤醒模块
uint8_t WAKEBACK[9] = {0x00,0x00,0xFF,0x02,0xFE,0xD5,0x15,0x16,0x00};//应答

• 认证卡片

    0x40, 0x01, 0x60, 0x02,  数据交换命令,1号卡片,A认证(认证A密码是0x60,认证B密码是0x61),2地址

    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 秘钥

    0x01, 0x02, 0x03, 0x04 卡片id

• 应答

    0x41, 0x00   无错

uint8_t AUTHOR[14] = {0x40,0x01,0x60,0x02,0xff,0xff,0xff,0xff,0xff,0xff,0x01,0x02,0x03,0x04};/*交换数据, 1卡,认证命令,2地址,key,id*/
/*41 0应答,无措*/

• 读卡

    0x40, 0x01, 0x30, 0x02   //交换数据,1号卡,读取块,2地址

• 应答

    0x41, 0x00, 16bytes  应答,无错,16个数据

uint8_t READ[4] = {0x40, 0x01, 0x30, 0x02};//交换数据,1号卡,读取块,2地址/*41, 0, 16bytes应答,无错,16个数据*/

• 写卡

    0x40, 0x01, 0xa0, 0x02,   交换数据,1号卡,写入块,2地址

    1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6 数据

• 应答

    0x41, 0x00 应答,无错

uint8_t WRITE[20] = {0x40, 0x01, 0xa0, 0x02, 1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6};//交换数据,1号卡,写入块,2地址,数据/*41, 0 应答,无错*/

• 充值

    0x40, 0x01, 0xc1, 0x02, 1,0,0,0 //交换数据,1号卡,充值,2地址,数据

    0x40, 0x01, 0xB0, 0x02;//交换数据,1号卡,保存,2地址

• 应答

    0x41, 0x00   应答,无错

uint8_t INCREMENT[8] = {0x40, 0x01, 0xc1, 0x02, 1,0,0,0};//交换数据,1号卡,充值,2地址,数据
uint8_t TRANSFER[4] = {0x40, 0x01, 0xB0, 0x02};//交换数据,1号卡,保存,2地址/*41, 0 应答,无错*/

• 扣款

    0x40, 0x01, 0xc0, 0x02, 1,0,0,0;//交换数据,1号卡,扣款,2地址,数据

    0x40, 0x01, 0xB0, 0x02;//交换数据,1号卡,保存,2地址

• 应答

    0x41, 0x00 应答,无错

uint8_t DECREMENT[8] = {0x40, 0x01, 0xc0, 0x02, 1,0,0,0};//交换数据,1号卡,扣款,2地址,数据
uint8_t TRANSFER[4] = {0x40, 0x01, 0xB0, 0x02};//交换数据,1号卡,保存,2地址/*41, 0 应答,无错*/

注意:充值或者扣款操作一定要加上保存动作

读写卡片(P35,P33)

读写卡片的每一条命令都要按照帧格式

• 唤醒芯片,将芯片设置为普通模式

• 扫描卡片,一次最多2张。成功可以得到ID

• 认证,需要发送秘钥和ID。注意这里没有防冲突环节,因为在扫描的时候已经拿到卡片的ID,在认证的时候指明ID就可以了

• 读、写、充值、扣款

PN532读取卡片的过程要比MFRC522简单很多

修改S50卡片秘钥:控制秘钥位共四个字节,但是只有三个字节是有效的,Byte9不起作用,可以不用管

明申智能卡 物联网之RFID三(NFC)

每个字节的bit7控制block3,Bit6控制block2,Bit5控制block1

Bit4控制block0,Bit3~0其实就是bit7~4取反的结果

Block3出厂默认 ff ff ff ff ff ff    ff 07 80 69    ff ff ff ff ff ff

S50控制字注意_b结尾的位取反之后才是最终的控制模式

明申智能卡 物联网之RFID三(NFC)

每个字节的bit7控制block3,Bit6控制block2,Bit5控制block1

Bit4控制block0,Bit3~0其实就是bit7~4取反的结果

Block3出厂默认 ff ff ff ff ff ff    ff 07 80 69    ff ff ff ff ff ff

秘钥区控制(block3)

明申智能卡 物联网之RFID三(NFC)

Block3由bit7控制,出厂默认的是001控制

秘钥A永远不可读,读出来的是0x00

修改秘钥A的过程:

第6、7、8字节最高位(第7位)出厂时分别为0 0 1,则控制模式如下所示:需要修改秘钥A,需要先传入最初的秘钥A

明申智能卡 物联网之RFID三(NFC)

1、唤醒

2、扫描卡片

3、认证(此时必须传入秘钥A,才能修改秘钥A)

4、修改秘钥A(16bytes,注意,不要修改本密码以外的其他地方

5、保存修改

6、重新认证

7、数据的读写

内容提纲

1、读写卡流程-掌握

2、读卡(BLOCK0)实战-掌握

3、写卡(BLOCK3)实战-掌握

命令格式(P35,P33)

• 唤醒

• 扫描

• 认证

• 读卡

• 唤醒

• 扫描

• 认证

• 写卡

唤醒芯片(P23,P99)

0x55,0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

0x00,0x00,0xFF,0x03,0xFD,0xD4,0x14,0x01,0x17,0x00

从手册可以看出,唤醒命令要在原有的数据包之前加入唤醒头,这个比较特殊一点

0xd4代表主机向PN532写入数据

0x14,0x01代表选择了普通模式

明申智能卡 物联网之RFID三(NFC)

命令格式(P35)

• 扫描卡片

    0x4a, 0x02, 0x00   //扫描命令,卡片个数,波特率

• 应答

    0x4b, 应答码

    0x02, 卡片个数

    0x01, 第一个卡片

    0x04, 0x00, 卡片类型

    0x08, 卡片容量

    0x04, id长度

    0x01, 0x02, 0x03, 0x04 卡片id

• 认证卡片

    0x40, 0x01, 0x60, 0x02,  数据交换命令,1号卡片,A认证,2地址

    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 秘钥

    0x01, 0x02, 0x03, 0x04 卡片id

• 应答

    0x41, 0x00   无错

• 读卡

    0x40, 0x01, 0x30, 0x02   //交换数据,1号卡,读取块,2地址

• 应答

    0x41, 0x00, 16bytes  应答,无错,16个数据

• 写卡

    0x40, 0x01, 0xa0, 0x02,   交换数据,1号卡,写入块,2地址

    1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6 数据

• 应答

    0x41, 0x00 应答,无错

读写卡片(P35,P33) 

读写卡片的每一条命令都要按照帧格式

• 唤醒芯片,将芯片设置为普通模式

• 扫描卡片,一次最多2张。成功可以得到ID

• 认证,需要发送秘钥和ID。注意这里没有防冲突环节,因为在扫描的时候已经拿到卡片的ID,在认证的时候指明ID就可以了

• 读、写、充值、扣款

• PN532读取卡片的过程要比MFRC522简单很多

内容提纲

1、S50值段-掌握

2、充值操作-掌握

3、扣款-掌握

S50存储结构

值段

值段可以实现电子钱包的功能 有效的命令有 读 写 增 减 恢复 发送
值段有一个固定的数据格式 可以进行错误检测和纠正并备份管理
值段只能在值段格式的写操作时产生 

• 值 表示一个带符号 4 字节值 这个值的最低一个字节保存在最低的地址中 取反的字节以标准2 的格式保存 为了保证数据的正确性和保密性 值被保存了 3 次 两次不取反保存 一次取反保存 

• Adr 表示一个 1 字节地址 当执行强大的备份管理时用于保存存储段的地址 地址字节保存了 4次 取反和不取反各保存两次 在执行增 减 恢复 传送操作时 地址保持不变 它只能通过写命令改变 

明申智能卡 物联网之RFID三(NFC)

• 初始化为固定的格式(使用Write_Card函数初始化)

   以 block1(地址为1)为例:        00,00,00,00,           ff,ff,ff,ff,    00,00,00,00,                1,fe,1,fe
                                                      值:初始化为0           值取反           值             两次写地址,两次地址取反

• 唤醒

• 扫描

• 认证

明申智能卡 物联网之RFID三(NFC)

命令格式(P35,P33)

• 充值

    0x40, 0x01, 0xc1, 0x02, 1,0,0,0 //交换数据,1号卡,充值,2地址,数据

    0x40, 0x01, 0xB0, 0x02;//交换数据,1号卡,保存,2地址

• 应答

    0x41, 0x00   应答,无错

• 扣款

    0x40, 0x01, 0xc0, 0x02, 1,0,0,0;//交换数据,1号卡,扣款,2地址,数据

    0x40, 0x01, 0xB0, 0x02;//交换数据,1号卡,保存,2地址

• 应答

    0x41, 0x00 应答,无错

注意:充值或者扣款操作一定要加上保存动作

充值框架(扣款类似操作):

//while(1)
uint8_ t value[4]= {0xff, 0x00, 0x00, 0x00};
uint8_ t data[16]= {0x0a, 0x00, 0x00, 0x00, 0xf5, 0xff,0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x0l, 0xfe};
uint8_ t key[6]= {0xff, 0xff,0xff, 0xff, 0xff,0xff};
HAL_Delay(l000) ;
Wake_Card();//唤醒
Scan_Card();//扫描
author(1, key) ; //地址1认证
write(1, data) ;//地址1初始化为充值付款的固定格式
increment(1, value);//地址1充值
transfer(1); //地址1保存
read(1) ;//读取地址1的数据

如下为全部相关参考代码: 

532.c功能:掌握NFC的操作流程和相关功能函数

#include "PN532.h"
#include "stm32f0xx_hal.h"
#include "dma.h"
#include "i2c.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "lcd.h"
#include "string.h"
#include "logo.h"

#include "stdlib.h"
#include "stdio.h"

#define BUFFER_SIZE 128

uint8_t buf[100];
uint8_t len = 0;
uint8_t write_data[16];
uint8_t write_value[4];
uint8_t ID[4];
uint8_t KEY_A[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
extern uint8_t rx_buffer[128];

uint8_t WAKE[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}; //唤醒模块
uint8_t WAKEBACK[9] = {0x00,0x00,0xFF,0x02,0xFE,0xD5,0x15,0x16,0x00};
/* PN532操作命令结构定义 */
struct Pn532Cmd{
uint8_t Preamble; //前序
uint8_t StartCode[2]; //包头
uint8_t LEN; //包长 TFI+PD0...PDn
uint8_t LCS;//长度校验 LEN + LCS = 0x00
uint8_t TFI;//命令帧识别位 (0xD4:input) (0xD5:output)
uint8_t *data;//数据域
uint8_t DCS;//数据校验 [TFI + PD0 + PD1 + ... + PDn + DCS] = 0x00
uint8_t Postamble;//后序
};

struct Pn532Cmd cmd ={
.Preamble = 0x00,
.StartCode[0] = 0x00,
.StartCode[1] = 0xff,
.TFI = 0xd4,
.Postamble = 0x00,
};

uint8_t SCAN[3] = {0x4a, 0x02, 0x00}; //扫描指令,卡片个数,波特率
/*
4B 应答
02 目标个数
01 第一个目标
04 00 选卡应答
08 防冲突应答
04 id长度
12 67 58 32id
02
44 00
00
08
88 04 B6 E4 00 00 00 00
*/
uint8_t AUTHOR[14] = {0x40,0x01,0x60,0x02,0xff,0xff,0xff,0xff,0xff,0xff,0x01,0x02,0x03,0x04};//交换数据,1卡,认证命令,2地址,key,id
/*41 0应答,无措*/
uint8_t READ[4] = {0x40, 0x01, 0x30, 0x02};//交换数据,1号卡,读取块,2地址
/*41, 0, 16bytes应答,无错,16个数据*/
uint8_t WRITE[20] = {0x40, 0x01, 0xa0, 0x02, 1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6};//交换数据,1号卡,写入块,2地址,数据
/*41, 0 应答,无错*/
uint8_t INCREMENT[8] = {0x40, 0x01, 0xc1, 0x02, 1,0,0,0};//交换数据,1号卡,充值,2地址,数据
uint8_t DECREMENT[8] = {0x40, 0x01, 0xc0, 0x02, 1,0,0,0};//交换数据,1号卡,扣款,2地址,数据
uint8_t TRANSFER[4] = {0x40, 0x01, 0xB0, 0x02};//交换数据,1号卡,保存,2地址
/*******************************************************************************
* Function Name : GetCmd
* Description : 拿到命令包,在buf中存放一个完整的数据包
* Input : 命令,命令长度
* Output : none
* Return : none
* date : 2016.12.28
* modify :
*******************************************************************************/
void GetCmd(uint8_t *data, uint8_t l)
{
uint8_t temp=0;
len = 0;
memset(buf,0,100);
cmd.LEN = l+1; //计算长度,包括数据长度和传输标志
cmd.LCS = 0-cmd.LEN; //长度校验和
cmd.data = data;
for(int i=0; i<l; i++) //拿到数据
temp += data[i];
temp += cmd.TFI;
cmd.DCS = 0-temp; //数据校验和
len = 1+2+1+1+cmd.LEN+1+1;

buf[0] = 0x00; //包前序
buf[1] = 0x00; //包头
buf[2] = 0xff;
buf[3] = cmd.LEN; //长度
buf[4] = cmd.LCS; //校验和
buf[5] = 0xd4; //输出
memcpy(&buf[6], cmd.data, l); //命令
buf[6+l] = cmd.DCS; //校验和
buf[7+l] = cmd.Postamble; //后序
}
/*******************************************************************************
* Function Name : SendCmd
* Description : 串口发送命令,模块使用高速串口和主机通信
* Input : none
* Output : none
* Return : none
* date : 2016.12.28
* modify :
*******************************************************************************/
void SendCmd(uint8_t *command, uint8_t num)
{
while(HAL_UART_Transmit(&huart2, command, num, 100));
}
/*******************************************************************************
* Function Name : Wake_Card
* Description : 唤醒卡片,这个命令特殊
* Input : none
* Output : none
* Return : 成功0,失败1
* date : 2016,12,28
* modify :
*******************************************************************************/
uint8_t Wake_Card()
{
memset(rx_buffer,0,sizeof(rx_buffer));
SendCmd(WAKE, 24);
HAL_Delay(50);
if(!memcmp(WAKEBACK, rx_buffer, 9))
{
printf("wake success ");
return 0;
}
printf("wake failed ");
return 1;
}

/*******************************************************************************
* Function Name : Scan_Card()
* Description : 扫描卡片,可以拿到卡片的ID
* Input : none
* Output : none
* Return : 成功返回0,失败返回1
* date : 2016.12.28
* modify :
*******************************************************************************/
uint8_t Scan_Card()
{
memset(rx_buffer,0,sizeof(rx_buffer));
GetCmd(SCAN, 3);
SendCmd(buf, len);
HAL_Delay(100);
if(rx_buffer[5]==0xd5 && rx_buffer[6]==0x4b)
{
printf("scan success ");
memcpy(ID, &rx_buffer[13], 4); //拿到卡片ID
// for(int i=0; i<32; i++)
// printf("0x%x,", rx_buffer[i]);
// printf(" ");
return 0;
}
printf("scan failed ");
return 1;
}
/*******************************************************************************
* Function Name : Author_Card
* Description : 认证卡片秘钥A,需要秘钥和id
* Input : none
* Output : none
* Return : none
* date : 2016.12.28
* modify :
*******************************************************************************/
uint8_t Author_Card()
{
memset(rx_buffer,0,sizeof(rx_buffer));
memcpy(&AUTHOR[10], ID, 4); //传入id
memcpy(&AUTHOR[4], KEY_A, 6); //传入key A
GetCmd(AUTHOR, 14);
SendCmd(buf, len);
HAL_Delay(100);
if(rx_buffer[5]==0xd5 && rx_buffer[6]==0x41 && rx_buffer[7]==0x00)
{
printf("author success "); //认证成功
return 0;
}
printf("author failed ");
return 1;
}
/*******************************************************************************
* Function Name : Read_Card()
* Description : 读取卡片内容
* Input : none
* Output : none
* Return : 成功返回0,失败返回1
* date :
* modify :
*******************************************************************************/
uint8_t Read_Card()
{
memset(rx_buffer,0,sizeof(rx_buffer));
READ[3] = cmd_addr; //设置地址
GetCmd(READ, 4);
SendCmd(buf, len);
HAL_Delay(100);
if(rx_buffer[5]==0xd5 && rx_buffer[6]==0x41 && rx_buffer[7]==0x00)
{
printf("read success "); //读取成功
for(int i=0; i<16; i++)
printf("0x%x,", rx_buffer[8+i]);
printf(" ");
return 0;
}
return 1;
}
/*******************************************************************************
* Function Name : Write_Card
* Description : 向卡片写入内容
* Input : none
* Output : none
* Return : 成功返回0,失败返回1
* date :
* modify :
*******************************************************************************/
uint8_t Write_Card()
{
memset(rx_buffer,0,sizeof(rx_buffer));
WRITE[3] = cmd_addr; //设置地址
memcpy(&WRITE[4], write_data, 16); //设置要写入的数据
GetCmd(WRITE, 20);
SendCmd(buf, len);
HAL_Delay(100);
if(rx_buffer[5]==0xd5 && rx_buffer[6]==0x41 && rx_buffer[7]==0x00)
{
printf("write success "); //写入成功
Read_Card();
return 0;
}
return 1;
}
/*******************************************************************************
* Function Name : Increment_Value
* Description : 充值
* Input : none
* Output : none
* Return : 成功返回0,失败返回1
* date :
* modify :
*******************************************************************************/
uint8_t Increment_Value()
{
memset(rx_buffer,0,sizeof(rx_buffer));
INCREMENT[3] = cmd_addr; //充值地址
memcpy(&INCREMENT[4], write_value, 4); //充值金额
GetCmd(INCREMENT, 8);
SendCmd(buf, len);
HAL_Delay(100);
if(rx_buffer[5]==0xd5 && rx_buffer[6]==0x41 && rx_buffer[7]==0x00)
{
printf("increment success ");
return 0;
}
return 1;
}
/*******************************************************************************
* Function Name : Decrement_Value
* Description : 扣款
* Input : none
* Output : none
* Return : 成功返回0,失败返回1
* date :
* modify :
*******************************************************************************/
uint8_t Decrement_Value()
{
memset(rx_buffer,0,sizeof(rx_buffer));
INCREMENT[3] = cmd_addr; //扣款地址
memcpy(&DECREMENT[4], write_value, 4); //扣款金额
GetCmd(DECREMENT, 8);
HAL_Delay(100);
if(rx_buffer[5]==0xd5 && rx_buffer[6]==0x41 && rx_buffer[7]==0x00)
{
printf("decrement success ");
return 0;
}
return 1;
}
/*******************************************************************************
* Function Name : Transfer_Value()
* Description : 将缓存区数据保存
* Input : none
* Output : none
* Return : none
* date :
* modify :
*******************************************************************************/
uint8_t Transfer_Value()
{
memset(rx_buffer,0,sizeof(rx_buffer));
TRANSFER[3] = cmd_addr; //保存地址
GetCmd(TRANSFER, 4);
HAL_Delay(100);
if(rx_buffer[5]==0xd5 && rx_buffer[6]==0x41 && rx_buffer[7]==0x00)
{
printf("Transfer success ");
Read_Card();
return 0;
}
return 1;
}
/*******************************************************************************
* Function Name : PN532_Read()
* Description : 从卡片读内容
* Input : none
* Output : none
* Return : none
* date : 2016.12.28
* modify :
*******************************************************************************/
void PN532_Read()
{
if(!Wake_Card())//唤醒卡片
{
if(!Scan_Card())//扫描卡片
{
if(!Author_Card())//认证卡片
{
Read_Card(); //读取卡片的数据
}
}
}
}
/*******************************************************************************
* Function Name : PN532_Write()
* Description : 向卡片写入内容
* Input : none
* Output : none
* Return : none
* date : 2016.12.28
* modify :
*******************************************************************************/
void PN532_Write()
{
if(!Wake_Card())//唤醒卡片
{
if(!Scan_Card()) //扫描卡片
{
if(!Author_Card())//认证卡片
{
Write_Card(); //向卡片写数据
}
}
}
}
/*******************************************************************************
* Function Name : PN532_Value(uint8_t mode)
* Description : 卡片钱包操作,根据参数选择充值或者扣款
* Input : 钱包模式,充值或者扣款
* Output : none
* Return : none
* date : 2016.12.28
* modify :
*******************************************************************************/
void PN532_Value(uint8_t mode)
{
if(!Wake_Card())
{
if(!Scan_Card())
{
if(!Author_Card())
{
if(mode==0xc0)
Increment_Value();
if(mode==0xc1)
Decrement_Value();
Transfer_Value();
}
}
}
}


void CardData_screen_LCD_show(uint8_t *data, uint8_t num)
{
char buf1[16];
snprintf(buf1,4*num,"%02x%02x%02x%02x",data[0],data[1],data[2],data[3]);
Gui_DrawFont_GBK16(44,80,BLACK,GREEN,(uint8_t*)buf1);
memset(buf1,0,sizeof(buf1));
}

/************************
*func : 开机初始化
*************************/
void BoardInit(void)
{
printf("NFC read card! ");
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);//beep off
Lcd_Init();//lcd init:clear screen green, show logo, show words
Lcd_Clear(GREEN);
showimage_farsight(gImage_logo);
Gui_DrawFont_GBK16(20,50,BLUE,GREEN,(uint8_t *)"NFC");
Gui_DrawFont_GBK16(76,50,BLUE,GREEN,(uint8_t *)"RFID");
Gui_DrawFont_GBK16(15,80,RED,GREEN,(uint8_t *)"ID:");
__HAL_I2C_ENABLE_IT(&hi2c1,I2C_IT_ADDRI);
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart2,rx_buffer,BUFFER_SIZE);
BeepTickDouble();
}
main.c文件对nfc做具体操作实验

#include "stm32f0xx_hal.h"
#include "dma.h"
#include "i2c.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"

#include "PN532.h"

void SystemClock_Config(void);
void Error_Handler(void);

extern uint8_t KEY2_FLAG;
extern uint8_t KEY3_FLAG;

unsigned char cmd_dat[16];//卡片要写的数据
unsigned char aRxBuffer[2];
unsigned char cmd_buf[100]; //串口数据缓冲区
unsigned char count = 0;
unsigned char cmd_type; //命令类型,读或者写,r/w
unsigned char cmd_addr; //操作的地址0~63
unsigned char cmd_check;//二级命令,0正常区域,1改秘钥,2充值,3扣款,4改控制字
unsigned char KEY_B[6];//秘钥B
unsigned char CMD_OVER = 0;
unsigned char DATA_OVER = 0;
unsigned char error = 0;
int main(void)
{
uint8_t j;

HAL_Init();

SystemClock_Config();

MX_GPIO_Init();
MX_DMA_Init();
MX_I2C1_Init();
MX_TIM6_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();

BoardInit();//开机初始化
PN532_Read();

//在中断里接收串口数据
HAL_UART_Receive_IT(&huart1,aRxBuffer,1);

while (1)
{
if(error)
{
printf("指令错误 ");
error = 0;
continue;
}
if(CMD_OVER)
{
if(cmd_type == 'r')
{
if(cmd_addr<=63 && cmd_addr>=0)
PN532_Read();
else
printf("地址不对 ");
CMD_OVER = 0;
cmd_type = 0;
}
else if(cmd_type == 'w')
{
if(DATA_OVER)
{
printf("data over ");
j = Write_Check();
printf("j = %d ", j);
if(j==0)
{
if(cmd_check==0||cmd_check==1)
PN532_Write();
else if(cmd_check==2)
PN532_Value(0xc1);
else if(cmd_check==3)
PN532_Value(0xc0);
}
else if(j==1)
printf("要写入的地址不对 ");
else if(j==2)
printf("要写入的控制字不对 ");
CMD_OVER = 0;
DATA_OVER = 0;
cmd_type = 0;
cmd_check = 0;

}
}
}
}

}

/*******************************************************************************
* Function Name :
* Description : 串口接收回调函数,串口接收中断最终会调用这里
* Input :
* Output :
* Return :
* date : 2016.12.27
* modify :
*******************************************************************************/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// printf("r");
if (huart == &huart1)
{
cmd_buf[count] = USART1->RDR;
count++;
if(cmd_buf[count-2]==0x0d && cmd_buf[count-1]==0x0a) //结束符
{
if(cmd_buf[0]=='c' && count==18) //命令
{
cmd_type = cmd_buf[1]; //获取命令 读/写
cmd_check = cmd_buf[2]; //获取命令级别 写普通区还是特殊区 或者是充值
cmd_addr = cmd_buf[3]; //获取操作地址
memcpy(KEY_A, &cmd_buf[4], 6);//获取秘钥A
memcpy(KEY_B, &cmd_buf[10], 6);//获取秘钥B
CMD_OVER = 1;
}
else if(cmd_buf[0]=='d' && count==19) //数据
{
memcpy(cmd_dat, &cmd_buf[1], 16);
DATA_OVER = 1;
}
else if(cmd_buf[0]=='d' && count==7) //数据
{
memcpy(cmd_dat, &cmd_buf[1], 4);
DATA_OVER = 1;
}
else
error = 1; //格式完全不正确

count = 0;
memset(cmd_buf, 0, 100);
}
}
HAL_UART_Receive_IT(&huart1,aRxBuffer,1);
}
/*******************************************************************************
* Function Name : Write_Check()
* Description : 卡片写入检测函数,目前暂不支持控制字的修改
* Input : none
* Output : none
* Return : 成功0,地址错误1,控制字错误2
* date : 2016.12.27
* modify :
*******************************************************************************/
uint8_t Write_Check()
{
if(cmd_addr>=0 && cmd_addr<=63)
{
if(cmd_check==0 || cmd_check==2 || cmd_check==3) //普通写入,不包含block3,因此要检测地址
{
if(cmd_addr!=0 && (cmd_addr+1)%4!=0)
return 0;
else
return 1;
}
else if(cmd_check==1) //修改秘钥A,控制区不可以改动
{
if(cmd_addr==0 || (cmd_addr+1)%4!=0) //地址不正确
return 1;
if(cmd_dat[6]!=0xff || cmd_dat[7]!=0x07 || cmd_dat[8]!=0x80 || cmd_dat[9]!=0x69) //控制字不正确
return 2;
else
return 0;
}
}
else
return 1; //地址不正确
}

void SystemClock_Config(void)
{
...
}

int fputc(int ch, FILE *f)
{
while((USART1->ISR&0X40)==0);
USART1->TDR = (uint8_t) ch;
return ch;
}

#ifdef USE_FULL_ASSERT

#endif
首页
产品
新闻
联系