8-Bit-Breadboard-Computer auf Basis einer 6502-CPU - 3x16 Zeichen Anzeige mit DOGM163 LCD - Debugging
Bisherige Artikel dieser Serie - hier könnt ihr nochmal alle Grundlagen nachlesen, falls ihr jetzt erst einsteigt:- Digitale Logik und Logikgatter einfach erklärt
- Verwendung des 555-Timer als Taktgeber / Clock
- Das Clock-Modul: Taktgeber für unseren Breadboard-Computer
- Speichertypen und Zugriff auf Speicher
- Erste Schritte mit der CPU
- Eine echte WDC W65C02-CPU
- Das Speicher-Modul: Anbindung von RAM und ROM
- Erstes Programm in Maschinensprache: RAM-Test
- Das Sniffer-Modul: Ein Arduino/STM32 zeigt an, was auf dem Bus los ist.
- Erstes Ausgabegerät: Adressierung und Ausgabe auf 8 LEDs
- Programmiersprache-Evolution: von Maschinensprache zu Assembler
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 1: Taktungsprobleme
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 2: 20 Nanosekunden, die nicht sein sollten
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 3: Assembler-Programme
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 4: neue Adressierungsarchitektur mit 74HC688 und 74HC138
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 5: Programmierung in Assembler
- 3x16 Zeichen LCD DOGM162 als Textausgabe-Gerät, Teil 1: Breadboard-Aufbau und erste Programmversion
Nur leider funktionierte die erste Version des Programmes - wie das so häufig ist - nicht auf Anhieb. Jetzt ist die große Frage: liegt der Fehler in der Hardware oder in der Software?
Ich habe schon ein paar Erfahrungen mit dem DOGM163-Display gemacht, z. B. mit einem STM32, 3.3V und SPI und weiß das es ein wenig tückisch ist.
Klugerweise habe ich im Vorfeld den für mich neuen 4-Bit-Modus schon einmal mit dem Arduino getestet und dabei festgestellt, dass die Initialisierungs-Sequenz im Datenblatt nicht stimmen kann; denn wenn ich diese verwende, bleibt das Display einfach nur dunkel. Auf dem Arduino und mit C stehe ich auf ein wenig festeren Füßen als mit einer selbst entworfenen Breadboard-Schaltung mit 6502 CPU und eigener IO-Architektur.
Darum war das keine schlechte Idee, die Hardware erst einmal unter Arduino zum Laufen zu bringen. Und nach längerer Tüftelei ist es mir dann auch gelungen, das Display unter 5V und im 4Bit-Modus zum laufen zu bringen.
Sonst nämlich hätte ich mich eventuell auf die Angaben im Datenblatt verlassen und den Fehler ewig in der Breadboard-Schaltung gesucht und dort natürlich nicht gefunden, weil er halt in der Display-Ansteuerung liegt.
Trotzdem könnte ich natürlich das Display immer noch falsch verkabelt haben. Ich müsste nur eine Datenleitung vertauscht haben und schon wären die Bits durcheinander und es kämen unsinnge Befehle beim Display an. Evtl. taktet der 74HC138 nicht richtig auf der neu verwendeten Leitung? Der Möglichkeiten für Fehlerquellen gibt es viele.
Da ich aber besondere Sorgfalt beim Verdrahten habe walten lassen und die Verkabelung vom Arduino quasi kopieren konnte und ich ehrlicherweise gerade keine Lust auf stupides Durchmessen hatte, wollte ich mir doch zuerst einmal den Source noch einmal genauer anschauen, ob nicht da ein Fehler ist.
Schon als ich den Code noch einmal in Ruhe durchging, fand ich den ersten Fehler. Das EPROM-Image wurde neu gebrannt und die neue Version ausgetestet, aber das Display blieb schwarz.
Also im Code einmal ein STP an der Stelle, wo das Datenbyte an das Display geschickt wird, eingebaut und am Breadboard-Computer den integrierten Bus-Sniffer aus Arduino Basis benutzt, um mir anzuschauen, was da genau vor sich ging. Und dabei einen weiteren Fehler gefunden: Ich hatte ein BRA, einen Sprung vergessen. Wieder: an den PC, Source ändern, EPROM neu brennen, wieder in den Breadboard-Computer einsetzen und neu testen.
Noch stand nichts auf dem Display, also beschließe ich, die auf dem Breadboard integrierte 8-LED-Ausgabe zu benutzen, um dort das Datenbyte, das zum LCD geschickt wird zu setzen. Das macht das Debuggen einfacher. Ich muss nicht Befehl für Befehl auf der Sniffer-Anzeige durchschauen, sondern kann erst einmal grob die LEDs beobachten. Das geht viel schneller. Später, wenn der Fehler eingegrenzt ist, kann ich ja dann wieder feingranularer auf dem Sniffer nachschauen. So stelle ich den Takt auf ein Tempo, wo ich noch alles mitbekomme und lasse das Programm laufen.
Die LED für die Reset-Leitung ist immer an. Reset ist also immer auf high. So soll es sein. Auch die Schreib-Leitung ist immer auf low - es wird ja nur geschrieben - auch das ist richtig. Und die 4 Bits der Nibbles scheinen auch in der richtigen Reihenfolge zu sein. Wunderbar. Alles richtig verdrahtet.
Doch dann fällt mir auf, dass Enable nicht gepulst wird. Ein Bick auf den Source und mir fällt auf, dass ich beim pulsen einen STA absetzen, ohne vorher einen LDA gemacht zu haben. Dummer Fehler. Aber passiert. Das EPROM wieder gebrannt und neu getestet.
Noch immer ist das Display dunkel... doch halt. Beim ganz genau hinschauen kann ich schwach Buchstaben auf dem Display erkennen. Dort steht doch tatsächlich "HELLO WORLD". So ein hinterhätiges Display; böser Hund! (kleiner Wortwitz wegen DOG). Doch statt mich über das Display zu ärgern, dass es scheinbar bei 5 Volt auch hier den Kontrast nicht konstant hinkriegt (also war der Arduino bei meinen Tests vorher nicht schuld) überwiegt die Freude, dass es doch läuft.
Beim nächsten Lauf ist auch der Kontrast besser. Na, wo es jetzt läuft, kann die Lichtorgel ja wieder aus, denn die LED-Debug-Anzeige brauche ich jetzt ja nicht mehr. Das sollte die Ausgabe auch noch ein bisschen schneller machen. Denn man kann locker und leicht mitlesen, so langsam ist die Ausgabe bei der momentanen Höchstfrequenz von 500 Hertz.
Video
Ich habe auch diesmal wieder ein Video gemacht, dass nur wenig geschnitten ist und in dem ihr sozusagen live mitverfolgen könnt, wie ich beim Debuggen vorgegangen bin:
Source-Code
Damit ist das Assembler-Programm jetzt fehlerfrei und fertig und ich will es euch nicht weiter vorenthalten.6-DOGM163-LCD-Ausgabe.asm (klicken, um diesen Abschnitt aufzuklappen)
; Einschaltmeldung auf DOGM163-LCD ausgeben
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-08-10
CHIP W65C02S ; (default)
ORG $8000 ; Programmstartadresse (von der CPU aus gesehen)
; --- Konstanten: IO-Adressen ---
LED: equ $7000 ; 8-LED-Ausgabe
SEGL: equ $7001 ; linke 7-Segment-Anzeige
SEGM: equ $7002 ; mittlere 7-Segment-Anzeige
SEGR: equ $7003 ; rechte 7-Segment-Anzeige
LCD: equ $7004 ; DOGM163 LCD mit 3*16 Zeichen
; --- Kontanten: Bitpositionen ---
bitLCD_Reset: equ 1 ; Bit 0 = Reset (0) / normal (1)
bitLCD_RS: equ 2 ; Bit 1 = RS = Command (0) / Character (1)
bitLCD_RW: equ 4 ; Bit 2 = RW = Write (0, normal) / Read (1, nicht benutzt)
bitLCD_E: equ 8 ; Bit 3 = Enable (pulse=1, normal=0)
; bit 4 bis 7 = Datenbits D4 bis D7
; --- Konstanten für LCD
opLCD_Clear: equ %00000001
opLCD_Home: equ %00000010
opLCD_DispOn: equ %00001100
opLCD_DispOff: equ %00001000
opLCD_CursorOn: equ %00001111
opLCD_CursorOff: equ %00001100
; --- Speicheradressen RAM
lcdhb: equ $00
lcdlb: equ $01
lcdrs: equ $02 ; 1= char, 0= cmd, vor jsr lcdByte setzen
init:
; LED-Ausgabe löschen
lda #0
sta LED
; Segment-Ausgabe löschen
lda #$FF
sta SEGL
sta SEGM
sta SEGR
init_LCD:
; Reset an LCD (Bit 0 auf Low für ein paar ms)
lda #0
sta LCD
lda #5
jsr delay
; wieder auf high und min 50 ms warten
lda #1
sta LCD
lda #15
jsr delay
jsr lcdInit
start:
ldx #0
print:
ldy HELLO,x ; X. Buchstaben aus "HELLO WORLD" in Y-Register laden
beq fertig
lda #1 ; ist ein zeichen
sta lcdrs
tya
jsr lcdByte
inx
bra print
fertig:
stop:
STP ; Hält die CPU an
; --- Unterprogramme ---
delay:
NOP ; Zeit verschwenden, NOP = 2 Taktzyklen
NOP
dec A
bne delay
rts
; --- für LCD ---
INCLUDE lcd.inc
; --- im ROM abgelegte Konstanten
HELLO:
; 1234567890123456 Spaces am Ende gelten auch, auch wenn man sie nicht sieht
ASCII 6502 Breadboard
ASCII Retro Computer
ASCII www.cool-web.de
BYTE 0
INCLUDE seg-defs.inc
; --- Sprungvektoren
ORG $FFFA
word $8000 ; NMI bei $FFFA/B
word $8000 ; Programmstartadresse bei $FFFC/D
word $8000 ; BRK/IRQ bei $FFFE/F
lcd.inc (klicken, um diesen Abschnitt aufzuklappen)
; Unterprogramme für DOGM163-LCD
; last edit: Oliver Kuhlemann (www.cool-web.de), 2020-08-09
lcdByte: ; Übergabe Bytewert in Akku, Command (0) / Character (1) in lcdrs
sta lcdhb ; Übergabe aus Akku speichern -> Highbyte kommt zuerst dran
sta lcdlb ; Übergabe aus Akku speichern -> schon mal Lowbyte speichern
asl lcdlb ; unteren 4 Bit nach links nach oben schieben
asl lcdlb
asl lcdlb
asl lcdlb
smb0 lcdhb ; Reset aus
smb0 lcdlb
rmb2 lcdhb ; Write
rmb2 lcdlb
rmb3 lcdhb ; Enable erst einmal auf low
rmb3 lcdlb
; command (lcdrs=0) oder Character (lcdrs=1)
lda lcdrs
bne lcdByteCmd
rmb1 lcdhb ; Command (0) / Character (1)
rmb1 lcdlb ; Command (0) / Character (1)
bra lcdByteCmd_after
lcdByteCmd:
smb1 lcdhb ; Command (0) / Character (1)
smb1 lcdlb ; Command (0) / Character (1)
lcdByteCmd_after:
; High-Byte übertragen und pulsen (E-Leitung auf High für ein paar ms, min. 2 ms)
lda lcdhb
sta LCD
;sta LED
nop
smb3 lcdhb ; E-Leitung kurz auf High pulsen
lda lcdhb
sta LCD
;sta LED
nop
rmb3 lcdhb ; E-Leitung wieder auf Low
lda lcdhb
sta LCD
;sta LED
nop
; Low-Byte übertragen und pulsen (E-Leitung auf High für ein paar ms, min. 2 ms)
lda lcdlb
sta LCD
;sta LED
smb3 lcdlb ; E-Leitung kurz auf High pulsen
lda lcdlb
sta LCD
;sta LED
nop
rmb3 lcdlb ; E-Leitung wieder auf Low
lda lcdlb
sta LCD
;sta LED
nop
rts
lcdInit: ; LCD initialisieren --------------------------------------------------------------
; 8-Bit-Mode einschalten; nötig, um nach deinem Reset die Spur wieder zu finden
lda #0 ;cmd
sta lcdrs
lda #%00110000
jsr lcdByte
; nötig, um nach deinem Reset die Spur wieder zu finden
lda #0 ;cmd
sta lcdrs
lda #%00110011
jsr lcdByte
; nötig, um nach deinem Reset die Spur wieder zu finden
lda #0 ;cmd
sta lcdrs
lda #%00110010
jsr lcdByte
; 4-Bit-Mode einschalten; extended instruction table 1 setzen
lda #0 ;cmd
sta lcdrs
lda #%00101001
jsr lcdByte
; bias set
lda #0 ;cmd
sta lcdrs
lda #%00011101
jsr lcdByte
; Power/ICON control/Contrast set
lda #0 ;cmd
sta lcdrs
lda #%01010011
jsr lcdByte
; Follower control
lda #0 ;cmd
sta lcdrs
lda #%01101111
jsr lcdByte
; Contrast set(low byte)
lda #0 ;cmd
sta lcdrs
lda #%01111111
jsr lcdByte
; es folgen nur noch Standard-Befehle: normal instruction table
lda #0 ;cmd
sta lcdrs
lda #%00101000
jsr lcdByte
; Entry Mode Set
lda #0 ;cmd
sta lcdrs
lda #%00000110
jsr lcdByte
; clear
lda #0 ;cmd
sta lcdrs
lda #opLCD_Clear
jsr lcdByte
; display on
lda #0 ;cmd
sta lcdrs
lda #opLCD_DispOn
jsr lcdByte
rts

Sonstige Problemchen mit dem LCD
Scheinbar mag das DOGM163 nicht immer hundertprozentig mit 5V und ohne Booster-Kondensatoren laufen. Das war mir ja schon bei den Tests mit dem Arduino aufgefallen, dass der Kontrast bzw. die Helligkeit schwankt.Auch habe ich festgestellt, dass unten beim Display nur noch vier der fünf Volt Versorgungsspannung ankommen. Das was am meisten Strom zieht, dürfte der Sniffer sein. Den brauche ich auch nicht immer und habe deshalb für ihn einen Schalter nachgerüstet und ihn abschaltbar gemacht. Zudem habe ich statt der Drahtbrücke für die Hintergrundbeleuchtung einen 100 Ohm Widerstand eingesetzt, damit die Hintergrundbeleuchtung des Sniffer-Displays nicht die des DOGM163 überstrahlt, wenn es denn an ist.
Das DOGM163 ist ganz gut ablesbar (wenn ich nicht mal wieder gerade spinnt), wenn man davor sitzt, aber auch der Kamera kommt es nicht so deutlich rüber wie etwa das Nokia 5110 Display. Aber die Kamera-Tüchtigkeit soll jetzt nicht oberste Priorität sein; zur Not lese ich euch dann in zukünfitgen Videos einfach vor, was auf dem DOGM163 steht.