Forum: Mikrocontroller und Digitale Elektronik Hilfe bei DMA + ADC + Regler


von Werner13 (Gast)


Lesenswert?

Hallo,

ich bräuchte Hilfe bei der Verwendung des DMA in Kombination mit dem ADC 
(hab noch nie DMA verwendet).

Ich verwende den 32bit µC AT32UC3C1128C, das Datenblatt dazu:
http://www.atmel.com/Images/doc32117.pdf

Damit möchte ich nun folgendes machen:
Es sollen 6 ADC- Eingänge verwendet werden, um 6 verschiedene 
Analogwerte einzulesen. Diese Werte sollen dann einem Reglermodul zur 
Verfügung stehen, um bestimmte Größen zu regeln. Nachdem nur 1 ADC mit 
16 Kanälen zur Verfügung steht, müsste ich den Kanal ständig in einer 
Schleife umschalten, um die Werte nach der Reihe einzulesen.

Um den Prozessor nun nicht unnötig zu belasten, würde ich gerne den 
DMA-Controller verwenden, um dies zu erledigen, ist das prinzipiell 
möglich? Ich hab mich zwar schon in das Thema eingelesen, jedoch ist mir 
nicht ganz klar, wie ich DMA verwenden kann.

Kann mir jemand dazu einen Gedankenanstoß geben?

Danke sehr!

von Dennis (Gast)


Lesenswert?

Werner13 schrieb:
> Ich hab mich zwar schon in das Thema eingelesen,

Werner13 schrieb:
> jedoch ist mir
> nicht ganz klar, wie ich DMA verwenden kann.

Schließt sich irgendwie doch gegenseitig aus, oder?

Werner13 schrieb:
> Kann mir jemand dazu einen Gedankenanstoß geben?

DMA wird verwendet für die automatische Bewegung von Daten zwischen 
verschiedenen Speicherplätzen. Wenn davon zufällig ein Speicherplatz der 
ADC Datenregister ist und der zweite Speicherplatz im Flash ist, so 
liest der DMA z.B. das Datenregister aus und legt es im Flash ab. Eine 
Verfeinerung wäre z.B. die Adresse des Speicherplatzes im Flash 
automatisch im DMA zu inkrementieren.

Das sollte als Denkanstoss genügen. Der Rest steht im Manual/Datenblatt 
oder wo auch immer

von Werner13 (Gast)


Lesenswert?

Danke zunächst mal für die Antwort...

Dass DMA die Daten hin- und her schaufelt, ist mir klar.

Ich verstehe aber den Zusammenhang mit dem ADC nicht ganz:
Ich hab ein Reglermodul (c-File), in dem ich die 6 ADC Werte benötige, 
um damit zu regeln. Das heißt, DMA müsste mir diese Daten vom ADC zur 
Verfügung stellen.

Meine Unklarheiten:
1.) Wie wird eine AD-Wandlung getriggert? Muss ich die ADC Kanäle 
genauso initielieren, wie wenn ich kein DMA verwenden würde? Wie erfolgt 
die Kanalumschaltung der 6 ADC-Kanäle? Wird das auch durch DMA erledigt 
oder muss ich das trotzdem in einer Ringschleife machen?

Danke, lG

von Martin H. (marrtn)


Lesenswert?

Ja, es ist möglich und gar nicht mal sooo kompliziert...
Mir haben die Beispiele aus dem ASF recht gut geholfen.

Werner13 schrieb:
> Kann mir jemand dazu einen Gedankenanstoß geben?
Ja...
1. ADC+Sequencer konfigurieren --> adcifa_get_calibration_data, 
adcifa_calibrate_offset, adcifa_configure, adcifa_configure_sequencer
2. DMA konfigurieren --> pdca_init_channel
3. DMA einschalten --> pdca_enable
4. Sequencer starten --> adcifa_start_sequencer
5. Wenn der DMA fertig ist (Interrupt oder pollen auf 
PDCA_TRANSFER_COMPLETE) der Applikation mitteilen, dass neue Daten da 
sind und ggf. die Geschichte neu starten (es reicht die Register 
AVR32_PDCA.channel[KANAL].mar,AVR32_PDCA.channel[KANAL].tcr, 
AVR32_PDCA.channel[KANAL].cr und AVR32_ADCIFA.cr neu zu schreiben).

von Werner13 (Gast)


Lesenswert?

Vielen Dank für die Hilfe!

Gibt es eigentlich von Atmel auch fertige Reglermodule oder muss man das 
selbst programmieren?

LG

von Werner13 (Gast)


Lesenswert?

Hallo,

bin gerade bei der Konfiguration des ADC, im Datenblatt steht:
"The ADC is fully differential. To perform single ended measures, the 
user can perform pseudo
unipolar conversions by connecting ground onto the negative input. User 
can connect it to an
external ground through pads or internal ground depending on if there's 
one connected onto the
negative input multiplexer. Since conversion results are always 12 bits 
in 2's complement representation,
the sign bit will not change, and then the resulting resolution is 11 
bits max."

Ich benötige keine differentiellen Messungen, sondern gegen Masse. Heißt 
das nun, dass ich den ADCREFN-PIN des µC hardwaremäßig auf Masse legen 
muss?

THX Werner

von Werner13 (Gast)


Lesenswert?

Und noch eine Frage:
Wenn man den ADC im FreeRunningModus betreibt, werden vom Prozessor die 
meisten Ressourcen gebraucht oder? Ich stelle mir das so vor, wenn eine 
neue Wandlung unmittelbar nach Abschluss der alten Wandlung startet, 
dann wandelt der µC ja nur noch, ohne Zeit für andere Aufgaben zu haben. 
Geht das in die richtige Richtung oder verstehe ich da was falsch?

Nachdem ich nur alle Min. ca eine neue Wandlung brauche, denke ich, dass 
es besser ist, die Wandlung mit einem Timer nach einer Min. zu 
triggern...

Sehe ich das richtig?

von Werner13 (Gast)


Lesenswert?

Hmmm, hat keiner Erfahrungen damit?

von Werner13 (Gast)


Lesenswert?

@martin:

ich finde im ASF nur die Funktionen, die ich im adcifa.h auch 
beschrieben habe. Allerdings ist mir da noch einiges unklar, ich bin 
bald am verzweifeln...

Ich finde da keine expliziten beispiele, könntest du mir ev. einen link 
schicken? Das ist wirklich sehr kompliziert für einen Anfänger :(

Ich poste mal meinen code, vielleicht kann mir jemand helfen:
1
// ADC config
2
adcifa_opt_t adc_options = {
3
  .frequency= 100000,// internal ADC frequency (has to be between 32 kHz and 1,5 MHz)
4
  .reference_source  = ADCIFA_ADCREF0,    // external reference ADC Ref 0  (2,51 V)
5
  .sample_and_hold_disable= false,// higher accuracy when disabled (but in this case only single sequencer availaible -> we need dual sequencer, so leave S&H enabled)
6
  .single_sequencer_mode= true,  // only one sequencer
7
  .free_running_mode_enable  = false,        // don't run free (trigger with provided timer)
8
  .sleep_mode_enable= false
9
  };
10
11
  //ADC sequencer config
12
  adcifa_sequencer_opt_t adc_sequencer_options = {
13
  .convnb  = ADC_CHANNELS_TEC, // 4 channels
14
  .resolution= ADCIFA_SRES_12B,// resolution 12 Bit
15
  .trigger_selection= ADCIFA_TRGSEL_ITIMER,// trigger with internal timer
16
  .start_of_conversion= ADCIFA_SOCB_ALLSEQ,      // A complete sequence is performed on a SOC event
17
  .sh_mode  = ADCIFA_SH_MODE_OVERSAMP,    // oversampling acivated, higher accuracy, but a conversion takes two clock cycles
18
  .half_word_adjustment= ADCIFA_HWLA_NOADJ,      // default adjustment
19
  .software_acknowledge= ADCIFA_SA_NO_EOS_SOFTACK// Sequencer will restart a new sequence on a new Start-Of-Conversion command
20
  };
21
22
  adcifa_sequencer_conversion_opt_t adc_seq_conv_opt[ADC_CHANNELS_TEC] = {
23
    {
24
      .channel_n        = AVR32_ADCIFA_INN_GNDANA,
25
      .channel_p        = AVR32_ADCIFA_INP_ADCIN0, // TEC0_CUR
26
      .gain          = ADCIFA_SHG_1,      
27
    },
28
    {
29
      .channel_n        = AVR32_ADCIFA_INN_GNDANA,
30
      .channel_p        = AVR32_ADCIFA_INP_ADCIN1, // TEC1_CUR
31
      .gain          = ADCIFA_SHG_1,
32
    },
33
    {
34
      .channel_n        = AVR32_ADCIFA_INN_GNDANA,
35
      .channel_p        = AVR32_ADCIFA_INP_ADCIN2, // TEC0_TEMP
36
      .gain          = ADCIFA_SHG_1,
37
    },    
38
    {
39
      .channel_n        = AVR32_ADCIFA_INN_GNDANA,
40
      .channel_p        = AVR32_ADCIFA_INP_ADCIN2, // TEC1_TEMP
41
      .gain          = ADCIFA_SHG_1,
42
    },    
43
    
44
  };
45
46
adcifa_get_calibration_data(&AVR32_ADCIFA, &adc_options);
47
  
48
adcifa_configure(&AVR32_ADCIFA, &adc_options, CPU_SPEED);
49
  adcifa_configure_sequencer(&AVR32_ADCIFA,ADCIFA_SEQ0, &adc_sequencer_options, adc_seq_conv_opt);
50
  
51
adcifa_start_itimer(&AVR32_ADCIFA, ITIMER_1s);
52
adcifa_start_sequencer(&AVR32_ADCIFA, ADCIFA_SEQ0);

ich will, dass mit dem Sequencer 0 4 pins eingelesen werden (die 4, die 
bei den sequencer conversion options eingestellt sind). Ist das so 
korrekt?

LG

von Martin H. (marrtn)


Lesenswert?

Werner13 schrieb:
> Heißt
> das nun, dass ich den ADCREFN-PIN des µC hardwaremäßig auf Masse legen
> muss?

Nein. Nur, dass Du dem Sequencer sagst, dass jeweils eine Seite der 
channels (P oder N) GND sein soll, wie es auch in Deinem Codeschnipsel 
schon drin ist.


Werner13 schrieb:
> Wenn man den ADC im FreeRunningModus betreibt, werden vom Prozessor die
> meisten Ressourcen gebraucht oder? Ich stelle mir das so vor, wenn eine
> neue Wandlung unmittelbar nach Abschluss der alten Wandlung startet,
> dann wandelt der µC ja nur noch, ohne Zeit für andere Aufgaben zu haben.
> Geht das in die richtige Richtung oder verstehe ich da was falsch?

Free running Mode heisst nur, dass der ADC dauernd beschäftigt ist. Die 
CPU im µC hat da erstmal nix mit zu tun. Allerdings ist die Kombination 
FreeRunning und ADC etwas seltsam. Ich habe das nie richtig zum laufen 
bekommen - irgendwann hat der Speicher nicht mehr mit der 
Sequencer-Reihenfolge zusammengepasst (z.B. 1. Sequencerpos an 5. 
Speicherstelle). Also am besten so machen, wie ich es oben umrissen hab.

UWie schnell musst/willst Du denn eigentlich sein? Der AVR32 ist auch 
ohne DMA schon ziemlich flott.
Ich musste auf DMA zurückgreifen, da ich zeitkritische Sachen im 
10µs-Takt machen will/muss (ein bisschen Rechnen, beide SPI bedienen, 
Analogwerte verwurschten, falls vorhanden und noch ein bisschen mehr)... 
Da gings einfach nicht mehr ohne.

von Werner13 (Gast)


Lesenswert?

Hey martin,

danke für die Hilfe... bin noch Anfänger :)

Also zwecks geschwindigkeit kann ich leider nicht viel sagen, bearbeite 
nur einen Teil eines projektes und mir ist gesagt worden, ich soll DMA 
für diese Sache verwenden..

OK, free running werd ich e nicht nutzen, verwende den internen Timer 
zum Triggern einer Wandlung... (itimer, der anscheinend extra dafür 
vorgesehen ist)..

Was mir noch nicht ganz klar ist:
1.) Wie funktioniert das mit dem adcifa_calibrate_offset. Muss ich da 
einmalig Masse an den Eingang legen und schaun was rauskommt, diesen 
Offset dann in ein Register legen? Ist das einmalig oder muss ich das 
vor jedem Start machen?

2.) Wie muss ich den sequencer initialisieren, wenn ich bei den 
sequencer_conv_options ein Array [4] habe. Funktioniert das dann so 
überhaupt
1
adcifa_configure_sequencer(&AVR32_ADCIFA,ADCIFA_SEQ0, &adc_sequencer_options, adc_seq_conv_opt);

Oder muss ich diese Zeile dann 4 mal für jedes Arrayelement aufrufen?


Danke für die Hilfe,
lG

von Jonas B. (jibi)


Lesenswert?

>Also zwecks geschwindigkeit kann ich leider nicht viel sagen, bearbeite
>nur einen Teil eines projektes und mir ist gesagt worden, ich soll DMA
>für diese Sache verwenden..

Ihr Helden, aber ob der ADC schnell genug ist (min 6x) habt ihr 
evaluiert?

gruß Jonas

von Werner13 (Gast)


Lesenswert?

Mindestens 6x von was?

von Jonas B. (jibi)


Lesenswert?

eurer Samplingfrequenz pro Channel

von Werner13 (Gast)


Lesenswert?

Jetz versteh ich gar nichts mehr, wie gesagt, hab DMA noch nie 
verwendet, bin Anfänger... bitte klär mich auf was du damit meinst...

Ich habe mir bis jetzt überlegt: ich mach jede Sekunde eine AD-Wandlung 
(Sequenz) und diese Ergebnisse werden dann per DMA dem Reglermodul zur 
Verfügung gestellt...

von Werner13 (Gast)


Lesenswert?

Hallo,

lassen wir das mit dem DMA mal weg, ich will zunächst nur die richtigen 
Werte des sequencers einlesen...

Der ADC ist wie oben beschrieben konfiguriert, ich lese dann mit dem 
Befehl
1
int16_t adc_value[4];
2
adcifa_get_values_from_sequencer(&AVR32_ADCIFA,ADCIFA_SEQ0,&adc_sequencer_options,adc_value);

die werte ein.
wenn ich die 4 array elemente dann betrachte, kommen folgende werte raus 
(Referenzspannung ADC = 2,51V):

Kanal 1: Spannung am ADCIN-PIN: 16mV      ADC Wert: 65527
Kanal 2: Spannung am ADCIN-PIN: 207mV     ADC Wert: 260
Kanal 3: Spannung am ADCIN-PIN: 3,3V      ADC Wert: 2039
Kanal 4: Spannung am ADCIN-PIN: 990mV     ADC Wert: 1990

Nachdem das Ergebnis im 2er-Komplemet dargestellt wird, hab ich nur noch 
11 Bit auflösung, was einen maximalen Wert von 2048 bedeutet. Bei sehr 
kleinen Werten kann ich mir vorstellen, dass durch einen Offset ein 
negativer Wert rauskommt und dann durch die 2er Komplementdarstellung 
der 16-Bit_int so hoch ist (Kanal 1)

Kanal 2: Hier würd es ungefähr passen (theoretisch sollte der wert ca 
bei 170 liegen)

Kanal 3: auch das scheint mir zu passen, weil die spannugn über der 
referenzspannugn liegt und somit (fast, theoretisch sollte es so sein) 
das maxuimum ausgegeben wird

Kanal4: den hohen Wert kann ich mir nicht erklären...

Was sagt ihr dazu? Mache ich hier möglicherweise noch irgendetwas mit 
dem Auslesen des wertes (2er Komplement usw) falsch?

von Werner13 (Gast)


Lesenswert?

Hallo!

Hat sich damit noch niemand beschäftigt?!?! Ich finde keinen Fehler...

LG

von Werner13 (Gast)


Lesenswert?

Hallo!

In meinem Post von 19.12 ist dwer Code zu sehen. Kann mir jemand sagen, 
welchen Wert ich für ITIMER_1s einsetzen muss, damit eine Wandlung in 1s 
durchgeführt wird (also nach jeder Sekunde eine Wandlung)? Ich habe zur 
Zeit den Wert 99999, aber die Wandlungen passieren viel zu schnell! KAnn 
mir dabei jemand helfen?

Danke, lG

von Werner13 (Gast)


Lesenswert?

Kann es sein, dass ich vergessen habe, das Interrupt flag zu löschen? 
muss man das in jeder ISR machen? Wie mach ich das in meinem Fall?

von Werner13 (Gast)


Lesenswert?

Zur erinnerung: ich trigger den ADC mit dem internen ITIMER; bleibt das 
interrupt flag gesetzt, wenn die ISR einmal ausgeführt wurde? das würde 
dann bedeuten, dass sofort wieder in die ISR gesprungen wird?

von Martin H. (marrtn)


Lesenswert?

Werner13 schrieb:
> Kann mir jemand sagen,
> welchen Wert ich für ITIMER_1s einsetzen muss, damit eine Wandlung in 1s
> durchgeführt wird (also nach jeder Sekunde eine Wandlung)?

--> Number of ADC clock cycles to wait for is (ITMC + 1).
Sofern Deine Clocks richtig konfiguriert sind und die o.g. Frequenz 
stimmt, müsste 99999 passen. Da ITMC 17 bit lang ist, sollte das sogar 
gehen.


Werner13 schrieb:
> Kann es sein, dass ich vergessen habe, das Interrupt flag zu löschen?
> muss man das in jeder ISR machen? Wie mach ich das in meinem Fall?

Weiß ich jetzt leider nicht auswendig, ob Du das machen musst - lies 
entweder selber nach oder probier es aus. Nachdem es aber eine 
adcifa_clear_interrupt Funktion gibt, liegt es nahe, dass Du es machen 
müsstest.

Ich kann mir leider keinen ADCIFA-Interrupt leisten (damit würde der 
Jitter bei meinem 10µs-Timer-Interrupt zu groß), sonst könnte ich Dir 
vielleicht besser helfen.

: Bearbeitet durch User
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.