mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik STM32 ADC & DMA


Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
DMA_InitTypeDef  DMA_InitStructure;
ADC_InitTypeDef  ADC_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

/* Enable peripheral clocks 
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

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


/* Enable DMA1 channel5 IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&dma_buf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 1;
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);

ADC_DeInit(ADC1);

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 = 1;
ADC_Init(ADC1, &ADC_InitStructure);

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

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

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

ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);

/* Enable ADC1 reset calibaration register */
ADC_ResetCalibration(ADC1);

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

/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1);

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

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

ADC und DMA scheinen auch zu funktionieren, jedoch wenn ich
/* Enable DMA1 Channel6 Transfer Complete interrupt */
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

Autor: Magreet (Gast)
Datum:

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

Autor: Magreet (Gast)
Datum:

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

Autor: Gebhard Raich (geb)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Magreet (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Gebhard Raich (geb)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Magreet (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK, danke.

Gruß

Autor: Jules (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Mehmet Kendi (mkmk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vermutlich liegt der Fehler bei
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC1ConvertedValue;

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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
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.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.