STM32 PWM输入模式采用DMA方式测量频率和占空比
测试平台: STM32F030C8T6
固件库: STM32F0xx_HAL_Driver
固件库中提供了一个通过PWM输入模式测量频率和占空比的Demo,采用定时器中断实现,测量频率时中断过于频繁,虽然可以通过设置多个待测信号周期产生一次定时器中断,来降低中断频率,但是这种方式测量出来的频率和占空比会出现跳动,数据不是很稳定。
改进方案:用PWM输入模式,将IC1和IC2的获取,由定时器中断方式,改为DMA方式。可以降低频繁中断引起的系统开销,同时测量的数据更稳定。
代码中使用的宏定义如下
#define TIMx TIM1
#define TIMx_CLK_ENABLE() __HAL_RCC_TIM1_CLK_ENABLE()
#define DMAx_CLK_ENABLE __HAL_RCC_DMA1_CLK_ENABLE
#define TIMx_CHANNEL_GPIO_PORT() __HAL_RCC_GPIOA_CLK_ENABLE()
#define GPIO_PORT GPIOA
#define GPIO_PIN_CHANNEL2 GPIO_PIN_9
#define GPIO_AF_TIMx GPIO_AF2_TIM1
#define GPIO_PIN_CHANNEL1 GPIO_PIN_8
1. 配置并初始化TIM
TimHandle.Instance = TIMx;
TimHandle.Init.Period = 0xFFFF;
TimHandle.Init.Prescaler = 0;
TimHandle.Init.ClockDivision = 0;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_IC_Init(&TimHandle) != HAL_OK)
{
Error_Handler();
}
HAL_UART_Init
会调用HAL_TIM_IC_MspInit
,可在HAL_TIM_IC_MspInit
的实现中配置硬件资源。
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_InitStruct;
DMA_HandleTypeDef hdma_tim_ch1;
DMA_HandleTypeDef hdma_tim_ch2;
TIMx_CLK_ENABLE();
DMAx_CLK_ENABLE();
TIMx_CHANNEL_GPIO_PORT();
/* Configure (TIMx_Channel) in Alternate function, push-pull and High speed */
GPIO_InitStruct.Pin = GPIO_PIN_CHANNEL2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF_TIMx;
HAL_GPIO_Init(GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_CHANNEL1;
HAL_GPIO_Init(GPIO_PORT, &GPIO_InitStruct);
hdma_tim_ch1.Instance = DMA1_Channel2;
hdma_tim_ch1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_tim_ch1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim_ch1.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_tim_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_tim_ch1.Init.Mode = DMA_CIRCULAR;
hdma_tim_ch1.Init.Priority = DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&hdma_tim_ch1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(htim, hdma[1], hdma_tim_ch1);
hdma_tim_ch2.Instance = DMA1_Channel3;
hdma_tim_ch2.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_tim_ch2.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim_ch2.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim_ch2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_tim_ch2.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_tim_ch2.Init.Mode = DMA_CIRCULAR;
hdma_tim_ch2.Init.Priority = DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&hdma_tim_ch2) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(htim, hdma[2], hdma_tim_ch2);
}
2. 配置输入捕获通道
/* Common configuration */
sConfig.ICPrescaler = TIM_ICPSC_DIV1;
sConfig.ICFilter = 0;
/* Configure the Input Capture of channel 1 */
sConfig.ICPolarity = TIM_ICPOLARITY_FALLING;
sConfig.ICSelection = TIM_ICSELECTION_INDIRECTTI;
if (HAL_TIM_IC_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* Configure the Input Capture of channel 2 */
sConfig.ICPolarity = TIM_ICPOLARITY_RISING;
sConfig.ICSelection = TIM_ICSELECTION_DIRECTTI;
if (HAL_TIM_IC_ConfigChannel(&TimHandle, &sConfig, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
3. 配置从模式
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;
sSlaveConfig.InputTrigger = TIM_TS_TI2FP2;
sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_NONINVERTED;
sSlaveConfig.TriggerPrescaler = TIM_TRIGGERPRESCALER_DIV1;
sSlaveConfig.TriggerFilter = 0;
if (HAL_TIM_SlaveConfigSynchronization(&TimHandle, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
4. 启动输入捕获DMA
uint32_t PWM_IN[2]={0,0};
if(HAL_TIM_IC_Start_DMA(&TimHandle,TIM_CHANNEL_1,(uint32_t *)&PWM_IN[0],1) != HAL_OK)
{
Error_Handler();
}
/* 捕获通道1的DMA启动之后,State状态变为HAL_TIM_STATE_BUSY,修改之后,捕获通道2才能启动 */
TimHandle.State = HAL_TIM_STATE_READY;
if(HAL_TIM_IC_Start_DMA(&TimHandle,TIM_CHANNEL_2,(uint32_t *)&PWM_IN[1],1) != HAL_OK)
{
Error_Handler();
}
5. 计算频率及占空比
int freq, duty;
freq = HAL_RCC_GetHCLKFreq() / PWM_IN1[1];
duty = PWM_IN1[0] * 1000 / PWM_IN1[1];