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];
posted @ 2019-08-30 15:58  berry_chen  阅读(3781)  评论(1编辑  收藏  举报