Skip to main content
Visitor II
May 13, 2020
Question

STM32G431KB DAC DMA not working

  • May 13, 2020
  • 16 replies
  • 10585 views

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

    This topic has been closed for replies.

    16 replies

    PBieg.1Author
    Visitor II
    May 13, 2020

    Additional piece of information: it appears to throw a DMA transfer error for no obvious reason.

    Super User
    May 13, 2020

    Read out and check/post the relevant TIM, DMAMUX, DMA and perhaps also DAC and GPIO registers' content.

    JW

    PBieg.1Author
    Visitor II
    May 13, 2020

    The DMAMUX documentation is not very helpful and HAL (and CUBE IDE) seems to basically ignore it. Does anything need to be set in DMAMUX beyond what HAL would set automatically?

    Visitor II
    May 13, 2020

    (Uint32_t *)x doesn't look right to me. The DMA wants the 32bit base address of the array. Try (uint32_t)&x[0] instead.

    PBieg.1Author
    Visitor II
    May 13, 2020

    It's the same thing (this is C :-), but thanks for looking.

    Super User
    May 13, 2020

    > The challenge is knowing what is relevant -

    Then read it and post all.

    > the promise HAL makes is not having to know.

    You don't need to know but then you also don't demand it to work.

    > Otherwise might as well go bare metal, which given the state of the documentation seems best avoided.

    Yeah, because Cube/HAL documentation is so great.

    JW

    Visitor II
    May 13, 2020

    What is the problem with the DMAMUX documentation? Do you have the latest revision (Rev 4) of the reference manual?

    What information is missing?

    Please post the contents of the peripheral registers of all peripherals involved, RCC, TIM2, DAC, GPIO, DMA1, DMAMUX.

    > this is extremely frustrating and has held up a a project for 3 days.

    That's why my policy is to forget HAL the moment something does not work. 3 days should be enough to get a DMA transfer working based on the reference manual.

    PBieg.1Author
    Visitor II
    May 13, 2020

    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

    Super User
    May 13, 2020

    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

    Super User
    May 13, 2020

    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

    Visitor II
    May 13, 2020

    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.1Author
    Visitor II
    May 14, 2020

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