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
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
    16f628 lcd driver at work

    The source file can be downloaded here

    Back to List

    All form fields are required.
    A confirmation mail for the comments will be send to you.