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