赞
踩
目录
定时器作为单片机一个重要的外设,它分为高级控制定时器,通用定时器和基础定时器,基础定时器功能简单,只能用于定时,通用定时器和高级控制定时器还具有输入捕获、输出比较、PWM输出等功能,我们会结合STM32CubeMX与HAL库来使用这些功能
在本章中只介绍两种最常用芯片的定时器资源 STM32F1系列和F4系列单片机
STM32F1系列共有8个定时器:
高级定时器(TIM1、TIM8)、通用定时器(TIM2、TIM3、TIM4、TIM5)、基本定时器(TIM6、TIM7)。
STM32F4系列共有15个定时器:
高级定时器(TIM1、TIM8)、通用定时器(TIM2、TIM3、TIM4、TIM5、TIM9~TIM14)、基本定时器(TIM6、TIM7)。
定时器时钟信号来源与APB1和APB2总线的定时器时钟信号,下图是时钟树上的一部分区域,STM32F103的HCLK最高频率为72MHZ,APB1总线的时钟频率PCLK1是最高为36MHZ,挂在APB1总线上的定时器时钟频率固定为PCLK1的2倍,所以挂在APB1总线上的定时器输入时钟频率最高为72MHZ,同样APB2总线上的也为72MHZ
定时器内部还有一个预分频器,它可以设置0-65535中的任何一个整数对输入时钟信号分频(实际分频系数为寄存器值加1)预分频之后的时钟信号再进入计数器
TIM6和TIM7是两个基础定时器,都在APB1总线上,它们的功能相同,有以下基本特征
1、只能使用内部时钟信号CK_INT,这个时钟信号频率为APB1总线频率的2倍,所以这两个定时器输入时钟频率最高为72MHZ
2、有16位自动重载寄存器(auto-reload register)用于设置计数周期
3、有16位可编辑预分频器,设置范围为0-65535,分频系数范围为1-65536
4、可输入触发信号(TRGO)用于触发DAC的同步电路,只有基础定时器有这个功能
5、只有一种事件引发中断或者DMA请求,既计数器上溢时产生的事件
基础定时器它有3个16位寄存器,这三个寄存器的值都可以读写,其中,预分频寄存器和自动重载寄存器有影子寄存器用于底层工作
1、预分频寄存器(TIMx_PSC)它存储的一个值用于对输入时钟信号CK_PSC进行分频,得到输出时钟信号CK_CNT,实际分频系数是预分频寄存器的值加1
2、计数寄存器(TIMx_CNT)计数器使用时钟信号CK_CNT进行计数,当计数器的值等于自动重装载值时产生计数溢出,同时可以产生中断或者DMA请求
3、自动重装载寄存器(TIMx_ARR)存储的值用于与计数器的值作为比较
基础定时器的功能只有定时
内部工作原理简单了解即可,这里不再过多介绍,感兴趣可以自行上网搜索查阅资料,通用定时器具有以下特性
1、16位或32位的自动重载计数器
2、16位可编程预分频器,分频系数为1-65536,分频系数可在运行时修改
3、有1、2或4个独立通道,可用于 输入捕获 输出比较 PWM生成 单脉冲模式输出
4、可使用外部信号控制定时器,可实现多个定时器互连的同步电路
5、发生以下事件可以产生中断或者DMA请求 计数器溢出 计数器初始化(通过软件内部触发) 输入捕获 输出比较 触发事件(计数器停止或者启动,初始化或者通过外部触发计数)
6、支持针对定位的增量(正交)编码器和霍尔传感器电路
而高级定时器则拥有基础定时器和通用定时器的全部功能,而且位于APB2总线上,总结一下基本定时器就是单纯的定时计数器,通用定时器多了四个通道,相对应的增加了功能,高级定时器具有基本,通用定时器的所有的功能,并且添加了其他功能
下表是基础定时器的一些主要的HAL库驱动函数,所有定时器都有定时功能,所以这些函数同样适用于通用定时器与高级定时器,函数头文件为stm32f1xx_hal_tim.h
基础定时器的一些主要HAL库驱动函数
分组 | 函数名 | 功能描述 |
初始化 | HAL_TIM_Base_Init(TIM_HandleTypeDef *htim) | 定时器初始化,设置各种参数和连续定时模式 |
HAL_TIM_OnePulse_Init(TIM_HandleTypeDef *htim) | ||
HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim) | 弱函数,在函数1中被调用,重新实现这个函数一般用于定时器时钟使能和中断设置 | |
启动和停止 | HAL_TIM_Base_Start(TIM_HandleTypeDef *htim) | 以轮询模式启动定时器,不会启动中断 |
HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim) | 停止轮询工作模式的定时器 | |
HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim) | 以中断模式启动定时器 | |
HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim) | 停止中断工作方式的定时器 | |
HAL_TIM_Base_Start_DMA(TIM_HandleTypeDef *htim, const uint32_t *pData, uint16_t Length) | 以DMA工作方式开启定时器 | |
HAL_TIM_Base_Stop_DMA(TIM_HandleTypeDef *htim) | 停止DMA工作模式的定时器 | |
获取状态 | HAL_TIM_Base_GetState(TIM_HandleTypeDef *htim) | 获取基础定时器的当前状态 |
基础定时器最常用的函数就是 HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
当函数使用定时器计数模式后需要在对应的定时器后面加上开启定时器的代码,使用CubeMX配置完会帮助你初始化,但不会帮你开启定时器,一定要手动开启定时器
我们先配置好时钟与工程管理,如果不会配置请去看我第一篇文章
随便选择一个定时器,定时器时钟选择内部时钟
Clock Source(时钟来源)
这两个为定时器主从模式配置,很少用到,我们用不到,所以全部关闭
接下来轮到配置我们的定时器参数了
Prtscaler (定时器分频系数) : PSC
Counter Mode(计数模式) Up(向上计数模式)
Counter Period(自动重装载值) : ARR
CKD(时钟分频因子) : No Division 不分频
选项: 可以选择二分频和四分频
auto-reload-preload(自动重装载) : Enable 使能
下面可以不用管了,默认即可
TRGO Parameters 触发输出 (TRGO) 不使能
TRGO: 定时器的触发信号输出 在定时器的定时时间到达的时候输出一个信号(如:定时器更新产生TRGO信号来触发ADC的同步转换,)
接下来我们可以计算一下定时器溢出的时间
这里 arr 和 psc 就是我们刚才设置的 自动重装载值 和 定时器分频系数 除以 TCLK 就是我们时钟中设置的这个东西 就是72MHZ
这里我们将PSC设置为719 而ARR设置成999 则我们的溢出时间为
Tout = (720*1000)/72M = 0.01s = 10ms,接下来不要忘记将定时器中断使能
然后我们就可以使用STM32CubeMX生成10ms产生一次中断的定时器了
在我们打开工程后,定时器不是立马就可以使用的,如果从底层来分析都是通过中断服务函数 HAL_TIM_IRQHandler() 来判断是否产生中断然后进入相应的中断回调函数void HAL_TIM_PeriodElapsedCallback(),中断服务函数在初次生成代码中已经被编写在头文件stm32f1xx_hal_it.c中,我们用户需要做的只有编写回调函数中的代码和将定时器中断打开,底层如下
void TIM3_IRQHandler(void) 首先进入中断函数
HAL_TIM_IRQHandler(&htim3);之后进入定时器中断处理函数
判断产生的是哪一类定时器中断(溢出中断/PWM中断.....) 和定时器通道
void HAL_TIM_PeriodElapsedCallback(&htim3); 进入相对应中断回调函数
在中断回调函数中添加用户代码
而我们生成工程后,会有tim.c文件在用户的user目录中我们先进入文件将定时器中断打开,这些代码均为CubeMX生成,唯独有注释那句,是用户自己写入代码沙箱中的,这个函数他会在主程序中被调用从而进行初始化
- /* TIM3 init function */
- void MX_TIM3_Init(void)
- {
-
- /* USER CODE BEGIN TIM3_Init 0 */
-
- /* USER CODE END TIM3_Init 0 */
-
- TIM_ClockConfigTypeDef sClockSourceConfig = {0};
- TIM_MasterConfigTypeDef sMasterConfig = {0};
-
- /* USER CODE BEGIN TIM3_Init 1 */
-
- /* USER CODE END TIM3_Init 1 */
- htim3.Instance = TIM3;
- htim3.Init.Prescaler = 719;
- htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
- htim3.Init.Period = 999;
- htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
- htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
- if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
- {
- Error_Handler();
- }
- sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
- if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
- {
- Error_Handler();
- }
- sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
- sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
- if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
- {
- Error_Handler();
- }
- /* USER CODE BEGIN TIM3_Init 2 */
-
- HAL_TIM_Base_Start_IT(&htim3); /* 使能定时器更新中断 */
-
- /* USER CODE END TIM3_Init 2 */
-
- }
我们来简单编写一段使用定时器功能的代码
- /**
- * @brief 基础定时中断回调函数(所有控制代码在此处)
- * @param htim:定时器句柄指针
- * @note 该函数在HAL_TIM_IRQHandler中会被调用
- * @retval 无
- */
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
- {
- if (htim->Instance == TIM6) //TIM6周期为500ms
- {
- HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5) //让LED翻转
- }
- else if (htim == (&htim7)) //两种判断形式均可 TIM7周期1000ms
- {
- HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_6) //让LED翻转
- }
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。