PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : AD-Wandler freilauf Fragen



Superhirn
15.02.2006, 17:26
Hi,
ich habe mit C angefangen und interesiiere mich jetzt für den AD-Wandler. ich will den Freilaufmodus verwenden da er glaub ich einfacher mitten im programm abzufragen ist. Aber meine Frage ist ob er im freilaufmodus die geschwindigkeit des ATmega16 verändert bzw. wie viel verändert.

super_castle
16.02.2006, 17:33
der avr läuft mit dem mhz-qurz weiter wie immer....

es kommt darauf an, wie gut oder wie schlecht du proggen tust.

ich glaube, du hast den freilaufmodus noch nicht richtig verstanden, schau mal ins datenblat vom avr oder in der wiki-german.

Superhirn
16.02.2006, 18:09
Was ist die Wiki-german?

Sternthaler
19.02.2006, 22:04
Hallo Superhirn,
der Freilaufmodus ist tatsächlich interessant, wenn du nur genau einen ADC-Kanal abfragen möchtest. Sobald du aber verschiedene Kanäle benötigst, musst du laut der AVR-Doku schon etwas aufpassen. (Seite 203 in der ATmega8-Doku, die beim ASURO dabei war)
Wenn dein Hauptprogramm den ADC-Kanal über das Register ADMUX ändert, dann wird die aktuell laufende Wandlung noch weiter durchgeführt und erst danach wird der neu eingestellte Kanal 'freerunningmäßig' gewandelt.
Da das Hauptprogramm aber nun den neuen ADC-Kanal erwartet, bekommst du nun trotzdem ersteinmal noch einen Wert zum vorher eingestellten ADC-Kanal.
Falls du in deiner Hauptprogrammschleife ein Sleep hast, das länger als ZWEI Wandlerzeiten ist, kannst du direkt vor dem Sleep den ADC-Kanal wechseln, halt ein bisschen warten, und danach den Wert, der einfach in der ADC-Interruptfunktion in eine globale Variable geschrieben wurde, benutzen.
Wenn du den Kanal NICHT wechselst, dann ist der Wert tatsächlich immer aktuell.
Somit benötigst du den Sleep NUR dann, wenn du den ADC-Kanal wechseln willst.
Der code könnte ungefähr so aussehen:


unsigned int global_adc_wert;

main ()
{
char kanal_akt = 1;
char kanal_neu = 1;
unsigned int adc_wert;

while (1)
{
// Hier muss Programmcode sein, der den neuen ADC-Kanal bestimmt.
// Natürlich nur dann, wenn du es tatsächlich benötigst.
// Vielleicht ja auch nur zum testen über irgendeine Taste

if (kanal_akt != kanal_neu)
{
wechsel_den_Kanal_auf kanal_neu;
kanal_akt = kanal_neu;
Sleep (100); // Hier ein bisschen ausprobieren. DIe 100 habe ich nur geraten.
}

adc_wert = global_adc_wert; // global_adc_wert wird in der ADC-Interrupt-Funktion gefüllt.
// Hier kanst du nun beliebig auf den ADC-Wert reagieren
if (adc_wert > 100)
Lampe an;
else
Lampe aus;
}
}

P.S.: Eine Reaktion auf super_castle ist nie notwendig.

super_castle
19.02.2006, 22:29
man kann den freilaufmodus austricksen, bei mir laufen 7 verschiedene wandlungen (5 sharpsensoren und 2 radencoder). alle ergebnisse sind einwandfrei.

das obige programm kann so nicht laufen...he...he...du hast das wichtigste vergessen. der adc(porta0) läuft immer wenn es los geht admux=0 gibt es nicht.

castle

izaseba
19.02.2006, 22:33
Hallo,
was haltet Ihr davon, wenn Ihr den Kanal in der adcready Interrupt routine umschaltet?

Gruß Sebastian

@castle

Anstatt blöde zumzulabbern, würde ich auch Deiner Stelle mal die Lösung sagen

super_castle
19.02.2006, 22:35
zu umständlich......

man kann winavr-c nur kennenlernen wenn man auch mal mit dem datenblatt arbeitet. es ist hier nicht alles erklärbar.

izaseba
19.02.2006, 22:45
man kann winavr-c nur kennenlernen wenn man auch mal mit dem datenblatt arbeitet.

Der Kandidat hat hiermit 100 Punkte gewonnen.
Damit gege ich Dir zum erstem Mal recht, das ist war, darin sind schon viele Weisheiten verborgen...

Gruß Sebastian

Sternthaler
20.02.2006, 20:07
An die, die das Verstehen:

wenn Ihr den Kanal in der adcready Interrupt routine umschaltet?
Wenn man der Doku traut, dann heißt es zum ADC-Wandler, dass die nächste Wandlung gestartet wird, sobald die aktuelle fertig ist. Dazu wird dann noch der Interrupt ausgelösst.
Somit läuft die nächste Wandlung schon wenn wir in der Interrupt-Funktion angekommen sind. Es ist hier demnach schon zu spät, da wir den MUX ja hier erst nach dem Start der jetzt schon laufenden 'freerunning'-Wandlung umschalten können.

Ich möchte keinesfalls ausschliessen, das ich da nichts verstehe. (Rechtlicher Hinweis)

Vielleicht nochmal (vor allem für mich): Wir reden doch vom ATmega16?
Die von mir angegeben Seite (203) kam vom ATmega8, da ich nur die CPU nutze, und entsprechend nur diese Doku habe.
Ich kann mir aber nicht vorstellen, das der interne Ablauf im Kontroller komplett anders sein sollte. Aber man weiss ja nie.
Deshalb hier die 'eventuell möglichen Kapitelüberschriften' die ich zu diesem Thema für relevant halte:
ADC-Konverter (natürlich auf Englisch)
Interrupt-System (klar, auch auf Englisch)

@izaseba
Die Idee ist nicht schlecht. Leider bin ich aber auf diese 'Falle' schon gestoßen und habe deshalb den 'freeruning'-Mode beim Asuro komplett aufgegeben. Mag sein, dass man es hinbekommt, aber ich verwalte alle anstehenden ADC-Kanal-Anforderungen nun komplett ohne den 'freerunner'. Mal sehen, wann ich es gelernt habe einen Thread aufzumachen um darüber zu berichten ;-)

super_castle
20.02.2006, 20:19
Damit gege ich Dir zum erstem Mal recht, ....

brauchst du nicht, ich fühl mich auch so pudelwohl....

super_castle
20.02.2006, 20:29
beim avr8 schaltest du einfach zwischendurch auf den mux3, den gibt es nämlich nicht beim avr 8 (sondern nur das register) und schaltest dann auf den port mit dem mux den du brauchen tust im freilaufmodus und schon klappt es. also mux3 und dann den port zum auslesen und immer weiter so.

izaseba
20.02.2006, 20:44
@Sternthaler

Ein Beispiel braucht man nicht weit suchen, bleiben wir doch beim Asuro.
Ich erlaube mir die erweiterte Bibliothek von Weja zu zitieren,

SIG_ADC Routine für Odometrie, in Encoder_Init() wird adc Freerunning
eingeschaltet



SIGNAL (SIG_ADC)

{

static unsigned char tmp[2],flag[2],toggle;

if (autoencode){

tmp[toggle]= ADCH;

if (toggle) ADMUX = (1 <<ADLAR) | (1 <<REFS0) | WHEEL_RIGHT;

else ADMUX = (1 <<ADLAR) | (1 <<REFS0) | WHEEL_LEFT;



if ( (tmp[toggle] <= 160) && (flag[toggle] == TRUE)) {

encoder[toggle] ++;

flag[toggle] = FALSE;

}

if ( (tmp[toggle] > 160) && (flag[toggle] == FALSE)) {

encoder[toggle] ++;

flag[toggle] = TRUE;

}

toggle ^= 1;


Ich brauche Dir das wohl nicht näher zu erklären, was hier passiert, aber klar, bei jedem Interrupt wird der Kanal umgeschaltet und es klappt, das habe ich schon öfters umgesetzt und keine Problemme mit gehabt...

@castle

klar fühlst Du Dich pudelwohl, wahrscheinlich geht Dir immer einer ab, wenn Du jemanden niedermachen kannst...

Gruß Sebastian

super_castle
20.02.2006, 20:59
aber klar, bei jedem Interrupt wird der Kanal umgeschaltet und es klappt...

nicht immer...he.....aber immer öfter.... und das ist mir zu unsicher und schalte darum den mux3 dazwischen und erst dann habe ich die 100%

Sternthaler
20.02.2006, 23:21
Changing Channel or Reference Selection
The MUXn and REFS1:0 bits in the ADMUX Register are single buffered through a temporary
register to which the CPU has random access. This ensures that the channels
and reference selection only takes place at a safe point during the conversion. The
channel and reference selection is continuously updated until a conversion is started.
Once the conversion starts, the channel and reference selection is locked to ensure a
sufficient sampling time for the ADC. Continuous updating resumes in the last ADC
clock cycle before the conversion completes (ADIF in ADCSRA is set). Note that the
conversion starts on the following rising ADC clock edge after ADSC is written. The user
is thus advised not to write new channel or reference selection values to ADMUX until
one ADC clock cycle after ADSC is written.
If both ADFR and ADEN is written to one, an interrupt event can occur at any time. If the
ADMUX Register is changed in this period, the user cannot tell if the next conversion is
based on the old or the new settings. ADMUX can be safely updated in the following
ways:
1. When ADFR or ADEN is cleared.
2. During conversion, minimum one ADC clock cycle after the trigger event.
3. After a conversion, before the Interrupt Flag used as trigger source is cleared.
When updating ADMUX in one of these conditions, the new settings will affect the next
ADC conversion.

Punkt 1 ist klar, hier soll ADFR gesetzt sein.

Punkt 2 ist ein Timing um der S/H-Schaltung Zeit zu geben. Kein Einflus, da das Timing ja über den ADC-Interrupt kommt und wir dort ja den MUX 'umschalten' wollen. Und zwar möglichst schnell. ABER: 'Once the conversion starts, the channel and reference selection is locked' dies wird bestimmt auf das Timing achten!?!?

Punkt 3. Hier stehe ich etwas im Regen. Ich verstehe das so: Conversion ist fertig; Interrupt noch nicht ausgelösst; Unser Programm löscht das ADIF-Flag. Das aber kann nicht im Interrupt passieren, da er ja nicht ausgelösst wurde, und somit verstehe ich es so, das 'aus versehen' ein loopendes Hauptprogramm das Flag 'so nebenbei' zurücksetzt.
Das sollte unsere Loop also nicht unbedingt machen.


Gut; soweit lese ich hieraus, was im internen Ablauf in der CPU, zu welchem Zeitpunkt passiert, um die in ADMUX gesetzten Bits überhaupt zu berücksichtigen.

Nun folgendes aus der Doku:

ADC Input Channels
When changing channel selections, the user should observe the following guidelines to
ensure that the correct channel is selected:
In Single Conversion mode, always select the channel before starting the conversion.
The channel selection may be changed one ADC clock cycle after writing one to ADSC.
However, the simplest method is to wait for the conversion to complete before changing
the channel selection.
In Free Running mode, always select the channel before starting the first conversion.
The channel selection may be changed one ADC clock cycle after writing one to ADSC.
However, the simplest method is to wait for the first conversion to complete, and then
change the channel selection. Since the next conversion has already started automatically,
the next result will reflect the previous channel selection. Subsequent conversions
will reflect the new channel selection.


Nicht dass ihr denkt, ich hätte das selbst geschrieben. Englisch war nicht unbedingt mein Lieblingsfach. Das lesen ist aber noch OK, so hoffe ich. Manchmal hapert es halt an der Interpretation.

Frage:
Warum läuft Weja's Bibliothek-Funktion? (Hier habe ich leider tatsächlich eine Vermutung. Trotz meiner Vermutung, ist sie aber in genau der vorliegenden Form funktionsfähig. Später mehr dazu bei Bedarf.)
Aber es kann ja noch folgende Frage beantwortet werden:
Was habe ich nicht an der Doku verstanden?

Superhirn
28.02.2006, 19:07
Hi,
kann mir wer schnell alle die register erklären. kurz und bündig. reicht ein kleiner satz zu jeden register, was eben dass wichtigste ist bei ihnen.

Sternthaler
28.02.2006, 22:40
kurz und bündig.Ob das Geht?
Kleiner Versuch:



Register:
ADMUX
Bit 7: REFS1
Bit 6: REFS0
Bit 5: ADLAR
Bit 4: -
Bit 3: MUX3
Bit 2: MUX2
Bit 1: MUX1
Bit 0: MUX0

REFS-Bit-Kombinationen wählt die Referenzspannung
(Gut im Asuro ist AVCC)
REFS1 REFS0
0 0 AREF
0 1 AVCC
1 0 Nicht erlaubt
1 1 Interne 2.5V Referenz

ADLAR-Bedeutung
0 ADC-Ergebniss in den Registern ADCL und ADCH steht so:
ADCH: Bits 1 und 0: Höchste Bits der Wandlung
ADCL: Bits 7 bis 0: Die niedriegen Bits der Wandlung
1 In den Registern ADCL und ADCH steht das Ergebniss nun so:
ADCH: Bits 7 bis 0: Die höchsten 8 Bit vom Ergeniss
ADCL: Bits 7 und 6: Die beiden niedrigsten Ergebnissbits

MUX-Bits
0000 wählt ADC-Kanal 0
0001 wählt ADC-Kanal 1
0010 wählt ADC-Kanal 2
0011 wählt ADC-Kanal 3
0100 wählt ADC-Kanal 4
0101 wählt ADC-Kanal 5
0110 wählt ADC-Kanal 6
0111 wählt ADC-Kanal 7
1000
1001
1010
1011
1100
1101
1110 1.23 Volt wird gemessen
1111 0 Volt werden gemessen


Register:
ADCSRA
Bit 7: ADEN
Bit 6: ADSC
Bit 5: ADFR
Bit 4: ADIF
Bit 3: ADIE
Bit 2: ADPS2
Bit 1: ADPS1
Bit 0: ADPS0

ADEN: 1 schaltet den ADC-Wandler erst ein
ADSC: Eine 1 startet die Wandlung, oder das 'freerunning'
ADFR: 1 erlaubt das 'freerunning'
ADIF: Wird von der CPU auf 1 gesetzt, wenn eine Wandlung fertig ist
ADIE: 1 erlaubt der CPU eine Interrupt-Funktion aufzurufen
ADPS2 bis ADPS0: Wählt einen Vorteiler, der die Wandlergeschwindigkeit
und damit allerdings wohl auch die Genauigkeit steuert.
Großer Vorteilerwert ergibt eine langsamere Wandlung. Damit wird
dann aber auch das laufende Programm nicht so häufig unter-
brochen und somit läuft es 'etwas' schneller.
ADPS2 ADPS1 ADPS0
0 0 0 Vorteilteiler 2
0 0 1 Vorteilteiler 2 (Ja, nochmal 2)
0 1 0 Vorteilteiler 4
0 1 1 Vorteilteiler 8
1 0 0 Vorteilteiler 16
1 0 1 Vorteilteiler 32
1 1 0 Vorteilteiler 64
1 1 1 Vorteilteiler 128

Du solltest also immer erst in das Register ADMUX schreiben.
Dann in das ADCSRA-Register schreiben.
z.B.:

ADMUX = 0b01000000; /* AVCC-Referenz; Kanal 0 */
ADCSRA = 0b11101111 /* Enable; ADC-Start; freerunning; Interrupt enable; Vorteiler 128

Um den Interrupt überhaupt nutzen zu können benötigst du eine Funktion.
z.B.


volatile unsigned int adc_wert;

SIGNAL (SIG_ADC)
{
adc_wert = ADCL + (ADCH << 8);
}

Damit liefert dir der 'freerunning' ADC-Wandler nun automatisch immer den gemessenen Wert vom Kanal 0 in die Variable adc_wert.
Diesen Wert kannst du in deinem Hauptprogramm immer lesen.

P.S.: Kürzer schaffe ich leider nicht ;-)

Superhirn
01.03.2006, 14:35
Danke. Kürzer gehts sicher nicht. jetzt weiß ich alles was ich wissen muss. (derzeit)

SprinterSB
01.03.2006, 15:24
Das ADC-Register sollte unbedingt als 16-Bit-Wert gelesen werden, damit der Zugriff in der richtigen Reihenfolge gemacht wird.
ausserdem ist der Wert darin unsigned.
das Lesen muss atomar erfolgen, da sonst während des Auslesens ein ADC-IRQ auftreten kann, und man dann einen korrupten Wert liest (passiert zwar selten, aber es passiert)


#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>

uint16_t volatile adc_wert;

SIGNAL (SIG_ADC)
{
adc_wert = ADC;
}

// Lesen:
...
uint16_t wert;
uint8_t sreg;
...
sreg = SREG;
cli();
wert = adc_wert;
SREG = sreg;
...

Superhirn
01.03.2006, 15:48
Muss das so kompliziert gemacht werden wie es Sprinter sagte? Es reicht ja dass man die register setzt, den wert evt über interrupt ausließt und sofort in eine variable schreibt und dann im normalen programm weitergeht. Oder?

SprinterSB
01.03.2006, 16:03
Es kann natürlich auch einfacher gemacht werden, wenn er Code nicht korrekt zu sein braucht.

Superhirn
01.03.2006, 16:16
Er muss nur funktionieren.

fambi_mail
01.03.2006, 17:55
Wenn 8-bit Genauigkeit reichen, einfach ADC-Wert linksbündig einstellen und dur ADCH (nur das HighByte) lesen.

Superhirn
04.03.2006, 16:50
Dnake vorerst für eure Hilfe. melde mich wieder wenns wieder fragen gibt. Aber man braucht ja nicht den Freilaufmodus. man könnte ja bei jedem interrupt den ADC wieder aktivieren mit den neuen kanal