Moin zusammen, ich habe eine Schaltung entwickelt, in der mein Atmega169P ein LCD-Glas (3Backplanes und 24 Segmente,8stellige 7-Segment-Anzeige) ansteuern soll. Nun stehe ich vor dem Problem einen C-Code zu erstellen, in der die Zahlen von 1-9 (das reicht mir) angezeigt werden sollen. Ich habe bereits bei den AVR-Notes geschaut und auch was zu dem Thema gefunden, jedoch hat Atmel ein eigens dafür gebautes LCD verwendet. Teile kann ich aber verwenden Im Anhang findet ihr mein LCD Bit-Mapping. Zur Programmierung habe ich mir auch schon folgendes überlegt. Die Zahlen stehen in einem Table, hinter jedem Zeichen steht ein Byte (7Bit für 7Segmente) - genauer gesagt, der Bit-Code für die das LCD-Glas. Bis dahin kein Problem. Nun schaue ich an welcher Stelle ich die Zahl positionieren muss. Wenn ihr jetzt auf mein LCD Bit-Mapping schaut, so erkennt man, dass man eigentlich nur bei Digit One anfangen muss und dann einfach immer 3Bit pro weiterem Digit weiter geht. Bis dahin auch kein Problem mit der Programmierung. Wenn man sich aber Digit Number Three anschaut, so würde er von 1-C (5.Bit LCDDR2-Register) beispielsweise 2*3Bit, also auf 3-C (7.Bit LCDDR1) springen müssen. Wie kann ich dem Controller sagen, dass wenn er aus dem momentanen Register "zählt" einfach ein Register weiter runtergeht (also bspw. von LCDDR2 auf LCDDR1) und dann in dem Register weiterzählt, bis er die Stelle gefunden hat? Geht das irgendwie über die Speicheradressen, dass er die dekrementiert? Ich bin für jeden Tipp oder auch Code-Schnipsel zu haben. PS: Die LCDDR-Register des Controller findet ihr hier auf Seite 250: <http://www.atmel.com/dyn/resources/prod_documents/doc8018.pdf> und die Register Summary mit den Speicheradressen auf Seite 371 Vielen Dank im Vorraus :-). Marcel
Ich würde eine zweite Tabelle aufbauen, und darüber die Zugriffe verteilen. So ungefähr:
1 | struct segmap { |
2 | volatile uint8_t *port; |
3 | uint8_t bitmask; |
4 | };
|
5 | |
6 | static const struct segmap digits[9][5] = { |
7 | { /* digit 1 */ |
8 | { &LCDDR7, 0x40 }, /* segment 1A */ |
9 | { &LCDDR7, 0x20 }, /* 1B */ |
10 | { &LCDDR2, 0x20 }, /* 1C */ |
11 | ...
|
12 | { &LCDDR12, 0x20 }, /* 1DP */ |
13 | { &LCDDR12, 0x80 } /* 1AN */ |
14 | },
|
15 | ...
|
16 | { /* digit 5 */ |
17 | ...
|
18 | { &LCDDR11, 0x02 }, /* 5DP */ |
19 | { &LCDDR11, 0x08 } /* 5AN */ |
20 | }
|
21 | };
|
Das hat zwar etwas Redundanz, weil sich manche Muster ja wiederholen, aber so viel Platz ist es letztlich auch nicht, und der Zugriff geht einigermaßen schnell. Wenn du willst, kannst du das natürlich noch in den progmem packen, aber im SRAM sind mit dem GCC die Zugriffe einfacher aufzuschreiben.
Wie hast du die Wechselspg für das LCD realisiert? Klaus.
Klaus2 schrieb:
> Wie hast du die Wechselspg für das LCD realisiert?
Das macht doch der ATmegaXX9 selbst.
Jörg Wunsch schrieb: > Ich würde eine zweite Tabelle aufbauen, und darüber die Zugriffe verteilen. Moin, das kann man naütlich auch machen, denn Speicherplatz hat der Atmega169 genug. Ich versuche deinen Vorschlag nochmal in Worte zufassen, damit ich schaue ob ich's verstanden habe: Wir definieren ein 9x8 Array, da ein Zeichen 9Segmente hat und es insgesamt 8Zeichen gibt. Jeder Eintrag in diesem Array ist eine Struktur, die den anzusteuernden Port und die Bitmask besitzt. Das würde einen Speicherplatz bedarf von 2Byte (Struktur)*9(Segmente) * 8(Zeichen) = 144Byte mitsich ziehen. Im Source-Code müsste ich dann doch nur noch den LCD-Code Bit für Bit untersuchen und dann jeweils in dem Array die Stelle raussuchen und dort ggf. das Bit setzen. War das so deine Intention Jörg? Vielen Dank! Grüße Marcel PS: Klaus2 schrieb: > Wie hast du die Wechselspg für das LCD realisiert? Das macht der Controller wie Jörg schon sagte selbstständig. Hierfür setzt du in gewissen Registern deine Segment und Backplanes-Anzahl.
Marcel schrieb: > Wir definieren ein 9x8 Array, da ein Zeichen 9Segmente hat und es > insgesamt 8Zeichen gibt. OK, ich war von 5 Stellen ausgegangen. > Jeder Eintrag in diesem Array ist eine > Struktur, die den anzusteuernden Port und die Bitmask besitzt. > Das würde einen Speicherplatz bedarf von 2Byte (Struktur)*9(Segmente) * > 8(Zeichen) = 144Byte mitsich ziehen. Jede Struktur ist 3 Byte groß, da ein Zeiger ja 16 bit hat. Im Prinzip könnte man den Zeiger in eine 8-bit-Zahl casten und von da wieder zurück, da ja alle IO-Ports beim ATmega169 unterhalb 0x100 liegen. > Im Source-Code müsste ich dann doch nur noch den LCD-Code Bit für Bit > untersuchen und dann jeweils in dem Array die Stelle raussuchen und dort > ggf. das Bit setzen. Ja, wenn "LCD-Code" die Segmentbelegung meint:
1 | void
|
2 | lcd_out(uint8_t digit, uint8_t segments) |
3 | {
|
4 | uint8_t i, mask; |
5 | struct segmap *sm; |
6 | |
7 | for (i = 0, mask = 1; i < 8; i++, mask <<= 1) { |
8 | sm = &digits[i][digit]; |
9 | if (segments & mask) { |
10 | /* enable this segment */
|
11 | *(sm->port) |= sm->bitmask; |
12 | } else { |
13 | /* disable this segment */
|
14 | *(sm->port) &= ~sm->bitmask; |
15 | }
|
16 | }
|
17 | }
|
Wenn du die Tabelle im Flash unterbringst, dann müsstest du vorab eine Kopie für die aktuelle Stelle im SRAM anfertigen. Wenn du den IO-Port als 8-bit-Adresse ablegst, müsstest du noch per Typecast einen gültigen Zeiger draus machen. Was man mit dem AN-Bit anstellen muss, weiß ich gerade nicht, das habe ich hier mal ignoriert. Bei der Reihenfolge der Indizes bei zweidimensionalen Felder bin ich mir immer nicht so ganz sicher, ausprobieren oder nachlesen. ;-)
Jörg Wunsch schrieb: > Bei der Reihenfolge der Indizes bei zweidimensionalen Felder bin ich > mir immer nicht so ganz sicher, ausprobieren oder nachlesen. ;-) Du meinst wegen der Initialisierung? Merksatz: Der letzte Index läuft am schnellsten int a[2][3] = { 0, 1, 2, 3, 4, 5 }; -> a[0][0] == 0 a[0][1] == 1 a[0][2] == 2 a[1][0] == 3 a[1][1] == 4 a[1][2] == 5
Karl heinz Buchegger schrieb: > Du meinst wegen der Initialisierung? Ja. > Merksatz: Der letzte Index läuft am schnellsten OK, werde ich versuchen mir zu merken. ;-) > int a[2][3] = { 0, 1, 2, 3, 4, 5 }; Ich bin Fan der expliziten Klammerung: int a[2][3] = { { 0, 1, 2 }, { 3, 4, 5 } }; Dann würde der Compiler sicher auch meckern, wenn meine Idee nicht zu seiner passt. ;-)
Jörg Wunsch schrieb: > >> int a[2][3] = { 0, 1, 2, 3, 4, 5 }; > > Ich bin Fan der expliziten Klammerung: Ist schon klar. Mach ich auch Ich wollte nur explizit Verwirrung schaffen um die Zuordnung zu den Elementen zu zeigen, wie sie aufgeteilt werden
Jörg Wunsch schrieb: > Wenn du die Tabelle im Flash unterbringst, dann müsstest du vorab eine > Kopie für die aktuelle Stelle im SRAM anfertigen. Wenn du den IO-Port > als 8-bit-Adresse ablegst, müsstest du noch per Typecast einen > gültigen Zeiger draus machen. Was genau meinst du damit? - Wieso müsste ich dann die Stelle in dem SMRAM schieben? Da konnte ich dir jetzt gerade nicht folgen Jörg Wunsch schrieb: > Was man mit dem AN-Bit anstellen muss, weiß ich gerade nicht, das habe > ich hier mal ignoriert. Das AN-Bit ist ein Sonderzeichen, ein >, nur mit der Spitze nach unten. :) Grüße Marcel
Marcel schrieb: > Was genau meinst du damit? - Wieso müsste ich dann die Stelle in dem > SMRAM schieben? Da konnte ich dir jetzt gerade nicht folgen Ich würde mir dann die 9 structs für die aktuelle Stelle innerhalb der Funktion aus dem ROM in den Stack kopieren, um anschließend darauf zuzugreifen. Es werden ja letztlich ohnehin alle 9 benötigt, und die kann man alle mit einem einzigen memcpy_P reinlutschen. Ist einfacher, als jedes Byte oder jeden Zeiger einzeln zu lesen.
Supi, hab vielen Dank Jörg. Ich setz mich dann mal drann. Bye Marcel
Kann man dein LCD irgendwo kaufen, oder ist das eine Sonderanfertigung?
Hi, nein das bekommste bei ELPRO. LCD Standard Panel DE 133 - (Series Display Elektronik Gmbh) Kontrast ist echt super und der Stromverbrauch klein. :)
Moin zusammen, ich habe nochmal eine Frage zur Programmierung. Wenn ich >uint8_t a = &LCCDR5; beispielsweise schreibe, so gibt er mir eine Warnung von wegen: initialization makes integer from pointer without a cast Gebe ich jedoch die Speicheradresse per Hand an: >uint8_t a = 0xF1; so findet keine Warnung statt - warum? Wenn ich hingegen: >uint8_t *a = &LCCDR5; schreibe, so kommt, warning: initialization discards qualifiers from pointer target type Wenn ich dann wieder >uint8_t *a = 0xF1; schreibe, so kommt warning: initialization makes pointer from integer without a cast warum das? Meine zweite Frage ist: Wenn ich den Array[8][9] in den Flash verschiebe und anschließend ein Element des Arrays holen möchte, welche Fkt. muss ich da aus der <avr/pgmspace.h> nehmen? Ein Element besteht dabei aus einer Struktur von uint8_t, sowie einem uint8_t pointer. Merci - Marcel
Marcel schrieb: > Wenn ich >>uint8_t a = &LCCDR5; > > beispielsweise schreibe, so gibt er mir eine Warnung von wegen: > > initialization makes integer from pointer without a cast Klar. &LCCDR5 ist ein Zeiger, a aber nur eine (kleine) Ganzzahl. Da du aber weißt, dass der Zeiger garantiert kleiner als 0x100 ist und daher in einen uint8_t passt, kannst du schreiben:
1 | uint8_t a = (uint8_t)&LCCDR5; |
Zum Derferenzieren dann umgekehrt:
1 | volatile uint8_t *lcddrp = (uint8_t *)a; |
> Gebe ich jedoch die Speicheradresse per Hand an: >>uint8_t a = 0xF1; > so findet keine Warnung statt - warum? Weil auf der rechten Seite eine Ganzzahl steht, kein Zeiger. > Wenn ich hingegen: > >>uint8_t *a = &LCCDR5; > schreibe, so kommt, > warning: initialization discards qualifiers from pointer target type "type qualifiers" im C-Standard sind die (reservierten) Worte "const", "volatile" und "restricted". Einen dieser drei hast du also mit deiner Zuweisung weggeworfen. ;-) Damit du nicht erst nachgucken musst im Headerfile: es war "volatile". Zeiger auf IO-Register sind alle als volatile gekennzeichnet, damit der Compiler die entsprechenden Zugriffe nicht versuchen darf zu optimieren. > Wenn ich dann wieder >>uint8_t *a = 0xF1; > > schreibe, so kommt > warning: initialization makes pointer from integer without a cast > > warum das? Weil du den Typecast weggelassen hast. ;-) Wenn du aus einer Zahl einen Zeiger machen willst, dann musst du schon schreiben:
1 | uint8_t *a = (uint8_t *)0xF1; |
Aber Achtung: das "volatile" fehlt hier, der Compiler darf dann die Zugriffe nach Gutdünken wegoptimieren. > Wenn ich den Array[8][9] in den Flash verschiebe und anschließend ein > Element des Arrays holen möchte, welche Fkt. muss ich da aus der > <avr/pgmspace.h> nehmen? memcpy_P > Ein Element besteht dabei aus einer Struktur von uint8_t, sowie einem > uint8_t pointer. Alternative wäre ein pgm_read_byte und ein pgm_read_word separat. Da memcpy_P ein wenig Overhead braucht, der für nur drei Byte eigentlich übertrieben ist, würde ich dann an deiner Stelle stattdessen gleich die ganzen 9 Strukturen für eine Anzeigestelle in einem Rutsch mit memcpy_P kopieren. Aber bring deine Ansteuerung erstmal ohne progmem zum Laufen, im Moment wirst du sicher genug RAM übrig haben. Das macht die Sache einfacher, und da kannst dann von einem "known to be good"-Stand aus die progmem-Sache angehen.
Jörg Wunsch, du bist der Größte :-) - kannst echt super erklären! Vielen Dank, Marcel
Haste vielleicht ein gutes Buch für mich, denn C-Programieren kann ich eigentlich, nur bin ich in der AVR-Programmierung noch frisch. Natürlich gibts da noch das AVR-GCC Tutorial. Das hab ich auch schon durch nur finde ich das nicht wirklich tiefgründig. :-( Danke Grüße Marcel
Marcel schrieb:
> Haste vielleicht ein gutes Buch für mich
Tut mir leid, damit kann ich nicht dienen.
Ich habe C auf Unixen gelernt, und bin dann erst danach in die
Controllerprogrammierung eingestiegen. Damit musste ich nicht beides
auf einmal lernen.
Stell deine Fragen einfach weiter hier, für neue Fragen sinnvollerweise
auch in einem neuen Thread.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.