项目中要控制一个步进电机控制器,因为涉及到加减速过程,需要频率任意可变。
总体思路是先初始化PWM定时器输出,之后直接修改ARR和PSC寄存器。
初始化代码如下:
/*
*********************************************************************************************************
* 函 数 名: bsp_SetTIMOutPWM
* 功能说明: 设置引脚输出的PWM信号的频率和占空比. 当频率为0,并且占空为0时,关闭定时器,GPIO输出0;
* 当频率为0,占空比为100%时,GPIO输出1.
* 形 参: GPIOx : GPIOA - GPIOK
* GPIO_Pin : GPIO_PIN_0 - GPIO__PIN_15
* TIMx : TIM1 - TIM17
* _ucChannel:使用的定时器通道,范围1 - 4
* _ulFreq : PWM信号频率,单位Hz (实际测试,可以输出100MHz),0 表示禁止输出
* _ulDutyCycle : PWM信号占空比,单位: 万分之一。如5000,表示50.00%的占空比
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_SetTIMOutPWM(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, TIM_TypeDef* TIMx, uint8_t _ucChannel,
uint32_t _ulFreq, uint32_t _ulDutyCycle)
{
TIM_HandleTypeDef TimHandle = {0};
TIM_OC_InitTypeDef sConfig = {0};
uint16_t usPeriod;
uint16_t usPrescaler;
uint32_t pulse;
uint32_t uiTIMxCLK;
const uint16_t TimChannel[6+1] = {0, TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3, TIM_CHANNEL_4};
if (_ucChannel > 6)
{
// Error_Handler(__FILE__, __LINE__);
}
if (_ulDutyCycle == 0)
{
//bsp_RCC_TIM_Disable(TIMx); /* 关闭TIM时钟, 可能影响其他通道 */
bsp_ConfigGpioOut(GPIOx, GPIO_Pin); /* 配置GPIO为推挽输出 */
HAL_GPIO_WritePin(GPIOx, GPIO_Pin, GPIO_PIN_RESET); /* PWM = 0 */
return;
}
else if (_ulDutyCycle == 10000)
{
//bsp_RCC_TIM_Disable(TIMx); /* 关闭TIM时钟, 可能影响其他通道 */
bsp_ConfigGpioOut(GPIOx, GPIO_Pin); /* 配置GPIO为推挽输出 */
HAL_GPIO_WritePin(GPIOx, GPIO_Pin, GPIO_PIN_SET); /* PWM = 1 */
return;
}
/* 下面是PWM输出 */
bsp_ConfigTimGpio(GPIOx, GPIO_Pin, TIMx); /* 使能GPIO和TIM时钟,并连接TIM通道到GPIO */
/*-----------------------------------------------------------------------
system_stm32f4xx.c 文件中 void SetSysClock(void) 函数对时钟的配置如下:
HCLK = SYSCLK / 1 (AHB1Periph)
PCLK2 = HCLK / 2 (APB2Periph)
PCLK1 = HCLK / 4 (APB1Periph)
因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = PCLK1 x 2 = SystemCoreClock / 2;
因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = PCLK2 x 2 = SystemCoreClock;
APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13,TIM14
APB2 定时器有 TIM1, TIM8 ,TIM9, TIM10, TIM11
----------------------------------------------------------------------- */
if ((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM9) || (TIMx == TIM10) || (TIMx == TIM11))
{
/* APB2 定时器时钟 = 168M */
uiTIMxCLK = SystemCoreClock;
}
else
{
/* APB1 定时器 = 84M */
uiTIMxCLK = SystemCoreClock / 2;
}
if (_ulFreq < 100)
{
usPrescaler = 10000 - 1; /* 分频比 = 10000 */
usPeriod = (uiTIMxCLK / 10000) / _ulFreq - 1; /* 自动重装的值 */
}
else if (_ulFreq < 3000)
{
usPrescaler = 100 - 1; /* 分频比 = 100 */
usPeriod = (uiTIMxCLK / 100) / _ulFreq - 1; /* 自动重装的值 */
}
else /* 大于4K的频率,无需分频 */
{
usPrescaler = 0; /* 分频比 = 1 */
usPeriod = uiTIMxCLK / _ulFreq - 1; /* 自动重装的值 */
}
pulse = (_ulDutyCycle * usPeriod) / 10000;
HAL_TIM_PWM_DeInit(&TimHandle);
/* PWM频率 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)*/
TimHandle.Instance = TIMx;
TimHandle.Init.Prescaler = usPrescaler;
TimHandle.Init.Period = usPeriod;
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = 0;
if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK)
{
// Error_Handler(__FILE__, __LINE__);
}
/* 配置定时器PWM输出通道 */
sConfig.OCMode = TIM_OCMODE_PWM1;
sConfig.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfig.OCFastMode = TIM_OCFAST_DISABLE;
sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
sConfig.OCIdleState = TIM_OCIDLESTATE_RESET;
/* 占空比 */
sConfig.Pulse = pulse;
if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TimChannel[_ucChannel]) != HAL_OK)
{
// Error_Handler(__FILE__, __LINE__);
}
/* 启动PWM输出 */
if (HAL_TIM_PWM_Start(&TimHandle, TimChannel[_ucChannel]) != HAL_OK)
{
// Error_Handler(__FILE__, __LINE__);
}
}
更新频率的函数如下:
void bsp_ModifyTIMFreqOut(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, TIM_TypeDef* TIMx, uint8_t _ucChannel,
uint32_t _ulFreq)
{
uint16_t usPeriod;
uint16_t usPrescaler;
uint32_t pulse;
uint32_t uiTIMxCLK;
const uint16_t TimChannel[6+1] = {0, TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3, TIM_CHANNEL_4};
if (_ucChannel > 6)
{
// Error_Handler(__FILE__, __LINE__);
}
if ((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM9) || (TIMx == TIM10) || (TIMx == TIM11))
{
/* APB2 定时器时钟 = 168M */
uiTIMxCLK = SystemCoreClock;
}
else
{
/* APB1 定时器 = 84M */
uiTIMxCLK = SystemCoreClock / 2;
}
_ulFreq = _ulFreq * 4;
if (_ulFreq < 100)
{
usPrescaler = 10000 - 1; /* 分频比 = 10000 */
usPeriod = (uiTIMxCLK / 10000) / _ulFreq - 1; /* 自动重装的值 */
}
else if (_ulFreq < 3000)
{
usPrescaler = 100 - 1; /* 分频比 = 100 */
usPeriod = (uiTIMxCLK / 100) / _ulFreq - 1; /* 自动重装的值 */
}
else /* 大于4K的频率,无需分频 */
{
usPrescaler = 0; /* 分频比 = 1 */
usPeriod = uiTIMxCLK / _ulFreq - 1; /* 自动重装的值 */
}
TIM8->PSC = usPrescaler;
TIM8->ARR = usPeriod;
TIM8->EGR = TIM_EGR_UG; //修改之后要更新寄存器
}
这里注意几点:
1、预分频寄存器是否修改都可以,只影响一定的定时精度
2、修改之后一定要更新EGR寄存器,修改才会生效