- Akku Tests und Balkonkraftwerk Speicher         
Seite 1 von 2 12 LetzteLetzte
Ergebnis 1 bis 10 von 17

Thema: Atmega 8 / 16 ADC mit 2 Kanälen

  1. #1
    Benutzer Stammmitglied
    Registriert seit
    08.06.2009
    Beiträge
    41

    Atmega 8 / 16 ADC mit 2 Kanälen

    Anzeige

    LiFePo4 Akku selber bauen - Video
    Hallo,
    ich habe Probleme bei meinem AVR den Adc mit zwei Kanälen zu betreiben. Habe jetzt schon 3 Tage damit herumgespielt und komme einfach nicht mehr weiter.
    Ich poste am besten erst mal den C Code, der bei mir in einem sourcefile names timer.c untergebracht ist:

    Code:
    #include "include.h"
    
    uint16_t current_adc_null =0;
    uint16_t old_adc_null     =0;
    uint16_t current_adc_eins =0;
    uint16_t old_adc_eins     =0;
    
    
    
    void adc_init (void)
    {
      ADCSRA = (1<<ADEN) |(1<<ADSC) | (1<<ADATE) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);   // free running prescaler = 128
      ADMUX  = (1<<REFS1) | (1<<REFS0) | (1<<MUX0);	 // interne Referenzspannung, Mux0 = Pin ADC1 
     // auf Abschluss der Konvertierung warten
     // ADCSRA |= (1<<ADSC);
     //	while(ADCSRA & (1<<ADSC));     geht nicht !
    
             
    // einmalige Messung
     current_adc_null = ADCL;
     current_adc_null |= (ADCH<<8);
     current_adc_null = 0;
    }
    uint16_t read_adc0 (void){
    
      unsigned char sreg;
      sreg = SREG;
      cli ();
      current_adc_null = ADC;
     SREG = sreg;
     return current_adc_null;}
    
    uint16_t read_adc1 (void){
         uint8_t admux;
         uint8_t sreg;
         sreg = SREG;
         admux = ADMUX;
       
    
        ADMUX  = (1<<REFS1) | (1<<REFS0) | (1<<MUX1); //ADC2
        while (!(ADCSRA & (1<<ADIF))){}  //Wait until current conversion is complete
        ADCSRA |= (1<<ADIF);  //clear interrupt Flag again
        while (!(ADCSRA & (1<<ADIF))){} //Wait until new conversion is complete
        ADCSRA |= (1<<ADIF);  //reset Flag 
        while (!(ADCSRA & (1<<ADIF))){} //Wait until new conversion is complete
     cli ();
    current_adc_eins = ADC;
    
     ADMUX = admux;
     SREG = sreg;
     return current_adc_eins;
    }
    Der Code ist so aufgebaut, dass Adc0 ganz normal gelesen wird, während bei der Messung von Adc1 erst das Admux Register zum Einstellen des Kanals gesichert wird, dann auf den neuen Kanal von Adc1 umgestellt wird und danach der alte Zustand wiedergehellt wird.

    Dazu habe ich gleich zwei Fragen:
    1. Mein Kontroller kommt aus folgender while Schleife, die ich oben auskommentiert habe, nicht mehr raus:
    // ADCSRA |= (1<<ADSC);
    // while(ADCSRA & (1<<ADSC));
    Ist das normal?
    2. Warum muss ich folgende Anweisungen aus dem Code mindestens 3 mal hintereinander verwenden:
    ADCSRA |= (1<<ADIF); //reset Flag
    while (!(ADCSRA & (1<<ADIF))){} //Wait until new conversion is complete
    Wenn ich die Anweisung nur zwei mal schreibe, hat current_adc_null den gleichen Wert wie current_adc_eins.

    Da die Adc Werte bei interner Referenzspannung und Kondensator am Aref Pin immer noch zwischen Werte von +/- 5 schwanken habe ich in meiner Main.c folgende If Schleifen eingebaut, deren Funktionsrümpfe nur aufgerufen werden sollen, wenn sich die Adc Werte außerhalb der Schwankungen geändert haben:

    Code:
    // Adc0 einlesen
    read_adc0();
    if  ((old_adc_null - 6) > current_adc_null  || (old_adc_null + 6) < current_adc_null ) {        
    
     old_adc_null = current_adc_null;
    
    
    }
    
      //Adc1 einlesen
      read_adc1();
    if ((old_adc_eins - 6) > current_adc_eins || (old_adc_eins + 6) < current_adc_eins ) {
    
      old_adc_eins = current_adc_eins ;
    
    
    	}
    In den Schleifen lasse ich natülich auch noch andere Befehle ausführen, wie zum Beispiel das Beschreiben des Displays mit den aktuellen ADC Werten und das Setzen von OCP Timerwerten.
    Nun zu meinem Problem: So wie oben gepostet funktioniert funktioniert das ganze nicht, denn current_adc_null hat den selben Wert wie current_adc_eins und die erste if Schleife wird auch nur aufgerufen, wenn sich current_adc_eins ändert.
    Lasse ich die Anweisung " old_adc_eins = current_adc_eins ;" in der zweiten If Schliefe oben weg, werden beide Adc Werte korrekt auf dem Display ausgegeben. Es scheint also alles zu funktionieren, bis auf die Tatsache, dass die Schwankungen von current_adc_eins nicht ausgeblendet werden.
    Ich versteh überhaupt nicht wie das sein kann. Normalerweise dürfte doch current_adc_eins mit current_adc_null überhaupt nicht zusammenhängen, oder?
    Wenn ich die Bedingungen in der If Schleife zum Ausblenden der Schwankungen einfach weglasse und beide Werte permanent auf dem Display ausgeben lasse, funktioniert es auch wieder (auch mit old_adc_eins = current_adc_eins )

    Letzte und wichtigste Frage:
    Ist es normal, dass die Adc Werte um bis zu +/- 100 Schwanken, wenn ich am PC6 Pin über einen 100 Ohm widerstand die Basis eines Transistor schalte?

    Ich hoffe man kann das nachvollziehen, da es wohl nicht so einfach ist sich hier reinzudenken. Ich wäre Euch trotzdem sehr dankbar, wenn mir jemand helfen könnte, da ich jetzt schon viel Zeit reininvestiert habe und das Teil endlich zum Laufen bekommen möchte/muss.

    MfG
    Destrono

  2. #2
    Erfahrener Benutzer Robotik Visionär Avatar von Hubert.G
    Registriert seit
    14.10.2006
    Ort
    Pasching OÖ
    Beiträge
    6.220
    Ganz klar ist mir dieses Programm nicht.
    Was soll das SREG sichern, lass das den Compiler machen, der weiss was er tut.
    Was setzt du hier zurück? ADCSRA |= (1<<ADIF); //clear interrupt Flag again
    Warum verwendest du nicht den Interrupt für den ADC.
    Geringe Schwankungen gleicht man aus indem man mehrmals misst und den Mittelwert bildet.
    Einen Transistor über 100 Ohm anschalten bedeutet, das dort schlagartig 40mA fließen. Das tut der Stromversorgung des ADC-Port sicher nicht gut.
    Grüsse Hubert
    ____________

    Meine Projekte findet ihr auf schorsch.at

  3. #3
    Benutzer Stammmitglied
    Registriert seit
    08.06.2009
    Beiträge
    41
    Hallo,
    der Kommentar "clear interrupt again" steht an der falschen Stelle. Ich habe die Anweisungen zum Löschen des Flagbits an unterschiedlichen Stellen im Code zu testzwecken kopiert. Hatte aber alles nichts geholfen.
    Auf Interrupts möchte ich sehr ungern zurückgreifen, weil ich die Timer schon viele Interrupts produzieren.
    Was für einen Vorteil hätte ich, wenn ich da Interrupts einsetzen würde?
    Die Anweisung "ADCSRA |= (1<<ADIF); " löscht das Flag. Wenn eine neue Messung erfolgt ist und das Datenregister akutalisiert wurde, wird dieses Flag wieder gesetzt. So steht das im Datenblatt.
    Nur wieso reicht es nicht, die ganze Prozedur mit Flag Zurücksetzen und Warten bis ein neuer Wert ermittelt wurde zwei mal zu machen? Meine Tests haben ergeben, dass das mindestens drei mal nacheinander erfolgen muss, sonst wird nicht der Wert vom neuen Kanal genommen.
    Die kleinen Schwankungen im Adc Wert wären mir eigentlich egal, es geht viel mehr darum, dass das Display nicht jedes mal mit einen neuen Wert aktualisiert werden soll, wenn sich am Poti nichts getan hat. Daher die zwei if schleifen.
    Der Widerstand zwischen Basis vom Transistor und µc ist eigentlich ein Poti. Laut Stellung waren 100 Ohm, laut Messgerät sind es sogar 250 Ohm und der Kontroller wird nicht mit 5 Volt versorgt. Ich habe den Pin also nicht mehr wie mit den zulässigen 0.04 Ampere belastet.

    MfG
    Destrono

  4. #4
    Erfahrener Benutzer Robotik Visionär Avatar von Hubert.G
    Registriert seit
    14.10.2006
    Ort
    Pasching OÖ
    Beiträge
    6.220
    Warum handelst du das Interrupt-Flag wenn du den Interrupt nicht benutzt.
    Es genügt doch ADSC abzufragen.
    Wenn du mit Interrupt arbeitest macht der Kontroller wärend der Conversion was anderes, so wartet er in der while-Schleife.
    Es steht im Datenblatt das Schaltvorgänge am ADC-Port wärend einer Conversion das Messergebnis beeinflussen.
    Grüsse Hubert
    ____________

    Meine Projekte findet ihr auf schorsch.at

  5. #5
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    25.11.2003
    Beiträge
    1.112
    Zu 1 und 2: Der logische "und" Operator ist "&&".
    Zu "wichtigste Frage": Ja. Sowas macht man nicht, zu hoher Strom.

  6. #6
    Benutzer Stammmitglied
    Registriert seit
    08.06.2009
    Beiträge
    41
    danke schon mal für die Antworten.
    Ist der bitweise Und-Opterator hier falsch? Im ADC Tutorial wird auch nur der "&" verwendet und nicht der logische "&&".

  7. #7
    Ich verstehe hier ein paar Dinge nicht. Grundätzlich rate ich vom Free Running Mode ab. Du hast ihn hier zwar nicht aktiviert, aber die Kommentar

    // free running prescaler = 128

    lässt vermuten das du evtl. doch davon aus gehst. Das passended Bit wäre ADFR. Würde ich aber wie gesagt nicht machen.


    1. Mein Kontroller kommt aus folgender while Schleife, die ich oben auskommentiert habe, nicht mehr raus:
    // ADCSRA |= (1<<ADSC);
    // while(ADCSRA & (1<<ADSC));
    Ist das normal?
    Definitiv nicht. Aber wie stellst du das fest? Und vor allem in welcher Funktion? Schon in der adc_init?

    2. Warum muss ich folgende Anweisungen aus dem Code mindestens 3 mal hintereinander verwenden:
    ADCSRA |= (1<<ADIF); //reset Flag
    while (!(ADCSRA & (1<<ADIF))){}
    Wer sagt, das du das musst? Mit dem ADIF Bit hast du eigentlich nicht wirklich was am Hut - selbst mit aktiviertem Interrupt nur in den seltensten Fällen. Daher verstehe ich auch nicht so recht, was du mit dieser Anweisung bewirken willst. Auf die fertige Wandlung warten? Das geht über das ADSC Bit, das wird automatisch auf 0 gesetzt wenn die Wandlung durch ist. ADIF macht nur sowas ähnliches, eignet sich hier aber kaum.

    3 mal hintereinander musst du aber gar nichts machen. Lediglich die erste Messung nach dem ADEN gesetzt wurde, sollte verforfen werden, aber das machst du ja schon in der init. In den normalen Wandelroutinen hat so ein mehrfach-wandlung-auslösen nichts verloren.

    current_adc_null = ADCL;
    current_adc_null |= (ADCH<<;
    current_adc_null = 0;
    Solche Spielereien lieber den Compieler erledigen lassen, der weiß wie er auf die 16 Bit Register zugreifen muss. Das hier tut es genau so:

    current_adc_null = ADC;

    Wo genau ist jetzt eiegntlich das Problem? Einerseits schreibst du, das du nicht aus einer while raus kommst, andererseits scheinst du schon irgendwelche Messwerte zu bekommen. Irgendwo passt hier was nicht

    Diese ganzen SREG Sachen solltest du im übrigen auch lieber sein lassen, das geht früher oder später schief. Der Compiler macht schon das nötige.

  8. #8
    Benutzer Stammmitglied
    Registriert seit
    08.06.2009
    Beiträge
    41
    Hallo,
    also bevor wir aneinander vorbei reden, sollte ich vielleicht erst mal erwähnen, dass ich in diesem Fall den Atmega 16 verwende. Später soll die Aufgabe aber ein Atmega 8 erledigen.
    Bei meinem Code habe ich mich an folgendes Datenblatt gehalten:
    http://www.atmel.com/dyn/resources/p...ts/doc2466.pdf
    Du hast die Freerunningvariante hier zwar nicht aktiviert, der Kommentar(...)lässt vermuten das du evtl. doch davon aus gehst. Das passended Bit wäre ADFR.
    Dieses ADFR Bit finde ich beim Atmega 16 nicht. So wie angegeben müsste ich eigentlich schon die Freerunning Variante gewählt haben. Siehe Datenblatt Seite 219 Bit5.
    Zu meiner Frage 1. : Ich habe das festgestellt, weil der Kontroller in eine Endlosschleife fällt, wenn ich adc_init aufrufe und die jetzt auskommentierten Stellen ohne "//" schreibe.

    Hier scheint irgendwie jeder gegen die Freerunning-Variante zu sein. Ich möchte sie trotzdem einsetzen, weil das für mich praktischer ist. Die Main Schleife läuft einfach durch und braucht nur noch den aktuellen Wert vom ADC kopieren.
    Wer sagt, das du das musst? Mit dem ADIF Bit hast du eigentlich nicht wirklich was am Hut - selbst mit aktiviertem Interrupt nur in den seltensten Fällen. Daher verstehe ich auch nicht so recht, was du mit dieser Anweisung bewirken willst. Auf die fertige Wandlung warten? Das geht über das ADSC Bit, das wird automatisch auf 0 gesetzt wenn die Wandlung durch ist. ADIF macht nur sowas ähnliches, eignet sich hier aber kaum.
    Laut Datenblatt müsste ich mit dem ADIF Flag richtiger sein. Zum ADSC Bit steht im Datenblatt "In Free Running Mode,
    write this bit to one to start the first conversion."
    Es geht hier also in der Freerunning-Variante nur um die erste Messung?
    Unabhängig davon, ob ich jetzt das ADIF oder das ADSC Bit wähle, müsste der Code doch trotzdem funktionieren. Und über meine Tests, bei denen ich die zwei ADC Werte einfach auf dem Display ausgeben lasse, habe ich herausgefunden, dass ich folgende Anweisungen mindestens drei mal hintereinander schreiben muss, damit current_adc_ein nicht den selben Wert hat, wie current_adc_null :
    ADCSRA |= (1<<ADIF); //reset Flag
    while (!(ADCSRA & (1<<ADIF))){}
    Und genau das verstehe ich nicht. Normalerweise müsste es doch reichen diese Anweisungen höchstens zwei mal hinereinander zu schreiben.

    Wo genau ist jetzt eiegntlich das Problem? Einerseits schreibst du, das du nicht aus einer while raus kommst, andererseits scheinst du schon irgendwelche Messwerte zu bekommen. Irgendwo passt hier was nicht Zwinkern
    Das Problem ist, dass der Code so wie oben gepostet nicht so funktioniert, wie er soll.
    Er funktioniert einwandfrei, wenn ich die Anweisung "old_adc_eins = current_adc_eins ;" in der zweiten If Schleife der Main funktion (habe ich oben auch gepostet!) rauslasse. Aber auch das habe ich oben schon erwähnt.
    Ohne diese Anweisung flackert der Wert aber auf dem Display, was ja auch logisch und von mir nicht erwünscht ist.

    MfG
    Destrono

  9. #9
    Ganz ehrlich, ich verstehe deinen Code nicht wirklich

    Aber mal ein paar Grundsätzliche Dinge zum ADC und den verschiedenen Modi.

    Free Running: Es wird ständig gewandelt, und es steht auch zu jedem Zeitpunkt ein gültiges Ergebnis in ADC. Das heißt im Umkehrschluss, wenn du in irgend einer Funktion mit einer while Schleife auf etwas warten musst, ist grundlegend etwas verkehrt, denn warten musst du in keinem Fall. Du kannst jederzeit einfach ADC auslesen (Wenn das zu schnell geht, hast du logischerweise mehrmals ein Messwert von dem gleichen Zeitpunkt, der ADC ist ja nicht beliebig schnell)

    Single Conversion: Du bestimmt, wann eine Messung gestartet wird. Jetzt musst du auch tatsächlich abfragen (oder ausreichend lange etwas anderes tun, Stichwort Timer) ob die Wandlung abgeschlossen ist, und kannst dann dein Ergebnis holen.

    Du versuchst hier gerade eine Kombination aus beidem (Free Running + Warteschhleifen). Der Free Running Mode bringt diverse Stolpersteine mit sich, und ist in den allermeisten Fällen kontraproduktiv.

    Beispiel Single Conversion:
    Code:
    void adc_init (void)
    {
    	ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);   //prescaler = 128
    	ADMUX  = (1<<REFS1) | (1<<REFS0);    			// interne Referenzspannung
    
    	ADCSRA |= (1<<ADSC);                  	//Erste Messung anschubsen
    	while (ADCSRA & (1<<ADSC)); 			//Auf abschluss der Messung warten
    	unsigned int dummy = ADC;				//Und ersten Messwert wegwerfen
    }
    
    
    unsigned int read_adc (unsigned char channel){
    	ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);	//Logisches geraffel damit die restlichen Bits in ADMUX erhalten bleiben
    	ADCSRA |= (1<<ADSC);            //Messung anschubsen
    	while (ADCSRA & (1<<ADSC));     //warten...
    	return ADC;        
    } 
    
    int main(){
    	adc_init();
    	
    	unsigned int ch1, ch2;
    	
    	ch1 = read_adc(1); //ADC Channel 1 wandeln
    	ch2 = read_adc(2); //ADC Channel 2 wandeln
    }
    Code ungetestet, sollte aber das Prinzip veranschaulichen. Vom Free Running Mode würde ich die finger lassen.

  10. #10
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    25.11.2003
    Beiträge
    1.112
    Du musst das ADFR Bit setzen, damit Du den Freerunning Mode einleitest. FR wie FreeRunning :-D
    Zu erst hatte ich etwas ganz anderes verstanden, ist etwas konfus, weil scheinbar wenig für diese Variante spricht...
    Interrupts helfen wirklich.

Seite 1 von 2 12 LetzteLetzte

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

MultiPlus Wechselrichter Insel und Nulleinspeisung Conrad