TPWM — PWM 测量模式(TIM PWM Measurement Mode)

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;

关键点:

  1. 为什么先 GPR1 后 GPR0 :手册 13.4 规定 ARU_EN=0(CPU 消费)时,读 GPR0 这一下就是 ACK,硬件据此解除 GPROFL、才允许下一次测量覆盖 GPR0/GPR1。所以顺序固定:先读 GPR1、再读 GPR0 收尾。
  2. 为什么校验用 7:1 而非 7:0:ECNT0 被复用为当前输入电平 F_OUT(上升沿=奇、下降沿=偶),是电平不是序号,校验只看 bit7:1 的边沿序号。
  3. 为什么能防止"读到一半被写" :GPR0/GPR1 的更新发生在同一 NEWVAL 事件(一次完整 PWM 周期结束),且 ACK 之后才会发生下一次更新。若两次读 GPR1 的 ECNT7:1 相同,说明这中间没有新增边沿、也就没有触发新一轮捕获,读到的 v0/v1a 是同一周期的一致数据;若不同,说明读取窗口内来了新边沿,丢弃重读。
  4. 若需更严谨,可对 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
相关推荐
Zwawa17 小时前
把按键和 LED 状态显示出来:第一次看见程序到底在干什么
单片机·嵌入式
小程序设计18 小时前
基于STM32的办公室智能空气净化系统设计
stm32·单片机
不悔哥6 天前
开源OV-Watch:怎么做一个智能手表
单片机·开源·嵌入式
XiHongShi20166 天前
STM32F407 RTC定时器例程,建议保存
stm32·单片机·学习
深圳老胡6 天前
STM32F407 控制 L6470 步进电机驱动 —— 控制过程简介
笔记·stm32·单片机·嵌入式硬件·代码规范
女神下凡6 天前
嵌入式设计的各种存储芯片的硬件/软件设计规范,非常全面。
arm开发·单片机·嵌入式硬件
梅梅9666 天前
一款mipi转lvds的lcd调试(LT8912B)
单片机·计算机外设·电脑·显示器·pd芯片
开发笔记-阿牛6 天前
蓝牙音乐灯拆解,又一次拆到蓝牙音乐灯芯片CK6865L
人工智能·单片机·嵌入式硬件·音视频·音频
从零开始的嵌入式之旅6 天前
day47
arm开发·经验分享·笔记·嵌入式硬件
恒锐丰科技林技术员6 天前
EG1164 高压大电流升压同步整流芯片解析
经验分享·嵌入式硬件·硬件工程