【STM32】定时器系列:时基单元详解

时基单元

  • 1、什么是定时器
  • 2、时基单元的基本结构
  • 2.1:脉冲的来源
  • 2.2:预分频器PSC
  • 2.3:计数器CNT
  • 2.4:update事件与预加载
  • 3、标准库编程
  • 3.1:通过定时器中断来设置延迟函数
  • 1、什么是定时器

    定时器是一种专门负责定时功能的片上外设,而F1系列的单片机最多由14个定时器(TIM1~TIM14)。而STM32F103C8T6一共只有4个定时器。


    如上图所示:STM32F103C8T6有一个高级定时器TIM1,3个通用定时器TIM2~4。而高级定时器TIM1挂载在APB2上面。

    2、时基单元的基本结构


    如图为定时器的基本结构,而时基单元是定时器结构的一部分,如下图所示。


    如图:时基单元的基本结构由预分频器PSC,计数器CNT,自动重装寄存器ARR,重复计数器RCR组成。而RCR只有高级定时器才有,通用定时器没有。

     PSC (Prescaler): 对速时钟信号 分频
     CNT (Counter) :在时钟脉冲激励下 计数
     ARR (Auto Reload Register ):用于设置定时周期
     RCR (Repetition Counter Register):用于设置定时的 的次 
     Update事件 - 当RCR溢 时产生
     Update中断 - 由Update事件引发的中断
    

    2.1:脉冲的来源


    由晶振而来,具体情况可参考时钟树的相关知识。

    由上图所示:如果是APB1产生的频率,那么频率 * 2,如果是APB2产生的频率,那么频率 * 1 STM32F103C8T6的定时器来源的频率max = 72MHz

    2.2:预分频器PSC

    预分频器由PSC由计数器,比较器和自动重装器构成。


    想要分频率的频率脉冲连接计数器,作为输入信号。当计数器从1开始计数,数值和计数周期通过比较器相较,如果计数值 > 计数周期时,比较器输出一个脉冲,计数值归0,重新开始计数。

    如上图所示:计数周期为7,输入脉冲输入了8个脉冲,比较器才输出1个脉冲,实现8分频
    如上图:预分频器为16bit,就是计数器为16bit,则它能分频的最大倍数为:2^16 = 65536 。

    2.3:计数器CNT


    例如:如果是2bit的计数器,N也为2bit,那么最大分频倍数为2^2 = 4。
    没有脉冲来时:计数器为00
    第1个脉冲来:计数器为01
    第2个脉冲来:计数器为10
    第3个脉冲来:计数器为11
    第4个脉冲来:计数器变为00,然后向外输出一个脉冲。

    2.4:update事件与预加载

    N值决定了 的周期,有时我们需要在 运行的过程中 态地调整N的值为了防止出错这个调整需要加一些保护措施(预加载 Preload)。
    新的N值首先被写 shadow中,等到下一次Update事件时传 active ,至此才能真正发挥作用。

    如图:在计数的中途改变N的值时,N的值保存在影子寄存器中,然后等待这一次的计数完成后,影子寄存器在将N的值写入shadow里面。

    3、标准库编程

    3.1:通过定时器中断来设置延迟函数

    #include "stm32f10x.h"                  // Device header
    
    
    void Time_Init(void);
    void LED_Init(void);
    void Delay_us(uint64_t us);
    void Delay_ms(uint32_t ms);
    static uint32_t count = 0;
    
    int main(void)
    {
    	Time_Init();
    	LED_Init();
    	while(1)
    	{
    		GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
    		Delay_ms(1000);
    		GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
    		Delay_us(1000000);
    	}
    }
    
    
    void LED_Init(void)
    {
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    	
    	GPIO_InitTypeDef GPIO_InitStruct;
    	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
    	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
    	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    	GPIO_Init(GPIOC, &GPIO_InitStruct);
    }
    
    /*
    	定时器的初始化
    */
    void Time_Init(void)
    {
    	//1. 使能挂载定时器TIM3的总线时钟
    	RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM3,ENABLE);//复位
    	RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM3,DISABLE);//复位
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能时钟
    	
    	//2. 使能ARR寄存器的预加载特性
    	TIM_ARRPreloadConfig(TIM3,ENABLE);//打开预加载特性
    	
    	//3. 初始化时基单元,这些都是配置影子寄存器中
    	TIM_TimeBaseInitTypeDef TIMInitStruct;
    	TIMInitStruct.TIM_Prescaler = 71;//配置预分频器PSC
    	TIMInitStruct.TIM_Period = 999;//配置自动重装寄存器ARR
    	TIMInitStruct.TIM_CounterMode = TIM_CounterMode_Up;//配置计数器为向上计数模式
    	TIM_TimeBaseInit(TIM3,&TIMInitStruct);
    	
    	//4. 收到启动Update事件,必须手动启动,因为配置好了的定时器参数在影子寄存器中
    	TIM_GenerateEvent(TIM3,TIM_EventSource_Update);
    	
    	//5. 使能Updata中断
    	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//打开Updata触发的中断源
    	
    	//6. 配置NVIC
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    	NVIC_InitTypeDef NVICInitStruct;
    	NVICInitStruct.NVIC_IRQChannel = TIM3_IRQn;
    	NVICInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    	NVICInitStruct.NVIC_IRQChannelSubPriority = 0;
    	NVICInitStruct.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_Init(&NVICInitStruct);
    	
    	//7. 使能定时器TIM3
    	TIM_Cmd(TIM3,ENABLE);
    	
    }
    
    /*
    	定时器的中断函数,由上面的定时器初始化得,PSC = 71,则分频72倍,72MHz/72 = 1MHz
    	1MHz代表1s/1000000 = 0.000001s = 1us,所以没间隔1us计数一次。
    	自动重装器为999,则代表每隔1000 * 1us = 1ms触发一个Update事件,然后产生一个中断。
    
    */
    void TIM3_IRQHandler(void)
    {
    	//判断中断标准位,是谁产生的中断源
    	if(TIM_GetFlagStatus(TIM3,TIM_FLAG_Update) == SET)//如果是Update产生的中断源
    	{
    		TIM_ClearFlag(TIM3,TIM_FLAG_Update);//清除中断标准位
    		count++;//代表每隔1ms,count加1。
    	}
    }
    
    /*
    	定义一个ms的延迟函数
    */
    void Delay_ms(uint32_t ms)
    {
    	uint64_t time = count + ms;
    	while(count < time);
    }
    
    /*
    	定义一个us的延迟函数
    */
    void Delay_us(uint64_t us)
    {
    	uint64_t time = count * 1000 + TIM_GetCounter(TIM3) + us;//TIM_GetCounter(TIM3)获取计数器计数的值
    	while(count * 1000 + TIM_GetCounter(TIM3) < time);
    }
    

    作者:浅陌pa

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【STM32】定时器系列:时基单元详解

    发表回复