当前位置:   article > 正文

HI3861学习笔记(17)——NFC标签NT3H1201使用_nfc tag带gpio检测

nfc tag带gpio检测

一、简介

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 射频通信流程

  • 识别和选择流程:
  1. 上电复位(POR)后,NTAG I2C切换到空闲状态(IDLE)。只有在这种状态下才会从NFC设备接收请求命令(REQA)或唤醒命令(WUPA),任何其他在这种状态下接收到的数据被解释为错误。
  2. 在就绪状态1(READY 1),NFC设备通过防碰撞选择第一层的防碰撞命令(ANTICOLLISION)或选择命令(SELECT)解析出UID的第一部分(3个字节)。在执行成功防碰撞选择第一层的选择命令(SELECT)后,进入就绪状态2。
  3. 在就绪状态2(READY 2),NTAG I2C支持NFC设备通过防碰撞选择第二层的防碰撞命令(ANTICOLLISION)解析出UID的第二部分(4个字节)。在执行成功防碰撞选择第二层的选择命令(SELECT)后,进入激活状态。
  • 内存操作:
  1. 所有内存操作均在激活状态(ACTIVE)下操作。如读取16Byte操作(READ)、快速读取操作(FAST_READ)、写入操作(WRITE)、扇区选择操作(SECTOR_SELECT)、获取版本操作(GET_VERSION)。取决于其先前的状态,NTAG I2C返回到空闲状态(IDLE)或停止状态(HALT)。
  2. 停止和空闲状态构成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

二、硬件连接

功能口引脚
SCLGPIO0
SDAGPIO1
LA&LB线圈
VSSGND
VCC3.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"
    ]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

注意这里加入以下三条语句,防止有些函数中的参数没有用到,编译报错!!!

cflags = [ "-Wno-unused-variable" ]
cflags += [ "-Wno-unused-but-set-variable" ]
cflags += [ "-Wno-unused-parameter" ]
  • 1
  • 2
  • 3

该文件相当于 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_SDA
    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_0, WIFI_IOT_IO_FUNC_GPIO_0_I2C1_SDA);

    //GPIO_1复用为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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

这部分代码为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);
  • 1
  • 2
  • 3
  • 4

这部分的代码是向NFC芯片写入数据,但需要写入2个记录时,第2个记录的位置需要用 NDEFLastPos 来定义;当需要写入3个记录时,第2个和第3个记录的位置分别需要用 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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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_ */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

5.3 nfc.c

storeUrihttp()storeText() 两个函数首先按照 rtdText.hrtdUri.h 中 RTD 协议进行处理。然后与 ndef.hNT3HwriteRecord() 进行记录写入。

#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 );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

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_ */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

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_ */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

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_ */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

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_ */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

5.5 ndef.h

#ifndef NDEF_H_
#define NDEF_H_

#include "NT3H.h"

bool NT3HwriteRecord(const NDEFDataStr *data);

#endif /* NDEF_H_ */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226

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 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_ */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122

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];
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189

六、查看打印


用带NFC的手机靠近读取,会弹出识别到一个网页


• 由 Leung 写于 2021 年 10 月 20 日

• 参考:[NFC]NDEF和RTD协议介绍
    NFC Forum发布NFC数据交换格式(NDEF)规范
    BearPi-HM_Nano开发板基础外设开发——I2C控制NFC芯片

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/223459
推荐阅读
相关标签
  

闽ICP备14008679号