一、概述
无论是新手还是大佬,基于STM32单片机的开发,使用STM32CubeMX都是可以极大提升开发效率的,并且其界面化的开发,也大大降低了新手对STM32单片机的开发门槛。
本文主要讲述STM32芯片的定时器的配置及其相关知识。
二、软件说明
STM32CubeMX是ST官方出的一款针对ST的MCU/MPU跨平台的图形化工具,支持在Linux、MacOS、Window系统下开发,其对接的底层接口是HAL库,另外习惯于寄存器开发的同学们,也可以使用LL库。STM32CubeMX除了集成MCU/MPU的硬件抽象层,另外还集成了像RTOS,文件系统,USB,网络,显示,嵌入式AI等中间件,这样开发者就能够很轻松的完成MCU/MPU的底层驱动的配置,留出更多精力开发上层功能逻辑,能够更进一步提高了嵌入式开发效率。
演示版本 6.7.0
三、定时器简介
定时器,顾名思义,就是定时的功能,定时器在单片机中算是除GPIO外最基本的外设。在ST中,定时器分为几种,基础定时器,通用定时器,高级定时器和低功耗定时器。本文重点在于基础定时器的配置,其他类型的定时器在其他篇章中详细说明。
基础定时器,就是只提供最基础的定时功能,下面我们就从ST这个基础定时器框图来讲解一下定时器原理。
以定时1s为例,如果当前输入的时钟频率为1MHz,那怎么样才能得到1s的间隔呢?众所周知,1MHz,本身的含义就是每秒1000000次的频次,那么很容易就能想到,如果让定时器每个时钟周期计一个数,计到1000000次的时候就是1s的时间。于是第一个参数就出现了——自动重装载值(Auto Reload Value),这个参数的作用就是:当计数达到自动重装载值时,可以触发一个溢出中断标志,用户获取这个溢出中断标志就可以得到1s的时间。
然而现实并没这么理想,这个自动重装载值只有16位,定时器的计数值也只有16位,也就是说计数最大只能到65535,根本到不了1000000,那应该怎么办呢?想一下,既然计数没办法达到这么大的数,那是不是可以让计数不要计那么快,比如让定时器以1kHz的频率计数,那只要计1000次就可以得到1s的时间。但定时器的输入是1MHz,怎么样才能让它变慢呢?这时候另一个参数就派上用场了——预分频系数(Prescaler Value)。1MHz进行2分频,就是500kHz,进行1000分频,就是1kHz。
四、定时器配置
看完原理,这里我们就来看下具体的功能实现,就以上面的定时1s钟为例,在CubeMX中应该如何设置。
- 基本配置
首先看下配置界面,因为这里我们只需要一个定时知会的功能,并不需要使用到外部端口,所以我们可以直接在选项卡"Timers"中选择"TIM6"(TIM6就是基础定时器),勾选"Activated"启用定时器。
接下来看下基本的一些配置信息。
单脉冲模式(One Pulse Mode): 开启这个模式后,只要触发了一次溢出标志,就会自动把计数使能关掉,想再次触发需要手动开启计数使能。所以如果想要实现周期触发事件,就不用勾选这个选项。
预分频系数(Prescaler): 新手杀手之一,虽然这里写的是预分频系数,但实际设置的值是预分频系数-1,也就是需要10分频时,需要设置9。
计数模式(Counter Mode): 计数模式,也称计数方向,决定定时器是递增计数还是递减计数。如果只是用来定时,那递增或递减都没什么影响。
计数周期(Counter Period): 就是当计数值达到计数周期值时,会触发一个溢出标志,新手杀手之二,因为计数是从0开始计的,所以如果想要实现10次计数,这里只需要设置9即可。
自动重装载(auto-reload preload): 如果选择了自动重装载,那么在触发了一次溢出标志后,定时器会自动将计数清0并重新计数。
触发事件选择(Trigger Event Selection): 可以选择通过UG标志、计数使能、溢出标志来触发输出,这个一般用不上。
为了实现1s的计时,我们得先知道当前定时器的时钟频率。先查看下时钟树,TIM6是挂在APB时钟总线上的,这里我们按单片机最高主频设置的,是48MHz。
为了方便计算,这里我们预分频系数可以填47999,也就是48000分频。计数周期就可以填999,也就是计数1000次。另外再使能自动重装载,这样就可以得到一个1s的周期计数。
为了让计数周期到时可以立即知会到我们去执行一些操作,这里还需要打开中断服务函数。在"NVIC Setting"选项卡中,勾选"TIM6"的中断使能。
做完以上配置,选择好使用的库就可以生成工程代码了。
- LL库代码实现
在生成的工程中添加如下代码,在触发中断时,对一个bool变量进行翻转。
/***************************main.c******************************/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM6_Init();
/* USER CODE BEGIN 2 */
/* 使能更新中断 */
LL_TIM_EnableIT_UPDATE(TIM6);
/* 使能计数 */
LL_TIM_EnableCounter(TIM6);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/***********************************************************************/
/***************************stm32f0xx_it.c******************************/
void TIM6_DAC_IRQHandler(void)
{
/* USER CODE BEGIN TIM6_DAC_IRQn 0 */
/* 进中断后判断如果使能了更新中断,且更新中断标志为1,则翻转数据,并清除标志 */
if ( (LL_TIM_IsActiveFlag_UPDATE(TIM6))
&& (LL_TIM_IsEnabledIT_UPDATE(TIM6))
)
{
LL_TIM_ClearFlag_UPDATE(TIM6);
TestTimer = (++TestTimer) % 2;
}
/* USER CODE END TIM6_DAC_IRQn 0 */
/* USER CODE BEGIN TIM6_DAC_IRQn 1 */
/* USER CODE END TIM6_DAC_IRQn 1 */
}
/*******************************************************************/
- HAL库代码实现
/* USER CODE BEGIN 0 */
uint8_t TestTimer = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(htim);
TestTimer = (++TestTimer) % 2;
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_TIM_PeriodElapsedCallback could be implemented in the user file
*/
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM6_Init();
/* USER CODE BEGIN 2 */
/* 使能更新中断 */
HAL_TIM_Base_Start_IT(&htim6);
/* 启动定时器 */
HAL_TIM_Base_Start(&htim6);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
- 效果演示
烧录软件进行调试,可以看到该变量1s翻转一次电平。
五、注意事项
1、留意预分频系数和重装载值设置的时候都是要减1处理,也就是想要2分频,PSC需要设置为1,想要计数10次复位,则需要给重装载值设置9。
2、配置完生成的工程,并不会在初始化的时候就给打开计数,需要在使用过程中自行调用LL库打开。