Forum: Mikrocontroller und Digitale Elektronik STM32Duino DMA DAC


von Marc (Gast)


Lesenswert?

Ich möchte ein Sinus- und Cosinus Signal auf den zwei DACs eines 
STM32F303 ausgeben.
Praktischerweise hat hier "IVONOMICON" eine sehr schöne Bare-Metal 
Konfiguration für einen Kanal geschrieben.
Nun will ich nicht STM-Cube installieren, sondern das ganze mit der 
Arduino-IDE compilieren.

Ich habe die Bare-Metal Konfiguration etwas modifiziert und in "setup"- 
und "loop" verpackt, das Bare-Metal Delay und durch das Arduino Delay 
ersetzt, die Tabelle ins RAM verlagert und auf 1024 für einen 
hochauflösenden Sinus erweitert.
Und siehe da, es funktioniert super ! Cool :-)
1
// STM32F303 bare bone DMA DAC
2
// create a sin wave table an move the values via DMA to the DAC
3
4
// https://vivonomicon.com/2019/07/05/bare-metal-stm32-programming-part-9-dma-megamix/
5
6
uint16_t Frequency = 1000;
7
const size_t SINE_SAMPLES = 1024;
8
uint16_t SINE_WAVE[SINE_SAMPLES];
9
10
void initializeSineTable()
11
{
12
  float amplitude = 0.8;
13
  for (int n = 0; n < SINE_SAMPLES; n++)
14
  {
15
    SINE_WAVE[n] = sin(n * 2 * PI / SINE_SAMPLES) * amplitude * 2047 + 2048;
16
  }
17
}
18
19
// Global variable to hold the core clock speed in Hertz.
20
//uint32_t SystemCoreClock = 8000000;
21
22
void setup()
23
{
24
  initializeSineTable();
25
26
  // Enable peripherals: GPIOA, DMA, DAC, TIM6, SYSCFG.
27
  RCC->AHBENR   |= ( RCC_AHBENR_GPIOAEN |
28
                     RCC_AHBENR_DMA1EN );
29
  RCC->APB1ENR  |= ( RCC_APB1ENR_DAC1EN |
30
                     RCC_APB1ENR_TIM6EN );
31
  RCC->APB2ENR  |= RCC_APB2ENR_SYSCFGEN;
32
  // Pin A4: analog mode. (PA4 = DAC1, Channel 1)
33
  GPIOA->MODER    &= ~( 0x3 << ( 4 * 2 ) );
34
  GPIOA->MODER    |=  ( 0x3 << ( 4 * 2 ) );
35
  // Set the 'TIM6/DAC1 remap' bit in SYSCFG_CFGR1,
36
  // so that DAC1_Ch1 maps to DMA1_Ch3 instead of DMA2_Ch3.
37
  // (Not all STM32F303 chips have a DMA2 peripheral)
38
  SYSCFG->CFGR1 |=  ( SYSCFG_CFGR1_TIM6DAC1Ch1_DMA_RMP );
39
  // DMA configuration (DMA1, channel 3).
40
  // CCR register:
41
  // - Memory-to-peripheral
42
  // - Circular mode enabled.
43
  // - Increment memory ptr, don't increment periph ptr.
44
  // - 16-bit data size for both source and destination.
45
  // - High priority (2/3).
46
  DMA1_Channel3->CCR &= ~( DMA_CCR_MEM2MEM |
47
                           DMA_CCR_PL |
48
                           DMA_CCR_MSIZE |
49
                           DMA_CCR_PSIZE |
50
                           DMA_CCR_PINC |
51
                           DMA_CCR_EN );
52
  DMA1_Channel3->CCR |=  ( ( 0x2 << DMA_CCR_PL_Pos ) |
53
                           ( 0x1 << DMA_CCR_MSIZE_Pos ) |
54
                           ( 0x1 << DMA_CCR_PSIZE_Pos ) |
55
                           DMA_CCR_MINC |
56
                           DMA_CCR_CIRC |
57
                           DMA_CCR_DIR );
58
  // Set DMA source and destination addresses.
59
  // Source: Address of the sine wave buffer in memory.
60
  DMA1_Channel3->CMAR  = ( uint32_t )&SINE_WAVE;
61
  // Dest.: DAC1 Ch1 '12-bit right-aligned data' register.
62
  DMA1_Channel3->CPAR  = ( uint32_t ) & ( DAC1->DHR12R1 );
63
  // Set DMA data transfer length (# of sine wave samples).
64
  DMA1_Channel3->CNDTR = ( uint16_t )SINE_SAMPLES;
65
  // Enable DMA1 Channel 1.
66
  // Note: the transfer won't actually start here, because
67
  // the DAC peripheral is not sending DMA requests yet.
68
  DMA1_Channel3->CCR |= ( DMA_CCR_EN );
69
  // TIM6 configuration. This timer will set the frequency
70
  // at which the DAC peripheral requests DMA transfers.
71
  // Set prescaler and autoreload for a 440Hz sine wave.
72
  TIM6->PSC  =  ( 0x0000 );
73
  TIM6->ARR  =  ( SystemCoreClock / ( Frequency * SINE_SAMPLES ) );
74
  // Enable trigger output on timer update events.
75
  TIM6->CR2 &= ~( TIM_CR2_MMS );
76
  TIM6->CR2 |=  ( 0x2 << TIM_CR2_MMS_Pos );
77
  // Start the timer.
78
  TIM6->CR1 |=  ( TIM_CR1_CEN );
79
  // DAC configuration.
80
  // Set trigger sources to TIM6 TRGO (TRiGger Output).
81
  DAC1->CR  &= ~( DAC_CR_TSEL1 );
82
  // Enable DAC DMA requests for channel 1.
83
  DAC1->CR  |=  ( DAC_CR_DMAEN1 );
84
  // Enable DAC channel 1.
85
  DAC1->CR  |=  ( DAC_CR_EN1 );
86
  // Delay briefly to allow sampling to stabilize.
87
88
  delay(10);
89
  // Enable DAC channel trigger.
90
  // The DMA channel and timer are both already on, so the
91
  // DMA transfer will start as soon as the DAC peripheral
92
  // starts making requests. The DAC peripheral will make a
93
  // request every time that TIM6 ticks over, but only after
94
  // this 'trigger enable' bit is set.
95
  DAC1->CR  |=  ( DAC_CR_TEN1 );
96
  // Done; a low-res 440Hz sine wave should be playing on PA4.
97
  Serial.begin(115200);  
98
}
99
100
void loop()
101
{
102
  Serial.println(analogRead(A0)); // just reading the ADC to show something on the serial line
103
  delay(100);
104
}

Allerdings brauche ich ja zwei Kanäle. Den zweiten für den Cosinus.
Wie würdet ihr vorgehen?
Was gibt es für einen Grund, dass im Code "DMA1_Channel3" verwendet 
wird?
Kann man für den zweiten Kanal einfach "DMA1_Channel4" verwenden?

von J. S. (jojos)


Lesenswert?

Die DMA Kanäle sind nicht ganz frei zuzuordnen, da muss man in das 
Reference Manual schauen welche Möglichkeiten es gibt.

von Marc (Gast)


Angehängte Dateien:

Lesenswert?

>Die DMA Kanäle sind nicht ganz frei zuzuordnen, da muss man in das
>Reference Manual schauen welche Möglichkeiten es gibt.

So richtig klar wird es mir nicht, wie die Kanäle zugeordnet sind.

Es gibt an der MCU

3 DACs: DAC1/1 DAC1/2 und DAC1/2 .. warum die seltsamen Bezeichnungen?
Dann scheint es so zu sein, dass man DAC1_CH1 auf DMA1 Kanal 3 routen 
kann (so wie oben im Code gemacht)

Frage:
Warum gibt es zwei DMAs, die aber mit vielen Kanälen?
Im Schaubild sieht es so aus, als wenn man DAC1_CH2 auf den DMA Kanal 4 
der DMA1 routen kann. Geht das gleichzeitig mit der Einstellung für 
Kanal 3 ?
Der Eintrag in der Tabelle 79 passt aber nicht, weil dort bei Kanal 4 
DAC2_CH2 steht .. ist das ein Fehler im Datenblatt?

von STK500-Besitzer (Gast)


Lesenswert?

Marc schrieb:
> Warum gibt es zwei DMAs, die aber mit vielen Kanälen?

Weil andere Schnittstellen auch per DMA steuerbar sind.

Marc schrieb:
> Im Schaubild sieht es so aus, als wenn man DAC1_CH2 auf den DMA Kanal 4
> der DMA1 routen kann. Geht das gleichzeitig mit der Einstellung für
> Kanal 3 ?

Für diese Information bietet sich ein Blick in das Reference Manual des 
STM32F303 an.

von Marc (Gast)


Lesenswert?

>Für diese Information bietet sich ein Blick in das Reference Manual des
>STM32F303 an.

Genau .. und daraus habe ich auch die obigen Bilder kopiert.
Im Pinout für den NucleoF303K8 gibt es einen DAC2_CH1 aber in der 
Tabelle gibt es DAC2_CH2. Das passt irgendwie nicht zusammen.

von Kevin M. (arduinolover)


Lesenswert?

Auf dem NUCLEO sind DAC1 Ch1 und Ch2 rausgeführt. DMA1 kann aber nur 
DAC1 Ch1.

von Marc (Gast)


Angehängte Dateien:

Lesenswert?

>Auf dem NUCLEO sind DAC1 Ch1 und Ch2 rausgeführt. DMA1 kann aber nur
>DAC1 Ch1.

Das passt aber nicht zur Footnote im Reference-Manual.

Dort gibt es das Bit 14 im SYSCFG Register zum Mappen von Timer 7, 
DAC1_CH2

SYSCFG_CFGR1_TIM7DAC1Ch2_DMA_RMP

von LM337 vs. LM317 (Gast)


Angehängte Dateien:

Lesenswert?

Versuchs doch mal mit dem DualMode (siehe Anhang). Damit brauchst du für 
beide Kanäle nur einen DMA. Ein weiterer Vorteil ist das beide Kanäle 
gleichzeitig geschrieben werden.

Deine SIN Tabelle müsste dann mit uint32_t sein und sowohl sin als auch 
cos Werte enthalten.

Die DMA musst du auch noch anpassen.

Das DMA1_Channel3->CPAR kann DAC_DHR12LD oder DAC_DHR12RD sein
16-bit data size sollte noch 32-bit data size geändert werden.

Cheers

von Marc (Gast)


Lesenswert?

>Versuchs doch mal mit dem DualMode (siehe Anhang).

Danke für den Hinweis. Nach einiger Suche bin ich auch schon auf den 
Dual-Mode gestoßen.
Aber irgendwie funktioniert trotzdem nur der Ausgang PA4. Es sieht so 
aus, als wenn PA5 hochohmig bleibt (das Oszi floated). Die Gegenprobe 
mit analogWrite zeigt aber, dass der Kanal hardwaremässig funktioniert.

Mache ich irgend was falsch bei der Port Konfiguration?

Hier mal der nur auf einem Kanal funktionierende Code, bei dem der 
zweite einfach nicht will:
1
// STM32F303 bare bone DMA DAC
2
// create a sin wave table an move the values via DMA to the DAC
3
4
// https://vivonomicon.com/2019/07/05/bare-metal-stm32-programming-part-9-dma-megamix/
5
6
uint16_t Frequency = 100;
7
8
const size_t SINE_SAMPLES = 1024;
9
10
uint32_t SINE_WAVE[SINE_SAMPLES];
11
12
void initializeSineTable()
13
{
14
  float amplitude = 0.8;
15
  for (int n = 0; n < SINE_SAMPLES; n++)
16
  {
17
    uint32_t x= sin(n * 2 * PI / SINE_SAMPLES) * amplitude * 2047 + 2048;
18
    uint32_t y= cos(n * 2 * PI / SINE_SAMPLES) * amplitude * 2047 + 2048;
19
    SINE_WAVE[n] = x+(y<<16);
20
  }
21
}
22
23
void setDMA_DAC_dualMode()
24
{
25
  // Enable peripherals: GPIOA, DMA, DAC, TIM6, SYSCFG.
26
  RCC->AHBENR   |= ( RCC_AHBENR_GPIOAEN | RCC_AHBENR_DMA1EN );
27
  RCC->APB1ENR  |= ( RCC_APB1ENR_DAC1EN | RCC_APB1ENR_TIM6EN );
28
  RCC->APB2ENR  |= RCC_APB2ENR_SYSCFGEN;
29
  // Pin A4: analog mode. (PA4 = DAC1, Channel 1)
30
  GPIOA->MODER    &= ~( 0x3 << ( 4 * 2 ) );
31
  GPIOA->MODER    |=  ( 0x3 << ( 4 * 2 ) );
32
  // Pin A5: analog mode. (PA5 = DAC1, Channel 2)
33
  GPIOA->MODER    &= ~( 0x3 << ( 5 * 2 ) );
34
  GPIOA->MODER    |=  ( 0x3 << ( 5 * 2 ) );
35
  // Set the 'TIM6/DAC1 remap' bit in SYSCFG_CFGR1,
36
  // so that DAC1_Ch1 maps to DMA1_Ch3 instead of DMA2_Ch3.
37
  // (Not all STM32F303 chips have a DMA2 peripheral)
38
  SYSCFG->CFGR1 |=  ( SYSCFG_CFGR1_TIM6DAC1Ch1_DMA_RMP );
39
  // DMA configuration (DMA1, channel 3).
40
  // CCR register:
41
  // - Memory-to-peripheral
42
  // - Circular mode enabled.
43
  // - Increment memory ptr, don't increment periph ptr.
44
  // - 32-bit data size for both source and destination.
45
  // - High priority (2/3).
46
  DMA1_Channel3->CCR &= ~( DMA_CCR_MEM2MEM |
47
                           DMA_CCR_PL |
48
                           DMA_CCR_MSIZE |
49
                           DMA_CCR_PSIZE |
50
                           DMA_CCR_PINC |
51
                           DMA_CCR_EN );
52
  DMA1_Channel3->CCR |=  ( ( 0x2 << DMA_CCR_PL_Pos ) |
53
                           ( 0x2 << DMA_CCR_MSIZE_Pos ) |
54
                           ( 0x2 << DMA_CCR_PSIZE_Pos ) |
55
                           DMA_CCR_MINC |
56
                           DMA_CCR_CIRC |
57
                           DMA_CCR_DIR );
58
               
59
  // Set DMA source and destination addresses.
60
  // Source: Address of the sine wave buffer in memory.
61
  DMA1_Channel3->CMAR  = ( uint32_t )&SINE_WAVE;
62
  //obsolete: Dest.: DAC1 Ch1 '12-bit right-aligned data' register.
63
  //Dual DAC 12-bit right-aligned data holding register
64
  //DMA1_Channel3->CPAR  = ( uint32_t ) & ( DAC1->DHR12RD );
65
  // Set DMA data transfer length (# of sine wave samples).
66
  DMA1_Channel3->CNDTR = ( uint16_t )SINE_SAMPLES;
67
68
  // TIM6 configuration. This timer will set the frequency
69
  // at which the DAC peripheral requests DMA transfers.
70
  // Set prescaler and autoreload for the frequency
71
  TIM6->PSC  =  ( 0x0000 );
72
  TIM6->ARR  =  ( SystemCoreClock / ( Frequency * SINE_SAMPLES ) );
73
  // Enable trigger output on timer update events.
74
  TIM6->CR2 &= ~( TIM_CR2_MMS );
75
  TIM6->CR2 |=  ( 0x2 << TIM_CR2_MMS_Pos );
76
  // Start the timer.
77
  TIM6->CR1 |=  ( TIM_CR1_CEN );
78
  
79
  // DAC dual mode
80
  // https://electronics.stackexchange.com/questions/607624/what-does-dual-dac-mode-mean-for-this-microcontroller-board
81
  // Reference Manual RM0316
82
  // page 432
83
  // DAC configuration.
84
  //
85
  // ***  Simultaneous trigger without wave generation **
86
  //  To configure the DAC in this conversion mode, the following sequence is required:
87
  //  1. Set the two DAC channel trigger enable bits TEN1 and TEN2
88
  DAC1->CR  |=  ( DAC_CR_TEN1 );
89
  DAC1->CR  |=  ( DAC_CR_TEN2 ); // channel 2 trigger enable
90
91
  // 2. Configure the same trigger source for both DAC channels by setting the same value in
92
  // the TSEL1[2:0] and TSEL2[2:0] bits
93
  // Set trigger sources to TIM6 TRGO (TRiGger Output).
94
  DAC1->CR  &= ~( DAC_CR_TSEL1 );
95
  DAC1->CR  &= ~( DAC_CR_TSEL2 ); // channel 2 trigger source the same
96
97
  //3. Load the dual DAC channel data to the desired DHR register (DAC_DHR12RD,
98
  //    DAC_DHR12LD or DAC_DHR8RD)
99
  DMA1_Channel3->CPAR  = ( uint32_t ) & ( DAC1->DHR12RD );
100
  // Note: the transfer won't actually start here, because
101
102
  //When a trigger arrives, the DHR1 and DHR2 registers are transferred into DAC_DOR1 and
103
  //DAC_DOR2, respectively (after three APB clock cycles).
104
  
105
  // Enable DAC DMA requests for channel 1.
106
  DAC1->CR  |=  ( DAC_CR_DMAEN1 );
107
  DAC1->CR  |=  ( DAC_CR_DMAEN2 );
108
  // Enable DAC channel 1.
109
  DAC1->CR  |=  ( DAC_CR_EN1 );
110
  DAC1->CR  |=  ( DAC_CR_EN2 );
111
  // Delay briefly to allow sampling to stabilize.
112
113
  delay(10);
114
  // the DAC peripheral is not sending DMA requests yet.
115
  DMA1_Channel3->CCR |= ( DMA_CCR_EN );
116
  
117
}
118
119
void setup()
120
{
121
  initializeSineTable();
122
  setDMA_DAC_dualMode();
123
  
124
  // Done; a low-res 440Hz sine wave should be playing on PA4.
125
  Serial.begin(115200);
126
}
127
128
void loop()
129
{
130
  static int n;
131
132
  Serial.print(SINE_WAVE[n]&0xFFFF); Serial.print(' ');Serial.println((SINE_WAVE[n++]>>16)&0xFFFF);
133
  if(n>=SINE_SAMPLES)n=0;
134
  delay(10);
135
}

von Marc (Gast)


Lesenswert?

Wenn ich die direkte Initialisierung von GPIOA durch analogWrite ersetze 
geht es ...
Hat irgend jemand eine Idee, woran das liegen könnte?
1
  analogWrite(PA4,0);
2
  analogWrite(PA5,0);
3
  //GPIOA->MODER    &= ~( 0x3 << ( 4 * 2 ) );
4
  //GPIOA->MODER    |=  ( 0x3 << ( 4 * 2 ) );
5
  // Pin A5: analog mode. (PA5 = DAC1, Channel 2)
6
  //GPIOA->MODER    &= ~( 0x3 << ( 5 * 2 ) );
7
  //GPIOA->MODER    |=  ( 0x3 << ( 5 * 2 ) );

von LM337 vs. LM317 (Gast)



Lesenswert?

Hast du ein Nucleo 32 F303?
Falls ja wie hast du PB7 konfiguriert? Bzw. wie sieht es mit SB18 aus?

von Marc (Gast)


Lesenswert?

>Hast du ein Nucleo 32 F303?

Ja, es ist ein Nucleo F303K8 wie oben im Bild 
https://www.mikrocontroller.net/attachment/576207/NucleoF303K8_DAC_DMA.png
Das Board ist unverändert, I2C benutze ich nicht und die DMA 
funktioniert ja auf beiden Kanällen, wenn ich die analogWrite statt der 
Baremetal Initialisierung mit MODER benutze.

von Marc (Gast)


Lesenswert?

Hier mal eine "baremetal" Implementierung für ein Nucleo STM32L432KC 
ohne DMA.
1
// Nucleo STM32L432KC baremetal DACs for STM32Duino
2
// Sawtooth signals are generated on DAC1 and DAC2 outputs
3
// 11.11.22 
4
// 
5
// https://www.mikrocontroller.net/topic/545574#7245120
6
7
/*
8
  RM0394
9
  Reference manual
10
  STM32L41xxx/42xxx/43xxx/44xxx/45xxx/46xxx
11
  advanced Arm ® -based 32-bit MCUs
12
*/
13
14
// definitions can be found in
15
// .. arduino15/packages/STMicroelectronics/hardware/stm32/2.3.0/system/Drivers/CMSIS/Device/ST/STM32L4xx/Include
16
// stm32l432xx.h
17
18
19
#define DAC1_1 A3
20
#define DAC1_2 A4
21
22
23
void initializeDAC()
24
{
25
  analogWrite(DAC1_1, 0); // initialize DAC1 port
26
  analogWrite(DAC1_2, 0); // initialize DAC2 port
27
28
  // DAC configuration.
29
  //TSEL1[2:0]: DAC channel1 trigger selection
30
  //These bits select the external event used to trigger DAC channel1
31
  //Refer to the trigger selection tables in Section 17.4.6: DAC trigger selection for the details on
32
  //trigger configuration and mapping.
33
  //Note: Only used if bit TEN1 = 1 (DAC channel1 trigger enabled).
34
  DAC1->CR  |=  ( DAC_CR_TSEL1 );
35
  
36
  //MODIFY_REG(DAC1->CCR, DAC_MCR_MODE1, 0);// 000: DAC Channel 1 is connected to external pin with Buffer enabled
37
  DAC1->CCR &= ~(DAC_MCR_MODE1);
38
  
39
  // Enable DAC channel 1.
40
  DAC1->CR  |=  ( DAC_CR_EN1 );
41
  // Delay briefly to allow sampling to stabilize.
42
  delay(10);
43
  
44
  //TEN1: DAC channel1 trigger enable
45
  //This bit is set and cleared by software to enable/disable DAC channel1 trigger.
46
  //0: DAC channel1 trigger disabled and data written into the DAC_DHR1 register are
47
  //transferred one APB1 clock cycle later to the DAC_DOR1 register
48
  //1: DAC channel1 trigger enabled and data from the DAC_DHR1 register are transferred
49
  //three APB1 clock cycles later to the DAC_DOR1 register
50
  //When software trigger is selected, the transfer from the DAC_DHR1 register to the
51
  //DAC_DOR1 register takes only one APB1 clock cycle.
52
  //DAC1->CR  |=  ( DAC_CR_TEN1 );
53
  DAC1->CR  &= ~  ( DAC_CR_TEN1 ); // no trigger, change directly after write
54
55
// channel 2
56
  DAC1->CR  |=  ( DAC_CR_TSEL2 );
57
  DAC1->CCR &= ~(DAC_MCR_MODE2);
58
  DAC1->CR  |=  ( DAC_CR_EN2 );
59
  delay(10);
60
  DAC1->CR  &= ~  ( DAC_CR_TEN2 );
61
}
62
63
void setup()
64
{
65
  initializeDAC();
66
}
67
68
void loop()
69
{
70
  for (uint16_t n = 0; n < 4096; n++)
71
  {
72
    uint32_t a=n;
73
    uint32_t b=4095-n;
74
    
75
    DAC1->DHR12RD = a+(b<<16);
76
77
    //DAC1->SWTRIGR|=(1<<DAC_SWTRIGR_SWTRIG1_Pos); // not trigger needed when TEN1=0
78
79
    //delayMicroseconds(1);
80
  }
81
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.