Forum: Mikrocontroller und Digitale Elektronik STM32 ADC & DMA


von Thomas (Gast)


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

von Magreet (Gast)


Lesenswert?

Schließe mich da an, für eine Lösung wäre ich sehr dankbar!

von Magreet (Gast)


Lesenswert?

Vllt ist es auch normal, dass er ständig in der DMA ISR hängt, wenn 
dieser andauernd geschrieben wird...

von Gebhard R. (Firma: Raich Gerätebau & Entwicklung) (geb)


Angehängte Dateien:

Lesenswert?

Hmmm.. Da kann vieles sein,vollständiger Code wäre hilfreich. Ich mach 
das mit 12 channels, ADC1 getriggert von TIM4. Ich stell mal den Code 
hier rein, dann kannst du ja vergleichen.

Grüsse

von Magreet (Gast)


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?

von Gebhard R. (Firma: Raich Gerätebau & Entwicklung) (geb)


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

von Magreet (Gast)


Lesenswert?

OK, danke.

Gruß

von Jules (Gast)


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!

von Mehmet K. (mkmk)


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
:

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.