day05- 定时器中断、PWM输出
1、stm32F4有14个定时器,基本定时器TIM6-TIM7,通用定时器TIM2-TIM5,TIM9-TIM14、高级定时器TIM2-TIM8。通用定时器通常用来检测输入波形的脉冲宽度、产生
输出波形。
2、定时器配置步骤:
1、TIM3定时器时钟使能:
2、初始化定时器参数,
3、设置定时器TIM3更新中断:
4、设置TIM3中断的中断优先级:
5、使能TIM3
6、编写中断服务函数:
3、定时器溢出时间:Tout=((arr+1)*(psc+1))/Ft us. arr:自动重装载值;psc:时钟预分频数;
4、通用定时器TIM4初始化:
// arr:重装载计数值 // psr: 预分频系数 // TIM4初始化 void TIM_Init(u16 arr, u16 psr) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; NVIC_InitTypeDef NVIC_InitStruct; // 定时器TIM4时钟初始化 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); // 定时器TIM4参数初始化 TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStruct.TIM_Period = arr; // 设置重装载计数值 TIM_TimeBaseInitStruct.TIM_Prescaler = psr; //TIM_TimeBaseInitStruct.TIM_RepetitionCounter = // 在高级定时器中有使用到,通用定时器用不到 TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStruct); // 使能TIM4定时器的更新中断 TIM_ITConfig(TIM4,TIM_IT_Update, ENABLE); // 中断优先级配置 NVIC_InitStruct.NVIC_IRQChannel = TIM4_IRQn; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStruct); // 使能定时器 TIM_Cmd(TIM4,ENABLE); // 编写中断服务函数 }
5、通用定时器TIM4中断服务函数:
// TIM4中断服务函数 void TIM4_IRQHandler() { if(TIM_GetITStatus(TIM4, TIM_IT_Update) == SET) { LED1 = !LED1; } // 清除中断标志位 TIM_ClearITPendingBit(TIM4, TIM_IT_Update); }
其他通用定时器,也是类似配置;
6、使用定时器产生PWM波形:
PWM产生原理如下图中所示,通过比较计数值CNT与比较值CCRx进行比较,输出逻辑电平的高低;因此可以看出来,PWM波形的占空比,在时钟以及ARR确定情况

下,取决于CCRx的值;而逻辑值的高低查看下面这幅图,

在寄存器OC1M[2:0]中写入,
110:PWM 模式 1——在递增计数模式下,只要 TIMx_CNT<timx_ccr1,通道 1="" 便为有效="" 状态,否则为无效状态。在递减计数模式下,只要="" timx_cnt="">TIMx_CCR1,通道 1 便为无 效状态(OC1REF=“0”),否则为有效状态(OC1REF=“1”)。
111:PWM 模式 2——在递增计数模式下,只要 TIMx_CNT<timx_ccr1,通道 1="" 便为无效="" 状态,否则为有效状态。在递减计数模式下,只要="" timx_cnt="">TIMx_CCR1,通道 1 便为有 效状态,否则为无效状态。
通过这样的操作后,不管是向上计数还是向下计数,都保证有效位的电平状态;但是有效位的电平状态又取决于CC1P寄存器,
在寄存器CC1P中,写入0:表示OC1高电平有效;写入1:表示OC1低电平有效;
最后,在上图中,需要使能下输出,在寄存器CC1E中;
7、PWM的配置步骤:
1、开启TIM14和GPIO的时钟;
2、配置对应位的GPIO为复用模式;
3、初始化TIM14的参数
4、设置TIM14_CH1的PWM模式,使能TIM14输出
5、使能TIM14
6、修改TIM14_CCR1来控制器占空比
8、程序设计:
TIM14输出PWM初始化
1 // arr: 重装载值 psc:预分频系数 2 void TIM14_PWM_Out(u16 arr, u16 psc) 3 { 4 GPIO_InitTypeDef GPIO_InitStruct; 5 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; 6 TIM_OCInitTypeDef TIM_OCInitStruct; 7 // 使能定时器时钟,GPIO时钟 8 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE); 9 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); 10 11 // 配置对应的GPIO为定时器复用输出 12 GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); 13 14 // GPIO 配置GPIO端口 15 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; 16 GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; 17 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; 18 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; 19 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; 20 GPIO_Init(GPIOF,&GPIO_InitStruct); 21 22 // TIM14定时器初始化 23 TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; 24 TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; 25 TIM_TimeBaseInitStruct.TIM_Period = arr; 26 TIM_TimeBaseInitStruct.TIM_Prescaler = psc; 27 TIM_TimeBaseInit(TIM14,&TIM_TimeBaseInitStruct); 28 29 // TIM14 设置定时器PWM模式输出 30 //TIM_OCInitStruct.TIM_OCIdleState = 31 TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; 32 TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_Low; 33 TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; 34 35 //TIM_OCInitStruct.TIM_OCNIdleState = 36 //TIM_OCInitStruct.TIM_OCNPolarity = 37 //TIM_OCInitStruct.TIM_OutputNState = 38 //TIM_OCInitStruct.TIM_Pulse = 39 TIM_OC1Init(TIM14, &TIM_OCInitStruct); 40 41 // 使能定时器预加载 42 TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); 43 44 // 45 TIM_ARRPreloadConfig(TIM14,ENABLE); 46 47 //使能TIM14 48 TIM_Cmd(TIM14, ENABLE); 49 50 } 51
主函数中调整占空比变化:
int main(void) { u16 led0pwmval=0; u8 dir =1; delay_init(168); TIM14_PWM_Out(500-1, 48-1); while(1){ delay_ms(10); if(dir ==1) led0pwmval++; else led0pwmval--; if(led0pwmval>300)dir=0; if(led0pwmval == 0) dir=1; TIM_SetCompare1(TIM14,led0pwmval); } }
9、可以使用其他定时器在练习
10、定时器还可以用于输入捕获,捕获电平的脉冲宽度:
脉冲宽度捕获原理如下图所示:

在t1时刻发生一次捕获事件,清除计数器的值,将改变捕获类型为下降沿捕获,并获取当前计数器的计数值;根据此时计数器中的值,以及定时器的更新中断次数,便可以计算出t2-t1的值;
11、输入捕获配置步骤:
1、开启定时器TIM5、GPIO时钟
2、设置GPIO管脚为TIM5通道1复用输出
3、初始化定时器Tim5的自动装载值arr、预分频值pcs;
4、设置定时器TIM5的输入捕获参数、并开启捕获输入
5、使能捕获中断和更新中断;
6、设置中断优先级、以及编写中断函数;
7、使能定时器
定时器初始化代码:
1 u8 TIM5CH1_CAPTURE_STA=0; 2 u32 TIM5CH1_CAPTURE_VAL; 3 // TIM5CH1_CAPTURE_STA[7]:0捕获未结束,1:捕获结束标志位 4 // TIM5CH1_CAPTURE_STA[6]:0还没有捕获到第一次电平变化,1:捕获到了第一次电平变化 5 void TIM_Input_Cap_Init(u32 arr, u16 psc) 6 { 7 GPIO_InitTypeDef GPIO_InitStruct; 8 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; 9 TIM_ICInitTypeDef TIM_ICInitStruct; 10 NVIC_InitTypeDef NVIC_InitStruct; 11 12 // 1、定时器时钟 GPIO时钟初始化 13 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); 14 RCC_APB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); 15 16 // 端口设置为复用模式 17 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; 18 GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; 19 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; 20 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; 21 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; 22 GPIO_Init(GPIOA,&GPIO_InitStruct); 23 24 // 2、 GPIO端口设置定时器通道复用 25 GPIO_PinAFConfig(GPIOA, GPIO_PinSource0,GPIO_AF_TIM5); 26 27 // 3、定时器重装载值、预分频值初始化 28 TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; 29 TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; 30 TIM_TimeBaseInitStruct.TIM_Period = arr; 31 TIM_TimeBaseInitStruct.TIM_Prescaler = psc; 32 TIM_TimeBaseInit(TIM5, &TIM_TimeBaseInitStruct); 33 34 // 4、输入捕获参数初始化 35 TIM_ICInitStruct.TIM_Channel = TIM_Channel_1; 36 TIM_ICInitStruct.TIM_ICFilter = 0x00; // 37 TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; 38 TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; 39 TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; 40 TIM_ICInit(TIM5, &TIM_ICInitStruct); 41 42 // 5、使能定时器更新中断和捕获中断 43 TIM_ITConfig(TIM5, TIM_IT_Update| TIM_IT_CC1, ENABLE); 44 // 6、使能定时器 45 TIM_Cmd(TIM5, ENABLE); 46 47 // 7、中断优先级配置 48 NVIC_InitStruct.NVIC_IRQChannel = TIM5_IRQn; 49 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; 50 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; 51 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; 52 NVIC_Init(&NVIC_InitStruct); 53 54 }
定时器中断函数:
void TIM5_IRQHandler(void) { if((TIM5CH1_CAPTURE_STA & 0x80) == 0) // 第一次脉冲宽度没有测试完成 { // 判断是否更新中断 if(TIM_GetITStatus(TIM5,TIM_IT_Update) != RESET) // 定时器溢出判断 { if(TIM5CH1_CAPTURE_STA & 0x40) // 捕获下一次下降沿变化 { if((TIM5CH1_CAPTURE_STA & 0x3F) < 0x3F) { TIM5CH1_CAPTURE_STA++; // 定时器溢出次数统计 }else{ // 定时器溢出次数太多 TIM5CH1_CAPTURE_STA |= 0x80; // TIM5CH1_CAPTURE_VAL = 0xFFFFFFFF; } } } // 捕获到第一次脉冲变化 if(TIM_GetITStatus(TIM5,TIM_IT_CC1) != RESET) // 捕获事件发生 { if(TIM5CH1_CAPTURE_STA & 0x40) // 进行下降沿脉冲捕获 { TIM5CH1_CAPTURE_STA |= 0x80; TIM5CH1_CAPTURE_VAL = TIM_GetCapture1(TIM5); // 获取定时器中此时的计数值 TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); // 进行上升沿捕获 }else { // 未下一次捕获准备 TIM5CH1_CAPTURE_STA = 0; TIM5CH1_CAPTURE_VAL = 0; TIM5CH1_CAPTURE_STA |= 0x40; TIM_Cmd(TIM5, DISABLE); TIM_SetCounter(TIM5,0); TIM_OC1PolarityConfig(TIM5, TIM_ICPolarity_Falling); // 改变捕获极性 TIM_Cmd(TIM5,ENABLE); } } } // 清除中断标志位 TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); }
主函数中:
extern u8 TIM5CH1_CAPTURE_STA; // ²¶»ñ״̬ extern u32 TIM5CH1_CAPTURE_VAL; int main(void) { long long tmp =0; // 设置系统中断优先级分组 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); delay_init(168); uart_init(115200); TIM14_PWM_Out(500-1,84-1); TIM_Input_Cap_Init(0XFFFFFFFF,84-1); // 设置定时器计数器溢出值,分频系数 while(1) { delay_ms(10); //printf("Hello World \r\n"); TIM_SetCompare1(TIM14,TIM_GetCapture1(TIM14)+1); if(TIM_GetCapture1(TIM14)==300) TIM_SetCompare1(TIM14,0); if(TIM5CH1_CAPTURE_STA & 0x80) { tmp = TIM5CH1_CAPTURE_STA&0X3F; tmp *= 0XFFFFFFFF; // 定时器溢出的计数值 tmp += TIM5CH1_CAPTURE_VAL; // 定时器溢出值+第二次捕获时的计数值,得到总的计数值 printf("HIGH:%lld us\r\n",tmp); // 计数器1us增加一次计数 TIM5CH1_CAPTURE_STA=0; } } }

浙公网安备 33010602011771号