定时器——简单学习

目录

一、定时器基本操作原理

1.定时器就是计数器

2.定时器时钟源

3.PWM的概念

二、高精度计时

1.Systick高精度计时

2.定时器高精度计时

三、使用PWM控制三色灯

四、总结


一、定时器基本操作原理

STM32 系列单片机有系统定时器(SysTick)、还有 2 个高级定时器 TIM1 和 TIM8,4 个 通用定时器 TIM2/3/4/5,2 个基本定时器 TIM6 和 TIM7。就功能方面,高级定时器>通用定 时器>基本定时器。理解了高级定时器,就能理解其他两种定时器。

1.定时器就是计数器

定时器最基础的功能是“计数”。一下框图是“高级定时器”:

看②,它是一个 16位的计数器:每进来一个时钟(CK_CNT),里面的计数值“CNT counter”就累加,可以从 0一直累加到0xFFFF。如果时钟频率是 72MHz,那么从 0累加到 0xFFFF的时间是 65535/72=910us。

对于计数器①,它的时钟来源有多种选择,如下图所示:

① 内部时钟:用作普通的定时器,比如系统定时器 ;

② ETR(External Trigger Input),外部触发的输入信号:可以统计外部触发信号 ;

③ ITR0~ITR3(Internal Trigger0~3),内部触发信号:内部触发信号来自其他定时器的“TRGO”信号,用于定时器的级联

④ TIMx_CH1/2/3/4:也可以作为外部输入信号 。

2.定时器时钟源

STM32F103C8定时器的时钟源可以是内部时钟(HSI)或外部时钟(HSE),具体取决于系统设计和配置。在大多数情况下,默认使用内部高速时钟(HSI)作为定时器的时钟源。

通用定时器有4种时钟源: ①内部时钟(CK_INT) ②外部时钟模式1:外部输入引脚(TIx),x=1,2(即只能来自于通道 1 或者通道 2) ③外部时钟模式2:外部触发输入(ETR) ④内部触发时钟:使用一个定时器作为另一定时器的预分频器

定时器时钟 TIMxCLK,即内部时钟 CK_INT,经 APB1预分频器后分频提供,如果APB1 预分频系数等于 1,则频率不变, 否则频率乘以 2,库函数中 APB1预分频的系数是2,即 PCLK1=36M,所以定时器时钟 TIMxCLK=36*2=72M。

参考此博客:STM32 第20讲 通用定时器(简介/框图/时钟源)_stm32定时器通道及引脚图-CSDN博客

3.PWM的概念

我们经常使用定时器来实现 PWM脉冲输出。PWM(Pulse Width Modulation)简称脉宽调制,使用定时器输出如下图所示的波形:

T 和 T1 都可以调整,频率 f=1/T,占空比 duty = T1/T。占空比是一个脉冲周期内, 高电平的时间与整个周期时间的比例,取值为 0%~100%。

PWM 的控制,通常使用定时器来实现。定时器里有这几个寄存器: ① CNT:计数寄存器,定时器每接收到一个时钟,CNT 值就加一 ② ARR(Auto Reload Register,自动加载寄存器):当 CNT 累加到 ARR 里的值后,CNT 就 从 0 开始,并且 GPIO 引脚的电平值反转 ③ CCR(Capture/Compare Register,捕获/比较寄存器):当 CNT 累加到 CRR 的值时, GPIO 引脚的电平值就反转 。

假设 CNT 值不断累加,这几个寄存器的值、GPIO 的电平,关系如下图所示:

二、高精度计时

1.Systick高精度计时

driver_timer.c

#include "driver_timer.h"
​
#include "stm32f1xx_hal.h"
#include "cmsis_version.h"
​
/********************************************************************** 
 * 函数名称: udelay 
 * 功能描述: us级别的延时函数(复制rt-thread的代码) 
 * 输入参数: us - 延时多少us 
 * 输出参数: 无 
 * 返 回 值: 无 
 * 修改日期 版本号 修改人 修改内容 
 * ----------------------------------------------- 
 * 2024/7/24 V1.0 大柯基 创建 
 ***********************************************************************/ 
void udelay(int us)
{
    uint32_t told = SysTick -> VAL;
    uint32_t tnow;
    
    uint32_t load = SysTick ->LOAD;
    
    /* LOAD + 1 个时钟对应1ms
     * n us对应 n*(load + 1)/1000 个时钟 
     */
    uint32_t ticks = us*(load +1)/1000;
    uint32_t cnt = 0;
    
    while(1)
    {
        tnow = SysTick -> VAL;
        if(told >= tnow)
            cnt += told - tnow;
        else
            cnt += told + told + 1 - tnow;
        /*刷新told 便于下次计算*/
        told = tnow;
        if(cnt >= ticks)
            break;
    }
    
}
 
/********************************************************************** 
 * 函数名称: mdelay 
 * 功能描述: ms级别的延时函数 
 * 输入参数: ms - 延时多少ms 
 * 输出参数: 无 
 * 返 回 值: 无 
 * 修改日期 版本号 修改人 修改内容 
 * ----------------------------------------------- 
 * 2024/7/24 V1.0 大柯基 创建  
 ***********************************************************************/ 
void mdelay(int ms)
{
    for(int i=0; i< ms; i++){
        udelay(1000);
    }
}
 
/********************************************************************** 
 * 函数名称: system_get_ns 
 * 功能描述: 获得系统时间(单位ns) 
 * 输入参数: 无 
 * 输出参数: 无 
 * 返 回 值: 系统时间(单位ns) 
 * 修改日期 版本号 修改人 修改内容 
 * ----------------------------------------------- 
 * 2024/7/24 V1.0 大柯基 创建 
 ***********************************************************************/ 
uint64_t system_get_ns(void)
{
    uint64_t ns = HAL_GetTick();
    ns = ns*1000000;
    
    uint32_t tnow = SysTick ->VAL;
    uint32_t load = SysTick ->LOAD;
    uint64_t cnt;
    cnt = load + 1 - tnow;
    
    ns += cnt * 1000000 / (load+1);
    return ns;
    
}
​

2.定时器高精度计时

driver_timer.c

#include "driver_timer.h"
​
#include "stm32f1xx_hal.h"
#include "cmsis_version.h"
​
//#define USE_SYSTICK
​
extern TIM_HandleTypeDef        htim4;
​
/********************************************************************** 
 * 函数名称: udelay 
 * 功能描述: us级别的延时函数(复制rt-thread的代码) 
 * 输入参数: us - 延时多少us 
 * 输出参数: 无 
 * 返 回 值: 无 
 * 修改日期 版本号 修改人 修改内容 
 * ----------------------------------------------- 
 * 2024/7/24 V1.0 大柯基 创建 
 ***********************************************************************/ 
void udelay(int us)
{
#ifdef USE_SYSTICK
    uint32_t told = SysTick -> VAL;
    uint32_t tnow;
    
    uint32_t load = SysTick ->LOAD;
    
    /* LOAD + 1 个时钟对应1ms
     * n us对应 n*(load + 1)/1000 个时钟 
     */
    uint32_t ticks = us*(load +1)/1000;
    uint32_t cnt = 0;
    
    while(1)
    {
        tnow = SysTick -> VAL;
        if(told >= tnow)
            cnt += told - tnow;
        else
            cnt += told + told + 1 - tnow;
        /*刷新told 便于下次计算*/
        told = tnow;
        if(cnt >= ticks)
            break;
    }
#else
    uint32_t told = __HAL_TIM_GET_COUNTER(&htim4);
    uint32_t tnow;
    uint32_t load = __HAL_TIM_GET_AUTORELOAD(&htim4);
    
    
    /* LOAD + 1 个时钟对应1ms
     * n us对应 n*(load + 1)/1000 个时钟 
     */
    uint32_t ticks = us*(load +1)/1000;
    uint32_t cnt = 0;
    
    while(1)
    {
        tnow = __HAL_TIM_GET_COUNTER(&htim4);
        if(tnow >= told)
            cnt += tnow - told;
        else
            cnt += load + 1 - told + tnow;
        /*刷新told 便于下次计算*/
        told = tnow;
        if(cnt >= ticks)
            break;
    }
#endif
}
 
/********************************************************************** 
 * 函数名称: mdelay 
 * 功能描述: ms级别的延时函数 
 * 输入参数: ms - 延时多少ms 
 * 输出参数: 无 
 * 返 回 值: 无 
 * 修改日期 版本号 修改人 修改内容 
 * ----------------------------------------------- 
 * 2024/7/24 V1.0 大柯基 创建 
 ***********************************************************************/ 
void mdelay(int ms)
{
    for(int i=0; i< ms; i++){
        udelay(1000);
    }
}
 
/********************************************************************** 
 * 函数名称: system_get_ns 
 * 功能描述: 获得系统时间(单位ns) 
 * 输入参数: 无 
 * 输出参数: 无 
 * 返 回 值: 系统时间(单位ns) 
 * 修改日期 版本号 修改人 修改内容 
 * ----------------------------------------------- 
 * 2024/7/24 V1.0 大柯基 创建  
 ***********************************************************************/ 
uint64_t system_get_ns(void)
{
#ifdef USE_SYSTICK
    uint64_t ns = HAL_GetTick();
    ns = ns*1000000;
    
    uint32_t tnow = SysTick ->VAL;
    uint32_t load = SysTick ->LOAD;
    uint64_t cnt;
    cnt = load + 1 - tnow;
    
    ns += cnt * 1000000 / (load+1);
    return ns;
#else
    uint64_t ns = HAL_GetTick();
    ns = ns*1000000;
    
    uint32_t tnow = __HAL_TIM_GET_COUNTER(&htim4);
    uint32_t load = __HAL_TIM_GET_AUTORELOAD(&htim4);
    uint64_t cnt;
    cnt = tnow;
    
    ns += cnt * 1000000 / (load+1);
    return ns;
#endif
}
​
​
​

三、使用PWM控制三色灯

driver_color_oled.c

​
#include "driver_color_led.h"
​
#include "stm32f1xx_hal.h"
#include "cmsis_version.h"
​
extern TIM_HandleTypeDef htim2;
​
#define COLOR_LED_R TIM_CHANNEL_3
#define COLOR_LED_G TIM_CHANNEL_1
#define COLOR_LED_B TIM_CHANNEL_2
​
void ColorLED_Init(void)
{
    //MX_TIM2_Init();
    HAL_TIM_PWM_Start(&htim2, COLOR_LED_R);
    HAL_TIM_PWM_Start(&htim2, COLOR_LED_G);
    HAL_TIM_PWM_Start(&htim2, COLOR_LED_B);
}
​
void ColorLED_SetColor(uint32_t color)
{
  TIM_OC_InitTypeDef sConfigOC_R = {0};
    TIM_OC_InitTypeDef sConfigOC_G = {0};
    TIM_OC_InitTypeDef sConfigOC_B = {0};
​
  sConfigOC_R.OCMode = TIM_OCMODE_PWM1;
  sConfigOC_R.Pulse = ((color >> 16) & 0xff)*1999/255;  /* 最大值1999 */
  sConfigOC_R.OCPolarity = TIM_OCPOLARITY_LOW;
  sConfigOC_R.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC_R, COLOR_LED_R) != HAL_OK)
  {
    Error_Handler();
  }
​
  sConfigOC_G.OCMode = TIM_OCMODE_PWM1;
  sConfigOC_G.Pulse = ((color >> 8) & 0xff)*1999/255;  /* 最大值1999 */
  sConfigOC_G.OCPolarity = TIM_OCPOLARITY_LOW;
  sConfigOC_G.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC_G, COLOR_LED_G) != HAL_OK)
  {
    Error_Handler();
  }
​
  sConfigOC_B.OCMode = TIM_OCMODE_PWM1;
  sConfigOC_B.Pulse = ((color >> 0) & 0xff)*1999/255;  /* 最大值1999 */
  sConfigOC_B.OCPolarity = TIM_OCPOLARITY_LOW;
  sConfigOC_B.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC_B, COLOR_LED_B) != HAL_OK)
  {
    Error_Handler();
  }
    
}
​

main.c

    ColorLED_Init();
​
    ColorLED_SetColor(0x00ff0000);
    HAL_Delay(1000);
    ColorLED_SetColor(0x00ff00);
    HAL_Delay(1000);
    ColorLED_SetColor(0x00ff);
    HAL_Delay(1000);

实验现象:全彩LED灯红绿蓝延迟1s交替显示。

四、总结

定时器在单片机开发过程中具有相对重要的作用,合理配置相关的定时寄存器,能够为我们提供精确的时间控制和计数功能。

  • 22
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值