Forum: Mikrocontroller und Digitale Elektronik put Pixel für dotmatrix Display


von Pixel (Gast)


Lesenswert?

Hallo,

ich habe hier den folgenden Display Controller für ein 240 x 128 Pixel 
Display (UC1698):

http://wenku.baidu.com/view/cbf8c18102d276a200292ece.html

Der RGB Controller scheint mir für ein monochromes Display etwas 
exotisch zu sein :)

Ich befinde mich RRRR GGGG BBBB, 4K Color Mode, d.h. jeder Pixel wird 
mit 4 Bit angesteuert :)

Der Controller erwartet aber immer mindestens 12 Bit, d.h. ich sollte 
immer Minimum 3 Byte senden, um auf eine gerade Pixel Anzahl zu kommen.

Ich würde jetzt gerne eine Funktion schreiben, die gezielt einen Pixel 
schreibt und keinen anderen löscht. Zeilen und Spalten kann ich gezielt 
ansteuern. Ich habe es im Folgenden mal mit 2 Byte versucht, was den 
Nachteil hat, dass immer auch Pixel wieder gelöscht werden !!!!

Wie kann man das besser machen?
1
// Zeile u. Spalte
2
void put_pixel(unsigned char pos, unsigned char row, unsigned char column)
3
{
4
    set_row(row);
5
    set_column(column);
6
7
    unsigned char Data1, Data2;
8
    switch(pos)
9
    {
10
        case 0: // setzt ersten Pixel (4Bit) und löscht 2-4
11
        Data1 = 0xF0;
12
        Data2 = 0x00;
13
        break;
14
15
        case 1:
16
        Data1 = 0x0F;
17
        Data2 = 0x00;
18
        break;
19
20
        case 2:
21
        Data1 = 0x00;
22
        Data2 = 0xF0;
23
        break;
24
    }
25
    Write_Data(Data1);
26
    Write_Data(Data2);
27
}

von Karl H. (kbuchegg)


Lesenswert?

Pixel schrieb:

> Ich würde jetzt gerne eine Funktion schreiben, die gezielt einen Pixel
> schreibt und keinen anderen löscht. Zeilen und Spalten kann ich gezielt
> ansteuern. Ich habe es im Folgenden mal mit 2 Byte versucht, was den
> Nachteil hat, dass immer auch Pixel wieder gelöscht werden !!!!
>
> Wie kann man das besser machen?

Dann musst du dir eben aus dem Displayspeicher zuerst das Byte holen, in 
welchem die 4 Bit erhalten bleiben müssen. welches das ist, hängt 
wiederrum von der auszugebenden Position ab, die du ja in deinem switch 
schon berücksichtigt hast.
die 4 Bit extrahierst du dir und ergänzt damit die 4 Bit in deinen neu 
zu schreibenden 2 Bytes. Im Endeffekt dürfen sich im Speicher nur die 12 
zum relevanten Pixel gehörenden Bits geändert haben und keine anderen.

von Pixel (Gast)


Lesenswert?

Ich steuere das Display via SPI an und es ist nur eine Data Input 
Leitung und keine SDO Leitung vorhanden. Ich weiss also nicht ganz wie 
ich den Displayspeicher auslesen soll, obwohl mir die Idee schon klar 
ist.
Überprüfe 16Bit und merke wie die nicht zu setzenden 12 Bit belegt 
waren. Schreibe diese 12 Bit wieder und manipuliere nur die neu zu 
setzenden 4 Bit?

Eine andere Idee wäre immer 3 Byte, also 24 Bit oder 6 Pixel zu 
schreiben!
Etwas unschön, aber das müsste leicht zu realisieren sein.

von Pixel (Gast)


Lesenswert?

> Eine andere Idee wäre immer 3 Byte, also 24 Bit oder 6 Pixel zu
> schreiben!
> Etwas unschön, aber das müsste leicht zu realisieren sein.

Würde dann so aussehen:
1
// Zeile u. Spalte
2
void put_pixel(unsigned char sechspixel, unsigned char row, unsigned char column)
3
{
4
    set_row(row);
5
    set_column(column);
6
    0b00010101;
7
    unsigned char Data1, Data2, Data3;
8
    Data1 = (sechspixel & 0b00110000) >> 4;
9
    Data2 = (sechspixel & 0b00001100) >> 2;
10
    Data3 = (sechspixel & 0b00000011);
11
    switch(Data1)
12
    {
13
        case 0:
14
        Data1 = 0x00;
15
        break;
16
17
        case 1:
18
        Data1 = 0x0F;
19
        break;
20
21
        case 2:
22
        Data1 = 0xF0;
23
        break;
24
25
        case 3:
26
        Data1 = 0xFF;
27
        break;
28
    }
29
30
    switch(Data2)
31
    {
32
        case 0:
33
        Data2 = 0x00;
34
        break;
35
36
        case 1:
37
        Data2 = 0x0F;
38
        break;
39
40
        case 2:
41
        Data2 = 0xF0;
42
        break;
43
44
        case 3:
45
        Data2 = 0xFF;
46
        break;
47
    }
48
49
    switch(Data3)
50
    {
51
        case 0:
52
        Data3 = 0x00;
53
        break;
54
55
        case 1:
56
        Data3 = 0x0F;
57
        break;
58
59
        case 2:
60
        Data3 = 0xF0;
61
        break;
62
63
        case 3:
64
        Data3 = 0xFF;
65
        break;
66
    }
67
    Write_Data(Data1);
68
    Write_Data(Data2);
69
    Write_Data(Data3);
70
}
71
72
put_pixel(0b00010101,0,0); // zeichne 2., 4. und 6. Pixel

Hat natürlich den bachteil, dass ich IMMER 6 Pixel schreiben muss!

von Karl H. (kbuchegg)


Lesenswert?

Pixel schrieb:
> Ich steuere das Display via SPI an und es ist nur eine Data Input
> Leitung und keine SDO Leitung vorhanden. Ich weiss also nicht ganz wie
> ich den Displayspeicher auslesen soll, obwohl mir die Idee schon klar
> ist.
> Überprüfe 16Bit und merke wie die nicht zu setzenden 12 Bit belegt
> waren. Schreibe diese 12 Bit wieder und manipuliere nur die neu zu
> setzenden 4 Bit?
>
> Eine andere Idee wäre immer 3 Byte, also 24 Bit oder 6 Pixel zu
> schreiben!
> Etwas unschön, aber das müsste leicht zu realisieren sein.

Das hilft dir aber beim Problem nicht weiter.
Denn von einer Pixel Setz Routine geh ich schon davon aus, dass ich 
damit beliebige Pixel setzen kann. Und wenn ich damit ein blaues Pixel 
in eine Fläche setzen will, von der ich nichts weiss (weil es zb eine 
Bitmap mit einem Bild ist), dann muss sich die Pixel-Setz Routine darum 
kümmern, dass die Pixel rundum sich nicht verändern.

Wenn du vom Controller den Speicher nicht auslesen kannst, dann musst du 
dir eben im Programm ein Duplikat des Speichers zurecht legen. So 
schmerzhaft das auch ist.

Oder aber du lebst mit der Krücke, dass du eben nicht wahlfrei 
irgendwelche Pixel verändern kannst.
Kann man machen. Wird aber schnell zu einem 'Pain in the ass'.

von Stephan (Gast)


Lesenswert?

Hi,
wenn du solche Manipulationen machen möchtest, empfiehlt es sich den 
Bildschirmspeicher min 1x als Abbild im eigenen Speicher zu haben.

Dann werden solche SetPixel Funktionen auf das Abbild angewandt und ans 
Display neu übertragen.

von Pixel (Gast)


Lesenswert?

OK danke für die Antworten.
Brauche nochmal einen kleinen Anstoss.

Habe es jetzt ein Array angelegt mit 3840 Byte (280 x 128 Pixel -> 3840 
Bytes).

Dieses Update ich regelmäßig. Ich bekomme es jetzt hin, dass ich jeden 
Pixel darstellen kann.
1
void put_pixel(unsigned char row, unsigned char column) // 0,1
2
{
3
    set_row(row);
4
    set_column(column);
5
    shadow_RAM[column + row * 240] = shadow_RAM[column + row * 240] | 
6
}

Die Put Pixel Funktion ist jetzt natürlich nich falsch. Ich möchte 
dieser Zeilen und Spalten übergeben und dann automatisch den zugehörigen 
Pixel übergeben.
Bis jetzt kann ich ja nur den 1. 9. Pixel usw. setzen.

von Karl H. (kbuchegg)


Lesenswert?

Pixel schrieb:

> Die Put Pixel Funktion ist jetzt natürlich nich falsch. Ich möchte
> dieser Zeilen und Spalten übergeben und dann automatisch den zugehörigen
> Pixel übergeben.

Und wo ist da jetzt die Frage?

> Bis jetzt kann ich ja nur den 1. 9. Pixel usw. setzen.

Wenn du mit dieser PuPixel Funktion ein Pixel neu setzt, dann kennst du 
damit ja aber auch implizit alle Bytes (die ergeben sich aus der Zeile 
und der Spalte des neu zu setzenden Pixels), die sich ändern. Deren 
Information suchst du dir aus deinem Array zusammen und überträgst sie 
so zum Controller, wie der das haben will.
Wo ist da jetzt das Problem, ausser dass es Programmierarbeit bedeutet?

von Karl H. (kbuchegg)


Lesenswert?

Ich denke, du solltest dir mal ein paar Zeichnungen machen.
Dann wird vieles klarer.
IMHO ist das einer der Grundfehler in der Entwicklung, auf Papier und 
Bleistift zu verzichten. Man verliert dann leicht den Überblick.

von W.S. (Gast)


Lesenswert?

Pixel schrieb:
> Die Put Pixel Funktion ist jetzt natürlich nich falsch. Ich möchte
> dieser Zeilen und Spalten übergeben und dann automatisch den zugehörigen
> Pixel übergeben.

In Grunde ist deine gesamte Herangehensweise falsch. Du hast dir ein 
Display zugelegt und dir dann gedacht, "jetzt mach ich mir zu allererst 
ne PutPixel-Routine, wo ich später drauf aufbauen will." Also geradeaus 
mit dem Kopf durch die Wand.

Mach es anders, und zwar ganz anders. Trenne die Grafik vom konkreten 
Display.

Hä?
denkst du jetzt vielleicht, aber ich meine es ernst:
Richte dir einen Bildspeicher im RAM ein, den du so organisierst, daß es 
dir am besten paßt. Also entweder wortweise unter Freilassen von 4 Bit 
oder eben kompakt unter Nutzung aller Bits. Dann schreib dir ein GDI 
dazu, was nur auf den Bildspeicher arbeitet. Das kanst du dir dann auf 
einem PutPixel für den Bildspeicher aufbauen.

Getrennt davon sind dann alle Routinen zum Einrichten des konkreten 
Displays und zum Blocktransfer vom Bildspeicher zum Display. Ich mache 
das so, daß ich bei jedem Schreibvorgang in den Bildspeicher ein 
Kennzeichen setze, was später vom Timertick abgefragt wird, der wiederum 
einen "event" generiert, der dann zum Blocktransfer vom Bildspeicher zum 
Display führt. Dadurch kann man sorgenlos seine Grafik aufbauen und der 
Transfer wird nicht bei jedem Furz, sondern nur dann durchgeführt, wenn 
die ganze Zeichnerei im wesntlichen über die Bühne ist.

Und sowas sieht mir viel zu umständlich und rechenintensiv aus:
    set_row(row);
    set_column(column);
    shadow_RAM[column + row * 240] = shadow_RAM[column + row * 240]

Aus dem Handgelenk würde ich es etwa so machen:
    L = (X+Bildbreite*Y)*12;
    Index = L >> 3;
    BitPos = L & 7;
    wenn man mit einem Array of byte arbeiten will.

W.S.

von Pixel (Gast)


Lesenswert?

Karl H. schrieb:
> Ich denke, du solltest dir mal ein paar Zeichnungen machen.
> Dann wird vieles klarer.
> IMHO ist das einer der Grundfehler in der Entwicklung, auf Papier und
> Bleistift zu verzichten. Man verliert dann leicht den Überblick.

Danke für den Tipp mit der Zeichnung. Ich habe es jetzt wie folgt 
realisiert. Es geht vermutlich weit aus eleganter :)
1
// 0 ... 239 -> spalte
2
// 0 ... 127 -> zeile
3
void put_pixel(unsigned char zeile, unsigned char spalte) // 1,0
4
{
5
6
    unsigned char zahl = fmod(spalte,8);
7
    unsigned int hilf = spalte/8;
8
9
    switch(zahl)
10
    {
11
12
        case 0:
13
        shadow_RAM[hilf + zeile * 240/8] = shadow_RAM[hilf + spalte * 240/8] | 0b10000000;
14
        break;
15
16
        case 1:
17
        shadow_RAM[hilf + zeile * 240/8] = shadow_RAM[hilf + spalte * 240/8] | 0b01000000;
18
        break;
19
20
        case 2:
21
        shadow_RAM[hilf + zeile * 240/8] = shadow_RAM[hilf + spalte * 240/8] | 0b00100000;
22
        break;
23
24
        case 3:
25
        shadow_RAM[hilf + zeile * 240/8] = shadow_RAM[hilf + spalte * 240/8] | 0b00010000;
26
        break;
27
28
        case 4:
29
        shadow_RAM[hilf + zeile * 240/8] = shadow_RAM[hilf+ spalte * 240/8] | 0b00001000;
30
        break;
31
32
        case 5:
33
        shadow_RAM[hilf + zeile * 240/8] = shadow_RAM[hilf + spalte * 240/8] | 0b00000100;
34
        break;
35
36
        case 6:
37
        shadow_RAM[hilf + zeile * 240/8] = shadow_RAM[hilf + spalte * 240/8] | 0b00000010;
38
        break;
39
40
        case 7:
41
        shadow_RAM[hilf + zeile * 240/8] = shadow_RAM[hilf + spalte * 240/8] | 0b00000001;
42
        break;
43
44
    }
45
    
46
}

Danke auch an W.S. für seine Anmerkungen. Gerade den Code aus dem 
"Handgelenk" würde ich mir mal zu Gemüte

von Karl H. (kbuchegg)


Lesenswert?

Pixel schrieb:

> realisiert. Es geht vermutlich weit aus eleganter :)

Allerdings
1
uint8_t bitMask[] = { 0b10000000, 0b01000000, 0b00100000, 0b00010000,
2
                      0b00001000, 0b00000100, 0b00000010, 0b00000001 };
3
4
// 0 ... 239 -> spalte
5
// 0 ... 127 -> zeile
6
void put_pixel(unsigned char zeile, unsigned char spalte) // 1,0
7
{
8
    unsigned char zahl = fmod(spalte,8);
9
    unsigned int hilf = spalte/8;
10
 
11
    shadow_RAM[hilf + zeile * 240/8] |= bitMask[zahl];
12
}

Ich kenn deinen Prozessor nicht. Wenn bei dem ein variabler Shift nichts 
extra kostet, dann natürlich so (und ohne das extra Array, welches 
Bitnummern in Bitmasken umsetzt):
1
    shadow_RAM[hilf + zeile * 240/8] |= ( 1 << ( 7 - zahl ) );

von Pixel (Gast)


Lesenswert?

Vielen Dank schon einmal für die Hilfen.

Ich habe noch eine weitere Frage. Ich habe hier einen 12x16 Pixel Font 
der folgenden Form (hier für 'A'):
1
{0x60,0x00,0x60,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0x98,0x01,0x98,0x01,0x98,0x01,0x0C,0x03,0xFC,0x03,0xFC,0x03,0x06,0x06,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00},  // 0x41 -> 'A'

Es sind 32 Byte, also vermute ich stark, dass es im Prinzip ein 16 x 16 
Font ist.
Weiss jemand, wie dies in der Regel codiert ist?

Ich habe es mit jeweils 2 Byte horizontal versucht und dann in neue 
Zeile usw.

Das Passt noch nicht so richtig.
Ebenfalls die ersten 16 Byte von oben nach unten und dann die letzten 16 
Byte wieder von oben nachunten passt nicht.


Dateiname des Fonts ist 12x16 horizontal MSB_1.

8x8 klappt wunderbar und ist eigentlich selbsterklärend.

Nur bei 12 x 16 bin ich nicht sicher in welcher Reihenfolge die Bits 
geschrieben werden (im Normalfall).
Gibt es irgend nen Standard wie sowas normalerweise codiert ist?

von Karl H. (kbuchegg)


Lesenswert?

Pixel schrieb:
> Vielen Dank schon einmal für die Hilfen.
>
> Ich habe noch eine weitere Frage. Ich habe hier einen 12x16 Pixel Font
> der folgenden Form (hier für 'A'):
>
>
1
> {0x60,0x00,0x60,0x00,0xF0,0x00,0xF0,0x00,0xF0,0x00,0x98,0x01,0x98,0x01,0x98,0x01,0x0C,0x03,0xFC,0x03,0xFC,0x03,0x06,0x06,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00}, 
2
> // 0x41 -> 'A'
3
>
>
> Es sind 32 Byte, also vermute ich stark, dass es im Prinzip ein 16 x 16
> Font ist.
> Weiss jemand, wie dies in der Regel codiert ist?

In der Regel ist 1 Bit mit 1 Pixel gleichzusetzen.
Wenn man nichts weiter weiss, dann nimmt man sich einfach mal die Bytes 
her, schlüsselt die in Bits auf und malt einfach mal auf einem Papier 
(karriertes Papier ist da ideal) für die 1 Bits ein Kreuzerl und für ein 
0 Bit nichts.

Mit der Methode des 'scharfen Hinsehens' erkennt man dann meist rasend 
schnell, wie der Font aufgebaut ist.

> Gibt es irgend nen Standard wie sowas normalerweise codiert ist?

Nein.
Probieren und die Methode des "scharfen Hinsehens" führen aber bei so 
einfachen Fontbeschreibungen in kürzester Zeit zum Ziel.

von Karl H. (kbuchegg)


Lesenswert?

Karl H. schrieb:

> Nein.
> Probieren und die Methode des "scharfen Hinsehens" führen aber bei so
> einfachen Fontbeschreibungen in kürzester Zeit zum Ziel.


Mach ich das mal mit den von dir geposteten Daten, dann sehe ich
1
 7654321076543210 
2
  **                  0x60 0x00
3
  **                  0x60 0x00
4
 ****                 0xF0 0x00
5
 ****                 0xF0 0x00
6
 ****                 0xF0 0x00
7
 *  **          *     0x98 0x01
8
 *  **          *     0x98 0x01
9
 *  **          *     0x98 0x01
10
     **        **     0x0C 0x03
11
 ******        **     0xFC 0x03
12
 ******        **     0xFC 0x03
13
      **      **      0x06 0x06
14
      **      **      0x06 0x06
15
      **      **      0x06 0x06
16
                      0x00 0x00
17
                      0x00 0x00

und das sieht schon fast wie ein 'A' aus. Auffällig ist, dass das 'A' 
fast in der Mitte durchgeschnitten erscheint. Aus dieser Ansicht vermute 
ich mal ganz stark, dass
* jeweils 2 Bytes zusammengehören
* das jeweils 2 Byte grafisch links vom jeweils 1ten Byte steht

Probiere ich das mal grafisch aus, dann ergibt sich
1
 7654321076543210 
2
          **                  0x00 0x60
3
          **                  0x00 0x60
4
         ****                 0x00 0xF0
5
         ****                 0x00 0xF0
6
         ****                 0x00 0xF0
7
        **  **                0x01 0x98
8
        **  **                0x01 0x98
9
        **  **                0x01 0x98
10
       **    **               0x03 0x0C
11
       ********               0x03 0xFC
12
       ********               0x03 0xFC
13
      **      **              0x06 0x06
14
      **      **              0x06 0x06
15
      **      **              0x06 0x06
16
                              0x00 0x00
17
                              0x00 0x00

man muss kein Hellseher sein um zu sehen, dass es jetzt "passt".

Allenfalls kann es jetzt noch sein, dass die Zeichen gespiegelt sind.
Das kann man natürlich auch noch ausprobieren, indem man naiv mit der 
bis jetzt gewonnenen Vorschrift einfach mal einen nicht symetrischen 
Buchstaben zu entschlüsseln versucht (zb. ein 'B')

von Falk B. (falk)


Lesenswert?

@ Karl Heinz (kbuchegg) (Moderator)

>* das jeweils 2 Byte grafisch links vom jeweils 1ten Byte steht

Big/Little Endian.

von Pixel (Gast)


Lesenswert?

Super, vielen Dank. Dies hat mir sehr weitergeholfen. Danke dafür!

von W.S. (Gast)


Angehängte Dateien:

Lesenswert?

Pixel schrieb:
> Ich habe noch eine weitere Frage. Ich habe hier einen 12x16 Pixel Font
> der folgenden Form (hier für 'A'):

Ja. Habe Font in unbekanntem Format, weiß nicht wie gut er ist und hab 
auch kein GDI dafür... Mein Rat: schmeiß sowas weg, du ärgerst dich bloß 
drüber.

Falls du tatsächlich Fonts und dazu passendes GDI haben willst, dann 
lade dir die Lernbetty (Gebetsmühle...) herunter. Die dortigen Fonts 
sind auf unterschiedlichsten Architekturen einsetzbar (auch Bigendian) 
und du hast eine dazu passende Routine im GDI, welche dir deine Zeichen 
an frei wählbare Stellen im Display setzt. Wie das Ganze funktioniert, 
kannst du in der Quelle nachlesen - und wie so ein Font aufgebaut ist, 
siehst du im Font. Obendrein hatte ich m.W. auch noch einen kleinen 
Fontcompiler dazugepackt, der von Textdateien (in bestimmtem Format) 
derartige Font-C-Quellen produziert. Damit hast du das Werkzeug, um dir 
verhältnismäßig einfach per Editor eigene Zeichen zu kreieren. 
Prinzipiell sind bei dieser Lösung alle Fonts proportional, du kannst 
also die Zeichenbreiten der einzelnen Zeichen individuell wählen. 
Lediglich die Brutto-Zeichenhöhen (also gesamte Zeichenhöhe plus 
Durchschuß) sind innerhalb eines Fonts gleich. Und Fonts mit fester 
Breite sind nur ein Sonderfall davon.

So, und wenn du die Lernbetty nicht findest, dann guck in die angehängte 
Datei hinein. Ist ein Beispiel.

W.S.

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.