Free running mode ADC

Gast #4118811
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
#4118827
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 = ..}
Gast #4118846
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.
#4119006
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
#4119063
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
Gast #4119070
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 -.-
(Firma: matzetronics) #4119085
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) {
Gast #4119412
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.
Gast #4119500
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:
...
#4119668
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.
#4119777
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.
Gast #4120157
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!
#4120165
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);
Gast #4120177
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.
Gast #4120202
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...
Gast #4120210
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.
Gast #4120230
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^^
#4120350
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
Angehängte Dateien:
Gast #4120380
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!

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