Forum: Compiler & IDEs Free running mode ADC


von Tom (Gast)


Lesenswert?

Hallo allerseits,

ich soll ein kleines Programm zum Auslesen des ADC im free running mode 
schreiben. An den Pins PC1,PC4 und PC5 befinden sich Dioden, die in 
Abhängigkeit des ADC-Wertes leuchten sollen.

Hier mein Code
1
#define F_CPU 7372800UL
2
3
4
#include <avr/io.h>
5
#include <avr/signature.h>
6
#include <avr/interrupt.h>
7
#include <avr/sleep.h>
8
9
static uint16_t ADCresult;    // beinhaltet den Inhalt des ADC Registers
10
11
// Interrupt Handler
12
13
ISR(ADC_vect){
14
15
  //ADC Register auslesen
16
  ADCresult = ADC;
17
18
  //LEDs in Abhängigkeit des Wertes von ADCresult gemäß vorgegebener Tabelle setzen
19
  if((ADCresult>=0) && (ADCresult<128)){
20
    PORTC &= ~(1<<PC1);
21
    PORTC &= ~(1<<PC4);
22
    PORTC &= ~(1<<PC5);
23
    }
24
  else if((ADCresult>127) && (ADCresult<512)){
25
    PORTC &= ~(1<<PC1);
26
    PORTC &= ~(1<<PC4);
27
    PORTC |=  (1<<PC5);
28
    }
29
  else if((ADCresult>511) && (ADCresult<769)){
30
    PORTC &= ~(1<<PC1);
31
    PORTC |=  (1<<PC4);
32
    PORTC |=  (1<<PC5);
33
    }
34
  else if((ADCresult>768) && (ADCresult<1024)){
35
    PORTC |=  (1<<PC1);
36
    PORTC |=  (1<<PC4);
37
    PORTC |=  (1<<PC5);
38
    }
39
  
40
}
41
42
43
/* FUNKTIONEN */
44
void adu(void)
45
{
46
  cli();
47
  
48
  //Referenzspannung Uref=2.56V, Single Ended Input ADC0, da MUX0...MUX5=0
49
  ADMUX|=(1<<REFS1) | (1<<REFS0);    
50
  // ADC wird aktiviert, Auto-Triggerung wird aktiviert, ADU-Frequenz wird auf f_clk/32 gesetzt 
51
  ADCSRA|= (1<< ADEN ) | (1<< ADATE ) | (1<< ADPS2 ) |(1<< ADPS0 );
52
  //ADC-Interrupt enablen  
53
  ADCSRA |= (1<<ADIE);
54
  // ADC wird gestartet
55
  ADCSRA |= (1<<ADSC);
56
}
57
58
59
60
int main(void){
61
62
  // Ports initialisieren
63
  DDRC |= (1<<PC1)|(1<<PC4)|(1<<PC5);
64
  PORTC |= (1<<PC1)|(1<<PC4)|(1<<PC5);
65
  adu();
66
  // Interrupts global aktivieren
67
  sei() ;
68
69
70
  while(1){}
71
  return 0;
72
}

Da ich als Referenzspannung 2,56V gewählt habe, müssten alle LEDs bei 
einer Eingangsspannung von 4V am PIN ADC0 leuchten. Nur funktioniert es 
leider nicht und ich stize schon 2 Stunden daran, durch das Datenblatt 
das Problem zu lösen. Leider sehe ich meinen Fehler nicht.

Würde mich sehr darüber freuen, wenn ihr mir Tipps geben würdet.
MfG Tom

von Uwe S. (de0508)


Lesenswert?

Hallo,

ich denke Du hast einiges nicht verstanden:

Referenzspannung Uref=2.56V und 4V Eingangsspannung, das kann den ADC 
Eingang nicht verkraften ohne weitere Vorsichtsmaßnahmen.

Warum taktest Du den ADC so schnell ?

Das passt auch nicht zusammen, da fehlt ein Modifizierer von ADCresult, 
er fängt mit V an.
1
static uint16_t ADCresult;    // beinhaltet den Inhalt des ADC Registers
2
// Interrupt Handler
3
ISR(ADC_vect){ ADCresult = ..}

von Tom (Gast)


Lesenswert?

Hallo Uwe,

der Takt war in der Aufgabenstellung gegeben.

Was ist ein Modifizierer? Bei den Tutorial zu AVR-GCC Tutorials bin ich 
leider nie darauf gestoßen.

Habe bisher in Java/Matlab/Labview progammiert und komme hin C leider 
nicht so ganz zurecht.

von Peter D. (peda)


Lesenswert?

Welches Target?
JTAG disabled?

von Walter S. (avatar)


Lesenswert?

Uwe S. schrieb:
> Referenzspannung Uref=2.56V und 4V Eingangsspannung, das kann den ADC
> Eingang nicht verkraften ohne weitere Vorsichtsmaßnahmen.

wenn der Prozessor mit 4 oder mehr V betrieben wird ist das kein Problem

von Timmo H. (masterfx)


Lesenswert?

Kommt halt nur fullhouse...

von Tom (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Welches Target?
> JTAG disabled?

Wir verwenden Atmel Studio 4 und laden mithilf evon Nodetool die .hex 
Dateien auf den Microcontroller. Von einem Target und JTAG habe ich nie 
was mitbekommen.

von Uwe S. (de0508)


Lesenswert?

Hallo,

Walter S. schrieb:
> wenn der Prozessor mit 4 oder mehr V betrieben wird ist das kein Problem

es geht nicht um Vcc, sondern er schrieb am ADC Eingang liegen 4V an.

Siehe seinen ersten Beitrag.

Und dass ist laut Datenblatt nicht ok !

: Bearbeitet durch User
von Timmo H. (masterfx)


Lesenswert?

Uwe S. schrieb:
> Hallo,
>
> Walter S. schrieb:
>> wenn der Prozessor mit 4 oder mehr V betrieben wird ist das kein Problem
>
> es geht nicht um Vcc, sondern er schrieb am ADC Eingang liegen 4V an.
>
> Siehe seinen ersten Beitrag.
>
> Und dass ist laut Datenblatt nicht ok !
Für alle I/Os gilt max. Vcc+0,5. Für den ADC gilt Vref als max was er 
messen kann, aber die Schutzdioden greifen nach wie vor erst bei 
Vcc+0,5. Bei >= Vref hat man halt nur Fullhouse
1
[...] Single ended channels that exceed VREF will result in codes close to 0x3FF

: Bearbeitet durch User
von Walter S. (avatar)


Lesenswert?

Tom schrieb:
> Von einem Target und JTAG habe ich nie
> was mitbekommen.

mit Target ist der Prozessor gemeint den du verwendest,
ein Schaltplan wäre auch hilfreich

Uwe S. schrieb:
> Das passt auch nicht zusammen, da fehlt ein Modifizierer von ADCresult,
> er fängt mit V an.

wenn du damit volatile meinst:
das braucht's hier nicht da ADCresult nur im Interrupt verwendet wird

: Bearbeitet durch User
von Tom (Gast)


Lesenswert?

Bei nächsten mal werde ich mir auf jeden Fall mehr Gedanken darüber 
machen, welche Spannungen ich auf die Pins einspeisen kann. Ich dachte, 
dass alle Spannungen bis Vcc erlaubt sind.

Kann bzw. möchte mir keiner beim Code helfen?
Ich habe mir das Kapitel über den ADC noch einmal durchgelesen. Leider 
weiß ich immer noch nicht, was ich falsch mache -.-

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


Lesenswert?

Tom schrieb:
> Ich dachte,
> dass alle Spannungen bis Vcc erlaubt sind.

Das ist auch so. Wenn du mehr als VREF auswerten willst, ist der ADC 
halt auf Anschlag.
Untersuch doch bitte mal das MAP File, ob deine ISR überhaupt kompiliert 
wird, oder ob sie weg optimiert wird (es ist ja nirgends im Programm ein 
Aufruf).
'static' muss deine Variable nicht sein, mach sie aber entgegen Walters 
Ratschlag doch bitte mal 'volatile', damit wird der Compiler 
'gezwungen', die ISR zu kompilieren.
Noch was:
1
  if((ADCresult>=0) && (ADCresult<128)){
2
// kannst du einfach durch 
3
 if (ADCresult<128) { 
4
// ersetzen, das gleiche gilt für 
5
  else if((ADCresult>768) && (ADCresult<1024)){
6
// kann einfach heissen
7
 else if (ADCresult>768) {

: Bearbeitet durch User
von Tom (Gast)


Lesenswert?

Danke für den Tipp :)
Werde morgen früh es gleich mal testen und darüber berichten.

von Walter S. (avatar)


Lesenswert?

Matthias Sch. schrieb:
> Untersuch doch bitte mal das MAP File, ob deine ISR überhaupt kompiliert
> wird, oder ob sie weg optimiert wird (es ist ja nirgends im Programm ein
> Aufruf).

den Compiler der die ISR wegoptimiert würde ich in die Tonne treten,

aber bitte bitte Tom:
SCHALTPLAN!

von Peter D. (peda)


Lesenswert?

Tom schrieb:
> Von einem Target und JTAG habe ich nie
> was mitbekommen.

Target ist die exakte Bezeichnung Deines ungenannten MCs.
Z.B. ein ATtiny1634 und ein AT90CAN128 unterscheiden sich erheblich.

Für JTAG ist bei einigen AVR-Derivaten per default PORTC reserviert, 
d.h. erstmal nicht als IO zugänglich.

von Tom (Gast)


Lesenswert?

Wir verwenden einen Sensorknoten, der vom Fachgebiet mit verschiedenen 
Anschlüssen erweitert wurde. Den Schaltplan besitze ich nicht.

Auf dem Sensorknoten wird der Atmega 1281 verwendet.

Matthias Sch. schrieb:
> 'static' muss deine Variable nicht sein, mach sie aber entgegen Walters
> Ratschlag doch bitte mal 'volatile', damit wird der Compiler

Leider hat diese Veränderungen nichts bewirkt.

Matthias Sch. schrieb:
> Untersuch doch bitte mal das MAP File, ob deine ISR überhaupt kompiliert
> wird, oder ob sie weg optimiert wird (es ist ja nirgends im Programm ein
> Aufruf).

Nach dem kompilieren erhalte ich eine .c und .aws Datei, im 
Default-Ordner befinden sich Dateien vom Typen .eep .elf .hex .lss .p + 
Makefile +Linker Address Map.

von Tom (Gast)


Lesenswert?

Ich vermute, dass die if-Anweisungen im Interrupt fehlerhaft sein 
müssen, da die Dioden nicht im erwarteten Bereich leuchten.

von DJ T. (Gast)


Lesenswert?

Taste Dich systematisch vor:

1. Schritt:
Versuche erstmal, die LEDs fix an und aus zu machen. Funktioniert das? 
Dann kannst Du die LEDs/Ports als Ursache ausschließen.

2. Schritt:
Lasse eine LED im ADC-Interrupt togglen. Toggelt sie? Dann läuft der 
ADC.

3. Schritt:
...

von Ingo L. (corrtexx)


Lesenswert?

Tom schrieb:
> sei() ;
>
>   while(1){}
>   return 0;
> }
Fehlt da wirklich das Semikolon hinter while(1){}?
Ganz nebenbei läuft der ADC mit 230,4 KHz außerhalb der Spezifikationen 
für 10Bit. Mach mal den Prescaler größer.

: Bearbeitet durch User
von Uwe S. (de0508)


Lesenswert?

Hallo Ingo,

Ingo Less schrieb:
> Fehlt da wirklich das Semikolon hinter while(1){}?

Nein alles tutti so.

> Ganz nebenbei läuft der ADC mit 230,4 KHz außerhalb der Spezifikationen
> für 10Bit. Mach mal den Prescaler größer.

Ja das weis er und interessiert ihn nicht:
Beitrag "Re: Free running mode ADC"

Man sieht schon wohin die Reise geht, es gibt keinen Schaltplan und Tom 
ist erst am Anfang seiner Reise mit den Atmel AVR µC. Da werden noch 
viele Fehler gemacht, aus denen man viel lernen kann.

Wenn ich keinen Schaltplan würde ich mir einen Zeichen und wenn er noch 
nicht mal den Namen des AVR µC kennt, dann wäre das Lesen des 
Datenblatts die erste Pflichtaufgabe.

von Le X. (lex_91)


Lesenswert?

Matthias Sch. schrieb:
> mach sie aber entgegen Walters
> Ratschlag doch bitte mal 'volatile', damit wird der Compiler
> 'gezwungen', die ISR zu kompilieren.

Nein wieso?
Wenn der Compiler beweisen kann dass ein bestimmter Pfad, in dem der 
Variablenzugriff erfolgt niemals erreicht werden kann so darf er den 
Zugriff wegoptimieren.
Volatile hin oder her.

Das nur am Rande. Hat mit dem Problem des TE nichts zu tun.

von Tom (Gast)


Lesenswert?

DJ Tobsen schrieb:
> 1. Schritt:
> Versuche erstmal, die LEDs fix an und aus zu machen. Funktioniert das?
> Dann kannst Du die LEDs/Ports als Ursache ausschließen.
>
> 2. Schritt:
> Lasse eine LED im ADC-Interrupt togglen. Toggelt sie? Dann läuft der
> ADC.
>
> 3. Schritt:
> ...

Danke für den Tipp. Ich habe alle drei LED's getestet und mit dem 
folgenden Code lande ich im Interrupt und das LED toggelt:
1
#define F_CPU 4000000UL
2
#include <avr/io.h>
3
#include <avr/signature.h>
4
#include <avr/interrupt.h>
5
#include <avr/sleep.h>
6
#include <util/delay.h>
7
//volatile uint16_t ADCresult;    // beinhaltet den Inhalt des ADC Registers
8
9
10
// Interrupt Handler
11
12
ISR(ADC_vect){
13
  _delay_ms(100);
14
  if(PORTC & (1<<PC5))
15
    PORTC &= ~(1<<PC5);
16
  else
17
    PORTC |= (1<<PC5);
18
19
20
21
  /*
22
  //ADC Register auslesen
23
  ADCresult = ADC;
24
25
  //LEDs in Abhängigkeit des Wertes von ADCresult gemäß vorgegebener Tabelle setzen
26
  if(ADCresult<12){
27
    PORTC &= ~(1<<PC1);
28
    PORTC &= ~(1<<PC4);
29
    PORTC &= ~(1<<PC5);
30
    }
31
  if((ADCresult>127) && (ADCresult<512)){
32
    PORTC &= ~(1<<PC1);
33
    PORTC &= ~(1<<PC4);
34
    PORTC |=  (1<<PC5);
35
    }
36
  if((ADCresult>511) && (ADCresult<769)){
37
    PORTC &= ~(1<<PC1);
38
    PORTC |=  (1<<PC4);
39
    PORTC |=  (1<<PC5);
40
    }
41
  if(ADCresult>768){
42
    PORTC |=  (1<<PC1);
43
    PORTC |=  (1<<PC4);
44
    PORTC |=  (1<<PC5);
45
    }
46
    */
47
  
48
}
49
50
51
/* FUNKTIONEN */
52
void adu(void)
53
{
54
  cli();
55
  
56
  ADCSRA |= (1<<ADEN)| (1<<ADIE);
57
  ADCSRA|= (1<<ADATE);
58
  ADMUX|=(1<<REFS1) | (1<<REFS0);
59
  ADCSRA |=(1<< ADPS2 ) |(1<< ADPS0 );
60
  ADCSRA |= (1<<ADSC);
61
  /*
62
  //Referenzspannung Uref=2.56V, Single Ended Input ADC0, da MUX0...MUX5=0
63
  ADMUX|=(1<<REFS1) | (1<<REFS0);    
64
  // ADC wird aktiviert, Auto-Triggerung wird aktiviert, ADU-Frequenz wird auf f_clk/32 gesetzt 
65
  ADCSRA|= (1<< ADEN ) | (1<< ADATE ) | (1<< ADPS2 ) |(1<< ADPS0 );
66
  //ADC-Interrupt enablen  
67
  ADCSRA |= (1<<ADIE);
68
  // ADC wird gestartet
69
  ADCSRA |= (1<<ADSC);
70
  */
71
72
}
73
74
int main(void){
75
76
  // Ports initialisieren
77
  DDRC |= (1<<PC5);//|(1<<PC4)|(1<<PC5);
78
  PORTC |= (1<<PC5);//|(1<<PC4)|(1<<PC5);
79
  
80
  adu();
81
  // Interrupts global aktivieren
82
  sei();
83
84
85
  while(1){}
86
  return 0;
87
}

Habe f_clk auf 4 MHz eingestellt und erhalte nun für f_ADU=125 kHz.
Leider funktioniert mein Code imme noch nicht.

Uwe S. schrieb:
> Man sieht schon wohin die Reise geht, es gibt keinen Schaltplan und Tom
> ist erst am Anfang seiner Reise mit den Atmel AVR µC. Da werden noch
> viele Fehler gemacht, aus denen man viel lernen kann.
>
> Wenn ich keinen Schaltplan würde ich mir einen Zeichen und wenn er noch
> nicht mal den Namen des AVR µC kennt, dann wäre das Lesen des
> Datenblatts die erste Pflichtaufgabe.

Als Neuling werde ich viele Fehler machen. Das stimmt.
Den verwendeten MC habe ich angegeben und mir das Datenblatt auch 
durchgelesen!

von Uwe S. (de0508)


Lesenswert?

Hallo Tom,

noch eine Anmerkung:
dieses Defines
1
#define F_CPU 4000000UL
 ändert nicht die µC Frequenz, sondern muss mit der externen Beschaltung 
synchronisiert werden !

Also Quarz dran, die Fusebits richtig eingestellt und dann diesen Wert 
in das Makefile eingetragen.

Eine Interrupt-Service-Routine soll so schnell als möglich abgearbeitet 
werden. Also was soll ein Delay dort?
1
ISR(ADC_vect){
2
  _delay_ms(100);
3
  if(PORTC & (1<<PC5))
4
    PORTC &= ~(1<<PC5);
5
  else
6
    PORTC |= (1<<PC5);
7
}

Einfacher ist diese Anweisung zu schreiben:
1
PORTC ^= (1<<PC5);

: Bearbeitet durch User
von Tom (Gast)


Lesenswert?

Liegt der Fehler wirklich an der Frequenz des MC?
Ich habe mir den Code von Kommilitonen angeschaut, die funktionieren 
ohne  eine Änderung an der Frequenz des Mcs vorzunehmen.

Ich habe auch geschaut, ob ich alle relevanten Bits setze. Leider fällt 
mir kein Unterschied auf.

von Tom (Gast)


Lesenswert?

Uwe S. schrieb:
> Also was soll ein Delay dort?

Das war nur ein Test, um zu sehen, dass die LED toggelt.

von Uwe S. (de0508)


Lesenswert?

Tom,
wie willst Du feststellen können, wie die eingestellt µC Frequenz ist ?

Z.B. Toggle eine LED alle 500ms und messe mit einem Oszi an diesem Pin 
die Zeit, damit erhälst Du Gewissheit.

: Bearbeitet durch User
von bla (Gast)


Lesenswert?

Kompilierst du überhaupt für den richten uC? Eventuell ist das Target ja 
versehentlich verstellt worden... Grundfunktionen wie Ports toggeln sind 
vielleicht kompatibel, aber der ADC-Zugriff vielleicht nicht...

von Tom (Gast)


Lesenswert?

bla schrieb:
> Kompilierst du überhaupt für den richten uC? Eventuell ist das Target ja
> versehentlich verstellt worden... Grundfunktionen wie Ports toggeln sind
> vielleicht kompatibel, aber der ADC-Zugriff vielleicht nicht...

Danke für den Tipp der möglichen Fehlerquelle.

Habe zur Sicherheit ein neues Projekt erstellt:
AVR GCC -> AVR Simulator -> ATmega1281... save, build
Unter Nodetool habe ich ohnehin nur eine Auswahl_ COM11...-> 
ATmega1281/...

Ich werde mich anscheinend nicht mit der C-Programmierung anfreunden.

von Uwe S. (de0508)


Lesenswert?

Tom,
Kann es sein, dass Du gar keine reale Hardware hast ?
Und die Programmschnipsel nur virtuell testen möchtest?

von Tom (Gast)


Lesenswert?

Uwe S. schrieb:
> Tom,
> Kann es sein, dass Du gar keine reale Hardware hast ?
> Und die Programmschnipsel nur virtuell testen möchtest?

Doch ich habe eine reale Hardware vor mir liegen^^

von Tom (Gast)


Lesenswert?

Was ich komisch finde, ist, dass beim Debuggen das Programm beim ersten 
Durchlaufen der Interrupt-Routine stehen bleibt.

Beim Debuggen des Codes für das Toggeln der LED im Interrupt ist er 
permanent weitergelaufen

von Ralf G. (ralg)


Lesenswert?

Tom schrieb:
> Was ich komisch finde, ist, dass beim Debuggen das Programm beim ersten
> Durchlaufen der Interrupt-Routine stehen bleibt.

Na, das ist doch ein Ansatz.

-> Datenblatt -> ADCSRA -> hier steht auch, wie so eine Wandlng 
vonstatten geht.

von Tom (Gast)


Lesenswert?

Ralf G. schrieb:
> -> Datenblatt -> ADCSRA -> hier steht auch, wie so eine Wandlng
> vonstatten geht.

Laut Datenblatt muss ich also ADCSRA in der Interrupt-Routine noch ein 
mal setzen. Danke für den Hinweis. Jetzt läuft das Programm permanent 
nur die LED's leuchten nicht so, wie sie sollten. Ich werde verrückt^^

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Tom,

ich habe Dir nach der Vorlage des ersten Posts, was Port-Pins und 
Taktfrequenz angeht, ein (Test-)Programm geschrieben.

Es macht das selbe wie in Deiner Vorlage, bis auf die ADC-Messungen, 
dort bilde ich eine Mittelwert über 8 aufeinander folgende Messungen.

Desweiteren habe ich über das Digital Input Disable Register 0 den 
digitalen Eingang von ADC0 abgeschaltet.
1
DIDR0 |= (1<<ADC0D);

Wichtig: die Eingangsspannung an ADC0 darf max 2.56V betrangen, da dies 
dein erster Beitrag so vorgibt.

*ADMUX – ADC Multiplexer Selection Register*
/1 1 Internal 2.56V Voltage Reference with external capacitor at AREF 
pin/

Das Setzen der LED erfolgt nun in der Hauptprogramm-Schleife über ein 
Event-Flag. Dieses Event-Flag wird in der ADC Interrupt-Routine gesetzt.

Bitte berichte.

F_CPU  := 7372800
Device := atmega1281
ADC-Channel := 0
LED1 := PORTC.1
LED2 := PORTC.4
LED3 := PORTC.5
LED_OFF := 0
LED_ON  := 1

: Bearbeitet durch User
von bla (Gast)


Lesenswert?

Ich bin immer ein Verfechter des definierten Setzens von Registern. Also 
nicht verlassen auf Defaultwerte. Wenn etwas Input sein soll, dann auch 
das entsprechende Bit löschen, auch wenn es laut Default schon so ist. 
Genauso mit den ganzen ADC-Registern.
Ich lese es schon nicht gerne, wenn der erste Zugriff auf ein Register 
mit z.B. |= oder &= passiert. Weil man dann immer irgendwas voraussetzt.
Einfach klare Verhältnisse schaffen!

von Peter D. (peda)


Lesenswert?

Warum nicht ganz einfach mal den ADC-Wert per UART ausgeben.
Man muß schon etwas nachdenken bei der Fehlersuche, statt im Nebel zu 
stochern.

Offensichtliche Programmfehler hat bisher keiner gefunden, mehr können 
wir nicht prüfen.

von Peter D. (peda)


Lesenswert?

Uwe S. schrieb:
> Wichtig: die Eingangsspannung an ADC0 darf max 2.56V betrangen

Nö.
Es wird nur bis U_ref*1023/1024 gemessen, alles darüber wird als 1023 
ausgegeben.

von Uwe S. (de0508)


Lesenswert?

Danke Peter,

für die Klarstellung. Ich bezog mich bei diesem Beitrag auf die max. 
Eingangsspannung für den Messbereich des ADC.

Peter Dannegger schrieb:
> Uwe S. schrieb:
>> Wichtig: die Eingangsspannung an ADC0 darf max 2.56V betrangen
>
> Nö.
> Es wird nur bis U_ref*1023/1024 gemessen, alles darüber wird als 1023
> ausgegeben.

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.