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);
    // 编写中断服务函数
}
View Code

5、通用定时器TIM4中断服务函数:

// TIM4中断服务函数
void TIM4_IRQHandler()
{
    if(TIM_GetITStatus(TIM4, TIM_IT_Update) == SET)
    {
        LED1 = !LED1;
    }
        // 清除中断标志位  
    TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
}
View Code

  其他通用定时器,也是类似配置;

 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     
View Code

  主函数中调整占空比变化:

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); 
    }
}
View Code

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 }  
View Code

  定时器中断函数:

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);
}
View Code

主函数中:

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;
        }
    
    }
}
View Code

 

posted @ 2021-07-24 22:52  笑不出花的旦旦  阅读(995)  评论(0)    收藏  举报