Hallo,
nachdem die Forensuche mir nicht weitergeholfen hat wollte ich mal
nachfragen:
Ich versuche mit dem ADC eines ATMEGA8 eine Temperatur und eine Spannung
zu messen und das auf dem Display anzuzeigen. Das ganze geht dann
schief, wenn ich unten im Code vor der Spannungsmessung den Eingang von
// Eingang einstellen
ADMUX |= (0 << MUX2 ) | (0 << MUX1 ) | (0 << MUX0 );
(Temperaturmessung)
auf
// Eingang einstellen
ADMUX |= (0 << MUX2 ) | (0 << MUX1 ) | (1 << MUX0 );
(Spannungsmessung)
umschalte.
Auf dem LCD wackeln dann beide Werte total komisch und es kommt nur
Blödsinn raus. Kann mir da jemand helfen?
Gruß
Sefco schrieb:> Auf dem LCD wackeln dann beide Werte total komisch und es kommt nur> Blödsinn raus. Kann mir da jemand helfen?
Mittels
ADMUX |= (0 << MUX2 ) | (0 << MUX1 ) | (0 << MUX0 );
kann man ein einmal gesetztes Bit nicht löschen.
Verwende die Routinen aus dem ADC-GCC-Tutorial.
Sefco schrieb:> Ich weiß nicht wie man Code Tags macht.
Siehe den Abschnitt "Formatierung" in der grauen Box direkt über dem
Eingabefeld
> Mein Text ist doch lesbar oder nicht?
Geht noch. Aber bei längerem Code ist es einfacher, wenn du die [ C ]
Tags setzt.
// Eingang einstellen
ADMUX |= (0 << MUX2 ) | (0 << MUX1 ) | (0 << MUX0 );
versus
Sefco schrieb:> Kannst du das bitte konkretisieren? Das Tutorial ist sehr lang,
aber es hat nur einen Abschnitt, dessen Titel ADC lautet. Und es gibt ja
immerhin auch ein Inhaltsverzeichnis im Tutorial
Sefco schrieb:> Würde ich um Hilfe bitten, wenn ich das was da stehen würde, verstanden> hätte?
Dort sind fertige Funktionen!
Du brauchst sie nur benutzen. Das ist doch nicht so schwer
1
hierdieFunktionenausdemTutorialeinkopieren
2
3
intmain()
4
{
5
uint16_tspannung,temperatur;
6
7
....
8
9
ADC_Init();
10
11
12
while(1){
13
14
spannung=ADC_Read(0);// Kanal 0 ist die Spannung
15
16
temperatur=ADC_Read(1);// Kanal 1 ist die Temperatur
17
18
}
19
}
in der ADC-Init Funktion noch die richtige Einstellung für die
Referenzspannung machen und .... fertig.
Edit: und genau dieselbe Beispielanwendung ist auch im Tutorial bei den
Routinen dabei. OK, andere Variablennamen.
Hast du gleich zu jammern angefangen, oder hast du erst noch über den
Tutorialabschnitt drübergescrollt? Den Code hast du dir auf jeden Fall
nicht angesehen oder gar studiert. Sonst hättest du gesehen, wie man die
Funktionen benutzt.
Jetzt versteh ich den Quelltext etwas besser =)
"Gejammert"...
Naja, finds doof, wenn statt Hilfe, nur dumme Antworten kommen. Auch
wenn es da steht, vielleicht versteht man es nicht immer.
Also, was soll dann immer diese "Steht doch da, lies doch mal!".
Sefco schrieb:> Also, was soll dann immer diese "Steht doch da, lies doch mal!".
Na ja. Was erwartest du?
Deswegen gibt es die Tutorien, damit dort ausführlich auf
Problemstellungen eingegangen wird. Oder willst du jeweils eine auf dich
zugeschnittene Version haben?
Und in den meisten Fällen sind im Tutorium auch noch fertige Funktionen
bzw. Codestücke angegeben, die dann auch noch in der Praxis zeigen wie's
geht.
> vielleicht versteht man es nicht immer.
Sorry. Aber ein Bit in einem Register löschen zu können, das sind
absolute Beginner Grundlagen. Jeder der das erste mal seine LED ein /
ausschalten muss, lernt wie das geht
1
Register|=(1<<Bit);// Bit auf 1 setzen
2
Register&=~(1<<Bit);// Bit auf 0 löschen
und er lernt auch, dass er so
1
Register|=(0<<Bit);
ein Bit nicht löschen kann. Warum? Weil man eine 0 nach links schieben
kann sooft man will, es bleibt trotzdem 0. Und 1 (bereits gesetztes
Bit) ODER 0 gibt als Ergebnis 1 und nicht 0.
Sefco schrieb:> OK, gut zu wissen. Das werde ich mir merken.
Gut.
(Das solltest du eigentlich schon wissen :-)
Findet sich im übrigen auch im Tutorial. Ganz oben, wenn das erste
allgemeine Voregplänkel vorbei ist.
11.2 Verändern von Registerinhalten
Karl Heinz Buchegger schrieb:>> vielleicht versteht man es nicht immer.> Sorry. Aber ein Bit in einem Register löschen zu können, das sind> absolute Beginner Grundlagen. Jeder der das erste mal seine LED ein /> ausschalten muss, lernt wie das geht
Ich muss da Karl Heinz echt zustimmen.
Das sind die Sachen die wir hier immer predigen. Grundlagen vernünftig
lernen. Wer das nicht mach, der tut sich schwer -> siehe TO hier. Naja
einer hat's jetzt wohl (hoffentlich) gelernt, dass man ohne Grundlagen
nicht weit kommt.
Karl Heinz Buchegger schrieb:> Sorry. Aber ein Bit in einem Register löschen zu können, das sind> absolute Beginner Grundlagen. Jeder der das erste mal seine LED ein /> ausschalten muss, lernt wie das geht> Register |= ( 1 << Bit ); // Bit auf 1 setzen> Register &= ~( 1 << Bit ); // Bit auf 0 löschen
Ich bin der mit dem "lies noch einmal". Ich bin von einem copy & paste
Fehler ausgegangen, weil zweimal das selbe code Stück da stand. Ich
konnte mir nicht vorstellen, dass man davon ausgeht mit 'oder' auch
wieder löschen zu können.
Es war also nicht böse gemeint.
Ich muss jetzt gerade nochmal nachfragen:
Obwohl ich den Code aus dem Tutorial nachvollzogen und kopiert habe im
Bezug auf mein Problem, klappt es trotzdem nicht.
Ich zeige im LCD oben die Temperatur und unten die Spannung an.
Sobald ich die Eingänge wechsel, stimmen beide Werte nicht mehr und
zeigen komische Dinge an. Wenn ich an der Spannung am ADC Eingang drehe,
dann ändert sich in dem selben Verhältnis die Temperatur. Wenn ich die
Spannung auf GND lege, wir mir -273 Grad, also 0K angezeigt. Auf den
Temperatur Sensor reagier er garnicht mehr, sobald ich den ADC einmal im
Programm umstelle. Ich poste mal den Code:
[c]
#include <avr/io.h>
#include "lcd-routines.h"
#include "util/delay.h"
#include <stdlib.h>
#include <avr/interrupt.h>
#include <math.h>
uint8_t adcvalue = 0;
double ntc_kohm = 0;
double spannung_v = 0;
double temperature = 0;
double spannungsmessung = 0;
int main(void)
{
lcd_init();
/* Initialisiere ADC */
// ADC Auto Trigger aktivieren - Single Conversation Mode
ADCSRA &= ~(1 << ADFR );
// ADC Interrupt aktivieren
ADCSRA |= (1 << ADIE );
// Teilungsfaktor auf 64 setzen
ADCSRA |= (1 << ADPS2 ) | (1 << ADPS1 );
// Referenz auf AVCC und Ergebnis links ausgerichtet
ADMUX = (1 << REFS0 ) | (1 << ADLAR );
// Eingang einstellen: Erst alles löschen im ADMUX
ADMUX = (ADMUX & ~(0x1F)) | (0 & 0x1F);
// ADC aktivieren
ADCSRA |= (1 << ADEN );
// Aktiviere Interrupts global
sei ();
/* Hauptschleife */
while(1)
{
////////////////////////////////////////////////////////////////////////
////////////////////////////////
//Temperaturmessung
////////////////////////////////////////////////////////////////////////
////////////////////////////////
//An ADC0 (PC0) hängt über einen Spannungsteiler ein NTC
// ADC Wandlung starten
ADCSRA |= (1 << ADSC );
//Spannung aus ADC Wert berechnen 5/255 = 0.0196
spannung_v = (5.0/255.0)*adcvalue;
//Berechne aus Spannung Widerstand des NTC
ntc_kohm = 100*(5.0/spannung_v-1);
//Aus Widerstand Temperatur in K berechnen
temperature = 1/((log((ntc_kohm/100))/4600.0)+(1/298.15));
//Kelvin in Grad umrechnen
temperature = temperature - 273.15K;
////////////////////////////////////////////////////////////////////////
////////////////////////////////
//Spannungsmessung
////////////////////////////////////////////////////////////////////////
////////////////////////////////
//An ADC2 (PC2) hängt die Spannung die ich messe möchte
ADMUX = (ADMUX & ~(0x1F)) | (2 & 0x1F);
//Kurze Zeit zum umschalten lassen
_delay_ms(10);
// ADC Wandlung starten
ADCSRA |= (1 << ADSC );
//Spannung aus ADC Wert berechnen 5/255 = 0.0196
spannungsmessung = (5.0/255.0)*adcvalue;
////////////////////////////////////////////////////////////////////////
////////////////////////////////
//LCD-Ausgabe
////////////////////////////////////////////////////////////////////////
////////////////////////////////
//Temperature auf dem LCD ausgeben
char BufferT[20];
itoa( temperature, BufferT, 10 );
lcd_clear();
lcd_string("Innentemp.: ");
lcd_string(BufferT);
lcd_string("C");
//Spannung auf dem LCD ausgeben
char BufferV[20];
itoa( spannungsmessung, BufferV, 10 );
lcd_setcursor(0,2);
lcd_string("Bat.Zustand: ");
lcd_string(BufferV);
lcd_string("V");
_delay_ms(50);
}
return 0;
}
ISR( ADC_vect )
{
// Schreibe High - Byte von ADC in adcvalue
adcvalue = ADCH ;
}
Wäre nett wenn ihr helfen könntet =)
Gruß
Sefco schrieb:> Also, was soll dann immer diese "Steht doch da, lies doch mal!".
Damit man die elementarsten Grundlagen (wie z.B. Löschen eines Bits)
nicht
jedem individuell und persönlich erklären muß, nur weil er zu faul ist,
das durchzuarbeiten. Das ist der Sinn- und Zweck von Lehrbüchern und
Tutorials - Zusammenfassung der Grundlagen, die man als Fundament
braucht.
Meiner Meinung nach ein Timing-Problem!
direkt nach ADC-Messung-Start für die Spannung berechnest du die Spanung
- aus dem alten adcvalue.
die ADC-Isr kommt erst viel später.
lg
Limloz
Sefco schrieb:> // ADC Wandlung starten> ADCSRA |= (1 << ADSC );>> _delay_ms(10);> //Spannung aus ADC Wert berechnen 5/255 = 0.0196> spannungsmessung = (5.0/255.0)*adcvalue;
Und wie soll bei deinem Code das Wandlungsergebnis aus dem Register
ADCL/ADCH in die Variable "adcvalue" kommen?
Ich bin mir bei deinem Programm überhaupt nicht sicher, auf welchen
ADC-Eingang wann reagiert wird.
Welche Formel den adcvalue zu welchem Zeitpunkt nutzt ist doch
vollkommen unsynchronisiert.
hier mal meine gekürzte ADC_ISR (zwar Bascom, aber umsetzbar)
Isr_adc:
Select Case Admux
Case 65:
Kanal1 = Adcd
Admux = 66
Kanal1.ready = 1
Case 66:
Kanal2 = Adcd
Admux = 65
Kanal2.ready = 1
End Select
Adcsra.adsc = 1 'Nächste
ADC-Wandlung starten
Return
und in der Main-Loop frag einfach die ready-Signale ab und berechne
danach Spannung und Temperatur
lG
Limloz
Tip schrieb:> Und wie soll bei deinem Code das Wandlungsergebnis aus dem Register> ADCL/ADCH in die Variable "adcvalue" kommen?
Zur Erklärung:
Der Compiler weiß nicht, dass in der ISR die Zuweisung stattfindet und
optimiert die locker weg, weil ihm die Zuweisung überflüssig erscheint.
Wenn du adcvalue als volatile deklarierst, läßt er die Finger davon.
Das sollte im Simulator aber auch zu sehen sein.
MfG
Irgendwie klappt es immer noch nicht. Nach wie vor reagiert er nur noch
auf Spannungsmessungen und ignoriert den Temperatursensor sobald ich auf
ADC2 umstelle.
Hier noch einmal der Code wie er jetzt aussieht:
1
#include<avr/io.h>
2
#include"lcd-routines.h"
3
#include"util/delay.h"
4
#include<stdlib.h>
5
#include<avr/interrupt.h>
6
#include<math.h>
7
8
9
volatileuint8_tadcvalue=0;
10
doublentc_kohm=0;
11
doublespannung_v=0;
12
doubletemperature=0;
13
doublespannungsmessung=0;
14
15
16
17
18
intmain(void)
19
{
20
21
lcd_init();
22
23
24
/* Initialisiere ADC */
25
// ADC Auto Trigger aktivieren - Single Conversation Mode
26
ADCSRA&=~(1<<ADFR);
27
// ADC Interrupt aktivieren
28
ADCSRA|=(1<<ADIE);
29
// Teilungsfaktor auf 64 setzen
30
ADCSRA|=(1<<ADPS2)|(1<<ADPS1);
31
// Referenz auf AVCC und Ergebnis links ausgerichtet
Wieso ist das denn nicht eindeutig, wann der ADC was macht?
Bei //////Temperaturmessung stelle ich auf ADC0 und rechne rum.
Dann stelle ich bei ///////Spannungsmessung auf ADC2 und rechne rum.
Wieso klappt das denn nicht?!?!?!
HILFEEEEEEEEEEEE!!!!
>Und wie soll bei deinem Code das Wandlungsergebnis aus dem Register>ADCL/ADCH in die Variable "adcvalue" kommen?
Klappt doch, wenn ich nur eine Temperatur messe!
Hallo,
zumindest bei der Temperaturmessung wartest du nicht, bis der ADC
überhaupt fertig sein kann.
Du rechnest also definitiv mit dem Wert der Messung zuvor
(Spannungsmessung).
Um bei deinem Prinzip zu bleiben, fehlt da wieder ein delay.
Limloz schrieb:> Um bei deinem Prinzip zu bleiben, fehlt da wieder ein delay.
;-) (musste ich mal noch ergänzen)
@Sefco
Also: da gehöhren nirgends delays hin.
Für den Aufruf des ADC und den Wechsel der Kanäle gibts klare
Richtlinien und Codeschnipsel. Steht vom Feinsten beschrieben im
Datenblatt.
Wenn du das so übernommen hast, dann geh' im Simulator mal schöön
laaangsam, Schritt für Schritt, durch das Programm...
Irgendwann setzt du mal das ADC-IR-Flag und schaust mal was passiert...
Vielen Dank an Limloz!
>Hallo,>zumindest bei der Temperaturmessung wartest du nicht, bis der ADC>überhaupt fertig sein kann.>Du rechnest also definitiv mit dem Wert der Messung zuvor>(Spannungsmessung).>Um bei deinem Prinzip zu bleiben, fehlt da wieder ein delay.
Das war der Fehler. Ein 10ms Delay nach dem Starten des ADCs löst mein
Problem!
Aber ich warte nicht, sondern frag am besten einfach ab, ob der ADC
fertig ist. Das mach ich hier mit, stimmts?
while (ADCSRA & (1<<ADSC) ) {}
Gruß
Sefco schrieb:> Aber ich warte nicht, sondern frag am besten einfach ab, ob der ADC> fertig ist. Das mach ich hier mit, stimmts?
Ich denk', du hast einen Interrupt programmiert?
Sefco schrieb:> ISR( ADC_vect )> {> // Schreibe High - Byte von ADC in adcvalue> adcvalue = ADCH ;> }
Ich muss das Interrupt Flag ADIF auf 1 überprüfen.
Kann mir jemand die Formel sagen, die ich anwenden muss, um aus der 285
die 28 rauszubekommen? Weiß leider nicht wo ich sowas im Tutorial finde.
Gruß
Du musst wissen, was du willst:
Entweder Interrupt: Alles Stopp - Hier muss was wichtiges erledigt
werden - Keiner rührt sich - Jetzt bin ich dran!
Oder Abfrage: Gerade nichts besseres zu tun -> Wert einlesen.
Sefco schrieb:> Wie soll ich der Main Funktion denn sagen, dass Sie so lange warten> soll, bis der Interrupt ausgelöst worden ist?
Würde zwar gehen, ist aber nicht Sinn und Zweck.
Sefco schrieb:> Kann mir jemand die Formel sagen, die ich anwenden muss, um aus der 285> die 28 rauszubekommen?
Welche 285? Hier im ganzen Thread gibt's nur eine, und das ist diese.
Sefco schrieb:> Irgendwie klappt es immer noch nicht.
Ich frag mich, wozu dir im Tutorial fertige ADC Funktionen gegeben
werden, wenn du dann sowieso wieder dein eigenes Süppchen kochst, das
natürlich nicht schmeckt ähhh funktioniert
Was ist falsch an
1
....
2
3
4
intmain()
5
{
6
uint16_tspannung,temperatur;
7
8
....
9
10
ADC_Init();
11
12
13
while(1){
14
15
temperatur=ADC_Read(0);// Kanal 0 ist die Temperatur