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
structsegmap{
2
volatileuint8_t*port;
3
uint8_tbitmask;
4
};
5
6
staticconststructsegmapdigits[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.
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_tdigit,uint8_tsegments)
3
{
4
uint8_ti,mask;
5
structsegmap*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.
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_ta=(uint8_t)&LCCDR5;
Zum Derferenzieren dann umgekehrt:
1
volatileuint8_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.
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.