RFID基础——ISO15693标签存储结构及访问控制命令说明
ISO15693协议标准的高频RFID无源IC卡,具有高度防冲突与长距离运作等优点,主要应用与资产管理、图书馆管理、供应链管理、医疗领域。开发基于 ISO15693 的应用首先需要了解标签的存储结构以及如何读写操作。
ISO15693-3详细描述了 VICC 的内存结构、 VICC 和 VCD 之间通信所使用的数据帧格式以及访问控制 VICC 的命令。本文将结合ICODE SLIX详细介绍这些内容。
VICC 的内存结构#
ISO15693-3中规定的命令假定物理内存以固定大小的块(或页)出现。
- 最多可寻址256个块。
- 块大小最多可达256位。
- 这导致最大内存容量高达8 kBytes (64 kBits)。
内存中包含配置区域和用户存储两部分。配置区域存储了唯一标识符(UID)、应用族标识符(AFI)、数据存储格式标识符(DSFID)。以 ICODE SLIX 为例:
ICODE SLIX存储器分为32个块、每个块由4字节(32位)组成,共128字节,如下图,上部4个块(-4、-3、-2、-1块)分别用于UID(64位唯一ID序列号)、特殊功能(EAS、AFI、DSFID)和写入控制位,其他28个块为用户数据块。
Block -4 & Block -3#
Block -4 & Block -3共八个字节,用于记录标签的UID,每个字节含义如下图所示:
UID7(64~57):固定值“E0”
UID6(56~49):厂商代码,“04”代表NXP,“07”代表TI,"23"代表上海贝岭
UID5(48~41):产品分类代码,“01”代表“ICODE SLIX”,“80”代表Tag-it HF-I Plus Chip,“00”代表Tag-it HF-I Plus Inlay
UID4UID0(401):标签芯片生产序列代码
Block -2#
Block -2是特殊功能区,第一个字节未使用,剩下三个字节分别用于记录EAS、AFI和DSFID。
EAS(Electronic Article Surveillance,电子防盗系统)主要用来防止物品被盗,标签管理者可以设置(EAS=1)和清除(EAS=0)EAS标识,当设置有EAS标识的标签通过读写器的作用范围时,读写器会识别EAS标识,发出警报。EAS的数据结构如下图,EAS的LSB的第一位(e位)写1代表EAS标示有效,写0代表清除EAS标示,其他位无效。
特殊功能AFI(Application Family Idenfifier,应用族标识符),可事先规定应用族代码并写入AFI字节,在处理多个标签的时候进行分类处理。
例如在物流中心处理大量货物时,可根据标签上的AFI应用族标识符来区分是出口货物还是内销货物。
AFI被编码在一个字节里,由两个半字节组成。
AFI的高位半字节用于编码一个特定的或所有应用族,下图是AFI的族编码定义。
AFI的低位半字节用于编码一个特定的或所有应用子族。子族不同于0的编码有其自己的所有权。
数据存储格式标识符DSFID(data storage format identifier)。数据存储格式标识符指出了数据在 VICC 内存中是怎样构成的。DSFID 被相应的命令编程和锁定。DSFID 被编码在一个字节里。DSFID 允许即时知道数据的逻辑组织。假如VICC不支持DSFID 的编程,VICC将以值“0”作为应答。
Block -1#
BLock -1是写入控制位,具体控制分配见下图,它可以控制每个数据块的写入和块-2(特殊功能块)每个字节的写入。写入位1代表写入保护,且不可再修改控制位。其中Byte0用于控制 Block -2特殊功能区每个字节的写入保护。
传输协议#
传输协议(或协议)定义了 VCD 和 VICC 之间指令和数据双向交换的机制。它基于“VCD 首先说”的概念。这意味着除非收到并正确地解码一个 VCD 发送来的指令,任何 VICC 将不会开始传输。
- 每一次完整的数据传输都是 VCD 向 VICC 发送一次请求,然后从 VICC(s)到 VCD 的一次响应。
- 每一次请求和每一次响应包含在一帧内。帧分隔符(SOF, EOF)在 ISO/IEC 15693-2 中有规定。
- 每次请求包括以下的域:标志、命令编码、强制和可选的参数域(取决于命令)、应用数据域、CRC。
- 每次响应包括以下的域:标志、强制和可选的参数域(取决于命令)、应用数据域、CRC。
- 协议是双向的。一帧中传输的位的个数是 8 的倍数,即整数个字节。
- 一个单字节域在通讯中首先传输最低有效位(LSBit)。
- 一个多字节域在通讯中首先传输最低有效字节(LSByte),每字节首先传输最低有效位(LSBit)。
- 标志的设置表明可选域的存在。当标志设置为 1,这个域存在。当标志设置为 0,这个域不存在。
- RFU 标志应设置为 0。
请求格式#
请求数据的格式包含:标志、命令编码、参数和数据、CRC
SOF | 标志 | 命令编码 | 参数 | 数据 | CRC | EOF |
---|---|---|---|---|---|---|
8 bits | 8 bits | 可选 | 可选 | 16 bits |
请求标志#
在请求中,字段“flags”指定VICC要执行的操作,以及相应的字段是否存在。它包含 8 位(bits)。为了方便描述,以b1,b2,……,b7,b8
表示,每一位(bit)含义如下图所示:
其中高半字节b5~b8
的含义取决于低半字节中b3
的值。b3
值为0时,b5~b8
的含义参照图中表4的描述,b3
值为1时,b5~b8
的含义参照图中表5的描述。
b3
表示目录标志,这个名称的原文是Inventory_flag
,翻译为目录标志感觉过于生硬。其实是用于标志请求命令是否为列出射频场中符合条件的 VICCs,简单的说就是寻卡。
标志的典型选值及说明如下:
值(16进制) | 值(2进制) | 说明 |
---|---|---|
0x06 | 0000 0110 | 操作多张卡,不需要AFI参数,高速率 |
0x26 | 0010 0110 | 操作单张卡(射频场中有多张卡时都不会响应),不需要AFI参数,高速率 |
0x16 | 0001 0110 | 操作多张卡,需要AFI参数,高速率 |
0x36 | 0011 0110 | 操作单张卡,需要AFI参数,高速率 |
0x22 | 0010 0010 | 请求寻址需要UID,高速率 |
命名编码#
ISO15693中把请求命令分为四种类型:
- 强制的:命令码范围从 '01' 到 '1F' 。所有 VICCs 都支持强制命令码。
- 可选的:命令码范围从 '20' 到 '9F' 。VICCs 可以有选择地支持可选的命令码。
- 定制的:命令码范围从 'A0' 到 'DF' 。VICCs 支持定制命令,在它们的可选范围内,执行由制造商规定的功能。标志的功能(包括保留位)将不会被修改,除非是选择标志。可以被定制的域仅限于参数和数据域。
- 私有的:命令码范围从 'E0' 到 'FF' 。这个命令方便 IC 和 VICC 制造商用于各种目的的应用,如测试、系统信息编程等等。它们在这个标准中没有作规定。
响应格式#
响应标志#
在一次响应中,字段“flags”指出 VICC 是怎样完成动作的,并且相应字段是否出现。响应标志由 8 bits 组成。
响应错误码#
当错误标志被 VICC 置位,将包含错误码域,并提供出现的错误信息。错误码在表 7 中定义。假如 VICC 不支持表 7 中列出的规定错误码,VICC 将以错误码 '0F' 应答("不给出错误信息")。
命令详解#
强制的命令#
寻卡命令(INVENTORY=0x01)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+[AFI(1B)+MASK长度(1B)+mask值(1B)]+CRC16(2B) |
06 01 1E 4A | |
响应(VICC TO VCD) | 标志(1B)+DSFID(1B)+UID(8B)+CRC16(2B) |
00 00 99 07 30 1A 53 01 04 E0 A8 8D |
保持静默(STAY QUIET=0x02)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B) +CRC16(2B)(2B) |
22 02 99 07 30 1A 53 01 04 E0 A9 E2 | |
响应(VICC TO VCD) | 标志(1B) |
00 |
可选的命令#
读单个数据块(READ SINGLE BLOCK=0x20)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+数据(块数量1B)+CRC16(2B) |
22 20 99 07 30 1A 53 01 04 E0 01 23 2D | |
响应(VICC TO VCD) | 标志(1B)+数据(4B)+CRC16(2B) |
00 61 62 63 64 C2 90 |
写单个数据块(WRITE SINGLE BLOCK=0x21)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+块序号(1B)+数据(块长度4B)+CRC16(2B) |
22 21 99 07 30 1A 53 01 04 E0 01 61 62 63 64 5B 8B | |
响应(VICC TO VCD) | 标志(1B)+CRC16(2B) |
00 78 F0 |
锁定数据块(LOCK BLOCK=0x22)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+块序号(1B)+CRC16(2B) |
22 22 99 07 30 1A 53 01 04 E0 1B B6 CA | |
响应(VICC TO VCD) | 标志(1B)+CRC16(2B) |
00 78 F0 |
读多个数据块(READ MULTIPLE BLOCKS=0x23)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+数据(首块序号+块数量)(2B)+CRC16(2B) |
22 23 99 07 30 1A 53 01 04 E0 00 03 3C 36 | |
响应(VICC TO VCD) | 标志(1B)+数据(4B)*(块数量+1)+CRC16(2B) |
00 2C 00 00 00 61 62 63 64 00 00 00 00 00 00 00 00 8B 21 |
读取多个数据块时,块序号范围是 0x00~0xFF
,块数量的值比VICC返回的块数要少1。也就是请求数据中块数量的值为0,表示请求读单个块,值为 0x03
表示请求4个块。
写多个数据块(WRITE MULTIPLE BLOCKS=0x24)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+首块序号(1B)+块数量(1B)+数据(块长度NB)+CRC16(2B) |
22 24 99 07 30 1A 53 01 04 E0 00 03 01 01 01 01 02 02 02 02 03 03 03 03 96 D3 | |
响应(VICC TO VCD) | 标志(1B)+CRC16(2B) |
00 78 F0(写入成功) 01 0F 68 EE(写入失败 |
由于是可选命令,并不是所有的标签都支持,例如最流行的NXP的ICODE SLIX、ICODE SLIX2系列都没有支持该命令 [1]。ST的ST25DV04K、ST25DV16K、ST25DV64K支持该命令。[2]
选择(SELECT=0x25)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+CRC16(2B) |
22 25 99 07 30 1A 53 01 04 E0 72 FC | |
响应(VICC TO VCD) | 标志(1B)+CRC16(2B) |
00 78 F0 |
复位(RESET TO READY=0x26)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+CRC16(2B) |
22 26 99 07 30 1A 53 01 04 E0 75 2A | |
响应(VICC TO VCD) | 标志(1B)+CRC16(2B) |
00 78 F0 |
写AFI(WRITE AFI=0x27)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+AFI值(1B)+CRC16(2B) |
22 27 99 07 30 1A 53 01 04 E0 00 5F F8 | |
响应(VICC TO VCD) | 标志(1B)+CRC16(2B) |
00 78 F0 |
锁定AFI(LOCK AFI=0x28)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+CRC16(2B) |
22 28 99 07 30 1A 53 01 04 E0 A0 F1 | |
响应(VICC TO VCD) | 标志(1B)+CRC16(2B) |
00 78 F0 |
写DSFID(WRITE DSFID=0x29)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+DSFID值(1B)+CRC16(2B) |
22 29 99 07 30 1A 53 01 04 E0 06 92 1C | |
响应(VICC TO VCD) | 标志(1B)+CRC16(2B) |
00 78 F0 |
锁定DSFID(LOCK DSFID=0x2A)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+CRC16(2B) |
22 2A 99 07 30 1A 53 01 04 E0 5A 6A | |
响应(VICC TO VCD) | 标志(1B)+CRC16(2B) |
00 78 F0 |
获取系统信息(GET SYSTEM INFORMATION=0x2B)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+CRC16(2B) |
22 2B 99 07 30 1A 53 01 04 E0 A7 27 | |
响应(VICC TO VCD) | 标志(1B)+信息标志(1B)+UID(8B)+DSFID(1B)+AFI(1B)+信息域([VICC内存信息(2B)]+[IC参考(1B)])+CRC16(2B) |
00 0F 99 07 30 1A 53 01 04 E0 01 01 1B 03 01 BB C6 |
VICC 内存容量信息占两个字节,记录了块容量(以字节为单位),块数量。
块容量以 5 bits 的字节数量表达出来,允许定制到 32 字节,即 256 bits。它比实际的字节数目要少1。例如:值 1F
表示 32 字节,值 00
表示 1 字节。
块数目是基于 8 bits,允许定制到 256 个块。它比实际的字节数目要少 1。例如:值 FF
表示 256 个块,值 00
表示 1 个块。
最高位的 3 个 bits 保留做未来备用,可以设置为 0。
IC 参考基于 8 个 bits,它的意义由 IC 制造商定义。
上述示例中,响应的信息标志为 0x0F
,表示 DSFID 、 AFI、VICC内存容量、IC参考都会在结果中显示。其中信息域中VICC内存容量的值为1B 03
,由于传输协议中定义了一个多字节域在通讯中首先传输最低有效字节(LSByte),每字节首先传输最低有效位(LSBit),因此 1B
是表示有27个数据块,03
表示每块4个字节。
获取多个块安全状态(GET MULTIPLE BLOCK SECURITY STATUS=0x2C)#
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+UID(8B)+首块序号(1B)+块数量(1B)+CRC16(2B) |
22 2C 99 07 30 1A 53 01 04 E0 09 06 C5 AA | |
响应(VICC TO VCD) | 标志(1B)+块安全状态(nB)+CRC16(2B) |
00 00 00 00 00 00 00 00 73 83 |
与读取多个块的数据一样,块的编码从 '00' 到 'FF' (0 到 255)。请求中块的数量比块安全状态的数量少 1,VICC 将在其响应中返回块安全状态。例如:在“块数量”域中,值 '06' 要求返回 7 个块安全状态。在“块数量”域中,值 '00' 要求返回单个块安全状态。
定制的命令#
NXP、TI、ST等厂商对于定制命令的支持各不相同。
上述是几个有代表性的标签系列,还有一些国产标签也会兼容上述标签,比如复旦微电子的FM13HF01兼容ICODE SLIX,[4] 因此也会兼容其定制的命令。EAS(Electronic Article Surveillance,电子防盗系统)是 RFID 的常见应用,上述标签系列只有ICODE SLIX支持 EAS。以下是 EAS 相关的命令。
开启EAS(SET EAS=0xA2)#
命令用来在 EAS 未锁定的情况下开启EAS模式。如果启用了 EAS 密码保护,首先需要通过 SET PASSWORD 命令把 EAS 密码传输到 VICC上。
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+ IC Mfg code(1B)+UID(8B)+CRC16(2B) |
22 A2 04 99 07 30 1A 53 01 04 E0 44 C1 | |
响应(VICC TO VCD) | 标志(1B)+CRC16(2B) |
00 78 F0 |
重置EAS(RESET EAS=0xA3)#
命令用来在 EAS 未锁定的情况下关闭EAS模式。如果启用了 EAS 密码保护,首先需要通过 SET PASSWORD 命令把 EAS 密码传输到 VICC上。
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+ IC Mfg code(1B)+UID(8B)+CRC16(2B) |
22 A3 04 99 07 30 1A 53 01 04 E0 63 ED | |
响应(VICC TO VCD) | 标志(1B)+CRC16(2B) |
00 78 F0 |
锁EAS(RESET EAS=0xA4)#
命令用来锁定EAS模式的当前状态和EAS ID,一旦锁定就无法解锁。如果启用了 EAS 密码保护,首先需要通过 SET PASSWORD 命令把 EAS 密码传输到 VICC上。
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+ IC Mfg code(1B)+UID(8B)+CRC16(2B) |
22 A4 04 99 07 30 1A 53 01 04 E0 96 29 | |
响应(VICC TO VCD) | 标志(1B)+CRC16(2B) |
00 78 F0 |
EAS警报(EAS ALARM=0xA5)#
如果开启了 EAS 模式,执行该命令后 VICC 会返回 EAS 序列,如果关闭了 EAS 模式,VICC 不做响应。
请求(VCD TO VICC) | 标志(1B)+命令编码(1B)+厂商代码(1B)+UID(8B)+CRC16(2B) |
22 A5 04 99 07 30 1A 53 01 04 E0 B1 05 | |
响应(VICC TO VCD) | 标志(1B)+ EAS 序列(32)+ CRC16(2B) |
00 2F B3 62 70 D5 A7 90 7F E8 B1 80 38 D2 81 49 76 82 DA 9A 86 6F AF 8B B0 F1 9C D1 12 A5 72 37 EF 50 85 |
CRC计算方法#
ISO15693协议中的 CRC 校验没有从常用的21个标准CRC参数模型[5]中选择,而是使用下面的参数模型
CRC 类型 | 长度 | 多项式 | 方向 | 预置 | 余数 |
---|---|---|---|---|---|
ISO/IEC 13239 | 16 bits | X16 + X12 + X5 + 1 = 8408 | 向后 | FFFF | F0B8 |
代码实现如下:
/// <summary>
/// Provide a calculation of CRC for ISO15693
/// The PN5180 module seems to have implemented crc and does not need to calculate when coding
/// </summary>
/// <param name="buffer">The buffer to process</param>
/// <param name="crc">The CRC, Must be a 2 bytes buffer</param>
public void CalculateCrcIso15693(ReadOnlySpan<byte> buffer, Span<byte> crc)
{
if (crc.Length != 2)
{
throw new ArgumentException($"The length of crc must be 2 bytes.", nameof(crc));
}
ushort polynomial = 0x8408;
ushort currentCrc = 0xFFFF;
// ISO15693-3.pdf
for (int i = 0; i < buffer.Length; i++)
{
currentCrc = (ushort)(currentCrc ^ buffer[i]);
for (int j = 0; j < 8; j++)
{
if ((currentCrc & 0x0001) != 0)
{
currentCrc = (ushort)((currentCrc >> 1) ^ polynomial);
}
else
{
currentCrc = (ushort)(currentCrc >> 1);
}
}
}
currentCrc = (ushort)~currentCrc;
crc[0] = (byte)(currentCrc & 0xFF);
crc[1] = (byte)((currentCrc >> 8) & 0xFF);
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~