当前位置:   article > 正文

stm32定时器(标准库函数)_stm32标准库 定时器

stm32标准库 定时器
1.TIM定时器概念

定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断

16位计数器,预分频器,自动重装寄存器的时基本单元可以实现最大的59.65s的定时

定时器不仅具备中断功能,而且还包含内外时钟源选择,输入捕获,输出比较,编码器口

定时器主要分为3大类,高级定时器(TIM1,TIM8) 通用定时器(TIM2~TIM5) 基本定时(TIM6,TIM7)

不同芯片所带的资源不同,因此具体所带资源应该查看所对应的芯片手册。

1.定时器三大类的不同作用

img

高级定时器:拥有通用定时器的全部功能,并额外拥有重复计数器,死区生成,互补输出,刹车输入等功能

img

通用定时器:拥有基本定时器的所有功能,并且额外具有内外时钟源选择,输入捕获,输出比较,编码器接口,主从触发模式等功能

img

基本定时器:定时中断,主模式触发DAC的功能

img

2.定时器中断

定时器中断:定时中断是在受到时钟源的影响下,实现CNT计数器的增长,在增长的同时与自动重装寄器(ARR)相比较,当计数器达到自动重装寄存器的值时,会使CNT计数器清0,并且发出一个中断信号,以实现定时器中断的目标。

img

1.定时器中断

在定时器中断时,我们通常需要打开定时器的内部时钟,并且为定时器选择时钟源(选择内部时钟源)。使其在内部模式的状态下工作,此时我们也需要初始化时基单元。将时基单元的参数配置成我们实现功能所需要的参数。然后就时配置配置中断实现功能了。

对于定时器时基单元的参数选择:

img

计数器溢出频率:也就是计数器计完一个过程的频率,换句话说也就是计数器达到ARR触发中断的频率,通过这样也就可以得到他的周期,也就是定时器定的时间。CK_PSC也就是定时器时钟源的时钟频率。

 //定时器初始化函数
 void TIM_Init()
 {
     // 1.第一步打开定时器的时钟,使用RCC_APB1PeriphClockCmd函数打开TIM2资源
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
     
     //2.第二步将时钟资源设为内部时钟源
     TIM_InternalClockConfig(TIM2);
     
     //3.第三步配置时基单元,设置一个结构体来对其赋值
     TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
     
     
 //  TIM_CKD_DIV1:不分频,即输入时钟不分频。这意味着定时器的时钟频率等于输入时钟的频率。例如,如果输入时钟是72MHz,那么定时器的时钟频率也是72MHz。这样可以保持最高的定时器分辨率,但也意味着定时器的计数速度较快。
 ​
 //TIM_CKD_DIV2:除以2分频。定时器的时钟频率为输入时钟的一半。这可以减慢定时器的计数速度,使得定时器的周期变长。
 ​
 //TIM_CKD_DIV4:除以4分频。定时器的时钟频率为输入时钟的四分之一。这进一步减慢了定时器的计数速度,使得定时器的周期更长。
     TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1  ;
     TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up   ;
     TIM_TimeBaseInitStructure.TIM_Period=(10000-1);
     TIM_TimeBaseInitStructure.TIM_Prescaler=(7200-1);
     TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
     TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
     
     /*中断输出配置*/
     TIM_ClearFlag(TIM2, TIM_FLAG_Update);                       //清除定时器更新标志位
                                                                 //TIM_TimeBaseInit函数末尾,手动产生了更新事件
                                                                 //若不清除此标志位,则开启中断后,会立刻进入一次中断
                                                                 //如果不介意此问题,则不清除此标志位也可
     
     TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);                  //开启TIM2的更新中断
     
     /*NVIC中断分组*/
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);             //配置NVIC为分组2
                                                                 //即抢占优先级范围:0~3,响应优先级范围:0~3
                                                                 //此分组配置在整个工程中仅需调用一次
                                                                 //若有多个中断,可以把此代码放在main函数内,while循环之前
                                                                 //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
     
     /*NVIC配置*/
     NVIC_InitTypeDef NVIC_InitStructure;                        //定义结构体变量
     NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;             //选择配置NVIC的TIM2线
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //指定NVIC线路使能
     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;   //指定NVIC线路的抢占优先级为2
     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;          //指定NVIC线路的响应优先级为1
     NVIC_Init(&NVIC_InitStructure);                             //将结构体变量交给NVIC_Init,配置NVIC外设
     
     /*TIM使能*/
     TIM_Cmd(TIM2, ENABLE);          //使能TIM2,定时器开始运行
 }
 ​
 ​
 定时器中断函数
 //打开定时器二的中断函数
 void  TIM2_IRQHandler(void)
 {
     
     //判断定时器中断的标志位是否就绪,判断是否为更新中断
     if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
     {
         num++;
         //清楚中断定时标志
         TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
         
         
     }
 }
 在这段代码中设置了ARR为(10000-1),PCS为(7200-1)这样的配置也就达到了实现1s定时发生中断的功能
2.定时器外部中断

定时器的外部中断如定时器的基本结构图可知,定时器的外部中断需要选择一个外部时钟源,同时需要搭配GPIO口来使用,实现定时器外部中断的功能。

 //定时器初始化函数
 void TIM_Init()
 {
     // 1.第一步打开定时器的时钟,使用RCC_APB1PeriphClockCmd函数打开TIM2资源,并且打开GPIOA的时钟
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
     //2.第二步将时钟资源设为内部时钟源,选着不分频外部时钟,选择上升有效
     TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0);
     
     
 /*GPIO初始化*/
     GPIO_InitTypeDef GPIO_InitStructure;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(GPIOA, &GPIO_InitStructure);                      //将PA0引脚初始化为上拉输入    
     
     //3.第三步配置时基单元,设置一个结构体来对其赋值
     TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
     
     
 //  TIM_CKD_DIV1:不分频,即输入时钟不分频。这意味着定时器的时钟频率等于输入时钟的频率。例如,如果输入时钟是72MHz,那么定时器的时钟频率也是72MHz。这样可以保持最高的定时器分辨率,但也意味着定时器的计数速度较快。
 ​
 //TIM_CKD_DIV2:除以2分频。定时器的时钟频率为输入时钟的一半。这可以减慢定时器的计数速度,使得定时器的周期变长。
 ​
 //TIM_CKD_DIV4:除以4分频。定时器的时钟频率为输入时钟的四分之一。这进一步减慢了定时器的计数速度,使得定时器的周期更长。
     TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1  ;
     TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up   ;
     TIM_TimeBaseInitStructure.TIM_Period=9;
     TIM_TimeBaseInitStructure.TIM_Prescaler=0;
     TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
     TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
     
     /*中断输出配置*/
     TIM_ClearFlag(TIM2, TIM_FLAG_Update);                       //清除定时器更新标志位
                                                                 //TIM_TimeBaseInit函数末尾,手动产生了更新事件
                                                                 //若不清除此标志位,则开启中断后,会立刻进入一次中断
                                                                 //如果不介意此问题,则不清除此标志位也可
     
     TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);                  //开启TIM2的更新中断
     
     /*NVIC中断分组*/
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);             //配置NVIC为分组2
                                                                 //即抢占优先级范围:0~3,响应优先级范围:0~3
                                                                 //此分组配置在整个工程中仅需调用一次
                                                                 //若有多个中断,可以把此代码放在main函数内,while循环之前
                                                                 //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
     
     /*NVIC配置*/
     NVIC_InitTypeDef NVIC_InitStructure;                        //定义结构体变量
     NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;             //选择配置NVIC的TIM2线
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //指定NVIC线路使能
     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;   //指定NVIC线路的抢占优先级为2
     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;          //指定NVIC线路的响应优先级为1
     NVIC_Init(&NVIC_InitStructure);                             //将结构体变量交给NVIC_Init,配置NVIC外设
     
     /*TIM使能*/
     TIM_Cmd(TIM2, ENABLE);          //使能TIM2,定时器开始运行
 }

3.定时器的输出比较功能
1.输出比较的含义

OC(Output Compare)输出比较 输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形 每个高级定时器和通用定时器都拥有4个输出比较通道 高级定时器的前3个通道额外拥有死区生成和互补输出的功能。

2.PWM波的定义

PWM波

1. 什么是PWM:PWM简称脉冲宽度调制,即在一个周期内存在不同极性的电平状态。

2. PWM频率:是指一秒钟内从高电平时间在到低电平时间,再从低电平跳到高电平的瞬间次数,也就是一秒钟内有多少个PWM的周期。f = T / 1(HZ)。

\3. PWM周期:是指一秒钟内从高电平时间在到低电平时间,T = f / 1(s)。

4. PWM占空比:是指一个周期内高电平时间和总时间的比值。

例如:PWM的周期为1ms,高电平时间为0.5ms,低电平时间为0.5ms,则频率就为1kHz,占空比就为百分之五十。

3.实现过程

img

img

img

 void Init_PWM(void)
 {
     //第一步打开定时器TIM2时钟
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
     //打开GPIO时钟
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
     
     
     
     //配置GPIO口结构体
     GPIO_InitTypeDef    GPIO_InitStructure;
     
     //设置GPIO结构体对象的各个参数,选着复用推挽输出PWM波
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
     GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0;
     GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
     
     GPIO_Init(GPIOA,&GPIO_InitStructure);
     //第二步选择时钟源
     TIM_InternalClockConfig(TIM2);
     
     //第三步配置定时器参数
     TIM_TimeBaseInitTypeDef     TIM_TimeBaseInitStructure;
         //参数选择不分屏
     TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1  ;
         //计数模式选着向上计数
     TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
     TIM_TimeBaseInitStructure.TIM_Period=100-1; //arr
     TIM_TimeBaseInitStructure.TIM_Prescaler=720-1; //psc
     TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
     
     //初始化定时器参数
     TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
     
     
     //第四步配置输出比较参数
     
     TIM_OCInitTypeDef TIM_OCInitStruct;
     TIM_OCStructInit(&TIM_OCInitStruct);
     //选着模式为PWm1模式
     TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
     //表示在PWM波1模式下小于比较值时输出高电平,高于这个时输出低电平
     TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
     TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
     TIM_OCInitStruct.TIM_Pulse=50;//ccr
     
     //初始化
     TIM_OC1Init(TIM2,&TIM_OCInitStruct);
     
     
     
 }

此时更改cnt的值就可以修改占空了

4.输入捕获
1.输入捕获的含义

IC(Input Capture)输入捕获 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数 每个高级定时器和通用定时器都拥有4个输入捕获通道 可配置为PWMI模式,同时测量频率和占空比 可配合主从触发模式,实现硬件全自动测量

2.使用方法

img

1.测频法:早闸门时间内通过对上升沿进行几次即可得到fx的频率
2.测周法

3.使用方法

img

 #include "stm32f10x.h"                  // Device header
 ​
 //本驱动程序是实现输入捕获的功能,实现输入捕获测算频率
 void IC_Init(void)
 {
     //打开GPIO RCC时钟
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
     //打开定时器三    RCC时钟
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
     //配置GPIO口结构体
     GPIO_InitTypeDef    GPIO_InitStructure;
     
     //设置GPIO结构体对象的各个参数,选着复用推挽输出PWM波
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
     GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
     GPIO_Init(GPIOA,&GPIO_InitStructure);
     
     TIM_InternalClockConfig(TIM3);      //选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
     
     
     
     //设置时机单元
     TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
     TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
     TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
     TIM_TimeBaseInitStruct.TIM_Period=65536 - 1;
     TIM_TimeBaseInitStruct.TIM_Prescaler=(72-1);
     TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct); 
     
     /*输入捕获初始化*/
     TIM_ICInitTypeDef TIM_ICInitStructure;                          //定义结构体变量
     TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;                //选择配置定时器通道1
     TIM_ICInitStructure.TIM_ICFilter = 0xF;                         //输入滤波器参数,可以过滤信号抖动
     TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;     //极性,选择为上升沿触发捕获
     TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;           //捕获预分频,选择不分频,每次信号都触发捕获
     TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //输入信号交叉,选择直通,不交叉
     TIM_ICInit(TIM3, &TIM_ICInitStructure);                         //将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道
     
     
     
     /*选择触发源及从模式*/
     TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);                    //触发源选择TI1FP1
     TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);                 //从模式选择复位
                                                                     //即TI1产生上升沿时,会触发CNT归零
     
     /*TIM使能*/
     TIM_Cmd(TIM3, ENABLE);          
     
 }
 ​
 /**
   * 函    数:获取输入捕获的频率
   * 参    数:无
   * 返 回 值:捕获得到的频率
   */
 uint32_t IC_GetFreq(void)
 {
     return 1000000 / (TIM_GetCapture1(TIM3) + 1);       //测周法得到频率fx = fc / N,这里不执行+1的操作也可
 }

img

4.编码器接口的使用

img

 #include "stm32f10x.h"                  // Device header
 ​
 //本驱动程序是实现输入捕获的功能,实现输入捕获测算频率
 void IC_Init(void)
 {
     //打开GPIO RCC时钟
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
     //打开定时器三    RCC时钟
     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
     //配置GPIO口结构体
     GPIO_InitTypeDef    GPIO_InitStructure;
     
     //设置GPIO结构体对象的各个参数,选着复用推挽输出PWM波
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
     GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
     GPIO_Init(GPIOA,&GPIO_InitStructure);
     
     
     
     
     
     //设置时机单元
     TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
     TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
     TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
     TIM_TimeBaseInitStruct.TIM_Period=65536 - 1;
     TIM_TimeBaseInitStruct.TIM_Prescaler=0;
     TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct); 
     
     /*输入捕获初始化*/
     TIM_ICInitTypeDef TIM_ICInitStructure;                          //定义结构体变量
     //初始化输入捕获结构体,因为输入捕获的结构不是每一个都有用,因此将输入捕获结构体需要使用函数初始化一下
     TIM_ICStructInit(&TIM_ICInitStructure);
     TIM_ICInitStructure.TIM_Channel=TIM_Channel_1  ;
     TIM_ICInitStructure.TIM_ICFilter=0xF;
     
     TIM_ICInit(TIM3,&TIM_ICInitStructure);
     TIM_ICInitStructure.TIM_Channel=TIM_Channel_2  ;
     TIM_ICInitStructure.TIM_ICFilter=0xF;
     TIM_ICInit(TIM3,&TIM_ICInitStructure);
     
     TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
                                                                     //配置编码器模式以及两个输入通道是否反相
                                                                     //注意此时参数的Rising和Falling已经不代表上升沿和下降沿了,而是代表是否反相
                                                                     //此函数必须在输入捕获初始化之后进行,否则输入捕获的配置会覆盖此函数的部分配置
     
     //设置编码器喊数有直接的函数调用
     /*TIM使能*/
     TIM_Cmd(TIM3, ENABLE);          
     
 }
 ​
 /**
   * 函    数:获取输入捕获的频率
   * 参    数:无
   * 返 回 值:捕获得到的频率
   */
 uint32_t IC_GetFreq(void)
 {
     return 1000000 / (TIM_GetCapture1(TIM3) + 1);       //测周法得到频率fx = fc / N,这里不执行+1的操作也可
 }
 ​
 ​
 ​
 //测量占空比的计算
 uint32_t PWM_zhankongbi(void)
 {
     
     
     return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1); //占空比Duty = CCR2 / CCR1 * 100,这里不执行+1的操作也可
 }
 ​
 int16_t En_Get(void)
 {
     /*使用Temp变量作为中继,目的是返回CNT后将其清零*/
     int16_t Temp;
     Temp = TIM_GetCounter(TIM3);
     TIM_SetCounter(TIM3, 0);
     return Temp;
 }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/912857
推荐阅读
相关标签
  

闽ICP备14008679号