蓝桥杯嵌入式开发指南 08-PWM 配置(定时器)

实例

通过配置 PWM,在对应 I/O 口产生频率和占空比不同的方波。

电路分析

开发板可供用户使用的引脚有 PA1-PA7,查阅数据手册:

STMicroelectronicsSTM32F103 中文数据手册

可以看到我们可以使用的定时器有:TIM1、TIM2、TIM3 和 TIM5,根据往届赛题分析,蓝桥杯嵌入式开发主要考核的是定时器 TIM1、TIM2、TIM3,其中 TIM1 为高级定时器,仅出现过 1 次,用于产生互补输出的 PWM 波形,所以蓝桥杯主要考定时器 TIM2 和 TIM3,TIM1 的基本配置方法已经在前面的文章中有讲述,本节以 TIM2 和 TIM3 作为主要讲解对象。

工程建立

本次工程在上一次的工程上继续修改,复制上一次的工程作为本次的工程基础。

一般输出

初始化定时器

TIM2

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
void Timer2_PWM_Init(u16 pwm_freq,u8 ch1_duty,u8 ch2_duty)
{
u16 arr=0;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);

arr=100000-pwm_freq;
TIM_TimeBaseInitStructure.TIM_Period=arr-1;
TIM_TimeBaseInitStructure.TIM_Prescaler=72-1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);

TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse=(arr-1)*ch1_duty/100;
TIM_OC2Init(TIM2,&TIM_OCInitStructure);

TIM_CtrlPWMOutputs(TIM2,ENABLE);
TIM_Cmd(TIM2,ENABLE);
}

特别提醒


本次的工程是基于前面的工程进行修改,在调用了串口 2 以后,占用了 PA2 和 PA3 ,则无法使用定时器 2 通过 PA2 和 PA3 来产生 PWM 信号。

TIM3

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
void Timer3_PWM_Init(u16 pwm_freq,u8 tim3_ch1_duty,u8 tim3_ch2_duty)
{
u16 arr=0;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);

arr=1000000/pwm_freq;
TIM_TimeBaseInitStructure.TIM_Period=arr-1;
TIM_TimeBaseInitStructure.TIM_Prescaler=72-1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);

TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse=(arr-1)*tim3_ch1_duty/100;
TIM_OC1Init(TIM3,&TIM_OCInitStructure);

TIM_OCInitStructure.TIM_Pulse=(arr-1)*tim3_ch2_duty/100;
TIM_OC2Init(TIM3,&TIM_OCInitStructure);

TIM_CtrlPWMOutputs(TIM3,ENABLE);
TIM_Cmd(TIM3,ENABLE);
}

波形输出

实例,给程序添加如下功能:

  • 通过 PA1 输出频率为 1KHz,占空比为 80% 的 PWM 信号;
  • 通过 PA6 输出频率为 1KHz,占空比为 60% 的 PWM 信号;
  • 通过 PA7 输出频率为 1KHz,占空比为 40% 的 PWM 信号;

修改主程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main(void)
{
......
//初始化定时器2的PWM输出
Timer2_PWM_Init(1000,80);

//初始化定时器3的PWM输出
Timer3_PWM_Init(1000,60,40);
......
while(1)
{
......
}
}

仿真观察

修改 Debug 为软件仿真模式,通过逻辑分析仪观察 PA1、PA6 和 PA7 的输出波形:

下载调试

将程序下载到开发板,通过示波器观察输出的 PWM 波形。
PA1:

PA6:

PA7:

比较输出

下载程序观察后不难发现,按照上面的方法配置定时器,在同一定时器的模式中,不同通道的输出频率都是初始化时设定的频率值,即占空比可调,频率不可调,如果要使用一个定时器产生频率和占空比均不相同的 PWM 波形,则需要使用定时器的比较输出模式。

初始化定时器

本例以 TIM3 为例进行说明:

TIM3

定义全局变量:

1
2
3
4
u16 CH1_Val,CH2_Val;
u16 CH1_Duty,CH2_Duty;
u8 Channel_1_Flag=0;
u8 Channel_2_Flag=0;

修改 TIM3 初始化程序

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
46
47
48
void Timer3_PWM_Init(u16 TIM3_CH1_Freq,u8 TIM3_CH1_Duty,u16 TIM3_CH2_Freq,u8 TIM3_CH2_Duty)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);

TIM_TimeBaseInitStructure.TIM_Period=0xFFFF;
TIM_TimeBaseInitStructure.TIM_Prescaler=72-1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);

NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);

CH1_Val=1000000/TIM3_CH1_Freq;
CH2_Val=1000000/TIM3_CH2_Freq;
CH1_Duty=CH1_Val*TIM3_CH1_Duty/100;
CH2_Duty=CH2_Val*TIM3_CH2_Duty/100;

TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse=CH1_Val;
TIM_OC1Init(TIM3,&TIM_OCInitStructure);

TIM_OCInitStructure.TIM_Pulse=CH2_Val;
TIM_OC2Init(TIM3,&TIM_OCInitStructure);

TIM_SetCounter(TIM3,0x00);
TIM_SetCompare1(TIM3,0x00);
TIM_SetCompare2(TIM3,0x00);

TIM_Cmd(TIM3,ENABLE);
TIM_ITConfig(TIM3,TIM_IT_CC1|TIM_IT_CC2,ENABLE);
}

修改定时器 3 中断程序:

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
void TIM3_IRQHandler(void)
{
u16 Capture=0;
if(TIM_GetITStatus(TIM3,TIM_IT_CC1)==SET)
{
TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);
Capture=TIM_GetCapture1(TIM3);
if(Channel_1_Flag)
{
TIM_SetCompare1(TIM3,Capture+CH1_Duty);
}
else
{
TIM_SetCompare1(TIM3,Capture+CH1_Val-CH1_Duty);
}
Channel_1_Flag^=1;
}

if(TIM_GetITStatus(TIM3,TIM_IT_CC2)==SET)
{
TIM_ClearITPendingBit(TIM3,TIM_IT_CC2);
Capture=TIM_GetCapture2(TIM3);
if(Channel_2_Flag)
{
TIM_SetCompare2(TIM3,Capture+CH2_Duty);
}
else
{
TIM_SetCompare2(TIM3,Capture+CH2_Val-CH2_Duty);
}
Channel_2_Flag^=1;
}
}

波形输出

实例,修改程序,实现如下功能:

  • 通过 PA6 输出频率为 5KHz,占空比为 60% 的 PWM 信号;
  • 通过 PA7 输出频率为 1KHz,占空比为 40% 的 PWM 信号;

修改主程序:

1
2
3
4
5
6
7
8
9
10
11
int main(void)
{
......
//初始化定时器3的PWM输出
Timer3_PWM_Init(5000,60,1000,40);
......
while(1)
{
......
}
}

仿真观察

修改 Debug 为软件仿真模式,通过逻辑分析仪观察 PA6 和 PA7 的输出波形:

下载调试

PA6:

PA7:

输入捕获

STM32F103RBT6 的定时器不仅可以用于 PWM 输出,还可以用于 PWM 捕获(输入捕获功能),可用于捕获一个输入的方波信号并测算输入信号的占空比和频率。

初始化定时器

本例以初始化定时器 2 来进行输入捕获:

特别提醒


本次的工程是基于前面的工程进行修改,在调用了串口 2 以后,占用了 PA2 和 PA3 ,则无法使用定时器 2 通过 PA2 和 PA3 来产捕获 PWM 信号,故此处以 PA1 为例进行讲解。

TIM2

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
void Timer2_PWM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);

TIM_TimeBaseInitStructure.TIM_Period=0xFFFF;
TIM_TimeBaseInitStructure.TIM_Prescaler=72-1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);

NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);

TIM_ICInitStructure.TIM_Channel=TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter=0x0;
TIM_ICInit(TIM2,&TIM_ICInitStructure);

TIM_Cmd(TIM2,ENABLE);
TIM_ITConfig(TIM2,TIM_IT_CC2,ENABLE);
}

定时器 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
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_CC2)==SET)
{
TIM_ClearITPendingBit(TIM2,TIM_IT_CC2);
switch(TIM2_CH2_Capture_Mod)
{
case 0:
TIM2_CH2_Capture_T1=0;
TIM2_CH2_Capture_T2=0;
TIM2_CH2_Capture_Mod=1;
TIM_SetCounter(TIM2,0);
TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling);
break;
case 1:
TIM2_CH2_Capture_T1=TIM_GetCounter(TIM2);
TIM2_CH2_Capture_Mod=2;
TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);
break;
case 2:
TIM2_CH2_Capture_T2=TIM_GetCounter(TIM2);
TIM2_CH2_Capture_Mod=3;
TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);
break;
default:
break;
}
}
}

结果输出

1
2
3
4
5
6
7
8
9
10
11
void TIM2_Capture_Display(void)
{
if(TIM2_CH2_Capture_Mod==3)
{
u8 Display_String[20];
LCD_SetTextColor(Red);
sprintf((char*)Display_String," Freq:%d Duty:%d ",1000000/TIM2_CH2_Capture_T2,TIM2_CH2_Capture_T1*100/TIM2_CH2_Capture_T2);
LCD_DisplayStringLine(Line7, Display_String);
TIM2_CH2_Capture_Mod=0;
}
}