Forum: Mikrocontroller und Digitale Elektronik STM32F4 Discovery ADC & DMA


von Michi Müller (Gast)


Lesenswert?

Hey zusammen,

ich hab vor kurzer Zeit begonnen ein wenig mit mein STM32F4 Discovery 
Board zu spielen.

Nun habe ich geplant mehrere Sensoren auszulesen. Diese liefern eine 
Ausgangsspannung von 0-3 V. Sollte also kein problem für das Board sein.

Geplant ist die Durchführung mit (möglichst nur einem) ADC & DMA.

Nun habe ich mich bereits durch verschiedene Beispielcodes und Tutorials 
gearbeitet allerdings läuft mein Code noch nicht zu 100 %.

Verwendet werden:

PIN's PA3, PA5, PA7 (Ich weiß die Auswahl ist nicht perfekt aber zum 
testen wollte ich nur ADC1 initialsieren)

ADC1

DMA2 - Stream0


1
ADC_DeInit();                              //Resetet alle ADC Einstellungen
2
3
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
4
5
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
6
      ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;    // Wenn mehrere ADC's gleichzeitig verwendet werden zum synchronisieren !!!
7
      ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;    //AHB2 = 42 MHz     Div2 -> 21 MHz
8
      ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_10Cycles;
9
      ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
10
    ADC_CommonInit(&ADC_CommonInitStructure);
11
12
    ADC_InitTypeDef ADC_InitStructure;
13
      ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;    //Eingangsspannung in 12bit Wert mit max. Wert von 4096
14
      ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;      //Kontinuierliche Auswertung aller Regular-Kanäle (in Schleife)
15
      ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;    //wird _left _right bündig in 16 Bit Register abgespeichert
16
      ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;  //Signal welches die Wandlung auslösen soll
17
      ADC_InitStructure.ADC_ExternalTrigConv = 0;
18
      ADC_InitStructure.ADC_NbrOfConversion = 3;          //Anzahl der Kanäle in der "Regular Channel Groupe"
19
      ADC_InitStructure.ADC_ScanConvMode = ENABLE;        // ENABLE: Mehrere Kanäle werden im Scan-Modus gewandelt
20
      ADC_AutoInjectedConvCmd(ADC1, ENABLE);            //ENABLE: Nach abgearbeiteter Regular Group wird automatisch Injected Group angetriggert
21
    ADC_Init(ADC1, &ADC_InitStructure);
22
23
    //Kanäle werden manuell der Regular Group (bis 16 Kanäle) zugefügt (ADCx, Kanal, Reihenfolge zum Durchlaufen,Sample Time)
24
    ADC_RegularChannelConfig(ADC1, ADC_Channel_3  ,1 , ADC_SampleTime_84Cycles);  //Wählt Kanal 11 aus (PA3)
25
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5  ,2 , ADC_SampleTime_84Cycles);  //Wählt Kanal 12 aus (PA5)
26
    ADC_RegularChannelConfig(ADC1, ADC_Channel_7 ,3 , ADC_SampleTime_84Cycles);    //Wählt Kanal 13 aus (PA7)
27
28
    // Dauer einer Wandlung = (XCycles [-] + 12.5) /  ADCCLK [MHz] = X µs) z.B. 84+12,5/ 21 Mhz = 4,595 µs
29
30
31
    DMA_DeInit(DMA2_Stream0);                          //Resetet alle DMA Einstellungen
32
33
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
34
35
36
    DMA_InitTypeDef       DMA_InitStructure;
37
      DMA_StructInit(&DMA_InitStructure);
38
      DMA_InitStructure.DMA_Channel = DMA_Channel_0;
39
      DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCRead;      // Zieladresse
40
      DMA_InitStructure.DMA_PeripheralBaseAddr =  (uint32_t)&(ADC1->DR);   // Quelladresse / Peripheriegerät
41
      DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;         // from Peripheral to RAM
42
      DMA_InitStructure.DMA_BufferSize = 4;                  //
43
      DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;    // Source stays same address
44
      DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;         // Destination will be incremented
45
      DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16 bit results
46
      DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;     // 16 bit results
47
      DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;            // Läuft dauernd weiter ohne zu stoppen
48
      DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;         //
49
      DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
50
      DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
51
      DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
52
      DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
53
    DMA_Init(DMA2_Stream0, &DMA_InitStructure);                 // Init the stuff finally
54
55
56
    DMA_Cmd(DMA2_Stream0,ENABLE);    //Aktiviert DMA
57
58
59
    while(DMA_GetCmdStatus(DMA2_Stream0)==DISABLE){};
60
61
    ADC_DMACmd(ADC1, ENABLE);
62
    ADC_Cmd(ADC1, ENABLE);        //Aktiviert Wandler ADC1
63
    ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
64
65
    ADC_ContinuousModeCmd(ADC1,ENABLE);
66
67
    ADC_SoftwareStartConv(ADC1);    //Triggert erste Wandlung



Nun zur eigentlichen Frage:

Könntet ihr mir erklären was es mit der Initialisierung von

DMA_InitStructure.DMA_BufferSize = ...
DMA_InitStructure.DMA_PeripheralDataSize = ...
und
DMA_InitStructure.DMA_MemoryDataSize = ....

auf sich hat?



Was mir bisher bisher klar ist:
1 Byte = 8 bit
HalfWord = 2 Byte = 16 bit
Word = 4 Byte = 32 bit

Wonach werden aber die BufferSize und die DataSize ausgewählt?


Über eine Erklärung wäre ich sehr dankbar.


Gruß Michael

von Little B. (lil-b)


Lesenswert?

Das Reference Manual wird dir dabei sicher weiterhelfen.
Solltest du das noch nicht haben, so ist das sehr traurig, denn ohne 
dieses geht normalerweise gar nichts. Du findest es hier:
http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf

Dort findest du unter kapitel 10.5 verschiedene register, die durch 
diese initialisierung beschrieben werden. die erklärungen dazu im 
datenblatt werden die auswahl der parameter für die initialisierung 
erleichtern

grob zusammengefasst:

DMA_BufferSize = Anzahl der DMA-Transfers, oder Anzahl der Bytes, je 
nach dem, was für ein blödsinn STM an der stelle treibt

DMA_PeripheralDataSize = größe der periphären daten, hier 16 bit, damit 
12bit ADC werte übertragen werden können

DMA_MemoryDataSize = größe der RAM-plätze, natürlich auch 16 bit, damit 
die größen übereinstimmen.

Da ich mir immer unsicher bin, was STM für ein Rotz in seine 
Bibliotheken schreibt, initialisiere ich meine Hardware immer händisch. 
Das wirkt zwar zunächst umständlich, gibt aber einen besseren einblick 
in die hardware und läuft effizienter.

von public (Gast)


Lesenswert?

Mhh ich hatte auch mal mit dem DMA von ST zu tun.
Bin mir deshalb jetzt nicht mehr ganz sicher, aber deine Zieladresse 
(DMA_Memory0BaseAddr = (uint32_t)&ADCRead) ist das ein Array? Wo soll 
der DMA da genau hinschreiben? Wie sind deine Interrupts eingestellt, 
also wo/wann wird der Index deines Arrays zurückgesetzt? Braucht man 
dazu überhaupt einen Interrupt?

Da gibt es diesen jungen Typen hat ne Super Seite gemacht, vielleicht 
findest du dort deine Antworten!

http://www.diller-technologies.de/stm32.html#dma

(allerdings geht es dort um nen STM32F1/STM32V)

beste grüße
public

von Michi Müller (Gast)


Lesenswert?

Hey viele Dank für die Antworten.

Das Reference Manual habe ich bereits, aber bei 1800 Seiten ist das 
immer etwas kompliziert genau die richtigen Informationen 
herauszufiltern :-)

Trotzdem Danke für den Hinweis und die Erklärungen.


Die Website http://www.diller-technologies.de/stm32.html#dma habe ich 
auch bereits gefunden und die Initialisierung ist zwar bei einem STM32F1 
etwas anders als bei meinem Discovery Board aber die Erklärungen die er 
hat sind echt super.
Nur erläutert er die von mir angesprochenen Punkte für mich persönlich 
nicht genau genug. Ich möchte ja auch wissen und verstehen warum ich 
welchen Parameter bei der DMA einstelle :P

Ich werde mich also nochmal durch das Manual kämpfen.

Viele Dank trotzdem schonmal für eure Bemühungen.

Falls mir trotzdem noch jemand weiterhelfen mag ist das natürlich 
trotzdem gerne gesehen ;-)

Gruß Michael

von Michi Müller (Gast)


Lesenswert?

Ach ja und:

// Struktur zum Auslesen der Sensoren
typedef struct ADCValues{
  int32_t channel_3 ;
  int32_t channel_5 ;
  int32_t channel_7 ;
} ADCValues_t;


// DMA Stream schreibt hier rein
volatile ADCValues_t ADCRead;


Sensoren geben wie gesagt einen Spannungswert von 0-3 V aus. Interupt 
ist meiner Meinung nach nicht nötig da ja der Circular-Mode verwendet 
wird. Die Peripheral Adresse bleibt konstant und die Zieladresse wir 
nach jedem Konvertieren um eins inkrementiert-->Das Array ADCRead wird 
wie ein Ringspeicher überschrieben (Wenn ich das so richtig verstanden 
habe)

Gruß Michael

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.