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!
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
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
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).
Vielen Dank für die Hilfe! Gibt es eigentlich von Atmel auch fertige Reglermodule oder muss man das selbst programmieren? LG
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
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?
@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
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.
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
>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
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...
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?
Hallo! Hat sich damit noch niemand beschäftigt?!?! Ich finde keinen Fehler... LG
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
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?
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?
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.