Forum: Compiler & IDEs Atmega88 am STK500 ADC will einfach nicht


von kann_nichts (Gast)


Angehängte Dateien:

Lesenswert?

Hi, Ho,

ich stehe gerade mal wieder kurz vor einem Nervenzusammenbruch.

Die "lächerliche" Aufgabenstellung
Ich habe hier einen Atmega88 auf einem Stk-500. Dort möchte ich am PC0 
einen Analogwert lesen. Ist das Ergebnis größer 500, soll led2 angehen, 
sonst aus bleiben. led1 zeigt mir nur an, das eine messung beendet 
wurde. Ich habe die pins gemessen, soweit ok. Die CPU läuft mit 8Mhz, 
Takt intern

Ich habe das Gefühl, das er eine Messung macht und dann wars das. led1 
geht nach start auch an. led2 geht auch manchmal an (entsprechend dem 
wert), aber danach passiert mit led2 nichts mehr.
1
/*
2
 Atmega88 auf STK500  
3
 
4
 ADC-Test
5
*/
6
7
#include <avr/io.h>
8
#include <avr/interrupt.h>
9
#include <stdlib.h>
10
11
12
/*---------------------------------------------------------------------*/
13
int main( void)
14
{
15
 uint16_t adc_val;
16
17
 DDRD = (1 << PD3) | (1 << PD4);  // led ports
18
 
19
 PORTD |= (1 << PD3);  // led1 aus
20
 PORTD |= (1 << PD4);  // led2 aus
21
 
22
 ADMUX  = (1 << REFS0) + 0;  // MUX 0
23
 ADCSRA = (1<< ADPS1)| (1<< ADPS2)| (1<< ADPS0);
24
 ADCSRB = 0;
25
 ADCSRA |= (1 << ADEN) | (1<< ADSC) ;
26
 
27
 // main-loop
28
 while( 1)
29
 {
30
  if( !(ADCSRA & (1 << ADSC)))
31
  {
32
   PORTD &= ~(1 << PD4);           // led1 an, da wandlung beendet
33
   adc_val =  (ADCH << 8) + ADCL;  // adc lesen
34
   
35
   if( adc_val > 500) 
36
    PORTD &= ~(1 << PD3);          // led2 an wenn  > 500
37
   else
38
  PORTD |= (1 << PD3);           // led2 aus wenn < 501
39
  } // adc
40
41
 } // while 
42
43
 return 0;
44
} // main

Irgend etwas ist da oberfaul, nur was?

von Jens M. (muggel)


Lesenswert?

wenn du den ADC nicht im Free-Running Mode betreibst, musst du nach 
einer Messung eine neue Messung mit

ADCSRA |= (1<<ADSC);

initieren, ansonsten wird halt, wie von dir beschrieben, nur eine 
Messung durchgeführt.

Gruß, Muggel

von kann_nichts (Gast)


Lesenswert?

danke für die schnelle Antwort

wenn ich das
1
ADCSRA |= (1<<ADSC);
einfüge (zB am Ende des if konstrukt), ändert sich leider nichts.

Ich will natürlich den free-running-mode. Ich denke, ich habe ihn durch
1
ADCSRB = 0;
schon gesetzt.

von Jens M. (muggel)


Lesenswert?

Hi,

im Free-Running Mode ist ADSC permanent auf 1, also darfst du nicht ADSC 
abfragen, sonder musst auf das ADC-Interrupt Flag schauen (ADIF). Wenn 
das Flag 1 ist, wurde eine Konvertierung beendet. Du musst dann das Flag 
noch clearen (1 schreiben) oder du machst anstatt deiner 
Polling-Geschichte eine Interrupt-Routine daraus, die nach jeder 
AD-Wandlung deinen Code ausführt.

Gruß, Muggel

von kann_nichts (Gast)


Lesenswert?

hm,

es will noch immer nicht. Erst wird nur eine Wandlung durchgeführt. Mehr 
nicht.
1
/*
2
 Atmega88 auf STK500  
3
 
4
 ADC-Test V2 (geht nicht!)
5
*/
6
7
#include <avr/io.h>
8
9
int main( void)
10
{
11
 uint16_t adc_val;
12
13
 DDRD = (1 << PD3) | (1 << PD4);  // led ports
14
 
15
 PORTD |= (1 << PD3);  // led1 aus
16
 PORTD |= (1 << PD4);  // led2 aus
17
 
18
 ADMUX  = (1 << REFS0) + 0;  // MUX 0
19
 ADCSRA = (1<< ADPS1)| (1<< ADPS2)| (1<< ADPS0);
20
 ADCSRB = 0;
21
 ADCSRA |= (1 << ADEN) | (1<< ADSC); // | (1<< ADIF) ;
22
 
23
 // main-loop
24
 while( 1)
25
 {
26
  if( (ADCSRA & (1 << ADIF)))
27
  {
28
   PORTD &= ~(1 << PD4);           // led1 an, da wandlung beendet
29
   adc_val =  (ADCH << 8) + ADCL;  // adc lesen
30
   
31
   if( adc_val > 500) 
32
    PORTD &= ~(1 << PD3);          // led2 an wenn  > 500
33
   else
34
    PORTD |= (1 << PD3);           // led2 aus wenn < 501
35
  
36
   ADCSRA |= (1 << ADIF);
37
  } // adc
38
39
 } // while 
40
41
 return 0;
42
} // main

von Karl H. (kbuchegg)


Lesenswert?

kann_nichts schrieb:

> Ich will natürlich den free-running-mode. Ich denke, ich habe ihn durch
>
1
> ADCSRB = 0;
2
>

Das sehe ich nicht so.
Damit hast du nur gesagt, was die Trigger Source sein soll. Aber du hast 
nicht festgelegt, dass der ADC auf einen Trigger reagieren soll.
Dazu müsstest du ADATE in ADCSRA auch noch setzen. Siehe Datenblatt, 
Beschreibung zu den ADTS2:0 Bits
1
If ADATE in ADCSRA is written to one, the value of these bits selects
2
which source will trigger an ADC conversion. If ADATE is cleared, the
3
ADTS2:0 settings will have no effect."


Bis jetzt konnte ich auf den Free Running Modus allerdings immer 
verzichten. Geht ganz leicht

1
  ...
2
3
  ADCSRA |= ( 1 << ADSC );       // erste Messung anstossen
4
5
  while( 1)
6
  {
7
    while( ADCSRA & (1 << ADSC))   // Warte bis fertig
8
      ;
9
10
    adc_val = ADCW;                // Wert holen
11
12
    ADCSRA |= ( 1 << ADSC );       // und stoss gleich die nächste Messung an
13
14
    if( adc_val > 500) 
15
      PORTD &= ~(1 << PD3);
16
    else
17
      PORTD |= (1 << PD3);
18
  } // while

Der Trick liegt in der Erkentniss, dass die Auswertung auch Zeit 
braucht. Während ich mir also in aller Ruhe den Wert zu Gemüte führe und 
damit was tue, kann der ADC schon die nächste Messung machen. Der Teil 
"Warte bis fertig" ist in den meisten Fällen reine Absicherung, denn 
wenn das Programm wieder an diese Stelle kommt, ist die Chance recht 
groß, dass der ADC bereits lange mit seiner Messung fertig ist.

von kann_nichts (Gast)


Lesenswert?

Uff,

das wichtigste zu erst. Es funktioniert jetzt. Euch allen vielen Dank. 
Trotzdem bleiben noch Fragen

1)
1
adc_val =  (ADCH << 8) + ADCL;
Diese Zeile funktioniert nicht. Es gibt zwar keinen Fehler, trotzdem 
werden keine weiteren Messungen durchgeführt.
1
adc_val = ADCW;
Mit dieser Zeile funktioniert es. ADCW ist aber im datasheet nicht 
erwähnt.

2) Free-Running-Mode
Offensichtlich habe ich den bisher noch nicht richtig verstanden gehabt. 
Bedeutet es, das ohne passende ISR und die Flags (ADATE und ADIE) sich 
der ADC nur im "Single Mode" befindet?

3) einen anderen Kanal auswählen
Wenn ich jetzt den Kanal wechseln will (ADMUX), reicht es aus, vor dem 
Aufruf von  ADCSRA |= ( 1 << ADSC ); ADMUX zu ändern, oder muss ich ADEN 
erst abschalten.

@Karl Heinz Buchegger
Du hast völlig recht in Sachen "FRM". Ich hatte das Prinzip einfach 
anders verstanden. Ich ging davon aus, das der ADC permanent eine 
Wandlung macht und wenn man das Ergebnis nicht ausliest, wird es durch 
die nächste Wandlung einfach überschrieben.
Jetzt habe ich verstanden, das ich diesen Modus nicht brauche.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

kann_nichts schrieb:
> ADCW ist aber im datasheet nicht
> erwähnt.

ADCW ist ein künstlicher Konstrukt.  Normalerweise werden in den
Headerfiles der avr-libc 16-bit-Pseudoregister unter Weglassen der
nachgestellten "H" und "L" gebildet, also TCCR1A wird als
Kombination aus TCCR1AL und TCCR1AH gebildet usw.  Allerdings ergibt
sich hierbei für den Wandlerwert des ADCs der Name ADC.  Dieser Name
kollidiert jedoch mit dem Befehl ADC (add with carry) im Assembler-
modus (für den das <avr/io.h> ebenfalls benutzbar ist).  Aus diesem
Grunde wurde ein alternativer Namen ADCW eingeführt, wobei im
Assemblermodus nur ADCW zur Verfügung steht, während unter C sowohl
ADC als auch ADCW äquivalent nutzbar sind.

Der Vorteil der 16-bit-Pseudoregister ist, dass bei den Registern,
bei denen für das Lesen oder Schreiben der Teilregister eine bestimmte
Reihenfolge einzuhalten ist, der Compiler automatisch die richtige
Reihenfolge generiert.  Bei
1
adc_val =  (ADCH << 8) + ADCL;

dagegen ist die Lesereihenfolge unbestimmt und kann sich selbst
zwischen verschiedenen Compilerversionen oder Optimierungseinstellungen
unterscheiden, da es für die einzelnen Terme eines Ausdrucks keine
festgelegte Auswertereihenfolge gibt (mit wenigen Ausnahmen für
Sonderfälle wie den Komma-Operator, die wir aber hier außer Acht
lassen können).

von Bernd (Gast)


Lesenswert?

>     while( ADCSRA & (1 << ADSC))   // Warte bis fertig
>Der Teil
>"Warte bis fertig" ist in den meisten Fällen reine Absicherung, denn
>wenn das Programm wieder an diese Stelle kommt, ist die Chance recht
>groß, dass der ADC bereits lange mit seiner Messung fertig ist.

aber hardwaremässig ist das Bit in ADSC solange 1, bis die Konvertierung 
abgeschlossen ist und wird automatisch auf 0 gesetzt. Ergeben sich 
dennoch parktische Vorteile den Ausdruck in einer while-Schleife zu 
verarbeiten?

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.