Forum: Mikrocontroller und Digitale Elektronik ATM32F030 + ADC + DMA


von Frank D. (nyler)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich versuche nun schon seit einer Woche mit meinem STM32F030 meine 
Analogeingänge einzulesen.
Ich habe erst ein paar kleinere Projekte realisiert und noch nicht mit 
Timern bzw DMA gearbeitet. Ich weiß nur grob was man damit anstellen 
kann und bin von den Konfigurationsmöglichkeiten der Timer beim STM32 
etwas erschlagen worden... :-/

Nun zu meinem Anliegen.
Ich möchte 6 Analogwerte einlesen, PA0 bis PA3 + TSC (Temperatur Sensor 
Channel) & Vref Channel. Die Abtastrate ist mir zuerst relativ egal.
Wenn ich das richtig verstanden habe kann man mit DMA Werte direkt von 
der Peripherie in den Speicher schreiben lassen. Um das ganze 
Ressourcenschonend zu gestalten kann man den DMA Prozess intern bzw 
extern Triggern. Ich würde in meinem Fall gerne mit einem Timer zyklisch 
die Werte in den Speicher schreiben.
Ich  hoffe, dass ich das bis hierhin richtig verstanden habe…

Habe mit Hilfe des refmanual und der ST Bibliothek unter CooCox einzelne 
Analogeingänge einlesen können. Jedoch bekomme ich das ganze nicht mit 
DMA und mehreren Kanälen realisiert.

Infos:
Ich konnte in den Registern beobachten, dass TIM1->CNT bis zum 
vorgeschriebenen Wert hochzählt und wieder von vorne beginnt.
Das „Transfer Complete Flag“ vom DMA1 Channel 1 wird jedoch nie gesetzt.

Ich weiß leider echt nicht mehr weiter und hoffe, dass jemand von euch 
mir ggf. ein paar Tipps geben kann.

Gruß

: Bearbeitet durch User
von aSma>> (Gast)


Lesenswert?

Servus,

Frank C. schrieb:
> Ich konnte in den Registern beobachten, dass TIM1->CNT bis zum
> vorgeschriebenen Wert hochzählt und wieder von vorne beginnt.
> Das „Transfer Complete Flag“ vom DMA1 Channel 1 wird jedoch nie gesetzt.

wo ist dein:
1
TIM1_IRQHandler(){
2
und 
3
TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //hier den richtigen flag raussuchen
4
5
//ADC val bearbeiten
6
}

Du triggerst den ADC Port durch einen Timer. Du brauchst da nicht die 
DMA flags zu betrachten.

von Frank D. (nyler)


Lesenswert?

Benötigt man für DMA einen Interrupt? Ich dachte, dass das konfiguriert 
wird und dann der µC selbstständig im Hintergrund erledigt?
Wenn ich den Interrupt benötigt stellt sich für mich nur die Frage was 
ich da rein programmieren soll um den ADC-DMA Prozess zu triggern...

: Bearbeitet durch User
von Frank D. (nyler)


Lesenswert?

Also, beim starten werden die Analogwerte ein mal in das angelegte Array 
übertragen, danach nie wieder.

Ich habe nun mal IRQ Handler vom TIM1 aktiviert, weiß jedoch nicht so 
genau was ich da machen soll.
Ich kann mir trotz Bedienungsanleitung kein Bild davon machen was z.B. 
das "Capture Interrupt Flag" bzw "Update Interrupt Flag" bedeuten 
sollen. Bzw wann die genau gesetzt werden und was ich als Reaktion 
darauf im IRQHandler programmieren soll.
Anscheinen habe ich noch nicht so ganz verstanden, wie der Prozess, ADC, 
DMA, TIM funktioniert. Mit der Bedienungsanleitung komme ich leider 
nicht so weit, weil ich mir unter den "Fachvokabeln" nicht viel 
vorstellen kann :-/

Ich glaube es stimmt etwas mit Timer <> ADC-triggerung nicht. Der 
Counter zählt zwar seine Runden, triggert jedoch nicht den 
Analogconvertierungsprozess.

Hat vielleicht jemand eine Idee?

von aSma>> (Gast)


Lesenswert?

Frank C. schrieb:
> Ich habe nun mal IRQ Handler vom TIM1 aktiviert, weiß jedoch nicht so
> genau was ich da machen soll.

aSma>> schrieb:
1
 TIM1_IRQHandler(){
2
 und
3
 TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //hier den richtigen flag raussuchen
4
5
 ===>DMA ADC val bearbeiten
6
 }
Poste dein Tim1 Handler.

> Anscheinen habe ich noch nicht so ganz verstanden, wie der Prozess, ADC,
> DMA, TIM funktioniert.

Ja, TIM1_IRQHandler() wird zyklisch aufgerufen, nur wenn der passende 
UIF gelöscht wurde. Dort tust du deine Daten bearbeiten. Also dein DMA 
Array umkopieren oder was auch immer.

von Frank D. (nyler)


Lesenswert?

Ich verstehe nur nicht so ganz was ich dann in dem IRQ-Handler machen 
soll.
Die Daten sollen doch eigentlich direkt in "volatile uint16_t 
ADCReadBuffer[6];" geschrieben werden. Das würde mir erstmal reichen.

Da ich nicht weiß was UIF ist und wie es funktioniert bzw was es 
bewirkt, kann ich damit nicht viel anfangen.

Erstmal muss ja in den control registern konfiguriert werden was genau 
den interrupt auslösen soll. Hier habe ich einfach mal CC1IE 
(Capture/Compare 1 interrupt enable) aktiviert.
Ich habe jedoch keine Ahnung was das bedeutet, ich vermute das er den 
IRQ Handler auslöst, wenn der Counter seinen Wert erreicht hat und 
wieder zurückgesetzt wird. Oder macht das der UIF?

In meinem IRQ Handler mache ich gerade nicht besonderes, ich setze nur 
das Flag zurück. Demnach ist der ganze Interrupt aus meiner Sicht 
vorerst  überflüssig. Ich könnte diesen zwar dazu nutzen um die Daten 
aus "ADCReadBuffer[6]" irgendwoanders hin zu kopieren. Jedoch würde das 
erst dann Sinn machen, wenn in "ADCReadBuffer[6]" auch endlich die Werte 
meiner Analogeingänge geschrieben werden.

TIM1_IRQHandler()
{
  TIM_ClearITPendingBit(TIM1, TIM_FLAG_CC1);
}

: Bearbeitet durch User
von aSma>> (Gast)


Angehängte Dateien:

Lesenswert?

Fragen über fragen ==> Reference manual oder Beispiel Code angucken.

Siehe Anhang.

>TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
Das könnte durchaus stimmen. Muss du halt gucken, ob
1
TIMx->SR &= ~0x0001   // Reset UIF

Keine Ahnung, ob Bitbanging beim stm32f0 geht. Ich würde
1
ADC_StartOfConversion(ADC1);
erst nach der Initialisierung des Timers starten.

Du könntest auch mal versuchen im Timer ADC_StartOfConversion zu 
starten:
1
TIM1_IRQHandler(){
2
  TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
3
  Timer1_ADC_b=1;  //volatile var fuer main
4
  memcpy(&(DMA_ADC_BUFFER[0]) , &ADC_BUFFER,  12);  //hier cpy 12 bytes, 6ADC val
5
  ADC_StartOfConversion(ADC1);  //start next conversation
6
}
7
8
//main
9
if(Timer1_ADC_b)
10
{
11
   Timer1_ADC_b=0;
12
   
13
   x=ADC_BUFFER[0];
14
   y=ADC_BUFFER[1];
15
//usw
16
}

Du kannst auch pollen, oder Systick Handler fuer diese Aufgabe nutzen.

Was ich nicht beantworten kann ist, ob bei dieser Methode die ADC Values 
beim Interrupt "frisch" sind oder "alt".

von aSma>> (Gast)


Angehängte Dateien:

Lesenswert?

Das hier auch noch.

von Frank D. (nyler)


Angehängte Dateien:

Lesenswert?

Erstmal möchte ich mich für die Unterstützung bedanken…

aSma>> schrieb:
>>TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
> Das könnte durchaus stimmen. Muss du halt gucken, obTIMx->SR &= ~0x0001
> // Reset UIF

Habe schon alle Funktionen die in den Registern schreiben bzw lesen mit 
dem Handbuch abgeglichen und die Registern beobachtet. Es werden die 
richtigen Bits gesetzt...

aSma>> schrieb:
> Keine Ahnung, ob Bitbanging beim stm32f0 geht. Ich
> würdeADC_StartOfConversion(ADC1);erst nach der Initialisierung des
> Timers starten.

Was bedeutet Bitbanging?
Ich habe die Initialisierung mal an das Ende gesetzt. Jedoch ohne 
Erfolg.

aSma>> schrieb:
> Du kannst auch pollen, oder Systick Handler fuer diese Aufgabe nutzen.

Pollen möchte ich vermeiden. Auf dem guten Stück muss noch RS485, I2C 
ans Laufen gebracht werden.

aSma>> schrieb:
> TIM1_IRQHandler(){
>   TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
>   Timer1_ADC_b=1;  //volatile var fuer main
>   memcpy(&(DMA_ADC_BUFFER[0]) , &ADC_BUFFER,  12);  //hier cpy 12 bytes,
> 6ADC val
>   ADC_StartOfConversion(ADC1);  //start next conversation
> }

Nicht wundern, ich habe in meinem Projekt andere Pins gewählt um mein 
Programm mit der Hardware komfortabler testen zu können.
Beim aktuellen Code werden einmal beim Aufstarten die richten Werte in 
ADC_BUFFER bzw u16AnalogValue geschrieben. Danach leider nicht mehr.

Wenn ich ADC_StartOfConversion(ADC1) im IRQ-Handler aufrufe werden die 
Eingänge immer noch nicht zyklisch abgefragt. Die Funktion wird zwar 
zyklisch aufgerufen, jedoch ändern sich nichts an den Werten im DMA 
Puffer.

: Bearbeitet durch User
von Frank D. (nyler)


Angehängte Dateien:

Lesenswert?

Ich habe nun ein lauffähiges Programm.
Leider Funktioniert das selbstständige Triggern des ADC über einen Timer 
bei mir nicht :( Der Counter triggert zwar, jedoch wird keine 
Konvertierung ausgelöst.

> ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
> ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;

Ich löse nun mit TIM3 (100Hz) einen Interrupt aus und schaue in diesem 
Interrupt nach ob über DMA neue Daten eingetroffen sind. Wenn dies der 
Fall ist, kopiere ich die Daten aus dem DMA Speicherbereich und trigger 
eine neue ADC-Konvertierung.

Für den Fall, dass jemand das Programm gebrauchen kann, habe ich es 
diesem Post abgehangene. Wenn jemand eine elegantere Lösung einfallen 
sollte, wäre ich über weitere Anregungen nicht abgeneigt.

Gruß

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.