STM32第七课(TIM,HAL)

通用定时器包含一个 16 位或 32 位自动重载计数器(CNT),该计数器由可编程预分频器(PSC) 驱动。

定 时 器 相 关 的 库 函 数 主 要 集 中 在 HAL 库 文 件 stm32f4xx_hal_tim.h 和stm32f4xx_hal_tim.c 文件中。

+++++++++++++++++++++++++++++++++++++++++++++++
TIM3 时钟使能。
HAL 中定时器使能是通过宏拟函数来实现对相关寄存器操作的,方法如下:

__HAL_RCC_TIM3_CLK_ENABLE(); //使能 TIM3 时钟

初始化定时器参数,设置自动重装值, 分频系数,计数方式等。
在 HAL 库中,定时器的初始化参数是通过定时器初始化函数 HAL_TIM_Base_Init 实现的:

HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim);

入口参数,就是 TIM_HandleTypeDef 类型结构体指针,这是TIM的句柄。

我们看看这个结构体的定义:

typedef struct
{
	TIM_TypeDef *Instance;
	TIM_Base_InitTypeDef Init;
	HAL_TIM_ActiveChannel Channel;
	DMA_HandleTypeDef *hdma[7];
	HAL_LockTypeDef Lock;
	__IO HAL_TIM_StateTypeDef State;
}TIM_HandleTypeDef;

Instance 是寄存器基地址。在HAL中都定义好了,比如要初始化定时器1,那么 Instance 的值设置为 TIM1 即可。
Init 为真正的初始化结构体 TIM_Base_InitTypeDef 类型。该结构体定义如下:

 typedef struct
{
	uint32_t Prescaler; //预分频系数
	uint32_t CounterMode; //计数方式
	uint32_t Period; //自动装载值 ARR
	uint32_t ClockDivision; //时钟分频因子
	uint32_t RepetitionCounter;
} TIM_Base_InitTypeDef;

Prescaler 是用来设置分频系数的,
CounterMode 是用来设置计数方式,比 较 常 用 的 是
向 上 计 数 模 式 TIM_CounterMode_Up 和 向 下 计 数 模 式TIM_CounterMode_Down。
Period 是设置自动重载计数周期值。
ClockDivision 是用来设置时钟分频因子,也就是定时器时钟频率 CK_INT 与数字滤波器所使用的采样时钟之间的分频比。
RepetitionCounter 用来设置重复计数器寄存器的值,用在高级定时器中。

回到TIM的结构体。
Channel 用来设置活跃通道。每个定时器最多有四个通道可以用来做输出比较,输入捕获等功能之用。取值范围为: HAL_TIM_ACTIVE_CHANNEL_1 到 HAL_TIM_ACTIVE_CHANNEL_4。
Lock 和 State,是状态过程标识符,是 HAL 库用来记录和标志定时器处理过程。

+++++++++++++++++++++++++++++++++++++
定时器初始化范例如下:

TIM_HandleTypeDef TIM3_Handler; //定时器句柄

TIM3_Handler.Instance=TIM3; //通用定时器 3
TIM3_Handler.Init.Prescaler=8999; //分频系数
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器
TIM3_Handler.Init.Period=4999; //自动装载值
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子

HAL_TIM_Base_Init(&TIM3_Handler);

++++++++++++++++++++++++++++++++++++++++
HAL 库 中 , 使 能 定 时 器 更 新 中 断 和 使 能 定 时 器 两 个 操 作 可 以 在 函 数
HAL_TIM_Base_Start_IT()中一次完成的,该函数声明如下:

HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);

在定时器中断使能之后,因为要产生中断,必不可少的要设置 NVIC 相关寄存器,设置中
断优先级。

HAL 库为定时器初始化定义了回调函数 HAL_TIM_Base_MspInit。

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim);

++++++++++++++++++++++++++++++++++++++
编写定时器中断服务函数,通过该函数来处理定时器产生的相关中断。
在中断产生后,通过状态寄存器的值来判断此次产生的中断属于什么类型。

对于定时器中断, HAL 库同样为我们封装了处理过程。
首先,中断服务函数是不变的,定时器 3 的中断服务函数为:

TIM3_IRQHandler();

HAL 库为我们定义了 新的定时器中断共用处理函数 HAL_TIM_IRQHandler,在每个定时器的中断服务函数内部,我们会调用该函数。该函数声明如下:

void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim);

函数 HAL_TIM_IRQHandler 内部,会对相应的中断标志位进行详细判断,判断确定中断来源后,会自动清掉该中断标志位,同时调用不同类型中断的回调函数。

所以我们的中断控制逻辑只用编写在中断回调函数中,并且中断回调函数中不需要清中断标志位。
比如定时器更新中断回调函数为:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

对于其他类型中断, HAL 库同样提供了几个不同的回调函数,这里我们列出常用的几个回调函数:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);//更新中断
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim);//输出比较
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);//输入捕获
void HAL_TIM_TriggerCallback(TIM_HandleTypeDef *htim);//触发中断

++++++++++++++++++++++++++++++++++++++++++
实际应用中,TIM主要是用来作为事件发生器。
所以,TIM产生中断事件,触发ISR的执行,与TIM相关的处理工作,放在ISR中执行,更确切的,放在callback中执行。
如果实时性要求高,那么功能处理,可以直接放在calllback中执行,callback运行在ISR的运行上下文中,通常要求是尽量少占用CPU。
如果实时性要求不是特别高,可以在前后台架构中,分开处理。
在callback中打标,在main中检查标志,做出处理,然后清标。

++++++++++++++++++++++++++++++++++++++++++
实例。

预分频寄存器(TIMx_PSC)。该寄存器用设置对时钟进行分频,然后提供给计数器,作为计数器的时钟。
CK_CNT = fck_int /(PSC[15:0] + 1) = fapb1 *2 /(PSC[15:0] + 1)
CK_INT时钟是从 APB1 倍频的来的,

TIMx_CNT 寄存器,该寄存器是定时器的计数器,该寄存器存储了当前定时器的计数值。
TIMx_ARR,该寄存器在物理上实际对应着 2 个寄存器。
一个是程序员可以直接操作的,另外一个是程序员看不到的,这个看不到的寄存器被叫做影子寄存器。事实上真正起作用的是影子寄存器。
APRE=1 时,在每一次更新事件(UEV)时,才把预装载寄存器(ARR) 的内容传送到影子寄存器。
状态寄存器( TIMx_SR)。该寄存器用来标记当前与定时器相关的各种事件/中断是否发生。

在cubemx中配置TIM,
clock source选择 internal clock,
prescale设置为8399,预分频倍数为8400,所以,最后将84MHZ时钟分频到10KHZ,
auto reload value 设置为4999,计数周期为5000,所以,每次中断,周期为500ms。
CKD设置为disable,所以fclk_int并不会分频,就是84MHZ。
auto_reload preload 设置为enable。
trigger event 设置为update event。

配置nvic,使能TIM3的interrupt。

覆盖定义HAL_TIM_PeriodElapsedCallback

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim == &htim3){
        time_out = 1;   
    }  
}

由于callback会被所有的TIMER的ISR所调用,所以,为了区分当前是哪个TIMER的ISR调用了callback,所以传递了TIMER的句柄作为参数。
在callback中,要对TIMER的句柄进行检查判断,并针对不同的TIMER做出不同的处理。
这里,针对TIM3,callback打标。

在main中,
检查TIM3的标志,并做出后续处理,然后清标。

		if(time_out){          
            pin_value = HAL_GPIO_ReadPin(led1_GPIO_Port, led1_Pin);
            HAL_GPIO_WritePin(led1_GPIO_Port ,led1_Pin ,!pin_value);
            pin_value = HAL_GPIO_ReadPin(led2_GPIO_Port, led2_Pin);
            HAL_GPIO_WritePin(led2_GPIO_Port ,led2_Pin ,!pin_value);
            
            time_out = 0;
        }

注意,
在main中,初始化定时器,只是配置好各个参数,
启动定时器,需要手工使用start函数来启动。

	HAL_TIM_Base_Start_IT(&htim3);

版权声明:本文为CSDN博主「Huskar_Liu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42418557/article/details/121961636

通用定时器包含一个 16 位或 32 位自动重载计数器(CNT),该计数器由可编程预分频器(PSC) 驱动。

定 时 器 相 关 的 库 函 数 主 要 集 中 在 HAL 库 文 件 stm32f4xx_hal_tim.h 和stm32f4xx_hal_tim.c 文件中。

+++++++++++++++++++++++++++++++++++++++++++++++
TIM3 时钟使能。
HAL 中定时器使能是通过宏拟函数来实现对相关寄存器操作的,方法如下:

__HAL_RCC_TIM3_CLK_ENABLE(); //使能 TIM3 时钟

初始化定时器参数,设置自动重装值, 分频系数,计数方式等。
在 HAL 库中,定时器的初始化参数是通过定时器初始化函数 HAL_TIM_Base_Init 实现的:

HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim);

入口参数,就是 TIM_HandleTypeDef 类型结构体指针,这是TIM的句柄。

我们看看这个结构体的定义:

typedef struct
{
	TIM_TypeDef *Instance;
	TIM_Base_InitTypeDef Init;
	HAL_TIM_ActiveChannel Channel;
	DMA_HandleTypeDef *hdma[7];
	HAL_LockTypeDef Lock;
	__IO HAL_TIM_StateTypeDef State;
}TIM_HandleTypeDef;

Instance 是寄存器基地址。在HAL中都定义好了,比如要初始化定时器1,那么 Instance 的值设置为 TIM1 即可。
Init 为真正的初始化结构体 TIM_Base_InitTypeDef 类型。该结构体定义如下:

 typedef struct
{
	uint32_t Prescaler; //预分频系数
	uint32_t CounterMode; //计数方式
	uint32_t Period; //自动装载值 ARR
	uint32_t ClockDivision; //时钟分频因子
	uint32_t RepetitionCounter;
} TIM_Base_InitTypeDef;

Prescaler 是用来设置分频系数的,
CounterMode 是用来设置计数方式,比 较 常 用 的 是
向 上 计 数 模 式 TIM_CounterMode_Up 和 向 下 计 数 模 式TIM_CounterMode_Down。
Period 是设置自动重载计数周期值。
ClockDivision 是用来设置时钟分频因子,也就是定时器时钟频率 CK_INT 与数字滤波器所使用的采样时钟之间的分频比。
RepetitionCounter 用来设置重复计数器寄存器的值,用在高级定时器中。

回到TIM的结构体。
Channel 用来设置活跃通道。每个定时器最多有四个通道可以用来做输出比较,输入捕获等功能之用。取值范围为: HAL_TIM_ACTIVE_CHANNEL_1 到 HAL_TIM_ACTIVE_CHANNEL_4。
Lock 和 State,是状态过程标识符,是 HAL 库用来记录和标志定时器处理过程。

+++++++++++++++++++++++++++++++++++++
定时器初始化范例如下:

TIM_HandleTypeDef TIM3_Handler; //定时器句柄

TIM3_Handler.Instance=TIM3; //通用定时器 3
TIM3_Handler.Init.Prescaler=8999; //分频系数
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器
TIM3_Handler.Init.Period=4999; //自动装载值
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子

HAL_TIM_Base_Init(&TIM3_Handler);

++++++++++++++++++++++++++++++++++++++++
HAL 库 中 , 使 能 定 时 器 更 新 中 断 和 使 能 定 时 器 两 个 操 作 可 以 在 函 数
HAL_TIM_Base_Start_IT()中一次完成的,该函数声明如下:

HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);

在定时器中断使能之后,因为要产生中断,必不可少的要设置 NVIC 相关寄存器,设置中
断优先级。

HAL 库为定时器初始化定义了回调函数 HAL_TIM_Base_MspInit。

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim);

++++++++++++++++++++++++++++++++++++++
编写定时器中断服务函数,通过该函数来处理定时器产生的相关中断。
在中断产生后,通过状态寄存器的值来判断此次产生的中断属于什么类型。

对于定时器中断, HAL 库同样为我们封装了处理过程。
首先,中断服务函数是不变的,定时器 3 的中断服务函数为:

TIM3_IRQHandler();

HAL 库为我们定义了 新的定时器中断共用处理函数 HAL_TIM_IRQHandler,在每个定时器的中断服务函数内部,我们会调用该函数。该函数声明如下:

void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim);

函数 HAL_TIM_IRQHandler 内部,会对相应的中断标志位进行详细判断,判断确定中断来源后,会自动清掉该中断标志位,同时调用不同类型中断的回调函数。

所以我们的中断控制逻辑只用编写在中断回调函数中,并且中断回调函数中不需要清中断标志位。
比如定时器更新中断回调函数为:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

对于其他类型中断, HAL 库同样提供了几个不同的回调函数,这里我们列出常用的几个回调函数:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);//更新中断
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim);//输出比较
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);//输入捕获
void HAL_TIM_TriggerCallback(TIM_HandleTypeDef *htim);//触发中断

++++++++++++++++++++++++++++++++++++++++++
实际应用中,TIM主要是用来作为事件发生器。
所以,TIM产生中断事件,触发ISR的执行,与TIM相关的处理工作,放在ISR中执行,更确切的,放在callback中执行。
如果实时性要求高,那么功能处理,可以直接放在calllback中执行,callback运行在ISR的运行上下文中,通常要求是尽量少占用CPU。
如果实时性要求不是特别高,可以在前后台架构中,分开处理。
在callback中打标,在main中检查标志,做出处理,然后清标。

++++++++++++++++++++++++++++++++++++++++++
实例。

预分频寄存器(TIMx_PSC)。该寄存器用设置对时钟进行分频,然后提供给计数器,作为计数器的时钟。
CK_CNT = fck_int /(PSC[15:0] + 1) = fapb1 *2 /(PSC[15:0] + 1)
CK_INT时钟是从 APB1 倍频的来的,

TIMx_CNT 寄存器,该寄存器是定时器的计数器,该寄存器存储了当前定时器的计数值。
TIMx_ARR,该寄存器在物理上实际对应着 2 个寄存器。
一个是程序员可以直接操作的,另外一个是程序员看不到的,这个看不到的寄存器被叫做影子寄存器。事实上真正起作用的是影子寄存器。
APRE=1 时,在每一次更新事件(UEV)时,才把预装载寄存器(ARR) 的内容传送到影子寄存器。
状态寄存器( TIMx_SR)。该寄存器用来标记当前与定时器相关的各种事件/中断是否发生。

在cubemx中配置TIM,
clock source选择 internal clock,
prescale设置为8399,预分频倍数为8400,所以,最后将84MHZ时钟分频到10KHZ,
auto reload value 设置为4999,计数周期为5000,所以,每次中断,周期为500ms。
CKD设置为disable,所以fclk_int并不会分频,就是84MHZ。
auto_reload preload 设置为enable。
trigger event 设置为update event。

配置nvic,使能TIM3的interrupt。

覆盖定义HAL_TIM_PeriodElapsedCallback

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim == &htim3){
        time_out = 1;   
    }  
}

由于callback会被所有的TIMER的ISR所调用,所以,为了区分当前是哪个TIMER的ISR调用了callback,所以传递了TIMER的句柄作为参数。
在callback中,要对TIMER的句柄进行检查判断,并针对不同的TIMER做出不同的处理。
这里,针对TIM3,callback打标。

在main中,
检查TIM3的标志,并做出后续处理,然后清标。

		if(time_out){          
            pin_value = HAL_GPIO_ReadPin(led1_GPIO_Port, led1_Pin);
            HAL_GPIO_WritePin(led1_GPIO_Port ,led1_Pin ,!pin_value);
            pin_value = HAL_GPIO_ReadPin(led2_GPIO_Port, led2_Pin);
            HAL_GPIO_WritePin(led2_GPIO_Port ,led2_Pin ,!pin_value);
            
            time_out = 0;
        }

注意,
在main中,初始化定时器,只是配置好各个参数,
启动定时器,需要手工使用start函数来启动。

	HAL_TIM_Base_Start_IT(&htim3);

版权声明:本文为CSDN博主「Huskar_Liu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42418557/article/details/121961636

生成海报
点赞 0

Huskar_Liu

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

OV7670摄像头模块资料

OV7670摄像头模块资料 一、实物图和原理图 二、模块简介 OV7670 是 OV( OmniVision)公司生产的 CMOS VGA 图像传感器。该传感器体积小、工作电压低,提供单片 VGA

pwm电机调速的原理介绍与代码实现

1、pwm实现调速的原理与介绍 PWM(Pulse Width Modulation)脉冲宽度调制。 1)占空比 pwm占空比就是一个脉冲周期内有效电平在整个周期所占的比例。 通过调节PWM的占空比就能调节IO口上电压的持续

【STM32Cube笔记】2-STM32Cube安装教程

【STM32Cube笔记】系列文章目录 1-基于STM32的VSCode入门级教程前言 2-STM32Cube安装教程 3-STM32CubeIDE汉化 4-STM32Cube配置时钟设置 5-跑马灯引脚配置 6-Cortex-M7内核基本配