stm32 TIM定时器[操作寄存器+库函数]

stm32配备了2个高级定时器TIM1和TIM8,4个通用定时器 TIM2,TIM3,TIM4和TIM5,还有两个基本定时器TIM6和TIM7。 高级定时器常用于电机控制,因为其加入了死区控制,紧急制动,定时器同步等高级特性。基本定时器可以为数模转化器提供准确的时间基准。
stm32的通用定时器由一个通过可编程预分频器驱动的16位自动装载计数器构成。通用定时器可以用于测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。
通用计时器的使用,需要先配置一个时基单元,就是设定一个基准时间,确定计数一次耗去的时间,可以设定在几个微妙到几个毫秒之间。
通用定时器的都有4个独立通道(TIMx_CH1~4),这些通道可以用来作为:
  • 输入捕获
  • 输出比较
  • PWM生成
  • 单脉冲模式输出
    时基单元核心部件是一个16位分频器,通过对定时器时钟的分频实现确定时间基准的功能。
    根据手册可以知道 基准时钟的计算公式:     
    T = (分频寄存器+1)/TIM时钟 
    需要注意的是TIM时钟的大小,以TIM2为例,虽然其挂载在APB1总线上,PCLK时钟为36Mhz,但TIM2得到的却是72Mhz。所有挂载在APB1总线上的通用定时器时钟频率都为72Mhz;
    通用寄存器的四个通道,每一个通道相当于一个中断触发源,可以设置一个计数值,当TIM计数值和此计数值相等时,触发中断。 
    本例实现以TIM2为例产生一组不同频率的时钟,使4个LED不同频率闪烁
直接操作寄存器
首先是控制寄存器(TIMx_CR1),该寄存器各位描述如下:
timx_cr1.png
需要注意的是
ARPE位 :要开启自动重装必须将此为置1;
DIR位:  0:计数器向上计数;    1:计数器向下计数。 注:当计数器配置为中央对齐模式或编码器模式时,该位为只读。
CEN位:计数器使能位
计数器中断使能寄存器:
timx_dier.png
TIE:触发中断使能位;
UIE:允许更新中断位,允许由更新事件来产生中断;
  • 更新事件包括: 计数器向上/向下溢出,计数器初始化
  • 触发时间包括:计数器启动,停止,初始化

    CC1IE~CC4IE:允许捕获/比较1~4中断
    TDE,UDE,CC1DE~CC4DE为DMA相关中断设置,这里不讨论。
    预分频寄存器(TIMx_PSC),低16位有效,该寄存器用于设置时钟进行分频,然后提供给计数器作为时钟。
    自动重装载寄存器(TIMx_ARR),低16位有效。
    状态寄存器(TIMx_SR),该寄存器用于标识当前与定时器相关的各种事件和中断是否发生。
    描述如下:
    timx_sr.png
    UIF:更新中断标记 (Update interrupt flag)  当产生更新事件时该位由硬件置’1’。它由软件清’0’。

  • 若TIMx_CR1寄存器的UDIS=0、URS=0,当TIMx_EGR寄存器的UG=1时产生更新事件(软件对计数器CNT重新初始化);

  •  若TIMx_CR1寄存器的UDIS=0、URS=0,当计数器CNT被触发事件重初始化时产生更新事件。
    CC1IF~CC4IF:捕获/比较1~4 中断标记 (Capture/Compare 1 interrupt flag)
    TIF:触发器中断标记 (Trigger interrupt flag)  
    当发生触发事件(当从模式控制器处于除门控模式外的其它模式时,在TRGI输入端检测到有效边沿,或门控模式下的任一边沿)时由硬件对该位置’1’。它由软件清’0’。
    代码如下:(system.h 和 stm32f10x_it.h 等相关代码参照 stm32 直接操作寄存器开发环境配置
    User/main.c
    #include <stm32f10x_lib.h>
    #include “system.h”
    #include “tim.h”

void Gpio_Init(void);

int main(void)
{

Rcc_Init(9);              //系统时钟设置
//Usart1_Init(72,9600);    //设置系统时钟和波特率

// 相关TIM_x,CCR_x参数定义tim.h文件

Tim_Init(TIM_2,65535,7199);  //初始化TIM2定时器,设定重装值和分频值

Tim_CCR_Set(TIM_2,CCR_1,40000); 
Tim_CCR_Set(TIM_2,CCR_2,20000);
Tim_CCR_Set(TIM_2,CCR_3,10000);
Tim_CCR_Set(TIM_2,CCR_4,5000);

Nvic_Init(0,0,TIM2_IRQChannel,0);      //设置抢占优先级为0,响应优先级为0,中断分组为0

Gpio_Init();

while(1);

}

void Gpio_Init(void)
{
RCC->APB2ENR|=1<<2; //使能PORTA时钟

GPIOA-&gt;CRL&amp;=0x0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出
GPIOA-&gt;CRL|=0x33334444; 
//GPIOA-&gt;ODR &amp;=0xFFFFFFF0;

//USART1 串口I/O设置

//GPIOA -&gt; CRH&amp;=0xFFFFF00F;   //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
//GPIOA -&gt; CRH|=0x000008B0;      

}

User/stm32f10x_it.c
#include “stm32f10x_it.h”

#include “system.h”

#define LED0 PAout(4)

#define LED1 PAout(5)

#define LED2 PAout(6)

#define LED3 PAout(7)

void TIM2_IRQHandler(void)
{
if(TIM2->SR&0x02) //捕获比较中断1触发
{
LED0 = !LED0;

    TIM2 -&gt;CCR1 = TIM2 -&gt; CNT + 40000;     //更新捕获/比较1的值

    TIM2-&gt;SR &amp;= 0xFD;  //清除捕获比较中断
}

if(TIM2-&gt;SR&amp;0x04)   //捕获比较中断2触发
{
    LED1 = !LED1;        
    TIM2 -&gt;CCR2 = TIM2 -&gt; CNT + 20000;

    TIM2-&gt;SR &amp;= 0xFB;  //清除捕获比较中断
}

if(TIM2-&gt;SR&amp;0x08)   //捕获比较中断3触发
{
    LED2 = !LED2;    

    TIM2 -&gt;CCR3 = TIM2 -&gt; CNT + 10000;
    TIM2-&gt;SR &amp;= 0xF7;  //清除捕获比较中断

}

if(TIM2-&gt;SR&amp;0x10)   //捕获比较中断4触发
{
    LED3 = !LED3;

    TIM2 -&gt;CCR4 = TIM2 -&gt; CNT + 5000;    
    TIM2-&gt;SR &amp;= 0x0F;  //清除捕获比较中断

}

TIM2-&gt;SR &amp;= ~(1&lt;&lt;0);    //清除中断

}

Library/src/tim.c
#include <stm32f10x_lib.h>     

#include “tim.h”

//通用定时器初始化
//参数说明:TIM_x 为选择定时器 TIM_1为通用寄存器1又一次类推(定义于tim.h), arr为自动重装值 ;psc 为时钟预分频数
//待完善 目前只支持TIM2
void Tim_Init(u8 TIM_x,u16 arr,u16 psc)
{
switch(TIM_x)
{
case 1 :{

        RCC-&gt;APB2ENR |=1&lt;&lt;11;

        break;
    }

    case 2 :{
        RCC-&gt;APB1ENR |=1&lt;&lt;0;

        TIM2-&gt;ARR = arr;            //设定自动重装值
        TIM2-&gt;PSC = psc;            //设定预分频值
        TIM2-&gt;DIER |= 1&lt;&lt;0;            //允许更新中断
        TIM2-&gt;DIER |= 1&lt;&lt;6;            //允许触发中断

        TIM2-&gt;CR1 |= 0x81;            //使能定时器,自动重装允许                         

        break;
    }

    case 3 :{
        RCC-&gt;APB1ENR |=1&lt;&lt;1;

        break;
    }
    case 4 :{
        RCC-&gt;APB1ENR |=1&lt;&lt;2;                

        break;
    }

    case 5 :{
        RCC-&gt;APB1ENR |=1&lt;&lt;3;        

        break;
    }
     case 6 :{

        RCC-&gt;APB1ENR |=1&lt;&lt;4;

        break;
    }    
     case 7 :{

        RCC-&gt;APB1ENR |=1&lt;&lt;5;    

        break;
    }

     case 8 :{

        RCC-&gt;APB2ENR |=1&lt;&lt;13;

        break;
    }

}

}

//捕获比较值设定函数
//参数说明:
// TIM_x 为选择定时器 TIM_1为通用寄存器1又一次类推(定义于tim.h)
// CCR_x 为选择捕获/比较寄存器(1~4)(定义于tim.h)
// val 为要设定的捕获/比较寄存器的值
// 待完善,目前只支持TIM2

void Tim_CCR_Set(u8 TIM_x,u8 CCR_x,u32 val)
{
switch(TIM_x)
{
case 1 :{
break;
}

    case 2 :{

        TIM2-&gt;DIER |= 1 &lt;&lt; CCR_x;            //开启相应允许捕获/比较中断

        switch(CCR_x){

            case 1: {
                TIM2 -&gt;CCR1 = val;         //设置捕获/比较1的值 
                break;
            }

            case 2: {
                TIM2 -&gt;CCR2 = val;         //设置捕获/比较2的值 
                break;
            }

            case 3: {
                TIM2 -&gt;CCR3 = val;         //设置捕获/比较3的值 
                break;
            }

            case 4: {
                TIM2 -&gt;CCR4 = val;         //设置捕获/比较4的值 
                break;
            }
        }

        break;
    }

    case 3 :{
        break;
    }
    case 4 :{
        break;
    }

    case 5 :{
        break;
    }
     case 6 :{
        break;
    }    
     case 7 :{
        break;
    }

     case 8 :{
        break;
    }

}

}

Library/inc/tim.h
#include <stm32f10x_lib.h>

#define TIM_1 0x01

#define TIM_2 0x02

#define TIM_3 0x03

#define TIM_4 0x04

#define TIM_5 0x05

#define TIM_6 0x06

#define TIM_7 0x07

#define TIM_8 0x08

#define CCR_1 0x01

#define CCR_2 0x02

#define CCR_3 0x03

#define CCR_4 0x04

void Tim_Init(u8 TIM_x,u16 arr,u16 psc);
void Tim_CCR_Set(u8 TIM_x,u8 CCR_x,u32 val);

库函数操作
通用定时器有4个通道,每个通道都有6种工作模式:
TIM_OCMODE定义
TIM_OCMode 描述
    TIM_OCMODE_Timging TIM输出比较时间模式,中断时管脚无变化
    TIM_OCMODE_Active TIM输出比较时间模式,中断时管脚强制为有效电平
    TIM_OCMODE_Inactive TIM输出比较时间模式,中断时管脚强制为无效电平
    TIM_OCMODE_Toggle TIM输出比较时间模式,中断时管脚状态翻转,高变低,低变高
    TIM_OCMODE_PWM1 TIM脉冲宽度调制模式1
    TIM_OCMODE_PWM2 TIM脉冲宽度调制模式2
PS:至于有效电平是高还是低,要看CCER寄存器的CCxP位设置。两种PWM模式,区别在于通道的电平极性是相反的。
main.c
#include “stm32f10x.h”

vu16 CCR1_Val = 40000;
vu16 CCR2_Val = 20000;
vu16 CCR3_Val = 10000;
vu16 CCR4_Val = 5000;

void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void TIM_Configuration(void);

int main(void)
{

  RCC_Configuration();
  GPIO_Configuration();
 NVIC_Configuration();
TIM_Configuration();
while(1);

}

void TIM_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_Prescaler = 7199;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);

//TIM_PrescalerConfig(TIM2,7199,TIM_PSCReloadMode_Immediate);

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
TIM_OC1Init(TIM2,&amp;TIM_OCInitStructure);
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
TIM_OC2Init(TIM2,&amp;TIM_OCInitStructure);
TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
TIM_OC3Init(TIM2,&amp;TIM_OCInitStructure);
 TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
TIM_OC4Init(TIM2,&amp;TIM_OCInitStructure);

TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Disable);
TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Disable);
TIM_OC3PreloadConfig(TIM2,TIM_OCPreload_Disable);
TIM_OC4PreloadConfig(TIM2,TIM_OCPreload_Disable);

TIM_ITConfig(TIM2,TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4,ENABLE);

TIM_Cmd(TIM2,ENABLE);

}

void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;

#ifdef    VECT_TAB_RAM
    NVIC_SetVectorTable(NVIC_VectTab_RAM,0x0);
#else
    NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x0);
#endif

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

}

void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA , &amp;GPIO_InitStructure); 

}

void RCC_Configuration(void)
{
/ 定义枚举类型变量 HSEStartUpStatus /
ErrorStatus HSEStartUpStatus;

  /* 复位系统时钟设置*/
  RCC_DeInit();
  /* 开启HSE*/
  RCC_HSEConfig(RCC_HSE_ON);
  /* 等待HSE起振并稳定*/
  HSEStartUpStatus = RCC_WaitForHSEStartUp();
/* 判断HSE起是否振成功,是则进入if()内部 */
  if(HSEStartUpStatus == SUCCESS)
  {
    /* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
    RCC_HCLKConfig(RCC_SYSCLK_Div1); 
    /* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
    RCC_PCLK2Config(RCC_HCLK_Div1); 
    /* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
    RCC_PCLK1Config(RCC_HCLK_Div2);
    /* 设置FLASH延时周期数为2 */
    FLASH_SetLatency(FLASH_Latency_2);
    /* 使能FLASH预取缓存 */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
    /* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
    /* 使能PLL */ 
    RCC_PLLCmd(ENABLE);
    /* 等待PLL输出稳定 */
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
    /* 选择SYSCLK时钟源为PLL */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
    /* 等待PLL成为SYSCLK时钟源 */
    while(RCC_GetSYSCLKSource() != 0x08);
  } 
  /* 打开APB2总线上的GPIOA时钟*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE);

}

stm32f10x_it.c

#include “stm32f10x_it.h”

extern vu16 CCR1_Val;
extern vu16 CCR2_Val;
extern vu16 CCR3_Val;
extern vu16 CCR4_Val;

void TIM2_IRQHandler(void)
{
vu16 capture=0;
if(TIM_GetITStatus(TIM2,TIM_IT_CC1) != RESET)
{
GPIO_WriteBit(GPIOA , GPIO_Pin_4,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_4)));
capture = TIM_GetCapture1(TIM2);
TIM_SetCompare1(TIM2,capture + CCR1_Val);
TIM_ClearITPendingBit(TIM2,TIM_IT_CC1);
}
else if(TIM_GetITStatus(TIM2,TIM_IT_CC2) != RESET)
{
GPIO_WriteBit(GPIOA , GPIO_Pin_5,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_5)));
capture = TIM_GetCapture2(TIM2);
TIM_SetCompare2(TIM2,capture + CCR2_Val);
TIM_ClearITPendingBit(TIM2,TIM_IT_CC2);
}
else if(TIM_GetITStatus(TIM2,TIM_IT_CC3) != RESET)
{
GPIO_WriteBit(GPIOA , GPIO_Pin_6,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_6)));
capture = TIM_GetCapture3(TIM2);
TIM_SetCompare3(TIM2,capture + CCR3_Val);
TIM_ClearITPendingBit(TIM2,TIM_IT_CC3);
}
else
{
GPIO_WriteBit(GPIOA , GPIO_Pin_7,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_7)));
capture = TIM_GetCapture4(TIM2);
TIM_SetCompare4(TIM2,capture + CCR4_Val);
TIM_ClearITPendingBit(TIM2,TIM_IT_CC4);
}
}