概述

上一篇文章最后讲到了读写Block中的数据,这篇文章就来说说如何把Block中的数据解码成NDEF格式的数据并提取其中的有效内容.

根据芯片来看

这个Block数据转NDEF数据的过程会根据不同芯片或者不同标签类型而不同,这里我用的是Type 5标签芯片:ST25DV04KC.所以需要去意法半导体官网的这个芯片的介绍页面下方的"应用手册"这一栏去下载对应的说明文档,在他们页面上,对应的说明文档叫做<<NDEF management with ST25DV-I2C Series, and ST25TV16K and ST25TV64K products>>在文档中,可以看到如下内容:

在每个用户数据区域的第一个Block的开头,都会有一个CC数据.根据不同的芯片型号,CC数据的大小也不一样.比如在ST25DV04K和ST25DV04KC是4字节,在ST25DV16K,ST25DV16KC, ST25DV64K, ST25DV64KC, ST25TV16K, ST25TV64K上是8字节.

CC数据的第一个字节是个特定的特征值(Magic number),对于支持单字节地址模式的芯片,这个特征值是0xE1,对于支持双字节地址模式的芯片,这个特征值是0xE2.对于存储空间大于2KB的芯片,就必须用双字节地址模式了.

CC数据的第二个字节是版本号以及访问控制,每个bit的作用如下:

  • bit7-bit6:主版本号,例如01b=Version 1.x
  • bit5-bit4:次版本号:例如00b=Version x.0
  • bit3-bit2:读取控制:
    • 00b=Always
    • 01b=RFU
    • 10b=Proprietary
    • 11b=REF
  • bit1-bit0:写入控制:
    • 00b=AlWays
    • 01b=RFU
    • 10b=Proprietary
    • 11=Never

CC数据的第三个(或和第四个字节)是MLEN(数据长度),T5T数据区域的长度=MLEN*8.例如在ST25DV04KC的芯片上,因为CC数据是4个字节,那么如果用户数据区域都作为T5T数据区域,那MLEN=(512-4)/8=63=0x3F,如果只有一部分用户数据区域作为T5T数据区域的话,比如用了128byte,那就是MLEN=128/8=16=0x10Byte.对于更高容量的芯片计算方式也类似,只是需要考虑更大的CC数据.

但上面的计算方式只适用于iOS系统以及Android Oreo版本或更高版本的系统,对于Android Oreo以下版本系统,需按用户数据区域的总大小去计算,例如小容量芯片,CC数据是4字节的情况,MLE=512/8/64=0x40,对于大容量芯片,CC数据是8字节的情况下MLEN=8192/8=1024=0x400.这种计算方式不符合NFC规范,但是支持iOS与所有Android版本的正常使用.

CC数据的第四个字节(或第五个字节)是其他功能信息,每个bit的作用如下:

  • bit7-bit5=RFU
  • bit4=是否支持专用框架
  • bit3=是否锁定Block(建议设置为0)
  • bit2-bit1=RFU(建议设置为0)
  • bit0=是否支持读取多个Block命令

然后CC数据后面跟着的是T5T数据.在T5T数据中包含了NDEF Message TLV,其中的TLV分别代表了"Tag","Length","Value". NDEF Message TLV的格式如下:

名称长度描述
T1byte固定为0x03,代表NDEF消息TLV
L1byteTLV长度
VN byteTLV值(长度取决于上面的TLV长度值)

TLV值里面就是NDEF记录(NDEF Record)了.继续按NDEF记录的格式去解析即可.NDEF记录内部大体分为两个部分,头部(Header)以及负载(Playload).其中头部的结构如下:

字段名称长度描述
标识符(Identifier)1byte比较复杂,每个bit都代表了不同含义,具体看下面的表格
类型长度1byte是Header中类型字段的长度
负载长度0-4byte是负载的长度
ID长度0-1byte是Header中ID字段的长度
类型N byte负载数据的类型
ID0-N byte配合URI类型的负载一起使用,它使得一个NFC Record能通过ID来指向另外一个NFC Record.
位置字段名称描述
bit7消息开始标识符当这个NDEF记录是第一条记录时本标识符为1
bit6消息结束标识符当这个NDEF记录是最后一条记录时本标识符为1
bit5是否分块标识符如果分块储存为1,否则为0
bit4短记录标识符如果本标识符为1,则上一张表中的负载长度字段占用1byte,否则为4byte
bit3-bit2ID长度标识符如果本标识符为1,则上一表中的ID与ID长度字段存在.为0则不存在.
bit1-bit0格式类型名称具体的取值见下表
格式类型名称取值
无格式类型0x00
NFC组织定义的常见格式0x01
多用途 internet 扩展0x02
绝对URL地址0x03
第三方组织定义的格式0x04
Payload中的格式类型未知0x05
这种类型的数据用于NFC记录分片.例如一个大的数据需要通过多个NFC记录来承载,除第一个NFC记录分片外,该数据对应的其他NFC记录分片都必须设置为Unchanged.0x06
保留0x07

其中如果是使用"NFC组织定义的常见格式",则Header中类型字段有以下几个取值.

  • URI Record Type:用于存储URI数据,对应Type字段取值为"U".
  • Text Record Type:用于存储文本数据,对应Type字段取值为"T".
  • Signature Record Type:用于存储数字签名数据,对应Type字段取值为"Sig".
  • Smart Poster Record Type:智能海报,用于存储与该海报相关的一些资讯信息,如图片、相关介绍等,对应Type字段取值为"Sp".
  • Generic Control Record Type:用于传递控制信息,对应Type字段取值为"Gc"

实例解析

至此,从底层Block数据到NDEF记录的介绍就结束了.接下来举一个实际的例子:

Block数据为:

E1 40 40 1 3 53 D4 10 40 69 6E 66 69 6E 6F 76 6F 3A 63 67 6D 69 6E 66 6F 6C 50 38 65 4B 66 5A 2F 66 4A 57 48 75 4D 37 62 59 70 39 57 56 37 54 6A 50 64 77 2F 33 76 37 6A 4C 71 71 37 31 45 63 36 42 53 33 57 75 64 77 75 67 51 4C 63 41 30 2B 71 6F 79 70 67 72 34 4D 39 FE FF FF 0 0 0 0

开始做解析,前面的E1 40 40 1为CC数据.

3 53为TLV中的"T"跟"L",后面从D4开始的内容就是TLV中的"V"了,也是NDEF记录的开始,我们单独把NDEF记录的前面一部分提取出来:

D4 10 40 69 6E 66 69 6E 6F 76 6F 3A 63 67 6D 69 6E 66 6F 6C 50 38 65 4B 66 5A 2F 66 4A 57 48 75 4D 37 62 59 70 39 57 56 37 54 6A 50 64 77 2F 33 76 37

NDEF记录中第1个字节:0xD1 为标识符.

NDEF记录中第2个字节:0x10 类型长度.

NDEF记录中第3个字节:0x40 负载长度.

NDEF记录中第4个字节开始的共16个字节为类型数据:69 6E 66 69 6E 6F 76 6F 3A 63 67 6D 69 6E 66 6F,转换成字符串就是"infinovo:cgminfo".

NDEF记录中从第20个字节开始就是负载数据了: 6C 50 38 65 4B 66 5A 2F 66 4A 57 48 75 4D 37 62 59 70 39 57 56 37 54 6A 50 64 77 2F 33 76 37 .......