Hallo Leute,
das ist mein erster Beitrag hier, muss mich aber schon für die versch.
Tutorials und Beiträge bedanken, sie sind recht nützlich!
Nun habe ich auch selbst ein Problem, an dem ich schon eine Weile
grüble:
Gegeben ist ein
AVR-Board(http://ti.tuwien.ac.at/ecs/teaching/courses/mclu/manuals/atmega16)
und ein Erweiterungsboard
(http://ti.tuwien.ac.at/ecs/teaching/courses/mclu/manuals/i-o-board),
das wir auf der Uni verwenden. Ich habe mir das Board selbst zugelegt
und möchte nun eine Aufgabe erweitern:
Über den ADC möchte ich den Wert von einem Poti einlesen und dann den
Wert auf 4 Stk. der 7Segment Anzeigen ausgeben.
Nun zu meinen Problemen:
Ich rechne den Wert von ADC in 0-5V um, und gebe das auf den Anzeigen
aus. Um die einzelnen Stellen bestimmen zu können, experiemntiere ich
mal mit einem fixen Wert und beachet den Wert von ADC vorerst nicht.
Ich verwende Timer1 (also 16bit, mit Prescaler 8 und CPU_F 16MHz, CTC
Mode, OCR1A auf 8000) um meien Displays zu multiplexen. Das funktioniert
auch gut, davon vielleicht abgesehen, dass ich bei manchen Ziffern
einige Segmente als ganz leicht beleuchtet sehe. Ich nehme an, das
könnte davon kommen, dass ich zu schnell multiplexe und auf den
Ausgangspins noch ein wenig Spannung von den vorhergehenden Ziffern
anliegt (bin dem noch nicht nachgegangen).
Was aber für mich verwunderlich ist, sobald ich Timer2 (8bit, Prescaler
auf 32, CTC, OCR2 auf 200) nehme, bekomme ich statt 3.906 nur noch 3.880
auf dem Display angezeigt. Woran kann das liegen?
Schalte ich meine ADC-Init Routine dazu, dann erscheinen überhaupt nur
noch "3.3.", wobei der zweite "3." nur noch halb so hell leuchtet, wie
der erste. Das ist mit beiden Timern der Fall. Ich nehme an, die
ADC-Konversionsroutine würde zu viel Zeit brauchen, sodass die Anzeige
nicht mehr richtig dargestellt werden kann?
Da wäre noch dazu zu sagen, dass ich in der Timer-ISR nur die aktuelle
Position für das Multiplexing berechne, die Umrechnung und die Anzeige
mache ich in der For-Schleife in der main() Funtkion.
Verschiebe ich die Berechnung und die Anzeige in die ISR, wird "3.0"
anzeigt, wobei auch hier die "0" schwächer leuchtet.
Achja, ich programmiere in C, unter Linux.
Vielen Dank schon mal im Voraus.
MfG
BBKing
okok, ich weiss, ihr habt keine Glaskugel...
zum Code:
ich weiss, dass man da noch einiges "besser" machen kann, er ist noch
lange nicht "vollendet"
in der initTimer() Funktion gibt es die Initialisierung für den 8bit
bzw. 16bit Timer...wie oben schon beschrieben, ergeben sich die zwei
verschiedenen Verhaltensweisen bei der Ausgabe.
Weiters verstehe ich nicht, warum initADC() ebenfalls die Ausgabe
beeinflusst?
VIelen Dank im Voraus.
MfG
BBKing
Du liest zwei mal ADCH aus. Laut Datenblatt ist es bereits nach dem
ersten auslesen auf 0.
Für was ist diese float Berechnung?
Da kannst du ein Zeitproblem haben, sollte man vermeiden.
Mit der Float-Berechnung skaliere ich den Wert aus ADCH auf 0-5V hinauf,
und bestimme danach die Ziffern für die 1er, 10tel, 100stel und 1000stel
Stelle.
Bin jetzt schon ein bisschen weiter, dieser Thread hat mir einiges
geholfen ADC mit Multiplexanzeige, aber dei Anzeige flimmert immer
noch irsinnig.
Wie müsste man soetwas prinzipiell machen?
Danke
Hubert G. schrieb:
> Du liest zwei mal ADCH aus. Laut Datenblatt ist es bereits nach dem> ersten auslesen auf 0.
Quelle?
Ich glaube nämlich nicht, dass das Register nach dem Auslesen gelöscht
wird.
Soweit ich verstanden habe, stimmt das mit dem 0-Wert nicht:
"If the result is left adjusted and no more than 8-bit precision is
required, it is sufficient to read ADCH. Otherwise, ADCL must be read
first, then ADCH, to ensure that the content of the Data Registers
belongs to the same conversion. Once ADCL is read, ADC access to Data
Registers is blocked. This means that if ADCL has been read, and a
conversion completes before ADCH is read, neither register is updated
and the result from the conversion
is lost. When ADCH is read, ADC access to the ADCH and ADCL Registers is
re-enabled."
aber das ist eh egal, weil ich lese ADCH nur einmal, die andere Zeile
ist auskommentiert...
Ich glaube auch, dass das ein Timing-Problem ist, weiss nur nicht, wie
ich anfang soll zu debuggen....
Fließkommaberechnungen sind irre zeitaufwändig, mach das mit Ganzzahlen
und dividiere am Schluß.
Die Toleranz von Referenzspannung ist sicher größer als die
Ungenauigkeit wenn du ein paar Stellen weglässt.
Das mit dem ADCH auf 0 stellen ist falsch, the ADC Data Register is not
updated until ADCH is read.
Deine ganze Multiplexerei ist .... Müll
Du kannst dir ruhig 4 globale unsigned char (zb als ein Array)
zurechtlegen, in die du das auszugebende Bitmuster ablegst.
In der ISR machst du dann
momentan angezeigte Stelle abschalten
Stelle um 1 weiterschalten (unter Berücksichtigung des 'Überlaufs'
Die für diese Stelle notwendigen LED einschalten (Bitmuster
dafür hlst du dir aus dem Array
Anzeige einschalten.
Und du brauchst auch keine so dermassen hohe Multiplexfrequenz. Wenn
deine 4 Stück 7-Segment 100 mal in der Sekunde reihum einmal
eingeschaltet werden, dann langt das normalerweise dicke.
Wenn du dann eine Ausgabe machen willst, dann schreibst du einfach in
das globale Array für die richtige Stelle das Bitmuster, welches die
richtigen LED einer Stelle zum Leuchten bringt.
Habe das jetzt ausprobiert, der Unterschied ist irre! Ich bin zwar mit
den angezeigten noch nicht zufrieden, aber die Anzeige selbst ist schon
viel schöner (100%ig aber noch nicht OK)!
Danke!
// PORTB = Wert, damit die momentane iPos Anzeige dunkel wird
7
// Ich rate mal
8
PORTB=0;
9
10
if(iPos<3)
11
iPos++;
12
else
13
iPos=0;
14
15
PORTC=Stelle[iPos];
16
PORTB=aDisplayPos[iPos];
17
}
Wenn du einen neuen Wert auszugeben hast, dann schreibst du das Ergebnis
von aIntDisplayDecode in die jeweilige Stelle[i], je nach Wert.
1
voidOutNumber(unsignedintNumber)
2
{
3
Stelle[3]=aIntDisplayDecode[Number%10];
4
Number/=10;
5
Stelle[2]=aIntDisplayDecode[Number%10];
6
Number/=10;
7
Stelle[1]=aIntDisplayDecode[Number%10];
8
Number/=10;
9
Stelle[0]=aIntDisplayDecode[Number%10];
10
}
(Hab nicht rausgefunden, wie man bei dir eine 7_Segment über PORTB
abschalten kann)
(Wozu hast du eigentlich verschiedene Decodiertabellen für int und
float? Die einzelnen Ziffern 0 bis 9, A bis F sehen doch völlig gleich
aus?)
Jetzt hast du dann alle Zeit der Welt, eine Zahl in 4 Ziffern zu
zerlegen, unabhängig vom Multiplexing der Anzeige.
Die Werte treiben das Display! Und nicht die Multiplexfrequenz bestimmt,
wann du deine Zahl zerlegen musst!
Balazs B. schrieb:
> Mit der Float-Berechnung skaliere ich den Wert aus ADCH auf 0-5V hinauf,> und bestimme danach die Ziffern für die 1er, 10tel, 100stel und 1000stel> Stelle.
Wozu?
#define ADC_REF_QUOT_8BIT 0.01953125 //= 5/(255+1)
Wenn du nicht mal 5 sondern mal 5000 rechnen würdest, dann ist das
Ergebnis deiner Umrechnerei eine ganze Zahl von 0 bis 5000. Noch den
Dezimalpunkt nach der dritten Stelle einschalten und schon steht auf der
Anzeige 5.000 anstatt 5000 oder 3.654 anstelle von 3654.
Aber: Du hast alles in int gerechnet (oder in long, wegen der Überläufe)
und das geht um einiges schneller als float.
Karl heinz Buchegger schrieb:
> Deine ganze Multiplexerei ist .... Müll>> Du kannst dir ruhig 4 globale unsigned char (zb als ein Array)> zurechtlegen, in die du das auszugebende Bitmuster ablegst.>> In der ISR machst du dann> momentan angezeigte Stelle abschalten> Stelle um 1 weiterschalten (unter Berücksichtigung des 'Überlaufs'> Die für diese Stelle notwendigen LED einschalten (Bitmuster> dafür hlst du dir aus dem Array> Anzeige einschalten.
ich mach das jetzt so:
die Berechnung der gerade aktuellen Displaystelle mach ich in der ISR
vom Timer und starte dort die Konvertierung.
in der ISR vom ADC lese ich den Wert aus, mache meine Berechnungen und
schalte dort die aktuelle Anzeige ein. Jetzt ist die Anzeige
wunderschön!
> Und du brauchst auch keine so dermassen hohe Multiplexfrequenz. Wenn> deine 4 Stück 7-Segment 100 mal in der Sekunde reihum einmal> eingeschaltet werden, dann langt das normalerweise dicke.>> Wenn du dann eine Ausgabe machen willst, dann schreibst du einfach in> das globale Array für die richtige Stelle das Bitmuster, welches die> richtigen LED einer Stelle zum Leuchten bringt.
Das habe ich mittlerweile schon so umgebaut, der Code von Peter
Dannegger war dazu echt hilfreich!
Habe noch den Code erweitert, der ADC Wert wird auf Timer1 als PWM
Impuls ausgegeben um einen Motor anzutreiben.
Danke an alle für die Tipps!
Karl heinz Buchegger schrieb:
> Ungefähr so> [C]> ....> unsigned char Stelle[4];>> SIGNAL(SIG_OUTPUT_COMPARE2)> {> // PORTB = Wert, damit die momentane iPos Anzeige dunkel wird> // Ich rate mal> PORTB = 0;
geht eh so. Ich möchte dann das ganze in allgemeine Funktionen
umschreiben, sodass man die Ports einmal am Anfang angeben muss und
sonst nie wieder. Work in Progress...
> (Wozu hast du eigentlich verschiedene Decodiertabellen für int und> float? Die einzelnen Ziffern 0 bis 9, A bis F sehen doch völlig gleich> aus?)
Vielleicht eine Overkill Lösung, aber im Array mit den Floats habe ich
den Punkt noch dazugeschaltet...
> Die Werte treiben das Display! Und nicht die Multiplexfrequenz bestimmt,> wann du deine Zahl zerlegen musst!
Das ist wohl der Punkt! Die Frage ist nur, wo Du die Funktion dazu
aufrufst? In meinem vorigen Posting, das ca. zeitgleich mit Deinem
eingetroffen ist, habe ichs ein bisschen beschrieben....
Riesen Dank für die Mühe!
Balazs B. schrieb:
>> Die Werte treiben das Display! Und nicht die Multiplexfrequenz bestimmt,>> wann du deine Zahl zerlegen musst!>> Das ist wohl der Punkt! Die Frage ist nur, wo Du die Funktion dazu> aufrufst?
Gar nicht.
Das Multiplexing findet völlig unabhängig von allem anderen statt.
Das globale Array kann man sich als eine Art Displayspeicher darstellen,
welches die Anzeige repräsentiert. Schreibt man dort ein Muster für die
7-Segment Anzeige hinein, dann sorgt ein 'magischer Mechanismus' (die
ISR) dafür, dass dieses Muster auf der Anzeige erscheint. Für denjenigen
Programmteil, der sich um die eigentliche Auswertung kümmert und
letztendlich mit einem Zahlenwert hochkommt, ist es völlig unerheblich,
wie dieser Mechanismus passiert. Der beschreibt einfach nur seinen
'Displayspeicher'. Für ihn sind diese 4 Elemente des globalen Arrays
die 7-Segmentanzeige.
Und die Funktion, die eine Zahl in 4 Stellen aufdröselt: Die wird
klarerweise aufgerufen, wenn eine neue auszugebende Zahl vorliegt.
Balazs B. schrieb:
>> Vielleicht eine Overkill Lösung, aber im Array mit den Floats habe ich> den Punkt noch dazugeschaltet...
Wenn du eine allgmeine Lösung haben willst, dann lass dieses zusätzliche
Kodierarray weg. Das verwirrt ungemein.
Alles was du brauchst ist eine Funktion
der du eine Zahl übergibst und auch noch dazusagst an welcher Stelle der
Dezimalpunkt auftauchen soll.
Von mir aus mach dir noch eine Funktion für einen float, aber wie schon
gesagt wurde, vermeidet man float. In den allermeisten Fällen benötigt
man sie nicht. Man macht das eher so, dass alle Zahlen zb mal 1000
genommen werden und bei der Anzeige der Dezimalpunkt an der richtigen
Stelle eingeschmuggelt wird. Für den Benutzer, der aufs Display schaut,
kommt es aufs gleiche raus. Aber den µC entlastet man damit schon enorm.