STM32单片机基本定时器TIM配置(HAL库版本)

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的配置正确,代码编写无误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值