【中微半导体】BAT32G139GK48FA 定时器B输入捕获测速(寄存器TBSR/TBIER/TB/TBGRA/TBGRC)
//步进电机控制输出IO宏定义变量
#define StepA PORTCbits.RC6 //宏定义输出控制 RC6变量(步进电机驱动A)
#define StepB PORTCbits.RC7 //宏定义输出控制 RC7变量(步进电机驱动B)
#define StepC PORTCbits.RC8 //宏定义输出控制 RC8变量(步进电机驱动C)
#define StepD PORTCbits.RC9 //宏定义输出控制 RC9变量(步进电机驱动D)/*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------*/
#define Motor_Step_Driver_Freq (2)
#define CM_Engine_Pole_Pairs (7)
#define CM_RPM_BASE_Value (4600)
#define CM_RPM_BASE (114) //4600 (4096 / 4600 * 128 = 114)
#define R_GQ (7)
#define Protect_Idle_Rpm ((3850L*CM_RPM_BASE)>>R_GQ) //3720
#define CM_Engine_Speed_RefMAX (( 5600UL * CM_RPM_BASE ) >> R_GQ) //参考转速最大值限制// RPM = (60 * f) / 7极对数
#define ECAP_VALUE_FREQ_LIMIT_UP (26666)//300hz 2571RPM 8M 1/8us
#define ECAP_VALUE_FREQ_LIMIT_DOWN (10428) // 700Hz时的周期数 6000rpm
/*-----------------------------------------------------------------------------*/
/*************************************************
Description: TMB_InputCapture_Init
Input :
Return :
Others : 用于发电机转速信号捕获
*************************************************/
void TMB_InputCapture_Init(void)
{/*(1)设置TMB_InputCapture的时钟*/CGC->PER1 |= CGC_PER1_TMBEN_Msk; /* enables input clock supply */TMB->TBMR &= (uint8_t)~_80_TMB_COUNT_START;TMB->TBCR = _00_TMB_CLEAR_DISABLEED | _00_TMB_COUNT_RISING | _03_TMB_fCLK_8_SELECTED; //时钟8分频,上升沿计数 64M/8 = 8MTMB->TBMR = _00_TMB_SIGNALB_SELECTED | //选择外部输入信号1/数字滤波器通过信号1。_00_TMB_INCREMENT_MODE | //递增计数_00_TMB_TBIOA_FILTER_UNUSED| // TBIO0引脚的数字滤波器功能的选择_10_TMB_fCLK_8_SELECTED | //数字滤波器功能所用时钟的选择_00_TMB_TIMER_MODE; //定时器模式 /*(2)设置TMB_InputCapture捕获配置模式*/
// TMB->TBIOR |= _80_TMB_TBGRB_REGISTER_USED | _40_TMB_TBGRB_INPUT_CAPTURE ; //TBGRB配置为输入捕捉功能 ,使用缓冲寄存器,TBIO1的上升沿TMB->TBIOR = _08_TMB_TBGRA_REGISTER_USED | _04_TMB_TBGRA_INPUT_CAPTURE; //TBGRA配置为输入捕捉功能 ,使用缓冲寄存器,TBIO0的上升沿/*(4)设置TMB_InputCapture捕获中断*/ INTC_DisableIRQ(TMB_IRQn);/* disable INTTMB interrupt */INTC_ClearPendingIRQ(TMB_IRQn);/* clear INTTMB interrupt flag */TMB->TBIER = _08_TMB_INTERRUPT_TBOVF_ENABLE | _01_TMB_INTERRUPT_TBIMFA_ENABLE;//配置中断允许寄存器 /*(5)设置TMB_InputCapture中断优先级*/ NVIC_SetPriority(TMB_IRQn,3);/*(6)配置输入源IO端口模式*/ /* Set TBIO0 pin */PORT->PM5 |= 0x01U; // P50 输入模式// /* Set TBIO1 pin */
// PORT->PM5 |= 0x02U; // P51 输入模式/*(7)开启TMB_InputCapture模块*/ TMB0_Start();
}
/*************************************************
Description: tmb0_interrupt
Input :
Return :
Others : IC2捕获中断:获取发电机转速
*************************************************/
static void tmb0_interrupt(void)
{uint32_t Temp_AA;uint32_t Temp_BB;uint8_t tbsr_temp,tbier_temp; INTC_ClearPendingIRQ(TMB_IRQn); /* clear INTTMB interrupt flag */tbsr_temp = TMB->TBSR;tbier_temp = TMB->TBIER;TMB->TBIER = 0x00U; // 关闭中断 TBIER 允许寄存器// TBSR 定时器B状态寄存器// 当定时器B计数器(TB)上溢发生时,TBSR 寄存器的 TBOVF 位置1if ((TMB->TBSR & _08_TMB_REGISTER_OVERFLOW) == _08_TMB_REGISTER_OVERFLOW) // 溢出处理 TBOVF 是 TBSR 的溢出标志{TMB->TBSR = tbsr_temp & (uint8_t)~_08_TMB_REGISTER_OVERFLOW;g_tmb_overflow_count_a += 1UL;}#if 1if ((TMB->TBSR & _01_TMB_INTERRUPT_TBIMFA_ENABLE) == _01_TMB_INTERRUPT_TBIMFA_ENABLE) // TBIMFA 是 TBIO0 (测速)引脚的输入边沿 P50 引脚{TMB->TBSR = tbsr_temp & ~_01_TMB_INTERRUPT_TBIMFA_ENABLE;g_tmb_intTaken_a++;if (g_tmb_overflow_count_a == 0UL){g_tmb_active_width_a = (TMB->TBGRA - TMB->TBGRC); // TBGRC 是 TBGRA的缓冲寄存器, }else{g_tmb_active_width_a = (TMB->TBGRA - TMB->TBGRC) + (0x10000UL * g_tmb_overflow_count_a);//TMB->TBGRC为上一下的值g_tmb_overflow_count_a = 0UL; }}s32_GeneratorcapvalCal = g_tmb_active_width_a;//捕获As32_GeneratorcapvalCaltemp = s32_GeneratorcapvalCal;if ( s32_GeneratorcapvalCal > ECAP_VALUE_FREQ_LIMIT_UP ) s32_GeneratorcapvalCal = ECAP_VALUE_FREQ_LIMIT_UP;if ( s32_GeneratorcapvalCal < ECAP_VALUE_FREQ_LIMIT_DOWN ) s32_GeneratorcapvalCal = s32_GeneratorcapvalCal_OLd;s32_GeneratorcapvalCal_OLd = s32_GeneratorcapvalCal;Temp_AA = ( ( 480000000L/CM_Engine_Pole_Pairs )) >> R_GQ; // (8MHz)*60=360 000 000 例:5000RPM * 114 >> 7 5000RPM * CM_RPM_BASE >> R_GQTemp_BB = ( Temp_AA * CM_RPM_BASE );// >> R_GQ;Temp_AA = s32_GeneratorcapvalCal ; s16_GeneratorfreVal = ( Temp_BB / Temp_AA );if(s16_GeneratorfreVal_Old > 2862)//不等于最低转速时启用{ if((s16_GeneratorfreVal - s16_GeneratorfreVal_Old > 350 ||s16_GeneratorfreVal - s16_GeneratorfreVal_Old < -350) && ADSample_Info.Bus_Vol_Filiter_AD > 2730)s16_GeneratorfreVal = s16_GeneratorfreVal_Old;}s16_GeneratorfreVal_Old = s16_GeneratorfreVal;GeneratorfreFilter.half.hWord = (((uint32_t)GeneratorfreFilter.half.hWord * 1093) + ((uint32_t)s16_GeneratorfreVal * 3003) ) >> 12;//采集的转速一阶低通滤波 if(count_flag >= SpeedSampleCount) count_flag = 0;s16_GeneratorfreVal_Array[count_flag] = GeneratorfreFilter.half.hWord;count_flag ++; #endifTMB->TBIER = tbier_temp;}
初始化 + 中断采集 + 宏定义全部串起来,做一份“从硬件到公式到变量流”的完整解析。看完你就能对TMB 定时器如何采集转速、数据怎么流转、每一步算法在做什么心里有数。
一、硬件与时序基础
- 计数时钟:
TMB->TBCR
配成fCLK/8
。你注释写明主时钟 64 MHz,因此 计数频率 = 8 MHz,计数分辨率 = 1/8 µs = 0.125 µs = 125 ns/计数。 - 捕获通道:
TMB->TBIOR
使用 TBGRA 做输入捕获,TBIO0(P50) 上升沿触发。 - 测量量:连续两次上升沿之间的计数差(含溢出补偿)= 输入信号的电角周期。
- 寄存器与位宽:捕获寄存器是 16 位(你用
0x10000UL * 溢出次数
补偿),所以需要溢出记数。
二、初始化做了什么(TMB_InputCapture_Init)
-
开时钟/停计数:
CGC->PER1 |= TMBEN
使能模块 →TMB->TBMR
清计数开始位,先停止定时器再配。 -
计数模式:
TMB->TBCR = 上升沿计数 + fCLK/8
;TMB->TBMR = 递增计数 + (未用TBIOA滤波)
。 -
捕获选择:
TMB->TBIOR = TBGRA 作为输入捕获(TBIO0 上升沿),带缓冲
(降低丢样风险)。 -
中断:
开 溢出中断(TBOVF)+ 捕获 A 中断(TBIMFA),设置 NVIC 优先级。 -
引脚:
P50
输入模式(TBIO0)。 -
启动:
TMB0_Start()
,开始计数、等待外部脉冲。
三、中断里怎么“量周期”(tmb0_interrupt)
-
关中断、拍快照、清挂起:
防重入 → 读取TBSR/TBIER
暂存 →TBIER=0
关所有 TMB 中断。 -
溢出处理:
if (TBSR & OVERFLOW) {清标志;g_tmb_overflow_count_a++;
}
- 脉冲周期很长(低速)时,16 位计数会绕回,用溢出计数补偿。
- 捕获事件(上升沿到来):
清 TBIMFA 标志;
if (g_tmb_overflow_count_a == 0)g_tmb_active_width_a = TBGRA - TBGRC;
elseg_tmb_active_width_a = (TBGRA - TBGRC) + 0x10000 * g_tmb_overflow_count_a;
g_tmb_overflow_count_a = 0;
TBGRA
=本次时间戳,TBGRC
=上次时间戳(硬件缓冲/影子寄存器)。- 差值 + 溢出补偿 = 本周期计数值(capture_counts)。
- 原始周期到工作周期(限幅/防抖):
s32_GeneratorcapvalCal = g_tmb_active_width_a;if (capval > ECAP_VALUE_FREQ_LIMIT_UP) capval = ECAP_VALUE_FREQ_LIMIT_UP; // 下限转速饱和
if (capval < ECAP_VALUE_FREQ_LIMIT_DOWN) capval = capval_old; // 过高转速丢弃
capval_old = capval;
-
你设置了有效频率窗:
ECAP_VALUE_FREQ_LIMIT_UP = 26666
→ 对应 300 Hz(≈2571 rpm)ECAP_VALUE_FREQ_LIMIT_DOWN = 10428
→ 你注释写“700 Hz=6000 rpm”。按 8 MHz,700 Hz 的周期应是 11429 计数,10428 对应 ≈ 767 Hz(≈ 6571 rpm)。这儿建议你核对一下常量或注释是否笔误。
-
逻辑意图:低速(<≈2571 rpm)直接饱和到下限;高速(>≈6000 rpm)则忽略本次更新(保持旧值)。
区间锁定可保证控制算法处于有效速度窗内运行,避免极低/极高转速对后级环节造成干扰。
四、从“周期计数”算到“转速值”
核心代码:
Temp_AA = ((480000000L / CM_Engine_Pole_Pairs)) >> R_GQ; // 480e6 = 60 * 8e6
Temp_BB = (Temp_AA * CM_RPM_BASE);
s16_GeneratorfreVal = Temp_BB / capval; // capval=本周期计数
把它化简成数学式(全部是整数定点运算):
-
已知:
- 计数时钟
f_cnt = 8 MHz
- 电频率
f_elec = f_cnt / capval
- 机械转速(RPM):RPM=60⋅felecPolePairs\text{RPM} = \dfrac{60 \cdot f_{elec}}{\text{PolePairs}}RPM=PolePairs60⋅felec
- 计数时钟
-
你的定标/量化:
R_GQ = 7
→ 右移 7,相当于除以 27=1282^7=12827=128CM_RPM_BASE = 114
(来自 4096/4600×128≈1144096 / 4600 \times 128 \approx 1144096/4600×128≈114)
-
组合得到:
s16_GeneratorfreVal=(60⋅fcntPolePairs)⋅CM_RPM_BASE/2R_GQcapval=Kcapval, \begin{aligned} s16\_GeneratorfreVal &= \frac{\left(\frac{60 \cdot f_{cnt}}{\text{PolePairs}} \right) \cdot CM\_RPM\_BASE / 2^{R\_GQ}}{capval} \\ &= \frac{K}{capval}, \end{aligned} s16_GeneratorfreVal=capval(PolePairs60⋅fcnt)⋅CM_RPM_BASE/2R_GQ=capvalK,
其中
K=⌊60⋅8,000,000PolePairs⌋≫7×CM_RPM_BASE K = \left\lfloor \frac{60 \cdot 8{,}000{,}000}{\text{PolePairs}} \right\rfloor \gg 7 \times CM\_RPM\_BASE K=⌊PolePairs60⋅8,000,000⌋≫7×CM_RPM_BASE
把你的参数代入(PolePairs=7,R_GQ=7,CM_RPM_BASE=114):
- (480 000 000/7)≫7=535 714(480\,000\,000 / 7) \gg 7 = 535\,714(480000000/7)≫7=535714
- K=535 714×114=61 071 396K = 535\,714 \times 114 = 61\,071\,396K=535714×114=61071396
所以:
s16_GeneratorfreVal≈⌊61,071,396capval⌋ \boxed{s16\_GeneratorfreVal \approx \left\lfloor \frac{61{,}071{,}396}{capval} \right\rfloor} s16_GeneratorfreVal≈⌊capval61,071,396⌋
这是定标后的转速值,不是“真 RPM”。它与真值的线性换算关系见下。
五、定标量纲与“真 RPM”换算
你把 RPM 做了定点缩放:
s16≈RPM×CM_RPM_BASE2R_GQ=RPM×114128=RPM×0.890625 s16 \approx \text{RPM} \times \frac{CM\_RPM\_BASE}{2^{R\_GQ}} = \text{RPM} \times \frac{114}{128} = \text{RPM} \times 0.890625 s16≈RPM×2R_GQCM_RPM_BASE=RPM×128114=RPM×0.890625
所以:
- 由 s16 得 RPM: RPM≈s16×128114≈s16×1.1228\ \text{RPM} \approx s16 \times \frac{128}{114} \approx s16 \times 1.1228 RPM≈s16×114128≈s16×1.1228
- 由 RPM 得 s16: s16≈RPM×0.890625\ s16 \approx \text{RPM} \times 0.890625 s16≈RPM×0.890625
例子(用上面的 K 值计算):
场景 | 周期计数 capval | s16 计算 | 换算真 RPM |
---|---|---|---|
300 Hz(≈2571 rpm) | 26666 | 61071396 / 26666 = 2290 | 2290×128/114 ≈ 2571 rpm |
约 4000 rpm | 17143 | 61071396 / 17143 = 3562 | 3562×128/114 ≈ 3999 rpm |
你宏里 10428 | 10428 | 5856 | 5856×128/114 ≈ 6575 rpm(对应≈767 Hz) |
再次提示:如果你要把“上限”严格卡在 6000 rpm(700 Hz @ 7 极对数),
ECAP_VALUE_FREQ_LIMIT_DOWN
应为 11429 左右,而不是 10428。
六、瞬态抑制(跳变过滤)与一阶低通
- 跳变抑制(只在一定速度以上才启用):
if (s16_old > 2862) { // 约 3213 rpm(=2862×128/114)if (|s16 - s16_old| > 350 且 母线电压AD > 2730) s16 = s16_old; // 认为是毛刺/异常,丢弃
}
s16_old = s16;
- 350 的阈值在真 RPM 中 ≈ 350×128/114≈393 rpm350 \times 128 / 114 \approx 393\ \text{rpm}350×128/114≈393 rpm。
- 带上母线电压判据(AD>2730)进一步减少误判。
- 一阶 IIR 低通(平滑噪声/抖动):
y = (y_prev*1093 + x*3003) >> 12; // 1093+3003 = 4096
- 归一化后:y=0.267 y−1+0.733 xy = 0.267\,y_{-1} + 0.733\,xy=0.267y−1+0.733x(较快响应、适中平滑)。
- 环形缓冲:
把滤波后的 s16 值存进s16_GeneratorfreVal_Array[10]
,10 点窗口(SpeedSampleCount=10
)可用于后续均值/统计。
七、宏参数的作用与“速度窗”
#define CM_Engine_Pole_Pairs (7) // 极对数,决定电频/机频换算
#define R_GQ (7) // 定点右移位数(Q 格式)
#define CM_RPM_BASE (114) // 定点比例子
#define Protect_Idle_Rpm ((3850*114)>>7) // ≈3428(真RPM≈3850)—保护/怠速门槛
#define CM_Engine_Speed_RefMAX ((5600*114)>>7) // ≈4988(真RPM≈5600)—参考速度上限
#define ECAP_VALUE_FREQ_LIMIT_UP (26666) // 300 Hz(≈2571 rpm)以下饱和到此
#define ECAP_VALUE_FREQ_LIMIT_DOWN (10428) // 建议核对,应为 ~11429 对应 700 Hz/6000 rpm
-
速度窗(有效采样区间) ≈ 2571–6000 rpm:
- 低于下限:直接压到 2571 rpm 等效值(通过把周期 capval 压到 26666)。
- 高于上限:忽略本次更新(保持上一次值)。
-
控制环配合:上游测速限幅 + 下游 PI/表格(
P_table/Q_table
)通常会期望速度处于这个窗内,便于稳定控制。
八、变量数据如何流动(要点对照表)
变量/寄存器 | 含义 |
---|---|
TBGRA / TBGRC | 本次/上次捕获时间戳(16 位) |
g_tmb_overflow_count_a | 期间发生的 16 位溢出次数 |
g_tmb_active_width_a | 本周期计数 = (TBGRA - TBGRC) + 0x10000×溢出 |
s32_GeneratorcapvalCal | 经过限幅/防抖后的有效周期计数 |
s16_GeneratorfreVal | 定标后的转速值(≈ RPM×114/128) |
s16_GeneratorfreVal_Old | 上一次定标转速,用于跳变抑制 |
GeneratorfreFilter.half.hWord | 一阶低通滤波输出(仍是定标转速) |
s16_GeneratorfreVal_Array[10] | 最近 10 个滤波后的定标转速样本 |
ADSample_Info.Bus_Vol_Filiter_AD | 母线电压判断门槛(>2730 才启用跳变抑制) |
九、整体流程图
flowchart TD
A[中断入口: tmb0_interrupt] --> B[读 TBSR/TBIER 快照, 关闭 TMB 中断]
B --> C{TBSR 溢出标志?}
C -- 是 --> C1[清溢出标志; g_overflow++ ] --> D
C -- 否 --> D[检查捕获标志(TBIMFA)]
D -- 发生 --> E[清捕获标志; g_intTaken_a++]
E --> F{g_overflow == 0?}
F -- 是 --> G[cap = TBGRA - TBGRC]
F -- 否 --> H[cap = (TBGRA - TBGRC) + 0x10000 * g_overflow; g_overflow=0]
G --> I[capval = cap]
H --> I[capval = cap]
I --> J{capval > ECAP_UP(26666)?}
J -- 是 --> J1[capval = 26666(≈2571rpm)]
J -- 否 --> K{capval < ECAP_DOWN?}
K -- 是 --> K1[capval = capval_old(忽略本次)]
K -- 否 --> L[capval_old = capval]
J1 --> L
K1 --> L
L --> M[s16 = floor(K / capval), K=61071396]
M --> N{s16_old > 2862(≈3213rpm)?}
N -- 是 --> O{|s16-s16_old|>350 且 母线AD>2730?}
O -- 是 --> O1[s16 = s16_old]
O -- 否 --> P[保持 s16]
N -- 否 --> P
O1 --> Q
P --> Q[ s16_old = s16 ]
Q --> R[LPF: y = (y*1093 + s16*3003)>>12 ]
R --> S[写入环形缓冲数组]
S --> T[恢复 TBIER, 退出中断]
十、校核与建议
-
上限常量核对:如需严格卡 6000 rpm,
ECAP_VALUE_FREQ_LIMIT_DOWN
应按8e6/700 ≈ 11429
设置;你现在的 10428 对应 ≈ 6575 rpm。 -
定点精度:目前先除后移再乘,已尽量避免溢出;如要更精确可用 64 位:
uint64_t K = ( (uint64_t)60 * 8000000 * CM_RPM_BASE ) >> R_GQ; K /= CM_Engine_Pole_Pairs; s16 = (uint16_t)(K / capval);
-
低速可测范围:低于 ≈2571 rpm 被强制饱和,如需更低速测量,要调大 ECAP_VALUE_FREQ_LIMIT_UP(对应更长周期)。
-
输入去抖/数字滤波:现在 TBIOA 滤波未开,若前端传感器有毛刺,可适度开启数字滤波以改善
|Δs16|>350
的毛刺触发概率。 -
单位转换:需要真 RPM 时,用
rpm ≈ s16 * 128 / 114
,并注意整数四舍五入。
也可以把这套公式做成可直接拷进工程的注释+调参建议表(比如如何把速度窗改到 1500–7000 rpm、LPF 系数如何改快/改稳等),或者把上面的 Mermaid 进一步拆成“初始化/中断/计算”三张图,便于写文档。