Forum: Mikrocontroller und Digitale Elektronik STM32 - ADC mit DMA funktioniert nicht


von frank m. (frame)


Lesenswert?

Ich habe ein Problem mit ADC + DMA auf dem STM32. Die Controller-Spec.
und alle möglichen Beispiele, die Google auswirft, habe ich ausprobiert.
Vielleicht könnt ihr die Trial-And-Error Phase etwas abkürzen.
Das ist übrigens mein erstes STM32-Projekt, wenn auch nicht mein erstes
Controller-Projekt.

Ich will 2 ADC-Kanäle regelmäßig, aber selten auslesen (alle 
100...300ms).
Deswegen im Scan-Modus und mit Softwaretrigger.
Das Auslesen eines Kanals mit Interrupt funktioniert auch problemlos.

DMA funktioniert aber nicht. Das liegt aber nicht an einer fehlerhaften
ADC-Initialisierung. Nach dem Auslösen eine SW-Triggers kann ich im
Debugger stimmige ADC-Werte an ADC1->DR sehen.
Die DMA-Initialisierung habe auch geprüft, konnte aber keinen Fehler
entdecken. Ob die Werte wirklich in den DMA-Registern ankommen, weiß ich
nicht - die Atollic Lightversion, die ich verwende, zeigt keine Inhalte
der Peripherieregister an.
Den Inhalt der ADC-Register kann ich im Memory-Window sehen, die 
DMA-Register
nicht (warum eigentlich - wegen AHB vs. APB2 ?).

Der Interrupt für den DMA-Kanal dient eigentlich nur zur Kontrolle.
Aber der kommt natürlich auch nicht...

Hardware ist übrigens ein ST-Discovery mit STM32F100RBT6.

Folgend der Code der Initialisierung:
1
#define DR_ADDRESS   ((uint32_t)0x4001244C)
2
3
void  ADC_Configuration (void)
4
{
5
    NVIC_InitTypeDef  NVIC_InitStructure;
6
    ADC_InitTypeDef   ADC_InitStructure;
7
    DMA_InitTypeDef   DMA_InitStructure;
8
9
    /* PCLK2 is the APB2 clock */
10
    RCC_ADCCLKConfig (RCC_PCLK2_Div6);                        /* ADCCLK = PCLK2/6 = 72/6 = 12MHz*/
11
12
    RCC_APB2PeriphClockCmd (RCC_APB2Periph_ADC1, ENABLE);     /* Enable ADC1 clock so that we can talk to it */
13
    ADC_DeInit (ADC1);                                        /* Put everything back to power-on defaults */
14
15
    /* NVIC DMA interrupt setup */
16
    NVIC_InitStructure.NVIC_IRQChannel                   = DMA1_Channel1_IRQn;
17
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
18
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
19
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
20
    NVIC_Init(&NVIC_InitStructure);
21
22
    /* DMA1 channel1 configuration */
23
    DMA_DeInit(DMA1_Channel1);
24
    DMA_InitStructure.DMA_PeripheralBaseAddr = DR_ADDRESS;
25
    DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t) &ADCValues;
26
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;
27
    DMA_InitStructure.DMA_BufferSize         = 2;
28
    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
29
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
30
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
31
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;
32
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;
33
    DMA_InitStructure.DMA_Priority           = DMA_Priority_High;
34
    DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;
35
    DMA_Init (DMA1_Channel1, &DMA_InitStructure);
36
    DMA_Cmd (DMA1_Channel1, ENABLE);
37
38
    /* -------- ADC1 Configuration -------- */
39
    ADC_InitStructure.ADC_Mode               = ADC_Mode_Independent;
40
    ADC_InitStructure.ADC_ScanConvMode       = ENABLE;
41
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
42
    ADC_InitStructure.ADC_ExternalTrigConv   = ADC_ExternalTrigConv_None;
43
    ADC_InitStructure.ADC_DataAlign          = ADC_DataAlign_Right;
44
    ADC_InitStructure.ADC_NbrOfChannel       = 2;
45
    ADC_Init (ADC1, &ADC_InitStructure);
46
47
    /* ADC Ch1 & CH2 configuration */
48
    ADC_RegularChannelConfig (ADC1, ADC_Channel_1, 1, ADC_SampleTime_28Cycles5);
49
    ADC_RegularChannelConfig (ADC1, ADC_Channel_2, 2, ADC_SampleTime_28Cycles5);
50
    ADC_DMACmd (ADC1, ENABLE);                       /* Enable ADC1 DMA */
51
    ADC_Cmd (ADC1, ENABLE);                          /* Enable ADC1 */
52
53
54
    /* Enable DMA1 Channel Transfer Complete interrupt */
55
    DMA_ITConfig (DMA1_Channel1, DMA_IT_TC, ENABLE);
56
57
    ADC_ResetCalibration (ADC1);                     /* Enable Reset Calibration Register */
58
    while (ADC_GetResetCalibrationStatus (ADC1));    /* wait until reset of Calibration Register */
59
60
    ADC_StartCalibration (ADC1);                     /* Start ADC Calibration */
61
    while (ADC_GetCalibrationStatus (ADC1));         /* wait until end of calibration */
62
}
<END CODE>

Angestoßen wird eine Konvertierung mit:
1
    ADC_SoftwareStartConvCmd (ADC1, ENABLE);          /* start the conversion */

an anderer Stelle.

Wahrscheinlich ist die Lösung ganz einfach...

von STMBeginner (Gast)


Lesenswert?

Wo hast Du die RCC_APBPeriphClock für den DMA eingestellt?

von frank m. (frame)


Lesenswert?

Kurze Antwort: gar nicht.

Genau das hat gefehlt. Jetzt funktioniert es auch.

Ich habe die DMA und ADC-Abschnitte im STM32F10x-Referenzmanual bestimmt
ein Dutzend Mal durchgekämmt... Aber das die Peripherie standardmäßig
vom Takt abgeklemmt ist, wird wohl als Grundwissen vorausgesetzt.
Ich bin wohl nicht der erste ARM-Neuling, der da mit der Nase drauf
gestoßen wird.

Wenn ich Zeit habe, werde ich das Cortex-M3 Ref.-Manual durcharbeiten.
Ein ARM ist eben doch etwas komplexer als die 8-Bitter, mit den ich
sonst so hantiere.

Danke nochmal.

von STMBeginner (Gast)


Lesenswert?

frank meyer schrieb:
> Danke nochmal.
Kein Problem :)

> Wenn ich Zeit habe, werde ich das Cortex-M3 Ref.-Manual durcharbeiten.
> Ein ARM ist eben doch etwas komplexer als die 8-Bitter, mit den ich
> sonst so hantiere.
Ja es ist eine kleine Bürde und kann auch etwas lästig werden. Ich bin 
auch noch nicht so lang bei den STM und vorher bei den 8 Bittern.
Aber das Manual ist irgendwie Pflichtlektüre und macht auch Spass,
wenn man bedenkt was man alles in und an den 32Bittern entdecken kann. 
Es lohnt sich.

Viel Spass noch

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Ja, ja, das steht im Artikel STM32 und zwar hier:
http://www.mikrocontroller.net/articles/STM32#Tipps_f.C3.BCr_Umsteiger_von_Atmel.2FPIC.2F8051
"Tipps für Umsteiger von Atmel/PIC/8051"

von Joghurt3000 (Gast)


Lesenswert?

Macht das denn Sinn ohne Timer trigger? Momentan sieht es so aus als 
wenn eine Messung gemacht wird (oder zumindest jede einzeln getriggert 
wird) und dann das Ergebnis per DMA in den Speicher geschoben wird. Habe 
ich mich verguckt?

von frame (Gast)


Lesenswert?

Natürlich wird das ADC_SoftwareStartConvCmd() timergesteuert
aufgerufen, allerdings abgeleitet vom Systick-Timer.

Das hatte allerdings nichts mit dem Problem zu tun, das lag
schon weiter oben in der Initialisierung. Wenn der DMA keinen
Takt bekommt, transferiert er auch nichts.

Und als ARM-Einsteiger fällt es manchmal schwer, von Letzterem
auf Ersteres zu schließen...

von Markus K. (markus_k64) Flattr this


Lesenswert?

frank meyer schrieb:

Ich hoffe, das liest noch jemand nach einem Jahr, da ich vor fast dem 
gleichen Problem - wenn auch mit dem STM32F4 - stehe.

Meine Frage zielt genau auf folgende Zeilen von dir:
1
/* DMA1 channel1 configuration */
2
DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t) &ADCValues;
3
// [...]
4
DMA_InitStructure.DMA_BufferSize         = 2;
5
// [...]
6
ADC_InitStructure.ADC_NbrOfChannel       = 2;
Ich will auch zwei Channel per DMA auslesen und nehme an, dass deshalb 
Buffersize = 2 ist?
1
// [...]
2
/* ADC Ch1 & CH2 configuration */
3
ADC_RegularChannelConfig (ADC1, ADC_Channel_1, 1, ADC_SampleTime_28Cycles5);
4
ADC_RegularChannelConfig (ADC1, ADC_Channel_2, 2, ADC_SampleTime_28Cycles5);
5
// [...]
Hier wird offenbar die Reihenfolge des Auslesens der ADC Channel 
festgelegt.

Wie aber genau hast du die Variable "ADCValues" hier deklariert? Was 
genau erwartet der Struct, wenn ich ja hinterher ZWEI Werte benötige? 
Mit einem Kanal funktioniert das Ganze nämlich einwandfrei bei mir.

Ich habe es mit einem Array probiert, habe aber keine Werte hinterher 
darin. Irgendwie stehe ich auf dem Schlauch...

Ich versuche es nun schon seit Tagen, habe aber echt keine Idee, wäre 
also für jede Hilfe dankbar!

Gruß,

Markus

von Markus K. (markus_k64) Flattr this


Lesenswert?

Hm, habe gerade hier Beitrag "Re: STM32 ADC & DMA" 
genau die Antwort gefunden und gesehen, dass ich in meinem Code die 
DMA_BufferSize noch gar nicht auf 2 erhöht habe...

Kann mir das erst heute Abend ansehen und es dann hoffentlich sogar 
selbst lösen.

Melde mich. :-)

von Markus K. (markus_k64) Flattr this


Lesenswert?

Okay, eigenen Code gereviewd... Ich hatte tatsächlich
1
DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
vergessen! Danke für das schöne Beispiel! :-)

PS.: Und für die, die es interessiert, wie ADCvalues deklariert ist:
1
__IO uint16_t ADCvalues[2];

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Ich schieb die ADC Werte z.b. in ein struct:
1
typedef struct ADCPlace {
2
 uint16_t spare1;
3
 uint16_t speedInput;
4
 uint16_t current;
5
 uint16_t temperature;
6
} ADCplace_t;
7
adcplace_t ADCRead:
Find ich persönlich übersichtlicher, wenn ich dann mit z.B.
amplitude = ADCRead.speedInput rankomme, aber bei mir stehen eben auch 
sehr unterschiedliche Inputs (Motorsteuerung).

von Markus K. (markus_k64) Flattr this


Lesenswert?

Keine schlechte Idee. Macht's auf jeden Fall sprechender...

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.