一涡相、簡介
NT3H1x01W0FHK NFC芯片,是一款簡單,低成本的NFC標(biāo)簽谈为。
特點(diǎn):
- 工作頻率:13.56MHz;
- NT3H1101(NT3H1201)支持接觸式和非接觸式接口踢关,IIC從機(jī)接口支持標(biāo)準(zhǔn)模式(100KHz)和高速模式(高達(dá)400KHz)伞鲫;
- 用戶讀寫區(qū):1904 bytes;
- SRAM:64 bytes签舞;
- NT3H1101(NT3H1201) NFC標(biāo)簽可直接作為標(biāo)準(zhǔn)IIC EEPROM使用秕脓;
- 外部連接板載NFC射頻天線。
NT3H1201芯片與微控制器遵循I2C通信協(xié)議儒搭,NFC協(xié)議為2型通信標(biāo)準(zhǔn)吠架。如圖所示,芯片通過PCB上射頻天線從接觸的有源NFC設(shè)備上獲取能量搂鲫,并完成數(shù)據(jù)交互傍药。交互的數(shù)據(jù)被寫入片上EEPROM用以掉電后的再次讀寫。另一邊魂仍,經(jīng)過芯片轉(zhuǎn)換拐辽,NFC獲得的能量被供給到外部設(shè)備,同時(shí)芯片通過I2C與板載外部設(shè)備(微控制器)通信擦酌【阒睿可以看出,NTAG芯片在過程中起到了觸碰信息轉(zhuǎn)移和觸碰能量傳遞的中間介質(zhì)仑氛。
1.1 射頻通信流程
- 識(shí)別和選擇流程:
- 上電復(fù)位(
POR
)后乙埃,NTAG I2C切換到空閑狀態(tài)(IDLE
)闸英。只有在這種狀態(tài)下才會(huì)從NFC設(shè)備接收請(qǐng)求命令(REQA
)或喚醒命令(WUPA
),任何其他在這種狀態(tài)下接收到的數(shù)據(jù)被解釋為錯(cuò)誤介袜。 - 在就緒狀態(tài)1(
READY 1
)甫何,NFC設(shè)備通過防碰撞選擇第一層的防碰撞命令(ANTICOLLISION
)或選擇命令(SELECT
)解析出UID的第一部分(3個(gè)字節(jié))。在執(zhí)行成功防碰撞選擇第一層的選擇命令(SELECT
)后遇伞,進(jìn)入就緒狀態(tài)2辙喂。 - 在就緒狀態(tài)2(
READY 2
),NTAG I2C支持NFC設(shè)備通過防碰撞選擇第二層的防碰撞命令(ANTICOLLISION
)解析出UID的第二部分(4個(gè)字節(jié))鸠珠。在執(zhí)行成功防碰撞選擇第二層的選擇命令(SELECT
)后巍耗,進(jìn)入激活狀態(tài)。
- 內(nèi)存操作:
- 所有內(nèi)存操作均在激活狀態(tài)(
ACTIVE
)下操作渐排。如讀取16Byte操作(READ
)炬太、快速讀取操作(FAST_READ
)、寫入操作(WRITE
)驯耻、扇區(qū)選擇操作(SECTOR_SELECT
)亲族、獲取版本操作(GET_VERSION
)。取決于其先前的狀態(tài)可缚,NTAG I2C返回到空閑狀態(tài)(IDLE
)或停止?fàn)顟B(tài)(HALT
)霎迫。 - 停止和空閑狀態(tài)構(gòu)成NTAG I2C中實(shí)現(xiàn)的兩種等待狀態(tài)。已處理的NTAG I2C可以使用停止命令(
HALT
)設(shè)置為暫停狀態(tài)帘靡。在里面在防碰撞階段知给,此狀態(tài)有助于NFC設(shè)備區(qū)分已處理的標(biāo)簽和待選擇的標(biāo)簽。NTAG I2C只能在執(zhí)行喚醒命令(WUPA
)時(shí)退出此狀態(tài)描姚。
1.2 NDEF
NDEF(NFC data exchange format) 是在LLCP鏈路被激活時(shí)使用到涩赢。
NDEF spec的主要目的有:
- 封裝任意形式的文件和實(shí)體(如加密數(shù)據(jù),XML文件等)
- 封裝未知大小的文件和實(shí)體
- 組合按某種順序出現(xiàn)的多個(gè)文件和實(shí)體(如含有附件的標(biāo)準(zhǔn)文件)
- 同時(shí)需要注意小負(fù)載的封裝不應(yīng)該增加系統(tǒng)的負(fù)荷轰胁。
使用場(chǎng)景:
上層應(yīng)用產(chǎn)生由一個(gè)或多個(gè)文件生成的NDEF信息谒主,該消息交由底層LLC層傳送給對(duì)方朝扼,對(duì)方可以接受后直接處理或作為中間階段寫入Tag中赃阀。當(dāng)其他設(shè)備接近該tag時(shí),會(huì)讀到該tag中的內(nèi)容擎颖,并把讀到的NDEF消息傳給上層應(yīng)用分析和處理榛斯。
NDEF組成:
1.3 RTD
RTD(NFC Record Type Definition)
幾種常見類型:
- RTD_TEXT(T) ,記錄描述文本信息
- RTD_URI(U) 搂捧,存儲(chǔ)網(wǎng)絡(luò)地址驮俗,郵件或電話號(hào)碼
- RTD_SMART_POSTER( Sp ) ,綜合URL允跑,電話號(hào)碼或短信編入NFC論壇標(biāo)簽及如何在設(shè)備間傳遞這些信息
1.4 解析實(shí)例
1.4.1 NDEF封包格式
NDEF完整封包格式如下:
如果SR為1時(shí)王凑,對(duì)應(yīng)的封包格式如下:
其中各標(biāo)記說明如下:
關(guān)于TNF搪柑,具體值信息如下:
1.4.2 RTD_TEXT記錄解析實(shí)例
NDEF數(shù)據(jù): D1 01 0F 54 02 65 6E 68 65 6C 6C 6F 2C 77 6F 72 6C 64 21
解析結(jié)果: hello,world!
1.4.3 RTD_URI記錄解析實(shí)例
NDEF數(shù)據(jù): D1 01 0A 55 01 62 61 69 64 75 2E 63 6F 6D
解析結(jié)果: http://www.baidu.com
二、硬件連接
功能口 | 引腳 |
---|---|
SCL | GPIO0 |
SDA | GPIO1 |
LA&LB | 線圈 |
VSS | GND |
VCC | 3.3V |
三索烹、添加I2C驅(qū)動(dòng)
查看 HI3861學(xué)習(xí)筆記(15)——I2C接口使用
四工碾、I2C通信流程
讀和寫操作總要傳輸16個(gè)字節(jié)數(shù)據(jù)
對(duì)于讀操作,在啟動(dòng)條件之后百姓,總線主機(jī)發(fā)送NTAG I2C從機(jī)地址代碼(SA-7位)渊额,并將讀/寫位(RW)重置為0。NTAG I2C從機(jī)確認(rèn)這一點(diǎn)(A)垒拢,并等待一個(gè)地址字節(jié)(MEMA)旬迹,該字節(jié)應(yīng)與以下存儲(chǔ)器塊(SRAM或EEPROM)的想要讀的地址相對(duì)應(yīng)。NTAG I2C從機(jī)通過確認(rèn)響應(yīng)有效地址字節(jié)(A)后總線主機(jī)可以發(fā)出停止條件求类。
對(duì)于寫操作奔垦,在啟動(dòng)條件之后,總線主機(jī)發(fā)送NTAG I2C從機(jī)地址代碼(SA-7位)尸疆,并將讀/寫位(RW)重置為0宴倍。NTAG I2C從機(jī)確認(rèn)這一點(diǎn)(A),并等待一個(gè)地址字節(jié)(MEMA)仓技,該字節(jié)應(yīng)與以下存儲(chǔ)器塊(SRAM或EEPROM)的想要寫的地址相對(duì)應(yīng)鸵贬。NTAG I2C使用確認(rèn)(A),在寫操作的情況下脖捻,總線主機(jī)啟動(dòng)傳輸每16個(gè)字節(jié)(D0…D15)阔逼,該字節(jié)應(yīng)使用在每個(gè)NTAG I2C從機(jī)確認(rèn)字節(jié)(A)后。在收到NTAG I2C從機(jī)的最后一個(gè)字節(jié)確認(rèn)之后地沮,總線主機(jī)發(fā)出停止條件嗜浮。
-
只能通過讀寫操作訪問內(nèi)存地址對(duì)應(yīng)的EEPROM或SRAM。
對(duì)于NTAG I2C 1k為00h至3Ah或F8h至FBh
對(duì)于NTAG I2C 2k為00h至7Ah或F8h至FBh
五摩疑、HI3861作為主機(jī)與NFC標(biāo)簽NT3H1201通信
5.1 i2c_example.c
編譯時(shí)在業(yè)務(wù)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 = [
"http://utils/native/lite/include",
"http://kernel/liteos_m/components/cmsis/2.0",
"http://base/iot_hardware/interfaces/kits/wifiiot_lite",
"nfc/ndef",
"nfc/ndef/rtd/",
"nfc"
]
}
注意這里加入以下三條語句危融,防止有些函數(shù)中的參數(shù)沒有用到,編譯報(bào)錯(cuò)@状<辍!
cflags = [ "-Wno-unused-variable" ]
cflags += [ "-Wno-unused-but-set-variable" ]
cflags += [ "-Wno-unused-parameter" ]
該文件相當(dāng)于 main.c 文件楷怒,主要為初始化I2C和向NFC芯片寫入數(shù)據(jù)蛋勺。
#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復(fù)用為I2C1_SDA
IoSetFunc(WIFI_IOT_IO_NAME_GPIO_0, WIFI_IOT_IO_FUNC_GPIO_0_I2C1_SDA);
//GPIO_1復(fù)用為I2C1_SCL
IoSetFunc(WIFI_IOT_IO_NAME_GPIO_1, WIFI_IOT_IO_FUNC_GPIO_1_I2C1_SCL);
//baudrate: 400kbps
I2cInit(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()
函數(shù)將GPIO_0復(fù)用為I2C1_SDA鸠删,GPIO_1復(fù)用為I2C1_SCL抱完。然后調(diào)用 I2cInit()
函數(shù)初始化I2C1端口,最后使用 I2cSetBaudrate()
函數(shù)設(shè)置I2C1的頻率為400kbps刃泡。
IoSetFunc(WIFI_IOT_IO_NAME_GPIO_0, WIFI_IOT_IO_FUNC_GPIO_0_I2C1_SDA); // GPIO_0復(fù)用為I2C1_SDA
IoSetFunc(WIFI_IOT_IO_NAME_GPIO_1, WIFI_IOT_IO_FUNC_GPIO_1_I2C1_SCL); // GPIO_1復(fù)用為I2C1_SCL
I2cInit(WIFI_IOT_I2C_IDX_1, 400000); /* baudrate: 400kbps */
I2cSetBaudrate(WIFI_IOT_I2C_IDX_1, 400000);
這部分的代碼是向NFC芯片寫入數(shù)據(jù)巧娱,但需要寫入2個(gè)記錄時(shí)碉怔,第2個(gè)記錄的位置需要用 NDEFLastPos
來定義;當(dāng)需要寫入3個(gè)記錄時(shí),第2個(gè)和第3個(gè)記錄的位置分別需要用 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
中實(shí)現(xiàn)眨层。
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()
兩個(gè)函數(shù)首先按照 rtdText.h
和 rtdUri.h
中 RTD 協(xié)議進(jìn)行處理。然后與 ndef.h
中 NT3HwriteRecord()
進(jìn)行記錄寫入上荡。
#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 0xD0
typedef 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 0x07
typedef 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 typePayload
I2CMsg[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 typePayload
I2CMsg[2]=ndefRecord->payloadLength;
return 5;
/* retLen = sizeof(ndefRecord->header) +
sizeof(ndefRecord->typeLength) +
sizeof(ndefRecord->payloadLength) +
sizeof(1) + //ndefRecord->type.typePayload.uri.type
sizeof(ndefRecord->type.typeCode);
*/
}
void composeNDEFMBME(bool isFirstRecord, bool isLastRecord, NDEFRecordStr *ndefRecord) {
if (isFirstRecord)
ndefRecord->header |= BIT_MB;
else
ndefRecord->header &= ~MASK_MB;
if (isLastRecord)
ndefRecord->header |= BIT_ME;
else
ndefRecord->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 0b00111111
typedef 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 Field
httpWWW, //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 buffers
memset(&record,0,sizeof(NDEFRecordStr));
memset(nfcPageBuffer, 0, NFC_PAGE_SIZE);
// this is the first record
header.startByte = NDEF_START_BYTE;
composeNDEFMBME(true, true, &record);
// prepare the NDEF Header and payload
uint8_t recordLength = composeRtd[typeFunct](data, &record, &nfcPageBuffer[sizeof(NDEFHeaderStr)]);
header.payloadLength = data->rtdPayloadlength + recordLength;
// write first record
memcpy(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 Record
NT3HReadHeaderNfc(&newRecordPtr, &mbMe);
record.header = mbMe;
composeNDEFMBME(true, false, &record); // this is the first record
mbMe = record.header;
memset(&record,0,sizeof(NDEFRecordStr));
memset(tmpBuffer,0,NFC_PAGE_SIZE);
// prepare second record
uint8_t recordLength = composeRtd[typeFunct](data, &record, tmpBuffer);
if (rtdPosition == NDEFMiddlePos) {
// this is a record in the middle adjust it on the buffet
composeNDEFMBME(false, false, &record);
} else if (rtdPosition == NDEFLastPos){
// this is the last record adjust it on the buffet
composeNDEFMBME(false, true, &record);
}
tmpBuffer[0] = record.header;
header.payloadLength += data->rtdPayloadlength + recordLength;
// save the new value of length on the first page
NT3HWriteHeaderNfc((newRecordPtr+header.payloadLength), mbMe);
// use the last valid page and start to add the new record
NT3HReadUserData(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 page
pageToUse->page++;
pageToUse->usedBytes=recordLength-byteToCopy;
//copy the remain part in the pageBuffer because this is what the caller expect
memcpy(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 payload
if (NFC_PAGE_SIZE>payloadPtr) {
if (data->rtdPayloadlength > NFC_PAGE_SIZE-payloadPtr)
copyByte = NFC_PAGE_SIZE-payloadPtr;
else
copyByte = data->rtdPayloadlength;
}
// Copy the payload
memcpy(&nfcPageBuffer[payloadPtr], data->rtdPayload, copyByte);
addedPayload = copyByte;
//if it is sufficient one send add the NDEF_END_BYTE
if ((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 register
memset(nfcPageBuffer,0,NFC_PAGE_SIZE);
//special case just the NDEF_END_BYTE remain out
if (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 space
if ((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 page
if (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 Record
addPage.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 used
int16_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
從機(jī)地址為什么是 NT3H1X_SLAVE_ADDRESS 0x55
趴樱,我沒在數(shù)據(jù)手冊(cè)中看出來。
USER_START_REG 0x1
對(duì)于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 16
typedef 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 access
inline 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 that
if ((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 error
if (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 register
uint8_t reg = USER_START_REG+page;
// if the requested page is out of the register exit with error
if (reg > USER_END_REG) {
errNo = NT3HERROR_INVALID_USER_MEMORY_PAGE;
ret = false;
goto end;
}
dataSend[0] = reg; // store the register
memcpy(&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的手機(jī)靠近讀取叁征,會(huì)彈出識(shí)別到一個(gè)網(wǎng)頁
? 由 Leung 寫于 2021 年 10 月 20 日
? 參考:[NFC]NDEF和RTD協(xié)議介紹
NFC Forum發(fā)布NFC數(shù)據(jù)交換格式(NDEF)規(guī)范
BearPi-HM_Nano開發(fā)板基礎(chǔ)外設(shè)開發(fā)——I2C控制NFC芯片