Page 1 of 1

Multiple DMAs conflicts

Posted: Sun Apr 06, 2025 1:48 pm
by blackberryer
OS : ChibiOS-21.11.3
MCU : STM32F405VGT6

BUZZER : PB0 TIM1_CH2N STM32_DMA2_STREAM5

I use pwm in combination with dma to drive the buzzer, but it conflicts with I2C until I disable I2C's DMA(see viewtopic.php?t=4167), the buzzer works fine, why?

Code: Select all

void buzzer_init(void)
{
  // Configure pin as AF output. Initialize the output low.
  palSetPadMode(PORT_BUZZER, PIN_BUZZER, PAL_MODE_ALTERNATE(BUZZER_AF) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_PUPDR_PULLDOWN);

  // Initialize frame buffer
  last_buzzer_dutycycle = BUZZER_DUTYCYCLE;
  uint32_t i;
  for (i = 0; i < BUZZER_DATA_BIT_N; i++)    buzzer_frame_buffer[i]                       = last_buzzer_dutycycle; // All bits are zero duty cycle
  for (i = 0; i < BUZZER_RESET_BIT_N; i++)   buzzer_frame_buffer[i + BUZZER_DATA_BIT_N]   = 0; // All bits are zero duty cycle
  rccEnableDMA_buzzer(false);
  (BUZZER_DMA_STREAM)->stream->PAR  = (uint32_t)(&(BUZZER_PWMD.tim->CCR[BUZZER_TIM_CH]));
  (BUZZER_DMA_STREAM)->stream->M0AR = (uint32_t)(&buzzer_frame_buffer);//uint32_t match STM32_DMA_CR_MSIZE_WORD
  (BUZZER_DMA_STREAM)->stream->NDTR = (uint32_t)(BUZZER_BUFF_BIT_N);//sizeof(buzzer_frame_buffer)/sizeof(uint8_t));//
  (BUZZER_DMA_STREAM)->stream->CR   = 0;
  (BUZZER_DMA_STREAM)->stream->CR  |= (uint32_t)( STM32_DMA_CR_PL(BUZZER_DMA_PRIORITY) | STM32_DMA_CR_CHSEL(BUZZER_DMA_CHANNEL) |
                                                  STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_WORD | //0x00000000U |//STM32_DMA_CR_RESET_VALUE |
                                                  STM32_DMA_CR_MSIZE_WORD | STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE );//| STM32_DMA_CR_CIRC );//
 
  // Start DMA
  (BUZZER_DMA_STREAM)->stream->CR  |= ((uint32_t)0x00000001);//STM32_DMA_CR_EN;//dmaStreamEnable(BUZZER_DMA_STREAM);
  nvicEnableVector(BUZZER_DMA_STREAM_IRQ, BUZZER_PWM_IRQ_PRIORITY);

  pwmStart(&BUZZER_PWMD, &buzzer_pwm_config);
  pwmEnableChannel(&BUZZER_PWMD, BUZZER_TIM_CH, 5000);     // Initial period is 0; output will be low until first duty cycle is DMA'd in
  //pwmEnableChannel(&BUZZER_PWMD, BUZZER_TIM_CH, PWM_PERCENTAGE_TO_WIDTH(&BUZZER_PWMD, 5000));
}


1.Why does I2C use DMA by default? Isn't there an option?
2.How to avoid conflicts and make them work?

Re: Multiple DMAs conflicts

Posted: Sun Apr 06, 2025 4:20 pm
by Giovanni
Hi,

There is no option to disable use of DMA in that driver. Can you use another DMA channel for your buzzer?

Giovanni

Re: Multiple DMAs conflicts

Posted: Sun Apr 06, 2025 7:47 pm
by blackberryer
Giovanni wrote:Hi,

There is no option to disable use of DMA in that driver. Can you use another DMA channel for your buzzer?

Giovanni


The other channel has the same result, using TIM3 and using STM32_DMA1_STREAM2, but the result will conflict, the program is stuck, I use vscode, I can't use the debugger at the moment, so I don't know where the program crashes, but it is clear that I2C will conflict with buzzer's DMA when using DMA.
the other IO PB1, controls WS2812, PB0 and PB1 don't conflict when they use DMA respectively.

Re: Multiple DMAs conflicts

Posted: Tue Apr 08, 2025 5:43 pm
by blackberryer
Giovanni wrote:Hi,

There is no option to disable use of DMA in that driver. Can you use another DMA channel for your buzzer?

Giovanni


I guess I understand the error, I set the TCIE interrupt, but I'm not doing the processing in the OSAL_IRQ_HANDLER function.
When I set up I2C without DMA, I misunderstood commented out the OSAL_IRQ_HANDLER function, and an error also occurs at this time. When I turn on OSAL_IRQ_HANDLER function and clean up the flag operation(The most important thing is that I did buzzer_stop()), the error disappears.

How do I operate the OSAL_IRQ_HANDLER function corresponding to buzzer when I use I2C with DMA?

Because when I enable I2C DMA, the OSAL_IRQ_HANDLER function I wrote myself will conflict with the system and make it impossible to compile(multiple definition of `Vector74' or `Vector150' ).

Code: Select all

//  DMA2 stream 5 shared interrupt handler.
#if 1//0
OSAL_IRQ_HANDLER(STM32_DMA2_CH5_HANDLER) {
  uint32_t flags = DMA2->HISR; DMA2->HIFCR = flags;

  if (flags & DMA_HISR_TCIF5_Msk) DMA2_CH5_HANDLER_FUNC(flags); // DMA2 Channel 5 handler
}
#endif


I don't know how to use STM32_DMA2_CH5_HANDLER of the system, because the system's callback function is 'dma.streams[13].func', I don't know how to set it

Code: Select all

/**
 * @brief   DMA2 stream 5 shared interrupt handler.
 *
 * @isr
 */
OSAL_IRQ_HANDLER(STM32_DMA2_CH5_HANDLER) {
  uint32_t flags;

  OSAL_IRQ_PROLOGUE();

  flags = (DMA2->HISR >> 6U) & STM32_DMA_ISR_MASK;
  DMA2->HIFCR = flags << 6U;
  if (dma.streams[13].func)
    dma.streams[13].func(dma.streams[13].param, flags);

  OSAL_IRQ_EPILOGUE();
}

Re: Multiple DMAs conflicts

Posted: Tue Apr 08, 2025 6:10 pm
by Giovanni
Hi,

The problem is that you should not touch DMA registers directly but use the DMA driver functions, you can see an example of DMA driver use (and callback use) in any driver using DMA, see the SPI driver for example.

Giovanni

Re: Multiple DMAs conflicts

Posted: Tue Apr 08, 2025 8:36 pm
by blackberryer
Giovanni wrote:Hi,

The problem is that you should not touch DMA registers directly but use the DMA driver functions, you can see an example of DMA driver use (and callback use) in any driver using DMA, see the SPI driver for example.

Giovanni


I modified my program and used the following code, but in fact, the callback function was triggered for the first time, and the callback function was not triggered afterwards

Code: Select all

void buzzer_init(void)
{
  // Configure pin as AF output. Initialize the output low.
  palSetPadMode(PORT_BUZZER, PIN_BUZZER, PAL_MODE_ALTERNATE(BUZZER_AF) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_PUPDR_PULLDOWN);
  // Initialize frame buffer
  last_buzzer_dutycycle = BUZZER_DUTYCYCLE;
  uint32_t i;
  for (i = 0; i < BUZZER_DATA_BIT_N; i++)    buzzer_frame_buffer[i]                       = last_buzzer_dutycycle; // All bits are zero duty cycle
  for (i = 0; i < BUZZER_RESET_BIT_N; i++)   buzzer_frame_buffer[i + BUZZER_DATA_BIT_N]   = 0; // All bits are zero duty cycle
  buzzer_dma = dmaStreamAlloc(BUZZER_DMA_STREAM, BUZZER_PWM_IRQ_PRIORITY, (stm32_dmaisr_t)buzzer_tx_complete_interrupt, (void *)BUZZER_DMA_STREAM);
  dmaStreamSetPeripheral(buzzer_dma, &(BUZZER_PWMD.tim->CCR[BUZZER_TIM_CH]));
  dmaStreamSetMemory0(buzzer_dma, buzzer_frame_buffer);
  dmaStreamSetTransactionSize(buzzer_dma, BUZZER_BUFF_BIT_N);
  // TCIE mode, select channel BUZZER_DMA_CHANNEL
  dmaStreamSetMode( buzzer_dma,
                    STM32_DMA_CR_PL(BUZZER_DMA_PRIORITY) | STM32_DMA_CR_CHSEL(BUZZER_DMA_CHANNEL) |
                    STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD |
                    STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE );// | STM32_DMA_CR_CIRC );//
  // init FIFO to 0 aka only one element
  //dmaStreamSetFIFO(buzzer_dma, STM32_DMA_FCR_FTH_1Q);
  dmaStreamEnable(buzzer_dma);
  pwmStart(&BUZZER_PWMD, &buzzer_pwm_config);
  pwmEnableChannel(&BUZZER_PWMD, BUZZER_TIM_CH, 5000);     // Initial period is 0; output will be low until first duty cycle is DMA'd in
  //pwmEnableChannel(&BUZZER_PWMD, BUZZER_TIM_CH, PWM_PERCENTAGE_TO_WIDTH(&BUZZER_PWMD, 5000));
}


Code: Select all

void buzzer_tx_complete_interrupt(uint32_t flags)
{
  //(void)flags;
  #if BUZZER_TIM_N == 1
  if (flags & DMA_HISR_TCIF5)
  {
    //flags &= ~DMA_HISR_TCIF5;
    palTogglePad(GPIOC, GPIOC_LED);
    buzzer_stop();
  }
  #elif BUZZER_TIM_N == 3
  if (flags & DMA_LISR_TCIF2)
  {
    //flags &= ~DMA_LISR_TCIF5;
    palTogglePad(GPIOC, GPIOC_LED);
    buzzer_stop();
  }
  #endif
}


Code: Select all

void buzzer_stop(void)
{
  // Stop DMA
  #if 1
  dmaStreamDisable(buzzer_dma);
  //dmaStreamFree(buzzer_dma);
  #else
  //dmaStreamDisable(BUZZER_DMA_STREAM);
  (BUZZER_DMA_STREAM)->stream->CR &= ~(STM32_DMA_CR_TCIE | STM32_DMA_CR_HTIE |
                                       STM32_DMA_CR_TEIE | STM32_DMA_CR_DMEIE | STM32_DMA_CR_EN);
 
  nvicDisableVector(BUZZER_DMA_STREAM_IRQ);
  //rccDisableDMA2();
  #endif
  // Stop PWM
  //pwmStop(&BUZZER_PWMD);
  //pwmDisableChannel(&BUZZER_PWMD, BUZZER_TIM_CH);
}

Re: Multiple DMAs conflicts

Posted: Wed Apr 09, 2025 4:16 pm
by blackberryer
It's been solved, thanks

flags pre-shifted content of the xISR register, the bits are aligned to bit zero

Code: Select all

void buzzer_tx_complete_interrupt(void *param, uint32_t flags)
{
  //(void)flags;
  if (flags & (0x01 << 5))
  {
    //flags &= ~DMA_HISR_TCIF5;
    palTogglePad(GPIOC, GPIOC_LED);
    buzzer_stop();
  }
}