Hallo Community! Mir geht es darum ein 7-Segment Display mit einem ATmega8 anzusteuern. Das alles möchte ich in C (GCC) machen. Einzelne Zahlen darzustellen ist ja nicht das Problem, jedoch scheitere ich daran Variablen auszugeben. Also dem Controller beizubringen, welche Ziffer zu welchem Binärwert für PortD (da hängt das Display dran) gehört. Macht man sowas über einen #define-Block, mit einem Array oder noch anders? Wenn möglich sollte es nicht den SRAM belasten. Weiter habe ich Probleme beim Multiplexen (Segmentweise). Wie bekomme ich es hin, dass er bei z.B. einer 21 die 2 "isoliert" und an erster Stelle anzeigt und das gleiche mit der 1 an zweiter Stelle macht? Ich danke schon mal! Edgar
Gast
#3144115
Gast
#3144118
oder auch ohne 'Pleximult' Beitrag "7-Segm.-LED-Anzeige, 6-stellig, statische Ansteuerung mit (74HC)4094"
Gast
#3144121
zeiten link für C vergessen: http://www.mikrocontroller.net/articles/Uhr
Gast
#3144122
bitte w einfügen ;)
Ich danke euch schon mal!
Laut
>http://www.mikrocontroller.net/articles/Uhr
macht man das also mit nem Array. Und das liegt dann nicht auf dem Ram?
Das Array ist ja "const", kann also nicht mehr verändert werden. Wiso sollte es der GCC dann ins RAM stecken?
Gut, hatte ich mir schon gedacht, wollte nur sicher gehen. Dann wäre nur noch die Frage des Multiplexens. Das einzige was mir einfällt wären jetzt lustige Konstruktionen mit Strings, aber ich glaube irgendwie nicht, das das so richtig ist... Edgar
Gast
#3144154
Michael L. schrieb: > Das Array ist ja "const", kann also nicht mehr verändert werden. Wiso > sollte es der GCC dann ins RAM stecken? Weil das für den Compiler einfacher ist. Wenn man wirklich haben will daß er das Array im Flash läßt braucht man entsprechende "progmem" attribute.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
Gast
#3144158
So groß sind die Arrays nicht, dass man da unbedingt vermeiden muss die ins RAM zu packen. Das sind nur 10 Bytes für die Umrechnung von 0-9 in die Segmente. Den Speicher für die aktuelle Anzeige muss man ja ohnehin ins RAM ablegen, und das sind auch nur 1 Byte je Stelle. Wenn das Array im RAM steht, wird der Zugriff in der Regel schneller und einfacher. Damit die Daten nicht ins Flash kommen müsste man schon PGM_space nutzen und dann einen umständlicheren Zugriff, weil das Flash einen anderen Adressbereich nutzt. Entsprechend landen bei GCC-AVR auch konstante Arrays normalerweise im RAM. p.s. für ungenutztes RAM gibt es kein Geld zurück.
Gast
#3144167
Gut, dann ins RAM, das bisschen interessiert nun auch nicht... Ich als Anfäger bin nur etwas vorsichtiger, man liest ja auch überall, das man ressourcensparend programmieren soll. Die Frage mit dem Multiplexen wäre da noch, oder habe ich dort einen vollkommen falschen Ansatz?
Gast
#3144186
Was willst Du für eine Zahl darstellen? Wie viele Stellen? Also z.B. integer von 0000 bis 9999 bei 4 Stellen
Erstmal sollen es nur zweistellige sein.
Gast
#3144195
Dann brauchst Du nur die Funktionen Berechnung und Anzeige aus dem Uhr Beispiel anzupassen. Das wichtigste ist dabei die modulo Operation.
Mein Wert kommt vom ADC und wird ein bisschen umgerechnet, am Ende bleibt eine zweistellige Zahl. Meine Idee gerade: Bei einem Integer wird bei einer Division immer abgerundet(richtig?). Also einfach den Wert durch 10 und gut ist. Also 21 / 10 = 2,1 -> 2 wird ausgegeben und dann angezeigt. Für die 1 dann 21 % 10 müsste doch 1 ergeben Könnte das funktionieren?
Gast
#3144245
Ja.
Bin am programmieren ^^
Michael L. schrieb: > Das Array ist ja "const", kann also nicht mehr verändert werden. Wiso > sollte es der GCC dann ins RAM stecken? weil der prozessor das flash in der avr-architektur nicht einfach so adressieren kann. siehe datenblatt, suchfunktion, compiler-doku, etc pp.
Ok, es gab ein paar Schwierigkeiten (Fehler meinerseits), aber jetzt geht es! Ich danke euch! Edgar
Gast
#3144314
Michael H. schrieb: > > weil der prozessor das flash in der avr-architektur nicht einfach so > adressieren kann. Klar kann er das, kostet (jedenfalls bis 64k Flash) nur genau einen Takt mehr pro Zugriff.
c-hater schrieb: > Klar kann er das, kostet (jedenfalls bis 64k Flash) nur genau einen Takt > mehr pro Zugriff. nein, kann er nicht. direkte adressierung ist nicht möglich. und ein compiler wird den teufel tun und die adressierung einfach so einbauen. das ist bei harvard-architekturen prinzipbedingt so. aber wenn dus nicht glauben willst, kann mir das egal sein. ein drittes mal schreiben werd ichs nicht. lies es nach oder stirb dumm - deine sache.
Gast
#3144543
edgar 339 schrieb: > Ich als Anfäger bin nur etwas vorsichtiger, man liest ja auch überall, > das man ressourcensparend programmieren soll. Höre nicht auf die Angstmacher! Das sind vielfach Ratschläge (heute sind es Vorurteile), die im letzten Jahrhundert entstanden sind und nicht mehr aus den Köpfen wollen. Als Anfänger bietet Dir ein ATmega8 unendlich viel Platz. Kümmere Dich zunächst um die Lösung Deines Problems. Mit zunehmender Erfahrung programiert man automatisch effizienter. Zur Orientierung noch ein Beispiel, wie man ADC-Werte auf ein Display bekommen kann. Beitrag "7-Segm. LCD-Ansteuerung, 4-stelliges Einbaumodul, Attiny45" AVR-GCC erzeugt 2122 Byte Code und es werden 75 Byte Ram gebraucht - ohne irgendwelche platzsparenden Tricksereien. Für eine 2-stellige Anzeige würde ich kein Multiplex veranstalten. Zwei Schieberegister CD4094 (die CMOS-Version) direkt an die Segmente angeschlossen reichen aus. Statischer Betrieb: keine Treiber, keine Widerstände und kein Ausfall der Anzeige, falls der µC hängen bleibt, keine 'Geistersegmente', kein Ripple in der Versorgungsspannung, keine Störstrahlung (um mal selber Angst zu schüren :-), ... Aber mache, wie Du willst.
Danke für deine Vorschläge! Das mit dem Angst machen ist mir auch schon aufgefallen. Bisher aber nur in den ganzen Schaltungen. Mindestbeschaltung der Controller beispielsweise. Abblockkondensatoren kann ich ja noch verstehen, aber Start-Up-Kondensator, oder nen externen Brown-Out-Detektor? Ich wollte das Multiplexen, damit ich erstens lerne, wie das geht, und da dort noch weitere Digits dazukommen(derzeit läuft es mit 4, sollen 6 werden). Ein Problem fällt jetzt schon auf: die Berechnungen für die Ziffern dauern unterschiedlich lange, deshalb sind auch die Digits unterschiedlich hell. Ich werde das jetzt mit Timerinterrups machen, dann sollte das ja gehen. Edgar
Gast
#3144688
edgar 339 schrieb: > Ich wollte das Multiplexen, damit ich erstens lerne, wie das geht, und Das ist gut, mach es! Selber hatte ich im Laufe der Zeit einige Großanzeigen (25cm Ziffernhöhe) gebaut, wo Multiplexen nicht mehr sinnvoll war; aktuell habe ich eine Schaltung vor mir, die noch mit <3V arbeitet. Auch hier ist die statische Ansteuerung einfacher. Deshalb mein Rat zum statischen Betrieb. > unterschiedlich hell. Ich werde das jetzt mit Timerinterrups machen, > dann sollte das ja gehen. Muliplexen sollte man grundsätzlich per Timerinterrupt machen. Achte auf ein bißchen Totzeit zwischen dem Abschalten des letzten Digits und dem Einschalten des nächsten. Die Zeit dazwischen kann man für weitere Befehle nutzen, die auch im Interrupt ausgeführt werden müssen. Andernfalls kommt es zu den 'Geistersegmenten'. Aber das wirst Du selber sehen.
...und ich dachte, die Leds scheinen durch die Wände zwischen den Segmenten :D Ich bin fast fertig, benutze aber diesmal einen mega48 und bin erst mal nicht da... Edgar
So, fertig. Das Programm zählt mit Timer1 hoch und zeigt mit Timer0 an.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
49 | |
50 | |
51 | |
52 | |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | |
59 | |
60 | |
61 | |
62 | |
63 | |
64 | |
65 | |
66 | |
67 | |
68 | |
69 | |
70 | |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | |
Edgar
Gast
#3145041
Da hast Du ja schon mal ein Erfolgserlebnis. Das braucht man unbedingt.
Wenn Du kurze Kommentare in den Code einfügst, kann man ihn später
einmal besser verstehen, insbesondere wenn es nicht mehr 50 sondern 5000
Zeilen sind.
Eine Sache ist aber ganz wichtig: deklariere die Variablen i und y, die
nur in der ISR von T0 gebraucht werden auch dort.
ISR (TIMER0_OVF_vect)
{
static uint8_t i; // Schleifenzähler
uint16_t y; // Hilfsvariable und Index
....
Ferner sollte erst PORTD geschrieben werden und dann PORTB, auch wenn es
aktuell nichts auszumachen scheint.
Mit den Berechnungen in der T0-ISR hast Du ja allen Optimierungsgurus
zunächst die kalte Schulter gezeigt :-)
Irgendwann wirst Du ein 'ausgabe_register[MAX_DIGITS]' verwenden und im
Interrupt nur noch die betreffeden Digits indiziert ausgeben. Spätestens
dann, wenn noch ein '-' Vorzeichen angezeigt oder die Vornullen
unterdrückt werden sollen.
Die Kommentare habe ich nur weg gelassen, damit es schnell geht :D
Das die immer dazu gehören habe ich schon am eigenen Leibe erfahren
müssen...
Globale Variablen sind böse, ich weiß...
i muss aber global sein, es soll ja von Interrupt zu Interrupt erhöht
werden, ansonsten wird es jedes mal auf 0 gesetzt, oder?
Zur Leistungsoptimierung hatte ich mir gedacht: Für jede Ziffer eigene
Variable, und die Berechnung nur durchführen, wenn sich die anzuzeigende
Zahl auch wirklich geändert hat.
Außerdem wollte ich noch die Berechnung außerhalb der ISR durchführen.
Danke noch für das mit den Ports, da muss man nicht lange nachdenken, um
zu sehen das das sinnvoller ist ...aber man muss selbst erstmal drauf
kommen...
>ausgabe_register[MAX_DIGITS]
Ich verstehe zwar noch nicht, was das wird oder soll, aber wenn ich
soweit bin, kann ich ja wieder hier vorbeischauen :)
Ich danke dir(und natürlich auch den anderen hier) das ihr mir helfen
wollt und hoffe, das ich das irgendwann wieder gut machen kann(wie war
das mit Maus und Löwe?...)
Edgar
Gast
#3145306
edgar 339 schrieb: > Globale Variablen sind böse, ich weiß... Naja, ganz sooo ist es dann auch wieder nicht - es ist nur einfach sinnvoll, die Variablen in der kleinstmöglichen Sichtbarkeitsebene zu deklarieren - d. h. in einer Funktion, wenn sie nur ein der Funktion gebraucht werden, oder in der Datei, wenn sie nur in der Datei gebraucht werden. > i muss aber global sein, es soll ja von Interrupt zu Interrupt erhöht > werden, ansonsten wird es jedes mal auf 0 gesetzt, oder? Nicht, wenn Du es mit dem Schlüsselwort "static" versiehst. >>ausgabe_register[MAX_DIGITS] > Ich verstehe zwar noch nicht, was das wird oder soll, aber wenn ich > soweit bin, kann ich ja wieder hier vorbeischauen :) Der Hintergedanke ist, dass die Berechnung in der main()-Loop oder wo auch immer vorgenommen und in der Timer-ISR nur das Ergebnis ausgegeben wird. Als Variable zur Übergabe dient dann sinnvollerweise ein (globales) Array, das den anzuzeigenden Wert jedes Digits enthält.
>Berechnung in der main()-Loop oder wo >auch immer vorgenommen und in der Timer-ISR nur das Ergebnis ausgegeben >wird Dann habe ich ja schon aus Versehen richtig gedacht :D Ah, jetzt ist es klar, mich hatte "Register" nur verwirrt. Hatte überhaupt nicht an ein Array gedacht... Danke!
M. N. schrieb: > Andernfalls kommt es zu den 'Geistersegmenten'. Aber auch nur, wenn man Treibertransistoren im Emitterschaltung benutzt. Die Kollektorschaltung ist schnell genug und spart obendrein den Bassiswiderstand.
Gast
#3145834
Peter Dannegger schrieb: > Die Kollektorschaltung ist schnell genug und spart obendrein den > Bassiswiderstand. Das ist ja nicht verkehrt, aber wer macht es denn so? :-) Allerdings ist der Anwendungsbereich eingeschränkt, da der sinnvolle Versorgungsspannungsbereich bei rund 4-5V liegt. Ein 3V-Design geht nicht mehr, da der Spannungsabfall (Segment ca. 1,8-2,2V + 2 x Vbe ca. 1,4V) zu groß wird. Die obere Grenze wird durch VCC des Prozessors begrenzt. Größere Anzeigen mit mehreren LEDs in Reihenschaltung (>=2) funktionieren somit auch nicht. Auf der anderen Seite sind ca. 5µs Totzeit bei einer Multiplexfrequenz von 0,4 - 1kHz (>= 1ms) doch kein Thema. Die Helligkeit wird um max. 0,5% rduziert, was überhaupt nicht negativ auffällt. Wenn wir schon bei der Helligkeit sind: bei statischer Ansteuerung kann man diese bequem über ein PWM-Signal an output-enable steuern. Ein Pluspunkt, den ich oben noch vergessen hatte :-)
Noch was verbesserbar? :D Das einzige was mir einfallen würde, währe, nur die letzte(n) Ziffern neu zu berechnen, nachdem hochgezählt wurde, aber es soll ja später für Werte vom ADC sein, die sich willkürlich ändern. Edgar
edgar 339 schrieb: > Noch was verbesserbar? :D zb
1 | |
2 | |
3 | |
dir ist klar, dass du dir damit den Rest vom Port B unbenutzbar machst?
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
Die Aufsplittung eines uint16_t in die einzelnen Ziffern (und das
bestimmen des dafür anzuzeigenden Musters) hätte sich aber wirklich eine
eigene Funktion verdient. Sowas wird man ja schliesslich öfter brauchen.
> Werte vom ADC sein, die sich willkürlich ändern.
Eben. Du willst Werte vom ADC ausgeben. Und dafür hättest du gerne eine
Funktion
void disp_number( uint16_t value )
{
...
}
welche sich um das 'schmutzige' Geschäft kümmert, alles für die
Multiplexroutine herzurichten, so dass diese dann auch tatsächlich diese
Zahl anzeigt.
Lass diese Umkodierung von der numerischen Ziffer in das von der Anzeige
anzuzeigende Muster1 | |
auch gleich die Funktion disp_number machen. Zum einen muss das nicht ständig in jedem Interrupt gemacht werden, zum anderen soll sich die Multiplex-Funktion gar nicht darum kümmern, was das was du anzeigst, eigentlich darstellen soll. Wenn du in deinem Programm entscheidest, dass die 7-Seg Anzeige in allen 4 Stellen lauter '-' anzeigen soll, dann sollte das der Multiplex-Funktion aber sowas von egal sein. Dein Programm schreibt die entsprechenden Musterbytes in das Array und der Multiplex-Code in der ISR zeigt sie an.
Ok, Danke! PortB brauche ich nicht, PortB |= 1 << [0/1/was auch immer] wäre natürlich besser. Eine Funktion ist bestimmt sinnvoller, als den Code in andere Projekte zu kopieren... Edgar
edgar 339 schrieb: > Ok, Danke! > > PortB brauche ich nicht, :-) kennst du die Kategorie: Berühmte letzte Worte? > PortB |= 1 << [0/1/was auch immer] wäre > natürlich besser. Nicht nur "wäre". Es ist in Summe auch einfacher und universeller. Solche Basis-Sachen richtet man sich einmalig ordentlich her, so dass man sie die nächsten 10 Jahre in x Projekten benutzen kann, ohne dauernd das Rad neu erfinden zu müssen.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
Gast
#3146423
Zu Deiner T0-ISR: Lösche bitte ganz zu Anfang alle Segmente und den zuletzt aktiven Digit-Treiber (hatte Karl-Heinz ja schon gezeigt). Danach mach mit i++; .... weiter. Dadurch erhälst Du automatisch eine Totzeit zwischen den Digits. Als nächstes könntest Du probieren, die Vornullen zu unterdrücken, dass anstatt "0000" nun " 0", oder statt "0123" dann " 123" angezeigt wird. Wenn Du ein bißchen mutiger bist, setze den CPU-Takt ruhig auf 8MHz :-)
Gut, dann habe ich ja was, worüber ich nachdenklen kann, ich muss mich aber jetzt erst mal um die Schule kümmern... Naja, hat ja alles seinen Sinn. Edgar
So, langes Wochenende :3 So, ich habe die Zeit sinnvoll genutzt und den Code umgeschrieben. Die Vornullen werden unterdrückt, indem erst abgefragt wird, ob ganz links eine 0 steht. Falls ja wird dieses Digit mit Ziffer[10] belegt, was nichts anzeigt. Nun wird abgefragt ob die zweite Stelle eine 0 ist. Wenn ja, dann wird nachgesehen, ob die vorherige Stelle unterdrückt wurde, um nicht Nullen in der Zahl zu unterdrücken(soll ja vorkommen). Das geht dann immer so weiter. Ich habe auch schon überlegt, das in einer for-schleife zu machen, jedoch gab es da große Komplikationen und ich wollte, bevor ich weiter überlege erst einmal wissen, ob der Ansatz richtig ist. Des weiteren wird "EEEE" ausgegeben, wenn die Zahl höher ist als 9999. Das wird am Anfang der Berechnungen durchgeführt. Fragen die ich noch direkt habe: 1. Ist die Schleife notwendig, oder gibt es einen Weg, gleich mehreren Stellen eines Arrays den gleichen Wert zuzuweisen(Ziffer[0,1,2,3] mag der Compiler nicht)? 2. Wie ist die Null-Unterdrückung zu verbessern(Schleife?)? Ich danke nochmals! Edgar
Ähm, ich habe gerade bemerkt, dass nicht EEEE ausgegeben wird, sondern er wieder neu anfängt zu zählen. Zumindest beim erstem mal, bei den nächsten flackert er wieder, weil er über das Array hinausschießt. Das kann ich mir irgendwie nicht erklären... Naja, ich werde noch mal nachsehen. Edgar
> uint8_t Ausgabe_Ziffern[4] ; > for (i=0; i<5; i++) *** > { > Ausgabe_Ziffern[i] = Ziffer[11]; > } Ein Array der Länge 4 hat die Indizes 0, 1, 2, 3 das sind 4 Stück ... zähl nach. Und der höchste gültige Index ist 3. Also 1 weniger als die Arraylänge. eine Schleife > for (i=0; i<5; i++) durchläuft den Schleifenkörper mit Werten in i von 0, 1, 2, 3, 4 nach der nächsten Erhöhung von i wäre i dann 5 und für 5 gilt dann i < 5 nicht mehr und die Schleife bricht ab. Aber: Innerhalb der Schleife greifst du daher auf Ausgabe_Ziffern[0], Ausgabe_Ziffern[1], Ausgabe_Ziffern[2], Ausgabe_Ziffern[3] und Ausgabe_Ziffern[4] zu. Nur: Ein Ausgabe_Ziffern[4] existiert nicht! Der höchste zulässige Index war 3!
Ich sollte auch zurück schreiben, wenn ich das Problem gelöst habe und nich erst Tage warten... Das Problem liegt auf der Hand: er stellt es schön auf "EEEE" ein ... um danach wie üblich alles für die Ziffern zu berechnen und Mist auszugeben... Deshalb habe den Rest nach der for-Schleife in Klammern eines else gesteckt. Edgar
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.