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_TPCn编译 TPC MID;依赖 DRIVER_TPC、DRIVER_IRQ 和 OS_API。
DRIVER_TPCn提供 TPC HAL;Kconfig 会自动选择 DRIVER_DMA。
DRIVER_IRQn提供 IRQ_TPC 的申请、使能和释放接口。
OS_APIn提供线程等待器和任务上下文同步能力。
DRIVER_GPIOnIM_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_FOREVERim_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_SIZEFIFO/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_COMPLETEIM_OK请求成功完成。
IM_TPC_EVENT_STOPPEDIM_OK活动请求被应用停止。
IM_TPC_EVENT_PROGRESSIM_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_ALIGNTPC 自动对齐流程错误。
IM_TPC_ERROR_DMADMA handle 或传输错误,需要 PDMA 错误事件可见。
IM_TPC_ERROR_TIMEOUTim_tpc_wait() / im_tpc_print() 超时。
IM_TPC_ERROR_STREAM软件流记账、地址或槽位不一致。
IM_TPC_ERROR_PLATFORMplatform.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_hz12 MHz必须整除 functional_clock_hz,分频比 1 ~ 256。
idle_levelIM_TPC_LEVEL_LOW必须是合法 im_tpc_level_t。
endian / spi_polarity / spi_phaseBig / idle low / first edge必须是相应枚举的有效值。
setup_cycles / hold_cycles00 ~ 3。
channel_count11 ~ 16。
data_invertfalse为 true 时由 HAL 反转移位数据位。
line_bytes4非 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_levelIM_TPC_LEVEL_HIGH必须是合法 im_tpc_level_t。
wait_time_us0换算后的周期数不超过 24 位。
active_time_us0换算后的周期数不超过 24 位。
shift_wait_time_us0换算后的周期数不超过 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_levelIM_TPC_LEVEL_LOW必须是合法 im_tpc_level_t。
channel_count11 ~ 4。
wait_time_us0两个显式周期字段都为 0 时生效。
default_heat_time_us0不超过 max_heat_time_us,并能换算为 24 位周期。
max_heat_time_us1000不小于默认和 prime 加热时间,并能换算为 24 位周期。
module_wait_cycles0不超过 24 位;任一显式周期非 0 时与 next_wait_cycles 共同生效。
next_wait_cycles0同上;非对齐 + 电机流程由电机行周期替代该等待。
prime_heat_time_us00 表示沿用 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;
字段默认值约束
enabledfalse自动对齐流程必须为 true。
motor_id00 或 1。
channel_count11 ~ 8;Motor1 最多 4。
idle_level_mask / start_level_mask / end_level_mask0只能使用 channel_count 范围内的低位。
repeat_step_count1启用时为 1 ~ 64。
align_step_index11 ~ 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_us0不超过 max_step_time_us 和 24 位周期上限。
max_step_time_us0电机启用时作为其它步时间的上限。

上表中除 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;
字段默认值约束
instanceIM_TPC_INSTANCE_0当前只能取该值。
flow_modeAUTO_NON_ALIGN只能取两个自动流程之一;AUTO_ALIGN 要求启用电机。
functional_clock_hz24 MHz非 0;必须与板级实际 TPC 功能时钟一致。MID 只用它换算周期,不配置 CPM/时钟源。
max_line_count1非 0;且 prime_line_count + max_line_count + AUTO_ALIGN 的 2 个收尾槽 不得超过 UINT32_MAX。
prime_line_count1不大于 window_line_count。
window_line_count322 ~ 128。
shift / latch / heat / motor见各自类型使用对应约束。
workspace / workspace_sizeNULL / 0调用方分配,按 64 字节对齐,容量覆盖完整生命周期。
descriptor_workspace / descriptor_workspace_sizeNULL / 0调用方分配,虚拟/物理地址均按 4096 字节对齐。
platform全 0可选板级回调。
event_callback / user_ctxNULL / 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_dataNULL必须非空且 CPU 可读;异步请求终止前保持有效。若由 non-coherent DMA 生产者写入,调用方必须在 start 前保证数据对 CPU 可见。
bitmap_size0至少为 (line_count - 1) * stride_bytes + shift.line_bytes。
line_count01 ~ config.max_line_count。
stride_bytes0不小于 shift.line_bytes。
mirror_horizontalfalse为 true 时逐行执行位级水平镜像。
heat_time_us00 使用配置默认值;非 0 时不超过最大加热时间和 24 位周期。
repeat_step_time_us00 使用配置默认值;电机启用时非 0 值受最大步时间约束。

所有微秒配置都按 functional_clock_hz 向上取整换算为功能时钟周期:

cycles = ceil(time_us * functional_clock_hz / 1000000)

换算结果必须能放入对应硬件的 24 位周期字段。

2.4 状态与事件流转

STATE  RESET
→
STATE  READY
→
STATE  BUSY
→
STATE  ERROR

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_workspaceHAL 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_tMIDim_tpc_init() 成功后由 MID 持有,im_tpc_deinit() 后失效
workspace / descriptor_workspace调用方覆盖整个 MID 生命周期
bitmap_data调用方覆盖当前异步请求直到终止事件
IRQ_TPCMIDim_tpc_init() 申请,im_tpc_deinit() 释放
TPC HAL/DMA handleMID(经 HAL)im_tpc_init() 配置,im_tpc_deinit() 释放
板级时钟、pinmux、GPIOplatform 回调或 BSP由板级自行管理

3. 接口规范

本章按 ADC MID 文档的统一格式说明每个公开接口:

  1. 完整函数原型;

  2. 功能说明;

  3. 参数表;

  4. 返回值表;

  5. 补充说明。

除特别说明外,控制类接口只能在线程上下文调用,不能从 ISR 或事件回调中重入。

3.1 默认值与尺寸查询

3.1.1 im_tpc_config_struct_init

void im_tpc_config_struct_init(im_tpc_config_t *config);

功能说明: 把配置结构体清零并填入 MID 默认配置,不访问硬件。

参数表:

参数类型方向说明
configim_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);

功能说明: 把打印请求清零,不访问硬件。

参数表:

参数类型方向说明
requestim_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);

功能说明: 计算配置所需的运行时行窗口工作区字节数。

参数表:

参数类型方向说明
configconst 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 工作区所需字节数。

参数表:

参数类型方向说明
configconst 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_devim_tpc_t **输出接收 MID 拥有的设备句柄;不能在初始化期间自行释放或复制
configconst im_tpc_config_t *输入初始化配置;MID 会复制结构体,但仍保留其工作区指针

返回值表:

返回值说明
IM_OK初始化成功,IRQ_TPC 已申请并使能
IM_ERR_INVALID_PARAM空指针、形状/时间参数非法、工作区未对齐、两块工作区重叠等
IM_ERR_NO_MEMORY工作区容量不足、工作区尺寸计算溢出,或描述符数组无法绑定到要求的 4 KiB 窗口
IM_ERR_ALREADY_EXISTSTPC 实例已经被一个 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、板级和等待器资源。

参数表:

参数类型方向说明
devim_tpc_t *输入/输出已初始化或处于 ERROR 的 MID 设备

返回值表:

返回值说明
IM_OK资源全部释放,设备回到 RESET,句柄失效
IM_ERR_INVALID_PARAMdev == 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 窗口,启动同步打印流程或独立电机流程。

参数表:

参数类型方向说明
devim_tpc_t *输入/输出已初始化的 MID 设备
requestconst 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 事件、推进进度、回收槽位、补充窗口并判断是否完成。

参数表:

参数类型方向说明
devim_tpc_t *输入/输出处于 BUSY 的 MID 设备

返回值表:

返回值说明
IM_OK本次服务完成,请求可能仍在进行
IM_ERR_INVALID_PARAMdev == 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);

功能说明: 等待当前请求结束,同时周期服务窗口。

参数表:

参数类型方向说明
devim_tpc_t *输入/输出处于 BUSY 的 MID 设备
timeout_msuint32_t输入超时阈值,单位毫秒;0 表示立即检查,IM_TPC_WAIT_FOREVER 表示永久等待

返回值表:

返回值说明
IM_OK请求完成,或被另一任务 im_tpc_stop() 成功取消
IM_ERR_INVALID_PARAMdev == 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(),用于同步打印。

参数表:

参数类型方向说明
devim_tpc_t *输入/输出处于 READY 的 MID 设备
requestconst im_tpc_print_request_t *输入打印请求
timeout_msuint32_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。

参数表:

参数类型方向说明
devim_tpc_t *输入/输出MID 设备

返回值表:

返回值说明
IM_OK请求已停止,或设备本来处于 READY
IM_ERR_INVALID_PARAMdev == 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);

功能说明: 读取当前设备状态快照。

参数表:

参数类型方向说明
devconst im_tpc_t *输入MID 设备;允许为 NULL

返回值表:

返回值说明
IM_TPC_STATE_RESETdev == 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);

功能说明: 读取最近一次操作记录的公共状态码。

参数表:

参数类型方向说明
devconst im_tpc_t *输入MID 设备

返回值表:

返回值说明
IM_OK / IM_ERR_*最近一次记录的结果
IM_ERR_INVALID_PARAMdev == 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_* 位掩码。初始化阶段的平台准备失败和后续请求运行期错误 都可能记录在其中。

参数表:

参数类型方向说明
devconst im_tpc_t *输入MID 设备;允许为 NULL

返回值表:

返回值说明
IM_TPC_ERROR_* 位掩码当前累计错误
IM_TPC_ERROR_INTERNALdev == 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);

功能说明: 获取阶段、总行数、已排队行数和已完成行数的一致性快照。

参数表:

参数类型方向说明
devconst im_tpc_t *输入MID 设备
progress_outim_tpc_progress_t *输出进度快照,不能为 NULL

返回值表:

返回值说明
IM_OK快照已写入
IM_ERR_INVALID_PARAMdev 或 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。

参数表:

参数类型方向说明
instanceim_tpc_instance_t输入当前仅支持 IM_TPC_INSTANCE_0

返回值表:

返回值说明
IM_OK寄存器已通过 HAL 日志输出
IM_ERR_INVALID_PARAM实例号非法或 HAL 句柄校验失败
IM_ERR_UNSUPPORTEDHAL 调试助手未同时使能

补充说明:

  • 该接口不调用 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_UNDERRUNIM_ERR_IO
加热 FIFO 欠载 / 加热超时IM_TPC_ERROR_HEAT_UNDERRUN / IM_TPC_ERROR_HEAT_OVERTIMEIM_ERR_HARDWARE
电机 FIFO 欠载 / 步超时IM_TPC_ERROR_MOTOR_UNDERRUN / IM_TPC_ERROR_MOTOR_OVERTIMEIM_ERR_HARDWARE
自动对齐错误(AUTO_ALIGN_ERROR)IM_TPC_ERROR_ALIGNIM_ERR_HARDWARE
DMA 通道上报错误(需 PDMA 中断所有者,见 6.4)IM_TPC_ERROR_DMAIM_ERR_IO
软件流记账异常IM_TPC_ERROR_STREAMIM_ERR_IO
等待超时IM_TPC_ERROR_TIMEOUTIM_ERR_TIMEOUT
platform.prepare 失败IM_TPC_ERROR_PLATFORMim_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 已知限制与注意事项

  1. 只支持自动流程:手动打印头/手动电机、WDT 配置与喂狗需要用 HAL/LL 或系统安全 模块,MID 不提供。

  2. 不管理 GPIO 与 CPM:pinmux、模块时钟和软件复位由 BSP 或 platform.prepare负责。

  3. 单实例、不可重入:一个设备对象、一个控制路径;并发调用的任务必须自行安排调用 顺序并处理 IM_ERR_BUSY。

  4. bitmap 必须保持有效:异步启动后到收到终止事件之前,不能释放或改写bitmap_data。

  5. 描述符工作区必须满足 4 KiB 窗口约束:虚拟地址和物理地址都要对齐,每个描述符 数组还必须完全落在同一个 4 KiB 物理窗口内。当前绑定失败可能返回IM_ERR_NO_MEMORY,指针未对齐属于 IM_ERR_INVALID_PARAM。

  6. AUTO_ALIGN 必须启用电机,且 stop_step_time_us != 0;align_step_index从 1 开始计数,常见取值为 repeat_step_count。

  7. 加热等待字段的优先级:module_wait_cycles / next_wait_cycles 任一非 0 时,wait_time_us 被完全忽略(见 4.3.20)。

  8. 被停止的请求返回 IM_OK:见 5.3 的注意事项,需用事件区分完成与停止。

  9. MID 不打印任何逐行日志:进度与错误通过事件、im_tpc_get_progress() 和im_tpc_get_error() 暴露,应用自行决定是否落日志(逐行打印会拖慢 DMA 服务)。

  10. 平台释放回调依赖准备回调:当前实现只在非空 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。典型流程如下:

流程节点

NODE  HAL 配置 Shift / Latch / Heat / Motor
NODE
NODE  SHIFT_FINISH
NODE
NODE  LATCH_FINISH
NODE
NODE  HEAT_FINISH
NODE  电机流是否已启动?
NODE
NODE  等待下一次对齐重复步
NODE  还有后续打印行?
NODE  等待 MOTOR_FINISH,并确认所有行的 Shift/Heat 均完成

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 自动非对齐模式流程

流程节点

NODE  im_tpc_start()
NODE  填充 Shift/Heat 循环窗口
NODE
NODE
NODE  motor.enabled?
NODE
NODE  仅打印头运行
NODE  im_tpc_process() / im_tpc_wait() 持续补充 Shift/Heat 窗口
NODE
NODE  PRINT_HEAD_FINISH
NODE  MOTOR_FINISH
NODE  打印头和可选电机都完成?
NODE  IM_TPC_EVENT_COMPLETE

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 自动对齐模式流程

流程节点

NODE  im_tpc_start()
NODE  填充 Shift/Heat/Motor 三路循环窗口
NODE
NODE
NODE  TPC 自动对齐流程运行
NODE  im_tpc_process() / im_tpc_wait() 维护三路窗口
NODE
NODE  AUTO_ALIGN_FINISH / PRINT_HEAD_FINISH
NODE  IM_TPC_EVENT_COMPLETE

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_SELECTIONmulti_print选择示例入口。
IM_TPC_EXAMPLE_USE_CPM0为 1 时由示例配置 TPC CPM 时钟;FPGA 必须为 0。
IM_TPC_EXAMPLE_TPC_CLK_HZ500000000CPM 模式下的 TPC 功能时钟。
IM_TPC_EXAMPLE_SHIFT_CLK_HZ250000000CPM 模式下的移位输出时钟。
IM_TPC_EXAMPLE_TPC_CLK_PARENTMPLL可选 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_FLOWauto_non_align仅影响 single_print。
IM_TPC_EXAMPLE_PRIME_LINES32prime 行数,示例允许 0 ~ 32。

G32S107 板测已验证使用 125 MHz / 12.5 MHz;CMake 默认仍为 500 MHz / 250 MHz, 可通过显式参数切换。

6.4 示例文件

文件说明
code/middleware/tpc/example/tpc_example_defconfigFPGA / IMAGE4 配置
code/middleware/tpc/example/tpc_example_board_defconfigG32S107 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_TIMEOUTim_tpc_wait() / im_tpc_print() 超时(请求已被终止)
IM_ERR_IODMA 或流记账类错误结束请求;初始化清理后状态为 ERROR
IM_ERR_HARDWARE加热/电机/自动对齐错误,或停机失败
IM_ERR_UNSUPPORTEDim_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 后句柄已经失效,应用不应继续把它作为活动对象查询。