一、定时器的基本概述
1.软件定时器原理
原来我们使用51单片机的时候,是通过一个__nop()__来进行延时
我们通过软件的方式来进行延时功能是不准确的,受到很多不确定因素。
2.定时器原理:计数之间的比值
因为使用软件延时受到影响,所以我们尽量使用硬件来进行定时。硬件同时执行效率也比软件的高。
3.STM32定时器分类
4.STM32定时器特性表
注意点:
1)基本定时器是向上计数器(递增)
2)预分频器系数是从1-65536(但是实际上存储的数值范围是0-65535)--》所以存在+1和-1的问题
5.STM32基本,通用,高级定时器功能整体区别
二、基本定时器(TIM6/TIM7)
1.基本概念
"触发DAC"---》指的是数据电路和模拟电路之间的转换
2.基本定时器框图
1.注意点
1)基本定时器的时钟源是:内部时钟
2)自动重载寄存器和预分频器都有相对应的影子寄存器
3)决定自动重载寄存器(ARR)和预分频器(PSC)是否马上将值更新(是否有缓冲) 到影子寄存器的取决于ARPE位
有缓冲--》减少时间误差
无缓冲---》有大量的时间误差
4)影子寄存器中的值更新也要有事件触发
5)实际起作用的是:影子寄存器,其不能直接被访问到
6)溢出条件是CNT==ARR影子寄存器,而不是ARR本身
7)当溢出时,会产生两种结果:中断和DMA输出 中断事件
2.内部时钟频率问题
1)定时器最终的时钟频率不仅仅取决于外部传入的时钟频率还取决于是否进行分频。
举个例子
3.STM32定时器计数模式及溢出条件
递增计数模式实例说明
递减计数模式实例说明
中心对齐模式实例说明
三、定时器寄存器
1.TIM6 和TIM7 控制寄存器 1(TIMx_CR1)
该寄存器用于设置ARR寄存器是否具有缓冲,并且使能/关闭计数器
是否有缓冲的区别:应用场景
1.假设我们现在让led先亮1s,然后灭,在亮2s,假设1s对应的ARR=99,2s对应的ARR=199
a)此时没有缓冲,则在亮1s后灭,然后再要开始亮的时候,我们要修改CNT的值为199,这个修改过程需要浪费时间
b)此时有缓冲,则再亮1s的这个时间段,我们就可以修改ARR=199,因为有缓冲,是等到下一个周期才起作用。
2.假设我们现在让led先亮1s,然后灭,在亮1s,假设1s对应的ARR=99a)此时没有缓冲,则在亮1s后灭,然后再要开始亮的时候,此时我们不需要修改,则不需要时间
b)此时有缓冲,则再亮1s,然后再要开始亮的时候,此时我们不需要修改,则不需要时间
2.TIM6 和TIM7 DMA/中断使能寄存器(TIMx_DIER)
用于使能更新中断
3. TIM6 和TIM7 状态寄存器(TIMx_SR)
用于判断是否发生更新中断,由硬件置1,软件清零
4. TIM6 和TIM7 计数器(TIMx_CNT)
计数器是可读可写,并且再运行过程中也是可以对其进行修改。
5. TIM6 和TIM7 预分频器(TIMx_PSC)
6. TIM6 和TIM7 自动重装载寄存器(TIMx_ARR)
四、定时器溢出时间计算方法
1)这里ARR+1,可以这样理解,例如ARR的值为2,则计数器从0到1,从1到2,此时并不会立刻溢出,而是再经过一个时钟脉冲后才会溢出,所以实际的溢出时间是三个时钟脉冲,即ARR+1个时钟脉冲
2)ARR里面的值是从零开始的
推导过程
溢出时间=计数个数*f
五、定时器中断配置步骤
1.具体步骤
注意点:
1)记得开启计数器
2)记得使能中断(定时器,NVIC)
3)编写中断服务函数
4)设置NVIC,时钟等基础
5)设置定时器相关的基础参数
6)定时器溢出中断中断回调函数
1)定时器初始化--》HAL_TIM_Base_Init
2)设置中断优先级,使能中断---》HAL_NVIC_SetPriority && HAL_NVIC_EnableIRQ
3)编写中断服务函数---》去start.s文件中查找对应的名字【TIM6_IRQHandler】然后再这个函数中调用【HAL_TIM_IRQHandler(这个再hal_tim.c中)】
3)编写定时器的回调函数--》这个函数是在中断处理程序中调用的--》HAL_TIM_PeriodElapsedCallback
4)启动定时器(计数器)和中断---》HAL_TIM_Base_Start_IT
2.相关HAL库函数
1.HAL_TIM_OC_Init:定时器初始化
在stm32f1xx_hal_tim.c中
cpp
HAL_StatusTypeDef HAL_TIM_OC_Init(TIM_HandleTypeDef *htim)
{
/* Check the TIM handle allocation */
if (htim == NULL)
{
return HAL_ERROR;
}
/* Check the parameters */
assert_param(IS_TIM_INSTANCE(htim->Instance));
assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));
if (htim->State == HAL_TIM_STATE_RESET)
{
/* Allocate lock resource and initialize it */
htim->Lock = HAL_UNLOCKED;
/* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */
HAL_TIM_OC_MspInit(htim);
}
/* Set the TIM state */
htim->State = HAL_TIM_STATE_BUSY;
/* Init the base time for the Output Compare */
TIM_Base_SetConfig(htim->Instance, &htim->Init);
/* Initialize the TIM state*/
htim->State = HAL_TIM_STATE_READY;
return HAL_OK;
}
关键结构体介绍
2.HAL_TIM_Base_MspInit
这个函数的作用:MSP是指和MCU相关的初始化
3.HAL_TIM_Base_Start
启动计数器
4.HAL_TIM_Base_Start_IT
启动定时器的中断
5. TIM6_IRQHandler
1)这个函数名根据所使用的定时器类型去start.s中查找
2)再这个函数中调用"HAL_TIM_IRQHandler"
6.HAL_TIM_PeriodElapsedCallback
编写定时器溢出回调函数,这个是用户根据相关的要求编写
三、中断服务函数VS中断回调函数
HAL库中的中断,在callback回调函数和中断函数中处理有何区别 (amobbs.com 阿莫电子论坛 - 东莞阿莫电子网站)
中断服务函数:TIM6_IRQHandler--->这个实际上不编写业务代码,只是提供一个中断入口
中断回调函数:HAL_TIM_PeriodElapsedCallback--->**这个回调函数中编写了相关的业务代码,**真正的处理
-
中断服务函数(ISR): 中断服务函数是由硬件触发并由操作系统或者嵌入式系统执行的函数。当中断事件(例如,定时器溢出、外部触发等)发生时,CPU会跳转到相应的中断服务函数来执行相关的操作。在这个上下文中,ISR 是系统级别的,用于响应底层的硬件事件。
-
中断回调函数: 中断回调函数通常是用户或应用程序级别定义的函数,它会在中断服务函数的上下文中执行。在某些情况下,中断服务函数可能会调用用户定义的回调函数。这个回调函数是由用户定义的,用于处理中断事件,执行与中断相关的应用程序逻辑。在这个上下文中,回调函数是应用程序级别的,用于定制中断发生时的行为。
六、编程实战:定时器中断实验
STM32CubeMX学习笔记(5)------基本定时器接口使用_counter mode mx-CSDN博客
1.条件分析
2.相关频率,溢出时间的计算
3.代码编写
操作代码
cpp
TIM_HandleTypeDef g_timx_handle;
//定时器中断初始化函数
void btim_timx_int_init(uint16_t arr,uint16_t psc){
g_timx_handle.Instance=TIM6;
g_timx_handle.Init.Prescaler=psc;//分频参数
g_timx_handle.Init.Period=arr;//预装载值
//定时器初始化的基本参数
HAL_TIM_Base_Init(&g_timx_handle);
//打开定时器6的中断
HAL_TIM_Base_Start_IT(&g_timx_handle);
}
//定时器基础MSP初始化函数(就是初始化NVIC,CLOCK等)
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim){
//判断地址是否为定时器6
if(htim->Instance==TIM6){
//打开定时器6的时钟
__HAL_RCC_TIM6_CLK_ENABLE();
//初始化NVIC
HAL_NVIC_SetPriority(TIM6_IRQn,1,3);//设置优先级
HAL_NVIC_EnableIRQ(TIM6_IRQn);//打开定时器6中断
}
}
//编写中断服务函数
//去start.s文件中查找
//代码作用:实际上这个函数是提供了一个定时器中断的入口
//并不进行业务的处理
void TIM6_IRQHandler(){
HAL_TIM_IRQHandler(&g_timx_handle);
}
//编写定时器溢出回调函数
//代码作用:编写用户相关的业务代码
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
//判断地址是否为定时器6
if(htim->Instance==TIM6){
//翻转led,使得闪烁
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
}
}
main函数
cpp
int main(){
HAL_Init();
sys_stm32_clock_init(RCC_PLL_MUL9);
delay_init();
btim_timx_int_init(5000-1,7200-1);
while(1){
}
}
通用定时器