Forum: Mikrocontroller und Digitale Elektronik ADC ATMega16


von Armin (Gast)


Lesenswert?

Hallo zusammen!

Ich bin momentan dabei, meinen ATMega 16 zu programmieren, zumindest 
versuche ich es, und nun stehe ich vor dem Problem, dass er beim ADC 
nichts macht.
Ich habe ein Kondensatormikrophon mit einem Verstärker an den 4. Kanal 
des internen ADC`s gehängt. Als Vref verwende ich die interne 
Referenzspannung, ein Kondensator ist verschalten. ich vermute meinen 
Fehler im Code, komme aber im besten Willen nicht weiter...

Wäre super wenn mir jemand helfen könnte!
1
#include <avr/io.h>
2
#include <stdint.h>
3
#include <util/delay.h>
4
5
ADC_Init();
6
while(1) //Hauptprogramm - Endlosschleife!
7
{
8
/**************************CHANNEL4****************************/
9
10
if(ADIF==1)
11
{
12
ADMUX = (1<<MUX2);
13
Messung5 = ADCL+ADCH;
14
}
15
16
if (Messung5>=100)
17
{
18
1<<PORTD;                     //Wenn der Wert über 100, setze PortD auf 1
19
_delay_ms(200);
20
1<<PORTD;                     //Nach 2 Sekunden wieder auf Null
21
}
22
}
23
}
24
25
/**********************************ENDE DES HAUPTPROGRAMMS********************************/
26
27
/***************************** ADC initialisieren ************************/
28
void ADC_Init() 
29
{
30
 
31
  uint16_t result;
32
   
33
  // interne Referenzspannung als Referenz für den ADC wählen:
34
  ADMUX = 0b01001000;
35
 
36
  ADCSRA = 0b00100110;               // Frequenzvorteiler - "Free running Modus"
37
  ADCSRA |= (1<<ADEN);               // ADC aktivieren
38
 
39
  /* Dummy - Readout*/
40
 
41
  if (ADIF==1) 
42
  {
43
  result = ADCL+ADCH;      // auf Abschluss der Konvertierung warten
44
  }
45
46
}
47
/********************************ENDE INIT********************************/

: Verschoben durch User
von Felix P. (fixxl)


Lesenswert?

ADIF==1 wird nie 'true' zurückgeben, weil ADIF vom Präprozessor durch 
den Wert 4 ersetzt wird.

Du musst das Bit im Register abfragen.

von Felix P. (fixxl)


Lesenswert?

Armin schrieb:
1
1<<PORTD;                     //Wenn der Wert über 100, setze PortD auf 1 
2
_delay_ms(200); 
3
1<<PORTD;                     //Nach 2 Sekunden wieder auf Null
Bekommst du für diese Zeilen keine Fehlermeldung?

Zum Zugriff auf I/O-Ports unbedingt noch mal hier nachlesen: 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Zugriff_auf_IO-Ports

von Armin (Gast)


Lesenswert?

Sollte das dann in etwa so aussehen:
1
void ADC_Init() 
2
{
3
 
4
  uint16_t result;
5
   
6
  // interne Referenzspannung als Referenz für den ADC wählen:
7
  ADMUX = 0b01001000;
8
 
9
  ADCSRA = 0b00100110;     // Frequenzvorteiler - "Free running Modus"
10
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
11
 
12
  /* Dummy - Readout*/
13
 
14
  if (ADCSRA |= (ADIF==1)) 
15
  {
16
  result = ADCL+ADCH;      // auf Abschluss der Konvertierung warten
17
  }
18
19
}
und
1
if(ADCSRA = (ADIF==1))
2
{
3
ADMUX = (1<<MUX2);
4
Messung5 = ADCL+ADCH;
5
}
6
7
if (Messung5>=100)
8
{
9
DDRB = 0b00011111;
10
_delay_ms(200);
11
DDRB = ob00000000;
12
}
13
}
14
}
?

vielen dank schonmal für deine antwort!!

von Felix P. (fixxl)


Lesenswert?

Ich werde das mal Zeile für Zeile sezieren.
1
void ADC_Init() 
2
{
Das wird vom Compiler zwar toleriert, ist aber fehleranfällig. Wenn der 
Funktion nichts übergeben werden soll, sollte sie mit
1
void ADC_Init(void)
 initialisiert werden. Hilft bei komplexeren Programmen, wenn man mal 
Funktionen verwechselt ungemein bei der Fehlersuche.
1
 uint16_t result;
2
   
3
  // interne Referenzspannung als Referenz für den ADC wählen:
4
  ADMUX = 0b01001000;
Die Variablendeklaration von result passt so.
Bei ADMUX sollte aber der Kommentar zur Anweisung passen. Schau ins 
Datenblatt des ATmega16 auf Seite 217, dann siehst du, welche Referenz 
du hier wirklich einstellst und welche Einstellung du hier noch 
vornimmst.

Ganz allgemein sind Zuweisungen mit der "Holzhammermethode" - also in 
der Form REGISTER = Zahl - eher zu vermeiden, weil man damit alles 
überschreibt, was man vorher mit dem Register angestellt hat. Wie man 
einzelne Bits setzt und löscht ohne dabei den Rest des Registers zu 
beeinflussen, steht im AVR-GCC-Tutorial.
1
  ADCSRA = 0b00100110;     // Frequenzvorteiler - "Free running Modus"
2
  ADCSRA |= (1<<ADEN);                  // ADC aktivieren
Auch hier gilt wieder: Holzhammermethode. Was passiert bei der oberen 
Zuweisung genau? Schau ins Datenblatt auf S. 219. Dort steht, was die 
einzelnen Bits bedeuten. Wie eine Zuweisung aussehen sollte, zeigt die 
untere Zeile. Dort wird unabhängig von den sonstigen Stellen im Register 
und auch deutlich nachvollziehbar ein Bit gesetzt.

Was neben dem Setzen von ADEN noch fehlt, damit der ADC wirklich 
loslegt, ist das Setzen von ADSC (Datenblatt, S. 219).

Ganz nebenbei. Ob deine Einstellungen für den Prescaler richtig sind, 
kann dir niemand sagen, solange nicht klar ist, mit welcher Taktfrequenz 
dein Controller arbeitet.
1
  /* Dummy - Readout*/
2
 
3
  if (ADCSRA |= (ADIF==1)) 
4
  {
5
  result = ADCL+ADCH;      // auf Abschluss der Konvertierung warten
6
  }
Wichtig zu wissen ist, dass ein einzelnes '=' auch in Kombination mit 
den Operatoren zu '+=', '-=', '*=', '/=', %=', '&=', '|=', '^=' in C 
immer eine Zuweisung durchführt, du in der Klammer hinter dem 'if' aber 
eigentlich ja vergleichen willst bzw. prüfen, ob an einer bestimmten 
Stelle im Register eine 1 steht. Wie man vergleicht, kannst du hier 
lernen: 
http://www.mikrocontroller.net/articles/Bitmanipulation#Standard_C_4
Statt
1
if(ADCSRA&0x10)
 würde ich aber die Schreibweise
1
if(ADCSRA&(1<<ADIF))
 bevorzugen, weil man dann besser nachvollziehen kann, was hier die 
Bedingung ist.

Anschließend weist du result die Summe der beiden Register ADCL und ADCH 
zu, was Unsinn ist, weil du damit die Wertigkeit der einzelnen Bits 
durcheinander bringst. In C solltest du dich nicht mit ADCL und ADCH 
herumschlagen, sondern result einfach ADC oder ADCW zuweisen, weil der 
Rest vom Compiler erledigt wird.

Das ADIF-Flag wird nicht automatisch gelöscht, von daher muss es per 
Hand nach dem Auslesen des Registers wieder zurückgesetzt werden, wenn 
du nicht mit echten Interrupts arbeitest. Was dafür getan werden muss, 
steht auch auf S. 219 im Datenblatt. Soweit zur Initialisierungsroutine.
1
if(ADCSRA = (ADIF==1))
2
{
3
ADMUX = (1<<MUX2);
4
Messung5 = ADCL+ADCH;
5
}
Für die Bedingung gilt noch immer das Gleiche: Vergleichen, nicht 
zuweisen. Indem du hier statt '|=' einfach '=' verwendest, setzt du alle 
Bits in ADCSRA auf 0. Sprich: ADC wird ausgeschaltet, Free Running Mode 
gestoppt, Prescaler überschrieben...

In ADMUX stellst du mit der Direktzuweisung Ähnliches an. ADMUX sollte 
in der Initialisierung fest eingestellt und danach nicht mehr verändert 
werden, wenn es nicht nötig ist.

Die Variable Messung5 ist nirgendwo initialisiert und die Zuweisung als 
Summe der beiden Register wie schon weiter oben bei 'result' unsinnig.
1
if (Messung5>=100)
2
{
3
DDRB = 0b00011111;
4
_delay_ms(200);
5
DDRB = ob00000000;
6
}
Die Bedingung passt so, aber was soll danach passieren? Mach dir den 
Unterschied zwischen DDRx, PORTx und PINx klar. Du machst hier fünf 
Anschlüsse des Ports B zu Ausgängen, wartest 200ms und hast dann eine 
Anweisung, die niemand verstehen wird, weil da statt der 0 vor dem b ein 
kleines o steht.

von Armin (Gast)


Lesenswert?

Da bleibt mir nur ein paar Dinge zu sagen!

Vielen Dank, dass du dir die Zeit genommen hast, es so ausführlich zu 
beschreiben!

Dadurch funktioniert der ADC tatsächlich! Und wenn man mal weiß, wo man 
genau nachschauen muss, gestaltet sich das ganze um einiges einfacher!

Vielen herzlichen Dank!!!

von Armin (Gast)


Lesenswert?

1
int main(void)
2
{
3
DDRC = 0xFF;
4
PORTC = 0xFF;
5
DDRD = 0xFF;
6
PORTD = 0x00;
7
8
DDRB = 0xFF;
9
PORTB = 0x00;
10
11
uint16_t result;
12
13
ADMUX |= ((1<<REFS0) | (1<<MUX2)); //Referenzspannung an VREF mit Kondensator, //Kamal 0
14
15
ADCSRA |= ((1<<ADEN) | (1<<ADPS2) | (1<<ADPS0)); //Enable ADC, Prescaler (Teilerfrequenz) auf 32
16
17
ADCSRA |= (1<<ADSC); //Startet eine "conversation"
18
19
while(1)
20
{
21
while ( !(ADCSRA & (1 << ADIF)))  //Prüfe, ob ADIF gesetzt ist, also der Lesevorgang abgeschlossen:
22
{
23
}
24
result = ADCW;
25
if (result>=100)
26
{
27
PORTB = 0xFF;
28
_delay_ms(5000);
29
PORTB = 0x00;
30
_delay_ms(1000);
31
ADCSRA |= (1<<ADSC); //Neue Conversation
32
}
33
else
34
{
35
ADCSRA |= (1<<ADSC); //Neue Conversation
36
}
37
}
38
}

Zwar ohne Dummy-readout, aber funktioniert zumindest probehalber so. 
Falls es für igrendjemanden helfen sollte.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Armin schrieb:
> ADCSRA |= (1<<ADSC); //Startet eine "conversation"

Hehehe, schreib ruhig 'Wandlung' - wenn es in denglisch sein soll dann 
wäre das eher 'Startet eine conversion' :-)

Armin schrieb:
> Zwar ohne Dummy-readout, aber funktioniert zumindest probehalber so.

Du musst auch nicht unbedingt einen Pseudo-Lesezugriff machen, auch der 
erste Wert ist schon gültig, lediglich die Wandlungszeit ist eben 
länger.

von Karl H. (kbuchegg)


Lesenswert?

> while ( !(ADCSRA & (1 << ADIF)))  //Prüfe, ob ADIF gesetzt ist, also der 
Lesevorgang abgeschlossen:


Und lass bitte das ADIF Flag in Ruhe, wenn du es nicht zurücksetzt.

Die einfachste Variante ist beim ADC:
Du setzt das ADSC Flag, dann arbeitet der ADC und wenn er fertig ist, 
dann zieht der ADC genau dieses Flag wieder auf 0.


Und achte bitte mehr auf die äussere Form deiner Programme. Einrückungen 
sind nicht dazu da, dass man sie ignoriert, sondern sind ein Stilmittel, 
welches es einem leichter macht die logischen Zusammenhänge in einem 
Programm zu verfolgen.

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.