定时器有三种,基本定时器,通用定时器,以及高级定时器。
这篇博客以最简单的基本定时器为例,实现LED的闪烁。
后面两种定时器的用法后面再写。
实现功能:
TIM6控制LED每隔0.5s变一次状态。
TIM7控制LED1常量2s后熄灭。
因为都是用到LED,所以和上一篇基于STM32CubeMX与keil采用按键外部中断方式控制LED与蜂鸣器类似。
这里就直接复制粘贴了。
LED部分:
3.3V电压经过一个电阻R12连到DS0发光二极管,如果VCC和LED0对应的引脚之间存在压降,则DS0导通,发光。
3.3V电压经过一个电阻R14连到DS1发光二极管,如果VCC和LED1对应的引脚之间存在压降,则DS1导通,发光。
因此我们要先让LED发光的话,要让LED0和LED1标号对应的引脚输出低电平,才能确保LED0和LED1发光,但由于我们默认状态是LED亮的,所以这里都先要制为高电平。
STM32F407ZET6芯片中
TIM6,TIM7是基本定时器
TIM2-5,TIM9~14是通用定时器
TIM1,TIM8是高级定时器
定时器配置
:首先,您需要配置定时器的参数,例如时钟源、预分频系数和计数器的自动重装载值。这些参数决定了定时器的计数速度和定时间隔。
中断配置
:接下来,您需要配置定时器中断。在STM32中,每个定时器都有一个中断使能位和一个中断标志位。通过设置中断使能位,您可以启用或禁用定时器中断。中断标志位用于指示中断事件是否已经发生。您可以在中断处理函数中清除中断标志位。
中断处理函数
:当定时器达到设定的计数值时,将触发定时器中断,并跳转到相应的中断向量表入口处执行中断处理函数。您需要实现该中断处理函数来执行所需的操作。在中断处理函数中,可以进行一些定时相关的任务,例如更新变量、发送数据或触发其他事件。
中断优先级
:在多个中断同时发生时,优先级决定了哪个中断先被处理。STM32提供了优先级组的设置,您可以根据需要配置不同的中断优先级。
定时器相关参数详解参考:
STM32微控制器系列提供了多种时钟源选项,以满足不同的应用需求。以下是一些常见的时钟源选项:
HSI(High-Speed Internal)内部高速时钟:
HSI是STM32内部集成的高频振荡器,通常为16MHz。它是默认的系统时钟源,在芯片上电后自动启动。HSI适用于大多数应用场景,提供相对较高的精度和稳定性。
HSE(High-Speed External)外部高速时钟:
HSE使用外部晶体振荡器或时钟源提供稳定的时钟信号。HSE的频率范围可以根据具体的芯片型号而变化,通常为4MHz到25MHz。外部时钟源相对于内部时钟提供更高的精确性和稳定性。
LSI(Low-Speed Internal)内部低速时钟:
LSI是低频振荡器,通常为32kHz。它用于一些低功耗应用,例如RTC(实时时钟)或独立看门狗定时器(IWDG)。
LSE(Low-Speed External)外部低速时钟:
LSE使用外部晶体振荡器或时钟源提供低速时钟信号。它通常为32.768kHz,用于RTC和低功耗模式。
PLL(Phase-Locked Loop)锁相环:
PLL是一种用于产生高频时钟的电路。它可以通过将输入时钟信号(如HSI或HSE)经过倍频和分频来产生更高频率的时钟。PLL提供了灵活的时钟频率调整能力,适合高性能应用。
请注意,具体的时钟源选项和配置方法可能因芯片型号和系列而有所不同。在使用特定型号的STM32芯片时,您应查阅芯片的数据手册和参考手册,以获取详细的时钟源配置信息。
预分频系数用于设置定时器时钟频率的分频比。它确定了定时器计数器每个时钟周期递增的步长,从而影响定时器的计数速度和定时间隔。
具体的预分频系数选项和配置方法可能因芯片型号和系列而有所不同。以下是一些常见的预分频系数配置:
APB分频系数(PCLKx):APB(Advanced Peripheral Bus)是STM32中用于连接外设的总线。通过配置APB分频系数,可以将系统时钟(SYSCLK)分频得到适合外设工作的时钟频率。常见的预分频系数选项包括2、4、8和16。
TIMx预分频系数:定时器模块(TIM)有自己的预分频器,可以将时钟源的频率进一步分频。具体的预分频系数选项和配置方法因芯片型号和定时器模块而异。
PLL倍频系数(PLLM、PLLN、PLLP):如果使用PLL锁相环来产生高频时钟,可以通过设置不同的倍频系数来调整PLL输出时钟的频率。PLLM为输入分频器系数,PLLN为倍频器系数,PLLP为输出分频器系数。
自动重装载值(Auto-reload value)是一个重要的参数,用于控制定时器计数器的溢出和重新加载。
定时器计数器在每个时钟周期递增,当达到自动重装载值时,计数器将重新加载为初始值,并触发中断(如果已启用)。这种重新加载的操作使定时器可以周期性地生成中断或触发其他事件。
自动重装载值的大小决定了定时器的定时间隔。通常,自动重装载值的设置可以通过以下公式计算:
自动重装载值 = (定时周期 / 时钟周期) - 1
,这里减一是因为从0开始算,例如0~9实际上计数了10个.
其中,定时周期是所需的定时间隔,时钟周期是定时器的时钟频率。通过更改自动重装载值,可以调整定时器的定时间隔。
在STM32中,自动重装载值可以存储在定时器的自动重装载寄存器(ARR)中。通过将自动重装载寄存器设置为所需的值,可以实现定时器的周期性操作。
需要注意的是,自动重装载值应适当选择,以确保不会发生溢出。应根据所需的定时间隔和定时器的时钟频率来选择合适的值,避免溢出或过长的定时间隔。
为了便于我们计算,我们这里最终的时钟设置为100MHz,前面的时钟源以及分频系数等都会自动设置好。
给到定时器的时钟其实是APB1和APB2中获取的,因为我这篇博客写的是基本定时器的博客所以我们要找到的基本定时器TIM6,TIM7对应的时钟,这里是APB1。
STM32F407ZET3的其他的定时器所在总线等这里列个表
定时器类型 | 定时器名称 | 定时器所在总线 |
---|---|---|
基本定时器 | TIM6、TIM7 | APB1 |
通用定时器 | TIM2~5,TIM12~14 | APB1 |
通用定时器 | TIM9~11 | APB2 |
高级定时器 | TIM1,TIM8 | APB2 |
这里分频系数是49999,即0~49999,长度为50000,我们APB1总线传过来的时钟是50MHz,所以说明每一个时钟周期都是1ms。
A P B 1 时钟 T I M 6 分频系数 = 50 ∗ 1 0 6 H z 5 ∗ 1 0 4 = 1 K H z = 1 T ( 时间 ) = > T = 1 m s \frac{APB1时钟}{TIM6分频系数}=\frac{50*10^6Hz}{5*10^4}=1KHz=\frac{1}{T(时间)}=>T=1ms TIM6分频系数APB1时钟=5∗10450∗106Hz=1KHz=T(时间)1=>T=1ms
对于TIM6控制的LED,我们的目标是0.5s转换一次LED的状态,所以这里这里的计数重载是500-1,因为从0开始计数。
写到这,简单点理解就是,从时钟源分频时钟到总线,定时器从总线再分频,用来作为时间的分度值,这个说法可能不恰当,但意思就是一个时钟周期多少秒,接着用计数重载值 * 刚才的分度值就是我们想要的时间。即每隔多少时间,干什么。
TIM7的设置也同样,只不过计数重载值为1999,这里就不贴全图了。
其实时钟配置里面的NVIC打开了之后,这里其实就自动勾选配好了。
优先级抢占和响应:当两个中断发生且优先级不同时,优先级高的中断会抢占正在执行的低优先级中断。如果多个中断具有相同的优先级,则根据抢占式(Preemption)和响应式(Response)的设定来确定中断的顺序。先看抢占式优先级,前面数值越小,优先级越高,如果一样则看后面的优先级,数值也是越小越优先。
这篇博客其实对优先级要求不是那么高,不会怎么涉及抢占优先级等情况。所以这里默认0 , 0就行。
还有一个就是代码生成的时候是否生成对应的中断服务函数之类的,都默认勾选上。
打开生成的项目中的stm32f4xx_it.c文件
再最下面直接添加下列代码
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM6)
{
HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
}
if(htim->Instance==TIM7)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
}
}
说明一下为什么重写这个函数,当定时器达到设定的计数值时,将触发定时器中断,并跳转到相应的中断向量表入口处执行中断处理函数。您需要实现该中断处理函数来执行所需的操作。
计数值超过500就会触发中断,调用功能这个回调函数,这里我们看函数名就知道了。PeriodElapseCallback和我们设置的那个值对应。
最后就是烧录程序了,这个和我这个专栏第一篇都一样,具体参考这篇博客
这篇博客详细记录了定时器的简单使用,从目标出发到原理阐释再到配置与代码编写,一步一步实现功能。难度不是很大,中断处理部分与上一篇有很多共性的地方,学习起来也比较快。就是记录的时候比较费时间。写这篇博客的部分内容用了下chatGPT工具,效果也挺好,确实提高了部分效率。