STM32CubeMX可以很方便配置TIM,但总感觉还是得自己手敲一遍TIM的配置代码才感觉自己掌握了TIM。
使用单片机:STM32G474,配置TIM16,得到一个50us的定时中断。
首先,确保stm32g4xx_hal_conf.h文件中的宏定义:#define HAL_TIM_MODULE_ENABLED
是否取消了注释,这里不取消注释将可能提示无法找到TIM_HandleTypeDef的错误
一般会在stm32cubemx生成的Core/Inc目录下创建tim.h文件,在Core/Src目录下创建tim.c文件
以下是tim.h文件中的内容
//tim.h文件下的内容
#ifndef ProjectName_TIM_H
#define ProjectName_TIM_H
#include "main.h" //取消上文提到的宏定义注释后,main.h就会包含用于初始化定时器的头文件
extern TIM_HandleTypeDef htim16; //对于全局变量,我们在tim.c文件中声明。在tim.h文件中extern一下
void TIM16_Init(void); //声明TIM16的初始化函数
#endif
在tim.c文件中配置TIM16
//以下是tim.c文件中的代码
#include "tim.h" //包含创建的tim.h文件
//声明配置定时器的结构体
TIM_HandleTypeDef htim16;
/*
* @brief TIM16的初始化函数,让TIM16产生50us的定时中断 f = 20KHz,暂定PSC = 17-1 ARR = 500-1
* @note AHPB1和AHPB2的总线时钟频率均为170MHz*/
void TIM16_Init(void) {
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim16.Instance = TIM16;
htim16.Init.Prescaler = 17-1; //PSC
htim16.Init.Period = 500-1; //计数周期,理解成ARR
htim16.Init.AutoReloadPreload = TIM_AUTOMATICOUTPUT_DISABLE; //ARR
htim16.Init.CounterMode = TIM_COUNTERMODE_UP; //计数模式:向上计数
htim16.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; //时钟不分频
if(HAL_TIM_Base_Init(&htim16) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; //TRGO信号源
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if(HAL_TIMEx_MasterConfigSynchronization(&htim16,&sMasterConfig) != HAL_OK) {
Error_Handler();
}
}
/*
* @brief 此函数在HAL_TIM_Base_Init()函数中被调用,用于时钟使能和中断优先级设置*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *tim_baseHandle) {
if(tim_baseHandle -> Instance == TIM16) {
__HAL_RCC_TIM16_CLK_ENABLE(); //使能TIM16
/*
* @brief 初始化TIM16定时中断
* @note 要想在TIM16的中断回调函数中调用freeRTOS相关函数,中断优先级的值应大于等于5*/
HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn,5,0);
HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);
}
}
注意:要编写void TIM16_Init(void); 和 void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *tim_baseHandle);两个函数
对于TIM16的定时中断,需要去到.s启动文件:startup_stm32g474xx.s中以TIM16为关键词进行查询,可以找到这样一行代码:
这样,就知道了TIM16的中断函数名称
去到Core/Src/stm32g4xx_it.c文件中
首先extern TIM_HandleTypeDef htim16;
再写上TIM16的中断函数
//stm32g4xx_it.c应该添加的内容
//首先extern一下全局变量htim16
extern TIM_HandleTypeDef htim16;
//然后找一个合适位置写上TIM16的中断函数名称(从.s启动文件中查询,千万别把名字写错了)
void TIM_UP_TIM16_IRQHandler(void) {
//写上TIM的服务子函数,并传递&htim16的参数
HAL_TIM_IRQHandler(&htim16);
}
使用编译器的函数跳转功能,按住ctrl建,点击函数:HAL_TIM_IRQHandler(&htim16);
会跳转到stm32g4xx_hal_tim.c文件中,以__weak为关键词进行搜索,可以找到一下代码
第一个就是定时器的中断回调函数,根据上面TIM16的配置可知,该函数将会每隔50us执行一次
找个风水宝地重定义第一个函数
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM17 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM17) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
//定时器中断回调函数,TIM16会每隔50us触发一次
if (htim->Instance == TIM16) {
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_0);
}
/* USER CODE END Callback 1 */
}
由于,每个定时器中断都会进入该函数,因此需要判断是不是因为TIM16而引起的中断
如果一切顺利的话,启动定时器,会在PA0端口得到一个周期为100us的方波
在main.c文件中新增一下代码:
//main.c文件中应该新增的代码
#include "tim.h"
int main(){
TIM16_Init(); //配置TIM16
HAL_TIM_Base_Start_IT(&htim16); //以中断方式启动定时器TIM16
}
使用示波器抓取PA0端口的波形:
从波形来看,TIM16的配置正确,代码编写无误。