OLED mit I2C BitBang

von Michael Moske

Elektronik-Labor  Projekte  Mikrocontroller  TLScript  




Download: SSD1306_OLED_I2C.zip

Mit TLScript auf dem CH32V003 Mikrocontroller kann bereits die I2C-Adresse des OLED-Displays SSD1306 über das I2C-Protokoll mittels BitBang ermittelt werden. Dies wurde in einem früheren Beitrag gezeigt, siehe Link => TLScriptI2C1.html.

Jetzt folgt der nächste Schritt, nämlich auch ASCII-Zeichen auf dem OLED-Display darzustellen oder sogar Messdaten anzuzeigen. Es gibt zwar die Möglichkeit, dies auf Arduino-Basis mit einer speziellen Arduino-Bibliothek zu realisieren (funktioniert gut mit der Arduino-Bibliothek "SoftWire", Beispiel "CH32V003_SoftI2C_Scanner" in beigefügter zip-Datei). Warum aber nicht einfach dafür TLScript verwenden? Denn mit I2C-Direktkommandos, z.B. über die Pins PC1 und PC2, sollte ist dies doch auch möglich sein. Und es ist auch sinnvoll, wenn man nicht mit der oben genannten Bibliothek umgehen mag, oder wenn keine nutzbare I2C-Schnittstelle zur Verfügung steht. Nach etwas Programmierarbeit ist es mir nun gelungen, den ASCII-Zeichensatz auf dem OLED-Display z.B. im Lauftext anzuzeigen, siehe Datei => "I2C_OLED_ASCII_table.tls". Dies funktioniert zunächst einfacherweise schon mit der Original-Firmware Version 41, wobei allerdings noch die notwendige ASCII-Zeichen-Bitmap "ascii.dat" mit dem "Load"- bzw. „Open“-Befehl auf der Startseite der TestLab-IDE nachgeladen werden muss und anschließend auf der 2. Seite mit dem [DAT]-Befehl ins Ram geschoben werden muss. Alle internen Abläufe des Programms sind ausführlich im Script dokumentiert. Die Befehle für das Display werden als Direktkommandos via I2C übermittelt.

Da das Nachladen der ASCII-Bitmap für mich etwas umständlich ist, habe ich zusätzlich eine angepasste Version der Firmware erstellt (=> Firmware_CH32_41-M), basierend auf der letzten Original-Version 41. Damit kann nun wahlweise entweder mit "Call 244" der Sinus-Datensatz in das RAM geladen werden, oder neu mit "Call 247" der ASCII-Datensatz. So kann auch innerhalb eines Scripts zwischen den Datensätzen hin und her gewechselt werden und das Nachladen entfällt.

Auch ist noch eine zusätzliche Ergänzung mit "Call 248" enthalten, siehe auch => TLScriptIASCII.html. Hiermit werden alternativ zum "Print A"-Befehl nun auch ASCII-Zeichen im Serial Monitor angezeigt.

Information zur Struktur der 256-Byte ASCII-Bitmap Tabelle:

Die Bytes 0 bis 8 sind reserviert zum Laden von Flags und Zählern. In die Bytes 9 bis 21 wird die Grußbotschaft geladen. Die Bytes 22 bis 177 (6 * 26) enthalten die Großbuchstaben A bis Z, die Bytes 178 bis 183 das Sonderzeichen "!", die Bytes 184 bis 195 das Leerzeichen " ". In ausgewählten Programmen enthält Byte 190 das Toggle-Flag. Ab Byte 196 bis 255 (6 * 10) stehen die Ziffern "0" bis "9". Damit ist das Lesen und Speichern für die [B+] RAM-Befehle praktisch voll ausgereizt.

Die fünf Beispielprogramme in der zip-Datei:

     1)    Fortlaufende Anzeige des ASCII-Datensatzes

     2)    Interaktive Anzeige mittels Eingabefenster

     3a)  Anzeige von ADC-Daten der Analogeingänge

     3b)  Anzeige von ADC-Daten der Analogeingänge relativ zu 5V

     4)    Anzeige des WCH-Logos mit einer Bitmap

Zu Programm 1) => "I2C_OLED_ASCII_table.tls":
Das Programm wird gestartet und läuft selbstständig ab. Der ASCII-Datensatz der Großbuchstaben A bis Z und nachfolgend die Ziffern 0 bis 9 werden fortlaufend angezeigt.

Zu Programm 2) => "I2C_OLED_ASCII_HELLO_WORLD.tls"
Beim Start erscheint eine Grußmeldung. Im Eingabefenster können nachfolgend Befehle in Zahlenform eingegeben werden. Bei Eingabe der Zahlen 1 bis 26 erscheinen die hier durchnummerierten Großbuchstaben A bis Z. Die Zahlen 30 bis 39 gelten für die Ziffern 0 bis 9. Bei Zahl 40 wird die Grußmeldung wiederholt. Bei Zahl 41 wird der Zeilenabstand größer oder kleiner getoggelt, inklusive Display-Reset. Bei Zahl 99 erfolgt direkter Reset ohne sonstige Änderungen.

Zu Programm 3a) => "I2C_OLED_ASCII_ADC_Scan_plain.tls":
Als eine sinnvolle Anwendung werden bei diesem Script die ADC-Werte ADC0, ADC1 und ADC2 im 2-Sekunden-Takt auf dem Display angezeigt. Die Konvertierung der reinen Messwerte in Dezimal-Digits war ein wenig "tricky".


Zu Programm 3b) => "I2C_OLED_ASCII_ADC_Scan_voltage.tls":
In diesem Programm ist zusätzlich eine Umrechnung der ADC-Messwerte in Spannungswerte enthalten. Es wird davon ausgegangen, dass am CH32V003 eine Versorgungsspannung von 5V anliegt. Auch hier aktualisiert sich die Anzeige alle 2 Sekunden.


Zu Programm 4) => "I2C_OLED_ASCII_LOGO.tls":
Mit diesem Programm kann ein Logo auf dem Display angezeigt werden. Die Pixel-Daten müssen separat nachgeladen werden, da für 128*64 Pixel ( entsprechend 128*8 = 1024 Byte ) weder im Script noch im Ram genügend Platz ist. Das OLED-Display weist genau 8 Zeilen auf mit je 8 Pixeln Höhe, die als Byte geschrieben werden. Das Schreiben von 2 Zeilen (128*2 = 256 Byte) ist jedoch mit dem Nachladen einer 256-Byte großen .dat-Datei möglich. Teilt man also das Logo in 4 Dateien mit je 256 Byte auf und lädt diese hintereinander mit "Load“- bzw. "Open"-Befehl und nachfolgendem [Dat]-Befehl einzeln nach, dann erhält man das volle Logo als Standbild. Als Beispiel ist in der zip-Datei das 4-teilige WCH-Logo enthalten.

 



              Rem *****************
              Rem * OLED example *
              Rem *****************
              Rem
              Rem *****************
              Rem Load fonts
              Rem *****************
0x21F7  Call L247:
              Rem *****************
              Rem Init SDA/SCL
              Rem *****************
0x0906  Pdir = 0x06
              Rem
              Rem *****************
              Rem Init controls
              Rem *****************
0x21C3  Call L205:
              Rem *****************
              Rem Init display
              Rem *****************
0x21A4  Call L201:
              Rem *****************
              Rem Clear display
              Rem *****************
0x21AC  Call L202:
              Rem *****************
              Rem
              Rem *****************
              Rem Main Loop
              Rem *****************
              Rem
              Rem *****************
              Rem Read ADC
              Rem *****************
0x2057  Jmp L200:
              Rem
              Rem *****************
              Rem Print ASCII char
              Rem *****************
              Rem
              Rem *****************
              Rem ASCII offset
              Rem *****************
              L20:
0x0206  B = 6
0x2C00  A = A * B
0x0210  B = 16
0x2A00  A = A + B
              Rem *****************
              Rem Print ASCII
              Rem *****************
              Rem
              Rem *****************
              Rem ASCII pointer
              Rem *****************
0x0202  B = 2
0x3B00  [B+] = A
              Rem
0x0305  C = 5
              L31:
0x0202  B = 2
0x3A00  A = [B+]
0x3400  B = A
0x3A00  A = [B+]
0x213D  Call L112:
0x0202  B = 2
0x3A00  A = [B+]
0x2800  A = A + 1
0x0202  B = 2
0x3B00  [B+] = A
0x250D  C*Jmp L31:
0x4800  Ret
              Rem
              Rem *****************
              Rem I2C Bitbang
              Rem *****************
              Rem
              Rem *****************
              Rem Set_SDA
              Rem *****************
              L100:
0x3F00  A = Pin
0x1302  A = A OR 0x02
0x4500  Pout = A
              Rem PC1 = 1
0x4800  Ret
              Rem *****************
              Rem Reset_SDA
              Rem *****************
              L101:
0x3F00  A = Pin
0x12FD  A = A AND 0xfd
0x4500  Pout = A
              Rem PC1 = 0
0x4800  Ret
              Rem *****************
              Rem Read_SDA
              Rem *****************
              L102:
0x3F00  A = Pin
0x1202  A = A AND 0x02
0x3200  A = A Shr 1
0x4800  Ret
              Rem *****************
              Rem Set_SCL
              Rem *****************
              L103:
0x3F00  A = Pin
0x1304  A = A OR 0x04
0x4500  Pout = A
              Rem PC2 = 1
0x4800  Ret
              Rem *****************
              Rem Reset_SCL
              Rem *****************
              L104:
0x3F00  A = Pin
0x12FB  A = A AND 0xfb
0x4500  Pout = A
              Rem PC2 = 0
0x4800  Ret
              Rem *****************
              Rem Read_SCL
              Rem *****************
              L105:
0x3F00  A = Pin
0x1204  A = A AND 0x04
0x3200  A = A Shr 1
0x3200  A = A Shr 1
0x4800  Ret
              Rem *****************
              Rem Start I2C
              Rem *****************
              L110:
0x2119  Call L100:
0x2125  Call L103:
0x211D  Call L101:
0x2129  Call L104:
0x4800  Ret
              Rem *****************
              Rem Stop I2C
              Rem *****************
              L111:
0x211D  Call L101:
0x2125  Call L103:
0x2119  Call L100:
0x2129  Call L104:
0x211D  Call L101:
0x4800  Ret
              Rem *****************
              Rem Write byte
              Rem *****************
              L112:
              Rem Save byte
0x0201  B = 1
0x3B00  [B+] = A
0x0407  D = 0x07
              Rem ** TX Loop
              L113:
0x0201  B = 1
0x3A00  A = [B+]
              Rem ** Check bit 7
0x1280  A = A AND 0x80
0x0200  B = 0
0x2247  If A=B Jmp L114:
              Rem ** Bit found
0x2119  Call L100:
0x2048  Jmp L115:
              L114:
              Rem ** Bit not found
0x211D  Call L101:
              L115:
              Rem ** Reload byte
0x0201  B = 1
0x3A00  A = [B+]
0x3100  A = A Shl 1
              Rem ** Save shifted byte
0x0201  B = 1
0x3B00  [B+] = A
0x2125  Call L103:
0x2129  Call L104:
0x2640  D*Jmp L113:
              Rem ** Byte was sent
0x2119  Call L100:
0x2125  Call L103:
              Rem ** Read ACK
0x2121  Call L102:
0x3800  D = A
0x2129  Call L104:
0x3900  A = D
              Rem ** Return ACK
0x4800  Ret
              Rem *****************
              Rem
              Rem *****************
              Rem Reading ADC
              Rem *****************
              L200:
0x21C3  Call L205:
0x21A4  Call L201:
              Rem
              Rem
0x0103  A = 3
0x0205  B = 5
0x3B00  [B+] = A
              L210:
0x0205  B = 5
0x3A00  A = [B+]
0x0203  B = 3
0x2264  If A=B Jmp L211:
0x0202  B = 2
0x2266  If A=B Jmp L212:
0x0201  B = 1
0x2268  If A=B Jmp L213:
              L211:
0x3C00  A = AD0
0x2069  Jmp L214:
              L212:
0x3D00  A = AD1
0x2069  Jmp L214:
              L213:
0x3E00  A = AD2
              L214:
              Rem
0x0205  B = 5
0x2D00  A = A / B
              Rem Voltage correction
0x021A  B = 26
0x246E  If A<B Jmp L215:
0x2900  A = A - 1
              L215:
0x0203  B = 3
0x3B00  [B+] = A
              Rem
              Rem
0x020A  B = 10
0x2D00  A = A / B
0x3800  D = A
0x2C00  A = A * B
0x0204  B = 4
0x3B00  [B+] = A
0x3900  A = D
              Rem First digit out
0x021E  B = 30
0x2A00  A = A + B
0x2106  Call L20:
              Rem
              Rem Decimal point
0x0100  A = 0x00
0x213D  Call L112:
0x0100  A = 0x00
0x213D  Call L112:
0x01C0  A = 0xc0
0x213D  Call L112:
0x01C0  A = 0xc0
0x213D  Call L112:
0x0100  A = 0x00
0x213D  Call L112:
0x0100  A = 0x00
0x213D  Call L112:
              Rem
0x0203  B = 3
0x3A00  A = [B+]
0x3600  C = A
0x0204  B = 4
0x3A00  A = [B+]
0x3400  B = A
0x3700  A = C
0x2B00  A = A - B
0x0203  B = 3
0x3B00  [B+] = A
              Rem Last digit out
0x021E  B = 30
0x2A00  A = A + B
0x2106  Call L20:
              Rem
0x011C  A = 28
              Rem Spacer out
0x2106  Call L20:
              Rem
0x0116  A = 22
              Rem Voltage unit out
0x2106  Call L20:
0x011C  A = 28
              Rem Spacer out
0x2106  Call L20:
0x011C  A = 28
              Rem Spacer out
0x2106  Call L20:
              Rem
              Rem
              Rem
              Rem
0x0205  B = 5
0x3A00  A = [B+]
0x2900  A = A - 1
0x0205  B = 5
0x3B00  [B+] = A
              Rem
0x0200  B = 0
0x235C  If A>B Jmp L210:
0x1A02  Delay s = 2
              Rem
0x2057  Jmp L200:
              Rem
              Rem *****************
              Rem Init display
              Rem *****************
              L201:
0x2132  Call L110:
0x0178  A = 0x78
0x213D  Call L112:
0x0140  A = 0x40
0x213D  Call L112:
0x0100  A = 0x00
0x213D  Call L112:
0x4800  Ret
              Rem *****************
              Rem Clear display
              Rem *****************
              L202:
0x0108  A = 8
0x0203  B = 3
0x3B00  [B+] = A
              L11:
0x0180  A = 128
0x0204  B = 4
0x3B00  [B+] = A
              L12:
0x0100  A = 0
0x213D  Call L112:
0x0204  B = 4
0x3A00  A = [B+]
0x2900  A = A - 1
0x0204  B = 4
0x3B00  [B+] = A
0x0200  B = 0
0x23B2  If A>B Jmp L12:
              Rem
0x0203  B = 3
0x3A00  A = [B+]
0x2900  A = A - 1
0x0203  B = 3
0x3B00  [B+] = A
0x0200  B = 0
0x23AF  If A>B Jmp L11:
              Rem
0x4800  Ret
              Rem
              Rem *****************
              Rem Init controls
              Rem *****************
              L205:
0x2132  Call L110:
0x0178  A = 0x78
0x213D  Call L112:
0x0100  A = 0x00
0x213D  Call L112:
0x01AE  A = 0xae
0x213D  Call L112:
0x01D5  A = 0xd5
0x213D  Call L112:
0x0110  A = 0x10
0x213D  Call L112:
0x01A8  A = 0xa8
0x213D  Call L112:
0x013F  A = 0x3f
0x213D  Call L112:
0x01D3  A = 0xd3
0x213D  Call L112:
0x0100  A = 0x00
0x213D  Call L112:
0x0140  A = 0x40
0x213D  Call L112:
0x018D  A = 0x8d
0x213D  Call L112:
0x0114  A = 0x14
0x213D  Call L112:
0x0181  A = 0x81
0x213D  Call L112:
0x01FF  A = 0xff
0x213D  Call L112:
0x0120  A = 0x20
0x213D  Call L112:
0x0100  A = 0x00
0x213D  Call L112:
0x0101  A = 0x01
0x213D  Call L112:
0x0100  A = 0x00
0x213D  Call L112:
0x01A1  A = 0xa1
0x213D  Call L112:
0x01C8  A = 0xc8
0x213D  Call L112:
0x0121  A = 0x21
0x213D  Call L112:
0x0100  A = 0x00
0x213D  Call L112:
0x01FF  A = 0xff
0x213D  Call L112:
0x01DA  A = 0xda
0x213D  Call L112:
0x0112  A = 0x12
0x213D  Call L112:
0x0100  A = 0x00
0x213D  Call L112:
0x01D9  A = 0xd9
0x213D  Call L112:
0x01F1  A = 0xf1
0x213D  Call L112:
0x01DB  A = 0xdb
0x213D  Call L112:
0x0140  A = 0x40
0x213D  Call L112:
0x01A4  A = 0xa4
0x213D  Call L112:
0x01A6  A = 0xa6
0x213D  Call L112:
0x0122  A = 0x22
0x213D  Call L112:
0x0100  A = 0x00
0x213D  Call L112:
0x0107  A = 0x07
0x213D  Call L112:
0x01AF  A = 0xaf
0x213D  Call L112:
0x2137  Call L111:
0x4800  Ret


Elektronik-Labor  Projekte  Mikrocontroller  TLScript