实例
通过配置 PWM,在对应 I/O 口产生频率和占空比不同的方波。
电路分析
开发板可供用户使用的引脚有 PA1-PA7,查阅数据手册:
可以看到我们可以使用的定时器有: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) { ...... Timer2_PWM_Init(1000,80); 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) { ...... 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; } }
|