cancel
Showing results for 
Search instead for 
Did you mean: 

Start PWM with DMA (HAL_TIM_PWM_Start_DMA) results in Hardfault

Geo En
Associate III

Hello,

I want to create a simple PWM with changing duty cycle. To do so, I have created a PWM with fixed frequency. With the DMA, I change pulses which will result in a modification of duty cycle.

To create something clean, I have configured STM32CubeMX and generated fresh files (configuration in attached documents below).

If I test my PWM with fixed frequency and fixed duty cycle, everything is working (without DMA). I have not modified files, just added this line to start the PWM:

HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_4);

Result as expected:

0690X000006CyCKQA0.png

Now I want to do the same thing with the DMA. So I have created a buffer with 2 arrays, with my 2 pulses. And I use it like that:

uint32_t buffer[2] = {42, 21};  // 42 is 50% duty cycle for my config, 21 is 25%
[...]
// At the end of timer4 init
HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_4, buffer, 2);
[...]

And here is the problem:

0690X000006CyCPQA0.png

More particularly, it seems to crash at this moment:

/* Set the DMA error callback */
htim->hdma[TIM_DMA_ID_CC4]->XferErrorCallback = TIM_DMAError ;

Maybe I am missing to do something...

Any help would be appreciated. Thanks!

More information: I am using a stm32f103. As you can see in attached files, the last release of stm32CubeMx and the last version of F1 library : V1.7.0

I have looked carefully to provided examples (TIM_DMA, etc...) and I always reach the same result.

Quick access to some parts of the code:

main.c :

int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USB_PCD_Init();
  MX_TIM4_Init();
 
  /* Infinite loop */
  while (1)
  {
  }
}

time.c:

uint32_t buffer[2] = {42, 21};
 
TIM_HandleTypeDef htim4;
DMA_HandleTypeDef hdma_tim4_up;
 
/* TIM4 init function */
void MX_TIM4_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
 
  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 0;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 84;
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 42;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_TIM_MspPostInit(&htim4);
 
  //HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_4);
    HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_4, buffer, 2);
 
}
 
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
 
  if(tim_baseHandle->Instance==TIM4)
  {
    /* TIM4 clock enable */
    __HAL_RCC_TIM4_CLK_ENABLE();
  
    /* TIM4 DMA Init */
    /* TIM4_UP Init */
    hdma_tim4_up.Instance = DMA1_Channel7;
    hdma_tim4_up.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_tim4_up.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_tim4_up.Init.MemInc = DMA_MINC_ENABLE;
    hdma_tim4_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_tim4_up.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_tim4_up.Init.Mode = DMA_NORMAL;
    hdma_tim4_up.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    if (HAL_DMA_Init(&hdma_tim4_up) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(tim_baseHandle,hdma[TIM_DMA_ID_UPDATE],hdma_tim4_up);
  }
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
 
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(timHandle->Instance==TIM4)
  {  
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**TIM4 GPIO Configuration    
    PB9     ------> TIM4_CH4 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  }
 
}

dma.c

void MX_DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA1_Channel7_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
 
}

stm32f1xx_it.c

void DMA1_Channel7_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_tim4_up);
}

10 REPLIES 10
parth kothiya
Senior

see your external crystal frequency is setup correctly if it is wrong then hard fault occurs in MCU.

Thanks for you answer, but my problem does not seem to be related to any crystal frequency error as everything is working correctly except when I call the DMA.

My crystal seems to work correctly

0690X000006CyDrQAK.png

And STM32CubeMx is set correctly:

0690X000006CyDwQAK.png

S.Ma
Principal

Your need is to have PWM which when the percentage is changes should be taken into effect when the timer overflow back to 0x0000 ? If yes, this is programmable with the timer options without the need of a DMA.

I'm really not sure how the DMA will perform in this case. When and how the percentage change is triggered?

Likely missing an interrupt handler.

Consider also stack size, and errant pointers.

Use a better fault handler, and look at the faulting instructions/registers.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
VDELA.1
Associate

Hi Geo En

I faced exactly the same issue on BluePill STM32F103CB, when trying to generate a sawtooth on PB9 (Timer 4 CH4 PWM) for ADC dithering (but that's another story).

From my understanding, CubeMX generates wrong code line 76 of tim.c

It is

  1. __HAL_LINKDMA(tim_baseHandle,hdma[TIM_DMA_ID_UPDATE],hdma_tim4_up);

whereas it should be:

  __HAL_LINKDMA(tim_baseHandle,hdma[TIM_DMA_ID_CC4],hdma_tim4_up);

Once this line manually fixed, the bus fault disappears.

But now i'm stuck there, I cannot get my End of DMA interrupt.

Hope this helps.

Vincent

VDELA.1
Associate

Hey!

I gave up... I switched from channel 4 to channel 3.

Something is not clear, in stm32cubeMx: For channel 4 we have only TIM4_UP for DMA request. But for channel 3, we have TIM4_UP and TIM4_CH3 (we need to use this one). Most of the time, stm32cubeMx is really good to work quickly. But in cases like this one, I have read a big part of the datasheet and the library to understand the problem... If I need to do this, I can configure registers myself so I don't need a wizard ... Maybe one day, there will be some descriptions in stm32cubeMx and not only drop down menus with no sense ...

Yep, this is exactly the problem. We don't set the good pointer. So the library use a pointer = NULL and try to execute a function = NULL...

The problem is: we don't have the possibility to use the good pointer in stm32cubeMx. Maybe because that's impossible physically, or maybe because I have made a mistake, or maybe it is a bug in cubemx. Who knows? as there is no description at all in this wizard...

Dalmo Lima
Associate II

... may be a too late but likely this will fix the error:

  1. uint16_t buffer[2] = {42, 21}; // 42 is 50% duty cycle for my config, 21 is 25%
  2. [...]
  3. // At the end of timer4 init
  4. HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_4, (uint32_t*) buffer, 2);
  5. [...]

It wants a 32 bit pointer to match the register the data has to be 16 bit to match the counter. I hope it helps.