TODOPIC
Microcontroladores PIC => Almacén del Assembler => Mensaje iniciado por: antimason en 01 de Enero de 2009, 21:33:44
-
;****************************Librería "BUS_I2C.INC"*************************************
;
;Estas subrutinas permiten realizar las tareas básicas de control del bus serie I2C,
;por parte de un solo microcontrolador maestro.
;
;ZONA DE DATOS**************************************************************************
CBLOCK
I2C_ContadorBits ;Cuenta los bits a transmitir o a recibir.
I2C_Dato ;Dato a transmitir o recibido.
I2C_Flags ;Guarda la información del estado del bus I2C.
ENDC
#DEFINE I2C_UltimoByteLeer I2C_Flags,0
; - (I2C_UltimoByteLeer)=0, NO es el último byte a leer por el maestro.
; - (I2C_UltimoByteLeer)=1, SI es el último byte a leer por el maestro.
;La definición de las líneas SCL y SDA del bus I2C se puede cambiar según las
;necesidades del hardware.
#DEFINE SCL PORTA,3 ;Línea SCL del bus I2C.
#DEFINE SDA PORTA,4 ;Línea SDA del bus I2C.
;Subrutina "SDA_Bajo"********************************************************************
SDA_Bajo
bsf STATUS,RP0 ;Configura la línea SDA como salida.
bcf SDA
bcf STATUS,RP0
bcf SDA ;SDA en bajo.
return
;Subrutina "SDA_AltaImpedancia"**********************************************************
SDA_AltaImpedancia
bsf STATUS,RP0 ;Configura la línea SDA entrada.
bsf SDA ;Lo pone en alta impedancia y, gracias a la
bcf STATUS,RP0 ;Rp de esta línea, se mantiene a nivel alto.
return
;Subrutina"SCL_Bajo"*********************************************************************
SCL_Bajo
bsf STATUS,RP0
bcf SCL ;Configura la línea SCL como salida.
bcf STATUS,RP0
bcf SCL ;La línea de reloj SCL en bajo.
return
;Subrutina "SCL_AltaImpedancia"**********************************************************
SCL_AltaImpedancia
bsf STATUS,RP0 ;Configura la línea SCL entrada.
bsf SCL ;Lo pone en alta impedancia y, gracias a la Rp
bcf STATUS,RP0 ;de esta línea, se mantiene a nivel alto.
SCL_EsperaNivelAlto
btfss SCL ;Si algún esclavo mantiene esta línea en bajo
goto SCL_EsperaNivelAlto ;hay que esperar.
return
;Subrutina "I2C_EnviaStart"**************************************************************
;Esta subrutina envía una condición de Start o inicio.
I2C_EnviaStart
call SDA_AltaImpedancia ;Línea SDA en alto.
call SCL_AltaImpedancia ;Linea SCL en alto.
call Retardo_4micros ;Tiempo tBUF del protocolo.
call SDA_Bajo ;Flanco de bajada de SDA mientras SCL está en alto.
call Retardo_4micros ;Tiempo tHD;STA del protocolo.
call SCL_Bajo ;Flanco de bajada del reloj SCL.
call Retardo_4micros
return
;Subrutina "I2C_EnviaStop"***************************************************************
;Esta subrutina envía una condición de Stop o parada.
I2C_EnviaStop
call SDA_Bajo
call SCL_AltaImpedancia ;Flanco de subida de SCL.
call Retardo_4micros ;Tiempo tSU;STO del protocolo.
call SDA_AltaImpedancia ;Flanco de subida de SDA.
call Retardo_4micros ;Tiempo tBUF del protocolo.
return
;Subrutina "I2C_EnviaByte"***************************************************************
;El microcontrolador maestro transmite un byte por el bus I2C, comenzando por el bit
;MSB. El byte a transmitir debe estar cargado previamente en el registro de trabajo W,
;de la subrutina ejecutada anteriormente I2C_EnviaStart o esta misma I2C_EnviaByte,
;la línea SCL se debe encontrar a nivel bajo al menos durante 5us.
I2C_EnviaByte
movwf I2C_Dato ;Almacena el byte a transmitir.
movlw 0x08 ;A transmitir 8 bits.
movwf I2C_ContadorBits
I2C_EnviaBit
rlf I2C_Dato,F ;Chequea el bit, llevándolo previamente al Carry.
btfsc STATUS,C
goto I2C_EnviaUno
I2C_EnviaCero
call SDA_Bajo ;Si es "0" envía un nivel bajo.
goto I2C_FlancoSCL
I2C_EnviaUno
call SDA_AltaImpedancia ;Si es "1" lo activará alto.
I2C_FlancoSCL
call SCL_AltaImpedancia ;Flanco de subida del SCL.
call Retardo_4micros ;Tiempo tHIGH del protocolo.
call SCL_Bajo ;Termina el semiperiodo positivo del reloj.
call Retardo_4micros ;Tiempo tHD;DAT del protocolo.
decfsz I2C_ContadorBits,F ;Lazo para los ocho bits.
goto I2C_EnviaBit
call SDA_AltaImpedancia ;Libera la línea de datos.
call SCL_AltaImpedancia ;Pulso en alto de reloj para que el esclavo
call Retardo_4micros ;pueda envíar el bit ACK.
call SCL_Bajo
call Retardo_4micros
return
;Subrutina "I2C_LeeBit"*******************************************************************
;El microcontrolador maestro lee un byte desde el esclavo conectado al bus I2C. El dato
;recibido se carga en el registro I2C_Dato y lo envía a la subrutina superior a través
;del registro W .Se empieza a leer por el bit de mayor peso MSB.
;De alguna de las subrutinas ejecutadas anteriormente I2C_EnviaStart, I2C_EnviaByte
;o esta misma I2C_LeeByte, la línea SCL lleva en bajo al menos 5 us.
I2C_LeeByte
movlw 0x08 ;A recibir 8 bits.
movwf I2C_ContadorBits
call SDA_AltaImpedancia ;Deja libre la línea de datos.
I2C_LeeBit
call SCL_AltaImpedancia ;Flanco de subida del reloj.
bcf STATUS,C ;En principio supone que es "0".
btfsc SDA ;Lee el bit
bsf STATUS,C ;Si es "1" carga en el carry.
rlf I2C_Dato,F ;Lo introduce en el registro.
call SCL_Bajo ;Termina el semiperiodo positivo del reloj.
call Retardo_4micros ;Tiempo tHD;DAT del protocolo.
decfsz I2C_ContadorBits,F ;Lazo para los 8 bits.
goto I2C_LeeBit
;Chequea si este es el último byte a leer para enviar o no el bit de reconocimiento
;ACK en consecuencia.
btfss I2C_UltimoByteLeer ;Si es el último, no debe envíar
;el bit de reconocimiento AK.
call SDA_Bajo ;Envía el bit de reconocimiento ACK
;porque todavía no es el último byte a leer.
call SCL_AltaImpedancia ;Pulso en alto del SCL para transmitir el
call Retardo_4micros ;bit ACK de reconocimiento. Este es tHIGH.
call SCL_Bajo ;Pulso de parada del SCL.
call Retardo_4micros
movf I2C_Dato,W ;El resultado se manda en el registro de
return ;trabajo W.
Espero que os sirvan, lo he simulado en Proteus sin éxito, pero ya no me fio del puñetero PROTEUS.
-
Había un error en la subrutina "SDA_AltaImpedancia", ya lo he reeditado.
-
Aquí dejo otro código que por lo menos da muestra en el proteus generar actividad en una eeprom externa, pero no se simula correctamente y la salida a leds
no corresponde con lo que se espera del programa. (al final me tendré que hacer con un simulador hardware), espero que os sirva de ayuda y me digais que tal
os ha ido con uno u otro código en la realidad.
;PROTOCOLO I2C POR SOFWARE PARA COMUNICACIÓN DEL PIC 16F84A CON MEMORIAS EXTERNAS I2C, 24CXX.
;XTAL = 4 Mhz.
LIST P=16F84A
INCLUDE "P16F84A.inc"
EEPROM EQU 1CH
ADDR EQU 1DH
DATAI EQU 1EH
DATAO EQU 1FH
TXBUF EQU 20H
RXBUF EQU 21H
COUNT EQU 22H
DEL EQU 23H
SEL EQU 24H
DATOS EQU 25H
DI EQU 7
DO EQU 6
SDA EQU 1
SCL EQU 0
;PortA.0--------->Serial Clock
;PortA.1<-------->Serial Data
;PortB LED outs
__CONFIG _WDT_OFF & _XT_OSC & _CP_OFF
;************************************************************
;start
;************************************************************
ORG 00H
GOTO START
START
CALL PORT_INIT
PRINCIPAL
MOVLW B'00000000' ;pagina EEPROM a escribir
MOVWF ADDR
MOVLW B'00000000' ;direccion EEPROM a escribir
MOVWF DATAO
MOVLW B'10101010' ;dato a escribir en EEPROM
MOVWF DATOS
CALL WRITE_EEPROM ;escribe en EEPROM
MOVLW .100
CALL DELAY_NMS
MOVLW 00H ;pagina EEPROM a leer
MOVWF ADDR
MOVLW 00H ;direccion EEPROM a leer
MOVWF DATAO
CALL READ_EEPROM ;lee de EEPROM
MOVF DATAI,W
MOVWF PORTB ;saca el dato leido por PORTB
MOVLW .100
CALL DELAY_NMS
DONE
GOTO DONE
;*************************************************************
;Port Init
;*************************************************************
PORT_INIT
CLRF PORTA
CLRF PORTB
BSF STATUS,RP0 ;selecciona pagina 1
CLRF TRISA
CLRF TRISB
BCF STATUS,RP0 ;selecciona pagina 0
CLRF PORTB
RETURN
;*************************************************************
;COMUNICACIÓN CON EEPROM
;*************************************************************
WRITE_EEPROM
CALL BSTART
MOVLW B'10100000'
MOVWF TXBUF
CALL TX
MOVF ADDR,W
MOVWF TXBUF
CALL TX
MOVF DATAO,W
MOVWF TXBUF
CALL TX
MOVF DATOS,W
MOVWF TXBUF
CALL TX
CALL BSTOP
RETURN
READ_EEPROM
CALL BSTART
MOVLW B'10100000'
MOVWF TXBUF
CALL TX
MOVF ADDR,W
MOVWF TXBUF
CALL TX
MOVF DATAO,W
MOVWF TXBUF
CALL TX
CALL BSTART
MOVLW B'10100001'
MOVWF TXBUF
CALL TX
CALL RX
CALL BSTOP
RETURN
BSTART
BSF PORTA,SDA
BSF STATUS,RP0
MOVLW 00H
MOVWF TRISA
BCF STATUS,RP0
BCF PORTA,SCL
NOP
BSF PORTA,SCL
NOP
NOP
NOP
NOP
NOP
BCF PORTA,SDA
NOP
NOP
NOP
NOP
NOP
BCF PORTA,SCL
NOP
NOP
RETURN
BSTOP
BSF STATUS,RP0
MOVLW 00H
MOVWF TRISA
BCF STATUS,RP0
BCF PORTA,SDA
NOP
NOP
NOP
BSF PORTA,SCL
NOP
NOP
NOP
BSF PORTA,SDA
NOP
NOP
BCF PORTA,SCL
NOP
NOP
NOP
RETURN
BITOUT
BSF STATUS,RP0
MOVLW 00H
MOVWF TRISA
BCF STATUS,RP0
BTFSS EEPROM,DO
GOTO BITLOW
BSF PORTA,SDA
GOTO CLK_OUT
BITLOW
BCF PORTA,SDA
CLK_OUT
BSF PORTA,SCL
NOP
NOP
NOP
NOP
BCF PORTA,SCL
NOP
RETURN
BITIN
BSF EEPROM,DI
BSF STATUS,RP0
MOVLW 02H
MOVWF TRISA
BCF STATUS,RP0
BSF PORTA,SCL
NOP
NOP
NOP
NOP
NOP
BTFSS PORTA,SDA
BCF EEPROM,DI
BCF PORTA,SCL
RETURN
TX
MOVLW .8
MOVWF COUNT
TXLP
BCF EEPROM,DO
BTFSC TXBUF,7
BSF EEPROM,DO
CALL BITOUT
RLF TXBUF,F
DECFSZ COUNT,F
GOTO TXLP
CALL BITIN
RETURN
RX
CLRF DATAI
MOVLW .8
MOVWF COUNT
BCF STATUS,0
RXLP
RLF DATAI,F
CALL BITIN
BTFSC EEPROM,DI
BSF DATAI,0
DECFSZ COUNT,F
GOTO RXLP
BSF EEPROM,DO
CALL BITOUT
RETURN
DELAY_NMS
MOVWF DEL
OUTTER_MS
MOVLW .110
MOVWF SEL
INNER_MS
NOP
NOP
NOP
NOP
NOP
NOP
DECFSZ SEL,F
GOTO INNER_MS
DECFSZ DEL,F
GOTO OUTTER_MS
RETURN
END
-
Aquí os dejo otra rutina para comunicar el 16f84 con una memoria 24lcxxx.
; RUTINAS DE COMUNICACIÓN CON DISPOSITIVO I2C
;
; ABRIL 10 DEL 2000
;
;PORTX ES EL PUERTO DONDE SE ENCUENTRAN LOS BITS SCL Y SDA DEL DISPOSITIVO
;TRISX ES EL REGISTRO DE DIRECCIÓN DEL PUERTO PORTX
;
#DEFINE _c STATUS,C
;
;****************************************************************************
;ENVIA CONDICIÓN DE START AL DISPOSITIVO I2C
;**************************************************************************
START
BSF PORTX,SDA
BANK1
BCF TRISX,SDA
BCF TRISX,SCL
BANK0
BCF PORTX,SCL
NOP
BSF PORTX,SCL
NOP
NOP
NOP
NOP
NOP
BCF PORTX,SDA
NOP
NOP
NOP
NOP
NOP
BCF PORTX,SCL
NOP
NOP
RETURN
;**************************************************************************
;ENVIA CONDICIÓN DE STOP AL DISPOSITIVO I2C
;**************************************************************************
STOP
BANK1
BCF TRISX,SDA
BCF TRISX,SCL
BANK0
BCF PORTX,SDA
NOP
NOP
NOP
BSF PORTX,SCL
NOP
NOP
NOP
BSF PORTX,SDA
NOP
NOP
BCF PORTX,SCL
NOP
NOP
NOP
RETURN
;**************************************************************************
;RUTINA QUE COLOCA 8 BITS EN EL DISPOSITIVO I2C, AFECTA F0
;**************************************************************************
WRIBYTE
BANK1
BCF TRISX,SCL
BCF TRISX,SDA
BANK0
MOVLW .8
MOVWF F0
BCF _c
ROTA
RLF F4,F
BTFSS _c
GOTO ESCERO
ESUNO
BSF PORTX,SDA
GOTO PULSO
ESCERO
BCF PORTX,SDA
PULSO
nop
nop
BSF PORTX,SCL
nop
nop
nop
BCF PORTX,SCL
nop
DECFSZ F0,F
GOTO ROTA
RETLW TRUE
;****************************************************************************
;RUTINA QUE TOMA 8 BITS DEL DISPOSITIVO I2C, ALMACENA EL RESULTADO EN F4
;****************************************************************************
REBYTE
MOVLW .8 ;
MOVWF F0 ;CONTADOR
BANK1
BSF TRISX,SDA ;SDA de Entrada
BANK0
CLRF F4
RBIT
BSF PORTX,SCL ;
nop
BTFSS PORTX,SDA
GOTO ESTACERO
ESTAUNO
BSF _c ;CARRY=1
GOTO PULSDOWN
ESTACERO
BCF _c ;CARRY=0
PULSDOWN
RLF F4,F
BCF PORTX,SCL ;SCL=0
nop
DECFSZ F0,F ;DEC F0
GOTO RBIT
BANK1
BCF TRISX,SDA ;SDA DE SALIDA
BANK0
RETLW TRUE ;
;***************************************************************************
;RUTINA QUE LEE EL ACK DEL DISPOSITIVO I2C, AFECTA F0
;entrega en el reg W un TRUE si fue OK ó un FALSE si hubo error.
;***************************************************************************
ACKMEM
BANK1
BSF TRISX,SDA ;SDA=IN
BANK0
MOVLW .255
MOVWF F0
LOOPACKM
BSF PORTX,SCL
nop
nop
nop
nop
RSDA
BTFSS PORTX,SDA
GOTO SIACK
NOACK
nop
BCF PORTX,SCL
DECFSZ F0,F
GOTO LOOPACKM
RETLW FALSE ;EL DISP. I2C NO ENTREGÓ ACK
SIACK
nop
BCF PORTX,SCL
RETLW TRUE ;ACK = CORRECTO
;***************************************************************************
;ENVIA UN ACK AL DISPOSITIVO I2C
;***************************************************************************
ACKMICRO
BANK1
BCF TRISX,SDA
BCF TRISX,SCL
BANK0
BCF PORTX,SDA
BSF PORTX,SCL
nop
NOP
NOP
BCF PORTX,SCL
nop
RETLW TRUE
;***************************************************************************
;RUTINA LA CUAL ESCRIBE UN BYTE EN EL DISPOSITIVO I2C PARÁMETROS:
; DIREE y DATO (A ESCRIBIR)
;***************************************************************************
WR_EE
CALL START
MOVLW 0xA0 ; W=10100000 B
MOVWF F4
CALL WRIBYTE
CALL ACKMEM
MOVF DIREE,W ;ACOMODO DE WORD ADDRESS
MOVWF F4
CALL WRIBYTE
CALL ACKMEM
MOVF DATO,W ;ACOMODO DEL DATO A ESCRIBIR
MOVWF F4
CALL WRIBYTE
CALL ACKMEM
CALL STOP
CALL DEL5MS
CALL DEL5MS ;TIEMPO DE ESPERA twc
CALL DEL5MS
RETLW TRUE
;***************************************************************************
;ESTA RUTINA LEE DEL DISPOSITIVO I2C UN BYTE , PARÁMETROS:
; DIREE y DATO (LEÍDO)
;***************************************************************************
RD_EE
CALL START
MOVLW 0xA0 ; W=10100000 B
MOVWF F4
CALL WRIBYTE
CALL ACKMEM
MOVF DIREE,W ;ACOMODO DE WORD ADDRESS
MOVWF F4
CALL WRIBYTE
CALL ACKMEM
CALL START
MOVLW 0xA1
MOVWF F4
CALL WRIBYTE
CALL ACKMEM
CALL REBYTE
MOVF F4,W
MOVWF DATO
CALL STOP
RETLW TRUE
;***************************************************************************
-
Vamos, alguna funcionará en la vida real, digo yo.
-
Agrego una que hice yo solo para recepcion de datos como exclavo, por interrupcion de flaco de RB0.- Adjunto un posible hardware y el diagrama de flujo de la recepcion por interrupcion.-
List p=16f84a
#include <p16f84a.inc>
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC
; Cristal de 10 MHz !!!
; Dispositivo Maestro I2C configurado a 100 kHz.-
;**********************************************
; Declaracion de Registros
;**********************************************
CONTADOR equ 0x0C ;registro utilizado para conteo
PDel0 equ 0x10 ;registro utilizado en demoras
PDel1 equ 0x11 ;registro utilizado en demoras
PDel2 equ 0x12 ;registro utilizado en demoras
FLAGS equ 0x13 ;registro utilizado en carga de banderas para control de I2C
DATO_I2C equ 0x14 ;registro utilizado para el guardado del byte recibido por I2C
TEMP_W equ 0x15 ;registro temporario para guardado de W
STATUS_TEMP equ 0x16 ;registro temporario para guardado de STATUS
;**********************************************
; Declaracion de Bit
;**********************************************
SCL equ 0 ;pin SCL de comunicacion I2C, en este caso usamos RA0
SDA equ 0 ;pin SDA de comunicacion I2C, Debe ser siempre el Rb0, para deteccion de flanco x interrupcion
PFULL equ 0 ;si es 1 pila completa
SLAVE_OK equ 1 ;si es 1 se ha direccionado el esclavo correcto
KBHIT equ 2 ; Control de recepcion de datos
DIRE equ 3 ; Se recibe direccion I2C
;**********************************************
org 0x00
goto inicio
;******************************************
; Interrupcion
;******************************************
org 0x04
btfsc INTCON,INTF
goto interrupcion_rb0
goto salida_interrupcion
;******************************************
; Interrupcion RB0
;******************************************
; se genero interrupcion, posible envio de Start para la comunicacion.-
interrupcion_rb0
btfss PORTA,SCL ; si es 1 entonces se ha generado condicion de start
goto salida_interrupcion
movwf TEMP_W
swapf TEMP_W,1
movf STATUS,w
movwf STATUS_TEMP
clrf STATUS ;Bank0
bcf INTCON,GIE
movlw 0x08
movwf CONTADOR
;*******************************************
espera_bit btfsc PORTA,SCL ; esperamos que SCL=0
goto $-1
espera_clock btfss PORTA,SCL ; esperamos que SCL=1 para leer bit.
goto $-1
bcf DATO_I2C,0 ; cargo 0
btfsc PORTB,SDA ; si es 0 salta, sino carga 1
cargo_1 bsf DATO_I2C,0 ; cargo 1
consulta decfsz CONTADOR ; determina si ya llegaron los 8 bit's
goto $+2 ; no se cargaron
goto espera_ack
rlf DATO_I2C,1 ; rotamos a la izquierda
goto espera_bit
;*******************************************
espera_ack
bsf STATUS,RP0 ; Bank1
bcf TRISB,SDA ; SDA como salida
bcf STATUS,RP0 ; Bank0
bcf PORTB,SDA ; envio ack
btfsc FLAGS,PFULL ; determino si estoy en condiciones de recibir dato (PFULL=0 Si)
bsf PORTB,SDA ; envio un nack
btfss PORTA,SCL ; espero a que SCL=1
goto $-1
call demora_6us
bsf PORTB,SDA
bsf STATUS,RP0 ; Bank1
bsf TRISB,SDA ; ya se envio ack o nack, reconfiguro SDA como entrada.
clrf STATUS ; Bank0
;*******************************************
call grabar_dato
movlw 0x08
movwf CONTADOR ; por si se recibe otro byte
;*******************************************
btfss PORTA,SCL ; determinamos si es un stop u otro byte
goto $-1
btfsc PORTB,SDA ; si es 0 puede ser un stop
goto cargo_1 ;no es stop, se carga el byte, MSB es 1
nop ;demora de 5us.
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
btfsc PORTA,SCL ; pregunta si el SCL esta generando clock.
goto salida_interrupcion ; si no genera clock, entonces STOP
bcf DATO_I2C,0 ; si se genera clock, se carga byte, MSB=0
decf CONTADOR,1
rlf DATO_I2C,1
goto espera_clock
;******************************************
salida_interrupcion
bcf FLAGS,SLAVE_OK
bcf INTCON,INTF
bsf INTCON,GIE
movf STATUS_TEMP,w
movwf STATUS
swapf TEMP_W,w
retfie
;__________________________________________________________________
;******************************************
; Grabar Dato
;******************************************
; La recepcion fue creada como para recibir varios datos consecutivos y en este caso
; hay que crear un buffer (No expuesto para facilitar entendimiento), en este ejemplo
; solo se pueden recibir 1.-
grabar_dato
btfss FLAGS,DIRE ; Se recibio Direccion del Exclavo?
goto guardo_direccion ;no, lo verifico.-
bsf FLAGS,PFULL ;Se lleno el Buffer, para no recibir mas
bsf FLAGS,KBHIT ;Se recibio dato y queda guardado en DATO_I2C
return
guardo_direccion
movlw 0xAA ; Direccion de mi exlavo.-
subwf DATO_I2C,w ; verifico si es la correcta
btfss STATUS,Z
goto salida_interrupcion ; no lo es, salgo.-
bsf FLAGS,DIRE ; Si es, lo indico seteando DIRE
return
;-------------------------------------------------------------------------
;******************************************
; Programa Principal
;******************************************
inicio
movlw b'00000001' ;configurar el puerto a como xxxsssse
banksel TRISA ;cambiar a banco 1
movwf TRISA
movlw b'00000001' ;configurar el puerto b como ssssssse
movwf TRISB
bcf OPTION_REG,INTEDG ;interrupción rb0 por flanco descendente
banksel PORTB
clrf FLAGS
;**********************************************
; Activacion de interrupciones
;**********************************************
bsf INTCON,GIE ;activar habilitador general de interrupciones.
bsf INTCON,INTE ;activar interrupción por rb0.
;**********************************************
clrf FLAGS
clrf PORTA
;**********************************************
; Estado de espera
;**********************************************
Espera
btfss FLAGS,KBHIT ; Llego dato de I2C?
goto $-1 ; No
clrf FLAGS
; En DATO_I2C tenemos el byte enviado por el Dispositivo Maestro
; para posterior manejo, como enviarlo a un LCD, etc. No lo expuse
; para no complicar el entendimiento
goto Espera ; bucle infinito.-
;**********************************************
;** Subrutinas Demoras
;**********************************************
;**********************************************
demora_6us movlw .2
movwf PDel0
PLoop0_6 nop
decfsz PDel0, 1
goto PLoop0_6
PDelL1_6 goto PDelL2_6
PDelL2_6
return
;**********************************************
end
-
Interesante aportación, a ver si entre unos y otros vamos solventando los problemas de comunicación I2C del 16F84.
-
Otra rutina interesante esta vez del amigo Pedro Luis Sánchez Fornell.
Rutina: I2C.INC Fecha: MARZO - 2002
; Optimizadas por: Pedro Luis Sánchez Fornell
;
; Protocolo I2C para lectura / escritura de la Eeprom 24Cxx (4 Mhz)
;
; RB1: Señal de reloj del emisor (SCL)
; RB2: Señal de datos del emisor (SDA)
;
; Programa para PIC16F84
; Velocidad del reloj: 4 MHz Reloj instrucción: 1 MHz - 1 uS
; Perro guardián: deshabilitado Tipo de reloj: XT
; Protección del código: OFF
;
;*************************************************************************************
CBLOCK I2c_var
GenCount ; Contador
Mem_Loc ; Dirección de acceso a la EEPROM
Mem_Loc_H ; Dirección alta de acceso a la EEPROM
Data_Buf ; Byte de lectura de la EEPROM
OutputByte ; Byte de escritura en la EEPROM
flags ; Banderas de estado de lectura/escritura
ENDC
#define SDA TRISB, 2
#define SCL TRISB, 1
;*************************************************************************************
; Lectura de la EEPROM
;
; Llamada con la dirección en Mem_Loc
; Retorna con el byte en Data_Buf
;*************************************************************************************
ReadEPROM bcf STATUS,RP0 ; Banco 0
movf PORTB,0
andlw 0xF9
movwf PORTB ; Pone a 0 RB1 y RB2 para control pasivo del bus
bsf STATUS,RP0 ; Banco 1
bsf SDA ; SDA alto
bsf SCL ; SCL alto
bcf SDA ; START - SDA bajo
movlw b'10100000'
iorwf Mem_Loc_H,0 ; Coloca los tres bits más altos de la dirección
call Byte_Out ; Escribe cabecera
btfsc flags,0
goto Error_Routine ; Error
movf Mem_Loc,0
call Byte_Out ; Escribe dirección
btfsc flags,0
goto Error_Routine ; Error
bcf SCL ; SCL bajo
nop
bsf SDA ; SDA alto
bsf SCL ; SCL alto
bcf SDA ; 2º START - SDA bajo
movlw b'10100001' ; Esta vez NO SE DEBEN colocar los tres
;bits más altos
call Byte_Out ; Escribe 2ª cabecera
btfsc flags,0
goto Error_Routine ; Error
call Byte_In ; Recibe byte
movf Data_Buf,0 ; Almacena el valor en Data_Buf
bcf SCL ; SCL bajo
nop
bcf SDA ; SDA bajo
bsf SCL ; SCL alto
bsf SDA ; STOP - SDA alto
bcf STATUS, RP0 ; Banco 0
return
;*************************************************************************************
; Escritura de la EEPROM
;
; Llamada con la dirección en Mem_Loc
; No retorna nada
;*************************************************************************************
WriteEPROM bcf STATUS,RP0 ; Banco 0
movf PORTB,0
andlw 0xF9
movwf PORTB ; Pone a 0 RB1 y RB2 para control pasivo del bus
bsf STATUS,RP0 ; Banco 1
bsf SDA ; SDA alto
bsf SCL ; SCL alto
bcf SDA ; START - SDA bajo
movlw b'10100000'
iorwf Mem_Loc_H,0 ; Coloca los tres bits más altos de la dirección
call Byte_Out ; Escribe cabecera
btfsc flags,0
goto Error_Routine ; Error
movf Mem_Loc,0
call Byte_Out ; Escribe dirección
btfsc flags,0
goto Error_Routine ; Error
movf Data_Buf,0
call Byte_Out ; Escribe dato
btfsc flags,0
goto Error_Routine ; Error
bcf SCL ; SCL bajo
nop
bcf SDA ; SDA bajo
bsf SCL ; SCL alto
bsf SDA ; STOP - SDA alto
call Delay10ms ; Retardo max. para el ciclo de escritura de 10ms
bcf STATUS, RP0 ; Banco 0
return
;*************************************************************************************
; Recibe byte de la EEPROM
;*************************************************************************************
Byte_In clrf Data_Buf
movlw 0x08 ; 8 bits a recibir
movwf GenCount
ControlIn rlf Data_Buf,1 ; rota Data_Buf
bcf SCL ; SCL bajo
nop
bsf SCL ; SCL alto
bcf STATUS,RP0 ; Banco 0
btfss SDA ; Testea el bit de la EPROM
goto $+3
bsf STATUS,RP0 ; Banco 1
bsf Data_Buf,0 ; Bit actual de Data_Buf a 1
bsf STATUS,RP0 ; Banco 1
decfsz GenCount,1 ; Decrementa contador
goto ControlIn
return
;*************************************************************************************
; Envía byte a la EEPROM
; y espera el ACK (256us)
;*************************************************************************************
Byte_Out movwf OutputByte
movlw 0x08 ; 8 bits a enviar
movwf GenCount
rrf OutputByte,1 ; Rota al revés OutputByte
ControlOut rlf OutputByte,1 ; Rota OutputByte
bcf SCL ; SCL bajo
nop
btfsc OutputByte,7 ; Testea el bit 7
goto BitHigh
bcf SDA ; Bit a 0 - SDA bajo
goto ClockOut
BitHigh bsf SDA ; Bit a 1 - SDA alto
ClockOut bsf SCL ; SCL alto - envía bit
decfsz GenCount, 1 ; Decrementa contador
goto ControlOut
bcf SCL ; SCL bajo
bsf SDA ; SDA alto
nop
nop
nop
bsf SCL
clrf GenCount
WaitForACK bsf STATUS,RP0 ; Banco 1
incf GenCount,1
btfsc STATUS,Z ; Testea el desbordamiento del carry
goto No_ACK_Rec ; Se esperaron 256 useg y no se recibió el ACK
bcf STATUS,RP0 ; Banco 0
btfsc SDA ; Testea SDA
goto WaitForACK ; Aún no se recibe, continúa esperando...
bsf STATUS,RP0 ; Banco 1
bcf flags,0 ; ACK recibido, borra bandera.
return
No_ACK_Rec bsf flags,0 ; No se recibió el ACK, bandera a 1
return
;*************************************************************************************
; Error
;*************************************************************************************
Error_Routine bcf STATUS,RP0 ; Banco 0
return ; Retorna al programa principal
; (bandera permanece a 1)
;*************************************************************************************
; Retardos
;*************************************************************************************
Delay10ms movlw 0x0A
movwf GenCount
Delay_Start nop
movlw 0x07 ; 249 ciclos * 4us por ciclo + 5us = 1.000ms
Delay addlw 0x01
btfss STATUS,Z
goto Delay
decfsz GenCount,1
goto Delay_Start
return