www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik AD-Wandler Free Running - Mega8


Autor: Chris J. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn ich meinen AD-Wandler sagen will dass er:
-im FreeRunning-Mode laufen soll (ADEN=1) (ADCS=1) (ADFR=1)
-den Interrupt Flag setzen soll, aber > keinen Interrupt < auslösen soll
(ich will das Flag bei einem anderen Timer-Interrupt auslesen)

Dann muss ich doch nur (ADIE=1) setzen und kein Interrupt vom ADC funkt 
mir dazwischen, weil ich ja das I-bit im SREG nicht gesetzt habe.

Ist das so erstmal richtig?

Ich würd gern bei einem anderen Interrupt (der eh zur rechten Zeit 
kommt) abfragen ob die Wandlung fertig ist und dann den Wert in dieser 
Interrupt-Routine weiter verarbeiten.

Ich dachte da (bei 16MHz Takt) an einem Prescaler von 128 (9.6KHz 
Samplingrate bei 13 Takte/Umwandlung und 5kHz bei 25 Takte/Umwandlung)

Autor: 2920 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sollt so gehen.

Autor: Spess53 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

Da geht ewas durcheinander: Mit ADIE erlaubst du den ADC-Interrupt. Das 
I-Bit zeigt global einen Interrupt an. Ein ADC-Interrupt wird durch ADIF 
oder so ähnlich angezeigt.

MfG Spess

Autor: Knut Ballhause (Firma: TravelRec.) (travelrec) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn das I-Bit im SREG gelöscht (0) ist, wird gar kein Interrupt mehr 
ausgelöst. Wird das I-Bit wieder gesetzt, fallen alle in der 
Zwischenzeit ausgelösten Interrupts an. Den Interrupt des ADC kannst Du 
sperren, indem Du ADIE auf 0 läßt. Die Wandlung ist fertig, wenn ADIF 
gesetzt ist. Dieses Flag mußt Du nach dem Auslesen der 
ADC-Ergebnis-Register manuell auf 1 setzen, um es zu löschen und als 
Indikator für das nächste Wandlungsende zu nutzen. Im Free-Running Mode 
besteht, wenn Du den ADC-Interrupt nicht nutzt, die Gefahr, daß Du 
Wandlungen verpaßt.

Autor: Chris J. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Erklärung, lass ich ADIE auf null und lösch dann einfach 
ADIF durch setzen auf eins.

Hier hab ich etwas geschrieben, ich hoffe es macht das was es soll.
void  adc_init(){
  // wähle AREF, intern 2.54V 
  ADMUX |= (1 << REFS1) | (1 << REFS0); 
   // Enable ADC, AD-Wandlung starten, FreeRunning,AD-Interrupt Enable , Divisor 128 -> 125kHz
  ADCSRA |= ( 1<<ADEN )|( 1<<ADSC ) | (1<<ADFR) | (1<<ADIE) |( 1<<ADPS0 )| (1<<ADPS1) |( 1<<ADPS2 ); 

  //AD-Kanal wechsel ermöglichen
  // ADCSRA &= ~((1 << ADFR)|(1<<ADEN)); 
  // ADCSRA |= (1 << ADSC); //AD-Wandlung starten
  // ADCSRA &= ~(1 << ADSC); //AD-Wandlung stoppen
}

uint16_t  getADC(void){
  // ohne AD-Wert Verlust
  //while (ADCSRA & _BV(ADSC) ){}
  //int adc_wert = ADC;

  // Verlust einer AD-Wertes möglich
  if (bit_is_set(ADCSRA,ADIF)){
  int adc_wert = ADC; // wert auslesen

  ADCSRA |=(1<<ADIF); // Interrupt Flag mit 1 löschen
  return adc-wert; // gebe (10)16-bit Wert zurück?
  }
}

// Kanal von 0 bis 7 , 14 , 15
void setMux(byte kanal){
ADMUX = (ADMUX & 0b11110000); // ich will die Referenz Spannung so lassen
ADMUX |= kanal; // Kanal einstellen
}

Autor: Chris J. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Fehler (... lass ich ADIE auf null ...)
void  adc_init(){
  // wähle AREF, intern 2.54V 
  ADMUX |= (1 << REFS1) | (1 << REFS0); 
   // Enable ADC, AD-Wandlung starten, FreeRunning,AD-Interrupt Enable , Divisor 128 -> 125kHz
  ADCSRA |= ( 1<<ADEN )|( 1<<ADSC ) | (1<<ADFR) |( 1<<ADPS0 )| (1<<ADPS1) |( 1<<ADPS2 ); 
}

uint16_t  getADC(void){
  // ohne AD-Wert Verlust (max. 13 Takte warten)
  //while (ADCSRA & _BV(ADSC) ){}
  //int adc_wert = ADC;

  // Verlust eines AD-Wertes ist möglich
  if (bit_is_set(ADCSRA,ADIF)){
  int adc_wert = ADC; // wert auslesen

  ADCSRA |=(1<<ADIF); // Interrupt Flag mit 1 löschen
  return adc-wert; // gebe (10)16-bit Wert zurück?
  }
}

// Kanal von 0 bis 7 , 14 , 15
void setMux(byte kanal){
  //AD-Kanal wechsel ermöglichen
  ADCSRA &= ~((1 << ADFR)|(1<<ADEN)); 
  ADCSRA &= ~(1 << ADSC); //AD-Wandlung stoppen

  ADMUX = (ADMUX & 0b11110000); // ich will die Referenz Spannung so lassen
  ADMUX |= kanal; // Kanal einstellen

  ADCSRA |= (1 << ADSC); //AD-Wandlung starten
}

Hab ich irgend was vergessen?
Was macht man sonst noch normalerweise so bei einer AD-Wandlung?

Autor: Jörg X. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>>Hab ich irgend was vergessen?
 1) ja, du kannst ADSC nämlich nicht löschen, das passiert aber 
automatisch, wenn der ADC abgescaltet wird

 2) ja, setMux() schaltet den ADC nicht wieder ein

 3) ja, Was macht deine get_ADC Funktion, wenn der ADC noch nicht fertig 
ist? (du hast keine Warteschleife oder einen brauchbaren 
Rückgabe-'Code').

Das ganze könnte man z.B. mit dem ADC-complete interrupt lösen:
 - eine globale Var. enthält den nächsten Kanal.
 - eine Weitere enthält Flags:
    - eines für 'fertig'
    - ... (fällt mir grad nichts weiter ein ;) )

 - der ADC wird einmal gestartet (als "single conversion"!)
 - die ISR
    - liest das Ergebnis aus (in die Globale Var.)
    - stellt den neuen Kanal ein
    - startet die nächste Wandlung
 - get_adc_val:
    - fragt das flag ab, wenn das gesetzt ist:
      - setzt den nächsten kanal
      - gibt das Ergebnis zurück

hth. Jörg

Autor: Chris J. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Jörg X. Ich muss mir das noch mal genau aufzeichnen und mal deine 
Lösung umsetzen.

Erstmal die beiden angesprochenen Methoden:
// globale Variablen
adc_wert = 0; // mit null initialisiert

void setMux(byte kanal){
  //AD-Kanal wechsel ermöglichen
  ADCSRA &= ~((1 << ADFR)|(1<<ADEN)); 
  ADCSRA &= ~(1 << ADSC); //AD-Wandlung stoppen

  ADMUX = (ADMUX & 0b11110000); // ich will die Referenz Spannung so lassen
  ADMUX |= kanal; // Kanal einstellen
  
  ADCSRA |= (1 << ADFR) | (1<<ADEN); // FreeRunning Mode und ADC Enabled
  ADCSRA |= (1 << ADSC); //AD-Wandlung starten
  get_ADC(); // eine Wandlung verwerfen
}

uint16_t  getADC(void){
  // Verlust eines AD-Wertes ist möglich
  if (bit_is_set(ADCSRA,ADIF)){
  adc_wert = ADC; // wert auslesen
  ADCSRA |=(1<<ADIF); // Interrupt Flag mit 1 löschen
  return adc-wert; // gibt neuen (8 bis 10)16-bit Wert zurück
  }
return adc_wert; // gibt den alten Wert zurück
}

uint16_t  get_ADC(void){
  // ohne AD-Wert Verlust (max. 13 Takte warten)
  while ( !bit_is_set(ADCSRA,ADIF) ){}
  adc_wert = ADC; // auslesen
  ADCSRA |=(1<<ADIF); // Interrupt Flag mit 1 löschen
  return adc-wert; // gebe (8 bis 10)16-bit Wert zurück
}
Wird mir der Compiler das get_ADC() weg optimieren (Optimization flag 
-0s), weil ich damit nichts mache?

Autor: Jörg X. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
>> Wird mir der Compiler das get_ADC() weg optimieren [...]
ein kurzer Test sagt: Nein (Test im Anhang)
 - ist aber auch nicht so tragisch: Wandlung starten und dann abwarten 
geht kaum schneller (Eine Wandlung dauert min. 13,5 * 128(-> Prescaler) 
Takte ), da kommt es doch auf 6-10 unnötige (ASM-)Anweisungen (Aufruf, 
Einlesen etc.) nicht wirklich an, oder?

hth. Jörg

Autor: Chris J. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab mir jetzt die neue AVR Studio Version installiert und festgestellt 
dass die Speicher- Nutzung/Belegung angezeigt wird.

Ist das erst seit 4.13 Build 528 so ?

AVR Memory Usage
----------------
Device: atmega8

Program:     444 bytes (5.4% Full)
(.text + .data + .bootloader)

Data:          5 bytes (0.5% Full)
(.data + .bss + .noinit)

Build succeeded with 0 Warnings...


Also morgen kann ich jedenfalls testen :-)

Autor: Chris J. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, hab es jetzt alles aufgebaut.

1.) Läuft aber noch nicht :-(

Über "PORTD" lass ich mir den ADC-Wert ausgeben (mit Leuchtdioden)
An ADC7 hab ich mein Poti.

Ich kann drehen wie ich will, das Signal scheint für mein Programm immer 
High, 100%, 5V zu sein.
An ADC7 liegen jetzt 0.63V an (mit Multimeter gemessen) und während der 
Simulation in AVR Studio war auch Kanal 7 ausgewählt.

2.) (merkwürdiges Problem)
ADSC wird bei der Simulation immer gelöscht und zwar in get_ADC_8() 
nachdem ich ADIF lösche.

Als Fehlerumgehung hab ich ADSC einfach in get_ADC_8() nochmal gesetzt, 
eigentlich läuft mein AD-Wandler doch im FreeRunning Modus ... da müsste 
ich dieses bit eigentlich nicht immer wieder neu setzen.

main:
-----

main(){
  uint8_t adc_wert;

  set_ADC_8_Kanal(7); // Teste ADC auf Kanal 7 (Pin 22)
  init_ADC();

  // Port C
  // 7 = intern | 6=reset | 5,4 = 8bit | 3,2,1,0 = 10bit
  DDRC = 0b00000000; // Port C für analoge Eingänge
  PORTC= 0b00000000; // (ohne Pull-Up's da ADC aktiviert)

  // ADC6+ADC7 (TQFP Pin19+22)
  // nutze ADC6 und ADC7

  // Port D
  // 7,6,5,4,3,2 = ?????? | 1 = TXD, 0 = RXD (U(S)ART)
  DDRD = 0b11111111; // alles Ausgänge

  for (;;)
  {
    adc_wert = get_ADC_8(); 
    ad = (adc_wert /32 ); // unsigned Int

    switch(ad){
    case 0:  PORTD = 0b00000001; break;
    case 1:  PORTD = 0b00000010; break;
    case 2:  PORTD = 0b00000100; break;
    case 3:  PORTD = 0b00001000; break;
    case 4:  PORTD = 0b00010000; break;
    case 5:  PORTD = 0b00100000; break;
    case 6:  PORTD = 0b01000000; break;
    case 7:  PORTD = 0b10000000; break;
    }
  }
}


adc.c
-----

volatile uint8_t  adc_wert_8  = 0; // mit null initialisiert

void  init_ADC(){
  // wähle AREF, intern 2.54V
  ADMUX |= (1 << REFS1) | (1 << REFS0);
   // Enable ADC, AD-Wandlung starten, FreeRunning,AD-Interrupt Enable , Divisor 128 -> 125kHz
  ADCSRA |= ( 1<<ADEN )|( 1<<ADSC ) | (1<<ADFR) | (1<<ADPS0 ) | (1<<ADPS1) |( 1<<ADPS2 );
}

uint8_t  get_ADC_8(void){

  // Verlust eines AD-Wertes ist möglich
  if ( CHECKBIT(ADCSRA,ADIF) ){
  adc_wert_8 = ADCL; // LOW - wert auslesen

  ADCSRA |=(1<<ADIF); // Interrupt Flag mit 1 löschen

  // ADCSRA |= (1 << ADIF); // Interrupt Flag mit 1 löschen

  // Zur behebung des "ADSC Fehlers 
  //  ADSC wird nach einer weile immer gelöscht :-( "
  ADCSRA |= (1<<ADEN) | (1 << ADSC);

  return adc_wert_8; // gibt neuen (8 bis 10)16-bit Wert zurück
  }

return adc_wert_8; // gibt den alten Wert zurück
}

// Kanal von 0 bis 7
void set_ADC_8_Kanal(uint8_t kanal){
  //AD-Kanal wechsel ermöglichen
  ADCSRA &= ~(1 << ADFR); // stop free runing
  ADCSRA &= ~(1 << ADEN); // ADC disable
  ADCSRA &= ~(1 << ADSC); //AD-Wandlung stoppen

  ADMUX = (ADMUX & 0b11110000); // ich will die Referenz Spannung so lassen
  ADMUX |= kanal; // Kanal einstellen

  ADCSRA |= (1 << ADFR) | (1<<ADEN); // FreeRunning Mode und ADC Enabled
  ADCSRA |= (1 << ADSC); //AD-Wandlung starten
  get_ADC_8(); // eine AD-Wandlung verwerfen
}

In der Simulation beim AVR Studio geht das alles ... weiß echt nicht 
weiter  ... heul

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es muss grundsätzlich immer das ADCH ausgelesen werden! Steht auch im 
Datenblatt. Abgesehen davon, dass es sinnfrei ist, nur das Low-Byte 
auszuwerten, wird das Ergebnisregister erst wieder freigegeben, wenn 
ADCH gelesen wurde. Wird nur ADCL gelesen, dann kann der ADC keinen 
neuen Wert im Ergebnisregister ablegen. Ist klar, dass Du immer den 
selben Wert ausliest. Wenn Du nur 8 Bit Auflösung brauchst, dann setze 
ADLAR und lies nur das High-Byte aus. Dann gibts auch keine Probleme.

Deine switch-case Abfrage macht m.E. auch nicht viel Sinn. Was soll die 
Division durch 32 und das Abfragen auf 0...7 ?

Dass das ADSC gelöscht wird, hängt möglicherweise damit zusammen, dass 
der ADC im Simulator nur teilweise unterstützt wird. In Hardware sollte 
das nicht passieren, wenn der Free Running Mode korrekt aktiviert wurde.

Autor: Chris J. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ johnny-m

Danke, hab mir das Datenblatt durchgelesen und viel Zeit dafür 
aufgewendet.
War aber wohl noch nicht genug.

Dann setze ich ADLAR ... hab mich schon gefragt wozu dieses bit gut ist.

Zu der Division durch 32, das ist quasi ein bit-shift nach links.

z.B.: 10110011 >> 5 = 00000101
      ---                  ---
Das sind dann Zahlen von 0 bis 7 (3 Bit)
dann brauch ich keine 8 if(zahl<x && zahl>y) Abfragen ...

Ich will nur eine LED an PORTD leuchten lassen, je nach Wert. Bei 0 soll 
PinD0 high sein und bei 255 soll PinD7 high sein.

Autor: Knut Ballhause (Firma: TravelRec.) (travelrec) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Dann setze ich ADLAR ...

Auch dann mußt Du ADCH einlesen. ADCL muß dann nicht gelesen werden.

Autor: Jörg X. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Probier's wirklich mal mit dem Interrupt, statt Free-Runinng - 
vereinfacht, z.B. den Kanalwechsel.

hth. Jörg

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Chris J. wrote:
> Zu der Division durch 32, das ist quasi ein bit-shift nach links.
>
> z.B.: 10110011 >> 5 = 00000101
>       ---                  ---
> Das sind dann Zahlen von 0 bis 7 (3 Bit)
> dann brauch ich keine 8 if(zahl<x && zahl>y) Abfragen ...
Jau, ist mir mittlerweile auch aufgegangen... Hatte es nur überflogen.

Aber:
Wie Travel Rec. anmerkte: ADCH muss immer gelesen werden, unabhängig 
von irgendwelchen anderen Einstellungen.

Autor: Chris J. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ johnny-m
Hab's jetzt so gemacht und hat funktioniert.

> Dass das ADSC gelöscht wird
Das hat in der Simulation nicht funktionier und der Mega8 hat sich wie 
die Simulation verhalten und ADSC intern auch gelöscht.
Also wird ADSC bei fehlerhaften auslesen der ADCL/H Register gelöscht.

@ Travel Rec.
Ich weiß jetzt wie es richtig geht :-)

@ Jörg X.
Genau das werde ich jetzt machen.
Muss dazu nur noch meine Testplatine umlöten und zusätzlich 3 Taster 
anlöten.

Ich hab jetzt aber noch etwas festgestellt, wenn ich den Poti drehe und 
die Leuchtdioden beobachte hab ich nicht das Verhalten welches ich mir 
vorgestellt habe.
z.B.: Die LED an Pin3 ist an, jetzt dreh ich weiter und die LED an Pin3 
wird sozusagen gedimmt und die LED an Pin4 wird heller.
Wie kann das sein, ich dachte das ist eine rein logische Angelegenheit.

Kann es sein dass die Versorgungsspannung/Referenzspannung etwas 
schwankt und zeitweise der Pin3 und der Pin4 high sind? Sozusagen 
Pulsweitenmoduliert?

Autor: Chris J. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab's einfach mal getestet, es sind bei mir 2.05kHz die an den beiden 
Pin's dann anliegen. (Also sie schalten voll durch!)

Meine Abblockkondensatoren:
VCC  - 330µF (Versorgung von USB(5V, GND) )
AVCC - 330µF + 470nF(mit Spule zu VCC und kleine SMD-Spule nach GND)
... ob da was schwingt?

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.