LIST p=16F628 ;tell assembler what chip we are using
include "P16F628.inc" ;include the defaults for the chip
__config 0x3D18 ;sets the configuration settings (oscillator type etc.)
cblock 0x20 ;start of general purpose registers
counta ;used in delay routine
countb ;used in delay routine
countc ;used in delay routine
countx ;used in delay routine
flags
w_temp ;temp storage used by isr
status_temp
;general registers used by I2C
I2C_data ;here comes the data that will be send
I2C_buffer ;buffer used during the writing of a byte
I2C_index ;7>6>5>4>3>2>1
I2C_device_address ;7 bit-address of the slave plus 0 for writing, 1 for reading
I2C_failure ;I2C_failure > 0: true (1) or false(0)
I2C_countdown_tries ;if this is zero = total failure!!
I2C_counter ;counter used by waiting for ACK
endc
I2C_PORT equ PORTB
I2C_TRIS equ TRISB
#define SDA 7
#define SCL 6
org 0x0000 ;org sets the origin, 0x0000 for the 16F628,
;this is where the program starts running
goto main
;**************************************************************************
;I2C LOW LEVEL STUFF BEGINS HERE******************************************
;**************************************************************************
low_SDA
bsf STATUS,RP0
bcf I2C_TRIS,SDA
bcf STATUS,RP0
bcf I2C_PORT,SDA
return
low_SCL
bsf STATUS,RP0
bcf I2C_TRIS,SCL
bcf STATUS,RP0
bcf I2C_PORT,SCL
return
high_SDA
bsf STATUS,RP0
bcf I2C_TRIS,SDA
bcf STATUS,RP0
bsf I2C_PORT,SDA
return
high_SCL
bsf STATUS,RP0
bcf I2C_TRIS,SCL
bcf STATUS,RP0
bsf I2C_PORT,SCL
return
SCL_pulse
call high_SCL
call low_SCL
return
reset_I2C_registers
clrf I2C_buffer ;buffer used during the writing of a byte
clrf I2C_index ;7>6>5>4>3>2>1
clrf I2C_failure ;I2C_failure > 0: true (1) or false(0)
movlw d'100'
movwf I2C_countdown_tries ;if this is zero = total failure!!
movwf I2C_counter ;counter used by waiting for ACK
return
;*************************************************************************
send_start_bit
;initialize: SDA and SCL must be high
bsf STATUS,RP0
bcf I2C_TRIS,SDA
bcf I2C_TRIS,SCL
bcf STATUS,RP0
bsf I2C_PORT,SDA
bsf I2C_PORT,SCL
nop
;send start bit by bringing SCL low
bcf I2C_PORT,SCL
return
;**************************************************************************
send_stop_bit
;initialize: SDA and SCL must be high
bsf STATUS,RP0
bcf I2C_TRIS,SDA
bcf I2C_TRIS,SCL
bcf STATUS,RP0
bsf I2C_PORT,SCL
nop
;send stop bit by bringing SDA high
bsf I2C_PORT,SDA
return
;**************************************************************************
send_bit_high ;beginnig SDA and SCL low
call high_SDA
call SCL_pulse
return
;**************************************************************************
send_bit_low ;beginnig SDA and SCL low
call low_SDA
call SCL_pulse
return
;**************************************************************************
wait_for_ack
;SDA should be high! The slave is expected to bring it low
call high_SDA
call high_SCL
;prepare counter
movlw d'100'
movwf I2C_counter
wait_loop
btfsc I2C_PORT,SDA
goto SDA_is_still_high
goto ack_SDA_is_cleared
SDA_is_still_high
decfsz I2C_counter,f
goto wait_loop
bsf I2C_failure,0 ;add an error
call send_stop_bit
return
ack_SDA_is_cleared
;give a pulse
bsf STATUS,RP0
bsf I2C_TRIS,SCL
nop
bcf I2C_TRIS,SCL
bcf STATUS,RP0
;finished pulsing; clear failure (succes) and return
bcf I2C_failure,0 ;succes
return
;**************************************************************************
send_byte ;the byte should be in the working_register
;bits in the buffer are send MSB first
;prepare
movlw d'8'
movwf I2C_index
movwf I2C_buffer
;send the bits
send_the_bit
btfsc I2C_buffer,7
goto the_bit_is_high
goto the_bit_is_low
the_bit_is_high
call send_bit_high
goto check_if_sending_is_finished
the_bit_is_low
call send_bit_low
call high_SDA
goto check_if_sending_is_finished
check_if_sending_is_finished
decfsz I2C_index,f
goto get_next_bit
return
get_next_bit
rlf I2C_buffer,f
goto send_the_bit
;********************************************************************************
do_write_sequence
;prepare the 7bit-addres of the device, put in the buffer and roll left
;LSB should be 0 when writing
movfw I2C_device_address
movwf I2C_buffer
rlf I2C_buffer,f
bcf I2C_buffer,0
;start the writing, (startbit + address), wait for ACK, if errors: restart
call send_start_bit
call send_byte
call wait_for_ack
btfsc I2C_failure,0 ;succes?
goto another_try
;now write the data, if errors: restart
movfw I2C_data
call send_byte
call wait_for_ack
btfsc I2C_failure,0 ;succes?
goto another_try
call send_stop_bit
return ;succes!!!!!
another_try
decfsz I2C_countdown_tries,f
goto do_write_sequence ;restart
call send_stop_bit
return ;give up
;**************************************************************************
;I2C LOW LEVEL STUFF ENDS HERE******************************************
;**************************************************************************
setup
;turn comparators off (make it like a 16F84)
movlw 0x07
movwf CMCON ;turn comparators off (make it like a 16F84)
;setup ports
bsf STATUS, RP0 ;select bank 1
;in TRISA and TRISB 0= output, 1 = INPUT
;RA4 and RA5 only POSSIBLE AS OUTPUT
movlw b'11111111'
movwf TRISA ;set PortA all inputs
movlw b'11111111' ;set PortB all inputs
movwf TRISB
clrf OPTION_REG ;disable internal pullups on port-b
bsf OPTION_REG,7
;back to bank0:
bcf STATUS, RP0 ;select bank 0
movlw b'00000000'
movwf PORTA
movwf PORTB ;set all bits off
;NO interrupts:
clrf INTCON ;clear flags and switches
retlw 0
;**************************************************************************
;**************************************************************************
;**************************************************************************
;to wait x msec (max 255!) put x in the working register
;and call this routine (wait_x_msec)
wait_x_msec
Loop_c movwf countc
loop_b movlw d'10'
movwf countb
loop_a movlw d'100'
movwf counta
decfsz counta, f
goto $-1
decfsz countb, f
goto loop_a
decfsz countc ,f
goto loop_b
return
;**************************************************************************
;**************************************************************************
;**************************************************************************
main
call setup
call reset_I2C_registers
movlw b'100000' ;address of Philips port expander
movwf I2C_device_address
movlw b'11111111'
movwf I2C_data
loop
movlw d'250'
call wait_x_msec
movlw d'250'
call wait_x_msec
movlw d'250'
call wait_x_msec
movlw d'250'
call wait_x_msec
movlw b'00000000'
movwf I2C_data
call do_write_sequence
movlw d'250'
call wait_x_msec
movlw d'250'
call wait_x_msec
movlw d'250'
call wait_x_msec
movlw d'250'
call wait_x_msec
movlw b'11111111'
movwf I2C_data
call do_write_sequence
goto loop
end