STM32 定时器(一 基本定时器)

在这里插入图片描述

STM32 定时器(一 基本定时器)

大家好,我是杰哥
定时器东西是真的多,也不知道会出多少期,先从最简单的基本定时器开始写起,由浅入深。
欢迎大家关注我的gitee仓库:
gitee源码仓库链接跳转

TIM简介

顾名思义,定时器就是用来定时的机器
stm32F1中有高级定时器2个 通用定时器4个 基本定时器2个
每个定时器有 8 个外部 IO。

定时器位数计数器类型DMA捕获/比较通道互补输出补充
高级定时器TIM1、816位向上、向下4主要用于电机控制
通用定时器TIM2、3、4、516位向上、向下4没有捕获,比较,计时,PWM等
基本定时器TIM6、716位向上0没有主要用于驱动DAC

高级定时器、通用定时器、基本定时器区别

先说说三者区别:

通用定时器:

通用,16位自动重装载计数器、满足大部分需求

基本定时器:

是一个 16 位的只能向上计数的定时器,不能重载,只能定时,没有外部IO
是一个累加计数器,没有16位自动重装载计数器,可以直接驱动DAC

高级定时器:

用于电机控制较多,有死区、刹车、霍尔传感器等,骚操作一堆

基本定时器

简介

手册中的简介:
基本定时器TIM6和TIM7各包含一个16位自动装载计数器,由各自的可编程预分频器驱动。
它们可以作为通用定时器提供时间基准,特别地可以为数模转换器(DAC)提供时钟。实际上,它们在芯片内部直接连接到DAC并通过触发输出直接驱动DAC。
这2个定时器是互相独立的,不共享任何资源。

手册中的主要功能:
16位自动重装载累加计数器
16位可编程(可实时修改)预分频器,用于对输入的时钟按系数为1~65536之间的任意数值分频
触发DAC的同步电路
在更新事件(计数器溢出)时产生中断/DMA请求

看框图

在这里插入图片描述

白话文从头开始讲:

主要看1 2 3 4…几个点的说法,然后在每个点下面可能会有官方的定义或解释进行补充:

  1. 计数器的时钟TIMxCLK来由系统时钟RCC的内部时钟(CK_INT)提供

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

  1. 经由控制器输出ck_psc定时器预分频前的时钟

控制器可以预设启动复位、使能、计数等计数器模式功能

  1. 到PSC预分频器通过写入的16bit的PSC寄存器进行分频,得到并输出计数器时钟频率CK_CNT到CNT计数器

PSC 是一个16 位的预分频器,可以对定时器时钟 TIMxCLK 进行 1~65536 之间的任何一个数进行分频,算方式为:CK_CNT=TIMxCLK/(PSC+1)

  1. 为什么要这么搞PSC和RCC中APB1中的的预分频系数,因为能改变定时器中的时钟频率,直接决定了定时器能定时的一个时间精度。
  2. 计数器CNT是哥16位的计数器,只能往上计数,所以最大计数值为65535。从这能看出因为计数器有最大值,所以前面的预分频也算间接改变了能定时的最大时间上限,有失就有得。
  3. 自动重装载寄存器ARR也是一个16位的寄存器,这里面装着计数器能计数的最大数值,计数达到重装载值以后就会产生计数器溢出事件,并清零从头开始计数。

要让事件变成中断,记得使能中断喔。

  1. 计数器溢出事件也可以通过软件手动设置TIMx_EGR寄存器的UG位,主动去触发事件,如果使能了中断的话,其实就是主动触发中断。

定时时间计算

定时器的定时时间等于计数器的中断周期乘以中断的次数。
经过预分频PSC后得到计数时钟CK_CNT:
C k C n t = T I M x C L K ( P S C + 1 ) CkCnt = {TIMxCLK \over (PSC+1)} CkCnt=(PSC+1)TIMxCLK
每计数一次需要的时间为CK_CNT的倒数:
1 C k C n t {1 \over CkCnt} CkCnt1
所以产生一次中断的时间:
1 C k C n t ∗ A R R = ( P S C + 1 ) T I M x C L K ∗ A R R {1 \over CkCnt *ARR} = {(PSC+1) \over TIMxCLK}*ARR CkCntARR1=TIMxCLK(PSC+1)ARR
如果在中断服务函数里面设置一个变量count,用来记录中断的次数,那么就可以计算出我们需要的定时时间等于:
( P S C + 1 ) T I M x C L K ∗ A R R ∗ c o u n t {(PSC+1) \over TIMxCLK}*ARR*count TIMxCLK(PSC+1)ARRcount

开发

Hal库定时器初始化结构体

typedef struct
{
  uint32_t Prescaler;          //预分频系数 PSC
  uint32_t CounterMode;       //计数模式,基本定时器只能向上递增计数
  uint32_t Period;            //定时器周期 ARR
  uint32_t ClockDivision;     //外部输入时钟分频,基本定时器用不到
  uint32_t RepetitionCounter;  //指定重复计数器值,基本定时器用不到
  uint32_t AutoReloadPreload;  //重复计数,也用不到
} TIM_Base_InitTypeDef;

对于基本定时器只需设置其中TIM_Prescaler、TIM_Period就可以。

  1. Prescaler:定时器预分频器设置,时钟源经该预分频器才是定时器时钟,它设定TIMx_PSC 寄存器的值。可设置范围为 0 至 65535,实现 1 至 65536 分频。
  2. Period:定时器周期,实际就是设定自动重载寄存器的值,在事件生成时更新到影子寄存器。可设置范围为 0 至 65535。

寄存器表

有点多 要不算了 不看了。。。看寄存器图就行了。
在这里插入图片描述

前面框图的白话文那已经介绍了几个关键寄存器,
但是没详细讲解寄存器的不同使用、功效、作用。
所以剩下的想深入学习直接去看源码,注释写的非常健全,
(虽然hal已经很健全了但还是有点小bug 真烦!)
既能学习hal库的状态机机制又能锻炼位操作还能锻炼英语。
完美!!!

CubeMx配置

在这里插入图片描述

系统主频是72Mhz
这个时候我们的分频系数配置为7199(即7200),也就相当于告诉CPU:
将原来7200个频率脉冲看成一个频率脉冲。
这样的话,分频后定时器6的时钟频率为 72000000/7200=10000hz,
也就是它每1/10000秒会来一个脉冲。
然后我们将定时器周期设置为9999(即10000),
定时器6就会检测到每过10000个脉冲的话就会触发定时器中断。
结合上面定时器每1/10000秒会来一个脉冲就可以得到:
每过10000*(1/10000)=1s就会触发一次定时器中断。
以1s为例:1s = (7199+1)/72000000*(9999+1)
记得把中断也打开喔。

实例

下面所有的代码都来自于hal库  
并且被我删减掉非核心部分进行讲解   
Hal库定时器句柄结构体
typedef struct
{
  TIM_TypeDef                        *Instance;         /*!< Register base address                             */
  TIM_Base_InitTypeDef               Init;              /*!< TIM Time Base required parameters                 */
  HAL_TIM_ActiveChannel              Channel;           /*!< Active channel                                    */
  DMA_HandleTypeDef                  *hdma[7];          /*!< DMA Handlers array
                                                             This array is accessed by a @ref DMA_Handle_index */
  HAL_LockTypeDef                    Lock;              /*!< Locking object                                    */
  __IO HAL_TIM_StateTypeDef          State;             /*!< TIM operation state                               */
  __IO HAL_TIM_ChannelStateTypeDef   ChannelState[4];   /*!< TIM channel operation state                       */
  __IO HAL_TIM_ChannelStateTypeDef   ChannelNState[4];  /*!< TIM complementary channel operation state         */
  __IO HAL_TIM_DMABurstStateTypeDef  DMABurstState;     /*!< DMA burst operation state                         */
} TIM_HandleTypeDef;

HAL库基本用句柄htim6去做上层的操作封装,
可以看到句柄结构体中还有各种Lock和State用于状态机
库函数比较简单了,直接对TIM6寄存器地址去操作
TIM_TimeBaseInit(TIM6,&TIM_TimeBaseInitStruct);

初始化
void MX_TIM6_Init(void)
{
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  htim6.Instance = TIM6;
  htim6.Init.Prescaler = 7199;
  htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim6.Init.Period = 9999;
  htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
  {
    Error_Handler();
  }
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
  if(tim_baseHandle->Instance==TIM6)
  {//只有一个中断,优先级默认就行了
    /* TIM6 clock enable */
    __HAL_RCC_TIM6_CLK_ENABLE();
    /* TIM6 interrupt Init */
    HAL_NVIC_SetPriority(TIM6_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM6_IRQn);
  }
}
中断服务函数
/**
  * @brief  Period elapsed callback in non-blocking mode
  * @param  htim TIM handle
  * @retval None
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim == &htim6)
    {
        //前面已经说了我们1秒中断一次,所以可以做一个翻转点灯的示例程序
    }
}
主程序

放在所有外设初始化结束和主循环之间

  //开启定时器终端,如果未初始化成功,报错。
  if(HAL_TIM_Base_Start_IT(&htim6) != HAL_OK){
	  Error_Handler();
  }
编程要点
  1. 开定时器时钟
  2. 初始化时基初始化结构体;(只配置TIM_Prescaler、TIM_Period)
  3. 使能 TIMx中断;
  4. 打开定时器;
  5. 编写中断服务程序

总结

目前我了解到的STM32定时器能做下面这些功能
(捕获/比较
编码器
3路霍尔传感器 好像是
红外编码
红外解码
用PWM实现DAC
输入捕获PWM
高级定时器输出指定个数pwm
输出比较
互补输出带死区
OnePulse)
我希望能把这个系列出完吧,
如果出的完 顺带讲讲几个用定时器搞的电机控制算法 这是理想状态
这是第一期 从最简单的基本定时器开始讲
你们的点赞是对我肯定!!
(捕获/比较
编码器
3路霍尔传感器 好像是
红外编码
红外解码
用PWM实现DAC
输入捕获PWM
高级定时器输出指定个数pwm
输出比较
互补输出带死区
OnePulse)
我希望能把这个系列出完吧,
如果出的完 顺带讲讲几个用定时器搞的电机控制算法 这是理想状态
这是第一期 从最简单的基本定时器开始讲
你们的点赞是对我肯定!!
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值