xt-sdk 文档 v1.0.3
xt-sdk 嵌入式 SDK API 参考
载入中...
搜索中...
未找到
xt_std - 标准工具库

版本:0.1 | 日期:2026-07-03 | 路径: components/xt_std/


一、概述

xt_std 是 xt-sdk 的基础工具库,提供嵌入式开发中高频使用的类型定义、位操作、链表、环形缓冲区、CRC 校验、错误码和通用宏。所有子模块均为 header-only (仅头文件),函数以 static inline 或宏实现,未被引用的代码不产生任何二进制开销。

1.1 子模块列表

子模块 头文件 @defgroup 职责
xt_types(类型定义) xt_types.h xt_types(类型定义) 引入 stddef.h 、 stdint.h 、 stdbool.h ,统一基础类型来源
xt_macro(通用宏) xt_macro.h xt_macro(通用宏) 数组大小、对齐、MIN/MAX、container_of、静态断言
xt_bit(位操作) xt_bit.h xt_bit(位操作) 单位/多位操作宏:置位、清零、翻转、掩码、字段读写
xt_list(链表) xt_list.h xt_list(链表) 双向循环链表 + 单向链表,container_of 迭代器
xt_rb(环形缓冲区) xt_rb.h xt_rb 环形缓冲区:字节级读写、强制覆盖、连续空间查询
xt_crc(CRC 校验) xt_crc.h xt_crc(CRC 校验) 27 种 CRC 变体(CRC-4 ~ CRC-32),按位实现
xt_error(错误码) xt_error.h xt_error(错误码) 统一错误码( xt_err_t )与 XT_E* 宏
xt_utils(工具函数) xt_utils.h xt_utils(工具函数) 字节序转换、对齐读取、log2(总头文件,含 #include 全部子模块)

1.2 设计原则

  • **Header-only**:零链接成本,未使用的 static inline 函数被编译器优化掉
  • **防御式宏**: xt_bit.h 中所有宏均用 #if !defined() 保护,允许平台 SDK 覆盖
  • **侵入式容器**:链表节点嵌入结构体内部,通过 xt_container_of 反查宿主对象,无需泛型
  • **统一错误码**:所有模块返回 xt_err_t ( int32_t ),负值表示错误
  • **无动态内存**:所有子模块不依赖 malloc ,环形缓冲区由调用方提供内存

1.3 依赖

依赖 用途
stdint.h 定长整数类型
stddef.h size_t
stdbool.h bool
string.h xt_rb.h 内部 memcpy

无外部组件依赖。


二、核心概念

2.1 侵入式链表与 container_of

xt_list(链表) 和 xt_slist 采用 侵入式设计 :链表节点 xt_list_node 直接嵌入到业务结构体中,而非通过指针指向数据。这使得同一个结构体可以同时挂在多个链表上(只需嵌入多个 node 成员),且节点操作无需知道宿主类型。

从节点指针反查宿主结构体使用 xt_container_of 宏:

struct task {
int id;
struct xt_list_node node; // 嵌入链表节点
};
// 从 node 指针获取宿主 task 指针
struct xt_list_node *n = head->next;
struct task *t = xt_container_of(n, struct task, node);
// 等价于 xt_list_entry(n, struct task, node)
#define xt_container_of(ptr, type, member)
struct xt_list_node * next

xt_macro.h 也提供了功能相同的 XT_CONTAINER_OF 宏。两者实现一致,选择哪个取决于是否已引入对应头文件。

2.2 位操作的 GET/SET 约定

xt_bit.h 将位操作分为两类,命名遵循严格约定:

前缀 行为 示例
BIT_GET_* / BITS_GET_* **返回**修改后的值,不改变原变量 val = BIT_GET_MDF1(reg, 3)
BIT_SET_* / BITS_* **原地修改**,等价于 src = BIT_GET_*(src, ...) BIT_SET1(reg, 3)

单位操作( BIT_* )按单个 bit 索引操作;多位操作( BITS_* )按掩码操作。字段读写( BITSn_* )按 "n 位 + offset" 操作,适用于寄存器字段定义。

2.3 环形缓冲区的强制覆盖模式

xt_rb 的 write 函数接受 b_force 参数:

  • XT_RB_NO_FORCE :缓冲区满时丢弃新数据,返回实际写入字节数
  • XT_RB_FORCE :缓冲区满时覆盖最旧数据,始终写入全部请求字节

强制覆盖模式适用于 不可丢失的实时数据流 (如日志缓冲),但会丢失历史数据。此外, read 传入 dst = NULL 时仅移动读指针,不复制数据——用于跳过不需要的数据段。

2.4 CRC 变体选择

xt_crc.h 提供了 27 种 CRC 变体,覆盖 4 位到 32 位宽度。每个函数头注释包含完整参数(Poly、Init、Refin、Refout、Xorout),可在 http://www.ip33.com/crc.html 验证。常见选择:

协议 推荐 CRC 函数
Modbus RTU xt_crc16_modbus
USB xt_crc16_usb
蓝牙 BLE xt_crc24_ble
SD/MMC xt_crc7_mmc
通用文件校验 xt_crc32
XMODEM xt_crc16_xmodem

所有函数均为按位实现(非查表), 代码体积小但速度较慢 ,适合资源受限的嵌入式场景。


三、API 参考

以下为函数签名大纲,完整的参数说明、返回值、注意事项请查看 标准工具库 。 各子模块的 API 请查看上方子模块表格中的 @ref 链接。

3.1 xt_utils(工具函数)

函数签名 说明
uint32_t xt_log2_u32(uint32_t n) 计算 log2(向下取整)
uint16_t xt_bswap16(uint16_t val16) 16 位字节序翻转
uint32_t xt_bswap32(uint32_t val32) 32 位字节序翻转
uint16_t xt_read16p(const void *ptr16) 按字节读取 16 位(小端)
uint32_t xt_read32p(const void *ptr32) 按字节读取 32 位(小端)

3.2 xt_rb(环形缓冲区)

函数签名 说明
void xt_rb_init(struct xt_rb *rb, uint8_t *buf, uint32_t size) 初始化环形缓冲区
void xt_rb_reset(struct xt_rb *rb) 重置(清空数据)
bool xt_rb_is_empty(const struct xt_rb *rb) 判断是否为空
bool xt_rb_is_full(const struct xt_rb *rb) 判断是否已满
uint32_t xt_rb_get_continuous_read_space(const struct xt_rb *rb) 获取连续可读空间
uint32_t xt_rb_get_continuous_write_space(const struct xt_rb *rb, uint8_t b_force) 获取连续可写空间
uint32_t xt_rb_get_size(const struct xt_rb *rb) 获取总大小
uint32_t xt_rb_get_filled(const struct xt_rb *rb) 获取已填充字节数
uint32_t xt_rb_get_empty(const struct xt_rb *rb) 获取空闲字节数
void xt_rb_putc(struct xt_rb *rb, uint8_t ch) 写入单个字节
bool xt_rb_getc(struct xt_rb *rb, uint8_t *ch) 读取单个字节
uint32_t xt_rb_write(struct xt_rb *rb, const uint8_t *src, uint32_t size, uint8_t b_force) 写入数据
uint32_t xt_rb_read(struct xt_rb *rb, uint8_t *dst, uint32_t size) 读取数据

3.3 xt_list(链表)

函数签名 说明
void xt_list_init(struct xt_list_node *l) 初始化双向链表
void xt_list_insert_after(struct xt_list_node *l, struct xt_list_node *n) 在节点后插入
void xt_list_insert_before(struct xt_list_node *l, struct xt_list_node *n) 在节点前插入
void xt_list_remove(struct xt_list_node *n) 从链表中移除节点
int xt_list_isempty(const struct xt_list_node *l) 判断链表是否为空
void xt_slist_init(struct xt_slist_node *l) 初始化单向链表
void xt_slist_append(struct xt_slist_node *l, struct xt_slist_node *n) 追加到单向链表末尾
void xt_slist_insert(struct xt_slist_node *l, struct xt_slist_node *n) 插入到单向链表
int xt_slist_isempty(struct xt_slist_node *l) 判断单向链表是否为空

3.4 xt_crc(CRC 校验)

共 29 种 CRC 变体(CRC-4 ~ CRC-32),函数签名统一为 uintN_t xt_crcXX_YYY(uint8_t *data, uint16_t length) ,详见 xt_crc(CRC 校验) 。

3.5 xt_bit / xt_macro / xt_types / xt_error

以上子模块为纯宏和类型定义,无函数 API。详见 xt_bit(位操作) 、 xt_macro(通用宏) 、 xt_types(类型定义) 、 xt_error(错误码) 。 子模块已按 xt_types(类型定义) 、 xt_macro(通用宏) 、 xt_bit(位操作) 、 xt_list(链表) 、 xt_rb 、 xt_crc(CRC 校验) 、 xt_error(错误码) 、 xt_utils(工具函数) 分组,直接在 doxygen 页面查看。


四、常见模式

4.1 使用链表管理任务队列

struct task {
uint32_t id;
struct xt_list_node node;
};
struct xt_list_node task_list;
xt_list_init(&task_list);
// 添加任务
struct task t1 = { .id = 1 };
xt_list_insert_after(&task_list, &t1.node);
// 遍历任务
struct task *pos;
xt_list_for_each_entry(pos, &task_list, node) {
printf("task id: %u\n", pos->id);
}
// 安全遍历(遍历中删除节点)
struct task *pos, *n;
xt_list_for_each_entry_safe(pos, n, &task_list, node) {
if (pos->id == target_id) {
xt_list_remove(&pos->node);
break;
}
}
static void xt_list_init(struct xt_list_node *l)
initialize a list
static void xt_list_remove(struct xt_list_node *n)
remove node from list.
#define xt_list_for_each_entry_safe(pos, n, head, member)
#define xt_list_for_each_entry(pos, head, member)
static void xt_list_insert_after(struct xt_list_node *l, struct xt_list_node *n)
insert a node after a list

4.2 环形缓冲区接收串口数据

uint8_t uart_rx_buf[256];
struct xt_rb rx_rb;
xt_rb_init(&rx_rb, uart_rx_buf, sizeof(uart_rx_buf));
// ISR 中写入(强制覆盖,不丢失最新数据)
void uart_isr(void) {
uint8_t ch = UART->DR;
xt_rb_putc(&rx_rb, ch);
}
// 主循环中读取
uint8_t ch;
while (xt_rb_getc(&rx_rb, &ch)) {
process_byte(ch);
}
void xt_rb_init(struct xt_rb *rb, uint8_t *buf, uint32_t size)
初始化环形缓冲区
void xt_rb_putc(struct xt_rb *rb, uint8_t ch)
写入单个字节
bool xt_rb_getc(struct xt_rb *rb, uint8_t *ch)
读取单个字节
环形缓冲区结构体
定义 xt_rb.h:43

4.3 寄存器字段读写

// 假设寄存器 bit[13:6] 是某个 8 位字段
uint32_t reg = READ_REG(CFG);
// 读取字段(保留 8 位,从 bit 6 起)
uint32_t field = BITSn_GET_RSH(reg, 8, 6);
// 修改字段为 0xAF
BITSn_SET(reg, 8, 6, 0xAF);
WRITE_REG(CFG, reg);
// 多位置位/清零
uint32_t mask = BIT_MASK(4) << 3; // bit[6:3] 为 1
BITS_SET0(reg, mask); // 清零 bit[6:3]
BITS_SET1(reg, mask); // 置位 bit[6:3]
#define BITSn_SET(src, n, offset, value)
设置 src 的 offset 位起共 n 位为 value.
#define BIT_MASK(n)
获取低 n 位为 1 的位掩码.
#define BITS_SET0(src, bitmask)
设置 src 的对应位掩码 bitmask 为 1 的地方为 0.
#define BITSn_GET_RSH(src, n, offset)
获取 src 的从 offset 位起共 n 位数据.
#define BITS_SET1(src, bitmask)
设置 src 的对应位掩码 bitmask 为 1 的地方为 1.

4.4 协议帧 CRC 校验

uint8_t frame[] = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x0A };
uint16_t crc = xt_crc16_modbus(frame, sizeof(frame));
// 追加 CRC(低字节在前)
frame[6] = crc & 0xFF;
frame[7] = (crc >> 8) & 0xFF;
uint16_t xt_crc16_modbus(uint8_t *data, uint16_t length)

4.5 对齐读取与字节序转换

// 从非对齐地址读取小端 32 位值(不触发 HardFault)
uint32_t val = xt_read32p(&buf[2]);
// 网络字节序转主机字节序
uint16_t net_port = xt_bswap16(port_be);
static uint32_t xt_read32p(const void *ptr32)
static uint16_t xt_bswap16(uint16_t val16)

五、反模式

反模式 问题 正确做法
宏参数有副作用(如 BIT_SET1(reg++, 3) ) 宏展开后 reg 自增多次 宏参数只用简单变量,不传自增表达式
链表节点未初始化就插入 next / prev 野指针导致 HardFault 插入前调用 xt_list_init 或用 XT_LIST_OBJECT_INIT
环形缓冲区 write 时 src = NULL 且 b_force = XT_RB_NO_FORCE 不写入数据也不移动指针 NULL src 仅配合 XT_RB_FORCE 使用
CRC 函数返回值类型不匹配 32 位 CRC 返回 uint32_t ,赋值给 uint16_t 截断 检查函数返回类型, CRC-24/32 用 uint32_t
xt_container_of 和 XT_CONTAINER_OF 混用 重复定义,引入两个头文件的宏 统一使用一个,保持代码一致
BITSn_GET_MDF 的 n + offset > 32 整数溢出,结果未定义 确保字段不跨越 32 位边界,或将 BIT_IE 设为 1ULL
静态断言使用 XT_STATIC_ASSERT 但未引用 xt_macro.h 宏未定义,编译错误 xt_utils.h 已包含 xt_macro.h ,优先使用总头文件

六、编译配置

xt_bit.h 中的所有宏使用 #if !defined() 保护,若平台 SDK 已定义同名宏(如 BIT0 、 BIT_GET ),则跳过 xt_std 的定义,避免冲突。无需额外编译选项。

xt_rb.h 提供便捷宏:

宏 说明
XT_RB_IS_VALID(rb) 检查 rb 指针、缓冲区指针、大小是否有效
XT_RB_IS_EMPTY(rb) 等价于 xt_rb_is_empty(rb)
XT_RB_IS_FULL(rb) 等价于 xt_rb_is_full(rb)