单片机-STM32部分:9、定时器
飞书文档https://x509p6c8to.feishu.cn/wiki/A749wx8T0ioqfgkzZKlc9poknUf
SMT32F1系列共有8个定时器:
- 基本定时器(TIM6、TIM7)
- 通用定时器(TIM2、TIM3、TIM4、TIM5)
- 高级定时器(TIM1、TIM8)
高级定时器(TIM1,TIM8)的主要功能:
通用定时器(TIM2~TIM5)的主要功能:
基本定时器功能(TIM6、TIM7):
功能对比
定时器种类 | 位数 | 计数器模式 | 捕获/比较通道 | 互补输出 | 特殊应用场景 |
高级定时器 | 16 | 向上,向下,中心对齐 | 4 | 有 | 定时计数,DAC/ADC时钟、PWM输出,输入捕获,输出比较,带可编程死区的互补输出 |
通用定时器 | 16 | 向上,向下,中心对齐 | 4 | 无 | 定时计数,DAC/ADC时钟、PWM输出,输入捕获,输出比较 |
基本定时器 | 16 | 向上 | 0 | 无 | 定时计数,DAC时钟 |
基本定时器框图说明
基础定时器只有定时功能,使用基础定时器进行定时的工作流程如下: |
定时器配置的一些参数
虽然有3类定时器但,不管怎样,只要是配置作为定时器,那么便总是与基本定时器是类似的。
在下文配置的时候要注意:我们配置的是进入定时器中断的频率,然后要定的时间要跟据这个频率来定时的。
Prescaler(PSC): 定时器预分频设置,16位,设置 0~65535 以达到 1 至 65536 的分频。
Counter Mode : 定时器的计数方式,基本定时器只能向上(UP)计数
定时器有如下3种计数模式:
|
Counter Period : 定时器周期,16位,设置 0~65535 以达到 1 至 65536 的周期。每当定时器达到 设定值时,置位。
InternalCLock Division(CKD): 内部时钟分频因子,指设置定时器时钟频率与数字滤波器使用的采样频率之间的分频比例的(与输入捕获相关)。
Auto-reload preload :自动重载。一般每次触发以后需要重新填充。
Disable:自动重装载寄存器写入新值后,计数器立即产生计数溢出,然后开始新的计数周期
Enable:自动重装载寄存器写入新值后,计数器完成当前旧的计数后,再开始新的计数周期
Trigger Output Parameters : 触发输出 (TRGO) ,当定时器的定时时间到达的时候输出一个信号(如:定时器更新产生TRGO信号来触发ADC的同步转换)
Repetition Counter:重复计数器(RCR -8 bits),属于高级控制寄存器专用寄存器位,利用它可以非常容易控制输出 PWM 的个数。
Master/Slave Mode(MSM bit) :主从模式
定时器一般是通过软件设置而启动,STM32的每个定时器也可以通过外部信号触发而启动,还可以通过另外一个定时器的某一个条件被触发而启动。这里所谓某一个条件可以是定时到时间、定时器超时、比较成功等许多条件。 这种通过一个定时器触发另一个定时器的工作方式称为定时器的同步,发出触发信号的定时器工作于主模式,接受触发信号而启动的定时器工作于从模式。 定时器的四种主从机模式:外部触发模式1、IRC重置模式、门控模式、触发模式。 |
基本定时器的使用:
创建工程,设置SWD调试,HSE时钟,系统时钟为72MHz
然后打开Timers配置TIM6,设置为Activated激活定时器
Activated: |
然后如何设置定时器的定时时间呢?首先我们要知道定时器的时钟来自哪里?
从芯片手册的时钟树中可以知道,STM32F103定时器的时钟源:
TIM1、TIM8:来自APB2总线上的TIMxCLK
TIM2到TIM7:来自APB1总线上的TIMxCLK
所以TIM6的APB1时钟是72MHz。
Counter Setting:计数设置 |
如果想要使其50ms触发一次定时中断,可以把72MHz通过7200分频后,使其速度降为10KHz,也就意味着每计数10次需要1ms,那么50ms就需要计数500次。因为是从0为第一位开始计数,所以参数都需要减一。
72000000Hz/7200 = 10000Hz = 10KHz = 1s 10000次 = 1ms 10次
也可以通过公式计算 |
然后设置定时器中断
关于中断优先级,在实际工程项目中,根据具体业务需要,当中断过多时,才需要设置中断优先级,测试工程无需设置。
同时,为了方便查看效果,添加LPA6为LED1
点击生成代码
main.c
void MX_TIM6_Init(void)
{TIM_MasterConfigTypeDef sMasterConfig = {0};htim6.Instance = TIM6;htim6.Init.Prescaler = 7200-1;htim6.Init.CounterMode = TIM_COUNTERMODE_UP;htim6.Init.Period = 500-1;htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(&htim6) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK){Error_Handler();}
}stm32f1xx_it.c
/*** @brief This function handles TIM6 global interrupt.*/
void TIM6_IRQHandler(void)
{HAL_TIM_IRQHandler(&htim6);
}进入HAL_TIM_IRQHandler(&htim6)函数,这里面的代码很长,就是不同的中断类型,进入不同的中断回调函数,这里找到HAL_TIM_PeriodElapsedCallback(htim);
进入这个回调函数,我们看到是个虚函数,需要我们重写,在main.c中添加。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ if(htim == &htim6) //判断中断是否来自于定时器6{//do something//例如:HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin); //翻转LED灯的状态//可以把500-1改为5000-1设置500ms的定时中断}
}最后,需要启动定时器,需要在main函数里使能中断/* USER CODE BEGIN 2 */HAL_TIM_Base_Start_IT(&htim6);/* USER CODE END 2 */
同理,其它定时器的定时器中断可以通过同样的方式设置
记得设置完参数后,打开中断哦,否则将不会产生中断。
基本定时器
高级定时器
然后代码中也要手动启动定时器
main.c/* USER CODE BEGIN 2 */HAL_TIM_Base_Start_IT(&htim6)/* USER CODE END 2 */