Bueno. Pues para los que programar los PICs en C les suene a chino mandarino como a mi (es mi batalla pendiente), aqui les dejo el protocolo del maestro en asm.
Siento mucho no poder ofreceros el esclavo porque nunca hice un PIC como esclavo I2C, pero seguro que por la red y sobre todo en
hay ruinas para PIC que hagan de esclavo I2C por software.
Codigo:
;SUBRRUTINA "START":
;Realiza la condición de inicio de transmisión del protocolo I2C. ********* COMPROBADA ********
start bsf p_i2c,sda
bsf p_i2c,scl
nop ;Comprueba que SDA y SCL estén en alto
bcf p_i2c,sda
nop ;Baja SDA
bcf p_i2c,scl
nop ;BAJA SCL
return
;SUBRRUTINA "STOP":
;Realiza la condición de fin de transmisión del protocolo I2C. ********* COMPROBADA ********
stop bcf p_i2c,sda
bcf p_i2c,scl
nop ;Comprueba que SCL y SDA estén en bajo
bsf p_i2c,scl
nop ;Sube SCL
bsf p_i2c,sda
nop ;Sube SDA
return
;SUBRRUTINA "TX_BYTE": Transmite un byte.
;El byte a transmitir viene en el registro "w". ********* COMPROBADA ********
tx_byte movwf temp1 ;Salva el byte a transmitir en el registro temporal "temp1"
movlw 0x08
movwf temp2 ;Inicializa el contador
bck_txb bsf p_i2c,sda ;Sube sda
btfss temp1,7 ;Comprueba el bit y lo baja si es cero
bcf p_i2c,sda
nop ;Envia el SDA
bsf p_i2c,scl
nop ;Da un pulso de reloj
bcf p_i2c,scl
nop
rlf temp1,f ;Rotamos a la izquierda "temp1"
decfsz temp2,f ;Comprobamos el contador, para ver si hemos enviado los 8 bits
goto bck_txb ;Si no, repetimos el bucle
call rx_ack ;Espera el envio del ACK
return ;Terminamos
;SUBRRUTINA "RX_BYTE": Recibe un byte.
;El byte que es recibido se devuelve en el registro "w". ********* COMPROBADA ********
rx_byte movlw 0x08 ;Inicializamos el contador
movwf temp2
bsf status,5 ;SDA como entrada
bsf p_i2c,sda
bcf status,5
bck_rxb bsf p_i2c,scl
nop ;Subimos SCL
rlf temp1,f ;Rota el registro "temp1", donde vamos a guardar el byte recibido
bsf temp1,0 ;Subimos el bit
btfss p_i2c,sda ;Comprobamos que no es cero
bcf temp1,0
bcf p_i2c,scl
nop ;Bajamos SCL
decfsz temp2,f ;Vemos si hemos recivido ya los 8 bits
goto bck_rxb
bsf status,5 ;SDA como salida
bcf p_i2c,sda
bcf status,5
movf temp1,w ;Devolvemos el byte en el registro "w"
return ;Fin
;SUBRRUTINA "TX_ACK": Trasmite el bit de reconocimiento "ack". ********* COMPROBADA ********
tx_ack bcf p_i2c,sda
nop ;Baja SDA
bsf p_i2c,scl
nop ;Sube SCL
bcf p_i2c,scl
nop ;Baja SCL
bsf p_i2c,sda
nop ;Sube SDA
return ;Fin
;SUBRRUTINA "NO_ACK": Envía un "1" como bit de "ack". ********* COMPROBADA ********
no_ack bsf p_i2c,sda
nop ;Sube SDA
bsf p_i2c,scl
nop ;Sube SCL
bcf p_i2c,scl
nop ;Baja SCL
bsf p_i2c,sda
nop ;Sube SDA
return ;Fin
;SUBRRUTINA "RX_ACK": Espera el bit de agradecimiento "ack". ********* COMPROBADA ********
rx_ack bsf status,5 ;SDA como entrada
bsf p_i2c,sda
bcf status,5
movlw d"50" ;Inicializa el "temp2" que nos sirve de temporizador para no
movwf temp2 ;esperar el ACK indefinidamente
bcf temp3,7 ;Borra el bit de error
bck_rxa btfss p_i2c,sda ;Espera el ACK
goto no_err
decfsz temp2,f ;Decrementa el "temp2"
goto bck_rxa
bsf temp3,7 ;Si pasa mucho tiempo pone a uno el bit error y sale de la rutina
bsf status,5
bcf p_i2c,sda ;SDA como salida
bcf status,5
return ;Fin
no_err bsf p_i2c,scl ;Si no hay error
nop ;Manda un pulso de reloj
bcf p_i2c,scl
nop
bsf status,5
bcf p_i2c,sda ;SDA como salida
bcf status,5
return ;Fin
Las rutinas funcionan perfectamente. Hay que definir tres registros temporales temp1, temp2 y temp3 y definir el puerto que se vaya a utilizar para la comunicación como p_i2c, y los bits del puerto que se vayan a utilizar como sda (para los datos) y scl (para el reloj). Hay va un ejemplo: