cancel
Showing results for 
Search instead for 
Did you mean: 

Basic DAC + DMA configuration for on the stm32F446re without HAL-libaries

Michael8278
Associate II

Hi,

 

I'm using a Nucleo-F446RE Board and I'm trying to set up a basic DAC with DMA configuration without HAL-libaries.

The configuration without DMA works just fine, I can measure correct voltages with my oscilloscope on PA4 as intended. My following DMA configuration sets the PA4 always on 0V, nothing happens.

I checked the debugger and all registers are being properly set, DMA and DAC both have their clock and values.

It just doesn't copy the values into DAC->DHR12R1. I also checked the Flags in DMA_LISR, but no error there.

I'm probably missing somehow on how to start the whole process, so my last iteration of code tried to mess with software triggers. I also tried all combinations of PSIZE and MSIZE, as well as uint8_t and uint32_t arrays instead of uint16_t. I feel like I followed the stream configuration procedure from the reference manual, but I probably missed some small, gritty detail.

Anybody got code for a similar MCU or sees the problem in my configuration?

 

#include "stm32f4xx.h"

#define DAC_SIZE 8

void delay(volatile uint32_t count) {
    while(count--) __asm__("nop");
}

//uint8_t dac_buffer[DAC_SIZE] = {50, 0, 30, 60, 120, 150, 180, 240};
uint16_t dac_buffer[DAC_SIZE] = {2000, 512, 1024, 1536, 2048, 2560, 3072, 4095};

int main(void) {
    //  GPIOA Clock aktivieren (PA0 als analog)
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    GPIOA->MODER |= (0b11 << (4*2)); // PA4 = Analog Mode fuer DAC


    // DAC Clock
    RCC->APB1ENR |= RCC_APB1ENR_DACEN;

    // DMA Clock
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;



    /* 5. DMA1 Stream5 konfigurieren (DAC1) */
    DMA1_Stream5->CR = 0;  // Reset
    while (DMA1_Stream5->CR & DMA_SxCR_EN) {
    }
    DMA1_Stream5->PAR = (uint32_t)&(DAC->DHR12R1);     // Peripherieadresse
    DMA1_Stream5->M0AR = (uint32_t)dac_buffer;       // Speicheradresse
    DMA1_Stream5->NDTR = DAC_SIZE;               // Anzahl Daten X
    DMA1_Stream5->CR |= (0b111 << DMA_SxCR_CHSEL_Pos);   // Channel 7 = DAC1 X
    DMA1_Stream5->CR |= DMA_SxCR_PL_1;               // Priority High X
    //FIFO usage => direct mode not needed?
	DMA1_Stream5->CR |= (0b01 << DMA_SxCR_PSIZE_Pos); // aktuell auf 16bit, 32bit und 8bit wurde ausprobiert
    DMA1_Stream5->CR |= (0b01 << DMA_SxCR_MSIZE_Pos);

    DMA1_Stream5->CR |= DMA_SxCR_MINC;               // Speicher inkrementieren X
    DMA1_Stream5->CR |= DMA_SxCR_CIRC;               // Circular Mode  X
    DMA1_Stream5->CR |= DMA_SxCR_DIR_0;              // Memory -> Peripheral X
    //DMA1_Stream5->CR |= (1 << DMA_SxCR_PFCTRL_Pos); // forbidden in circular mode anyways
    DMA1_Stream5->CR |= DMA_SxCR_EN;                 // Stream aktiv X

    // DAC1 DMA enable
    DAC->CR |= DAC_CR_DMAEN1;

    //DAC1 einschalten
    DAC->CR |= DAC_CR_EN1;

    DAC->CR |= DAC_CR_TEN1;    // Enable trigger
    DAC->CR |= (0b111 << 3);   // Select software trigger (TSEL1 = 111)

    //DAC->DHR12R1 = 0; // test, ob es ueberhaupt etwas ausgibt

    while(1) {
    	delay(1000);
    	DAC->SWTRIGR |= DAC_SWTRIGR_SWTRIG1;  // Trigger DAC manually

    }
}

 

1 ACCEPTED SOLUTION

Accepted Solutions
Michael8278
Associate II

In the end i reverse-engineered a HAL-Example...

My lesson learned is: It only works with Timers and only with update in CR2 configured.

Here my working more or less minimal configuration with TIM6, for anybody struggling with the same problem:

#include "stm32f4xx.h"

#define DAC_SIZE 8

void delay(volatile uint32_t count) {
    while(count--) __asm__("nop");
}

//uint8_t dac_buffer[DAC_SIZE] = {50, 0, 30, 60, 120, 150, 180, 240};
volatile uint16_t dac_buffer[DAC_SIZE] = {0, 4000, 0, 4000, 0, 4000, 0, 4000};

volatile uint32_t cb_half = 0;
volatile uint32_t cb_full = 0;
volatile uint32_t error_flag = 0;
volatile uint32_t tim6_cnt = 0;
volatile uint32_t dma_cnt = 0;


void TIM6_DAC_IRQHandler(void)
{
	tim6_cnt++;
    TIM6->SR &= ~TIM_SR_UIF; // Clear flag
}

//void DMA1_Stream5_IRQHandler(void)
//{
//	dma_cnt++;
//    // ---------------- Half Transfer ----------------
//    if (DMA1->HISR & DMA_HISR_HTIF5)  // HT flag set?
//    {
//        DMA1->HIFCR |= DMA_HIFCR_CHTIF5;  // Clear HT flag
//        cb_half++;

//    }
//
//    // ---------------- Transfer Complete ----------------
//    if (DMA1->HISR & DMA_HISR_TCIF5)  // TC flag set?
//    {
//        DMA1->HIFCR |= DMA_HIFCR_CTCIF5;  // Clear TC flag
//        cb_full++;                       // Set user flag / handle buffer
//    }
//
//}


void TIM6_Init()
{
    // clk
    RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;


    TIM6->PSC = 0;  // PSC = prescaler
    TIM6->ARR = 500;        // ARR = auto-reload

    TIM6->DIER |= TIM_DIER_UIE;
    NVIC_SetPriority(TIM6_DAC_IRQn, 1);
    NVIC_EnableIRQ(TIM6_DAC_IRQn);

    //enable
    TIM6->CR2 = 0x20;
    TIM6->CR1 |= TIM_CR1_CEN;
}



int main(void) {
    //  GPIOA Clock aktivieren (PA0 als analog)
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    GPIOA->MODER |= (0b11 << (4*2)); // PA4 = Analog Mode fuer DAC


    // DAC Clock
    RCC->APB1ENR |= RCC_APB1ENR_DACEN;

    // DMA Clock
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;

    TIM6_Init();

    /* 5. DMA1 Stream5 konfigurieren (DAC1) */
    DMA1_Stream5->CR = 0;  // Reset
    while (DMA1_Stream5->CR & DMA_SxCR_EN) {
    }
    DMA1_Stream5->PAR = (uint32_t)&(DAC->DHR12R1);     // Peripherieadresse
    DMA1_Stream5->M0AR = (uint32_t)dac_buffer;       // Speicheradresse
    DMA1_Stream5->NDTR = DAC_SIZE;               // Anzahl Daten X
    DMA1_Stream5->CR |= (0b111 << DMA_SxCR_CHSEL_Pos);   // Channel 7 = DAC1 X
    //DMA1_Stream5->CR |= DMA_SxCR_PL_1;               // Priority High X
    //FIFO usage => direct mode not needed?
	DMA1_Stream5->CR |= (0b01 << DMA_SxCR_PSIZE_Pos); // aktuell auf 16bit, 32bit und 8bit wurde ausprobiert
    DMA1_Stream5->CR |= (0b01 << DMA_SxCR_MSIZE_Pos);

    DMA1_Stream5->CR |= DMA_SxCR_MINC;               // Speicher inkrementieren X
    DMA1_Stream5->CR |= DMA_SxCR_CIRC;               // Circular Mode  X
    DMA1_Stream5->CR |= DMA_SxCR_DIR_0;              // Memory -> Peripheral X
    //DMA1_Stream5->CR |= (1 << DMA_SxCR_PFCTRL_Pos); // forbidden in circular mode anyways
    DMA1_Stream5->CR |= (1 << 3); // HTIE
    DMA1_Stream5->CR |= (1 << 4); // TCIE
    DMA1_Stream5->CR |= (1 << 2); // TEIE
    DMA1_Stream5->CR |= (1 << 1); // TEIE
    DMA1_Stream5->CR |= DMA_SxCR_EN;                 // Stream aktiv X

    NVIC_SetPriority(DMA1_Stream5_IRQn, 1);
    NVIC_EnableIRQ(DMA1_Stream5_IRQn);
    // DAC1 DMA enable
    DAC->CR |= DAC_CR_DMAEN1;

    //DAC1 einschalten
    DAC->CR |= DAC_CR_EN1;

    DAC->CR |= DAC_CR_TEN1;    // Enable trigger
    DAC->CR |= (0b000 << 3);   // sel tim6


    while(1) {
    }
}

 

View solution in original post

3 REPLIES 3
TDK
Super User

With TSEL=7, you have selected software trigger, so you'll have to set DAC_SWTRIGR_SWTRIG1 for each value. If that's not what you want, select a different trigger and enable the TRGO event from that peripheral if using a timer.

TDK_0-1764529175295.png

 

If you feel a post has answered your question, please click "Accept as Solution".

I know, I didn't want this to be a final solution, I wanted to trigger _anything_. No value has been triggered with my setup, it's always 0.

I will try setting up a Timer 6 TRGO event next, although this makes the code more complicated and more error-prone.

Michael8278
Associate II

In the end i reverse-engineered a HAL-Example...

My lesson learned is: It only works with Timers and only with update in CR2 configured.

Here my working more or less minimal configuration with TIM6, for anybody struggling with the same problem:

#include "stm32f4xx.h"

#define DAC_SIZE 8

void delay(volatile uint32_t count) {
    while(count--) __asm__("nop");
}

//uint8_t dac_buffer[DAC_SIZE] = {50, 0, 30, 60, 120, 150, 180, 240};
volatile uint16_t dac_buffer[DAC_SIZE] = {0, 4000, 0, 4000, 0, 4000, 0, 4000};

volatile uint32_t cb_half = 0;
volatile uint32_t cb_full = 0;
volatile uint32_t error_flag = 0;
volatile uint32_t tim6_cnt = 0;
volatile uint32_t dma_cnt = 0;


void TIM6_DAC_IRQHandler(void)
{
	tim6_cnt++;
    TIM6->SR &= ~TIM_SR_UIF; // Clear flag
}

//void DMA1_Stream5_IRQHandler(void)
//{
//	dma_cnt++;
//    // ---------------- Half Transfer ----------------
//    if (DMA1->HISR & DMA_HISR_HTIF5)  // HT flag set?
//    {
//        DMA1->HIFCR |= DMA_HIFCR_CHTIF5;  // Clear HT flag
//        cb_half++;

//    }
//
//    // ---------------- Transfer Complete ----------------
//    if (DMA1->HISR & DMA_HISR_TCIF5)  // TC flag set?
//    {
//        DMA1->HIFCR |= DMA_HIFCR_CTCIF5;  // Clear TC flag
//        cb_full++;                       // Set user flag / handle buffer
//    }
//
//}


void TIM6_Init()
{
    // clk
    RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;


    TIM6->PSC = 0;  // PSC = prescaler
    TIM6->ARR = 500;        // ARR = auto-reload

    TIM6->DIER |= TIM_DIER_UIE;
    NVIC_SetPriority(TIM6_DAC_IRQn, 1);
    NVIC_EnableIRQ(TIM6_DAC_IRQn);

    //enable
    TIM6->CR2 = 0x20;
    TIM6->CR1 |= TIM_CR1_CEN;
}



int main(void) {
    //  GPIOA Clock aktivieren (PA0 als analog)
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    GPIOA->MODER |= (0b11 << (4*2)); // PA4 = Analog Mode fuer DAC


    // DAC Clock
    RCC->APB1ENR |= RCC_APB1ENR_DACEN;

    // DMA Clock
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;

    TIM6_Init();

    /* 5. DMA1 Stream5 konfigurieren (DAC1) */
    DMA1_Stream5->CR = 0;  // Reset
    while (DMA1_Stream5->CR & DMA_SxCR_EN) {
    }
    DMA1_Stream5->PAR = (uint32_t)&(DAC->DHR12R1);     // Peripherieadresse
    DMA1_Stream5->M0AR = (uint32_t)dac_buffer;       // Speicheradresse
    DMA1_Stream5->NDTR = DAC_SIZE;               // Anzahl Daten X
    DMA1_Stream5->CR |= (0b111 << DMA_SxCR_CHSEL_Pos);   // Channel 7 = DAC1 X
    //DMA1_Stream5->CR |= DMA_SxCR_PL_1;               // Priority High X
    //FIFO usage => direct mode not needed?
	DMA1_Stream5->CR |= (0b01 << DMA_SxCR_PSIZE_Pos); // aktuell auf 16bit, 32bit und 8bit wurde ausprobiert
    DMA1_Stream5->CR |= (0b01 << DMA_SxCR_MSIZE_Pos);

    DMA1_Stream5->CR |= DMA_SxCR_MINC;               // Speicher inkrementieren X
    DMA1_Stream5->CR |= DMA_SxCR_CIRC;               // Circular Mode  X
    DMA1_Stream5->CR |= DMA_SxCR_DIR_0;              // Memory -> Peripheral X
    //DMA1_Stream5->CR |= (1 << DMA_SxCR_PFCTRL_Pos); // forbidden in circular mode anyways
    DMA1_Stream5->CR |= (1 << 3); // HTIE
    DMA1_Stream5->CR |= (1 << 4); // TCIE
    DMA1_Stream5->CR |= (1 << 2); // TEIE
    DMA1_Stream5->CR |= (1 << 1); // TEIE
    DMA1_Stream5->CR |= DMA_SxCR_EN;                 // Stream aktiv X

    NVIC_SetPriority(DMA1_Stream5_IRQn, 1);
    NVIC_EnableIRQ(DMA1_Stream5_IRQn);
    // DAC1 DMA enable
    DAC->CR |= DAC_CR_DMAEN1;

    //DAC1 einschalten
    DAC->CR |= DAC_CR_EN1;

    DAC->CR |= DAC_CR_TEN1;    // Enable trigger
    DAC->CR |= (0b000 << 3);   // sel tim6


    while(1) {
    }
}