基本计时功能
最简单的,定时器嘛,基本的定时器就是定时功能,简单来说就是TIMx->CNT会跟随着输入时钟的脉冲而计数。
初始化定时器的参数,大家都好理解,因为TIM2的输入时钟是108Mhz,这里进行10800分频,输入频率为10K,重装载值设置为20K,每2秒溢出一次。
在HAL_TIM_Base_Init的执行过程中,会先调用HAL_TIM_Base_MspInit再进行其他参数的配置,即先开时钟。
TIM_HandleTypeDef TIM2_Handler;
static void MX_TIM2_Init(void)
{
TIM2_Handler.Instance = TIM2;
TIM2_Handler.Init.Prescaler = 10800;
TIM2_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;
TIM2_Handler.Init.Period = 20000;
TIM2_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&TIM2_Handler);
HAL_TIM_Base_Start(&TIM2_Handler);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
__HAL_RCC_TIM2_CLK_ENABLE(); //使能TIM3时钟
}
主函数中每秒打印一次定时器的值:
while (1)
{
printf("cnt:%drn",TIM2->CNT);
delay_ms(1000);
}
显示效果如下:
通过HAL_TIM_Base_Start可以开启基本计时功能,但要实现定时器中断功能,就需要开启相应的标志位,即使用HAL_TIM_Base_Start_IT进行定时的开启;
在配置定时器之前,除了要开启时钟,还需要先设置中断优先级,和使能中断向量;
在定时器中断TIM2_IRQHandler服务函数中调用HAL库提供的定时器中断处理函数HAL_TIM_IRQHandler,解析到的定时器超时中断会自动跳转到HAL_TIM_PeriodElapsedCallback;
TIM_HandleTypeDef TIM2_Handler;
static void MX_TIM2_Init(void)
{
TIM2_Handler.Instance = TIM2;
TIM2_Handler.Init.Prescaler = 10800;
TIM2_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;
TIM2_Handler.Init.Period = 20000;
TIM2_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&TIM2_Handler);
//HAL_TIM_Base_Start(&TIM2_Handler);
HAL_TIM_Base_Start_IT(&TIM2_Handler);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
__HAL_RCC_TIM2_CLK_ENABLE(); //使能TIM2时钟
HAL_NVIC_SetPriority(TIM2_IRQn, 1, 3); //设置中断优先级,抢占优先级1,子优先级3
HAL_NVIC_EnableIRQ(TIM2_IRQn); //开启ITM2中断
}
void TIM2_IRQHandler(void)
{
HAL_TIM_IRQHandler(&TIM2_Handler);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == (&TIM2_Handler))
{
printf("enter irqrn");
}
}
显示效果如下:
PWM输出
硬件PWM输出是不需要使用定时器中断的,但同样需要基本的定时参数配置,初始化也不再是使用HAL_TIM_Base_Init而是使用HAL_TIM_PWM_Init进行初始化了;
配置完定时器为PWM模式,那么相应的输出通道也需要通过HAL_TIM_PWM_ConfigChannel进行配置;
同样的启动也不是HAL_TIM_Base_Start或者HAL_TIM_Base_Start_IT了,而是HAL_TIM_PWM_Start了;
TIM_HandleTypeDef TIM2_Handler;
TIM_OC_InitTypeDef TIM2_CH2Handler;
static void MX_TIM2_Init(void)
{
TIM2_Handler.Instance = TIM2;
TIM2_Handler.Init.Prescaler = 10800;
TIM2_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;
TIM2_Handler.Init.Period = 20000;
TIM2_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&TIM2_Handler);
TIM2_CH2Handler.OCMode = TIM_OCMODE_PWM1;
TIM2_CH2Handler.Pulse = 10000;
TIM2_CH2Handler.OCPolarity = TIM_OCPOLARITY_HIGH;
TIM2_CH2Handler.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&TIM2_Handler, &TIM2_CH2Handler, TIM_CHANNEL_2) ;
HAL_TIM_PWM_Start(&TIM2_Handler, TIM_CHANNEL_2); //开启PWM通道2
}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_TIM2_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
通过万用表可以发现PA1口的电压在0和3.3V中大概每秒变动一次(疫情期间在家学习,没有示波器真是太惨了……为了确定确实有PWM波形,只能出此下策了T_T),串口打印数据如下:
PWM + 定时器溢出中断
我们都知道,PWM模式是在比较值处翻转,在溢出的时候再次翻转,而溢出的时候,我们也是可以产生中断的;所以同一个定时器,在这种定时时长和周期相同的时候,是可以即做硬件PWM输出,又做溢出中断的。
这里我们观察一下两种初始化函数:
/**
* @brief Initializes the TIM Time base Unit according to the specified
* parameters in the TIM_HandleTypeDef and create the associated handle.
* @param htim: pointer to a TIM_HandleTypeDef structure that contains
* the configuration information for TIM module.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_TIM_Base_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));
if(htim->State == HAL_TIM_STATE_RESET)
{
/* Init the low level hardware : GPIO, CLOCK, NVIC */
HAL_TIM_Base_MspInit(htim);
}
/* Set the TIM state */
htim->State= HAL_TIM_STATE_BUSY;
/* Set the Time Base configuration */
TIM_Base_SetConfig(htim->Instance, &htim->Init);
/* Initialize the TIM state*/
htim->State= HAL_TIM_STATE_READY;
return HAL_OK;
}
HAL_StatusTypeDef HAL_TIM_PWM_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));
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_PWM_MspInit(htim);
}
/* Set the TIM state */
htim->State= HAL_TIM_STATE_BUSY;
/* Init the base time for the PWM */
TIM_Base_SetConfig(htim->Instance, &htim->Init);
/* Initialize the TIM state*/
htim->State= HAL_TIM_STATE_READY;
return HAL_OK;
}
两者除了底层的HAL_TIM_Base_MspInit和HAL_TIM_PWM_MspInit,其他地方一模一样,所以我们随便调用一个初始化就可以了,但是要注意,我们在HAL_TIM_Base_MspInit里配置了NVIC,在HAL_TIM_PWM_MspInit里配置了GPIO,所以只调用一个的话,必须手动把底层的粘贴到另一个函数里去,如果是想两个都调用一次,这里就需要考虑htim->State这个变量了,因为在调用了一次之后,其值就从HAL_TIM_STATE_RESET变成了HAL_TIM_STATE_READY,后面的一个就得不到执行了。
再来看启动代码:
/**
* @brief Starts the TIM Base generation.
* @param htim: pointer to a TIM_HandleTypeDef structure that contains
* the configuration information for TIM module.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim)
{
/* Check the parameters */
assert_param(IS_TIM_INSTANCE(htim->Instance));
/* Set the TIM state */
htim->State= HAL_TIM_STATE_BUSY;
/* Enable the Peripheral */
__HAL_TIM_ENABLE(htim);
/* Change the TIM state*/
htim->State= HAL_TIM_STATE_READY;
/* Return function status */
return HAL_OK;
}
/**
* @brief Starts the TIM Base generation in interrupt mode.
* @param htim: pointer to a TIM_HandleTypeDef structure that contains
* the configuration information for TIM module.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
{
/* Check the parameters */
assert_param(IS_TIM_INSTANCE(htim->Instance));
/* Enable the TIM Update interrupt */
__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
/* Enable the Peripheral */
__HAL_TIM_ENABLE(htim);
/* Return function status */
return HAL_OK;
}
/**
* @brief Starts the PWM signal generation.
* @param htim: pointer to a TIM_HandleTypeDef structure that contains
* the configuration information for TIM module.
* @param Channel: TIM Channels to be enabled.
* This parameter can be one of the following values:
* @arg TIM_CHANNEL_1: TIM Channel 1 selected
* @arg TIM_CHANNEL_2: TIM Channel 2 selected
* @arg TIM_CHANNEL_3: TIM Channel 3 selected
* @arg TIM_CHANNEL_4: TIM Channel 4 selected
* @retval HAL status
*/
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
{
/* Check the parameters */
assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));
/* Enable the Capture compare channel */
TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
if(IS_TIM_ADVANCED_INSTANCE(htim->Instance) != RESET)
{
/* Enable the main output */
__HAL_TIM_MOE_ENABLE(htim);
}
/* Enable the Peripheral */
__HAL_TIM_ENABLE(htim);
/* Return function status */
return HAL_OK;
}
其中的核心代码分别是:
__HAL_TIM_ENABLE(htim);
__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
__HAL_TIM_ENABLE(htim);
TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
__HAL_TIM_ENABLE(htim);
那么我们可以不用hal库提供给我们的启动函数,直接写三句话,就实现了定时器中断+PWM功能了。
__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
上一篇:STM32F7实现ADC等周期采集(定时器触发+DMA传输)采集完成后的中断设置
下一篇:CubeMX 5.5 修改HAL库库函数版本
推荐阅读最新更新时间:2024-11-20 11:48
设计资源 培训 开发板 精华推荐
- 使用 Analog Devices 的 ADGM1004 的参考设计
- 具有可编程电流限制的 LT3091IT7 线性稳压器的典型应用
- LT1072HVCT 升压型升压转换器的典型应用
- 用于有线网络的 3.3V DC 到 DC 单路输出电源
- LTC4089EDJC 演示板,DFN 6X3 中的 USB 电源解决方案
- LT1086CM-3.3 高效率双电源的典型应用
- 典型应用显示使用 A8510 LED 驱动器的 P-MOSFET 感应进行 VIN 到 GND 短路保护
- 具有可编程时序的 ADM1186-2ARQZ 升序和降序器和监视器的典型应用电路
- memory-lcd-wing:用于OrangeCrab的最小翼
- #第五届立创电子设计大赛#万能红外遥控手表