STM32 ADC & DMA

Gast #2055018
Lesenswert?
• ▲
▼
Hallo,

ich möchte auf einem STM32 (STM32-H103 Olimex Board) mittels internem 
ADC mehrere analoge Werte ermitteln und diese dann per DMA 
"wegschreiben". Am Ende soll ein Interrupt ausgelöst werden, der mir 
mitteilt, dass Daten zur Auswertung vorhanden sind.
Ich habe zuerst versucht mittels des Channel 16 (da sollte ein interner 
Temperatursensor dran sein) ADC und DMA in Betrieb zu nehmen. Dazu nutze 
ich folgenden Code:
1
DMA_InitTypeDef  DMA_InitStructure;
2
ADC_InitTypeDef  ADC_InitStructure;
3
NVIC_InitTypeDef NVIC_InitStructure;
4

5
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
6

7
/* Enable peripheral clocks 
8
/* Enable DMA1 clock */
9
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
10

11
/* Enable ADC1 Periph clock */
12
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
13

14

15
/* Enable DMA1 channel5 IRQ Channel */
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
DMA_DeInit(DMA1_Channel1);
23
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
24
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&dma_buf;
25
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
26
DMA_InitStructure.DMA_BufferSize = 1;
27
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
28
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
29
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
30
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
31
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
32
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
33
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
34
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
35

36
ADC_DeInit(ADC1);
37

38
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
39
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
40
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
41
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
42
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
43
ADC_InitStructure.ADC_NbrOfChannel = 1;
44
ADC_Init(ADC1, &ADC_InitStructure);
45

46
/* ADC1 RegularChannelConfig Test */
47
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_28Cycles5);
48

49
/* Enable DMA1 Channel6 Transfer Complete interrupt */
50
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
51

52
/* Enable DMA1 Channel5 */
53
DMA_Cmd(DMA1_Channel1, ENABLE);
54

55
ADC_DMACmd(ADC1, ENABLE);
56
ADC_Cmd(ADC1, ENABLE);
57

58
/* Enable ADC1 reset calibaration register */
59
ADC_ResetCalibration(ADC1);
60

61
/* Check the end of ADC1 reset calibration register */
62
while(ADC_GetResetCalibrationStatus(ADC1));
63

64
/* Start ADC1 calibaration */
65
ADC_StartCalibration(ADC1);
66

67
/* Check the end of ADC1 calibration */
68
while(ADC_GetCalibrationStatus(ADC1));
69

70
/* Start ADC1 conversion */
71
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

ADC und DMA scheinen auch zu funktionieren, jedoch wenn ich
1
/* Enable DMA1 Channel6 Transfer Complete interrupt */
2
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
mache, scheint er sich entweder aufzuhängen, oder in einer 
Interruptroutine zu kreisen. Sicher ist nur, dass die ISR, in der er 
hängt, nicht die für den eingestellten DMA Channel 
(DMA1_Channel1_IRQHandler) ist. Hat jemand 'ne Idee, was ich da falsch 
mache?

Gr. Thomas
Gast #2228584
Lesenswert?
• ▲
▼
Danke für den Anhang. Der Quelltaxt entspricht dem Obigen.
Ich bin mir relativ sicher, dass es normal ist, schließlich wird der ADC 
sehr schnell gesamplet und der DMA entsprechend schnell getriggert.
Wenn man die NVIC Initialisierung weg lässt, ist alles ok.
Ich lasse ihn automatisch ein Array mit 8 ADC füllen.

Was tust Du in der ISR?

IRQ Frage: Da ich besagtes Array auslesen möchte, muss sichergestellt 
werden, dass der DMA nicht gleichzeitig hineinschreibt, also DISABLE ich 
ihn vorher. Ich war aber nicht sicher ob er die 2 byte im Block oder 
unterbrechbar schreibt. Es könnte also passieren, dass der Wert beim 
schreiben unterbrochen wird. Worst case sollte ein Sprung von
0x00FF auf 0x0100 sein, somit einen Wert von 0x01FF ergeben.
Um sicher zu gehen setze ich ein flag, dass ich in der ISR toggle um 
dort den DMA zu deaktivieren.
Weißt Du etwas genaueres?
(Firma: Raich Gerätebau & Entwicklung) #2228704
Lesenswert?
• ▲
▼
@Magreet
Die ISR arbeitet mit Doppelpuffer.Wenn der eine beschrieben wird, wird 
der andere verarbeitet. Dazu werden einfach die Pointer getauscht.Nach 
jedem Interrupt wird DMA disabelt (sonst lässt sich die dest. Adresse 
nicht ändern)und dann wieder neu gestartet.

Grüße
Gast #2372883
Lesenswert?
• ▲
▼
Hallo,

ich möchte noch mal das Problem neu aufwerfen!

Ich versuche ADC Werte einzulesen von 2 Kanäle und benutze auch DMA.

Leider ist bei 2 Schluss.
Wie also konfiguriere ich die ADC_Init richtig damit er mir bis zu 4 
Kanäle ausließt und übermittelt? Wenn geht ohne trigger und Timer, oder 
geht das gar nicht anders?

Folgenden Code habe ich und er gibt mir nur den AD wert vom Kanal mit 
der Höchsten Priorität zurück. Was mache ich falsch?

void adc_Init(void)
{
  /* DMA1 channel1 configuration 
----------------------------------------------*/
  DMA_DeInit(DMA1_Channel1);
  DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC1ConvertedValue;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  DMA_InitStructure.DMA_BufferSize = 2;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
  DMA_InitStructure.DMA_PeripheralDataSize = 
DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);
  /* Enable DMA1 channel1 */
  DMA_Cmd(DMA1_Channel1, ENABLE);

  /* ADC1 Configuration 
------------------------------------------------------*/
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 2;
  ADC_Init(ADC1, &ADC_InitStructure);

  /* ADC1 regular channel10 configuration */
  ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 1, 
ADC_SampleTime_13Cycles5);
  ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 2, 
ADC_SampleTime_13Cycles5);

  /* Enable ADC1 */
  ADC_Cmd(ADC1, ENABLE);
  /* Enable ADC1 DMA */
  ADC_DMACmd(ADC1, ENABLE);

  /* Enable ADC1 reset calibration register */
  ADC_ResetCalibration(ADC1);
  /* Check the end of ADC1 reset calibration register */
  while(ADC_GetResetCalibrationStatus(ADC1));

  /* Start ADC1 Software Conversion */
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

Danke im Voraus für die Hilfe!
#2373673
Lesenswert?
• ▲
▼
Vermutlich liegt der Fehler bei
1
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC1ConvertedValue;

Wenn es ein einzelner Wert waere, waere dies richtig.
Du willst aber 2 Werte einlesen.
Deshalb
1
volatile uint16_t adc_buffer[2];
2
:
3
:
4
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)adc_buffer;
5
:

Antwort schreiben

Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.