HI3861學(xué)習(xí)筆記(17)——NFC標(biāo)簽NT3H1201使用

一涡相、簡介

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í)別和選擇流程:
  1. 上電復(fù)位(POR)后乙埃,NTAG I2C切換到空閑狀態(tài)(IDLE)闸英。只有在這種狀態(tài)下才會(huì)從NFC設(shè)備接收請(qǐng)求命令(REQA)或喚醒命令(WUPA),任何其他在這種狀態(tài)下接收到的數(shù)據(jù)被解釋為錯(cuò)誤介袜。
  2. 在就緒狀態(tài)1(READY 1)甫何,NFC設(shè)備通過防碰撞選擇第一層的防碰撞命令(ANTICOLLISION)或選擇命令(SELECT)解析出UID的第一部分(3個(gè)字節(jié))。在執(zhí)行成功防碰撞選擇第一層的選擇命令(SELECT)后遇伞,進(jìn)入就緒狀態(tài)2辙喂。
  3. 在就緒狀態(tài)2(READY 2),NTAG I2C支持NFC設(shè)備通過防碰撞選擇第二層的防碰撞命令(ANTICOLLISION)解析出UID的第二部分(4個(gè)字節(jié))鸠珠。在執(zhí)行成功防碰撞選擇第二層的選擇命令(SELECT)后巍耗,進(jìn)入激活狀態(tài)。
  • 內(nèi)存操作:
  1. 所有內(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)霎迫。
  2. 停止和空閑狀態(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è)記錄的位置分別需要用 NDEFMiddlePosNDEFLastPos 來定義禁添。

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.hrtdUri.h 中 RTD 協(xié)議進(jìn)行處理。然后與 ndef.hNT3HwriteRecord() 進(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芯片

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市逛薇,隨后出現(xiàn)的幾起案子捺疼,更是在濱河造成了極大的恐慌,老刑警劉巖永罚,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啤呼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡官扣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門羞福,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惕蹄,“玉大人,你說我怎么就攤上這事治专÷袅辏” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵张峰,是天一觀的道長泪蔫。 經(jīng)常有香客問我,道長喘批,這世上最難降的妖魔是什么撩荣? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮谤祖,結(jié)果婚禮上婿滓,老公的妹妹穿的比我還像新娘老速。我一直安慰自己粥喜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布橘券。 她就那樣靜靜地躺著额湘,像睡著了一般卿吐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锋华,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天嗡官,我揣著相機(jī)與錄音,去河邊找鬼毯焕。 笑死衍腥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的纳猫。 我是一名探鬼主播婆咸,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼芜辕!你這毒婦竟也來了尚骄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤侵续,失蹤者是張志新(化名)和其女友劉穎倔丈,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體状蜗,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡需五,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了轧坎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖记劝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤耕驰,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布饭弓,位于F島的核電站弟断,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜排霉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浪谴,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至叫挟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奋献,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兆解。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像现拒,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容