定时器 (Timer) 最基本的功能就是定时了,比如定时发送 USART 数据、定时采集 AD 数据等等。 如果把定时器与 GPIO 结合起来使用的话可以实现非常丰富的功能,可以测量输入信号的脉冲宽 度,可以生产输出波形。定时器生产 PWM 控制电机状态是工业控制普遍方法,这方面知识非常 有必要深入了解。
tim.h和tim.cpp文件的使用
tim.h
#pragma once
#include "stm32f4xx_hal.h"
#include "functional"
#include "gpio.h"
enum{ BASE, PWM, IC };
class TIM
{
public:
TIM& Init(uint32_t Mode, TIM_TypeDef* TIM, uint32_t frequency);
void MspPostInit(reuse r) const;
void BaseInit(void);
TIM& PWMInit(uint32_t channel, float duty, reuse r);
void PWMDuty(uint32_t channel, float duty) const;
void ICInit(int32_t channel, uint32_t ICPolarity, reuse r);
bool operator==(const TIM_HandleTypeDef *htim)
{
return (&this->htim) == htim;
}
TIM_HandleTypeDef htim;
uint32_t counter;
};
extern TIM tasks, fraction, photogate;
tim.cpp
#include "tim.h"
#include "can.h"
#include "IMU.h"
#include "motor.h"
#include "control.h"
#include "RC.h"
#include "LED.h"
#include "judgement.h"
#include "powerlimit.h"
TIM_HandleTypeDef *phtim[4];
TIM& TIM::Init(const uint32_t Mode, TIM_TypeDef* TIM, const uint32_t frequency)
{
const float x = 84000000 / frequency;
if (x >= 5000)
{
htim.Init.Prescaler = x / 5000 - 1u;
htim.Init.Period = 5000 - 1u;
}
else
{
htim.Init.Prescaler = 84u - 1u;
htim.Init.Period = x / 84 - 1u;
}
htim.Instance = TIM;
htim.Init.CounterMode = TIM_COUNTERMODE_UP;
htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
switch (Mode)
{
case BASE:HAL_TIM_Base_Init(&htim); break;
case PWM:HAL_TIM_PWM_Init(&htim); break;
case IC:HAL_TIM_IC_Init(&htim); break;
default:;
}
if (TIM == TIM2)phtim[1] = &htim;
if (TIM == TIM3)phtim[2] = &htim;
if (TIM == TIM4)phtim[3] = &htim;
return *this;
}
void TIM::MspPostInit(const reuse r) const
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_CLK_ENABLE(r.GPIOx);
GPIO_InitStruct.Pin = r.Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = this->htim.Instance == TIM2 ? GPIO_AF1_TIM2 : GPIO_AF2_TIM3;
HAL_GPIO_Init(r.GPIOx, &GPIO_InitStruct);
}
extern "C" void TIM2_IRQHandler(void)
{
if (phtim[1])HAL_TIM_IRQHandler(phtim[1]);
}
extern "C" void TIM3_IRQHandler(void)
{
if (phtim[2])HAL_TIM_IRQHandler(phtim[2]);
}
extern "C" void TIM4_IRQHandler(void)
{
if (phtim[3])HAL_TIM_IRQHandler(phtim[3]);
}
void TIM::BaseInit(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);
HAL_TIM_Base_Start_IT(&htim);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
SET_BIT(RCC->APB1ENR, 0x1U << (((reinterpret_cast<unsigned>(tim_baseHandle->Instance) - APB1PERIPH_BASE)) / 0x400U));
/* Delay after an RCC peripheral clock enabling */
__IO uint32_t tmpreg = READ_BIT(RCC->APB1ENR, 0x1U << (((reinterpret_cast<unsigned>(tim_baseHandle->Instance) - APB1PERIPH_BASE)) / 0x400U));
UNUSED(tmpreg);
HAL_NVIC_SetPriority(static_cast<IRQn_Type>((reinterpret_cast<unsigned>(tim_baseHandle->Instance) - APB1PERIPH_BASE) / 0x400U + 28), 0, 0);
HAL_NVIC_EnableIRQ(static_cast<IRQn_Type>((reinterpret_cast<unsigned>(tim_baseHandle->Instance) - APB1PERIPH_BASE) / 0x400U + 28));
}
extern "C" void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint8_t can1_data[16], can2_data[16];
if (tasks.counter++, tasks == htim)
{
//在这里加入机器人需要执行的任务(update)
}
if (tasks.counter % 4 == 0)
{
for (auto &motor : can1_motor)motor.Ontimer(can1.data, can1_data);
for (auto &motor : can2_motor)motor.Ontimer(can2.data, can2_data);
}
//can通讯电机输出
switch (tasks.counter % 5)
{
case 0:can1.Transmit(0x200, can1_data); break;
case 1:can1.Transmit(0x1ff, can1_data + 8); break;
case 2:can1.Transmit(0x200, can1_data); break;//此处是为了增大底盘输出的频率
case 3:can2.Transmit(0x200, can2_data); break;
case 4:can2.Transmit(0x1ff, can2_data + 8); break;
default:;
}
}
TIM& TIM::PWMInit(uint32_t channel, float duty, reuse r)
{
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = static_cast<unsigned>(duty * (htim.Init.Period + 1)) - 1u;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, channel);
this->MspPostInit(r);
HAL_TIM_PWM_Start(&htim, channel);
return *this;
}
void TIM::PWMDuty(const uint32_t channel, const float duty) const
{
*reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(htim.Instance->CCR1) + (channel - TIM_CHANNEL_1)) = (htim.Init.Period + 1)*duty - 1u;
}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle)
{
__IO uint32_t tmpreg = 0x00U;
SET_BIT(RCC->APB1ENR, 0x1U << (((reinterpret_cast<unsigned>(tim_pwmHandle->Instance) - APB1PERIPH_BASE)) / 0x400U));
/* Delay after an RCC peripheral clock enabling */
tmpreg = READ_BIT(RCC->APB1ENR, 0x1U << ((reinterpret_cast<unsigned>(tim_pwmHandle->Instance - APB1PERIPH_BASE)) / 0x400U));
UNUSED(tmpreg);
}
void TIM::ICInit(int32_t channel, uint32_t ICPolarity, reuse r)
{
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;
this->MspPostInit(r);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);
sConfigIC.ICPolarity = ICPolarity;//TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htim, &sConfigIC, channel);
HAL_TIM_IC_Start_IT(&htim, channel);
}
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef* tim_icHandle)
{
SET_BIT(RCC->APB1ENR, 0x1U << ((reinterpret_cast<unsigned>(tim_icHandle->Instance - APB1PERIPH_BASE)) / 0x400U));
/* Delay after an RCC peripheral clock enabling */
__IO uint32_t tmpreg = READ_BIT(RCC->APB1ENR, 0x1U << ((reinterpret_cast<unsigned>(tim_icHandle->Instance - APB1PERIPH_BASE)) / 0x400U));
UNUSED(tmpreg);
HAL_NVIC_SetPriority(static_cast<IRQn_Type>(reinterpret_cast<unsigned>(tim_icHandle->Instance - APB1PERIPH_BASE) / 0x400U + 28), 0, 0);
HAL_NVIC_EnableIRQ(static_cast<IRQn_Type>(reinterpret_cast<unsigned>(tim_icHandle->Instance - APB1PERIPH_BASE) / 0x400U + 28));
}
extern "C" void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM4)
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
//HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_1);
if (!(photogate.counter++ & 1))
;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(htim, &sMasterConfig);
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(htim, &sConfigIC, htim->Channel);
HAL_TIM_IC_Start_IT(htim, htim->Channel);
}
}
}
将我们的项目文件添加如上两个文件,文件中包括TIM定时器的使用和设置,可以更方便我们使用定时器功能来进行机器人复杂功能代码的编写。