cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G431KB DAC DMA not working

PBieg.1
Associate II

Trying to get a basic sample DAC1 with DMA, triggered by TIM2 working.

This should be trivial, works fine on SMT32F3. I'm using a Nucleo-32 SMT32G431.

Using Cube IDE. The only non IDE-generated code in main is:

  /* USER CODE BEGIN 2 */
 
  static uint16_t x[4];
  x[0]= 100;
  x[1]= 1000;
  x[2]= 2000;
  x[3]= 3000;
 
  HAL_TIM_Base_Start_IT(&htim2);
  HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*)x, 4, DAC_ALIGN_12B_R);
 
  /* USER CODE END 2 */

IOC configuration:

  • DAC with output buffer (verified, works fine in non-DMA mode)
  • circular DMA buffer, 1/2 word
  • trigger: TIM2 update
  • TIM2: set to count up to 500, internal clock (works, verified by reading the clock)

Compared to some DAC/DMA examples - everything seems fine (and works on a Nucleo-32 SMT32F3. Please help - this is extremely frustrating and has held up a a project for 3 days.

test1.ioc and main.c files attached.

Thank you!

Paul

21 REPLIES 21
PBieg.1
Associate II

Thank you for responding!

Here it is (trying to trigger DMA to DAC1 using TIM6).

Configuration code followed by output:

  // ---------------------------------------
  // based on https://vivonomicon.com/2019/07/05/bare-metal-stm32-programming-part-9-dma-megamix/
 
  // Enable peripherals: GPIOA, DMA, DAC, TIM6.
  RCC->AHB1ENR   |= RCC_AHB1ENR_DMA1EN;
  RCC->AHB2ENR  |= ( RCC_AHB2ENR_DAC1EN);
  RCC->APB1ENR1 |= (RCC_APB1ENR1_TIM6EN);
 
  // Pin A4 output type: Analog.
  GPIOA->MODER    &= ~( 0x3 << ( 4 * 2 ) );
  GPIOA->MODER    |=  ( 0x3 << ( 4 * 2 ) );
 
  // DMA configuration (channel 1).
  // CCR register:
  // - Memory-to-peripheral
  // - Circular mode enabled.
  // - Increment memory ptr, don't increment periph ptr.
  // - 16-bit data size for both source and destination.
  // - High priority.
  uint32_t dma_ccr_clr = ~( DMA_CCR_MEM2MEM |
                            DMA_CCR_PL |
                            DMA_CCR_MSIZE |
                            DMA_CCR_PSIZE |
                            DMA_CCR_PINC |
                            DMA_CCR_EN |
							DMA_CCR_HTIE |
							DMA_CCR_TCIE |
							0);
  uint32_t dma_ccr_set =  ( ( 0x2 << DMA_CCR_PL_Pos ) |
                            ( 0x1 << DMA_CCR_MSIZE_Pos ) |
                            ( 0x1 << DMA_CCR_PSIZE_Pos ) |
                            DMA_CCR_MINC |
                            DMA_CCR_CIRC |
                            DMA_CCR_DIR );
  DMA1_Channel1->CCR &= dma_ccr_clr;
  DMA1_Channel1->CCR |= dma_ccr_set;
 
  // Select DAC Ch1 as DMA Ch1 request source in DMAMUX.
  // Note: DMAMUX channel numbers are slightly confusing in
  // the documentation. They aren't reliably 0- or 1-indexed.
  DMAMUX1_Channel0->CCR &= ~( DMAMUX_CxCR_DMAREQ_ID );
  DMAMUX1_Channel0->CCR |=  ( 0x8 << DMAMUX_CxCR_DMAREQ_ID_Pos );
 
  // Set DMA source and destination addresses.
  // Source: Address of the wave buffer in memory.
  DMA1_Channel1->CMAR  = ( uint32_t )&x[0];
 
  // Dest.: DAC1 Ch1 '12-bit right-aligned data' register.
  DMA1_Channel1->CPAR  = ( uint32_t )&( DAC1->DHR12R1 );
 
  // Set DMA data transfer length (# of wave samples).
  DMA1_Channel1->CNDTR = ( uint16_t )400;
 
  // Enable DMA1 Channels 1/2.
  DMA1_Channel1->CCR |= ( DMA_CCR_EN );
 
  // TIM6 configuration.
  // Set prescaler and autoreload (irrelevant, just slow enough to test)
  TIM6->PSC  =  10000;
  TIM6->ARR  =  1000;
 
  // Enable trigger output on timer update events.
  TIM6->CR2 &= ~( TIM_CR2_MMS );
  TIM6->CR2 |=  ( 0x2 << TIM_CR2_MMS_Pos );
 
  // Start the timer.
  TIM6->CR1 |=  ( TIM_CR1_CEN );
 
  // DAC configuration.
  // Set trigger source to TIM6 TRGO (event channel 7)
  DAC1->CR  &= ~( DAC_CR_TSEL1 );
  DAC1->CR  |=  ( 0x7 << DAC_CR_TSEL1_Pos );
 
  // Set outputs to buffered GPIO 'normal mode'.
  DAC1->MCR &= ~( DAC_MCR_MODE1 );
 
  // Enable DAC DMA requests.
  DAC1->CR  |=  ( DAC_CR_DMAEN1 );
 
  // Enable DAC Channels.
  DAC1->CR  |=  ( DAC_CR_EN1 );
 
  // Enable DAC channel trigger.
  DAC1->CR  |=  ( DAC_CR_TEN1 );
 
#endif
  p1_console_printf("ready!\n");
 
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  static int n;
	  n++;
	  p1_console_printf("#%d >>>>>>>>>>>>>>\n", n);
 
#define P(x) pr(#x, x)
 
	  p1_console_printf("TIMER =====\n");
 
	  P(TIM6->CR1);
	  P(TIM6->CR2);
	  P(TIM6->PSC);
	  P(TIM6->ARR);
	  P(TIM6->DIER);
	  P(TIM6->SR);
	  P(TIM6->EGR);
	  P(TIM6->CNT);
 
	  p1_console_printf("DMA =====\n");
 
	  P(DMA1->ISR);
	  P(DMA1_Channel1->CCR);
	  P(DMA1_Channel1->CMAR);
	  P(DMA1_Channel1->CPAR);
	  P(DMA1_Channel1->CNDTR);
 
	  p1_console_printf("DAC =====\n");
 
	  P(DAC1->CR);
	  P(DAC1->SR);
	  P(DAC1->DOR1);
 
	  HAL_Delay(5000);
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }

Ouput (two set, 5sec apart - the timer is spinning, DAC seems to report DMA underrun???

#11 >>>>>>>>>>>>>>

TIMER =====

TIM6->CR1 = 0x00000001 (1 dec)     31:00000000 23:00000000 15:00000000 7:00000001

TIM6->CR2 = 0x00000020 (32 dec)     31:00000000 23:00000000 15:00000000 7:00100000

TIM6->PSC = 0x00002710 (10000 dec)     31:00000000 23:00000000 15:00100111 7:00010000

TIM6->ARR = 0x000003E8 (1000 dec)     31:00000000 23:00000000 15:00000011 7:11101000

TIM6->DIER = 0x00000000 (0 dec)     31:00000000 23:00000000 15:00000000 7:00000000

TIM6->SR = 0x00000001 (1 dec)     31:00000000 23:00000000 15:00000000 7:00000001

TIM6->EGR = 0x00000000 (0 dec)     31:00000000 23:00000000 15:00000000 7:00000000

TIM6->CNT = 0x00000040 (64 dec)     31:00000000 23:00000000 15:00000000 7:01000000

DMA =====

DMA1->ISR = 0x00000000 (0 dec)     31:00000000 23:00000000 15:00000000 7:00000000

DMA1_Channel1->CCR = 0x000025B1 (9649 dec)     31:00000000 23:00000000 15:00100101 7:10110001

DMA1_Channel1->CMAR = 0x20000000 (536870912 dec)     31:00100000 23:00000000 15:00000000 7:00000000

DMA1_Channel1->CPAR = 0x50000808 (1342179336 dec)     31:01010000 23:00000000 15:00001000 7:00001000

DMA1_Channel1->CNDTR = 0x00000190 (400 dec)     31:00000000 23:00000000 15:00000001 7:10010000

DAC =====

DAC1->CR = 0x0000101F (4127 dec)     31:00000000 23:00000000 15:00010000 7:00011111

DAC1->SR = 0x00002800 (10240 dec)     31:00000000 23:00000000 15:00101000 7:00000000

DAC1->DOR1 = 0x00000000 (0 dec)     31:00000000 23:00000000 15:00000000 7:00000000

#12 >>>>>>>>>>>>>>

TIMER =====

TIM6->CR1 = 0x00000001 (1 dec)     31:00000000 23:00000000 15:00000000 7:00000001

TIM6->CR2 = 0x00000020 (32 dec)     31:00000000 23:00000000 15:00000000 7:00100000

TIM6->PSC = 0x00002710 (10000 dec)     31:00000000 23:00000000 15:00100111 7:00010000

TIM6->ARR = 0x000003E8 (1000 dec)     31:00000000 23:00000000 15:00000011 7:11101000

TIM6->DIER = 0x00000000 (0 dec)     31:00000000 23:00000000 15:00000000 7:00000000

TIM6->SR = 0x00000001 (1 dec)     31:00000000 23:00000000 15:00000000 7:00000001

TIM6->EGR = 0x00000000 (0 dec)     31:00000000 23:00000000 15:00000000 7:00000000

TIM6->CNT = 0x000003B2 (946 dec)     31:00000000 23:00000000 15:00000011 7:10110010

DMA =====

DMA1->ISR = 0x00000000 (0 dec)     31:00000000 23:00000000 15:00000000 7:00000000

DMA1_Channel1->CCR = 0x000025B1 (9649 dec)     31:00000000 23:00000000 15:00100101 7:10110001

DMA1_Channel1->CMAR = 0x20000000 (536870912 dec)     31:00100000 23:00000000 15:00000000 7:00000000

DMA1_Channel1->CPAR = 0x50000808 (1342179336 dec)     31:01010000 23:00000000 15:00001000 7:00001000

DMA1_Channel1->CNDTR = 0x00000190 (400 dec)     31:00000000 23:00000000 15:00000001 7:10010000

DAC =====

DAC1->CR = 0x0000101F (4127 dec)     31:00000000 23:00000000 15:00010000 7:00011111

DAC1->SR = 0x00002800 (10240 dec)     31:00000000 23:00000000 15:00101000 7:00000000

DAC1->DOR1 = 0x00000000 (0 dec)     31:00000000 23:00000000 15:00000000 7:00000000

You haven't posted the DMAMUX registers.

TIM6->DIER = 0x00000000

 

There is nothing which would trigger the DMA - TIMx_DIER.UDE bit is supposed to be set.

Ah you trigger DAC using TIM6 and then trigger DMA from DAC... okay I need to have a look at that.

JW

DMAMUX1_Channel0->CCR |= ( 0x8 << DMAMUX_CxCR_DMAREQ_ID_Pos );

Isn't it 0x6 rather than 0x8?

And again the numbering is not 0-based so I wouldn't be that sure with that either... [EDIT] it's okay, 0 is not in the table as it is special and when used it means "this channel is off"[/EDIT]

JW

berendi
Principal

DMAMUX and GPIOA should be enabled in RCC->****ENR too. Enable GPIOA first, because a few cycles delay is usually needed before the peripheral becomes active.

Yes, DMA request ID should be 6 instead of 8 if DMA is triggered by the DAC.

in stm32g4xx_hal_dma.h:

#define DMA_REQUEST_DAC1_CHANNEL1      6U

22.7 DAC registers

[...] The peripheral registers have to be accessed by words (32-bit).

so change the DMA PSIZE parameter to 2 (32 bits)

( 0x2 << DMA_CCR_PSIZE_Pos ) |

MSIZE can remain 01 (16 bits), DMA will pad the data with 0s when extending from 16 to 32 bits.

PBieg.1
Associate II

Thank you very much for your help - got it to work. Seems that the CUBE IDE code generator and HAL are simply ignoring DMAMUX.

I'm not going to investigate this, but IIRC DMAMUX in Cube (some other STM32 family's which has DMAMUX I looked at some time ago) is set automagically inside some of the usual DMA setup functions, so CubeMX does not need to be aware of it. I may be mistaken, I obviously don't Cube.

JW

SSorr.1
Associate II

Great thread! I am facing the same issue on a STM32G431 processor, I copied the code posted above with the proposed changes​, but the DAC out is not updating, despite a running timer.

I​ suspect that I missed something in the DMAMUX, is there anyone so kind to explain whether more DMAMUX registers need to be updated? Thanks!

rclar.6
Senior

Me too. Only strangely the version I made for the nucleo board works on the 100 pin chip used for the PCBs made. I solved the ADC by using a HAL_DMA_init() call that I never had to use before (perhaps later versions of the CubeMX used to put this call in and now don't?). However the DAC output which used to work fine on a neucleo board simply does not start. The

 

    if (HAL_DAC_Start_DMA(&hdac3, DAC_CHANNEL_2, (uint32_t*) &dac_data_out[0], size,
      DAC_ALIGN_12B_R) != HAL_OK) {
      Error_Handler();
    }

Returns HAL_OK. The DMA simply just does not start.

Sanjeok
Associate

I tried with STM32G431CBT6 but failed by Arduino IDE.

bool initDMA( uint8_t pin, uint8_t *wave, uint32_t freq, uint32_t len ) {
// https://vivonomicon.com/2019/07/05/bare-metal-stm32-programming-part-9-dma-megamix/ -- DAC configuration.
//-------------------------
RCC->AHB2ENR |= ( RCC_AHB2ENR_DAC1EN);
RCC->APB1ENR1 |= (RCC_APB1ENR1_TIM6EN);
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN | RCC_AHB1ENR_DMAMUX1EN;
// https://community.st.com/t5/stm32-mcus-products/stm32g431kb-dac-dma-not-working/td-p/251096/page/2
if( dac_channel == 0 ) {
DMAMUX1_Channel2->CCR &= ~DMAMUX_CxCR_DMAREQ_ID;
DMAMUX1_Channel2->CCR |= ( 0x6 << DMAMUX_CxCR_DMAREQ_ID_Pos );
//DMAMUX1_Channel2->CCR |= DMA_REQUEST_DAC1_CHANNEL1; // DAC1 CH1 6
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // It must not be written when the channel is enabled
DMA1_Channel3->CCR = DMA_CCR_MINC // memory increment
| DMA_CCR_DIR // memory to peripheral
//| ( 0x2 << DMA_CCR_PSIZE_Pos ) // 32 bit
| DMA_CCR_CIRC; // curcular mode
//
DMA1_Channel3->CNDTR = len; // how many data to transfer
DMA1_Channel3->CMAR = (uint32_t)&wave[ 0 ];
DMA1_Channel3->CPAR = (uint32_t)&( DAC->DHR8R1 ); // destination address - DAC 8 bit output register
DMA1_Channel3->CCR |= DMA_CCR_EN;
}
else {
DMAMUX1_Channel3->CCR &= ~DMAMUX_CxCR_DMAREQ_ID;
DMAMUX1_Channel3->CCR |= ( 0x7 << DMAMUX_CxCR_DMAREQ_ID_Pos );
//DMAMUX1_Channel3->CCR |= DMA_REQUEST_DAC1_CHANNEL2; // DAC1 CH2 7
DMA1_Channel4->CCR &= ~DMA_CCR_EN;
DMA1_Channel4->CCR = DMA_CCR_MINC // memory increment
| DMA_CCR_DIR // memory to peripheral
//| ( 0x2 << DMA_CCR_PSIZE_Pos ) // 32 bit
| DMA_CCR_CIRC; // curcular mode
//
DMA1_Channel4->CNDTR = len; // how many data to transfer
DMA1_Channel4->CMAR = (uint32_t)wave;
DMA1_Channel4->CPAR = (uint32_t)&( DAC->DHR8R2 ); // destination address - DAC 8 bit output register
}
//-------------------------------------------------
// https://vivonomicon.com/2019/07/05/bare-metal-stm32-programming-part-9-dma-megamix/
TIM6->CR1 &= ~TIM_CR1_CEN;
TIM6->ARR = SystemCoreClock / ( freq * len ) - 1;
TIM6->CR2 &= ~TIM_CR2_MMS;
TIM6->CR2 |= 2 << TIM_CR2_MMS_Pos; // the update event is selected as trigger output
TIM6->CR1 |= TIM_CR1_CEN;
//-----------------------------------------------------------------------
pinMode( pin, OUTPUT );
switch( pin ) {
case PA4 :
GPIOA->MODER &= ~( 0x3 << ( 4 * 2 ) );
GPIOA->MODER |= ( 0x3 << ( 4 * 2 ) );
//GPIOA->MODER |= GPIO_MODER_MODE4;
dac_channel = 0;
DAC->CR &= ~( DAC_CR_EN1 | DAC_CR_CEN1 ); // for register access
DAC->MCR &= ~DAC_MCR_MODE1;
DAC->CR |= DAC_CR_TEN1; // trigger enable
DAC->CR |= DAC_CR_DMAEN1; // DMA 사용
DAC->CR &= ~DAC_CR_TSEL1; // DAC channel1 trigger selection
DAC->CR |= DAC_TRIGGER_T6_TRGO; // DAC_TRIGGER_T6_TRGO
DAC->CR |= DAC_CR_EN1;
while( ( DAC->SR & DAC_SR_DAC1RDY ) == 0 );
break;
case PA5 :
GPIOA->MODER &= ~( 0x3 << ( 5 * 2 ) );
GPIOA->MODER |= ( 0x3 << ( 5 * 2 ) );
//GPIOA->MODER |= GPIO_MODER_MODE5;
dac_channel = 1;
DAC->CR &= ~( DAC_CR_EN2 | DAC_CR_CEN2 ); // for register access
DAC->MCR &= ~DAC_MCR_MODE2;
DAC->CR |= DAC_CR_TEN2; // trigger enable
DAC->CR |= DAC_CR_DMAEN2; // DMA 사용
DAC->CR &= ~DAC_CR_TSEL2; // DAC channel2 trigger selection
DAC->CR |= DAC_TRIGGER_T6_TRGO; // DAC_TRIGGER_T6_TRGO 7
DAC->CR |= DAC_CR_EN2;
while( ( DAC->SR & DAC_SR_DAC2RDY ) == 0 );
break;
default : return false;
}

return true;
}

"Manually" filled DAC (in a loop, using loopdelay) works?

TIM6_CNT, DMA_NDTR do change when observed in debugger?

Read out and check/post content of TIM, DMA, DAC and relevant GPIO registers.

JW