一、简介
NT3H1x01W0FHK NFC芯片,是一款简单,低成本的NFC标签。
特点:
- 工作频率:13.56MHz;
- NT3H1101(NT3H1201)支持接触式和非接触式接口,IIC从机接口支持标准模式(100KHz)和高速模式(高达400KHz);
- 用户读写区:1904 bytes;
- SRAM:64 bytes;
- NT3H1101(NT3H1201) NFC标签可直接作为标准IIC EEPROM使用;
- 外部连接板载NFC射频天线。
NT3H1201芯片与微控制器遵循I2C通信协议,NFC协议为2型通信标准。如图所示,芯片通过PCB上射频天线从接触的有源NFC设备上获取能量,并完成数据交互。交互的数据被写入片上EEPROM用以掉电后的再次读写。另一边,经过芯片转换,NFC获得的能量被供给到外部设备,同时芯片通过I2C与板载外部设备(微控制器)通信。可以看出,NTAG芯片在过程中起到了触碰信息转移和触碰能量传递的中间介质。
1.1 射频通信流程
- 识别和选择流程:
- 上电复位(
POR
)后,NTAG I2C切换到空闲状态(IDLE
)。只有在这种状态下才会从NFC设备接收请求命令(REQA
)或唤醒命令(WUPA
),任何其他在这种状态下接收到的数据被解释为错误。 - 在就绪状态1(
READY 1
),NFC设备通过防碰撞选择第一层的防碰撞命令(ANTICOLLISION
)或选择命令(SELECT
)解析出UID的第一部分(3个字节)。在执行成功防碰撞选择第一层的选择命令(SELECT
)后,进入就绪状态2。 - 在就绪状态2(
READY 2
),NTAG I2C支持NFC设备通过防碰撞选择第二层的防碰撞命令(ANTICOLLISION
)解析出UID的第二部分(4个字节)。在执行成功防碰撞选择第二层的选择命令(SELECT
)后,进入激活状态。
- 内存操作:
- 所有内存操作均在激活状态(
ACTIVE
)下操作。如读取16Byte操作(READ
)、快速读取操作(FAST_READ
)、写入操作(WRITE
)、扇区选择操作(SECTOR_SELECT
)、获取版本操作(GET_VERSION
)。取决于其先前的状态,NTAG I2C返回到空闲状态(IDLE
)或停止状态(HALT
)。 - 停止和空闲状态构成NTAG I2C中实现的两种等待状态。已处理的NTAG I2C可以使用停止命令(
HALT
)设置为暂停状态。在里面在防碰撞阶段,此状态有助于NFC设备区分已处理的标签和待选择的标签。NTAG I2C只能在执行唤醒命令(WUPA
)时退出此状态。
1.2 NDEF
NDEF(NFC data exchange format) 是在LLCP链路被激活时使用到。
NDEF spec的主要目的有:
- 封装任意形式的文件和实体(如加密数据,XML文件等)
- 封装未知大小的文件和实体
- 组合按某种顺序出现的多个文件和实体(如含有附件的标准文件)
- 同时需要注意小负载的封装不应该增加系统的负荷。
使用场景:
上层应用产生由一个或多个文件生成的NDEF信息,该消息交由底层LLC层传送给对方,对方可以接受后直接处理或作为中间阶段写入Tag中。当其他设备接近该tag时,会读到该tag中的内容,并把读到的NDEF消息传给上层应用分析和处理。
NDEF组成:
1.3 RTD
RTD(NFC Record Type Definition)
几种常见类型:
- RTD_TEXT(T) ,记录描述文本信息
- RTD_URI(U) ,存储网络地址,邮件或电话号码
- RTD_SMART_POSTER( Sp ) ,综合URL,电话号码或短信编入NFC论坛标签及如何在设备间传递这些信息
1.4 解析实例
1.4.1 NDEF封包格式
NDEF完整封包格式如下:
如果SR为1时,对应的封包格式如下:
其中各标记说明如下:
关于TNF,具体值信息如下:
1.4.2 RTD_TEXT记录解析实例
NDEF数据: D1 01 0F 54 02 65 6E 68 65 6C 6C 6F 2C 77 6F 72 6C 64 21
解析结果: hello,world!
1.4.3 RTD_URI记录解析实例
NDEF数据: D1 01 0A 55 01 62 61 69 64 75 2E 63 6F 6D
解析结果: http://www.baidu.com
二、硬件连接
功能口 | 引脚 |
---|---|
SCL | GPIO0 |
SDA | GPIO1 |
LA&LB | 线圈 |
VSS | GND |
VCC | 3.3V |
三、添加I2C驱动
查看 HI3861学习笔记(15)——I2C接口使用
四、I2C通信流程
读和写操作总要传输16个字节数据
-
对于读操作,在启动条件之后,总线主机发送NTAG I2C从机地址代码(SA-7位),并将读/写位(RW)重置为0。NTAG I2C从机确认这一点(A),并等待一个地址字节(MEMA),该字节应与以下存储器块(SRAM或EEPROM)的想要读的地址相对应。NTAG I2C从机通过确认响应有效地址字节(A)后总线主机可以发出停止条件。
-
对于写操作,在启动条件之后,总线主机发送NTAG I2C从机地址代码(SA-7位),并将读/写位(RW)重置为0。NTAG I2C从机确认这一点(A),并等待一个地址字节(MEMA),该字节应与以下存储器块(SRAM或EEPROM)的想要写的地址相对应。NTAG I2C使用确认(A),在写操作的情况下,总线主机启动传输每16个字节(D0…D15),该字节应使用在每个NTAG I²C从机确认字节(A)后。在收到NTAG I²C从机的最后一个字节确认之后,总线主机发出停止条件。
-
只能通过读写操作访问内存地址对应的EEPROM或SRAM。
对于NTAG I²C 1k为00h至3Ah或F8h至FBh
对于NTAG I²C 2k为00h至7Ah或F8h至FBh
五、HI3861作为主机与NFC标签NT3H1201通信
5.1 i2c_example.c
编译时在业务BUILD.gn中包含路径
static_library("i2c_example") {sources = ["nfc/NT3H.c","nfc/nfc.c","nfc/ndef/rtd/nfcForum.c","nfc/ndef/rtd/rtdText.c","nfc/ndef/rtd/rtdUri.c","nfc/ndef/ndef.c", "i2c_example.c"]cflags = [ "-Wno-unused-variable" ]cflags += [ "-Wno-unused-but-set-variable" ]cflags += [ "-Wno-unused-parameter" ]include_dirs = ["//utils/native/lite/include","//kernel/liteos_m/components/cmsis/2.0","//base/iot_hardware/interfaces/kits/wifiiot_lite","nfc/ndef","nfc/ndef/rtd/","nfc"]
}
注意这里加入以下三条语句,防止有些函数中的参数没有用到,编译报错!!!
cflags = [ "-Wno-unused-variable" ]
cflags += [ "-Wno-unused-but-set-variable" ]
cflags += [ "-Wno-unused-parameter" ]
该文件相当于 main.c 文件,主要为初始化I2C和向NFC芯片写入数据。
#include <stdio.h>
#include <string.h>
#include <unistd.h>#include "ohos_init.h"
#include "cmsis_os2.h"
#include "wifiiot_errno.h"
#include "wifiiot_gpio.h"
#include "wifiiot_gpio_ex.h"
#include "wifiiot_i2c.h"
#include "wifiiot_i2c_ex.h"
#include "nfc.h"#define I2C_TASK_STACK_SIZE 1024 * 8
#define I2C_TASK_PRIO 25#define TEXT "Welcome to BearPi-HM_Nano!"
#define WEB "harmonyos.com"static void I2CTask(void)
{uint8_t ret;GpioInit();//GPIO_0复用为I2C1_SDAIoSetFunc(WIFI_IOT_IO_NAME_GPIO_0, WIFI_IOT_IO_FUNC_GPIO_0_I2C1_SDA);//GPIO_1复用为I2C1_SCLIoSetFunc(WIFI_IOT_IO_NAME_GPIO_1, WIFI_IOT_IO_FUNC_GPIO_1_I2C1_SCL);//baudrate: 400kbpsI2cInit(WIFI_IOT_I2C_IDX_1, 400000);I2cSetBaudrate(WIFI_IOT_I2C_IDX_1, 400000);printf("I2C Test Start\n");ret = storeText(NDEFFirstPos, (uint8_t *)TEXT);if (ret != 1){printf("NFC Write Data Falied :%d ", ret);}ret = storeUrihttp(NDEFLastPos, (uint8_t *)WEB);if (ret != 1){printf("NFC Write Data Falied :%d ", ret);}while (1){printf("=======================================\r\n");printf("***********I2C_NFC_example**********\r\n");printf("=======================================\r\n");printf("Please use the mobile phone with NFC function close to the development board!\r\n");usleep(1000000);}
}static void I2CExampleEntry(void)
{osThreadAttr_t attr;attr.name = "I2CTask";attr.attr_bits = 0U;attr.cb_mem = NULL;attr.cb_size = 0U;attr.stack_mem = NULL;attr.stack_size = I2C_TASK_STACK_SIZE;attr.priority = I2C_TASK_PRIO;if (osThreadNew((osThreadFunc_t)I2CTask, NULL, &attr) == NULL){printf("Falied to create I2CTask!\n");}
}APP_FEATURE_INIT(I2CExampleEntry);
这部分代码为I2C初始化的代码,首先用 IoSetFunc()
函数将GPIO_0复用为I2C1_SDA,GPIO_1复用为I2C1_SCL。然后调用 I2cInit()
函数初始化I2C1端口,最后使用 I2cSetBaudrate()
函数设置I2C1的频率为400kbps。
IoSetFunc(WIFI_IOT_IO_NAME_GPIO_0, WIFI_IOT_IO_FUNC_GPIO_0_I2C1_SDA); // GPIO_0复用为I2C1_SDA
IoSetFunc(WIFI_IOT_IO_NAME_GPIO_1, WIFI_IOT_IO_FUNC_GPIO_1_I2C1_SCL); // GPIO_1复用为I2C1_SCL
I2cInit(WIFI_IOT_I2C_IDX_1, 400000); /* baudrate: 400kbps */
I2cSetBaudrate(WIFI_IOT_I2C_IDX_1, 400000);
这部分的代码是向NFC芯片写入数据,但需要写入2个记录时,第2个记录的位置需要用 NDEFLastPos
来定义;当需要写入3个记录时,第2个和第3个记录的位置分别需要用 NDEFMiddlePos
和 NDEFLastPos
来定义。
ret=storeText(NDEFFirstPos, (uint8_t *)TEXT);
if(ret != 1)
{printf("NFC Write Data Falied :%d ",ret);
}
ret=storeUrihttp(NDEFLastPos, (uint8_t *)WEB);
if(ret != 1)
{printf("NFC Write Data Falied :%d ",ret);
}
storeUrihttp()
和 storeText()
在 nfc.c
中实现。
5.2 nfc.h
#ifndef _NFC_H_
#define _NFC_H_#include "NT3H.h"/** The function write in the NT3H a new URI Rtd on the required position** param:* position: where add the record* http: the address to write**/
bool storeUrihttp(RecordPosEnu position, uint8_t *http);/** The function write in the NT3H a new Text Rtd on the required position** param:* position: where add the record* text: the text to write**/
bool storeText(RecordPosEnu position, uint8_t *text);#endif /* NFC_H_ */
5.3 nfc.c
storeUrihttp()
和 storeText()
两个函数首先按照 rtdText.h
和 rtdUri.h
中 RTD 协议进行处理。然后与 ndef.h
中 NT3HwriteRecord()
进行记录写入。
#include <stdbool.h>
#include "rtdText.h"
#include "rtdUri.h"
#include "ndef.h"
#include "nfc.h"bool storeUrihttp(RecordPosEnu position, uint8_t *http){NDEFDataStr data;prepareUrihttp(&data, position, http);return NT3HwriteRecord( &data );
}bool storeText(RecordPosEnu position, uint8_t *text){NDEFDataStr data;prepareText(&data, position, text);return NT3HwriteRecord( &data );
}
5.4 rtd
5.4.1 nfcForum.h
#ifndef NFCFORUM_H_
#define NFCFORUM_H_#include <stdbool.h>#include "rtdTypes.h"
#include "NT3H.h"#define NDEF_START_BYTE 0x03
#define NDEF_END_BYTE 0xFE#define NTAG_ERASED 0xD0typedef struct {uint8_t startByte;uint8_t payloadLength;
}NDEFHeaderStr;#define BIT_MB (1<<7)
#define BIT_ME (1<<6)
#define BIT_CF (1<<5)
#define BIT_SR (1<<4)
#define BIT_IL (1<<3)
#define BIT_TNF (1<<0)
#define MASK_MB 0x80
#define MASK_ME 0x40
#define MASK_CF 0x20
#define MASK_SR 0x10
#define MASK_IL 0x08
#define MASK_TNF 0x07typedef struct {uint8_t header;uint8_t typeLength;uint8_t payloadLength;RTDTypeStr type;
}NDEFRecordStr;uint8_t composeRtdText(const NDEFDataStr *ndef, NDEFRecordStr *ndefRecord, uint8_t *I2CMsg);
uint8_t composeRtdUri(const NDEFDataStr *ndef, NDEFRecordStr *ndefRecord, uint8_t *I2CMsg);void composeNDEFMBME(bool isFirstRecord, bool isLastRecord, NDEFRecordStr *ndefRecord);#endif /* NFCFORUM.H_H_ */
5.4.2 nfcForum.c
#include "nfcForum.h"
#include <string.h>static void rtdHeader(uint8_t type, NDEFRecordStr *ndefRecord, uint8_t *I2CMsg) {ndefRecord->header |= 1;ndefRecord->header |= BIT_SR;I2CMsg[0] = ndefRecord->header;ndefRecord->typeLength = 1;I2CMsg[1] = ndefRecord->typeLength;ndefRecord->type.typeCode=type;I2CMsg[3] = ndefRecord->type.typeCode;
}uint8_t composeRtdText(const NDEFDataStr *ndef, NDEFRecordStr *ndefRecord, uint8_t *I2CMsg) {uint8_t retLen;rtdHeader(RTD_TEXT, ndefRecord, I2CMsg);uint8_t payLoadLen = addRtdText(&ndefRecord->type.typePayload.text);memcpy(&I2CMsg[4], &ndefRecord->type.typePayload.text, payLoadLen);ndefRecord->payloadLength = ndef->rtdPayloadlength+payLoadLen; // added the typePayloadI2CMsg[2]=ndefRecord->payloadLength;retLen = 3 + /*sizeof(ndefRecord->header) +sizeof(ndefRecord->typeLength) +sizeof(ndefRecord->payloadLength) +*/3 + //sizeof(RTDTextTypeStr)-sizeof(TextExtraDataStr)1 /*sizeof(ndefRecord->type.typeCode)*/;return retLen;
}uint8_t composeRtdUri(const NDEFDataStr *ndef, NDEFRecordStr *ndefRecord, uint8_t *I2CMsg) {rtdHeader(RTD_URI, ndefRecord, I2CMsg);uint8_t payLoadLen = addRtdUriRecord(ndef, &ndefRecord->type.typePayload.uri);memcpy(&I2CMsg[4], &ndefRecord->type.typePayload.uri, payLoadLen);ndefRecord->payloadLength = ndef->rtdPayloadlength+payLoadLen; // added the typePayloadI2CMsg[2]=ndefRecord->payloadLength;return 5;/* retLen = sizeof(ndefRecord->header) +sizeof(ndefRecord->typeLength) +sizeof(ndefRecord->payloadLength) +sizeof(1) + //ndefRecord->type.typePayload.uri.typesizeof(ndefRecord->type.typeCode);*/}void composeNDEFMBME(bool isFirstRecord, bool isLastRecord, NDEFRecordStr *ndefRecord) {if (isFirstRecord)ndefRecord->header |= BIT_MB;elsendefRecord->header &= ~MASK_MB;if (isLastRecord)ndefRecord->header |= BIT_ME;elsendefRecord->header &= ~MASK_ME;
}
5.4.3 rtdText.h
#ifndef RTDTEXT_H_
#define RTDTEXT_H_#include "NT3H.h"#define BIT_STATUS (1<<7)
#define BIT_RFU (1<<6)#define MASK_STATUS 0x80
#define MASK_RFU 0x40
#define MASK_IANA 0b00111111typedef struct {char *body;uint8_t bodyLength;
}RtdTextUserPayload;typedef struct {uint8_t status;uint8_t language[2];RtdTextUserPayload rtdPayload;
}RtdTextTypeStr;uint8_t addRtdText(RtdTextTypeStr *typeStr);void prepareText(NDEFDataStr *data, RecordPosEnu position, uint8_t *text);
#endif /* NDEFTEXT_H_ */
5.4.4 rtdText.c
#include "rtdText.h"
#include "rtdTypes.h"
#include <string.h>uint8_t addRtdText(RtdTextTypeStr *typeStr) {// return addNDEFTextPayload(bodyLength, ndefRecord);typeStr->status=0x2;typeStr->language[0]='e';typeStr->language[1]='n';return 3;
}void prepareText(NDEFDataStr *data, RecordPosEnu position, uint8_t *text) {data->ndefPosition = position;data->rtdType = RTD_TEXT;data->rtdPayload = text;data->rtdPayloadlength = strlen((const char *)text);
}
5.4.5 rtdUri.h
#include "NT3H.h"#ifndef RTDURI_H_
#define RTDURI_H_typedef enum {freeForm, //0x00 No prepending is done ... the entire URI is contained in the URI FieldhttpWWW, //0x01 http://www.httpsWWW, //0x02 https://www.http, //0x03 http://https, //0x04 https://tel, //0x05 tel:mailto, //0x06 mailto:ftpAnonymous,//0x07 ftp://anonymous:anonymous@ftpFtp, //0x08 ftp://ftp.ftps, //0x09 ftps://sftp, //0x0A sftp://smb, //0x0B smb://nfs, //0x0C nfs://ftp, //0x0D ftp://dav, //0x0E dav://news, //0x0F news:telnet, //0x10 telnet://imap, //0x11 imap:rtps, //0x12 rtsp://urn, //0x13 urn:/*0x14 pop:0x15 sip:0x16 sips:0x17 tftp:0x18 btspp://0x19 btl2cap://0x1A btgoep://0x1B tcpobex://0x1C irdaobex://0x1D file://0x1E urn:epc:id:0x1F urn:epc:tag:0x20 urn:epc:pat:0x21 urn:epc:raw:0x22 urn:epc:0x23 urn:nfc:*/
}UriTypeE;typedef struct {char *body;uint8_t bodyLength;void *extraData; // herre should be stored specific URI msgs
}RtdUriUserPayload;typedef struct {UriTypeE type;RtdUriUserPayload userPayload; // here should be stored specific URI msgs
}RTDUriTypeStr;uint8_t addRtdUriRecord(const NDEFDataStr *ndef, RTDUriTypeStr *typeStr);void prepareUrihttp(NDEFDataStr *data, RecordPosEnu position, uint8_t *text);#endif /* RTDURI_H_ */
5.4.6 rtdUri.c
#include "rtdUri.h"
#include <string.h>
#include "rtdTypes.h"RTDUriTypeStr uri;uint8_t addRtdUriRecord(const NDEFDataStr *ndef, RTDUriTypeStr *uriType) {uriType->type=((RTDUriTypeStr*) ndef->specificRtdData)->type;return 1;
}void prepareUrihttp(NDEFDataStr *data, RecordPosEnu position, uint8_t *text) {data->ndefPosition = position;data->rtdType = RTD_URI;data->rtdPayload = text;data->rtdPayloadlength = strlen((const char *)text);;uri.type = httpWWW;data->specificRtdData = &uri;
}
5.4.7 rtdTypes.h
#ifndef RTDTYPES_H_
#define RTDTYPES_H_#include "rtdText.h"
#include "rtdUri.h"#define RTD_TEXT 'T'
#define RTD_URI 'U'typedef union {RtdTextTypeStr text;RTDUriTypeStr uri;
} RTDTypeUnion;typedef struct {uint8_t typeCode;RTDTypeUnion typePayload;
}RTDTypeStr;#endif /* RTDTYPES_H_ */
5.5 ndef.h
#ifndef NDEF_H_
#define NDEF_H_#include "NT3H.h"bool NT3HwriteRecord(const NDEFDataStr *data);#endif /* NDEF_H_ */
5.6 ndef.c
#include "ndef.h"
#include <string.h>
#include "nfcForum.h"
#include "rtdTypes.h"
#include "NT3H.h"typedef uint8_t (*composeRtdPtr)(const NDEFDataStr *ndef, NDEFRecordStr *ndefRecord, uint8_t *I2CMsg);
static composeRtdPtr composeRtd[] = {composeRtdText,composeRtdUri};int16_t firstRecord(UncompletePageStr *page, const NDEFDataStr *data, RecordPosEnu rtdPosition) {NDEFRecordStr record;NDEFHeaderStr header;uint8_t typeFunct=0;switch (data->rtdType){case RTD_TEXT:typeFunct =0;break;case RTD_URI:typeFunct = 1;break;default:return -1;break;}// clear all buffersmemset(&record,0,sizeof(NDEFRecordStr));memset(nfcPageBuffer, 0, NFC_PAGE_SIZE);// this is the first recordheader.startByte = NDEF_START_BYTE;composeNDEFMBME(true, true, &record);// prepare the NDEF Header and payloaduint8_t recordLength = composeRtd[typeFunct](data, &record, &nfcPageBuffer[sizeof(NDEFHeaderStr)]);header.payloadLength = data->rtdPayloadlength + recordLength;// write first recordmemcpy(nfcPageBuffer, &header, sizeof(NDEFHeaderStr));return sizeof(NDEFHeaderStr)+recordLength;}int16_t addRecord(UncompletePageStr *pageToUse, const NDEFDataStr *data, RecordPosEnu rtdPosition) {NDEFRecordStr record;NDEFHeaderStr header={0};uint8_t newRecordPtr, mbMe;bool ret = true;uint8_t tmpBuffer[NFC_PAGE_SIZE];uint8_t typeFunct=0;switch (data->rtdType){case RTD_TEXT:typeFunct =0;break;case RTD_URI:typeFunct = 1;break;default:return -1;break;}// first Change the Header of the first RecordNT3HReadHeaderNfc(&newRecordPtr, &mbMe);record.header = mbMe;composeNDEFMBME(true, false, &record); // this is the first recordmbMe = record.header;memset(&record,0,sizeof(NDEFRecordStr));memset(tmpBuffer,0,NFC_PAGE_SIZE);// prepare second recorduint8_t recordLength = composeRtd[typeFunct](data, &record, tmpBuffer);if (rtdPosition == NDEFMiddlePos) {// this is a record in the middle adjust it on the buffetcomposeNDEFMBME(false, false, &record);} else if (rtdPosition == NDEFLastPos){// this is the last record adjust it on the buffetcomposeNDEFMBME(false, true, &record);}tmpBuffer[0] = record.header;header.payloadLength += data->rtdPayloadlength + recordLength;// save the new value of length on the first pageNT3HWriteHeaderNfc((newRecordPtr+header.payloadLength), mbMe);// use the last valid page and start to add the new recordNT3HReadUserData(pageToUse->page);if (pageToUse->usedBytes+recordLength< NFC_PAGE_SIZE) {memcpy(&nfcPageBuffer[pageToUse->usedBytes], tmpBuffer, recordLength);return recordLength+pageToUse->usedBytes;} else {uint8_t byteToCopy = NFC_PAGE_SIZE-pageToUse->usedBytes;memcpy(&nfcPageBuffer[pageToUse->usedBytes], tmpBuffer, byteToCopy);NT3HWriteUserData(pageToUse->page, nfcPageBuffer);// update the info with the new pagepageToUse->page++;pageToUse->usedBytes=recordLength-byteToCopy;//copy the remain part in the pageBuffer because this is what the caller expectmemcpy(nfcPageBuffer, &tmpBuffer[byteToCopy], pageToUse->usedBytes);return pageToUse->usedBytes;}}static bool writeUserPayload(int16_t payloadPtr, const NDEFDataStr *data, UncompletePageStr *addPage){uint8_t addedPayload;bool ret=false;uint8_t finish=payloadPtr+data->rtdPayloadlength;bool endRecord = false;uint8_t copyByte=0;// if the header is less then the NFC_PAGE_SIZE, fill it with the payloadif (NFC_PAGE_SIZE>payloadPtr) {if (data->rtdPayloadlength > NFC_PAGE_SIZE-payloadPtr)copyByte = NFC_PAGE_SIZE-payloadPtr;elsecopyByte = data->rtdPayloadlength;}// Copy the payloadmemcpy(&nfcPageBuffer[payloadPtr], data->rtdPayload, copyByte);addedPayload = copyByte;//if it is sufficient one send add the NDEF_END_BYTEif ((addedPayload >= data->rtdPayloadlength)&&((payloadPtr+copyByte) < NFC_PAGE_SIZE)) {nfcPageBuffer[(payloadPtr+copyByte)] = NDEF_END_BYTE;endRecord = true;}ret = NT3HWriteUserData(addPage->page, nfcPageBuffer);while (!endRecord) {addPage->page++; // move to a new registermemset(nfcPageBuffer,0,NFC_PAGE_SIZE);//special case just the NDEF_END_BYTE remain outif (addedPayload == data->rtdPayloadlength) {nfcPageBuffer[0] = NDEF_END_BYTE;ret = NT3HWriteUserData(addPage->page, nfcPageBuffer);endRecord = true;if (ret == false) {errNo = NT3HERROR_WRITE_NDEF_TEXT;}goto end;}if (addedPayload < data->rtdPayloadlength) {// add the NDEF_END_BYTE if there is enough spaceif ((data->rtdPayloadlength-addedPayload) < NFC_PAGE_SIZE){memcpy(nfcPageBuffer, &data->rtdPayload[addedPayload], (data->rtdPayloadlength-addedPayload));nfcPageBuffer[(data->rtdPayloadlength-addedPayload)] = NDEF_END_BYTE;} else {memcpy(nfcPageBuffer, &data->rtdPayload[addedPayload], NFC_PAGE_SIZE);}addedPayload += NFC_PAGE_SIZE;ret = NT3HWriteUserData(addPage->page, nfcPageBuffer);if (ret == false) {errNo = NT3HERROR_WRITE_NDEF_TEXT;goto end;}} else {endRecord = true;}}end:return ret;
}typedef int16_t (*addFunct_T) (UncompletePageStr *page, const NDEFDataStr *data, RecordPosEnu rtdPosition);
static addFunct_T addFunct[] = {firstRecord, addRecord, addRecord};bool NT3HwriteRecord(const NDEFDataStr *data) {uint8_t recordLength=0, mbMe;UncompletePageStr addPage;addPage.page = 0;// calculate the last used pageif (data->ndefPosition != NDEFFirstPos ) {NT3HReadHeaderNfc(&recordLength, &mbMe);addPage.page = (recordLength+sizeof(NDEFHeaderStr)+1)/NFC_PAGE_SIZE;//remove the NDEF_END_BYTE byte because it will overwrite by the new RecordaddPage.usedBytes = (recordLength+sizeof(NDEFHeaderStr)+1)%NFC_PAGE_SIZE - 1;}// call the appropriate function and consider the pointer// within the NFC_PAGE_SIZE that need to be usedint16_t payloadPtr = addFunct[data->ndefPosition](&addPage, data, data->ndefPosition);if (payloadPtr == -1) {errNo = NT3HERROR_TYPE_NOT_SUPPORTED;return false;}return writeUserPayload(payloadPtr, data, &addPage);
}
5.7 NT3H.h
从机地址为什么是 NT3H1X_SLAVE_ADDRESS 0x55
,我没在数据手册中看出来。
USER_START_REG 0x1
对于NT3H1201 即 2k 情况
CONFIG_REG 0x7A
SRAM_START_REG 0xF8
SRAM_END_REG 0xFB // just the first 8 bytes
SESSION_REG 0xFE
#ifndef NT3H_H_
#define NT3H_H_#include "stdbool.h"
#include <stdint.h>#define NT3H1X_SLAVE_ADDRESS 0x55 #define MANUFACTORING_DATA_REG 0x0
#define USER_START_REG 0x1// NT3H1201 // for th 2K
#define USER_END_REG 0x77
#define CONFIG_REG 0x7A
// NT3H1101 // for th 1K
// #define USER_END_REG 0x38 // just the first 8 bytes for th 1K
// #define CONFIG_REG 0x3A#define SRAM_START_REG 0xF8
#define SRAM_END_REG 0xFB // just the first 8 bytes#define SESSION_REG 0xFE#define NFC_PAGE_SIZE 16typedef enum {NT3HERROR_NO_ERROR,NT3HERROR_READ_HEADER,NT3HERROR_WRITE_HEADER,NT3HERROR_INVALID_USER_MEMORY_PAGE,NT3HERROR_READ_USER_MEMORY_PAGE,NT3HERROR_WRITE_USER_MEMORY_PAGE,NT3HERROR_ERASE_USER_MEMORY_PAGE,NT3HERROR_READ_NDEF_TEXT,NT3HERROR_WRITE_NDEF_TEXT,NT3HERROR_TYPE_NOT_SUPPORTED
}NT3HerrNo;extern uint8_t nfcPageBuffer[NFC_PAGE_SIZE];
extern NT3HerrNo errNo;typedef enum {NDEFFirstPos,NDEFMiddlePos,NDEFLastPos
} RecordPosEnu;
/** This strucure is used in the ADD record functionality* to store the last nfc page information, in order to continue from that point.*/
typedef struct {uint8_t page;uint8_t usedBytes;
} UncompletePageStr;typedef struct {RecordPosEnu ndefPosition;uint8_t rtdType;uint8_t *rtdPayload;uint8_t rtdPayloadlength;void *specificRtdData;
}NDEFDataStr;void NT3HGetNxpSerialNumber(char* buffer);/** read the user data from the requested page* first page is 0** the NT3H1201 has 119 PAges * the NT3H1101 has 56 PAges (but the 56th page has only 8 Bytes)
*/
bool NT3HReadUserData(uint8_t page);/** Write data information from the starting requested page.* If the dataLen is bigger of NFC_PAGE_SIZE, the consecuiteve needed * pages will be automatically used.* * The functions stops to the latest available page.* first page is 0* the NT3H1201 has 119 PAges * the NT3H1101 has 56 PAges (but the 56th page has only 8 Bytes)
*/
bool NT3HWriteUserData(uint8_t page, const uint8_t* data);/** The function read the first page of user data where is stored the NFC Header.* It is important because it contains the total size of all the stored records.** param endRecordsPtr return the value of the total size excluding the NDEF_END_BYTE* param ndefHeader Store the NDEF Header of the first record*/
bool NT3HReadHeaderNfc(uint8_t *endRecordsPtr, uint8_t *ndefHeader);/** The function write the first page of user data where is stored the NFC Header.* update the bytes that contains the payload Length and the first NDEF Header** param endRecordsPtr The value of the total size excluding the NDEF_END_BYTE* param ndefHeader The NDEF Header of the first record*/
bool NT3HWriteHeaderNfc(uint8_t endRecordsPtr, uint8_t ndefHeader);bool getSessionReg(void);
bool getNxpUserData(char* buffer);
bool NT3HReadSram(void);
bool NT3HReadSession(void);
bool NT3HReadConfiguration(uint8_t *configuration);bool NT3HEraseAllTag(void);bool NT3HReaddManufactoringData(uint8_t *manuf) ;bool NT3HResetUserData(void);#endif /* NFC_H_ */
5.8 NT3H.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>#include "wifiiot_i2c.h"
#include "wifiiot_i2c_ex.h"
#include "NT3H.h"
#include "ndef.h"
#include "nfc.h"
#include "nfcForum.h"uint8_t nfcPageBuffer[NFC_PAGE_SIZE];
NT3HerrNo errNo;
// due to the nature of the NT3H a timeout is required to
// protectd 2 consecutive I2C accessinline const uint8_t* get_last_ncf_page(void) {return nfcPageBuffer;
}static bool writeTimeout( uint8_t *data, uint8_t dataSend) {uint32_t status = 0;WifiIotI2cData nt3h1101_i2c_data1 = {0};nt3h1101_i2c_data1.sendBuf = data;nt3h1101_i2c_data1.sendLen = dataSend;status = I2cWrite(WIFI_IOT_I2C_IDX_1, (NT3H1X_SLAVE_ADDRESS<<1)|0x00, &nt3h1101_i2c_data1);if (status != 0){printf("===== Error: I2C write status1 = 0x%x! =====\r\n", status);return 0;}usleep(300000);return 1;
}static bool readTimeout(uint8_t address, uint8_t *block_data) {uint32_t status = 0;WifiIotI2cData nt3h1101_i2c_data = {0};uint8_t buffer[1] = {address};nt3h1101_i2c_data.sendBuf = buffer;nt3h1101_i2c_data.sendLen = 1;nt3h1101_i2c_data.receiveBuf = block_data;nt3h1101_i2c_data.receiveLen = NFC_PAGE_SIZE;status = I2cWriteread(WIFI_IOT_I2C_IDX_1, (NT3H1X_SLAVE_ADDRESS<<1)|0x00, &nt3h1101_i2c_data);if (status != 0){printf("===== Error: I2C write status = 0x%x! =====\r\n", status);return 0;}return 1;
}bool NT3HReadHeaderNfc(uint8_t *endRecordsPtr, uint8_t *ndefHeader) {*endRecordsPtr=0;bool ret = NT3HReadUserData(0);// read the first page to see where is the end of the Records.if (ret == true) {// if the first byte is equals to NDEF_START_BYTE there are some records// store theend of thatif ((NDEF_START_BYTE == nfcPageBuffer[0]) && (NTAG_ERASED != nfcPageBuffer[2])) {*endRecordsPtr = nfcPageBuffer[1];*ndefHeader = nfcPageBuffer[2];}return true;} else {errNo = NT3HERROR_READ_HEADER;}return ret;
}bool NT3HWriteHeaderNfc(uint8_t endRecordsPtr, uint8_t ndefHeader) {// read the first page to see where is the end of the Records.bool ret = NT3HReadUserData(0);if (ret == true) {nfcPageBuffer[1] = endRecordsPtr;nfcPageBuffer[2] = ndefHeader;ret = NT3HWriteUserData(0, nfcPageBuffer);if (ret == false) {errNo = NT3HERROR_WRITE_HEADER;}} else {errNo = NT3HERROR_READ_HEADER;}return ret;
}bool NT3HEraseAllTag(void) {bool ret = true;uint8_t erase[NFC_PAGE_SIZE+1] = {USER_START_REG, 0x03, 0x03, 0xD0, 0x00, 0x00, 0xFE};ret = writeTimeout(erase, sizeof(erase));if (ret == false) {errNo = NT3HERROR_ERASE_USER_MEMORY_PAGE;}return ret;
}bool NT3HReaddManufactoringData(uint8_t *manuf) {return readTimeout(MANUFACTORING_DATA_REG, manuf);
}bool NT3HReadConfiguration(uint8_t *configuration){return readTimeout(CONFIG_REG, configuration);
}bool getSessionReg(void) {return readTimeout(SESSION_REG, nfcPageBuffer);
}bool NT3HReadUserData(uint8_t page) {uint8_t reg = USER_START_REG+page;// if the requested page is out of the register exit with errorif (reg > USER_END_REG) {errNo = NT3HERROR_INVALID_USER_MEMORY_PAGE;return false;}bool ret = readTimeout(reg, nfcPageBuffer);if (ret == false) {errNo = NT3HERROR_READ_USER_MEMORY_PAGE;}return ret;
}bool NT3HWriteUserData(uint8_t page, const uint8_t* data) {bool ret = true;uint8_t dataSend[NFC_PAGE_SIZE +1]; // data plus registeruint8_t reg = USER_START_REG+page;// if the requested page is out of the register exit with errorif (reg > USER_END_REG) {errNo = NT3HERROR_INVALID_USER_MEMORY_PAGE;ret = false;goto end;}dataSend[0] = reg; // store the registermemcpy(&dataSend[1], data, NFC_PAGE_SIZE);ret = writeTimeout(dataSend, sizeof(dataSend));if (ret == false) {errNo = NT3HERROR_WRITE_USER_MEMORY_PAGE;goto end;}end:return ret;
}bool NT3HReadSram(void){bool ret=false;for (int i = SRAM_START_REG, j=0; i<=SRAM_END_REG; i++,j++) {ret = readTimeout(i, nfcPageBuffer);if (ret==false) {return ret;}//memcpy(&userData[offset], pageBuffer, sizeof(pageBuffer));}return ret;
}void NT3HGetNxpSerialNumber(char* buffer) {uint8_t manuf[16];if (NT3HReaddManufactoringData(manuf)) {for(int i=0; i<6; i++) {buffer[i] = manuf[i];}}
}
六、查看打印
用带NFC的手机靠近读取,会弹出识别到一个网页
• 由 Leung 写于 2021 年 10 月 20 日
• 参考:[NFC]NDEF和RTD协议介绍
NFC Forum发布NFC数据交换格式(NDEF)规范
BearPi-HM_Nano开发板基础外设开发——I2C控制NFC芯片