Atmega88 am STK500 ADC will einfach nicht

Gast #2633242
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?
Angehängte Dateien:
#2633319
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
Gast #2633363
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
#2633368
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.
Gast #2633446
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.
Moderator Persönliche Seite #2633502
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).
Gast #2681819
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?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren