Skip to main content
Geo En
Associate III
Jan 5, 2019 11:44 AM UTC
Question

Start PWM with DMA (HAL_TIM_PWM_Start_DMA) results in Hardfault

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);
}

This topic has been closed for replies.

10 replies

parth kothiya
Associate III
Jan 5, 2019 12:07 PM UTC

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

Geo En
Geo EnAuthor
Associate III
Jan 5, 2019 12:19 PM UTC

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
Jan 5, 2019 1:57 PM UTC

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?

Tesla DeLorean
Guru
Jan 5, 2019 2:33 PM UTC

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 (See Profile) Up vote any posts that you find helpful, it shows what's working..
VDELA.1
Visitor II
Jan 28, 2019 12:57 PM UTC

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

Geo En
Geo EnAuthor
Associate III
Jan 28, 2019 4:54 PM UTC

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 ...

VDELA.1
Visitor II
Jan 28, 2019 3:31 PM UTC
Geo En
Geo EnAuthor
Associate III
Jan 28, 2019 4:59 PM UTC

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
Visitor II
Apr 20, 2019 9:22 PM UTC

... 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.

Isidre Sole
Associate II
Oct 6, 2019 5:28 PM UTC

Other solution could be change the size of your buffer items to uint8_t (and save memory btw) , and configure the DMA channel to handle bytes on memory side and halfword in peripheral side.

that is :

uint8_t buffer[2] = {42,21};

and in HAL_TIM_Base_MspInit place:

hdma_tim4_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

hdma_tim4_up.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

This last modifications can be defined using the STM32CUBEMX.