Hallo zusammen,
ich verzeifle gerade daran mehrere Analogeingänge hintereinander mit der
Funktion aus dem Tutorial abzufragen. Auf meinem LCD-Display gibt er mir
in jeder Zeile die gleichen Werte aus.
Habt ihr eine Idee warum? Ich lese doch jedes Mal einen anderen Kanal
ein.
Über einen Tipp wäre ich Euch sehr dankbar.
Ich habe den gleichen Code auch zusätzlich als Datei angehängt.
Viele Grüße und noch einen schönen Sonntag wünscht Matthias.
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include "E:\Eigene Dateien\Dropbox\Atmel\AVR
Programme\1aVorlagen\lcd_tools_h.h"
/* ADC initialisieren */
void ADC_ini(void)
{
DDRA=0b00000000; //PORT muss Eingang sein
PORTA=0b10000000; //Port muss low seink, oder
uint16_t result=0;
ADMUX |= (1<<REFS0) ; // interne Spannung nutzen
ADCSRA = (1<<ADPS2) | (1<<ADPS0); // Frequenzvorteiler: setzen auf
32 (4 MHz / 32 = 125 kHz) und ADC aktivieren
ADCSRA |= (1<<ADEN); // ADC aktivieren
ADCSRA |= (1<<ADSC); // eine ADC-Wandlung und danach wird
das Bit wieder auf Null gesetz
while (ADCSRA & (1<<ADSC) )
result=ADCW;
//ADCSRA |= (1<<ADATE); //free running mode aktivieren
}
/* ADC Einzelmessung */
uint16_t ADC_Read( uint8_t channel )
{
// Kanal waehlen, ohne andere Bits zu beeinflußen
ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"
while (ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung
warten
return ADCW; // ADC auslesen und zurückgeben
}
/* ADC Mehrfachmessung mit Mittelwertbbildung */
uint16_t ADC_Read_Avg( uint8_t channel, uint8_t average )
{
uint32_t result = 0;
uint8_t i = 0;
for (i=0; i < average; ++i )
result += ADC_Read( channel );
return (uint16_t)( result / average );
return ADCW;
}
int main(void)
{
uint16_t adcval;
lcd_ini();
ADC_ini();
lcd_writetext(1,1, "Kanal_0: ") ;
lcd_writetext(2,1, "Kanal_1") ;
lcd_writetext(3,1, "Kanal_2: ") ;
lcd_writetext(4,1, "Kanal_3") ;
while( 1 )
{
adcval = ADC_Read(0); lcd_writezahl(1,9, adcval);
adcval = ADC_Read(1); lcd_writezahl(2,9, adcval);
adcval = ADC_Read(2); lcd_writezahl(3,9, adcval);
adcval = ADC_Read(3); lcd_writezahl(4,9, adcval);
}
return 0;
}
Bekommst du auch die gleichen Werte angezeigt, wenn du die Funktion
ADC_Read_Avg nutzt?
Funktionieren die LCD Funktionen sonst ohne Probleme?
Stimmt der Wert des ADC in etwa, der angezeigt wird? Kannst du das
nachvollziehen? Ist wahrscheinlich der Wert von Channel 0 oder?
1
DDRA=0b00000000;//PORT muss Eingang sein
2
PORTA=0b10000000;//Port muss low seink, oder
Sind alle ADC Kanäle an PortA? Warum setzt du die Bits von PortA, sind
doch Eingänge...
Gruß Jens
Nach der Kanalumschaltung solltest du der Analogelektronik eine Chance
geben, sich auf die neue Spannung einzustellen, bevor die Wandlung
gestartet wird.
Gibt es dazu Faustregeln, wie lange die Elektronik braucht, um sich auf
den neuen Kanal einzustellen? Ich habe momentan das gleiche Problem:
Ich habe mir diese Schaltung hier nachgebaut
http://yveslebrac.blogspot.com/2008/10/cheapest-dual-trace-scope-in-galaxy.html,
schreibe die Firm- und Software dazu jedoch neu. Ich frage regelmäßig
die beiden ADC-Eingänge ab und habe dabei festgestellt, dass die
Messwerte ineinander "überbluten"; lege ich den einen Anschluss auf VCC
und den anderen auf GND, erhalte ich nicht wie erwartet 255 und 0 als
Messwerte (ich verwerte nur 8 Bit), sondern
ungefähr Werte um 130 herum. Der Code für den ATTiny85 (mit vUSB) sieht
folgendermaßen aus:
while (ADCSRA & (1<<ADSC)) { /* wait for conversion to finish */ }
7
samples[i] = ADCH;
8
}
Timergesteuert rufe ich dann irgendwann die Funktion zweimal auf:
1
/* sample PB3 (yellow) */
2
sample_data((1<<MUX1 | 1<<MUX0), 2*i+0);
3
/* sample PB4 (green) */
4
sample_data((1<<MUX1 | 0<<MUX0), 2*i+1);
Ohne das _delay_us(50) bluten die Messwert-Paare ineinander über, "50"
ist jetzt einfach willkürlich gewählt - gibt es da konkrete Anweisungen,
wie der Wert zu wählen ist? Wie würde sich der ADC im Freerunning-Mode
verhalten?
Ich mache sowas immer über die ISR.
Dabei lass ich die erste ADC-Messung von einem Timer triggern. Nach
erfolgter Messung wird der Wert in einem Array o.ä. abgespeichert und
gleich die Messung für den nächsten Kanal gestartet.Das wird wiederholt
bis alle Kanäle gemessen wurden.Pausen zwischen den Messungen gibt es
nicht. Funktioniert tadellos.
Grüße
Hallo zusammen,
danke für Eure Beteiligung, mir bei meinem Problem zu helfen.
Ich bin der Sache ein interessantes Stück näher gekommen.
Ich habe es jetzt geschafft den ADC im Free running modus zu betrieben
und mir von einem!!! ausgewählten Kanal die Werte ausgeben zu lassen.
Sobal ich aber mehr als einen Kanal nur im free running modus !!! wähle
und beide auf dem LCD ausgebe,
dann sind die beiden Werte vertauscht!!!
Kanal 1 zeigt dann den Wert von Kanal 2 an
Kanal 2 zeigt dann den Werrt von Kanal 1 an!!!!!
ich habe den Fehler ebend gefunden!!
Ich takte den Prozessor mit 4MHZ und mache im Augenblick nur diese
Mess-Testschleife!!
uint16_t ADC_Read_free_running_mode( uint8_t channel )
{
// Kanal waehlen, ohne andere Bits zu beeinflußen
ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
_delay_us(300);
return ADCW; // ADC auslesen und zurückgeben
}
ich mußte _delay_us(300); einfügen und dann klappte es!!!
In einem normalen Programm kann ihc vielleicht auf diese _delay routine
verzichten, weil der Prozessor noch andere sachen macht.
bin ich mit diesen 300us =0,3ms im free running mode immer noch
schneller als im single mode??
denn darum ging es mir Zeit zu gewinnen.
Viele Grüße,
Matthias.
Martin schrieb:> Nach der Kanalumschaltung solltest du der Analogelektronik eine Chance> geben, sich auf die neue Spannung einzustellen, bevor die Wandlung> gestartet wird.
Hallo Martin,
ja genau das war der Fehler. Habe nicht lang genug nach der
Kanalumschaltung gewartet
300us Zeit haben gereicht, jetzt geht es.
Hallo !
hast du das Datenblatt zu deinem AVR gelesen ?
Beim atMega88 steht das auf Seite 248
Table 23-1. "ADC Conversion Time"
Dort steht dass nach jedem Kanalwechsel ein Dummyread der ersten ADC
Wandlung gemacht werden muss.
Ich habe gerade heute eine neue 64-Fach Oversample Version mit Trigger
über Timer1 COMPB geschrieben.
Das sieht dann bei mir so aus:
1
#define ADC_PRESCALE (1<<ADPS2^1<<ADPS1^1<<ADPS0) // wird abhängig von F_CPU berechnet !
Hallo zusammen,
dake für Eure Tipps.
Ich habe leider erst wieder am Wochenende Zeit diese durch zuarbeiten.
Ich melde mich dann ggf. nochmal bei Euch.
Viele Grüße,
Matthias.
Matthias Lipinsky schrieb:> ..oder nach der Messung den Kanal auslesen und anhand dessen zuordnen..
Hallo Matthias,
wie kann ich denn bitte nach der Messung den Kanal auslesen?
Ich bin gerade dabei mich wieder damit zu beschäftigen.
Viele Grüße,
Matthias.
Matthias,
schaust du auch mal in das Datenblatt?
Das steht doch alles, z.B. für einen atMega88 auf Seite 254f,
Register Description
23.8.1 ADMUX – ADC Multiplexer Selection Register
ADMUX
0000 ADC0
0001 ADC1
:
0111 ADC7
Mitleser schrieb:> Matthias,>> schaust du auch mal in das Datenblatt?>> Das steht doch alles, z.B. für einen atMega88 auf Seite 254f,>> Register Description> 23.8.1 ADMUX – ADC Multiplexer Selection Register>>> *ADMUX*>> 0000 ADC0> 0001 ADC1> :> 0111 ADC7
Hi, grüß Dich,
danke für Deine Hilfe.
Ja ich schaue auch gerade seit einer Stunde ins Datenblatt und erarbeite
mir die Analogmessung im Detail, was ich vorher nicht ganz so intensiv
getan habe.
anbei ein Bild von einem Teil meiner aktuellen Ausarbeitung zum Beweis.
:-)
ich weiß auch wie man andere Kanäle wählt, habe aber Deine Ausführung
leider nicht verstanden wie Du nachträglich nach einer Messung einen
Kanäl zuordnen willst.
Darum ging es nur.
Viele Grüße,
Matthias.
Hallo,
du schriebst "wie kann ich denn bitte nach der Messung den Kanal
auslesen?"
1
uint8_tadc_register=ADMUX&0x07;
Wenn im Datenblatt R/W steht, dann können Register auch gelesen werden.
Falls das mal nicht geht, einfach eine statische Variable mit dem
aktuellen ADC-Eingang belegen, dann hat man auch immer die Information
parat.
Man muss ja auch vorher den ADC-Eingang setzen, dabei muss das ADMUX
Register verändert werden und ein "dummy readout" folgen !!
Oben steht eine seht eine funktionierendes Beispiel aus einem Programm:
Beitrag "Re: Analogmessung mehrerer Kanäle hinterteinander, überall gl. Werte?"
.
Hallo,
du schriebst "wie kann ich denn bitte nach der Messung den Kanal
auslesen?"
1
uint8_tadc_register=ADMUX&0x07;
Wenn im Datenblatt R/W steht, dann können Register auch gelesen werden.
Falls das mal nicht geht, einfach eine statische Variable mit dem
aktuellen ADC-Eingang belegen, dann hat man auch immer die Information
parat.
Man muss ja auch vorher den ADC-Eingang setzen, dabei muss das ADMUX
Register verändert werden und ein "dummy readout" folgen !!
Oben steht eine funktionierendes Beispiel aus einem Programm:
Beitrag "Re: Analogmessung mehrerer Kanäle hinterteinander, überall gl. Werte?"
.
64 Byte zur Mittelwertbildung ? Und dann noch eine Division ?
0. Reset
1. in ISR: increment Wandlungszähler
2. in ISR: gucken Wandlungszähler=1 wenn nicht weiter bei 5
3. in ISR: gucken ob Wandlungszähler>Anzahl der gewünschten Mittelungen
4. in ISR: bei der 1. Wandlung Flag setzen und Wert A speichern
5. in ISR: Wert B speichern und auf Wert A addieren und
um 1 nach rechts shiften (also geteilt durch 2) und in A speichern
speichersparend und einfach
Hallo,
danke für Deine Hilfe.
Mein konkretes Problem sieht folgendermaßen aus, oder ist es gar kein
Problem und ich packe es nur falsch an???
ich möchte in einem Programm mehrere Kanäle nacheinander abfragen.
Die Temperaturabfrage ist nicht zeitkritisch und wird einmal in der
Sekunde durchgeführt.
Auch die Strommessung des DC Motors ist nicht zeitkritisch und wird 1x
die Sekunde ausgeführt
Ein anderer, dritter Kanal, soll die Motorpotiwerte eines 24V DC Motors
so oft wie möglich messen.
Meine funktionierende Routine für die Aufgabenstellung sieht so aus:
was mich daran stört schreibe ich dann weiter unten
uint16_t ADC_Read_free_running_mode( uint8_t channel )
{
static uint8_t channel_vorheriger, result;
if (channel_vorheriger!=channel)
//wenn ein neuer Kanal gewählt wurde, dann Kanal einstellen und
Testmessung
{
ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);
// Kanal waehlen, ohne andere Bits zu beeinflußen
_delay_us(250);
// 250us Warten, damit die Elektronik sich darauf einstellen kann,
verlangsamt das Programm unnötig
result=ADCW;
//eine Messung machen die verworfen wird
}
channel_vorheriger=channel;
return ADCW; // ADC auslesen und zurückgeben
}
mich stört die Wartezeit von 250uS nach der Kanaleinstellung. Ich weiß,
das ich der Elektronik Zeit geben muss sich auf den neuen Kanal
einzustellen.
Kann ich das Programm technisch nicht aber anders lösen, ohne das das
Gesamte Programm steht und wartet????
Das Beispiel oben vom Kollegen verwendet einen festen Kanal, da tritt
das Problem nicht auf.
Ich muss nach der Kanaländerung Wartezeit einbauen, kann ich das aber
auch ohne DElay lösen???? Welche Möglichkeiten seht ihr da?
Ein Interruptflag gibt es im free running mode ja auch nicht wie ich
gelesen habe!!!
Selbst, wenn ich ein Timer für das Messen des Kanals alle 50ms aufsetze,
braucht die Elektronik nach der Kanaländerung Zeit und das delay würde
das Programm und somit die Geschwindigkeit der Programmausführung
verlangsamen.
Oder ich setze den Timer auf 25ms!! Beim ersten Aufruf der ISR setze ich
nur den Kanal und beim 2. Aufruf nach weiteren 25mS lese ich diesen aus.
Dann muss ich aber 3 Kanäle in die ISR einbauen, die sich zeitlich nicht
in die Quere kommen.
Temperaturmessung alle 1s und
Strommessung alle 1s
Ich hoffe, ich konnte mich verständlich ausdrücken.
Kann ich den Code hier noch besser darstellen? Bei Euch sah das immer
anders aus.
Viele Grüße,
Matthias.
Hallo Matthias,
ich habe noch noch einen kleinen Fehler drin:
1
if(..){
2
// Warte bis die Wandlung abgeschlossen ist
3
loop_until_bit_is_not_set(ADCSRA,(ADIF);
4
5
// sonst wie oben.
6
}
Im Handbuch steht
[q]
Bits 3:0 – MUX3:0: Analog Channel Selection Bits
The value of these bits selects which analog inputs are connected to the
ADC. See Table 23-3
for details. If these bits are changed during a conversion, the change
will not go in effect until this
conversion is complete (ADIF in ADCSRA is set).
[/q]
Hallo Matthias,
ich habe noch noch einen kleinen Fehler drin:
1
if(..){
2
// Warte bis die Wandlung abgeschlossen ist
3
loop_until_bit_is_set(ADCSRA,(ADIF);
4
5
// sonst wie oben.
6
}
Im Handbuch steht
[q]
Bits 3:0 – MUX3:0: Analog Channel Selection Bits
The value of these bits selects which analog inputs are connected to the
ADC. See Table 23-3
for details. If these bits are changed during a conversion, the change
will not go in effect until this
conversion is complete (ADIF in ADCSRA is set).
[/q]