2.1 uctypes 模块API手册
前言
概述
本文档主要介绍uctypes模块API。
读者对象
本文档(本指南)主要适用于以下人员:
- 技术支持工程师
- 软件开发工程师
缩略词定义
简称 | 说明 |
---|---|
修订记录
文档版本号 | 修改说明 | 修改者 | 日期 |
---|---|---|---|
V1.0 | 初版 | 软件部 | 2023-09-17 |
1. 概述
以结构化方式访问二进制数据
该模块为MicroPython实现“外部数据接口”。 它背后的想法类似于CPython的ctypes
模块,但实际的API是不同的,流线型和小尺寸优化。 该模块的基本思想是定义具有与C语言允许的功能大致相同的数据的数据结构布局,然后使用熟悉的点语法访问它以引用子字段。
警告
uctypes模块允许访问机器的任意内存地址(包括I / O和控制寄存器)。 不小心使用它可能会导致崩溃,数据丢失,甚至硬件故障。
也可参考
ustruct
模块用于访问二进制数据结构的标准Python方法(不能很好地扩展到大型和复杂的结构)。
2. 结构说明
2.1 示例
import uctypes
# Example 1: Subset of ELF file header
# https://wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
ELF_HEADER = {
"EI_MAG": (0x0 | uctypes.ARRAY, 4 | uctypes.UINT8),
"EI_DATA": 0x5 | uctypes.UINT8,
"e_machine": 0x12 | uctypes.UINT16,
}
# "f" is an ELF file opened in binary mode
buf = f.read(uctypes.sizeof(ELF_HEADER, uctypes.LITTLE_ENDIAN))
header = uctypes.struct(uctypes.addressof(buf), ELF_HEADER, uctypes.LITTLE_ENDIAN)
assert header.EI_MAG == b"\x7fELF"
assert header.EI_DATA == 1, "Oops, wrong endianness. Could retry with uctypes.BIG_ENDIAN."
print("machine:", hex(header.e_machine))
# Example 2: In-memory data structure, with pointers
COORD = {
"x": 0 | uctypes.FLOAT32,
"y": 4 | uctypes.FLOAT32,
}
STRUCT1 = {
"data1": 0 | uctypes.UINT8,
"data2": 4 | uctypes.UINT32,
"ptr": (8 | uctypes.PTR, COORD),
}
# Suppose you have address of a structure of type STRUCT1 in "addr"
# uctypes.NATIVE is optional (used by default)
struct1 = uctypes.struct(addr, STRUCT1, uctypes.NATIVE)
print("x:", struct1.ptr[0].x)
# Example 3: Access to CPU registers. Subset of STM32F4xx WWDG block
WWDG_LAYOUT = {
"WWDG_CR": (0, {
# BFUINT32 here means size of the WWDG_CR register
"WDGA": 7 << uctypes.BF_POS | 1 << uctypes.BF_LEN | uctypes.BFUINT32,
"T": 0 << uctypes.BF_POS | 7 << uctypes.BF_LEN | uctypes.BFUINT32,
}),
"WWDG_CFR": (4, {
"EWI": 9 << uctypes.BF_POS | 1 << uctypes.BF_LEN | uctypes.BFUINT32,
"WDGTB": 7 << uctypes.BF_POS | 2 << uctypes.BF_LEN | uctypes.BFUINT32,
"W": 0 << uctypes.BF_POS | 7 << uctypes.BF_LEN | uctypes.BFUINT32,
}),
}
WWDG = uctypes.struct(0x40002c00, WWDG_LAYOUT)
WWDG.WWDG_CFR.WDGTB = 0b10
WWDG.WWDG_CR.WDGA = 1
print("Current counter:", WWDG.WWDG_CR.T)
2.2定义结构布局
结构布局由“描述符”定义 - 一个Python字典,它将字段名称编码为键,以及将它们作为关联值访问它们所需的其他属性:
{
"field1": <properties>,
"field2": <properties>,
...
}
目前,uctypes
需要明确规定每个字段的偏移量。 从结构开始以字节为单位给出偏移量。
以下是各种字段类型的编码示例:
- 标量类型:
"field_name": offset | uctypes.UINT32
换句话说,该值是标量类型标识符,与结构起始处的字段偏移量(以字节为单位)进行或运算。
- 递归结构:
"sub": (offset, {
"b0": 0 | uctypes.UINT8,
"b1": 1 | uctypes.UINT8,
})
即,值是2元组,其第一个元素是偏移量,第二个是结构描述符字典(注意:递 归描述符中的偏移量与其定义的结构相关)。 当然,递归结构不仅可以通过文字字典指定,还可以通过按名称引用结构描述符字典(前面定义)来指定。
- 原始类型的数组:
"arr": (offset | uctypes.ARRAY, size | uctypes.UINT8),
即,值是一个2元组,其第一个元素是ARRAY标志与偏移进行OR运算,第二个是标量元素类型ORed数组中的元素数。
*聚合类型数组:
"arr2": (offset | uctypes.ARRAY, size, {"b": 0 | uctypes.UINT8}),
即,值是一个3元组,其第一个元素是ARRAY标志,与偏移相关,第二个是数组中的元素数,第三个是元素类型的描述符。
*指向原始类型的指针:
"ptr": (offset | uctypes.PTR, uctypes.UINT8),
即,值是2元组,其第一个元素是PTR标志与偏移进行OR运算,第二个元素是标量元素类型。
*指向聚合类型的指针:
"ptr2": (offset | uctypes.PTR, {"b": 0 | uctypes.UINT8}),
即,值是2元组,其第一个元素是PTR标志与偏移量进行OR运算,第二个元素是指向的类型的描述符。
*比特场:
"bitf0": offset | uctypes.BFUINT16 | lsbit << uctypes.BF_POS | bitsize << uctypes.BF_LEN,
即,value是一种包含给定位域的标量值(类型名称类似于标量类型,但前缀为“BF”),与包含位域的标量值的偏移进行OR运算,并进一步与位位置和位长度的值进行“或”运算。标量值内的位域,分别移位BF_POS和BF_LEN位。位域位置从标量的最低有效位(具有0的位置)开始计数,并且是字段的最右位的数量(换句话说,它是标量需要向右移位的位数)提取位域)。
在上面的例子中,首先在偏移0处提取UINT16值(当访问硬件寄存器时,这个细节可能很重要,需要特定的访问大小和对齐),然后是最右边的位是此UINT16的lsbit位的位域,以及length是bitsize bits,将被提取。例如,如果lsbit为0且bitsize为8,那么它将有效地访问UINT16的最低有效字节。
注意,位域操作独立于目标字节字节序,特别是上面的例子将在小端和大端结构中访问UINT16的最低有效字节。但它取决于最低有效位被编号为0.某些目标可能在其原生ABI中使用不同的编号,但是“uctypes”总是使用上述的标准化编号。
3. API描述
3.1 struct类
class uctypes.struct(addr, descriptor, layout_type=NATIVE)
基于内存中的结构地址,描述符(编码为字典)和布局类型(参见下文)来实例化“外部数据结构”对象。
【参数】
- addr: 内存地址
- descriptor: 结构描述
- layout_type: 结构大小端,可选参数,默认NATIVE
【返回值】
struct类实例
3.2 sizeof
uctypes.sizeof(struct, layout_type=NATIVE)
以字节为单位返回数据结构的大小。 struct参数可以是结构类,也可以是特定的实例化结构对象(或其聚合字段)。
【参数】
- struct: struct类或结构描述
- layout_type: 结构大小端,可选参数,默认NATIVE
【返回值】
数据结构的大小
3.3 addressof
uctypes.addressof(obj)
返回对象的地址。 参数应该是字节,字节数组或其他支持缓冲区协议的对象(该缓冲区的地址实际上是返回的)。
【参数】
- obj: 支持缓冲区协议的对象
【返回值】
对象的地址
3.4 bytes_at
uctypes.bytes_at(addr, size)
以给定的地址和大小捕获内存作为bytes对象。 由于bytes对象是不可变的,因此内存实际上是复制并复制到bytes对象中,因此如果内存内容稍后更改,则创建的对象将保留原始值。
【参数】
- addr: 给定地址
- size: 给定大小
【返回值】
bytes对象
3.5 bytearray_at
uctypes.bytearray_at(addr, size)
将给定地址和大小的内存捕获为bytearray对象。 与上面的bytes_at()函数不同,内存是通过引用捕获的,因此它也可以写入,并且您将在给定的内存地址访问当前值。
【参数】
- addr: 给定地址
- size: 给定大小
【返回值】
bytearray对象
3.6 string_at
uctypes.string_at(addr, size=1048576)
将给定地址的内存捕获为str对象。string长度不超过指定size。
【参数】
- addr: 给定地址
- size: 给定大小,可选参数,默认1048576
【返回值】
str对象
4. 常量定义
4.1 uctypes.LITTLE_ENDIAN
little-endian压缩结构的布局类型。 (打包意味着每个字段占用描述符中定义的字节数,即对齐为1)。
4.2 uctypes.BIG_ENDIAN
big-endian压缩结构的布局类型。