TIM_MODE = 0b000,依赖 CLK_SEL。手册 13.4.2.1。
1. 一句话说明
测量输入 PWM 信号的脉宽(占空比) 和周期,结果存入 GPR0(脉宽)与 GPR1(周期)。
2. 时钟来源与选择
| 项目 | 来源 | 说明 |
|---|---|---|
| 计数时钟 | CMU 时钟 | CNT 按 CCM[i]_CLK_RES[CLK_SEL] 递增,直到检测到下降沿(DSL=1 时) |
| 时钟选择 | CTRL.CLK_SEL [26:24] |
从 CCMi_CLK_RES0..7 中选一路 |
| 时间戳 | TBU | 通过 GPR0_SEL/GPR1_SEL 可选 TS0/TS1/TS2 打时间戳 |
计数分辨率 = 所选 CMU 时钟周期。例如 CLK_SEL 选 50ns 分辨率,则 CNT 每 50ns +1。
3. 计数模式
- CNT 递增计数:在有效电平期间按 CMU 时钟递增;
- 下降沿(DSL=1):把 CNT 值存入影子寄存器 CNTS(脉宽),CNT 继续计数直到下一个上升沿(周期);
- 上升沿(DSL=1):CNTS 值 → GPR0(脉宽),CNT 值 → GPR1(周期),随后 CNT 清零并触发 NEWVAL;
- 之后每个离开 DSL 定义电平的边沿都会更新 CNTS。
起始条件 :
-
IMM_START=0:等首个 DSL 定义的边沿(DSL=1 时等上升沿)才开始; -
IMM_START=1:通道使能(TIM_EN=1)后立即开始。
DSL 极性 :
-
DSL=1:测高电平脉宽 + 周期(高电平占空比); -
DSL=0:测低电平脉宽 + 周期(低电平占空比),等下降沿开始。
ECNT_RESET :
-
=0:CNT 在 DSL 定义的有效边沿 清零; -
=1:CNT 在每个输入边沿都清零。
SWAP_CAPTURE:交换 GPR1 与 CNTS 的捕获边沿(见手册 Table 17 详细动作表)。
4. 外部读取的内容
| 寄存器 | 默认内容(EGPRx_SEL=0, GPRx_SEL=11) | 读取方式 |
|---|---|---|
| GPR0 | 脉宽长度(脉宽计数值) | 先读 GPR1,再读 GPR0(读 GPR0 即 ACK) |
| GPR1 | 周期长度(周期计数值) | |
| CNTS | 脉宽影子值 | 可选读出 |
通过 GPR0_SEL/GPR1_SEL 可改为读 TBU 时间戳等。
数据一致性 :可用
ECNT[7:1]校验 GPR0/GPR1 是否一致(防止读取瞬间被更新)。
5. 对应中断
| 中断 | 触发 |
|---|---|
| NEWVAL | 一个完整 PWM 周期测量完成(GPR0/GPR1 更新) |
| CNTOFL | CNT 溢出(0xFFFFFF→0) |
| ECNTOFL | ECNT 溢出 |
| GPROFL | 上次数据未消费即被覆盖 |
5a. 周期与占空比测量方法(重点)
TPWM 是 GTM 里**测单周期脉宽+周期(占空比/频率)**的唯一正选模式。完整步骤:
配置:
CTRL.TIM_MODE = 0b000 // TPWM
CTRL.DSL = 1 // 测高电平脉宽(=0 则测低电平脉宽)
CTRL.CLK_SEL = 选 CMU 分辨率 // 如 CLK0=20MHz → 50ns/bit
CTRL.GPR0_SEL = 0b11, EGPR0_SEL=0 // GPR0 ← CNTS(脉宽)
CTRL.GPR1_SEL = 0b11, EGPR1_SEL=0 // GPR1 ← CNT(周期)
ECTRL.IMM_START= 0 // 等首个 DSL 边沿开始
CTRL.TIM_EN = 1
测量时序(DSL=1):
┌────────┐ ┌────────┐
──┘ └──────────┘ └──
↑上升沿: CNT=0 开始计数
↓下降沿: CNT 值 → CNTS(脉宽),CNT 继续
↑下一上升沿: CNTS→GPR0(脉宽), CNT→GPR1(周期), CNT=0, 触发 NEWVAL
换算(T_CLK = 所选 CLK_SEL 分辨率周期):
脉宽 t_on = GPR0 × T_CLK
周期 T = GPR1 × T_CLK
占空比 D = GPR0 / GPR1
频率 f = 1 / (GPR1 × T_CLK)
⚠️ TPIM(0b001)不能做周期/占空比测量------它是"脉冲时间累加积分",CNT 跨周期持续累计,无单周期概念。单周期占空比用 TPWM,多周期能量/平均占空比才用 TPIM。
5b. ECNT7:1 数据一致性校验(polling 怎么防读到一半被更新)
原理(硬件快照) :ECNT 每收到一个滤波后边沿 +1。GPR0、GPR1、CNTS 三个寄存器里,每个都自带一个只读的 ECNT 字段 在 bit31:24(GPR0.ECNT / GPR1.ECNT / CNTS.ECNT,Access Type = R),其值"refers to ECNT.ECNT[7:0]"------即这 8 位随时反映此刻 ECNT 计数器的低 8 位。
关键澄清------它并不是"捕获那一刻的冻结快照" :从寄存器定义看,GPR0.ECNT[31:24] 是"活的"(Volatile=True、R 只读),读到的就是读取当下 ECNT 的值,而不是 GPR023:0 被捕获那瞬间的值。因此它的正确用法是读两次、比较两次读到的 ECNT 是否相同,来判断"读取期间是否又来了新边沿/新测量",而不是直接拿 GPR0.ECNT 和 GPR1.ECNT 比(这两个是不同时刻读的,ECNT 一直在涨,天然不等)。
polling 读取 + 校验流程(先 GPR1 再 GPR0,读 GPR0 即 ACK):
uint32_t v1a, v1b, v0;
do {
v1a = READ(GPR1); // 第 1 次读 GPR1(低 24 位=周期,高 8 位=本次 ECNT)
v0 = READ(GPR0); // 读 GPR0(低 24 位=脉宽,读这一下即向硬件 ACK)
v1b = READ(GPR1); // 第 2 次读 GPR1
} while ( ((v1a >> 24) & 0xFE) != ((v1b >> 24) & 0xFE) );
// 两次读 GPR1 的 ECNT[7:1] 一致 → 读取期间无新边沿插入 → v0/v1a 可信
uint32_t pulse = v0 & 0xFFFFFF;
uint32_t period = v1a & 0xFFFFFF;
关键点:
- 为什么先 GPR1 后 GPR0 :手册 13.4 规定
ARU_EN=0(CPU 消费)时,读 GPR0 这一下就是 ACK,硬件据此解除 GPROFL、才允许下一次测量覆盖 GPR0/GPR1。所以顺序固定:先读 GPR1、再读 GPR0 收尾。 - 为什么校验用 7:1 而非 7:0:ECNT0 被复用为当前输入电平 F_OUT(上升沿=奇、下降沿=偶),是电平不是序号,校验只看 bit7:1 的边沿序号。
- 为什么能防止"读到一半被写" :GPR0/GPR1 的更新发生在同一 NEWVAL 事件(一次完整 PWM 周期结束),且 ACK 之后才会发生下一次更新。若两次读 GPR1 的 ECNT7:1 相同,说明这中间没有新增边沿、也就没有触发新一轮捕获,读到的 v0/v1a 是同一周期的一致数据;若不同,说明读取窗口内来了新边沿,丢弃重读。
- 若需更严谨,可对 GPR0 也做同样的"读两次比 ECNT"操作。注意不同 TIM 模式寄存器更新策略不同,校验方式要按各模式动作表调整。
6. 应用场景
- ICU 输入捕获:发动机转速(曲轴/凸轮轴传感器)、轮速等 PWM/占空比型信号测量;
- PWM 占空比监测:配合 TDU 做 0%/100% 占空比异常检测(见之前 TDU 文档);
- 频率测量:由周期 GPR1 反推频率。
7. 关键寄存器配置
CTRL.TIM_MODE = 0b000 // TPWM
CTRL.DSL = 1 // 高电平脉宽测量(或 0=低电平)
CTRL.CLK_SEL = 选择 CMU 分辨率
CTRL.GPR0_SEL = 0b11, EGPR0_SEL=0 // GPR0 = CNTS(脉宽)
CTRL.GPR1_SEL = 0b11, EGPR1_SEL=0 // GPR1 = CNT(周期)
CTRL.OSM = 0 // 连续模式(或 1 单次)
ECTRL.IMM_START = 0 // 等首边沿(或 1 立即)
CTRL.TIM_EN = 1 // 使能
8. 信号路径框图
DSL=1(高电平脉宽测量)示例
┌──────────────┐
TIM_IN[x] ─►INPUTSRC─►FLT─►F_OUT──►│ 边沿检测 │
│ (REDGE/FEDGE) │
└──────┬───────┘
CMU_CLK_RES[CLK_SEL]──► CNT (递增计数)
│ 上升沿: CNT=0 开始
│ 下降沿: CNT值→CNTS(脉宽)
│ 下一上升沿: CNTS→GPR0, CNT→GPR1
▼
┌──────────────┐
│ GPR0=脉宽 │──► CPU(AEI) / ARU
│ GPR1=周期 │──► NEWVAL_IRQ
└──────────────┘
时序示意(DSL=1):
┌────────┐ ┌────────┐
──────┘ └──────────┘ └─────
↑ ↑ ↑
│ └下降沿: CNT→CNTS(脉宽)
└上升沿: CNT=0 开始计数
↑上升沿: CNTS→GPR0, CNT→GPR1, CNT=0, NEWVAL