API GUIDE / TPC / OVERVIEW
G32S10 TPC API 概述
模块概述:本文档描述 G32S10 SDK 的 TPC(热敏打印头控制器)HAL 与 LL 接口。
1. 文档范围
本文档描述 G32S10 SDK 的 TPC(热敏打印头控制器)HAL 与 LL 接口。TPC 同时驱动打印头 移位、锁存、加热、步进电机和内部 WDT;一次完整打印通常由这些子模块按确定顺序协同完成。
1.1 先认识几个概念
TPC 的典型分层关系如下。MID 是整图打印编排层;应用也可以跳过 MID,直接调用 HAL:
| 概念 | 在本模块中的含义 |
|---|---|
| TPC | SoC 内的热敏打印头时序控制器。它输出移位、锁存、加热和电机控制信号,不生成 bitmap |
| LL | ll_tpc.h 中的内联函数,直接读写寄存器和位域;不校验参数,不维护运行时状态 |
| HAL | hal_tpc.h 中的有状态驱动。负责配置校验、模块活动状态、中断分发、DMA 生命周期和 FIFO 启动/停止时序 |
| MID | im_tpc 中间件。负责 bitmap 行组织、打印窗口滚动、DMA 描述符、进度和完成策略 |
| DMA / FIFO | DMA 把 32 位数据字搬入 TPC FIFO;TPC 再按移位、加热或电机时序消费 FIFO 数据 |
| Shift / Latch / Heat | 打印头数据依次经历移位、锁存、加热三个阶段 |
| Motor | 步进电机按重复步和周期推进打印纸;自动对齐模式用对齐步把打印与走纸串联 |
1.2 本文边界
HAL 只在“句柄、实例、参数范围、模块当前状态”这一层做校验。它不判断一次打印是否完整, 也不替应用决定错误后是否重试或停机。
不属于 HAL/LL 的职责:
| 事项 | 归属 |
|---|---|
| TPC 模块时钟门控、时钟分频、软件复位 | CPM / 板级(HAL_CPM_ID_CLK_CGU_TPC、HAL_CPM_ID_GATE_TPC、SRBC_TPC_SR_BIT) |
IRQ_TPC 在 SoC 中断控制器里的注册、使能和优先级 | PLIC 与中断框架(im_irq_*) |
| 打印数据流的行/窗口管理、bitmap 语义、超时与重试、命令队列 | TPC middleware im_tpc |
| 打印头、电机、加热驱动板的电气连接与 pinmux | 板级 BSP 与硬件设计 |
1.3 源码依据
本文按以下源码核对编写;当代码、板级配置与外部规范不一致时,以当前 SDK 源码为准, 并在硬件联调时核对实际 SoC 地址表:
| 类型 | 路径 |
|---|---|
| HAL 公开接口 | code/include/drivers/g32s10/tpc/hal_tpc.h |
| HAL 实现 | code/drivers/g32s10/tpc/hal_tpc.c |
| LL 公开接口 | code/include/drivers/g32s10/tpc/ll_tpc.h |
| 寄存器定义 | code/include/drivers/g32s10/tpc/tpc_regs.h |
| 构建配置 | code/drivers/g32s10/tpc/Kconfig、code/drivers/g32s10/tpc/CMakeLists.txt |
| HAL 示例 | code/examples/hal/ |
2. 构建配置与文件组织
直接使用 HAL/LL 时需要使能:
CONFIG_DRIVER_TPC=y
DRIVER_TPC 会自动选择 DRIVER_DMA(HAL 依赖 DMA HAL)。它不会选择 OS、中断框架
或 TPC middleware。
应用程序通常直接包含:
#include "hal_tpc.h"
开始调用启动、停止 API 前,板级初始化必须已经完成以下事项:
| 前置资源 | 原因 |
|---|---|
| TPC 功能时钟、模块门控和必要的软件复位 | HAL 不管理 CPM |
| TPC 引脚 pinmux | HAL 不管理 GPIO |
| SDK systick | HAL 等待 FIFO 非空和 Flush 完成时使用 im_time_* |
IRQ_TPC handler 与使能 | 只有使用回调/中断驱动路径时才必需;纯轮询路径不需要 |
| 共享 PDMA 中断 owner | 若系统需要 DMA 完成回收或 DMA 故障明细,应由统一 DMA owner 管理;TPC 不注册 PDMA IRQ |
可选的寄存器 dump 助手(hal_tpc_regs_dump())需要同时使能两个选项,缺一不可:
CONFIG_SDK_ENABLE_DEBUG_HELPERS=y CONFIG_TPC_ENABLE_DEBUG_HELPERS=y
未同时使能时,hal_tpc_regs_dump() 保留公开符号但返回 IM_ERR_UNSUPPORTED,不会读取
或打印寄存器。
相关源文件:
| 文件 | 职责 |
|---|---|
code/include/drivers/g32s10/tpc/hal_tpc.h | HAL 公开类型、宏和函数 |
code/drivers/g32s10/tpc/hal_tpc.c | HAL 实现、软件状态机、DMA 通道管理 |
code/include/drivers/g32s10/tpc/ll_tpc.h | LL 内联寄存器和位域接口 |
code/include/drivers/g32s10/tpc/tpc_regs.h | 寄存器布局、实例、位域宏 |
code/drivers/g32s10/tpc/Kconfig | DRIVER_TPC、TPC_ENABLE_DEBUG_HELPERS |
code/drivers/g32s10/tpc/CMakeLists.txt | 组件 g32s10_tpc 的源文件注册 |
hal_tpc.h 已经包含 hal_dma.h、ll_tpc.h、im_types.h 和 im_error.h,包含hal_tpc.h 后即可使用 tpc_regs_t、TPC 实例和 LL 接口。
HAL 还依赖两套 SDK 基础设施:
时间基准(
im_time_*):启动/停止时等待 FIFO 就绪和 Flush 完成,超时上限 1 s (内部常量TPC_WAIT_TIMEOUT_US,轮询间隔10 us)。调用前必须初始化 systick。数据缓存维护(
im_cache_*):每次启动 DMA 前由 HAL 对源缓冲区做 data clean (write-back),应用无需重复调用。
3. 硬件资源
| 项目 | 值 | 来源 |
|---|---|---|
| TPC 实例 | TPC = (tpc_regs_t *)TPC_IOBASE,当前 SDK 中 TPC_IOBASE = 0x1349_0000 | base.h、tpc_regs.h |
| TPC 中断号 | IRQ_TPC = IRQ_PLIC0_START + 14(当前等于 14) | irq_table.def |
| 电机控制器数 | 2(Motor0 / Motor1) | TPC_MOTOR_COUNT |
| 电机输出通道 | 每个控制器最多 8 路(Motor1 实际使用 4 路) | TPC_MOTOR_CHANNEL_COUNT |
| Motor0 8 通道模式 | 仅当 Motor1 未配置时可用 | HAL 参数校验 |
| Shift 通道数 | 1 ~ 16 | TPC_SHIFT_CHANNEL_MAX |
| Heat 通道数 | 1 ~ 4 | TPC_HEAT_CHANNEL_MAX |
| 电机重复步数 | 1 ~ 64 | TPC_MOTOR_REPEAT_STEP_MAX |
地址提示:当前 SDK 和所用地址表为
0x1349_0000。应用应使用TPC/TPC_IOBASE,不要自行硬编码外部规范地址;若板上 访问异常,应先确认所测 SoC 版本和地址表。
4. DMA 与资源所有权
4.1 描述符
| 项目 | 值 |
|---|---|
| 最大描述符数 | HAL_DMA_MAX_DESCRIPTOR_COUNT = 128 |
| 描述符对齐 | HAL_DMA_DESCRIPTOR_ALIGN = IM_CACHE_MAX_LINE_SIZE_BYTES = 64 字节 |
| 缓冲区大小宏 | HAL_DMA_DESCRIPTOR_BUFFER_SIZE(count) |
| 循环模式默认描述符数 | 2(单模块启动接口 hal_tpc_shift_start() / _heat_start() / _motor_start() 在 descriptor_count = 0 时使用) |
描述符在首次成功配置该模块时绑定,之后重新配置不会更换存储地址或容量。若之后
可能需要循环 DMA,必须在首次配置前提供对齐的 dma_desc 和非零 dma_desc_count。
hal_tpc_sync_write() 按每路窗口的逻辑单元数 unit_count = len / unit_size 选择实际
描述符数:
unit_count <= 128 -> descriptor_count = unit_count unit_count > 128 -> descriptor_count = 2 ~ 128 范围内的最大整除数 无法得到 2 ~ 128 的整除数 -> 返回 IM_ERR_INVALID_PARAM
例如 218 个逻辑单元选择 109 个描述符,217 个逻辑单元选择 31 个描述符。配置容量必须
至少覆盖所选数量;逻辑单元数与描述符数并非总是一一对应。一个描述符负责窗口总字节数 / descriptor_count 的连续数据,所谓“共享描述符”不会丢弃其余逻辑单元。
每路逻辑单元数必须至少为 2。若单元数大于 128 且没有 2 ~ 128 的整除数,hal_tpc_sync_write() 返回 IM_ERR_INVALID_PARAM,不会启动任何 DMA,也不会退化为
单个自循环描述符。连续打印时应让窗口单元数具有不超过 128 的合法整除数,并为描述符内
的排空数据保留空间。
4.2 中断所有权
TPC 打印事件使用 IRQ_TPC。应用负责向 SoC 中断框架注册 IRQ_TPC,并在 handler 中
调用 hal_tpc_irq_handler();TPC HAL 本身只处理 TPC 的 PINTS/PINTM0,不注册 PLIC
中断线。
共享 PDMA 中断遵循以下边界:
TPC HAL/MID 不注册
IRQ_PDMA0/IRQ_PDMA0_DIP;系统已有统一 DMA owner 时,由该 owner 处理 PDMA IRQ,勿在 TPC 应用里重复注册;
没有 PDMA IRQ owner 时,TPC 依靠下一次启动或 deinit 做 handle reap,DMA 故障不会 自动转换为 TPC HAL 错误码,通常表现为进度停滞、等待超时或后续
IM_ERR_BUSY;需要 DMA 错误明细时,应通过统一的 DMA 管理接口读取状态,不要从 TPC IRQ 里抢接 PDMA 中断线。
4.3 并发与实例
TPC 只有一个寄存器块实例,HAL 句柄不是可重入的:同一句柄不能并发调用,多任务 共享时需要外部串行化。
hal_tpc_init()/hal_tpc_deinit()成对使用;重复hal_tpc_init()返回IM_ERR_BUSY。模块正在运行时再次配置或启动该模块都会返回
IM_ERR_BUSY,需要先停止。
5. 错误码
HAL 自身会返回:
| 错误码 | 常见原因 |
|---|---|
IM_OK | 成功 |
IM_ERR_INVALID_PARAM | 句柄/实例非法、配置字段越界、缓冲区未对齐或长度不匹配、描述符与个数不匹配、掩码包含未知中断位 |
IM_ERR_NOT_READY | 未调用 hal_tpc_init()、模块尚未配置、同步流程所需模块或窗口未就绪 |
IM_ERR_BUSY | 模块正在运行、重复初始化、hal_tpc_deinit() 时仍有模块活动、WDT 已使能 |
IM_ERR_TIMEOUT | 启动时等待 FIFO 非空超过 1 s |
IM_ERR_UNSUPPORTED | 调试助手未使能时调用 hal_tpc_regs_dump() |
打开或启动 DMA 时,HAL 会透传 DMA HAL 的错误码(例如无可用通道、控制器未就绪); 停止接口按 6.5 所述执行 best-effort 清理,不向上传播 DMA 停止或 Flush 失败。
6. 示例与参考实现
| 参考 | 内容 |
|---|---|
code/examples/hal/tpc_manual_mode_example.c | 手动模式整图打印:Shift→Latch→Heat→Motor0 事件链 |
code/examples/hal/tpc_auto_mode_print_example.c | HAL 单次打印:自动对齐与自动非对齐两种模式,含 sync_write / sync_stream_end 用法 |
code/middleware/tpc/include/im_tpc.h | MID 公共配置、状态和 API,适合产品级整幅打印流程 |
code/middleware/tpc/example/main/im_tpc_example_single_print.c | MID 单次打印和非对齐路径 |
code/middleware/tpc/example/main/im_tpc_example_single_print_sync.c | MID 单次打印和自动对齐路径 |
code/middleware/tpc/example/main/im_tpc_example_multi_print.c | MID 连续多次打印及资源复用 |
code/middleware/tpc/example/README.md | MID 示例的构建、切换和上板说明 |
建议按“HAL 单模块 → HAL 整图 → MID 单次打印 → MID 多打印”的顺序阅读。只做接口查询时 可直接转到第 7、8 章。
7. 已知限制与注意事项
HAL 不管 CPM:模块时钟门控和软件复位由应用/板级完成;
hal_tpc_init()只做 寄存器层面的复位。错误事件不自动停机:各
*_FIFO_UNDERRUN、*_OVERTIME、AUTO_ALIGN_ERROR只通过回调上报,活动掩码不会自动清除,停机策略由应用选择。停止接口是 best-effort:DMA 停止和 Flush 的失败不会传播,只保证流程走完; 需要严格确认时自行读寄存器核对(
PFCEN、PDEN、PMnEN、PINTS、PFFS、PFSM)。sync_stream_end()依赖 LINK 和描述符范围:只能用于由 2 ~ 128 个描述符组成的 循环窗口;单描述符或无法等长分片的窗口会被hal_tpc_sync_write()拒绝。传入的是最后 一份数据所在逻辑单元的源地址,HAL 会映射到一个或多个逻辑单元共用的描述符。LINK 清除 后仍会完成当前描述符,应用需预留收尾/排空数据。WDT 分频字段只有 0 ~ 5 有效,6、7 为保留值,HAL 配置时校验。
电机通道模式互斥:Motor0 使用 8 通道时 Motor1 不可配置;反之 Motor0 通道数 不能超过 4。
hal_tpc_deinit()要求先停机:任何模块仍活动都会返回IM_ERR_BUSY。正常顺序是 先*_stop()再hal_tpc_deinit();当前 deinit 对 DMA reap/close 采用 best-effort 清理,不向上传播这些错误。WDT 不受 active mask 管理,也不会由 deinit 自动关闭。实例基址以 SDK/地址表为准:见第 3 章,当前值为
0x1349_0000;不要按外部规范 地址硬编码。