PIC16F628A LCD driver (assembly)
On the internet a found a nice blog about building a capacitance meter with the PIC16F628A. But in the blog a 16 Mhz crystal was used which I didn't have. I only had 20 Mhz crystals. Because only a HEX file was supplied, I could not combine this with the 20 Mhz crystal. So I decided to write my own code.
Step 1 was writing code for driving the LCD-module. Step 2 was writing the code for measuring and calculating the capacitance value. Unfortunately I didn't manage to complete step 2. Due to the version of the HI-TEC C-compiler I used and the amount of available memory, I got out of memory errors while compiling. The PIC16F628a has only:
2048 x 14 bits words (flash) 224 bytes (ram) So these were my options:
buy a license for the HI-TEC C-compiler switch to assembly. use a PIC16F648a which has more memory Buy a 16 Mhz crystal and just use the HEX file. Because I'm eager to learn and understand things, but didn't want to spend money on the compiler-license, I decided to switch to assembly. First let me show you the scheme of the LCD-driver, see picture below
PIC16f628a lcd driver
My experience with writing assembly code is that you need to add comment on almost every line in order to understand it when you're debugging or reading it after a few weeks. Let me first start with the configuration part. For the 20 Mhz crystal you need the high speed oscillator configuration: _HS_OSC
For binary value presentation I use defines with hex values. For example:
#define b01010101 0x55
In code I can now use b01010101 which makes more sense when you are configuring certain registers because you can see which bits are set and which are not.
My variables are located in RAM of bank 0 (starting at address 0x20). I use a MY_BITS variable for storing Boolean values in order to save memory (you can store 8 Boolean values in one byte). Instead of an array of bytes, I use 8 separate byte values for storing the LCD driver data. As long as there's data available, data is written and the bytes are shifted 'one down' and the last byte is cleared.
#include <E:\Elec\PicProg2009\Include\P16F628A.INC>
__CONFIG(_HS_OSC & _WDT_OFF & _MCLRE_OFF & _BOREN_OFF & _LVP_OFF & _DATA_CP_OFF & _CP_OFF);
org 0x0000
goto MAIN_INIT
org 0x0004
goto MY_INTERRUPT_HANDLER
;N.B. all sub routines must return with BANK 0 selected
;N.B. lower and higher nibbles are LCD nibbles, but they are stored in two bytes because bytes also contain status of LCD_E and LCD_RS
#define LCDE 0 ;LCD Enable bit
#define WRT 1 ;Ready for writing bit
#define SKF 2 ;Skip frame bit
#define U4B 3 ;Write upper four bits (high nibble)
;PORTB wiring
#define RB0 0 ;RB0 => RS is wired to RB0
#define RB1 1 ;RB1 => E is wired to RB1
;binary constants
#define b00000000 0x00
#define b00000001 0x01
#define b00001111 0x0f
#define b00010000 0x10
#define b00011000 0x18
#define b00011111 0x1F
#define b00100000 0x20
#define b01000000 0x40
#define b01010101 0x55
#define b01100000 0x60
#define b01110000 0x70
#define b10000000 0x80
#define b10101010 0xAA
#define b11000000 0xC0
#define b11110000 0xF0
#define b11111111 0xFF
;Timer constants
#define TMR_DELTA 0f0 ;start at: 255 - x, overflow on 255+1
;RAM data variables bank 0
cblock 0x20
MY_BITS
W_TEMP
STATUS_TEMP
LCD_BUF_00
LCD_BUF_01
LCD_BUF_02
LCD_BUF_03
LCD_BUF_04
LCD_BUF_05
LCD_BUF_06
LCD_BUF_07
CNT_VAR
CNT0
CNT1
CNT2
CNT3
CNT_RW ;number of items in LCD_BUF
LCD_TMP ;buffer for tmp lcd data
endc;
One of the important aspects of driving the LCD-module is timing. The PIC16F628a has 3 timers. For the LCD-driver I use timer 0. The LCD-module needs an oscillator frequency (E) between 150 kHz and 350 kHz. Because the signal on the E-pin is toggled every timer 0 overflow, we need two timer 0 cycles for one LCD-driver cycle. So we need a frequency that is twice the specified range: 300 kHz... 700 kHz. Timer 0 runs at fosc/4 = 20 Mhz / 4 = 5 Mhz. It overflows on transition of 255 to 0. So by default it would run at a frequency of 78k125 Hz. In order to have it run within the given range, we need to initialize timer 0 at a value somewhere between 190 and 248. Timer 0 runs from x...255, then overflows.
248 means 8 timer ticks for each overflow. T = 8 x 0.0000002 sec = 0.0000016 sec. Which is approximately 625 kHz
190 means 16 timer ticks for each overflow. T = 16 x 0.0000002 sec = 0.0000032 sec. Which is approximately 312.5 kHz
So a value of 240 (0xf0) should do.
The method 'start_lcd_timer' sets the TMR0 clock source to internal instruction cycle clock, no prescalar, starts the timer and enables the timer 0 overflow interrupt.
start_lcd_timer:
;--- BANK 1 ---
bsf STATUS, RP0 ;select bank 1
;disable all interrupts
clrf INTCON ;GIE PEIE T0IE INTE RBIE T0IF INTF RBIF
clrf PIE1 ;EEIE CMIE RCIE TXIE xxx CCP1IE TMR2IE TMR1IE
bsf PCON, OSCF ;4 Mhz
movlw b00011111 ;
movwf TRISA ;TRISA, bank 1
movlw b00000000 ;RB0...RB7 as outputs
movwf TRISB ;TRISB, bank 1
;bit 7 RBPU: PORTB Pull-up Enable bit
; 1 = PORTB pull-ups are disabled
; 0 = PORTB pull-ups are enabled by individual port latch values
;bit 6 INTEDG: Interrupt Edge Select bit
; 1 = Interrupt on rising edge of RB0/INT pin
; 0 = Interrupt on falling edge of RB0/INT pin
;bit 5 T0CS: TMR0 Clock Source Select bit
; 1 = Transition on RA4/T0CKI/CMP2 pin
; 0 = Internal instruction cycle clock (CLKOUT)
;bit 4 T0SE: TMR0 Source Edge Select bit
; 1 = Increment on high-to-low transition on RA4/T0CKI/CMP2 pin
; 0 = Increment on low-to-high transition on RA4/T0CKI/CMP2 pin
;bit 3 PSA: Prescaler Assignment bit
; 1 = Prescaler is assigned to the WDT
; 0 = Prescaler is assigned to the Timer0 module
;bit 2-0 PS<2:0>: Prescaler Rate Select bits
; 0 = nPBPU INTEDG T0CS T0SE PSA PS<2:0>: Prescaler Rate Select bits
movlw b00011000 ;pull-ups, internal oscillator, WDT prescalar
movwf OPTION_REG ;OPTION_REG, bank 1
;--- BANK 0 ---
bcf STATUS, RP0 ;select bank 0
movlw U4B;
movwf MY_BITS;
movlw TMR_DELTA ;set initial value
movwf TMR0; ;TMR0, bank 0
clrf PORTA ;PORTA, bank 0
clrf PORTB ;PORTB, bank 0
bsf INTCON, T0IE ;INTCON enable timer 0 overflow
bsf INTCON, GIE ;enable interrupts
return
All interrupts are handled in MY_INTERRUPT_HANDLER.
First the content of the STATUS and the W-registers are saved. Then the current interrupt is handled. Finally the STATUS and W registers are restored to the old state.
MY_INTERRUPT_HANDLER:
;SAVING THE STATUS AND W REGISTERS IN RAM
movwf W_TEMP ;copy W to temp register, could be in any bank
swapf STATUS,W ;swap status to be saved into W
bcf STATUS,RP0 ;change to bank 0 regardless of current bank
movwf STATUS_TEMP ;save status to bank 0 register
;--- ISR ---
bsf STATUS, RP0 ;select bank 1
btfsc INTCON, T0IF ;timer0 overflow? skip if clear
call timer0_overflow ;
; more interrupt handling can be added here
;--- end IF USR ---
swapf STATUS_TEMP,W ;swap STATUS_TEMP register
;into W, sets bank to original state
movwf STATUS ;move W into STATUS register
swapf W_TEMP,F ;swap W_TEMP
swapf W_TEMP,W ;swap W_TEMP into W
retfie ;return from interrupt
end
The timer 0 interrupts are handled in timer0_overflow.
It is responsible for oscillator frequency of the LCD (pin 6). It alternates between enabled=1 and enabled=0. It checkes whether there's data to be written If the high nibble or the low nibble of the data has to be written. It skipes frames between every two writes (LCD is written in 4 bit mode, so two writes make one LCD byte. After each LCD byte, the LCD module needs some time to process the data, therefore the skipframe is added)
timer0_overflow:
;--- BANK 0 ---
bcf STATUS, RP0
bcf STATUS, RP1
t0if_set_data:
;--- BANK 0 ---
bcf STATUS, RP0 ;select bank 0
bcf STATUS, RP1 ;select bank 0
btfss MY_BITS, SKF ;Bit test flag, skip if set 'Skip frame'
goto t0if_nSKF ;SKF is not set, normal mode
bcf MY_BITS, SKF ;clear SKF-flag and go to toggle LCD_E
goto t0if_toggle_lcd_e
t0if_nSKF:
btfss MY_BITS, LCDE ;Bit test flag, skip if set LCD_E == LCD enable bit is set
goto t0if_reset_lcd_e ;go to reset lcde
btfss MY_BITS, WRT ;Bit test flag, skip if set WRT == ready to write data to LCD
goto t0if_reset_lcd_e ;go to reset lcde
bcf STATUS, C ;clear carry
bcf STATUS, Z ;clear carry
movf CNT_RW, 0 ;check if CNT_RW is 0
addlw b11111111
btfss STATUS, C ;if CNT_RW not is 0, then Carry flag will be set
goto t0if_no_more_data ;go to toggle LCD_E
movf LCD_BUF_00, 0 ;Move content of LCD_BUF_00 to W
movwf PORTB ;Move content of W to PORTB
movf LCD_BUF_01, 0 ;Move LCD_BUF one down, this could probably be optimized
movwf LCD_BUF_00
movf LCD_BUF_02, 0
movwf LCD_BUF_01
movf LCD_BUF_03, 0
movwf LCD_BUF_02
movf LCD_BUF_04, 0
movwf LCD_BUF_03
movf LCD_BUF_05, 0
movwf LCD_BUF_04
movf LCD_BUF_06, 0
movwf LCD_BUF_05
movf LCD_BUF_07, 0
movwf LCD_BUF_06
clrf LCD_BUF_07
decf CNT_RW, 1 ;Decrement CNT_RW and store result in CNT_RW
btfsc MY_BITS, U4B ;Bit test flag, skip if clear
goto t0if_lower_nibble
bsf MY_BITS, SKF ;After writing lower nibble, set skip frames flag. We don't wair for LCD is busy, have build in delay
bsf MY_BITS, U4B ;Next write will be upper nibble of next frame
goto t0if_toggle_lcd_e ;go to toggle LCD_E
t0if_lower_nibble:
bcf MY_BITS, U4B ;Next write will be lower nibble of current frame.
goto t0if_toggle_lcd_e ;go to toggle LCD_E
t0if_no_more_data:
bcf MY_BITS, LCDE ;clear 'LCD Enable'
bcf PORTB, 1;
goto t0if_toggle_lcd_e
t0if_reset_lcd_e:
bcf PORTB, 1 ;clear PORTB RB1, bank 0
bcf MY_BITS, LCDE ;clear LCDE
t0if_toggle_lcd_e:
btfss MY_BITS, LCDE ;if LCDE is set, set RB1 and clear LCDE
goto t0if_set_lcd_e; ;else clear RB1 and set LCDE
bsf PORTB, 1 ;set PORTB RB1, bank 0
bcf MY_BITS, LCDE ;clear LCDE
t0if_set_lcd_e:
bcf PORTB, 1 ;set PORTB RB1, bank 0
bsf MY_BITS, LCDE ;clear LCDE
movlw TMR_DELTA ;set initial value
movwf TMR0; ;TMR0, bank 0
bcf INTCON, T0IF ;reset interrupt T0IF
return
Additional methods:
init_write_mode: clears the WRT-bit to indicate that no data can is available to be written set_char: splits the character value stored in LCD_TMP in high and low nibble and writes the data to LCD
init_write_mode:
bcf MY_BITS, WRT ;not ready for writing data to LCD
movlw 0x00
movwf CNT_RW ;
bsf MY_BITS, U4B ;upper four bits
return
set_position: sets the cursor to the position stored in LCD_TMP
set_position:
call init_write_mode
;Set DDRAM Address 0
;RS RW DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
;0 0 1 0 AC5 AC4 AC3 AC2 AC1 AC0
movf LCD_TMP, 0 ;write LCD_TMP to W
andlw b11110000 ;get high nibble and move result in W
movwf LCD_BUF_00 ;write high nible to LCD_BUF
movlw b10000000 ;set DB7 to 1
IORWF LCD_BUF_00, 1 ;write back to LCD_TMP
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movf LCD_TMP, 0 ;write LCD_TMP to W
andlw b00001111 ;get lower nibble and move result in W
movwf LCD_BUF_01 ;
bcf STATUS, C ;clear carry
rlf LCD_BUF_01, 1 ;shift left and store result in LCD_BUF
rlf LCD_BUF_01, 1
rlf LCD_BUF_01, 1
rlf LCD_BUF_01, 1
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
bsf MY_BITS, WRT ;ready for writing data bank 0
movlw 0x80 ;
movwf CNT_VAR ;move w to CNT_VAR bank 0
call delay_msec;
return
set_char: splits the character value stored in LCD_TMP in high and low nibble and writes the data to LCD
set_char:
call init_write_mode
movf LCD_TMP, 0 ;write LCD_TMP to W
andlw b11110000 ;get high nibble and move result in W
movwf LCD_BUF_00 ;write high nible to LCD_BUF
movlw b00000001 ;or with 1 in order to set LCD_RS
iorwf LCD_BUF_00, 1 ;Inclusive OR and write back to LCD_BUF_00
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movf LCD_TMP, 0 ;
andlw b00001111 ;get lower nibble and move result in W
movwf LCD_BUF_01
bcf STATUS, C ;clear carry
rlf LCD_BUF_01,1 ;shift left and store result in LCD_BUF
rlf LCD_BUF_01,1
rlf LCD_BUF_01,1
rlf LCD_BUF_01,1
movlw b00000001 ;or with 1 in order to set LCD_RS
iorwf LCD_BUF_01, 1 ;Inclusive OR and write back to LCD_BUF_00
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
bsf MY_BITS, WRT ;ready for writing data bank 0
movlw 0x80 ;
movwf CNT_VAR ;move w to CNT_VAR bank 0
call delay_usec;
return
And some delay methods: delay_sec, delay_msec, delay_usec. These methods wait for the value stored in CNT_VAR sec/msec/usec.
delay_sec: ;20 Mhz
movf CNT_VAR, 0 ;1 cycle, move content of CNT_VAR to W
movwf CNT0 ;1 cycle
delay_sec_cnt0:
movlw 0x60 ;1 cycle 96
movwf CNT1 ;1 cycle
delay_sec_cnt1:
movlw 0x45 ;1 cycle 69
movwf CNT2 ;1 cycle
delay_sec_cnt2:
movlw 0xFA ;1 cycle 250
movwf CNT3 ;1 cycle
delay_sec_cnt3:
decfsz CNT3 ;1 cycle
goto delay_sec_cnt3 ;2 cycles
decfsz CNT2 ;1 cycle
goto delay_sec_cnt2 ;2 cycles
decfsz CNT1 ;1 cycle
goto delay_sec_cnt1 ;2 cycles
decfsz CNT0 ;2 cycles
goto delay_sec_cnt0 ;1 cycle
return
delay_msec: ;20 Mhz
movf CNT_VAR, 0 ;1 cycle, move content of CNT_VAR to W
movwf CNT0 ;1 cycle
delay_msec_cnt0:
movlw 0x28 ;1 cycle 40
movwf CNT1 ;1 cycle
delay_msec_cnt1:
movlw 0x28 ;1 cycle 40
movwf CNT2 ;1 cycle
delay_msec_cnt2:
decfsz CNT2 ;1 cycle
goto delay_msec_cnt2 ;2 cycles
decfsz CNT1 ;1 cycle
goto delay_msec_cnt1 ;2 cycles
decfsz CNT0 ;2 cycles
goto delay_msec_cnt0 ;1 cycle
return
delay_usec: ;20 Mhz
movf CNT_VAR, 0 ;1 cycle, move content of CNT_VAR to W
movwf CNT0 ;1 cycle
delay_usec_cnt0:
nop ;1 cycle
nop ;1 cycle
decfsz CNT0 ;2 cycles
goto delay_usec_cnt0 ;1 cycle
return
An initialization method that
clears all variables sets the operation mode: number of lines and character height switches the LCD-module on sets cursor and blink mode Clears the display
MAIN_INIT:
;--- select bank 0 ---
bcf STATUS, RP0;
bcf STATUS, RP1;
clrf PORTA;
clrf PORTB;
clrf MY_BITS
clrf W_TEMP
clrf STATUS_TEMP
clrf CNT_RW
clrf LCD_BUF_00
clrf LCD_BUF_01
clrf LCD_BUF_02
clrf LCD_BUF_03
clrf LCD_BUF_04
clrf LCD_BUF_05
clrf LCD_BUF_06
clrf LCD_BUF_07
clrf LCD_TMP
clrf CNT_VAR
clrf CNT0
clrf CNT1
clrf CNT2
clrf CNT3
;--- select bank 1 ---
bsf STATUS, RP0
;disable all interrupts
clrf INTCON ;GIE PEIE T0IE INTE RBIE T0IF INTF RBIF
clrf PIE1 ;EEIE CMIE RCIE TXIE xxx CCP1IE TMR2IE TMR1IE
movlw b11000000 ;RA0...RA5 as outputs
movwf TRISA ;TRISA, bank 1
movlw b00000000 ;RB0...RB7 as outputs
movwf TRISB ;TRISB, bank 1
;--- select bank 0 ---
bcf STATUS, RP0 ;
clrf PIR1 ;EEIF CMIF RCIF TXIF xxx CCP1IF TMR2IF TMR1IF
call start_lcd_timer;
;RS => is wired to RB0
;E => is wired to RB1
;Wait for more than 30ms
;after VDD rises to 4.5v
movlw 0x80 ;wait > 30 msec;
movwf CNT_VAR ;CNT_VAR bank 0
call delay_msec ;
;N: 0 = 1-line mode , 1 = 2-line mode
;F: 0 = 5 x 7 dots, 1 = 5 x 10 dots
;
;RS RW DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
;0 0 0 0 1 0 X X X X
;0 0 0 0 1 0 X X X X
;0 0 N F X X X X X X
call init_write_mode;
movlw b00100000 ;
movwf LCD_BUF_00 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movlw b00100000 ;
movwf LCD_BUF_01 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movlw b01000000 ;
movwf LCD_BUF_02 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movlw b01000000 ;
movwf LCD_BUF_03 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
bsf MY_BITS, WRT ;ready for writing data bank 0
movlw 0x80 ;wait >100 msec
movwf CNT_VAR ;CNT_VAR bank 0
call delay_msec ;
;D: 0 display off, 1 display on
;C: 0 cursor off, 1 cursor on
;B: 0 blink off, 1 blink on
;RS RW DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
;0 0 0 0 0 0 X X X X
;0 0 1 D C B X X X X
call init_write_mode;
movlw b00000000 ;
movwf LCD_BUF_00 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movlw b11000000 ;
movwf LCD_BUF_01 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
bsf MY_BITS, WRT ;ready for writing data bank 0
movlw 0x80 ;wait >50 msec
movwf CNT_VAR ;move w to CNT_VAR bank 0
call delay_msec;
;Clear Display
;RS RW DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
;0 0 0 0 0 0 X X X X
;0 0 0 0 0 1 X X X X
call init_write_mode;
movlw b00000000 ;
movwf LCD_BUF_00 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movlw b00010000 ;
movwf LCD_BUF_01 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
bsf MY_BITS, WRT ;ready for writing data bank 0
movlw 0x80 ;wait >50 msec
movwf CNT_VAR ;move w to CNT_VAR bank 0
call delay_msec ;
;I/D: 0 decrement mode, 1 increment mode
;SH: 0 entire shift off, 1 entire shift on
;RS RW DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
;0 0 0 0 0 0 X X X X
;0 0 0 1 I/D SH X X X X
call init_write_mode;
movlw b00000000 ;
movwf LCD_BUF_00 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
movlw b01100000 ;
movwf LCD_BUF_01 ;move w to LCD_BUFxx bank 0
incf CNT_RW, 1 ;RW index bank 0, write result back in CNT_RW
bsf MY_BITS, WRT ;ready for writing data bank 0
movlw 0x80 ;wait >100 msec
movwf CNT_VAR ;move w to CNT_VAR bank 0
call delay_msec;
And a main loop that is responsible for the actual data that is displayed
MAIN_LOOP:
movlw 0x00 ;set LCD position to 0
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_position
movlw 0x48 ;'H'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x61 ;'a'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x6C ;'l'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x6C ;'l'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x6F ;'o'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x20 ;' '
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x4D ;'M'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x6F ;'o'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x6E ;'n'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x69 ;'i'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x71 ;'q'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x75 ;'u'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x65 ;'e'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x01 ;wait 1 sec
movwf CNT_VAR ;move w to CNT_VAR bank 0
call delay_sec;
movlw 0x00 ;set LCD position to 0
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_position
movlw 0x4D ;'M'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x6F ;'o'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x69 ;'i'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x20 ;' '
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x49 ;'I'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x72 ;'r'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x69 ;'i'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x6E ;'n'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x61 ;'a'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x20 ;' '
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x20 ;' '
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x20 ;' '
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x20 ;' '
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x01 ;wait 1 sec
movwf CNT_VAR ;move w to CNT_VAR bank 0
call delay_sec;
movlw 0x00 ;set LCD position to 0
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_position
movlw 0x48 ;'H'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x6F ;'o'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x69 ;'i'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x20 ;' '
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x4a ;'J'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x61 ;'a'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x72 ;'r'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x6F ;'o'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x6D ;'m'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x69 ;'i'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x72 ;'r'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x20 ;' '
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x20 ;' '
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x01 ;wait 1 sec
movwf CNT_VAR ;move w to CNT_VAR bank 0
call delay_sec;
movlw 0x00 ;set LCD position to 0
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_position
movlw 0x77 ;'w'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x77 ;'w'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x77 ;'w'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x2e ;'.'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x66 ;'f'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x73 ;'s'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x61 ;'a'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x79 ;'y'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x73 ;'s'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x2e ;'.'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x65 ;'e'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x75 ;'u'
movwf LCD_TMP ;move w to LCD_TMP bank 0
call set_char
movlw 0x01 ;wait 1 sec
movwf CNT_VAR ;move w to CNT_VAR bank 0
call delay_sec;
goto MAIN_LOOP
Below you can see the final result. So now I can start writing the code for measuring the capacitance.
16f628 lcd driver at work
The source file can be downloaded
here