Help with I2C between pic 16f690 and LCD I2C interfase LCM1602

MMcLaren

Joined Feb 14, 2010
861
Hi Cristian. I've had these "recipe" errors in MPLABX before, too. I seem to recall having to delete the project and start anew.

I just assembled the program in MPLAB 8.92 (absolute mode) and it works fine.

Good luck, Sir. Cheerful regards, Mike, K8LH

Code:
;******************************************************************
;                                                                 *
;   Filename: 16F690_LCD_I2C_Backpack_x2.asm                      *
;     Author: Mike McLaren, K8LH                                  *
;    (C)2018: Micro Application Consultants                       *
;       Date: 31-Jul-2018                                         *
;                                                                 *
;                                                                 *
;   16F690 bit-banged I2C demo for PCF8574 I2C LCD Backpack and   *
;   HD44780 type LCD display.  Only basic I2C 'start', 'write',   *
;   and 'stop' routines included (enough to drive the backpack)   *
;   as well as LCD 'init', 'putCmd', and 'putDat' routines.       *
;                                                                 *
;                                                                 *
;      MPLab: 8.92    (tabs=8)          Target Device: 16F690     *
;      MPAsm: 5.51    (absolute mode)                             *
;                                                                 *
;******************************************************************

       #include <P16F690.INC>
        list st=off
        errorlevel -302
        radix dec

  __config _FCMEN_OFF& _IESO_OFF& _MCLRE_OFF& _WDT_OFF& _INTOSCIO

;  _FCMEN_OFF           ; -- fail safe clock monitor enable off
;  _IESO_OFF            ; -- int/ext switch over enable off
;  _BOR_ON              ; default, brown out reset on
;  _CPD_OFF             ; default, data eeprom protection off
;  _CP_OFF              ; default, program code protection off
;  _MCLR_OFF            ; -- use MCLR pin as digital input
;  _PWRTE_OFF           ; default, power up timer off
;  _WDT_OFF             ; -- watch dog timer off
;  _INTOSCIO            ; -- internal osc, OSC1 and OSC2 I/O

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  variables
;
        cblock  0x20
workvar                         ; putCmd(), putDat(), putNyb()
        endc
    
        cblock  0x70
loopctr                         ; iic_write()
payload                         ; iic_write()
delayhi                         ; DelayCy() subsystem variable
        endc
rsflag  equ     delayhi         ; putCmd(), putDat(), putNyb()

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  define 'scl' and 'sda' port and pins
;
iic_tris equ    TRISC           ; TRIS reg for scl/sda pins
iic_port equ    PORTC           ; PORT reg for scl/sda pins
sclndx  equ     RC0             ; index for scl pin (RC0)
sdandx  equ     RC1             ; index for sda pin (RC1)
;
;  LCD DDRAM address constants
;
line1   equ     128+0           ; LCD "line 1" command
line2   equ     128+64          ; LCD "line 2" command

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  LCD helper macros
;
putNyb  macro   param           ;
        movlw   param           ;                                 |B0
        call    lcdNyb          ;                                 |B0
        endm

putCmd  macro   param           ;
        movlw   param           ;                                 |B0
        call    lcdCmd          ;                                 |B0
        endm

putDat  macro   param           ;
        movlw   param           ;                                 |B0
        call    lcdDat          ;                                 |B0
        endm

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  K8LH DelayCy() subsystem macro generates four instructions      ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        radix dec
clock   equ     8               ; 4, 8, 12, 16, 20 (MHz), etc.
usecs   equ     clock/4         ; cycles/microsecond multiplier
msecs   equ     usecs*1000      ; cycles/millisecond multiplier
dloop   equ     5               ; loop size, 5 to 8 cycles
;
;  -- loop --  -- delay range --  -- memory overhead ----------
;  5-cyc loop, 11..327690 cycles,  9 words (+4 each macro call)
;  6-cyc loop, 11..393226 cycles, 10 words (+4 each macro call)
;  7-cyc loop, 11..458762 cycles, 11 words (+4 each macro call)
;  8-cyc loop, 11..524298 cycles, 12 words (+4 each macro call)
;
DelayCy macro   cycles          ; range, see above
    if (cycles<11)|(cycles>(dloop*65536+10))
        error " DelayCy range error "
    else
        movlw   high((cycles-11)/dloop)+1
        movwf   delayhi
        movlw   low ((cycles-11)/dloop)
;       rcall   uLoop-(((cycles-11)%dloop)*2)    ; (18F version)
        call    uLoop-((cycles-11)%dloop)        ; (16F version)
    endif
        endm

;******************************************************************
;  reset vector                                                   *
;******************************************************************
        org     0x000
v_res
        clrf    STATUS          ; force bank 0 and IRP = 0        |B0
        goto    setup           ;                                 |B0

;******************************************************************
;  interrupt vector                                               *
;******************************************************************
        org     0x004
v_irq

;******************************************************************
;  main setup                                                     *
;******************************************************************

setup
        bcf     STATUS,RP0      ;                                 |
        bsf     STATUS,RP1      ; bank 2                          |B2
        clrf    ANSEL           ; turn off analog pin functions   |B2
        clrf    ANSELH          ;                                 |B2
        bcf     STATUS,RP1      ; bank 0                          |B0
        bsf     STATUS,RP0      ; bank 1                          |B1

        movlw   b'01110000'     ; setup INTOSC for 8-MHz          |B1
        movwf   OSCCON          ;                                 |B1
        btfss   OSCCON,HTS      ; osc stable? yes, skip, else     |B1
        goto    $-1             ; test again                      |B1

        clrf    TRISC           ; RC5..RC0 all outputs            |B1
        clrf    TRISB           ; RB7..RB4 all outputs            |B1
        clrf    TRISA           ; all outputs, except RA3         |B1
        bcf     STATUS,RP0      ; bank 0                          |B0
        clrf    PORTC           ;                                 |B0
        clrf    PORTB           ;                                 |B0
;
;  setup I2C interface, scl = RC0, sda = RC1
;
        bsf     STATUS,RP0      ; bank 1                          |B1
        bsf     iic_tris,sclndx ; make scl pin input              |B1
        bsf     iic_tris,sdandx ; make sda pin input              |B1
        bcf     STATUS,RP0      ; bank 0                          |B0
;       bcf     iic_port,sclndx ; scl output latch = 0            |B0
;       bcf     iic_port,sdandx ; sda output latch = 0            |B0
;
;  LCD "initialize by instruction" procedure for 4-bit interface
;
        DelayCy(30*msecs)       ; delay 30-msecs after power up   |B0
;       clrf    rsflag          ; rs = 0 (LCD command)            |B0
        putNyb (0x30)           ; function set: 8-bit             |B0
        DelayCy(5*msecs)        ; required 5-msec delay           |B0
        putNyb (0x30)           ; function set: 8-bit             |B0
;  n/a  DelayCy(160*usecs)      ; required 160-usec delay         |B0
        putNyb (0x30)           ; function set: 8-bit             |B0
;  n/a  DelayCy(160*usecs)      ; required 160-usec delay         |B0
        putNyb (0x20)           ; function set: 4-bit             |B0
;  n/a  DelayCy(160*usecs)      ; required 160-usec delay         |B0
;
;  now we're in 4-bit mode and can handle 8-bit transactions by
;  sending both hi & lo nibbles using putCmd & putDat macros.
;
        putCmd (0x28)           ; 4-bit, 2-lines, 5x7 font        |B0
        putCmd (0x08)           ; display, cursor, blink all off  |B0
        putCmd (0x01)           ; clear display                   |B0
        DelayCy(1530*usecs)     ; required 1.53-msec delay        |B0
        putCmd (0x06)           ; cursor inc, shift off           |B0
        putCmd (0x0C)           ; display on, leave cursor off    |B0

;******************************************************************
;  main loop                                                      *
;******************************************************************

loop
        putCmd (line1+2)        ; LCD line 1, column 3            |B0
        putDat ('H')            ;                                 |B0
        putDat ('e')            ;                                 |B0
        putDat ('l')            ;                                 |B0
        putDat ('l')            ;                                 |B0
        putDat ('o')            ;                                 |B0
        putDat (' ')            ;                                 |B0
        putDat ('W')            ;                                 |B0
        putDat ('o')            ;                                 |B0
        putDat ('r')            ;                                 |B0
        putDat ('l')            ;                                 |B0
        putDat ('d')            ;                                 |B0
        putDat ('!')            ;                                 |B0

        goto    $               ;

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  low-level LCD drivers for PCF8574 based I2C LCD backpack       ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

lcdCmd                          ; entry point for "cmd" data
        clrf    rsflag          ; wreg = data, RS = 0 (cmd)       |B0
        skpz                    ; skip                            |B0
lcdDat                          ; entry point for "dat" data
        bsf     rsflag,0        ; wreg = data, RS = 1 (data)      |B0
        call    lcdNyb          ; send hi nybble                  |B0
        swapf   workvar,W       ; send lo nybble                  |B0
;
;  ~~ 'lcdNyb' sequence ~~~~~~~~~      < PCF8574 data format >
;  () I2C 'start' + I2C 'address'      P0(b0) -> LCD 'RS'
;  () write nibble, E = 1              P1(b1) -> LCD 'RW' (0)
;  () write nibble, E = 0              P2(b2) -> LCD 'E'
;  () I2C 'stop'                       P3(b3) -> LCD Backlight
;                                      P4(b4) -> LCD 'D4'
;   ~617-usecs (isochronous) for       P5(b5) -> LCD 'D5'
;   each lcdCmd and lcdDat call.       P6(b6) -> LCD 'D6'
;                                      P7(b7) -> LCD 'D7'
lcdNyb
        movwf   workvar         ; save temporarily                |B0
        movlw   0x4E            ; PCF8574 I2C address + 0 (wr)    |B0
        call    iic_start       ; I2C 'start' + 'address' + 'rw'  |B0
        movf    workvar,W       ;                                 |B0
        andlw   0xF0            ; use left nybble (b7..b4) bits   |B0
        btfsc   rsflag,0        ; RS = 0? yes, skip, else         |B0
        iorlw   b'00000001'     ; RS(b0) = 1                      |B0
        iorlw   b'00001100'     ; Backlight(b3) = 1, E(b2) = 1    |B0
        call    iic_write       ; data + bl, en, rw, rs bits      |B0
        xorlw   b'00000100'     ; clr E bit (b2)                  |B0
        call    iic_write       ; data + bl, en, rw, rs bits      |B0
        goto    iic_stop        ; I2C 'stop'                      |B0

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  bit-banged I2C backpack macros and low-level driver functions  ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

sda     macro   param           ; 5 cycles (2.5-us @ 8-MHz clock)
        bsf     STATUS,RP0      ; bank 1                          |B1
    if param == 0
        bcf     iic_tris,sdandx ; sda = 0                         |B1
    else
        bsf     iic_tris,sdandx ; sda = 1                         |B1
    endif
        movf    PCL,F           ; 2 cycles                        |B1
        nop                     ;                                 |B1
        endm
;
;  scl(0) -> output, save wreg to 'payload' variable, 3 cycles
;  scl(1) -> hi-z input, clear carry flag, 5 cycles
;
scl     macro   param           ;
        bsf     STATUS,RP0      ; bank 1                          |B1
    if param == 0               ; scl(0) uses 3 cycles
        movwf   payload         ; payload = wreg                  |B1
        bcf     iic_tris,sclndx ; scl = 0 (output '0')            |B1
    else                        ; scl(1) uses 5 cycles
        movf    PCL,F           ; 2 cycles                        |B1
        clrc                    ; 1 cycle                         |B1
        bsf     iic_tris,sclndx ; scl = 1 (hi-z input)            |B1
    endif
        endm

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  I2C 'Start' condition. WREG = I2C address << 1 + r/w (1 or 0)
;
iic_start
        scl(1)                  ; assure 'idle' condition         |B1
        sda(1)                  ;  "                              |B1
        sda(0)                  ; sda = 0                         |B1
;       scl(0)                  ; scl = 0                         |B1
;       bcf     STATUS,RP0      ; bank 0                          |B0
;       return                  ;                                 |B0

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  I2C 'write' (Rd/Wr) byte.  maintain 10 cycle (5.0-us) 'scl'
;  pin transitions for a 100-kHz I2C clock.
;
;  enter with wreg = write value or 0xFF if reading a byte
;  from slave device.  wreg is saved to 'payload' variable in
;  the scl(0) macro.
;
;  190 cycles (95.0-us), including call & return (isochronous)
;
iic_write
        clrf    loopctr         ; loopctr = 8 (preserve wreg)     |Bx
        bsf     loopctr,3       ;  "                              |Bx
;
;  write and read 8 bits.  send 0xFF to read and return 8-bits
;  from the I2C slave.  the return value is collected directly
;  from the 'sda' pin so when writing to the slave device the
;  return value is the same as the value that was sent.
;
rdwrbit
        bsf     STATUS,RP0      ; bank 1                          |B1
        movwf   payload         ;                                 |B1
        bcf     iic_tris,sclndx ; scl = 0                       < |B1
        movf    iic_tris,W      ;                                 |B1
        iorlw   1<<sdandx       ; sda = 1                         |B1
        btfss   payload,7       ; msb = 1? yes, skip, else        |B1
        xorlw   1<<sdandx       ; sda = 0                         |B1
        movwf   iic_tris        ;                               < |B1
        movf    PCL,F           ; 2 cycles                        |B1
        rlf     payload,F       ;                                 |B1
        bcf     payload,0       ;                                 |B1
        bsf     iic_tris,sclndx ; scl = 1                       < |B1
        bcf     STATUS,RP0      ; bank 0                          |B0
        movf    payload,W       ; shift a '0' bit into payload    |B0
        btfsc   iic_port,sdandx ; SDA pin = 0? yes, skip, else    |B0
        iorlw   b'00000001'     ; make it a '1'                   |B0
        decfsz  loopctr,F       ; done? yes, skip, else           |B0
        goto    rdwrbit         ; branch (next bit)               |B0
        movf    PCL,F           ; 2 cycles                        |B0
        bsf     STATUS,RP0      ; bank 1                          |B1
        bcf     iic_tris,sclndx ; scl = 0                       < |B1
;
;  9th clock for ACK / NACK.  this section needs work if you
;  want to support both write and read operations.
;
        movf    PCL,F           ; 2 cycles                        |B1
        movf    PCL,F           ; 2 cycles                        |B1
        bsf     iic_tris,sdandx ; sda = 1 (hi-z input)          < |B1
        movf    PCL,F           ; 2 cycles                        |B1
        movf    PCL,F           ; 2 cycles                        |B1
        bsf     iic_tris,sclndx ; scl = 1 (9th clock)           < |B1
        bcf     STATUS,RP0      ; bank 0                          |B0
        nop                     ; test for 'ACK' here ???         |B0
        movf    PCL,F           ; 2 cycles                        |B0
        movf    PCL,F           ; 2 cycles                        |B0
        movf    PCL,F           ; 2 cycles                        |B0
        bsf     STATUS,RP0      ; bank 1                          |B1
        bcf     iic_tris,sclndx ; scl = 0                       < |B1
        bcf     STATUS,RP0      ; bank 0                          |B0
        return                  ; return with wreg = payload      |B0

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  I2C 'Stop' condition.
;
iic_stop
        sda(0)                  ; sda = 0                         |B1
        scl(1)                  ; scl = 1                         |B1
        sda(1)                  ; sda = 1                         |B1
        bcf     STATUS,RP0      ; bank 0                          |B0
        return                  ;                                 |B0

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;  K8LH DelayCy() subsystem 16-bit uLoop timing subroutine        ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

a = dloop-1
    while a > 0
        nop                     ; (cycles-11)%dloop entry points  |00
a -= 1
    endw
uLoop   addlw   -1              ; subtract 'dloop' loop time      |00
        skpc                    ; borrow? no, skip, else          |00
        decfsz  delayhi,F       ; done?  yes, skip, else          |00
;       bra  uLoop-dloop*2+10   ; do another loop (18F version)   |
        goto uLoop-dloop+5      ; do another loop (16F version)   |00
        return                  ;                                 |00

;******************************************************************
        end
 
Last edited:
One thing that you might try is to make a new project and paste the source code into a new file in that project. I know that sounds nuts, but I have had such problems that were solved by doing that and I have read that others have as well. It may be that project files get corrupted in certain situations, including being updated to newer versions of MPLAB X IDE and so forth.

It is easy enough to try. Now, if you have many more files in your project, that is another situation, but still the approach could start as I am suggesting.

Good luck with it...

Edited to add: I now see @MMcLaren 's post with the same suggestion.
 

be80be

Joined Jul 5, 2008
2,072
I just had a play with this I use linux and mplab-x
I got the same error
it's the case of F isn't f to linux
p16f690.inc is not the same thing as P16F690.INC
 
Top