API GUIDE / TPC / Middleware
G32S10 TPC Middleware 接口
Middleware 层接口:TPC MID 的 Kconfig 已由 code/middleware/Kconfig 引入:
1. 构建配置与文件组织
1.1 Kconfig
TPC MID 的 Kconfig 已由 code/middleware/Kconfig 引入:
source "code/middleware/tpc/Kconfig"
menu "TPC Middleware" config MIDDLEWARE_TPC bool "TPC print-task middleware" depends on DRIVER_TPC && DRIVER_IRQ && OS_API help Build the task-oriented TPC middleware with cyclic DMA window handling, IRQ event delivery, timeout handling, and board platform adapters. endmenu
MIDDLEWARE_TPC 依赖 DRIVER_TPC、DRIVER_IRQ 和 OS_API,这些依赖项必须同时
使能。MID 不依赖 MIDDLEWARE_DMA:TPC 的 DMA 通道由 HAL 直接申请和使用。
| 选项 | 默认值 | 说明 |
|---|---|---|
MIDDLEWARE_TPC | n | 编译 TPC MID;依赖 DRIVER_TPC、DRIVER_IRQ 和 OS_API。 |
DRIVER_TPC | n | 提供 TPC HAL;Kconfig 会自动选择 DRIVER_DMA。 |
DRIVER_IRQ | n | 提供 IRQ_TPC 的申请、使能和释放接口。 |
OS_API | n | 提供线程等待器和任务上下文同步能力。 |
DRIVER_GPIO | n | IM_TPC_EXAMPLE_GPIO_INIT=1 或 IM_TPC_EXAMPLE_FPGA_CTRL_HIGH=1 时需要。 |
最小 MID 配置:
CONFIG_DRIVER_TPC=y CONFIG_DRIVER_IRQ=y CONFIG_OS_API=y CONFIG_MIDDLEWARE_TPC=y
im_tpc_regs_dump() 是否可用还取决于 HAL 的两个调试选项:
CONFIG_SDK_ENABLE_DEBUG_HELPERS=y CONFIG_TPC_ENABLE_DEBUG_HELPERS=y
2. 资源模型与公开类型
2.1 实例与设备对象
| 项目 | 值 | 说明 |
|---|---|---|
| 支持的实例 | IM_TPC_INSTANCE_0 | 当前唯一支持的 TPC 实例。 |
| 公开对象 | im_tpc_t | 不透明设备类型,调用方只保存 im_tpc_t *。 |
| 实例数量 | 1 | 第二个 im_tpc_init() 返回 IM_ERR_ALREADY_EXISTS。 |
| 对象所有者 | MID | 对象为 MID 内部静态实例,应用不得释放、复制或解引用。 |
2.2 MID 公开宏
| 宏 | 使用说明 | 取值/约束 |
|---|---|---|
IM_TPC_SHIFT_CHANNEL_COUNT_MAX | 移位输出通道上限。 | 16。 |
IM_TPC_HEAT_CHANNEL_COUNT_MAX | 加热输出通道上限。 | 4。 |
IM_TPC_MOTOR_COUNT | 电机控制器数量。 | 2。 |
IM_TPC_MOTOR_CHANNEL_COUNT_MAX | 单个电机控制器的输出通道上限。 | 8;Motor1 的实际上限为 4。 |
IM_TPC_MOTOR_REPEAT_STEP_COUNT_MAX | 一个电机序列的重复步上限。 | 64。 |
IM_TPC_WINDOW_LINE_COUNT_DEFAULT | 循环 DMA 窗口默认行槽数。 | 32。 |
IM_TPC_WAIT_FOREVER | im_tpc_wait() / im_tpc_print() 永久等待。 | 0xFFFFFFFF。 |
IM_TPC_WORKSPACE_ALIGN | 运行工作区对齐要求。 | IM_CACHE_MAX_LINE_SIZE_BYTES,当前为 64 字节。 |
IM_TPC_DESCRIPTOR_WORKSPACE_ALIGN | 描述符工作区对齐及单个描述符数组窗口大小。 | 4096 字节。 |
IM_TPC_DMA_UNIT_SIZE | FIFO/DMA 基本访问粒度。 | 4 字节。 |
2.3 MID 公开类型
本节按照 HAL/LL 文档“每个公开类型独立小节、类型定义与约束相邻”的组织方式说明
MID 类型。完整定义以 code/middleware/tpc/include/im_tpc.h 为准。
2.3.1 im_tpc_t
typedef struct im_tpc im_tpc_t;
im_tpc_t 是 MID 管理的不透明设备对象。应用通过 im_tpc_init() 获得句柄,并只把该
句柄传给其它 MID API;不得读取字段、复制对象、释放对象或自行构造对象。
2.3.2 im_tpc_instance_t
typedef enum {
IM_TPC_INSTANCE_0 = 0, /**< 当前唯一支持的 TPC 实例。 */
} im_tpc_instance_t;当前只接受 IM_TPC_INSTANCE_0,其它值返回 IM_ERR_INVALID_PARAM。
| 值 | 说明 |
|---|---|
IM_TPC_INSTANCE_0 | 当前 SDK 唯一支持的 TPC 寄存器实例。 |
2.3.3 im_tpc_flow_mode_t
typedef enum {
IM_TPC_FLOW_AUTO_ALIGN = 0, /**< 电机与打印头自动对齐。 */
IM_TPC_FLOW_AUTO_NON_ALIGN, /**< 打印头与电机独立运行。 */
} im_tpc_flow_mode_t;| 值 | 说明 |
|---|---|
IM_TPC_FLOW_AUTO_ALIGN | 打印头与电机由 TPC 自动对齐流程联动;要求 motor.enabled = true。 |
IM_TPC_FLOW_AUTO_NON_ALIGN | 打印头使用自动流程;电机可独立运行或关闭。 |
手动打印头、手动电机和 WDT 不属于本 MID 支持范围。
2.3.4 im_tpc_state_t
typedef enum {
IM_TPC_STATE_RESET = 0, /**< 未初始化或已反初始化。 */
IM_TPC_STATE_READY, /**< 已初始化,可以开始打印。 */
IM_TPC_STATE_BUSY, /**< 有打印请求正在执行。 */
IM_TPC_STATE_ERROR, /**< 资源或停机异常,需要恢复。 */
} im_tpc_state_t;| 状态 | 说明 |
|---|---|
IM_TPC_STATE_RESET | 尚未 im_tpc_init(),或 im_tpc_deinit() 已完整成功。 |
IM_TPC_STATE_READY | 已初始化且无活动请求。 |
IM_TPC_STATE_BUSY | 请求已启动,等待完成、停止或错误终结。 |
IM_TPC_STATE_ERROR | 资源与活动状态不一致,或停机失败;可先重试 im_tpc_stop(),仍无法恢复时执行 deinit。 |
2.3.5 im_tpc_phase_t
typedef enum {
IM_TPC_PHASE_IDLE = 0, /**< 没有活动请求。 */
IM_TPC_PHASE_PRIMING, /**< 正在准备初始 DMA 窗口。 */
IM_TPC_PHASE_PRINTING, /**< 图像行正在被 DMA 消费。 */
IM_TPC_PHASE_DRAINING, /**< 已下发收尾断链,等待排空。 */
IM_TPC_PHASE_STOPPING, /**< 正在停止硬件。 */
} im_tpc_phase_t;阶段由 im_tpc_progress_t::phase 报告,用于观察打印进度,不直接替代设备状态。
| 值 | 说明 |
|---|---|
IM_TPC_PHASE_IDLE | 没有活动请求。 |
IM_TPC_PHASE_PRIMING | 正在准备初始窗口,图像行尚未完成。 |
IM_TPC_PHASE_PRINTING | 正在消费图像行。 |
IM_TPC_PHASE_DRAINING | 已下发收尾断链,等待 DMA 排空。 |
IM_TPC_PHASE_STOPPING | 请求因运行期错误或超时进入错误停机路径。 |
2.3.6 im_tpc_event_t
typedef enum {
IM_TPC_EVENT_COMPLETE = 0, /**< 请求成功完成。 */
IM_TPC_EVENT_STOPPED, /**< 活动请求被应用停止。 */
IM_TPC_EVENT_PROGRESS, /**< 至少一个流槽被各活动 DMA 流共同消费。 */
IM_TPC_EVENT_ERROR, /**< 请求因错误或超时终结。 */
} im_tpc_event_t;事件通过 im_tpc_event_callback_t 同步投递,运行在调用 start/process/wait/print/stop的任务上下文,不在硬件 ISR 中执行。
| 事件 | status | 说明 |
|---|---|---|
IM_TPC_EVENT_COMPLETE | IM_OK | 请求成功完成。 |
IM_TPC_EVENT_STOPPED | IM_OK | 活动请求被应用停止。 |
IM_TPC_EVENT_PROGRESS | IM_OK | 至少一个流槽被所有活动 DMA 流共同消费;prime 阶段也可能触发,此时 completed_lines 仍可为 0。 |
IM_TPC_EVENT_ERROR | 终止错误码 | 请求因硬件、DMA、流、超时或内部错误结束。 |
一次 PROGRESS 回调可能汇总多个已消费槽位,不是逐行或逐槽的严格回调。应用没有配置event_callback 时 MID 不投递任何事件,但状态、last_status、error_flags 和进度仍会
照常更新。
2.3.7 im_tpc_error_t
typedef enum {
IM_TPC_ERROR_NONE = 0U,
IM_TPC_ERROR_SHIFT_UNDERRUN = (1UL << 0U),
IM_TPC_ERROR_HEAT_UNDERRUN = (1UL << 1U),
IM_TPC_ERROR_HEAT_OVERTIME = (1UL << 2U),
IM_TPC_ERROR_MOTOR_UNDERRUN = (1UL << 3U),
IM_TPC_ERROR_MOTOR_OVERTIME = (1UL << 4U),
IM_TPC_ERROR_ALIGN = (1UL << 5U),
IM_TPC_ERROR_DMA = (1UL << 6U),
IM_TPC_ERROR_TIMEOUT = (1UL << 7U),
IM_TPC_ERROR_STREAM = (1UL << 8U),
IM_TPC_ERROR_PLATFORM = (1UL << 9U),
IM_TPC_ERROR_INTERNAL = (1UL << 10U),
} im_tpc_error_t;| 值 | 说明 |
|---|---|
IM_TPC_ERROR_NONE | 没有错误,错误掩码为 0。 |
IM_TPC_ERROR_SHIFT_UNDERRUN | 移位 FIFO 欠载。 |
IM_TPC_ERROR_HEAT_UNDERRUN | 加热 FIFO 欠载。 |
IM_TPC_ERROR_HEAT_OVERTIME | 加热时间超过硬件上限。 |
IM_TPC_ERROR_MOTOR_UNDERRUN | 电机 FIFO 欠载。 |
IM_TPC_ERROR_MOTOR_OVERTIME | 电机步周期超过硬件上限。 |
IM_TPC_ERROR_ALIGN | TPC 自动对齐流程错误。 |
IM_TPC_ERROR_DMA | DMA handle 或传输错误,需要 PDMA 错误事件可见。 |
IM_TPC_ERROR_TIMEOUT | im_tpc_wait() / im_tpc_print() 超时。 |
IM_TPC_ERROR_STREAM | 软件流记账、地址或槽位不一致。 |
IM_TPC_ERROR_PLATFORM | platform.prepare() 失败。 |
IM_TPC_ERROR_INTERNAL | 中间件内部状态、停机失败或特定清理路径错误。 |
运行期错误标志在请求被接受时清零,在请求执行期间按位累加,直到下一次成功启动请求。IM_TPC_ERROR_PLATFORM 是初始化例外,由 platform.prepare() 失败直接置位。
2.3.8 im_tpc_level_t
typedef enum {
IM_TPC_LEVEL_LOW = 0, /**< 低电平。 */
IM_TPC_LEVEL_HIGH, /**< 高电平。 */
} im_tpc_level_t;用于移位、锁存和加热模块的空闲电平。
| 值 | 说明 |
|---|---|
IM_TPC_LEVEL_LOW | 空闲输出低电平。 |
IM_TPC_LEVEL_HIGH | 空闲输出高电平。 |
2.3.9 im_tpc_shift_endian_t
typedef enum {
IM_TPC_SHIFT_ENDIAN_BIG = 0, /**< 高位先移出。 */
IM_TPC_SHIFT_ENDIAN_LITTLE, /**< 低位先移出。 */
} im_tpc_shift_endian_t;| 值 | 说明 |
|---|---|
IM_TPC_SHIFT_ENDIAN_BIG | 高位先移出。 |
IM_TPC_SHIFT_ENDIAN_LITTLE | 低位先移出。 |
2.3.10 im_tpc_spi_polarity_t
typedef enum {
IM_TPC_SPI_POLARITY_IDLE_LOW = 0, /**< 时钟空闲低。 */
IM_TPC_SPI_POLARITY_IDLE_HIGH, /**< 时钟空闲高。 */
} im_tpc_spi_polarity_t;| 值 | 说明 |
|---|---|
IM_TPC_SPI_POLARITY_IDLE_LOW | 时钟空闲为低。 |
IM_TPC_SPI_POLARITY_IDLE_HIGH | 时钟空闲为高。 |
2.3.11 im_tpc_spi_phase_t
typedef enum {
IM_TPC_SPI_PHASE_FIRST_EDGE = 0, /**< 第一个时钟边沿采样。 */
IM_TPC_SPI_PHASE_SECOND_EDGE, /**< 第二个时钟边沿采样。 */
} im_tpc_spi_phase_t;| 值 | 说明 |
|---|---|
IM_TPC_SPI_PHASE_FIRST_EDGE | 第一个时钟边沿采样。 |
IM_TPC_SPI_PHASE_SECOND_EDGE | 第二个时钟边沿采样。 |
2.3.12 im_tpc_progress_t
typedef struct {
im_tpc_phase_t phase; /**< 当前阶段。 */
uint32_t total_lines; /**< 请求中的图像行总数。 */
uint32_t queued_lines; /**< 累计装入 DMA 流的图像行数。 */
uint32_t completed_lines; /**< DMA 已完成消费的图像行数。 */
} im_tpc_progress_t;| 字段 | 说明 |
|---|---|
phase | 当前打印阶段。 |
total_lines | 当前请求的图像行总数。 |
queued_lines | 累计已排入 DMA 流的图像行数,不含 prime、收尾和排空行;该值不会因槽位被消费而回退。 |
completed_lines | 最慢活动流已共同消费的图像行数。 |
im_tpc_get_progress() 返回一致性快照。图像行计数不包含 prime 行和收尾/排空行。
2.3.13 im_tpc_event_info_t
typedef struct {
im_tpc_progress_t progress; /**< 事件发生时的进度快照。 */
error_code_t status; /**< 事件对应的状态码。 */
uint32_t error_flags; /**< 到事件发生时的 IM_TPC_ERROR_* 快照。 */
} im_tpc_event_info_t;| 字段 | 说明 |
|---|---|
progress | 事件发生时的进度快照。 |
status | 与事件对应的 SDK 错误码。 |
error_flags | 事件发生时累计的 IM_TPC_ERROR_* 位掩码。 |
事件信息仅在当前回调调用期间有效;应用需要保留时必须复制。
2.3.14 im_tpc_event_callback_t
typedef void (*im_tpc_event_callback_t)( im_tpc_t *dev, im_tpc_event_t event, const im_tpc_event_info_t *info, void *user_ctx);
回调同步运行在 MID 控制路径,不是硬件 ISR。回调必须快速返回,不得阻塞、睡眠或重入init/start/process/wait/print/stop/deinit;这些调用在回调期间返回 IM_ERR_BUSY。
2.3.15 im_tpc_platform_prepare_t
typedef error_code_t (*im_tpc_platform_prepare_t)(void *context);
可选板级准备回调。im_tpc_init() 在工作区绑定后、HAL 配置前于任务上下文调用;返回
非 IM_OK 会终止初始化并设置 IM_TPC_ERROR_PLATFORM。当前实现不会在 prepare自身返回失败时调用 release,因此 prepare 必须先清理它已部分申请的资源再返回错误。
2.3.16 im_tpc_platform_release_t
typedef void (*im_tpc_platform_release_t)(void *context);
可选板级释放回调。只有在 prepare != NULL 且该回调成功返回后,im_tpc_deinit()才会在释放 TPC IRQ/HAL 资源后调用它。当前实现不会单独调用只配置了 release 的组合。
2.3.17 im_tpc_platform_ops_t
typedef struct {
im_tpc_platform_prepare_t prepare; /**< 可选板级准备回调。 */
im_tpc_platform_release_t release; /**< 可选板级释放回调。 */
void *context; /**< 回调上下文。 */
} im_tpc_platform_ops_t;| 字段 | 说明 |
|---|---|
prepare | 可选的板级资源准备回调,在 HAL 配置前调用。 |
release | 可选的板级资源释放回调,在 TPC IRQ/HAL 释放后调用。 |
context | 原样传给 prepare / release 的上下文。 |
平台回调只管理板级资源,不应申请 TPC 实例、IRQ_TPC、HAL 句柄或 MID 工作区。release 的调用依赖 prepare 成功,板级配置必须遵守这一组合约束。
2.3.18 im_tpc_shift_config_t
typedef struct {
uint32_t output_clock_hz; /**< 目标移位时钟,Hz。 */
im_tpc_level_t idle_level; /**< 移位输出空闲电平。 */
im_tpc_shift_endian_t endian; /**< 移位位序。 */
im_tpc_spi_polarity_t spi_polarity; /**< SPI 空闲极性。 */
im_tpc_spi_phase_t spi_phase; /**< SPI 采样相位。 */
uint8_t setup_cycles; /**< setup 周期,0 ~ 3。 */
uint8_t hold_cycles; /**< hold 周期,0 ~ 3。 */
uint8_t channel_count; /**< 移位通道数,1 ~ 16。 */
bool data_invert; /**< 是否反相。 */
uint16_t line_bytes; /**< 单行总字节数。 */
} im_tpc_shift_config_t;| 字段 | 默认值 | 约束 |
|---|---|---|
output_clock_hz | 12 MHz | 必须整除 functional_clock_hz,分频比 1 ~ 256。 |
idle_level | IM_TPC_LEVEL_LOW | 必须是合法 im_tpc_level_t。 |
endian / spi_polarity / spi_phase | Big / idle low / first edge | 必须是相应枚举的有效值。 |
setup_cycles / hold_cycles | 0 | 0 ~ 3。 |
channel_count | 1 | 1 ~ 16。 |
data_invert | false | 为 true 时由 HAL 反转移位数据位。 |
line_bytes | 4 | 非 0、是 4 的整数倍且能被 channel_count 整除;1 ~ 4 通道时每通道最多 256 字节,5 ~ 16 通道时最多 128 字节。 |
移位时钟必须精确表示:functional_clock_hz = output_clock_hz × (PRESCALE + 1),
其中分频比为 1 ~ 256,MID 写入的 PRESCALE 为分频比减 1。
2.3.19 im_tpc_latch_config_t
typedef struct {
im_tpc_level_t idle_level; /**< 锁存空闲电平。 */
uint32_t wait_time_us; /**< 锁存前等待,us。 */
uint32_t active_time_us; /**< 锁存有效时间,us。 */
uint32_t shift_wait_time_us; /**< 移位等待,us。 */
} im_tpc_latch_config_t;| 字段 | 默认值 | 约束 |
|---|---|---|
idle_level | IM_TPC_LEVEL_HIGH | 必须是合法 im_tpc_level_t。 |
wait_time_us | 0 | 换算后的周期数不超过 24 位。 |
active_time_us | 0 | 换算后的周期数不超过 24 位。 |
shift_wait_time_us | 0 | 换算后的周期数不超过 24 位。 |
2.3.20 im_tpc_heat_config_t
typedef struct {
im_tpc_level_t idle_level; /**< 加热空闲电平。 */
uint8_t channel_count; /**< 加热通道数,1 ~ 4。 */
uint32_t wait_time_us; /**< 默认等待时间,us。 */
uint32_t default_heat_time_us; /**< 普通行加热时间,us。 */
uint32_t max_heat_time_us; /**< 最大加热时间,us。 */
uint32_t module_wait_cycles; /**< 模块等待周期。 */
uint32_t next_wait_cycles; /**< 下一单元等待周期。 */
uint32_t prime_heat_time_us; /**< prime 行加热时间,us。 */
} im_tpc_heat_config_t;| 字段 | 默认值 | 约束 |
|---|---|---|
idle_level | IM_TPC_LEVEL_LOW | 必须是合法 im_tpc_level_t。 |
channel_count | 1 | 1 ~ 4。 |
wait_time_us | 0 | 两个显式周期字段都为 0 时生效。 |
default_heat_time_us | 0 | 不超过 max_heat_time_us,并能换算为 24 位周期。 |
max_heat_time_us | 1000 | 不小于默认和 prime 加热时间,并能换算为 24 位周期。 |
module_wait_cycles | 0 | 不超过 24 位;任一显式周期非 0 时与 next_wait_cycles 共同生效。 |
next_wait_cycles | 0 | 同上;非对齐 + 电机流程由电机行周期替代该等待。 |
prime_heat_time_us | 0 | 0 表示沿用 default_heat_time_us;仅非对齐 + 电机流程使用。 |
2.3.21 im_tpc_motor_config_t
typedef struct {
bool enabled; /**< 是否启用电机。 */
uint8_t motor_id; /**< 电机索引,0/1。 */
uint8_t channel_count; /**< 输出通道数。 */
uint8_t idle_level_mask; /**< 空闲电平 mask。 */
uint8_t start_level_mask; /**< 开始步电平 mask。 */
uint8_t end_level_mask; /**< 结束步电平 mask。 */
uint8_t repeat_step_count; /**< 重复步数,1 ~ 64。 */
uint8_t align_step_index; /**< 对齐步索引,从 1 开始。 */
uint8_t repeat_output_mask[IM_TPC_MOTOR_REPEAT_STEP_COUNT_MAX]; /**< 每步输出 mask。 */
uint32_t start_step_time_us; /**< 开始步时间,us。 */
uint32_t repeat_step_time_us; /**< 重复步时间,us。 */
uint32_t end_step_time_us; /**< 结束步时间,us。 */
uint32_t stop_step_time_us; /**< 自动对齐停止步时间,us。 */
uint32_t max_step_time_us; /**< 最大步时间,us。 */
} im_tpc_motor_config_t;| 字段 | 默认值 | 约束 |
|---|---|---|
enabled | false | 自动对齐流程必须为 true。 |
motor_id | 0 | 0 或 1。 |
channel_count | 1 | 1 ~ 8;Motor1 最多 4。 |
idle_level_mask / start_level_mask / end_level_mask | 0 | 只能使用 channel_count 范围内的低位。 |
repeat_step_count | 1 | 启用时为 1 ~ 64。 |
align_step_index | 1 | 1 ~ repeat_step_count。 |
repeat_output_mask[] | 全 0 | 启用时只校验前 repeat_step_count 个元素;每个元素只能使用配置通道范围内的位。 |
start_step_time_us / repeat_step_time_us / end_step_time_us / stop_step_time_us | 0 | 不超过 max_step_time_us 和 24 位周期上限。 |
max_step_time_us | 0 | 电机启用时作为其它步时间的上限。 |
上表中除 enabled 外的电机字段只在 enabled == true 时参与校验;关闭电机后这些字段
保留在结构体中但不会影响请求。repeat_output_mask[n] 对应第 n + 1 个重复步,
位 b 表示通道 b 在该步的输出电平。
自动对齐要求 stop_step_time_us != 0;非对齐 + 电机还要求repeat_step_time_us * align_step_index 能表示为 24 位周期。
2.3.22 im_tpc_config_t
typedef struct {
im_tpc_instance_t instance; /**< 实例,当前只能为 0。 */
im_tpc_flow_mode_t flow_mode; /**< 自动对齐或自动非对齐。 */
uint32_t functional_clock_hz; /**< TPC 功能时钟,Hz。 */
uint32_t max_line_count; /**< 单请求最大图像行数。 */
uint16_t prime_line_count; /**< prime 行数。 */
uint16_t window_line_count; /**< 循环窗口槽数。 */
im_tpc_shift_config_t shift; /**< 移位配置。 */
im_tpc_latch_config_t latch; /**< 锁存配置。 */
im_tpc_heat_config_t heat; /**< 加热配置。 */
im_tpc_motor_config_t motor; /**< 电机配置。 */
void *workspace; /**< 调用方运行工作区。 */
size_t workspace_size; /**< 运行工作区容量。 */
void *descriptor_workspace; /**< 调用方描述符工作区。 */
size_t descriptor_workspace_size; /**< 描述符工作区容量。 */
im_tpc_platform_ops_t platform; /**< 可选板级资源回调。 */
im_tpc_event_callback_t event_callback; /**< 可选事件回调。 */
void *user_ctx; /**< 事件回调上下文。 */
} im_tpc_config_t;| 字段 | 默认值 | 约束 |
|---|---|---|
instance | IM_TPC_INSTANCE_0 | 当前只能取该值。 |
flow_mode | AUTO_NON_ALIGN | 只能取两个自动流程之一;AUTO_ALIGN 要求启用电机。 |
functional_clock_hz | 24 MHz | 非 0;必须与板级实际 TPC 功能时钟一致。MID 只用它换算周期,不配置 CPM/时钟源。 |
max_line_count | 1 | 非 0;且 prime_line_count + max_line_count + AUTO_ALIGN 的 2 个收尾槽 不得超过 UINT32_MAX。 |
prime_line_count | 1 | 不大于 window_line_count。 |
window_line_count | 32 | 2 ~ 128。 |
shift / latch / heat / motor | 见各自类型 | 使用对应约束。 |
workspace / workspace_size | NULL / 0 | 调用方分配,按 64 字节对齐,容量覆盖完整生命周期。 |
descriptor_workspace / descriptor_workspace_size | NULL / 0 | 调用方分配,虚拟/物理地址均按 4096 字节对齐。 |
platform | 全 0 | 可选板级回调。 |
event_callback / user_ctx | NULL / NULL | 可选事件回调及其上下文。 |
2.3.23 im_tpc_print_request_t
typedef struct {
const uint8_t *bitmap_data; /**< 调用方 bitmap 基址。 */
size_t bitmap_size; /**< bitmap 可用字节数。 */
uint32_t line_count; /**< 本次打印行数。 */
uint32_t stride_bytes; /**< 相邻行首地址间距。 */
bool mirror_horizontal; /**< 是否逐行水平镜像。 */
uint32_t heat_time_us; /**< 本次加热时间覆盖值,0 使用配置。 */
uint32_t repeat_step_time_us; /**< 本次电机步时间覆盖值,0 使用配置。 */
} im_tpc_print_request_t;| 字段 | 默认值 | 约束 |
|---|---|---|
bitmap_data | NULL | 必须非空且 CPU 可读;异步请求终止前保持有效。若由 non-coherent DMA 生产者写入,调用方必须在 start 前保证数据对 CPU 可见。 |
bitmap_size | 0 | 至少为 (line_count - 1) * stride_bytes + shift.line_bytes。 |
line_count | 0 | 1 ~ config.max_line_count。 |
stride_bytes | 0 | 不小于 shift.line_bytes。 |
mirror_horizontal | false | 为 true 时逐行执行位级水平镜像。 |
heat_time_us | 0 | 0 使用配置默认值;非 0 时不超过最大加热时间和 24 位周期。 |
repeat_step_time_us | 0 | 0 使用配置默认值;电机启用时非 0 值受最大步时间约束。 |
所有微秒配置都按 functional_clock_hz 向上取整换算为功能时钟周期:
cycles = ceil(time_us * functional_clock_hz / 1000000)
换算结果必须能放入对应硬件的 24 位周期字段。
2.4 状态与事件流转
RESET → READY · im_tpc_init()
READY → BUSY · im_tpc_start()
BUSY → READY · COMPLETE / STOPPED
BUSY → ERROR · ERROR 或停机失败
ERROR → READY · im_tpc_stop() 且 active_mask != 0
READY / BUSY / ERROR → RESET · im_tpc_deinit() 清理成功
请求执行期间由调用方调用
im_tpc_process()或进入im_tpc_wait()。IM_TPC_EVENT_COMPLETE表示请求完整结束;STOPPED表示被im_tpc_stop()终止。ERROR事件保留error_flags和last_status供诊断。只有停机失败、资源不一致等异常路径进入
ERROR。ERROR且仍有活动硬件时,可以先重试im_tpc_stop();若没有活动硬件,stop 返回IM_ERR_NOT_READY,应改用 deinit 清理。
2.5 工作区与描述符工作区
| 工作区 | 用途 | 对齐 |
|---|---|---|
workspace | 移位、加热及同步电机行窗口 | IM_TPC_WORKSPACE_ALIGN,64 字节 |
descriptor_workspace | HAL DMA 描述符数组 | IM_TPC_DESCRIPTOR_WORKSPACE_ALIGN,虚拟/物理地址均为 4096 字节 |
实现公式:
shift_bytes = window_line_count * shift.line_bytes heat_bytes = window_line_count * (heat.channel_count + 1) * 4 motor_lines = (flow_mode == AUTO_ALIGN && motor.enabled) ? window_line_count : ((flow_mode == AUTO_NON_ALIGN && motor.enabled) ? prime_line_count + max_line_count : 0) motor_bytes = motor_lines * motor.repeat_step_count * 4 workspace_size = align64(shift_bytes) + align64(heat_bytes) + align64(motor_bytes)
descriptor_workspace_size_get() 为实际需要的描述符数组各预留 4096 字节:通常为
shift + heat 两个数组;AUTO_ALIGN 且启用电机时再加 motor 数组。
2.6 设备对象与资源所有权
| 资源 | 所有者 | 生命周期 |
|---|---|---|
im_tpc_t | MID | im_tpc_init() 成功后由 MID 持有,im_tpc_deinit() 后失效 |
workspace / descriptor_workspace | 调用方 | 覆盖整个 MID 生命周期 |
bitmap_data | 调用方 | 覆盖当前异步请求直到终止事件 |
IRQ_TPC | MID | im_tpc_init() 申请,im_tpc_deinit() 释放 |
| TPC HAL/DMA handle | MID(经 HAL) | im_tpc_init() 配置,im_tpc_deinit() 释放 |
| 板级时钟、pinmux、GPIO | platform 回调或 BSP | 由板级自行管理 |
3. 接口规范
本章按 ADC MID 文档的统一格式说明每个公开接口:
完整函数原型;
功能说明;
参数表;
返回值表;
补充说明。
除特别说明外,控制类接口只能在线程上下文调用,不能从 ISR 或事件回调中重入。
3.1 默认值与尺寸查询
3.1.1 im_tpc_config_struct_init
void im_tpc_config_struct_init(im_tpc_config_t *config);
功能说明: 把配置结构体清零并填入 MID 默认配置,不访问硬件。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
config | im_tpc_config_t * | 输出 | 待初始化配置;为 NULL 时直接返回 |
返回值表:
| 返回值 | 说明 |
|---|---|
| 无 | 接口不返回状态码 |
补充说明:
默认实例为
IM_TPC_INSTANCE_0,流程为IM_TPC_FLOW_AUTO_NON_ALIGN。默认功能时钟为 24 MHz、移位时钟为 12 MHz、最大行数为 1、prime 行数为 1、窗口为 32。
workspace和descriptor_workspace默认为空,必须在初始化前提供容量足够的对齐内存。平台回调和事件回调默认为空,二者都是可选项。
3.1.2 im_tpc_print_request_init
void im_tpc_print_request_init(im_tpc_print_request_t *request);
功能说明: 把打印请求清零,不访问硬件。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
request | im_tpc_print_request_t * | 输出 | 待初始化请求;为 NULL 时直接返回 |
返回值表:
| 返回值 | 说明 |
|---|---|
| 无 | 接口不返回状态码 |
补充说明:
清零后必须填写
bitmap_data、bitmap_size、line_count和stride_bytes才能提交。heat_time_us、repeat_step_time_us为 0 时使用设备配置中的默认时间。
3.1.3 im_tpc_workspace_size_get
size_t im_tpc_workspace_size_get(const im_tpc_config_t *config);
功能说明: 计算配置所需的运行时行窗口工作区字节数。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
config | const im_tpc_config_t * | 输入 | 待计算配置;只看结构形状,不访问硬件 |
返回值表:
| 返回值 | 说明 |
|---|---|
size_t | 所需工作区字节数 |
0 | 配置形状非法、尺寸溢出,或 config == NULL |
补充说明:
该接口校验实例、流程、通道、行窗口、描述符数量等结构字段。
微秒时间是否可表示成 24 位周期由
im_tpc_init()进一步校验;时间字段非法时本接口仍可能返回非 0。返回值不包含单独的 descriptor workspace。
3.1.4 im_tpc_descriptor_workspace_size_get
size_t im_tpc_descriptor_workspace_size_get(const im_tpc_config_t *config);
功能说明: 计算 HAL DMA descriptor 工作区所需字节数。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
config | const im_tpc_config_t * | 输入 | 待计算配置;只看结构形状,不访问硬件 |
返回值表:
| 返回值 | 说明 |
|---|---|
size_t | 所需 descriptor 工作区字节数 |
0 | 配置形状非法、尺寸溢出,或 config == NULL |
补充说明:
普通流程需要 Shift 和 Heat 两个描述符数组,共 8192 字节。
AUTO_ALIGN且启用电机时还需要 Motor 数组,共 12288 字节。每个数组必须位于自己的 4 KiB 对齐窗口中。
3.2 初始化与反初始化
3.2.1 im_tpc_init
error_code_t im_tpc_init(im_tpc_t **out_dev, const im_tpc_config_t *config);
功能说明:
初始化 MID 唯一实例、HAL/DMA 资源和 TPC 中断,使设备进入 IM_TPC_STATE_READY。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
out_dev | im_tpc_t ** | 输出 | 接收 MID 拥有的设备句柄;不能在初始化期间自行释放或复制 |
config | const im_tpc_config_t * | 输入 | 初始化配置;MID 会复制结构体,但仍保留其工作区指针 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 初始化成功,IRQ_TPC 已申请并使能 |
IM_ERR_INVALID_PARAM | 空指针、形状/时间参数非法、工作区未对齐、两块工作区重叠等 |
IM_ERR_NO_MEMORY | 工作区容量不足、工作区尺寸计算溢出,或描述符数组无法绑定到要求的 4 KiB 窗口 |
IM_ERR_ALREADY_EXISTS | TPC 实例已经被一个 MID 设备占用 |
IM_ERR_BUSY | 另一个 MID 控制路径调用尚未返回 |
| 其它 | platform.prepare()、HAL 或 IRQ 错误;清理失败时可能转换为 IM_ERR_IO |
补充说明:
必须在任务上下文调用。
BSP 时钟和 pinmux 必须预先准备好,除非
platform.prepare()负责。参数、工作区容量和实例占用检查失败时,
*out_dev不被修改。实例已预留后若后续步骤失败,
*out_dev已写入;若对象状态不是RESET,调用方应使用该句柄调用im_tpc_deinit()重试清理。资源顺序为实例 → 工作区绑定 → 平台准备 → waiter → HAL 初始化 → 模块配置 → 回调/IRQ → 使能
IRQ_TPC;失败时逆序回滚。如果
platform.prepare()自身返回失败,MID 不会调用platform.release();prepare必须自行清理已申请的部分资源。
3.2.2 im_tpc_deinit
error_code_t im_tpc_deinit(im_tpc_t *dev);
功能说明: 停止活动请求,并释放 MID 持有的 IRQ、HAL、DMA、板级和等待器资源。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
dev | im_tpc_t * | 输入/输出 | 已初始化或处于 ERROR 的 MID 设备 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 资源全部释放,设备回到 RESET,句柄失效 |
IM_ERR_INVALID_PARAM | dev == NULL |
IM_ERR_NOT_READY | 设备已处于 RESET 且没有资源 |
IM_ERR_BUSY | 等待、process 或事件回调正在执行 |
| 其它 | 清理过程中遇到的第一个错误码 |
补充说明:
活动请求会先被停止,但 deinit 不发送
IM_TPC_EVENT_STOPPED。资源按反初始化顺序释放:停止硬件 → 关闭 TPC 中断 → 关闭
IRQ_TPC→ HAL deinit → 平台释放 → waiter。任一资源未能释放时对象进入
ERROR,可再次调用本接口重试。成功返回后必须丢弃句柄,后续重新使用需再次调用
im_tpc_init()。
3.3 打印控制
3.3.1 im_tpc_start
error_code_t im_tpc_start( im_tpc_t *dev, const im_tpc_print_request_t *request);
功能说明: 校验并复制打印请求,初始化循环 DMA 窗口,启动同步打印流程或独立电机流程。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
dev | im_tpc_t * | 输入/输出 | 已初始化的 MID 设备 |
request | const im_tpc_print_request_t * | 输入 | 打印请求;结构体被复制,bitmap_data 指向的缓冲区不复制 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 请求已启动,状态为 BUSY |
IM_ERR_INVALID_PARAM | 空指针或请求字段不满足 4.3.23 的约束 |
IM_ERR_BUSY | 设备忙,或已有 wait/process/回调会话 |
IM_ERR_NOT_READY | 设备处于 RESET/ERROR,或资源状态不一致 |
| 其它 | HAL、DMA、流收尾或窗口启动错误 |
补充说明:
只允许在线程上下文调用。
bitmap_data从成功启动起必须保持可读且内容不变,直到COMPLETE、ERROR或STOPPED。MID 不创建工作线程;调用方必须周期调用
im_tpc_process()或进入im_tpc_wait()。请求成功接受后清零
error_flags和 pending 事件;校验失败不会改写上一次请求结果。
3.3.2 im_tpc_process
error_code_t im_tpc_process(im_tpc_t *dev);
功能说明: 服务一次活动请求:检查 DMA/TPC 事件、推进进度、回收槽位、补充窗口并判断是否完成。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
dev | im_tpc_t * | 输入/输出 | 处于 BUSY 的 MID 设备 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 本次服务完成,请求可能仍在进行 |
IM_ERR_INVALID_PARAM | dev == NULL |
IM_ERR_NOT_READY | 当前没有活动请求 |
IM_ERR_BUSY | 另一个 wait/process/回调控制路径正在执行 |
| 其它 | DMA、硬件、流记账或停机错误 |
补充说明:
只允许在线程上下文调用。
调用间隔必须满足 6.2 的窗口服务期限。
若发生终结错误,本接口先停止请求;停机成功时记录错误标志并投递
IM_TPC_EVENT_ERROR。若停机本身失败,设备进入ERROR,此时不再投递错误事件。
3.3.3 im_tpc_wait
error_code_t im_tpc_wait(im_tpc_t *dev, uint32_t timeout_ms);
功能说明: 等待当前请求结束,同时周期服务窗口。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
dev | im_tpc_t * | 输入/输出 | 处于 BUSY 的 MID 设备 |
timeout_ms | uint32_t | 输入 | 超时阈值,单位毫秒;0 表示立即检查,IM_TPC_WAIT_FOREVER 表示永久等待 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 请求完成,或被另一任务 im_tpc_stop() 成功取消 |
IM_ERR_INVALID_PARAM | dev == NULL |
IM_ERR_NOT_READY | 没有活动请求 |
IM_ERR_BUSY | 已有等待会话、process、事件回调或其它控制路径正在执行 |
IM_ERR_TIMEOUT | 等待超时;请求已停止并记录 IM_TPC_ERROR_TIMEOUT |
| 其它 | 请求的终止错误码 |
补充说明:
只允许在线程上下文调用。
描述符 IRQ 可唤醒等待者,同时以 1 ms 定时等待兜底服务。
等待会话结束前不能开始下一次请求。
被 stop 中断时返回
IM_OK,需通过STOPPED事件或进度快照区分完成与取消。timeout_ms是终止判据;控制路径短暂被占用时,实际返回时间可能晚于该阈值。
3.3.4 im_tpc_print
error_code_t im_tpc_print( im_tpc_t *dev, const im_tpc_print_request_t *request, uint32_t timeout_ms);
功能说明:
在同一个控制路径中执行 im_tpc_start() 和 im_tpc_wait(),用于同步打印。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
dev | im_tpc_t * | 输入/输出 | 处于 READY 的 MID 设备 |
request | const im_tpc_print_request_t * | 输入 | 打印请求 |
timeout_ms | uint32_t | 输入 | 等待超时或 IM_TPC_WAIT_FOREVER |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 请求完成,或等待期间被另一任务停止 |
IM_ERR_INVALID_PARAM | 空指针或启动请求非法 |
IM_ERR_BUSY | 已有控制路径占用 |
IM_ERR_NOT_READY | 设备状态不允许启动 |
IM_ERR_TIMEOUT | 等待超时,请求已停止 |
| 其它 | 启动或异步等待的终止错误码 |
补充说明:
本接口不创建独立任务;同步完成能力来自调用线程内部的合作式等待循环。
3.3.5 im_tpc_stop
error_code_t im_tpc_stop(im_tpc_t *dev);
功能说明:
停止当前活动请求并尝试把设备恢复到 READY。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
dev | im_tpc_t * | 输入/输出 | MID 设备 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 请求已停止,或设备本来处于 READY |
IM_ERR_INVALID_PARAM | dev == NULL |
IM_ERR_BUSY | 回调、process 或其它控制路径正在执行 |
IM_ERR_NOT_READY | 设备为 RESET,或处于 ERROR 且没有活动硬件 |
| 其它 | HAL 停机失败;设备进入 ERROR 且记录内部错误 |
补充说明:
可从另一任务调用,以中断正在
im_tpc_wait()/im_tpc_print()的请求。wait循环正在服务窗口时,跨任务stop仍可能短暂返回IM_ERR_BUSY;调用方应重试。只有确实停止了活动请求时才投递
IM_TPC_EVENT_STOPPED。在
READY调用是幂等的,不访问硬件,也不产生事件。
3.4 状态与诊断
3.4.1 im_tpc_get_state
im_tpc_state_t im_tpc_get_state(const im_tpc_t *dev);
功能说明: 读取当前设备状态快照。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
dev | const im_tpc_t * | 输入 | MID 设备;允许为 NULL |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_TPC_STATE_RESET | dev == NULL 或设备未初始化 |
IM_TPC_STATE_READY / _BUSY / _ERROR | 当前设备状态 |
补充说明:
该接口为无锁快照,可在任务或 ISR 上下文读取。
3.4.2 im_tpc_get_last_status
error_code_t im_tpc_get_last_status(const im_tpc_t *dev);
功能说明: 读取最近一次操作记录的公共状态码。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
dev | const im_tpc_t * | 输入 | MID 设备 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK / IM_ERR_* | 最近一次记录的结果 |
IM_ERR_INVALID_PARAM | dev == NULL |
补充说明:
该状态不能替代
im_tpc_get_error()提供的硬件错误标志。参数或状态校验直接失败的调用不一定更新
last_status;判断当前调用结果时应优先检查 该调用的返回值。
3.4.3 im_tpc_get_error
uint32_t im_tpc_get_error(const im_tpc_t *dev);
功能说明:
读取最近保留的 IM_TPC_ERROR_* 位掩码。初始化阶段的平台准备失败和后续请求运行期错误
都可能记录在其中。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
dev | const im_tpc_t * | 输入 | MID 设备;允许为 NULL |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_TPC_ERROR_* 位掩码 | 当前累计错误 |
IM_TPC_ERROR_INTERNAL | dev == NULL |
补充说明:
错误位在下一次请求成功接受时清零;
stop和成功的deinit不单独清零该值。
3.4.4 im_tpc_get_progress
error_code_t im_tpc_get_progress( const im_tpc_t *dev, im_tpc_progress_t *progress_out);
功能说明: 获取阶段、总行数、已排队行数和已完成行数的一致性快照。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
dev | const im_tpc_t * | 输入 | MID 设备 |
progress_out | im_tpc_progress_t * | 输出 | 进度快照,不能为 NULL |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 快照已写入 |
IM_ERR_INVALID_PARAM | dev 或 progress_out 为 NULL |
补充说明:
快照可能等待正在进行的任务上下文更新完成,不应在 ISR 中调用。
在首次成功启动请求前,阶段和三个行计数均为 0。
3.4.5 im_tpc_regs_dump
error_code_t im_tpc_regs_dump(im_tpc_instance_t instance);
功能说明: 为指定 TPC 实例创建临时 HAL 软件句柄并调用 HAL 寄存器 dump。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
instance | im_tpc_instance_t | 输入 | 当前仅支持 IM_TPC_INSTANCE_0 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 寄存器已通过 HAL 日志输出 |
IM_ERR_INVALID_PARAM | 实例号非法或 HAL 句柄校验失败 |
IM_ERR_UNSUPPORTED | HAL 调试助手未同时使能 |
补充说明:
该接口不调用
im_tpc_init()、不占用 MID 实例,也不修改 TPC 配置。如果 TPC 正在运行,本接口读取到的多个寄存器值可能来自不同时间点,不能视为原子快照。
4. 行为约束与实现说明
4.1 实时服务期限
循环窗口只能吸收有限的调度抖动,不能把打印流与任务调度解耦。窗口深度必须满足 最坏负载下:
最大服务间隔 < (window_line_count - 1) * 最小行周期
行周期要取可能出现的最短值(prime 行时序、请求级时序覆盖、同步电机时序)。一旦错过 期限,DMA 可能重复旧窗口数据,已经输出的打印数据无法由 MID 恢复——因此:
调用
im_tpc_process()/im_tpc_wait()的任务优先级必须能保证该期限;window_line_count越大越能容忍抖动,但每个槽需要一个 DMA 描述符,上限 128;MID 内部用“描述符中断唤醒 + 1 ms 定时重试”的方式服务,1 ms 是兜底而不是服务期限。
4.2 并发模型
| 规则 | 说明 |
|---|---|
| 单实例 | 整个 MID 只有一个设备对象;第二个 im_tpc_init() 返回 IM_ERR_ALREADY_EXISTS |
| 控制路径串行 | 控制类 API(init / start / process / wait / print / stop / deinit)进入时先抢一个全局非重入锁,抢不到直接返回 IM_ERR_BUSY,不会阻塞等待 |
| 等待会话独占 | im_tpc_wait() / im_tpc_print() 期间会登记“等待会话”,此时 im_tpc_start() 和 im_tpc_process() 都返回 IM_ERR_BUSY;必须等等待函数返回才能发起新请求 |
| 跨任务停止 | 等待循环在每次服务之间释放锁,因此另一个任务可以调用 im_tpc_stop() 中断本次打印 |
| ISR 限制 | init/start/process/wait/print/stop/deinit 都只能在任务上下文调用,ISR 内只能读 get_state() / get_error() / get_last_status() |
| 回调限制 | 事件回调在任务上下文同步执行,回调期间再调用上述控制 API 会返回 IM_ERR_BUSY |
4.3 错误、超时与恢复
错误来源与上报形式:
| 触发源 | error_flags | 公共返回码或状态记录 |
|---|---|---|
| 移位 FIFO 欠载 | IM_TPC_ERROR_SHIFT_UNDERRUN | IM_ERR_IO |
| 加热 FIFO 欠载 / 加热超时 | IM_TPC_ERROR_HEAT_UNDERRUN / IM_TPC_ERROR_HEAT_OVERTIME | IM_ERR_HARDWARE |
| 电机 FIFO 欠载 / 步超时 | IM_TPC_ERROR_MOTOR_UNDERRUN / IM_TPC_ERROR_MOTOR_OVERTIME | IM_ERR_HARDWARE |
自动对齐错误(AUTO_ALIGN_ERROR) | IM_TPC_ERROR_ALIGN | IM_ERR_HARDWARE |
| DMA 通道上报错误(需 PDMA 中断所有者,见 6.4) | IM_TPC_ERROR_DMA | IM_ERR_IO |
| 软件流记账异常 | IM_TPC_ERROR_STREAM | IM_ERR_IO |
| 等待超时 | IM_TPC_ERROR_TIMEOUT | IM_ERR_TIMEOUT |
platform.prepare 失败 | IM_TPC_ERROR_PLATFORM | im_tpc_init() 返回平台回调错误;清理成功后对象可为 RESET,因此不能只看 last_status |
| 请求停机失败或停机后资源不一致 | IM_TPC_ERROR_INTERNAL | 公共 API 返回停机或资源错误,状态转 ERROR |
| 启动前发现活动掩码或 ready 资源不一致 | 不保证置位 | im_tpc_start() 返回 IM_ERR_NOT_READY,last_status = IM_ERR_HARDWARE,状态转 ERROR |
deinit 清理 HAL/IRQ 失败 | 不保证置位 | im_tpc_deinit() 返回首个底层错误,状态保持 ERROR |
行为约定:
请求期间出现错误时,MID 会先停机(
STOPPING相位),成功停机后设备回到READY, 可以再次im_tpc_start();错误信息通过error_flags和IM_TPC_EVENT_ERROR保留。如果终结路径中的停机本身失败,设备进入
ERROR且不投递IM_TPC_EVENT_ERROR; 调用方不能只等待回调,必须同时检查控制 API 的返回值和设备状态。错误标志在下一次请求成功启动时清零,所以要在启动新请求之前读取并记录。
超时会终止请求,不会继续后台打印;超时后的
error_flags含IM_TPC_ERROR_TIMEOUT。请求路径进入
ERROR的主要原因是停机失败或停机后资源状态不一致;deinit的 HAL/IRQ 清理失败也会保持ERROR。若仍有活动硬件,可先重试im_tpc_stop();无法恢复时执行im_tpc_deinit()后重新初始化。并非所有清理失败都会设置
IM_TPC_ERROR_INTERNAL;当deinit返回非IM_OK时, 应同时检查返回码、get_state()和get_error()。分析欠载类错误时,优先检查:TPC 功能时钟配置、
window_line_count深度、任务是否 满足 6.2 的服务期限、以及系统里是否存在长时间关中断的操作。
4.4 已知限制与注意事项
只支持自动流程:手动打印头/手动电机、WDT 配置与喂狗需要用 HAL/LL 或系统安全 模块,MID 不提供。
不管理 GPIO 与 CPM:pinmux、模块时钟和软件复位由 BSP 或
platform.prepare负责。单实例、不可重入:一个设备对象、一个控制路径;并发调用的任务必须自行安排调用 顺序并处理
IM_ERR_BUSY。bitmap 必须保持有效:异步启动后到收到终止事件之前,不能释放或改写
bitmap_data。描述符工作区必须满足 4 KiB 窗口约束:虚拟地址和物理地址都要对齐,每个描述符 数组还必须完全落在同一个 4 KiB 物理窗口内。当前绑定失败可能返回
IM_ERR_NO_MEMORY,指针未对齐属于IM_ERR_INVALID_PARAM。AUTO_ALIGN必须启用电机,且stop_step_time_us != 0;align_step_index从 1 开始计数,常见取值为repeat_step_count。加热等待字段的优先级:
module_wait_cycles/next_wait_cycles任一非 0 时,wait_time_us被完全忽略(见 4.3.20)。被停止的请求返回
IM_OK:见 5.3 的注意事项,需用事件区分完成与停止。MID 不打印任何逐行日志:进度与错误通过事件、
im_tpc_get_progress()和im_tpc_get_error()暴露,应用自行决定是否落日志(逐行打印会拖慢 DMA 服务)。平台释放回调依赖准备回调:当前实现只在非空
prepare成功返回后记录平台资源, 因而释放阶段才会调用release;只提供release不会被调用。
5. TPC MID 典型用法
以下片段省略了 bitmap 提供方式与错误日志,只展示调用顺序。
5.1 三种打印模式
TPC 模块当前涉及三种打印流程。先明确 API 边界:
手动模式不属于 TPC MID 公共 API。它需要应用直接调用 HAL,由应用逐事件启动 Shift、Latch、Heat 和 Motor;SDK 参考实现为
code/examples/hal/tpc_manual_mode_example.c。自动非对齐模式属于 TPC MID,配置值为
IM_TPC_FLOW_AUTO_NON_ALIGN。自动对齐模式属于 TPC MID,配置值为
IM_TPC_FLOW_AUTO_ALIGN。
| 模式 | 入口 | 打印头与电机关系 | DMA 组织 | 完成条件 |
|---|---|---|---|---|
| 手动 | HAL/LL,应用编排 | 应用通过 TPC 事件逐行启停和衔接 | Shift/Heat 按行使用单次 DMA;Latch 直接触发;Motor 使用覆盖整次打印的独立 DMA | 所有行均已启动、所有 Heat 均已结束,且 Motor 已完成 |
| 自动非对齐 | MID im_tpc_start() / im_tpc_print() | PDDMC 驱动打印头自动流程;电机可选且独立运行 | Shift/Heat 使用循环窗口;电机使用一次性独立 DMA | 打印头完成;启用电机时还要求电机完成 |
| 自动对齐 | MID im_tpc_start() / im_tpc_print() | PAFC 自动对齐流程按 align_step_index 联动 | Shift/Heat/Motor 三路循环 DMA 窗口 | 收到 AUTO_ALIGN_FINISH 或 PRINT_HEAD_FINISH,实现把两者都归入打印头完成条件 |
5.1.1 手动模式流程
手动模式不使用 im_tpc_t。典型流程如下:
流程节点
HEAT_FINISH → 电机流是否已启动?
电机流是否已启动? → 等待下一次对齐重复步 · 是
等待下一次对齐重复步 → 还有后续打印行?
还有后续打印行? → 等待 MOTOR_FINISH,并确认所有行的 Shift/Heat 均完成 · 否
逻辑要点:
Shift、Latch、Heat 构成一条逐行事件链;应用必须在回调中启动下一阶段。 独立 Motor DMA。
后续行通常由 Motor 的
MOTOR0_REPEAT_STEP事件触发下一次 Shift;电机只放行align_step_index对应的重复步中断。完成条件由应用维护:所有行都已启动 Shift、所有 Heat 已结束,且 Motor 已完成。
手动模式需要应用自行处理错误、缓冲区和资源回收,不能通过 MID 的
im_tpc_start/process/wait/stop管理。
5.1.2 自动非对齐模式流程
流程节点
motor.enabled? → 仅打印头运行 · 否
仅打印头运行 → im_tpc_process() / im_tpc_wait() 持续补充 Shift/Heat 窗口
PRINT_HEAD_FINISH → 打印头和可选电机都完成?
MOTOR_FINISH → 打印头和可选电机都完成?
打印头和可选电机都完成? → IM_TPC_EVENT_COMPLETE · 是
逻辑要点:
打印头由 TPC 的自动打印头流程驱动,不存在应用逐行启动 Shift → Latch → Heat 的软件事件链。
PRINT_HEAD_FINISH是打印头完成条件;启用电机时还必须等待MOTOR_FINISH。电机使用独立 DMA,不与打印头形成自动对齐闭环。MID 会把电机行周期用于加热下一单元 的节奏计算,避免电机启用后仍沿用不相关的静态等待。
不需要电机时,该模式仍可工作,打印头按配置的自动非对齐时序运行。
5.1.3 自动对齐模式流程
流程节点
TPC 自动对齐流程运行 → im_tpc_process() / im_tpc_wait() 维护三路窗口
AUTO_ALIGN_FINISH / PRINT_HEAD_FINISH → IM_TPC_EVENT_COMPLETE
逻辑要点:
必须设置
motor.enabled = true,且stop_step_time_us != 0。align_step_index决定哪个重复步作为打印头与电机对齐点,取值范围为1 ~ repeat_step_count。Shift、Heat、Motor 都由循环窗口提供数据;进度按三路 DMA 中最慢的一路推进。
TPC 在
PAFC中完成打印头与电机的硬件联动,因此不采用自动非对齐模式的独立电机 DMA。MID 完成后不会额外调用 stop;可能残留为
BUSY的 DMA handle 在后续 start 或 deinit 时回收。
5.2 同步整图打印
static im_tpc_t *s_tpc;
static uint8_t s_workspace[16384]
__attribute__((aligned(IM_TPC_WORKSPACE_ALIGN)));
static uint8_t s_desc_workspace[IM_TPC_DESCRIPTOR_WORKSPACE_ALIGN * 3U]
__attribute__((aligned(IM_TPC_DESCRIPTOR_WORKSPACE_ALIGN)));
im_tpc_config_t config;
im_tpc_config_struct_init(&config);
config.flow_mode = IM_TPC_FLOW_AUTO_NON_ALIGN;
config.functional_clock_hz = 24000000U;
config.shift.output_clock_hz = 12000000U;
config.shift.line_bytes = 48U;
config.latch.wait_time_us = 1U;
config.latch.active_time_us = 1U;
config.latch.shift_wait_time_us = 3U;
config.prime_line_count = 32U;
config.window_line_count = 32U;
config.max_line_count = 384U;
config.heat.channel_count = 2U;
config.heat.default_heat_time_us = 3600U;
config.motor.enabled = true;
config.motor.channel_count = 4U;
config.motor.repeat_step_count = 8U;
config.motor.align_step_index = 8U;
config.motor.repeat_step_time_us = 1000U;
/* 每个重复步的输出电平掩码:按电机相序填写 repeat_output_mask[],
* 参考 code/middleware/tpc/example/main/im_tpc_example_single_print_sync.c
* 中的 s_tpc_motor_repeat_mask[]。 */
config.max_step_time_us = 20000U;
config.max_heat_time_us = 15000U;
config.workspace = s_workspace;
config.workspace_size = im_tpc_workspace_size_get(&config);
config.descriptor_workspace = s_desc_workspace;
config.descriptor_workspace_size = im_tpc_descriptor_workspace_size_get(&config);
error_code_t ret = im_tpc_init(&s_tpc, &config);
if (ret != IM_OK) {
if ((s_tpc != NULL) && (im_tpc_get_state(s_tpc) != IM_TPC_STATE_RESET)) {
(void)im_tpc_deinit(s_tpc);
}
return; /* 同时记录 ret 与 get_error() */
}
im_tpc_print_request_t request;
im_tpc_print_request_init(&request);
request.bitmap_data = bitmap;
request.bitmap_size = bitmap_size;
request.line_count = 384U;
request.stride_bytes = 48U;
ret = im_tpc_print(s_tpc, &request, 10000U);
if (ret != IM_OK) {
uint32_t flags = im_tpc_get_error(s_tpc);
/* 记录 ret 与 flags,见 4.3.7 */
}
if (im_tpc_deinit(s_tpc) == IM_OK) {
s_tpc = NULL;
}5.3 异步打印 + 事件回调
static void tpc_event(im_tpc_t *dev, im_tpc_event_t event,
const im_tpc_event_info_t *info, void *ctx)
{
(void)dev;
(void)ctx;
switch (event) {
case IM_TPC_EVENT_PROGRESS:
/* 可只记录计数,避免逐行打印拖慢服务 */
break;
case IM_TPC_EVENT_COMPLETE:
/* info->progress.completed_lines == info->progress.total_lines */
break;
case IM_TPC_EVENT_STOPPED:
break;
case IM_TPC_EVENT_ERROR:
/* info->status 终止错误码;info->error_flags 见 4.3.7 */
break;
default:
break;
}
}
/* event_callback 会在 im_tpc_init() 时复制进设备,必须在 init 前设置。 */
config.event_callback = tpc_event;
s_tpc = NULL;
error_code_t ret = im_tpc_init(&s_tpc, &config);
if (ret != IM_OK) {
if ((s_tpc != NULL) && (im_tpc_get_state(s_tpc) != IM_TPC_STATE_RESET)) {
(void)im_tpc_deinit(s_tpc);
}
return;
}
ret = im_tpc_start(s_tpc, &request); /* 启动会清零 error_flags,如需旧值请先读取 */
if (ret != IM_OK) {
if (im_tpc_deinit(s_tpc) == IM_OK) {
s_tpc = NULL;
}
return;
}
while (im_tpc_get_state(s_tpc) == IM_TPC_STATE_BUSY) {
ret = im_tpc_process(s_tpc);
if (ret != IM_OK) {
break; /* 请求已终结;若停机失败则设备转 ERROR 且可能没有 ERROR 事件 */
}
/* 间隔必须满足 6.2 的服务期限 */
msleep(1U); /* OS 阻塞睡眠,见 code/include/os/thread.h */
}
if (im_tpc_deinit(s_tpc) == IM_OK) {
s_tpc = NULL;
}异步方式下必须保证服务间隔满足 6.2;不能用“每次循环都做耗时工作”的方式轮询。
5.4 由另一任务停止
/* 任务 A:正在 im_tpc_wait() 或 im_tpc_print() */ /* 任务 B: */ im_tpc_stop(s_tpc); /* 成功返回后,任务 A 的等待函数返回 IM_OK */
任务 A 需要依据 IM_TPC_EVENT_STOPPED 或进度(completed_lines < total_lines)判断
本次是“被停止”而不是“打印完成”。
5.5 反初始化与再次使用
if (im_tpc_deinit(s_tpc) == IM_OK) {
s_tpc = NULL; /* 原句柄已失效 */
error_code_t ret = im_tpc_init(&s_tpc, &config);
/* 按 im_tpc_init() 的返回值处理失败;可能需要再次 deinit()。 */
}如果 im_tpc_deinit() 返回 IM_OK,设备对象与实例占用都已释放,可以立即重新im_tpc_init();如果返回其它错误,先按 6.5 判断是否处于 ERROR 状态再决定是否重试。
6. 示例程序与使用方式
6.1 示例选择
示例入口为 code/middleware/tpc/example/main/main.c。构建时通过IM_TPC_EXAMPLE_SELECTION 选择一个入口:
| 选择值 | 说明 | 主要 MID API |
|---|---|---|
single_print | 单次打印;流程由 IM_TPC_EXAMPLE_SYNC_FLOW 选择 | im_tpc_print() |
multi_print(默认) | 同一 MID 会话内连续打印 3 次 | im_tpc_print() |
auto_non_align | 自动非对齐打印一次 | im_tpc_print() |
auto_align | 自动对齐打印一次 | im_tpc_print() |
single_print 默认使用 auto_non_align;选择自动对齐时增加:
-D IM_TPC_EXAMPLE_SYNC_FLOW=auto_align
6.2 构建配置
G32S107 CPM 板测示例:
tools/imde/bin/imde use-defconfig \ code/middleware/tpc/example/tpc_example_board_defconfig tools/imde/bin/imde --no-preset -B build-tpc-mid-board \ -D SDK_APP_DIR=code/middleware/tpc/example \ -D IM_TPC_EXAMPLE_SELECTION=multi_print \ -D IM_TPC_EXAMPLE_USE_CPM=1 \ -D IM_TPC_EXAMPLE_GPIO_INIT=1 \ -D IM_TPC_EXAMPLE_FPGA_CTRL_HIGH=0 \ -D IM_TPC_EXAMPLE_TPC_CLK_HZ=125000000 \ -D IM_TPC_EXAMPLE_SHIFT_CLK_HZ=12500000 \ -D IM_TPC_EXAMPLE_TPC_CLK_PARENT=MPLL configure --fresh tools/imde/bin/imde --no-preset \ -B build-tpc-mid-board build --no-configure
FPGA 示例:
tools/imde/bin/imde use-defconfig \ code/middleware/tpc/example/tpc_example_defconfig tools/imde/bin/imde --no-preset -B build-tpc-mid-fpga \ -D SDK_APP_DIR=code/middleware/tpc/example \ -D IM_TPC_EXAMPLE_SELECTION=multi_print \ -D IM_TPC_EXAMPLE_USE_CPM=0 \ -D IM_TPC_EXAMPLE_GPIO_INIT=0 \ -D IM_TPC_EXAMPLE_FPGA_CTRL_HIGH=1 configure --fresh tools/imde/bin/imde --no-preset \ -B build-tpc-mid-fpga build --no-configure
切换板级 defconfig、示例或时钟后,应使用独立构建目录并执行 configure --fresh,避免
继续使用 CMakeCache.txt 中的旧参数。
6.3 常用配置
| CMake 变量 | 默认值 | 说明 |
|---|---|---|
IM_TPC_EXAMPLE_SELECTION | multi_print | 选择示例入口。 |
IM_TPC_EXAMPLE_USE_CPM | 0 | 为 1 时由示例配置 TPC CPM 时钟;FPGA 必须为 0。 |
IM_TPC_EXAMPLE_TPC_CLK_HZ | 500000000 | CPM 模式下的 TPC 功能时钟。 |
IM_TPC_EXAMPLE_SHIFT_CLK_HZ | 250000000 | CPM 模式下的移位输出时钟。 |
IM_TPC_EXAMPLE_TPC_CLK_PARENT | MPLL | 可选 MPLL、SCLK_A 或 KEEP。 |
IM_TPC_EXAMPLE_GPIO_INIT | 跟随 USE_CPM | 为 1 时配置 PA0..21、PC8..15 的 TPC 复用功能。 |
IM_TPC_EXAMPLE_FPGA_CTRL_HIGH | 跟随 USE_CPM 的相反值 | 为 1 时把 FPGA 专用 PA23/PA24 配置为 GPIO 输出并驱动高。 |
IM_TPC_EXAMPLE_SYNC_FLOW | auto_non_align | 仅影响 single_print。 |
IM_TPC_EXAMPLE_PRIME_LINES | 32 | prime 行数,示例允许 0 ~ 32。 |
G32S107 板测已验证使用 125 MHz / 12.5 MHz;CMake 默认仍为 500 MHz / 250 MHz,
可通过显式参数切换。
6.4 示例文件
| 文件 | 说明 |
|---|---|
code/middleware/tpc/example/tpc_example_defconfig | FPGA / IMAGE4 配置 |
code/middleware/tpc/example/tpc_example_board_defconfig | G32S107 CPM 板测配置 |
code/middleware/tpc/example/main/main.c | 示例入口选择 |
code/middleware/tpc/example/main/im_tpc_example_single_print.c | 自动对齐/非对齐单次打印 |
code/middleware/tpc/example/main/im_tpc_example_single_print_sync.c | 单次同步打印 |
code/middleware/tpc/example/main/im_tpc_example_multi_print.c | 连续多次打印 |
code/middleware/tpc/example/main/im_tpc_example_cpm.c | 可选 CPM 时钟初始化 |
code/middleware/tpc/example/main/im_tpc_example_gpio.h | 可选板测 pinmux 与 FPGA 专用 PA23/PA24 初始化 |
code/middleware/tpc/example/README.md | 示例构建与运行说明 |
7. MID 错误码与错误标志
7.1 返回码
| 错误码 | 常见原因 |
|---|---|
IM_OK | 成功 |
IM_ERR_INVALID_PARAM | 参数为空、配置形状/时间参数非法、请求越界、工作区未对齐或重叠、实例号非法 |
IM_ERR_NOT_READY | 设备处于 RESET/ERROR、没有活动请求、或需要的资源已不可用 |
IM_ERR_BUSY | 全局控制路径被占用、已有等待会话、事件回调进行中 |
IM_ERR_ALREADY_EXISTS | 已有设备占用 TPC 实例(im_tpc_init()) |
IM_ERR_NO_MEMORY | 工作区或描述符工作区容量不足、工作区尺寸计算溢出,或描述符数组无法绑定到要求的 4 KiB 窗口 |
IM_ERR_TIMEOUT | im_tpc_wait() / im_tpc_print() 超时(请求已被终止) |
IM_ERR_IO | DMA 或流记账类错误结束请求;初始化清理后状态为 ERROR |
IM_ERR_HARDWARE | 加热/电机/自动对齐错误,或停机失败 |
IM_ERR_UNSUPPORTED | im_tpc_regs_dump() 在 HAL 调试助手未使能时调用 |
(im_irq_*)的错误码,以及 HAL 内部透传的 DMA 错误码。判定“哪里出错”时,优先看im_tpc_get_error() 的 im_tpc_error_t 标志,再看返回码与 last_status。
7.2 错误标志
错误标志的位定义与触发源见 4.3.7 和 6.5。运行期错误在一次请求期间只累加,下一次
请求成功启动时清零;初始化阶段的 IM_TPC_ERROR_PLATFORM 是例外。stop 和成功的deinit 不清零这些位;deinit 后句柄已经失效,应用不应继续把它作为活动对象查询。