PIC16 Code to drive a HD44780 display over SPI, using a HC595
; Sample to drive a HD44780 LCD over SPI, using a 74HC595 shift register
; See: https://highfieldtales.wordpress.com/2011/12/04/very-fast-spi-to-parallel-interface-for-netduino/
;
; PIC 74HC595 HD44780
; 15 D4
; 1 D5
; 2 D6
; 3 D7
; 4 RS
; 5 RW
; 6 E
; 7
; 9 - 12
; 10
; CLK 11
; 12 - 9
; 13 - GND
; DAT 14
;
; The SS port resets the shift register, via an inverter
list P=16F876A
#include p16f876a.inc
__CONFIG _CP_ALL & _DEBUG_OFF & _CPD_OFF & _LVP_OFF & _BODEN_ON & _PWRTE_ON & _WDT_OFF & _HS_OSC
#define SS_PORT PORTB
#define SS PORTB,1
RES_VECT CODE 0x0000 ; processor reset vector
GOTO START ; go to beginning of program
; TODO ADD INTERRUPTS HERE IF USED
MAIN_PROG CODE ; let linker place main program
START
banksel TRISC ; Set up pins for SPI
bcf TRISC,5 ; Data pin 16
bcf TRISC,3 ; Clock pin 14
bsf TRISC,4 ; Data in
clrf TRISB
banksel SSPCON ; Set up SPI
bcf SSPCON,5 ; Disable SPI
movlw b'00010000'
movwf SSPCON
movlw b'11000000' ; 1 = Input data sampled at end of data output time
; 1 = Transmit occurs on transition from active to Idle clock state
movwf SSPSTAT ;
bsf SSPCON,4 ; CKP 1 = Idle state for clock is a high level
bcf SSPCON,0 ; 0000 = SPI Master mode, clock = FOSC/4
bcf SSPCON,1
bcf SSPCON,2
bcf SSPCON,3
bsf SSPCON,5 ; SSPEN 1 = Enables serial port and configures SCK, SDO, SDI, and SS as serial port pins
call init_lcd
loop:
GOTO loop ; loop forever
init_lcd
; Remember to init ports!
; Initialise HD44780 for 4-bits, 2 lines
call Delay_100ms ; 100ms, step 1
banksel FLAGS
movlw 0 ; Command
movwf FLAGS
movlw B'00110000' ; initialise module, step 2
call lcd_byte
call Delay5ms ; > 4.1ms
movlw B'00110000' ; initialise module, step 3
call lcd_byte
call Delay100us ; > 100us
movlw B'00110000' ; initialise module, step 4
call lcd_byte
call Delay100us ; > 100us
movlw B'00100000' ; initialise module, step 5
call lcd_byte
call Delay100us ; > 100us
;x3 goto x3 ; Step 6, the real function select
movlw B'00101100' ; display function (4-bits, 2 lines, 5x10 dots)
call lcd_byte
call Delay100us ; > 53us
movlw B'00001000' ; Step 7, display on/off
call lcd_byte
call Delay100us ; > 53us
movlw B'00000001' ; Step 8, display clear
call lcd_byte
call Delay5ms ; > 3ms
; |- 0 = Display off, 1 = display on
; |- 0 = 1 line, 1 = 2 line
movlw B'00000110' ; Step 9, entry mode. cursor moves right, display not shifted
call lcd_byte
call Delay100us ; > 53us
; Actual init ends here
; |- 0 = Display off, 1 = Display on
; |- 0 = Cursor off, 1 = Cursor on
; |- 0 = Blink off, 1 = Blink on
movlw B'00001111' ; display on, cursor on, blink on
call lcd_byte
call Delay100us ; > 53us
; Init done, stuff below is just for testing.
banksel FLAGS
movlw 1 ; Data
movwf FLAGS
movlw 'A'
call lcd_byte
movlw 'B'
call lcd_byte
movlw 'C'
call lcd_byte
xy movlw 'D'
call lcd_byte
call Delay_100ms
movlw 'E'
call lcd_byte
return
; Send byte in W to LCD as command or data, depending on FLAGS
; 0 in flags as command or 1 as data
lcd_byte
banksel D_STO
movwf D_STO
swapf D_STO,w ; Swap high and low nibbles, result in W
andlw b'00001111' ; Mask out
call lcd_push_nibble ; Push high nibble
banksel D_STO
movf D_STO,w ; get byte
andlw b'00001111' ; Mask ou
call lcd_push_nibble ; Push low nibble
call Delay100us ; > 100us delay
return
; Push high nibble in w as data or cmd
; Input: Nibble in W, 0 in flags as command or 1 as data
lcd_push_nibble ; Push high nibble in w as data or cmd
btfsc FLAGS,0 ; flag set=data, set Q4 in that case
iorlw b'00010000'
; Not set leave as is
iorlw b'10000000' ; Set high bit
call send_w_on_spi ; Send nibble, E is still low
iorlw b'11000000' ; Set E bit
call send_w_on_spi ; Send nibble, E is high
andlw b'10111111' ; E -> low
call send_w_on_spi ; Send nibble, E is low
return
send_w_on_spi
;banksel SS_PORT
bcf SS ; Assert SS (low)
movwf SSPBUF ; begin transmission
WAIT btfss PIR1, SSPIF ; send completed? if Yes skip next
goto WAIT
bcf PIR1, SSPIF ; yes, clear flag
;banksel SS_PORT
bsf SS ; Unassert SS (high)
return
;-----------------------------------------------
; Delay = 0.0001 seconds
; Clock frequency = 20 MHz
; Actual delay = 0.0001 seconds = 500 cycles
; Error = 0 %
Delay100us
;banksel STORE1
;499 cycles
movlw 0xA6
movwf STORE1
Delay100us_0
decfsz STORE1, f
goto Delay100us_0
;1 cycle
nop
return
;-----------------------------------------------
; Delay = 0.005 seconds
; Clock frequency = 20 MHz
; Actual delay = 0.005 seconds = 25000 cycles
; Error = 0 %
Delay5ms
;banksel STORE1
;24998 cycles
movlw 0x87
movwf STORE1
movlw 0x14
movwf STORE2
Delay5ms_0
decfsz STORE1, f
goto $+2
decfsz STORE2, f
goto Delay5ms_0
;2 cycles
goto $+1
return
Delay_100ms
;-----------------------------------------------
; Delay = 0.1 seconds
; Clock frequency = 20 MHz
; Actual delay = 0.1 seconds = 500000 cycles
; Error = 0 %
banksel STORE1
;499994 cycles
movlw 0x03
movwf STORE1
movlw 0x18
movwf STORE2
movlw 0x02
movwf STORE3
Delay_100ms_0
decfsz STORE1, f
goto $+2
decfsz STORE2, f
goto $+2
decfsz STORE3, f
goto Delay_100ms_0
;6 cycles
goto $+1
goto $+1
goto $+1
return
udata
STORE1 res 1
STORE2 res 1
STORE3 res 1
FLAGS res 1
D_STO res 1
END