Die Ansteuerung eines LCDs mit BASCOM ist wunderbar leicht. Manchmal hat man jedoch nicht so viele Ausgänge mehr am µC frei und man muss andere Wege suchen. Jetzt hat Cassio hier kürzlich eine sehr ausgefeilte Methode vorgestellt, ein Display über I2C anzusteuern. Aus praktischem und auch akademischem Interesse habe ich mich mal daran versucht, so eine Ansteuerung über ein Schieberegister zu bewerkstelligen. Herausgekommen ist der nachfolgende Code, der nur noch drei Leitung ("3 Wire") zur Steuerung des LCDs im 4 Bit Modus benötigt. Es wäre sogar möglich, mit nur 2 Leitungen auszukommen, aber damit habe ich mich (noch) nicht befasst.
Der Code ist eher praktischer Natur und berücksichtigt nur 16x2 LCDs im 4 Bit Modus, R/W wird nicht unterstützt. Eine Anpassung an 16x4 ist aber ohne weiteres möglich. Nachgebildet habe ich die folgenden BASCOM Befehle:
Initlcd > Init_3w
Cls > Cls_3w
Lcd x > Lcd_3w("MeinText")
Locate y, x > Locate_3w(y,x)
Als Schieberegister kommt ein günstiges 74HC595 zum Einsatz. Die Ansteuerung ist im Code angegeben.
Im Prinzip ist der Code nur eine Abarbeitung des HD44780-Datenblatts. Das Schieberegister wird über SHIFTOUT mit Daten gefüllt. Die ersten beiden Bits kontrollieren RS und E.
RS=0 : Befehl wird empfangen / RS=1 Daten werden empfangen. E(nable)=1/0 ist selbsterklärend.
Nachdem das Register mit Daten gefüllt ist, müssen diese noch über Q0....Q5 ausgegeben werden. Das erledigt die Befehlsfolge:
Im 4-Mit Modus müssen die beiden Nibbles des Command- oder Data-Bytes nacheinander übertragen werden. Dazu muss man das Byte mit SWAP "herumdrehen" und nur die ersten 4 Bits übertragen. Die zweite Hälfte des Bytes wird ohne SWAP übertragen. Dadurch das RS und E ebenfalls gesetzt werden müssen, werden bei jedem Vorgang 2 Bits + Nibble = 6 Bits übertragen.
Nach dem einschalten ist ein LCD immer im 8 Bit Modus, deshalb werden bei den ersten 4 Befehlen im Init nur 4 Bits übertragen. Das aber nur am Rande.
Der Code ist kurz getestet, nicht unbedingt optimiert und enthält nicht alle Befehle, die BASCOM für die LCD-Steuerung anbietet. Vielleicht findet ihn der ein oder andere jedoch trotzdem nützlich.
Der Code ist eher praktischer Natur und berücksichtigt nur 16x2 LCDs im 4 Bit Modus, R/W wird nicht unterstützt. Eine Anpassung an 16x4 ist aber ohne weiteres möglich. Nachgebildet habe ich die folgenden BASCOM Befehle:
Initlcd > Init_3w
Cls > Cls_3w
Lcd x > Lcd_3w("MeinText")
Locate y, x > Locate_3w(y,x)
Als Schieberegister kommt ein günstiges 74HC595 zum Einsatz. Die Ansteuerung ist im Code angegeben.
Im Prinzip ist der Code nur eine Abarbeitung des HD44780-Datenblatts. Das Schieberegister wird über SHIFTOUT mit Daten gefüllt. Die ersten beiden Bits kontrollieren RS und E.
RS=0 : Befehl wird empfangen / RS=1 Daten werden empfangen. E(nable)=1/0 ist selbsterklärend.
Nachdem das Register mit Daten gefüllt ist, müssen diese noch über Q0....Q5 ausgegeben werden. Das erledigt die Befehlsfolge:
Code:
Rck = 0
Rck = 1
Rck = 0
Im 4-Mit Modus müssen die beiden Nibbles des Command- oder Data-Bytes nacheinander übertragen werden. Dazu muss man das Byte mit SWAP "herumdrehen" und nur die ersten 4 Bits übertragen. Die zweite Hälfte des Bytes wird ohne SWAP übertragen. Dadurch das RS und E ebenfalls gesetzt werden müssen, werden bei jedem Vorgang 2 Bits + Nibble = 6 Bits übertragen.
Nach dem einschalten ist ein LCD immer im 8 Bit Modus, deshalb werden bei den ersten 4 Befehlen im Init nur 4 Bits übertragen. Das aber nur am Rande.
Der Code ist kurz getestet, nicht unbedingt optimiert und enthält nicht alle Befehle, die BASCOM für die LCD-Steuerung anbietet. Vielleicht findet ihn der ein oder andere jedoch trotzdem nützlich.
Code:
'##################################### 3 WIRE LCD BASCOM ###########################
' ATmega
' 74HC595 SHIFT REGISTER
' 2x16 LCD (HD44780)
' + 5V
'
' 74HC595
' ________
' | |----1-Q1~~~~DB6 LCD 13
' | |----2-Q2~~~~DB5 LCD 12
' Vcc-16--| |----3-Q3~~~~DB4 LCD 11
' GND--8--| |----4-Q4~~~~RS LCD 4
' | |----5-Q5~~~~E LCD 5
' Vcc~~MR-10--| |----6-Q6
' GND~~OE-13--| |----7-Q7
' | |---15-Q0~~~~DB7 LCD 14
' Q7S-9--| |
' | |---11-SHCP~~PB1 (Clk) µC
' | |---12-STCP~~PB2 (Rck) µC
' | |---14-DS~~~~PB0 (Dat) µC
' --------
'
' LCD: 1: GND / 2: VDD / 3: VEE / 4: RS = Q4 / 5: RW = GND / 6: E=Q5 .... 11: DB4 = Q3 / 12: DB5 = Q2 / 13: DB6 = Q1 / 14: DB7 = Q0
$regfile = "m8def.dat"
$crystal = 3686400
$baud = 9600
$hwstack = 64
$swstack = 64
$framesize = 64
'#####################################
Config Portb.0 = Output
Config Portb.1 = Output
Config Portb.2 = Output
Dat Alias Portb.0
Clk Alias Portb.1
Rck Alias Portb.2
'#####################################
Dim Rse As Byte 'RS and E
Dim I As Byte
Dim Lcd_string As String * 16
Dim Character(17) As Byte At Lcd_string Overlay
'#####################################
Declare Sub Write_lcd_cmd8(byval Cmd As Byte) 'Write command
Declare Sub Write_lcd_cmd(byval Cmd As Byte) 'Write command in 4 Bit mode
Declare Sub Write_lcd_txt(byval Txt As Byte) 'LCD Init
Declare Sub Init_3w 'LCD Cls
Declare Sub Cls_3w
Declare Sub Lcd_3w(byval Lcdstring As String) 'LCD output
Declare Sub Locate_3w(byval Y As Byte , Byval X As Byte) 'LCD Locate
'#####################################
Call Init_3w
Call Cls_3w
'##################################### Test Test Test
Do
Call Locate_3w(1 , 1) : Call Lcd_3w( "Hello World")
Wait 1
Call Locate_3w(2 , 1) : Call Lcd_3w( "Second Line")
Wait 2
Call Cls_3w
Call Locate_3w(1 , 1) : Call Lcd_3w( "1")
Wait 1
Call Locate_3w(1 , 2) : Call Lcd_3w( "2")
Wait 1
Call Locate_3w(1 , 3) : Call Lcd_3w( "3")
Wait 1
Call Locate_3w(1 , 3) : Call Lcd_3w( "C")
Wait 1
Call Locate_3w(1 , 2) : Call Lcd_3w( "B")
Wait 1
Call Locate_3w(1 , 1) : Call Lcd_3w( "A")
Wait 2
Call Cls_3w
Call Locate_3w(1 , 1) : Call Lcd_3w( "1234567890123456")
Call Locate_3w(2 , 1) : Call Lcd_3w( "ABCDEFGHIJKLMNOP")
Wait 2
Call Cls_3w
Wait 1
Loop
'##################################### Write LCD Command (8 Bit Mode)
Sub Write_lcd_cmd8(byval Cmd As Byte)
Rse = &B01 'E=1 Rs=0
For I = 0 To 1
Shiftout Dat , Clk , Rse , 2 , 2 'send Rs and E
Shiftout Dat , Clk , Cmd , 2 , 4 'send upper nibble
Rck = 0
Rck = 1
Rck = 0
Rse = &B00 'E=0 Rs=0
Next
End Sub
'##################################### Write LCD Command (4 Bit Mode)
Sub Write_lcd_cmd(byval Cmd As Byte)
Local Cmd2 As Byte
Cmd2 = Cmd
Swap Cmd
Rse = &B01 'E=1 Rs=0
For I = 0 To 1 'upper nibble
Shiftout Dat , Clk , Rse , 2 , 2 'send Rs and E
Shiftout Dat , Clk , Cmd , 2 , 4 'send nibble
Rck = 0
Rck = 1
Rck = 0
Rse = &B00 'E=0 Rs=0
Next
Rse = &B01
For I = 0 To 1 'lower nibble
Shiftout Dat , Clk , Rse , 2 , 2 'send Rs and E
Shiftout Dat , Clk , Cmd2 , 2 , 4 'send nibble
Rck = 0
Rck = 1
Rck = 0
Rse = &B00 'E=0 Rs=0
Next
End Sub
'##################################### Write LCD Character (4 Bit Mode)
Sub Write_lcd_txt(byval Txt As Byte)
Local Txt2 As Byte
Txt2 = Txt
Swap Txt
Rse = &B11 'E=1 Rs=1
For I = 0 To 1 'upper nibble
Shiftout Dat , Clk , Rse , 2 , 2 'send Rs and E
Shiftout Dat , Clk , Txt , 2 , 4 'send nibble
Rck = 0
Rck = 1
Rck = 0
Rse = &B10 'E=0 Rs=1
Next
Rse = &B11 'E=1 Rs=1
For I = 0 To 1 'lower nibble
Shiftout Dat , Clk , Rse , 2 , 2 'send Rs and E
Shiftout Dat , Clk , Txt2 , 2 , 4 'send nibble
Rck = 0
Rck = 1
Rck = 0
Rse = &B10 'E=0 Rs=1
Next
End Sub
'##################################### Init LCD
Sub Init_3w
Waitms 20
Call Write_lcd_cmd8(&B0000_0011) 'Init 4 Bit 1x
Waitms 5
Call Write_lcd_cmd8(&B0000_0011) '2x
Waitms 5
Call Write_lcd_cmd8(&B0000_0011) '3x
Waitms 5
Call Write_lcd_cmd8(&B0000_0010) '4 Bit Mode
Waitms 5
Call Write_lcd_cmd(&B0010_1000) '4 Bit / 2 Lines / 5x8 Font
Waitms 5
Call Write_lcd_cmd(&B0000_1100) 'Display ON / Cursor OFF / Blinking OFF
Waitms 5
Call Write_lcd_cmd(&B0000_0100) 'Increment / Shift OFF
Waitms 5
End Sub
'##################################### CLS
Sub Cls_3w
Call Write_lcd_cmd(&B0000_0001)
Waitms 5
End Sub
'##################################### LOCATE (y,x)
Sub Locate_3w(byval Y As Byte , Byval X As Byte)
X = X - 1
Call Write_lcd_cmd(&B0000_0010) 'Cursor home
Waitms 5
If Y = 1 Then
Call Write_lcd_cmd(&H80 + X) '1st line
Elseif Y = 2 Then
Call Write_lcd_cmd(&Hc0 + X) '2nd line
End If
End Sub
'##################################### LCD (String)
Sub Lcd_3w(byval Lcdstring As String)
Local Strlen As Byte
Local A As Byte
Strlen = Len(lcdstring)
Lcd_string = Lcdstring
For A = 1 To Strlen
Call Write_lcd_txt(character(a))
Next
End Sub
'#####################################