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
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: