1
\$\begingroup\$

I use the Blue pill STM32F103C8T6 to generate variable duty cycle PWM with DMA and using HAL library. And stuck with undesired results. First, I generated code with STM32CubeMX v5.0.1, HAL v1.7.0, with TIM1 configured to generate PWM on channel 1 with DMA (internal clock, prescaler 71, period 999), and with LED at pin C13 for debugging. The board runs at 72MHz.

Timer initialization:

static void MX_TIM1_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 71;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 999;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 1;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
  sBreakDeadTimeConfig.DeadTime = 0;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_TIM_MspPostInit(&htim1);
}

DMA initialization:

static void MX_DMA_Init(void) 
{
  __HAL_RCC_DMA1_CLK_ENABLE();

  HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
}

DMA interrupt:

void DMA1_Channel2_IRQHandler(void)
{
  HAL_TIM_PWM_Stop_DMA(&htim1, TIM_CHANNEL_1);
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
  HAL_DMA_IRQHandler(&hdma_tim1_ch1);
}

main():

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_DMA_Init();
  MX_TIM1_Init();
  MX_GPIO_Init();

  uint32_t pData[] = {300, 400, 500, 600, 700};

  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
  HAL_TIM_PWM_Start_DMA(&htim1,TIM_CHANNEL_1, pData, 5);

  while (1)
  {
    if (HAL_TIM_PWM_GetState(&htim1) == HAL_TIM_STATE_BUSY) {
          continue;
    }
    HAL_Delay(20);
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
    HAL_TIM_PWM_Start_DMA(&htim1,TIM_CHANNEL_1, pData, 5);
  }
}

Duty + Idle cycle takes 1ms. Transfer of 5 values lasts for 5 ms. Expecting to have 5 full cycles (duty/idle): 300/700, 400/600, 500/500, 600/400, 700/300. I repeat transfers with pause of ~20ms. Before each transfer C13 is changed to high and after transfer is complete to low (the second channel on analyzer).

There are several problems and possible workarounds (or my mistakes):

enter image description here

First transfer is delayed by one empty cycle in the beginning and the last cycle 700/300 is missing.

WORKAROUND: fill CCR1 (or Pulse value during init) register with pData[0] (partly described in HAL examples), pass &pData[1] to HAL_TIM_PWM_Start_DMA (not stated in HAL examples) and keep length at 5.

enter image description here

The last idle cycle is cut in first transfer and occurred in the beginning of the subsequent transfer. The second transfer starts from idle cycle, left after duty cycle of previous transfer.

WORKAROUND: add extra value (pData[5] = 0) and pass 6 as length to HAL_TIM_PWM_Start_DMA. Then reset CR1 and set EGR registers before each call to HAL_TIM_PWM_Start_DMA.

Eventually I got this:

enter image description here

Is it possible to achieve desired results with HAL without my workarounds and how?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.