Forum: Compiler & IDEs LCD Glas Ansteuerung mit Atmega169 - Frage C-Code


von Marcel (Gast)


Angehängte Dateien:

Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Klaus2 (Gast)


Lesenswert?

Wie hast du die Wechselspg für das LCD realisiert?

Klaus.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Klaus2 schrieb:
> Wie hast du die Wechselspg für das LCD realisiert?

Das macht doch der ATmegaXX9 selbst.

von Marcel (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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. ;-)

von Karl H. (kbuchegg)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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. ;-)

von Karl H. (kbuchegg)


Lesenswert?

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

von Marcel (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Marcel (Gast)


Lesenswert?

Supi, hab vielen Dank Jörg.
Ich setz mich dann mal drann.


Bye Marcel

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Kann man dein LCD irgendwo kaufen, oder ist das eine Sonderanfertigung?

von Marcel (Gast)


Lesenswert?

Hi, nein das bekommste bei ELPRO.

LCD Standard Panel DE 133 - (Series Display Elektronik Gmbh)


Kontrast ist echt super und der Stromverbrauch klein. :)

von Marcel (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Marcel (Gast)


Lesenswert?

Jörg Wunsch,
du bist der Größte :-) - kannst echt super erklären!

Vielen Dank, Marcel

von Marcel (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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
Noch kein Account? Hier anmelden.