Code:
;******************************************************************
; *
; Filename: ezLCD v3 (12F683).asm *
; Author: Mike McLaren, K8LH *
; (C)2010: Micro Application Consultants, All Rights Reserved *
; Date: 08-Dec-11 (rev:111210.0637z) *
; *
; K8LH 12F683 serial HD44780 LCD 4-bit interface experiment *
; *
; 8-bit test code. this interface traps a 0xFE character and *
; writes the character immediately following it to the LCD as *
; a command character (RS = 0) *
; *
; *
; MPLab: 8.80 (tabs=8) *
; MPAsm: 5.43 *
; *
;******************************************************************
#include <p12f683.inc>
list st=off
errorlevel -302
radix dec
__config _FCMEN_OFF&_IESO_OFF&_MCLRE_OFF&_WDT_OFF&_INTRC_OSC_NOCLKOUT
;--< variables >---------------------------------------------------
datahi equ 0x40 ; shadow for 1st nibble
datalo equ 0x41 ; shadow for 2nd nibble
temp equ 0x42 ; low level LCD routines
delayhi equ 0x43 ;
rsflag equ 0x44 ;
;--< constants >---------------------------------------------------
;
; bit time (cycles) used to sample a serial bit stream
;
; 9600 baud, 208 (208.3) cycles, 0.16% bit rate error
; 19200 baud, 104 (104.2) cycles, 0.16% bit rate error
; 57600 baud, 35 ( 34.7) cycles, 0.80% bit rate error
;
clock equ 8 ; 8 MHz clock
usecs equ clock/4 ; cycles/usec multiplier
msecs equ clock/4*1000 ; cycles/msec multiplier
bRate equ 57600 ; baudrate 9600, 19200, or 57600
bTime equ (usecs*10000000/bRate+5)/10
; bTime = 208, 104, or 35 cycles
;
#define D4 0 ; GP0 -> LCD 'D4'
#define D5 1 ; GP1 -> LCD 'D5'
#define D6 2 ; GP2 -> LCD 'D6'
#define SerPin GPIO,3 ; GP3 -> Serial Input
#define D7 4 ; GP4 -> LCD 'D7'
#define E 5 ; GP5 -> LCD 'E'
#define RS D4 ; RS 'rc' integrator output
;--< macros >------------------------------------------------------
;
; inDlyCy() in-line delay macro, range 0..1023 cycles,
; produces 0 to 6 instructions.
;
inDlyCy macro pTime
local dloop
if pTime > 3
movlw (pTime)/4-1 ;
dloop addlw -1 ;
bc dloop ;
endif
if pTime%4 & 1
nop ; 1 cycle
endif
if pTime%4 & 2
goto $+1 ; 2 cycles
endif
endm
;
; K8LH DelayCy() subsystem macro generates four instructions
;
DelayCy macro cycles ; 11..327690 cycle range
if (cycles<11)|(cycles>(5*65536+10))
error " DelayCy range error "
endif
movlw high((cycles-11)/5)+1
movwf delayhi
movlw low ((cycles-11)/5)
call uDelay-((cycles-11)%5)
endm
;******************************************************************
; Reset Vector *
;******************************************************************
org 0x0000
v_reset
clrf STATUS ; |B0
goto Main ; |B0
;******************************************************************
; Interrupt Vector *
;******************************************************************
org 0x0004
v_int
;
; using IOC interrupt on start bit leading edge
;
inDlyCy(bTime/2-4) ; half bit time minus 4 cycles |B0
movlw 1<<E ; preset E bit |B0
movwf datahi ; |B0
movwf datalo ; |B0
;
; collect lo nibble in 'datalo' (LCD data bits b0..b3)
;
bit0 inDlyCy(bTime-3) ; |B0
btfsc SerPin ; bit = 0? yes, skip, else |B0
bsf datalo,D4 ; set LCD D4 shadow bit |B0
bit1 inDlyCy(bTime-2) ; |B0
btfsc SerPin ; bit = 0? yes, skip, else |B0
bsf datalo,D5 ; set LCD D5 shadow bit |B0
bit2 inDlyCy(bTime-2) ; |B0
btfsc SerPin ; bit = 0? yes, skip, else |B0
bsf datalo,D6 ; set LCD D6 shadow bit |B0
bit3 inDlyCy(bTime-2) ; |B0
btfsc SerPin ; bit = 0? yes, skip, else |B0
bsf datalo,D7 ; set LCD D7 shadow bit |B0
;
; collect hi nibble in 'datahi' (LCD data bits b4..b7)
;
bit4 inDlyCy(bTime-2) ; |B0
btfsc SerPin ; bit = 0? yes, skip, else |B0
bsf datahi,D4 ; set LCD D4 shadow bit |B0
bit5 inDlyCy(bTime-2) ; |B0
btfsc SerPin ; bit = 0? yes, skip, else |B0
bsf datahi,D5 ; set LCD D5 shadow bit |B0
bit6 inDlyCy(bTime-2) ; |B0
btfsc SerPin ; bit = 0? yes, skip, else |B0
bsf datahi,D6 ; set LCD D6 shadow bit |B0
bit7 inDlyCy(bTime-2) ; |B0
btfsc SerPin ; bit = 0? yes, skip, else |B0
bsf datahi,D7 ; set LCD D7 shadow bit |B0
;
; if(datahi == 0x0F) // if data == 0xF0..0xFF
; { datahi.E = 0; // don't write LCD (E bit off)
; datalo.E = 0; // don't write LCD (E bit off)
; } //
; gpio = rsflag; // charge or drain RS cap
; delay_us(3); //
; gpio = datahi; // setup D4..D7 and E=1
; gpio.E = 0; // E = 0 (write LCD)
; gpio = rsflag; // charge or drain RS cap
; delay_us(3); //
; gpio = datalo; // setup D4..D7 and E=1
; gpio.E = 0; // E = 0 (write LCD)
;
; rsflag ^= 1<<RS; // toggle rs flag
; if(datahi.E) // if not 0xFE character
; rsflag = 1<<RS; // reset rs flag (RS=1)
;
wrhi
movf rsflag,W ; the RS bit flag & mask |B0
movwf GPIO ; charge or drain RS cap |B0
movlw b'00110111' ; mask for datahi = 0x0F |B0 --
xorwf datahi,W ; is data == 0xF0..0xFF? |B0 --
skpnz ; no, skip, else |B0 --
bcf datahi,E ; don't write LCD |B0 --
skpnz ; no, skip, else |B0 --
bcf datalo,E ; don't write LCD |B0 --
movf datahi,W ; get data 7..4 bits |B0
movwf GPIO ; setup D4..D7, E=1 |B0
bcf GPIO,E ; E = 0 (write LCD) |B0
wrlo
movf rsflag,W ; the RS bit flag & mask |B0
movwf GPIO ; charge or drain RS cap |B0
movlw 1<<RS ; |B0 --
xorwf rsflag,F ; toggle RS flag & mask |B0 --
btfsc datahi,E ; if datahi not 0x0F |B0 --
iorwf rsflag,F ; reset RS flag & mask (RS=1) |B0 --
goto $+1 ; |B0 --
movf datalo,W ; get data 3..0 bits |B0
movwf GPIO ; setup D4..D7, E = 1 |B0
bcf GPIO,E ; E = 0 (write LCD) |B0
stop
inDlyCy(bTime-24) ; during middle of stop bit |B0
movf GPIO,W ; clear IOC mismatch |B0
bcf INTCON,GPIF ; clear IOC interrupt flag |B0
retfie ; |B0
;******************************************************************
; main *
;******************************************************************
;
; void main()
; {
; cmcon0 = 7; // comparator off
; gpio = 0; // clear gpio output latches
; trisio = 0b00001000; // gp3 input, others outputs
; osccon = 0b01110000; // setup intosc for 8 MHz
; while(osccon.HTS == 0); // wait for osc stable
;
Main
movlw b'00000111' ; |B0
movwf CMCON0 ; comparator off |B0
clrf GPIO ; clear GPIO output latches |B0
bsf STATUS,RP0 ; bank 1 |B1
; clrf ANSEL ; no analog, all digital |B1
movlw b'00001000' ; GP3 input, all others outputs |B1
movwf TRISIO ; |B1
movwf IOCA ; enable IOC for GP3 'SerPin' |B1
movlw b'01110000' ; |B1
movwf OSCCON ; 8-mhz INTOSC system clock |B1
stable btfss OSCCON,HTS ; oscillator stable? |B1
goto stable ; no, branch, else |B1
;
; HD44780 "initialize by instruction" for 4-bit interface mode
;
bcf STATUS,RP0 ; bank 0 |B0
DelayCy(50*msecs) ; LCD 50 msec 'power up' reset |B0
movlw 0x03 ; hi nibble of "8-bit" command |B0
call PutNyb ; step 1 (send nibble only) |B0
DelayCy(4*msecs) ; required 4 msec delay |B0
movlw 0x03 ; |B0
call PutNyb ; step 2 (send nibble only) |B0
DelayCy(160*usecs) ; required 160-us delay |B0
movlw 0x03 ; |B0
call PutNyb ; step 3 (send nibble only) |B0
DelayCy(160*usecs) ; required 160-us delay |B0
movlw 0x02 ; hi nibble of "4-bit" command |B0
call PutNyb ; select 4-bit interface mode |B0
DelayCy(160*usecs) ; required 160-us delay |B0
;
; now in 4-bit interface mode. ok to send full bytes.
;
movlw 0x28 ; 4-bit, 2-lines, 5x7 font |B0
call PutCmd ; send "function set" command |B0
movlw 0x0C ; display on, cursor & blink off |B0
call PutCmd ; send "display on/off" command |B0
movlw 0x06 ; cursor inc, shift off |B0
call PutCmd ; send "entry mode set" command |B0
movlw 0x01 ; clear display (1.53-msecs) |B0
call PutCmd ; send "entry mode set" command |B0
DelayCy(1530*usecs) ; 1.53 msec delay for "clear" |B0
;
; // setup RA3 'SerPin' for interrupt-on-change
;
; rsflag = 1<<RS; // set RS flag & mask
; while(SerPin == 0); // wait for hi level on GP3
; temp = gpio; // clear any mismatch condx
; intcon.RAIF = 0; // clear IOC interrupt flag
; ioca.IOCA3 = 1; // eanble IOC for GP3 'SerPin'
; intcon.GIE = 1; // enable global interrupts
;
; while(1); // loop forever
; }
;
bcf STATUS,RP0 ; bank 0 |B0
movlw 1<<RS ; |B0
movwf rsflag ; set RS flag & mask (RS=1) |B0
btfss SerPin ; stop state? yes, skip, else |B0
goto $-1 ; loop (wait) |B0
movf GPIO,W ; clear GPIO mismatch condition |B0
bcf INTCON,GPIF ; clear IOC interrupt flag |B0
bsf INTCON,GIE ; enable global interrupts |B0
bsf INTCON,GPIE ; enable 'IOC' interrupts |B0
loop
goto loop ; loop forever |B0
;******************************************************************
; low level lcd subroutines *
;******************************************************************
PutCmd ; entry point for "cmd" data
clrc ; RS = 0 (command) |B?
skpnc ; skip unconditionally |B?
PutDat ; entry point for "dat" data
setc ; RS = 1 (data) |B?
banksel temp ; bank 0 |B0
movwf temp ; save WREG data byte |B0
swapf temp,W ; |B0
call PutNyb ; send hi nybble |B0
swapf temp,W ; |B0
call PutNyb ; send lo nybble |B0
DelayCy(50*usecs) ; required between writes |B0
return ; |B0
PutNyb
movwf temp ; |B0
bcf GPIO,RS ; RS = 0 (discharge RS cap) |B0
skpnc ; RS = 0? yes, skip, else |B0
bsf GPIO,RS ; RS = 1 (charge RS cap) |B0
movlw 1<<E ; |B0
btfsc temp,0 ; |B0
iorlw 1<<D4 ; |B0
btfsc temp,1 ; b1 = 0? yes, skip, else |B0
iorlw 1<<D5 ; |B0
btfsc temp,2 ; b2 = 0? yes, skip, else |B0
iorlw 1<<D6 ; |B0
btfsc temp,3 ; b3 = 0? yes, skip, else |B0
iorlw 1<<D7 ; |B0
movwf GPIO ; LCD D4..D7 = data1, E = 1 |B0
bcf GPIO,E ; E = 0 |B0
return ; |B0
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; K8LH DelayCy() 16-bit uDelay subroutine ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
nop ; entry for (delay-11)%5 == 4 |B0
nop ; entry for (delay-11)%5 == 3 |B0
nop ; entry for (delay-11)%5 == 2 |B0
nop ; entry for (delay-11)%5 == 1 |B0
uDelay addlw -1 ; subtract 5 cycle loop time |B0
skpc ; borrow? no, skip, else |B0
decfsz delayhi,F ; done? yes, skip, else |B0
goto uDelay ; do another loop |B0
return ; return with C = Z = 0 |B0
;******************************************************************
end
Bookmarks