一、引言
AS32系列MCU芯片集成4个高级定时器,每个定时器包含一个32位自动重载计数器,该计数器由可编程预分频器驱动,支持递增、递减、中心计数、编码器模式等计数方式。
高级定时器具有6个独立通道,可实现测量输入信号的脉冲宽度、可编程PWM输出、带死区插入的互补PWM等功能。
二、PWM简介
PWM,全称脉冲宽度调制。它是一种用数字信号来模拟模拟电压的技术。简单来说,就是快速地在"开"(高电平)和"关"(低电平)之间切换,通过改变一个周期内"开"的时间比例,来控制平均电压。
2.1 输出比较模式
定时器被配置为PWM模式时,会用到比较寄存器。
周期: 由自动重载寄存器决定。计数器从0计数到这个值,然后归零,这个过程就是一个PWM周期。
占空比: 由比较寄存器决定。它设定了电平翻转的阈值。
工作流程:
1.计数器从0开始向上计数。
2.当计数器的值 小于 比较寄存器的值时,输出高电平(例如)。
3.当计数器的值 达到或超过 比较寄存器的值时,输出翻转为低电平。
4.计数器到达自动重载值后归零,输出重新变为高电平,开始下一个周期。
通过修改比较寄存器 的值,就改变了高电平在一个周期内持续的时间,从而改变了占空比。
输出比较可用于:
控制 LED 亮度: 占空比越大,LED越亮。
驱动舵机: 舵机的角度由PWM脉冲的宽度精确控制。
控制电机速度: 通过改变平均电压来调节直流电机转速。
音频输出: 通过极高频率的PWM,经过滤波后可以生成简单的音频信号。
2. 2 输入捕获
输入捕获功能就像一个"高速抓拍机"。当外部引脚上发生一个特定事件(如上升沿)时,它立刻"抓拍"下当前计数器的值,并保存起来。通过分析两次"抓拍"的值,我们就能计算出这个事件的时间参
定时器配置为输入捕获模式时,会用到捕获寄存器。
工作流程:
1.定时器的计数器一直在自由运行。
2.当输入引脚上出现第一个上升沿 时,硬件会立即将计数器当前的值 复制到捕获寄存器中,并产生一个中断。
3.在中断服务程序里,程序读取这个捕获值(记为t1),并同时将捕获边沿设置为下降沿。
4.当引脚出现下降沿时,硬件再次将计数器的当前值捕获(记为t2)。
5.程序计算 t2 - t1,这个差值就是高电平期间计数器计数的次数,再乘以计数周期,就得到了高电平脉冲的精确宽度。
6.同理,可以再捕获下一个上升沿,计算出整个信号的周期。
输入捕获可用于:
测量脉冲宽度和频率: 例如解码红外遥控信号(NEC协议)、测量超声波测距模块返回的脉冲宽度。
解码编码器信号: 读取旋转编码器的位置和速度。
测量数字信号的占空比。
三、软件设计
本文同时启用定时器HTIM1与HTIM5的通道1与通道2。其中,两个定时器的通道1均配置为PWM输出模式,以生成PWM信号;相应的通道2则工作在输入捕获模式,并采用中断驱动方式,以精确测量PWM信号的频率与占空比。最终,测量数据将通过串口打印输出。引脚连线如下:PD4->PH9, PC9->PD5

3.1软件分析
HTIM1初始化函数: void User_TIM1_Config(uint32_t arr, uint32_t psc, uint32_t rcr);
硬件使能与准备
1.GPIOD_CLK_ENABLE(); // 使能GPIOD时钟
2.HTIM1_CLK_ENABLE(); // 使能HTIM1时钟
定时器时基配置
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
TIM_TimeBaseInitStructure.TIM_Period = arr; // 设定周期
TIM_TimeBaseInitStructure.TIM_Prescaler = psc; // 设定预分频
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = rcr; // 重复计数
输入捕获配置(通道 2 )
-
TIM_IC_InitStructure.TIM_Channel = TIM_Channel_2; // 使用通道2
-
TIM_IC_InitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿捕获
-
TIM_IC_InitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 直接输入
-
TIM_IC_InitStructure.TIM_ICFilter = 0x0; // 无滤波器
-
TIM_IC_InitStructure.TIM_ICPrescaler = 0x0; // 每个事件都捕获
中断与 PWM 输出配置
中断使能:
-
TIM_IT_Update://定时器溢出更新中断
-
TIM_IT_CC2://通道2捕获/比较中断
PWM 输出配置(通道 1 ):
-
TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1
-
TIM_OC_InitStructure.TIM_Pulse = arr/2; // 初始占空比50%
-
TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 高电平有效
注:HTIM5配置除GPIO引脚外其余配置通HTIM1,此外HTIM5和HTIM1挂在不同总线下,读者使用时需自行计算外设时钟
在输入捕获模式下,当相应的 ICx 信号检测到跳变沿后,将使用捕获/比较寄存器(TIMx_CCRx)来锁存计数器的值。简单的说就是通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存(TIMx_CCRx)里面,完成一次捕获。

从上图可以看出,t1-t2 时间就是需要测量的高电平时间,假如定时器工作在向上计数模式,测量方法是:首先设置定时器通道 x 为上升沿捕获,这样在 t1 时刻,就会捕获到当前的 CNT 值,然后立即清零 CNT,并设置通道 x 为下降沿捕获,这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值,记为 CCRx2。根据定时器的计数频率,就可以算出 t1-t2 的时间,从而得到高电平脉宽。在 t1-t2 时间内可能会出现 N 次定时器溢出,因此还需要对定时器溢出进行处理,防止因高电平时间过长发生溢出导致测量数据不准。CNT计数的次数等于:N*ARR+CCRx2,有了这个计数次数,再乘以 CNT 的计数周期,即可得到 t2-t1 的时间长度,即高电平持续时间。部分逻辑在中断函数中实现:
void TIM1_IRQ_Handler()
{
static uint32_t TIM1_counter=0;
/* Get the value of TIM_CNT*/
if(TIM1_GetComplete==0)
{
if(TIM_GetITStatus(TIM1, TIM_IT_Update)!= RESET)
{
TIM1_Update_counter++;
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
}
if(TIM_GetITStatus(TIM1, TIM_IT_CC2)!= RESET)
{
TIM1_counter++;
if(TIM1_counter==1)
{
TIM1_Update_counter=0;
TIM1_Value1=TIM_GetCounter(TIM1);
}
if(TIM1_counter==2)
{
TIM1_Value2=TIM_GetCounter(TIM1);
TIM1_counter=0;
TIM1_GetComplete=1;
}
TIM_ClearITPendingBit(TIM1, TIM_IT_CC2);
}
}
else
{
TIM_ClearITPendingBit(TIM1, TIM_IT_Update | TIM_IT_CC2);
}
}
计算并输出PWM信号周期和频率的函数
/* Calculate the input frequency and period */
TIM1_Input_Poriod=(10000*TIM1_Update_counter-TIM1_Value1+TIM1_Value2);
TIM1_Input_Poriod = TIM1_Input_Poriod/20;
Printf("TIM1 Input_Poriod: %d us\r\n", (uint32_t)TIM1_Input_Poriod);
Printf("TIM1 Frequence: %d hz\r\n", (uint32_t)(1000000/TIM1_Input_Poriod));
TIM1_Update_counter=0;
TIM1_GetComplete=0;
ClearCache();
其中第三行的20 为 HTIM1的时钟频率为20M。
四、开发板验证:
