8.4 PWM输出功能


title: 定时器

8.4.1 基本介绍

PWM( Pulse Width Modulation ) 脉冲带宽调制是一种对模拟信号电平进行数字编码的方式。
$$
占空比:Duty=\frac{T_{on}}{period}\times100%
$$
$$
平均电压=峰值\times占空比
$$

8.4.2 捕获 / 比较通道

![[Pasted image 20240516200510.png]]
![[Pasted image 20240516200614.png]]

  • 捕获 / 比较通道由三个模块组成:输入捕获单元、捕获/比较寄存器、输出比较单元。
  • 每个计时器都有4个通道:
    • 每个通道都有3个模块,但每个通道只能选择一个,捕获或是比较功能。
    • 每个通道的模块是独立的,但是==共用一个时基单元,故可产生不同占空比的PWM波,但他们的周期是一样的,CCCR不同,ARR和PSC相同==

8.4.3 PWM的数据类型data type

1 OCMode 输出比较模式

2 Pulse

用于设置捕获比较值CCR

3 OCPolarity 极性 & PWM1、2

  • TIM_OCPOLARITY_HIGH:有效电平是高电平

  • TIM_OCPOLARITY_LOW

  • PWM1:

    • ==递增计数==:TIMx_CNT(计数值) < TIMx_CCR(捕获比较值)时,输出==有效电平。==
    • ==递减计数==:TIMx_CNT(计数值) < TIMx_CCR(捕获比较值)时,输出==无效电平。==
  • PWM2:

    • ==递增计数==:TIMx_CNT(计数值) < TIMx_CCR(捕获比较值)时,输出==无效电平。==
    • ==递减计数==:TIMx_CNT(计数值) < TIMx_CCR(捕获比较值)时,输出==有效电平。==

相同==增减方式==和相同==有效电平==情况下,PWM1和PWM2为==互补输出==。

4 OCFastMode

8.4.4 PWM输出的两个HAL函数

1
2
3
4
5
6
7
>> 轮询方式启动PWM
HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);

>> 设置 CRR 捕获比较值
__HAL_TIM_SET_COMPARE(__HANDLE_, ___CHANNEL__, ___COMPARE__);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, duty);

8.5 输入捕获

8.5.1 公式

![[Pasted image 20240516220912.png]]
![[Pasted image 20240516221006.png]]

==如果信号周期大于完整的计数周期(0–ARR)==
$$
Diff=COUNT*(ARR+1)-CCRx_1+CCRx_2
$$

8.5.x 信号测量

1 任务要求

2 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
>> 主程序
>> period = CapVal[2]-CapVal[0]
>> hightime = CapVal[1]-CapVal[0]
>>
uint32_t CapVal[3] = {0}; // 存放捕获值
uint32_t CapIndex = 0; // 捕获状态指示:0,没有开始捕获;n,完成n次捕获;

volatile uint32_t CapFlag = 0; // 捕获完成标志:0表示未完成;1表示完成
uint32_t period = 0; // 存放周期
uint32_t hightime = 0; // 存放高电平脉冲宽度

int main(void)
{
printf("/* Timer Capture Function */ \r\n"); // 发送提示
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 将tim3的PWM作为输入信号
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); // 启用tim2的输入捕获

while(1)
{
if( CapFlag == 1 ) {
//周期测量
if( CapVal[2] >= CapVal[0] ) { // 计数在一个周期内
period = CapVal[2]-CapVal[0];
} else {
period = 0xFFFFFFFF + 1 - CapVal[0] + CapVal[2];
}
printf("Period :%.2fms\r\n", period/100000.0); // 计算信号周期
printf("frequency:%dHz\r\n", 100000000/period); // 计算周期

//占空比测量
if( CapVal[1] >= CapVal[0] ) { // 计数在一个周期内
hightime = CapVal[1]-CapVal[0];
} else {
hightime = 0xFFFFFFFF + 1 - CapVal[0] + CapVal[1];
}
printf("hightime :%.2fms\r\n", hightime/100000.0); // 计算高电平持续时间
printf("duty :%.1f%%\r\n", hightime/period*100.0); // 计算占空比
printf("*******************************\r\n");
CapFlag = 0; // 表示一次计算完成,可以开始下一次测量

HAL_Delay(1000);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
}
}
}
  • 0xFFFFFFFF即自动重载值,避免溢出时造成计数混乱。
  • period / 100,000 是因为tim2未PSC,只有ARR=0xFFFFFFFF,但这与计数count无关,所以count的周期为0.01us=1/100,000ms,周期单位为ms.
  • 其中频率为100,000,000 / period是因为上面的周期是ms单位,那么频率是跟s有关的,所以乘1000.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
>>回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if( htim->Instance == TIM2 ) {
if( htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1 ) {
switch( CapIndex ) {
case 0: { // 存放第一次捕获的CCR值,并修改为下降沿捕获
CapVal[0] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, \
TIM_INPUTCHANNELPOLARITY_FALLING);
CapIndex = 1; // 表示捕获1次
break;
}
case 1: { // 存放第二次捕获的CCR值,并修改为上升沿捕获
CapVal[1] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, \
TIM_INPUTCHANNELPOLARITY_RISING);
CapIndex = 2; // 表示捕获2次
break; // 捕获完成
}
case 2: {
CapVal[2] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
HAL_TIM_IC_Stop_IT(htim, TIM_CHANNEL_1); //停止捕获
CapIndex = 0;
CapFlag = 1; // 捕获完成标志
break;
}
default: {
Error_Handler(); // 出错指示
break;
}
}
}
}
}
  • 其中判断中断采用的时==ACTIVE_CHANNEL_1==,表示的是判断中断产生中==正在活动的通道==。
  • 还可以在一次上升沿之后修改为双边沿,就少修改一次触发方式,见[[嵌入式/作业/8 定时器 timer#6.3 Answer|8 定时器 timer]]