Mensaje dirigido a Artifox o cualquiera que pueda y quiera ayudarme...
Hola Artifox,
he encontrado este mensaje tuyo en el foro...llevo buscando semanas en varios foros a alguien que domine realmente el I2C
para que pueda resolverme un problema que tengo a la hora comunicar tres micro pic 16f876 vía I2C.
Tu emensaje:
"Hola, yo he hecho lo que necesitas conectar un pic maestro y varios pic's esclavos. El codigo lo hice en PBP utlilizando las interrupciones por assembler. Dejame buscarlas y en la noche te las pongo, pues ahorita estoy en el trabajo.
Un abrazo.
Por cierto use un 16F877 y dos 16F876"
Si realmente has conseguido lo que escribes, te agradecería mucho que me ayudaras. Te explico;
Las rutinas de lectura y escritura que utilizo son correctas dado que he conseguido establecer comunicacion entre un Master pic 16f876 y un Slave pic16f876, tanto transmision como recepción. También he conseguido comunicar un Master pic 16f876 con dos expansores PCF8574 y un Master pic 16f876 con una EEPROM 24LC256 y un PCF8574.
Mi problema se presenta cuando quiero establecer comunicacion entre tres microcontroladores pic 16f876, un Master y dos Slaves pic 16f876.
Después de la primera rutina de lectura, es decir el Master lee el byte del Slave1, pasamos a la rutina de escritura donde el Master transmite el byte recibido por la línea del bus al Slave2, con diferente Address.
El Master lee correctamente del primer Slave el byte pero en cuanto cambia de dirección para transmitir el byte al segundo Slave pierde automáticamente el control del bus para siempre. El problema puede estar en el protocolo utilizado; bit de START, 1º ADRESS, ACK , DATO, ACK, RESTART, 2º ADDRESS, ACK, DATO, ....
La verdad ya no se que hacer....
te adjunto los programas en ensamblador (.asm) del Master y los 2 Slaves
Gracias de antemano,
Master:
;Definición de constantes
#define ClockValue d'9' ;(100khz) valor para cálculo de vel. I2C que pasará a SSPADD
;Definición de variables
cblock 0x20
MensajeIn ;Contendrá el dato recibido por I2C del slave
MensajeOut ;Contendrá el dato a enviar por I2C al slave
DirSlave ;Dirección del Slave
BkStatus ;Backup del registro STATUS
BkW ;Backup W
BDel0 ;Usada en retardos. Usada por subr "HacerTiempo"
BDel1 ;Usada en retardos. Usada por subr "HacerTiempo"
BDel2 ;Usada en retardos. Usada por subr "HacerTiempo"
Pausa ;Pausa en centesima de sg. Usada por subr "HacerTiempo"
PDel0
PDel1
PDel2
Dato
endc ;Fin de definiciones
org 0
goto INICIO
org 4
;-------------------------------------------------------------------------------
INICIO ;Inicio del cuerpo del programa
banksel TRISD ;Apunta a banco 1
movlw b'00000000' ;Salida (Leds)
movwf TRISD ;
MOVLW b'11101111'
MOVWF OPTION_REG
MOVLW b'00000110'
MOVWF ADCON1
banksel PORTD ;Apunta a banco 0
clrf PORTD
call init_i2c_Master ;Configuración para uso de i2c
clrf MensajeIn
clrf MensajeOut
movlw d'10' ;Pausa de 10 centésimas de segundo para que en...
movwf Pausa ;...el arranque de tiempo a los slaves a quedar...
call HacerTiempo ;..configurados adecuadamente.
MLoop
;PETICION DE UN BYTE DE DATOS AL SLAVE
;Se ha de la dirección del Slave en "DirSlave". Tras esto solo hay que llamar
;a la subrutina "Recibir" y se obtendrá el dato en "MensajeIn"
movlw b'01111000' ;Establece dirección de solicitud..
movwf DirSlave ;..es decir, dirección del slave
call Recibir ;Toma dato del Slave...
movf MensajeIn,W ;... a través de "MensajeIn"...
movwf PORTD ;...y lo muestra por PORTB (Leds)
CALL PDelay
MOVLW b'11111111' ; encendemos todos los bombillos
MOVWF Dato
MOVLW b'01111100'
MOVWF DirSlave
CALL Enviar
CALL PDelay
MOVLW b'10101010' ; alternamos el encendido
MOVWF Dato
MOVLW b'01111100'
MOVWF DirSlave
CALL Enviar
CALL PDelay
goto MLoop
;---------------------------------------------------------------------------------
;SUBRUTINA PARA RETARDO DE 4 SEGUNDO
PDelay movlw .62 ; 1 set number of repetitions (C)
movwf PDel0 ; 1 |
PLoop0 movlw .63 ; 1 set number of repetitions (B)
movwf PDel1 ; 1 |
PLoop1 movlw .255 ; 1 set number of repetitions (A)
movwf PDel2 ; 1 |
PLoop2 clrwdt ; 1 clear watchdog
decfsz PDel2, 1 ; 1 + (1) is the time over? (A)
goto PLoop2 ; 2 no, loop
decfsz PDel1, 1 ; 1 + (1) is the time over? (B)
goto PLoop1 ; 2 no, loop
decfsz PDel0, 1 ; 1 + (1) is the time over? (C)
goto PLoop0 ; 2 no, loop
PDelL1 goto PDelL2 ; 2 cycles delay
PDelL2 clrwdt ; 1 cycle delay
return ; 2+2 Done
;-------------------------------------------------------------------------------
init_i2c_Master ;Inicializa valores para uso de I2C en Master
;Ha de ser llamado tras definir TRISC y un valor para
;ClockValue. Para frecuencia SCL=Fosc/(4x(ClockValue+1))
;-------------------------------------------------------------------------------
;Guardamos copia de algunos registros
movwf BkW ;Hace copia de W
movf STATUS,W ;Hace copia de registro de estado
banksel PORTA
movwf BkStatus
;Configuramos I2C
banksel TRISC ; Pasamos a direccionar Banco 1
movlw b'00011000' ; Establece líneas SDA y SCL como entradas...
iorwf TRISC,f ;..respetando los valores para otras líneas.
movlw ClockValue ; Establece velocidad I2C segun...
movwf SSPADD ; ...valor de ClockValue
bcf SSPSTAT,6 ; Establece I2C input levels
bcf SSPSTAT,7 ; Habilita slew rate
banksel SSPCON ; Pasamos a direccionar Banco 0
movlw b'00111000' ; Master mode, SSP enable, velocidad segun...
movwf SSPCON ; ... Fosc/(4x(SSPADD+1))
bcf PIR1,SSPIF ; Limpia flag de eventos SSP
bcf PIR1,7 ; Limpia bit. Mandatorio por Datasheet
;Restauramos las copias de los registros
movf BkStatus,W ;Restaura las copias de registros
movwf STATUS ;registro de estado
movf BkW,W ;registro W
return
;---------------------------------------------------------------------------------------
HacerTiempo ;realiza una pausa del numero de centesimas de segundo especificadas en "Pausa"
;---------------------------------------------------------------------------------------
movf Pausa,W ;Coloca el valor de pausa en BDel2...
movwf BDel2 ;...para no alterar su contenido
;............................................................
; Generado con PDEL ver SP r 1.0 el 24/02/03 Hs 18:31:22
; Descripcion: Delay 10000 ciclos (1 centésima de segundo)
;............................................................
BCiclo movlw .8 ; 1 set numero de repeticion (B)
movwf BDel0 ; 1 |
BLoop1 movlw .249 ; 1 set numero de repeticion (A)
movwf BDel1 ; 1 |
BLoop2 nop ; 1 nop
nop ; 1 ciclo delay
decfsz BDel1, 1 ; 1 + (1) es el tiempo 0 ? (A)
goto BLoop2 ; 2 no, loop
decfsz BDel0, 1 ; 1 + (1) es el tiempo 0 ? (B)
goto BLoop1 ; 2 no, loop
BDelL1 goto BDelL2 ; 2 ciclos delay
BDelL2 nop ; 1 ciclo delay
;............................................................
decfsz BDel2,F ;Repite tantas veces el ciclo de una decima de segundo...
goto BCiclo ;..como se lo indique ADel2
return ; 2+2 Fin.
;**********************************************************************
;SUBRUTINA PARA TRANSMITIR DATOS1 EN MODO MASTER DEL I2C
;**********************************************************************
Enviar
MOVF DirSlave,W ;...............................DIE ADRESSE SENDEN
MOVWF SSPBUF ; carga direccion
BTFSS PIR1,SSPIF ; ACK recibido?
GOTO $-1 ; no, espera
BCF PIR1,SSPIF ; Clear the interrupt flag before RX
MOVF Dato,W ; carga dato....................DIE DATEN SENDEN
MOVWF SSPBUF
BTFSS PIR1,SSPIF ; ACK recibido?
GOTO $-1 ; no espera
BCF PIR1,SSPIF
banksel SSPCON2
bsf SSPCON2,RSEN
I2C.Restart.Wait
btfsc SSPCON2,RSEN
GOTO I2C.Restart.Wait
BCF STATUS,RP0
RETURN
;************************************************************************
;SUBRUTINA PARA RECIBIR DATOS1 EN MODO MASTER DEL I2C
;************************************************************************
Recibir
BCF STATUS,RP0 ; Restore Bank0......... DEN BUS ÜBERNEHMEN
BCF PIR1,SSPIF
BSF STATUS,RP0 ; Restore Bank1
BCF STATUS,RP1
BSF SSPCON2,SEN ; envia bit START
BCF STATUS,RP0 ; Restore Bank0
BTFSS PIR1,SSPIF ; START finalizado?
GOTO $-1 ; no, espera
BCF PIR1,SSPIF
movf DirSlave,W ;Dirección esclavo
iorlw b'00000001' ;con orden de lectura
; MOVF DIREC,W ; ........................DIE ADRESSE SENDEN
MOVWF SSPBUF ; carga direccion
BTFSS PIR1,SSPIF ; ACK recibido?
GOTO $-1 ; no, espera
BCF PIR1,SSPIF
BSF STATUS,RP0 ; Restore Bank1
BCF STATUS,RP1
BSF SSPCON2,RCEN ; activa modo receptor....DATEN EMPFANG EINSCHALTEN
BCF STATUS,RP0 ; Restore Bank0
BTFSS PIR1,SSPIF ; ACK recibido?
GOTO $-1 ; no, espera
BCF PIR1,SSPIF
MOVF SSPBUF,W ; carga dato
MOVWF MensajeIn
; BSF STATUS,RP0 ; Restore Bank1...........EIN ANDERES BYTE SOLL GELESEN WERDEN
; BCF STATUS,RP1
; BSF SSPCON2,ACKEN ; Genera ACK
; BCF STATUS,RP0 ; Restore Bank0
banksel SSPCON2
bsf SSPCON2,RSEN
I2C.Restart.Wait
btfsc SSPCON2,RSEN
GOTO I2C.Restart.Wait
BCF STATUS,RP0
RETURN
END
Slave1 - TRANSMITE EL BYTE AL MASTER
;Definición de constantes
#define DirNodo b'01111000' ;Dirección I2C de este nodo
;Definición de variables
cblock 0x20
MensajeIn ;Contendrá el dato recibido por I2C del master
MensajeOut ;Contendrá el dato a enviar por I2C al master
BkStatus ;Backup del registro STATUS
BkW ;Backup W
Temp ;Variable Temporal usada para evaluación de eventos I2C
endc ;Fin de definiciones
org 0
goto INICIO
org 4
;-------------------------------------------------------------------------------
Interrupcion ;RUTINA DE INTERRUPCIÓN. Se ocupa de los eventos I2C
;-------------------------------------------------------------------------------
;Guardamos copia de algunos registros
movwf BkW ;Hace copia de W
movf STATUS,W ;Hace copia de registro de estado
banksel PORTA
movwf BkStatus
;Chequeamos si la interrupción es por evento I2C. En caso positivo llamamos
;a la rutina de proceso del evento
banksel PIR1
btfss PIR1,SSPIF ;Ha ocurrido un evento SSP? (I2C)
goto IntNoSSP ;No. entonces será por otra cosa. Saltamos.
call SSP_Handler ;Si. Procesamos el evento. Si se reciben ordenes, quedarán
;registradas en "MensajeIn". Se enviarán las ordenes
;guardadas en "MensajeOut".
banksel PIR1
bcf PIR1,SSPIF ;Limpiamos el flag
goto Rest
IntNoSSP ;Aquí se gestionan interrupciones que no son por SSP
;..........
; En caso de necesitarse, poner aquí la rutina de gestión de interrupciones
; que no sean por bus I2C
;..........
Rest ;Restauramos las copias de los registros
movf BkStatus,W ;Restaura las copias de registros
movwf STATUS ;registro de estado
movf BkW,W ;registro W
retfie
;-------------------------------------------------------------------------------
INICIO ;Inicio del cuerpo del programa
banksel TRISB ;Apunta a banco 1
movlw b'11111111' ;Salida (Leds)
movwf TRISB ;
banksel PORTB ;Apunta a banco 0
clrf PORTB ;Limpia puerto B
call init_i2c_Slave ;Configuración para uso de i2c
banksel INTCON
bsf INTCON,GIE ;Activamos las interrupciones
banksel MensajeIn
clrf MensajeIn
clrf MensajeOut
MLoop
movf PORTB,W ;dato cualquiera por I2C que metemos en
movwf MensajeOut ;"MensajeOut" para cuando el Master se lo pida
goto MLoop
;*********************************************************************************
; SUBRUTINAS
;*********************************************************************************
;-------------------------------------------------------------------------------
init_i2c_Slave ;Inicializa valores para uso de I2C en Slave
;Ha de ser llamado tras definir TRISC y un valor para
;ClockValue. Para frecuencia SCL=Fosc/(4x(ClockValue+1))
;-------------------------------------------------------------------------------
;Guardamos copia de algunos registros
movwf BkW ;Hace copia de W
movf STATUS,W ;Hace copia de registro de estado
banksel PORTA
movwf BkStatus
;Configuramos I2C
banksel TRISC ; Pasamos a direccionar Banco 1
movlw b'00011000' ; Establece líneas SDA y SCL como entradas...
iorwf TRISC,f ;..respetando los valores para otras líneas.
bcf SSPSTAT,CKE ; Establece I2C input levels
bcf SSPSTAT,SMP ; Habilita slew rate
bsf SSPCON2,GCEN ; Habilita direccionamiento global
movlw DirNodo ; Dirección esclavo
movwf SSPADD ;
banksel SSPCON ; Pasamos a direccionar Banco 0
movlw b'00110110' ; Slave mode, SSP enable, velocidad segun...
movwf SSPCON ; ... Fosc/(4x(SSPADD+1))
bcf PIR1,SSPIF ; Limpia flag de eventos SSP
bcf PIR1,7 ; Limpia bit. Mandatorio por Datasheet
;Configuración para interrupciones por evento I2C
banksel PIE1
bsf PIE1,SSPIE
bsf INTCON,PEIE
;Restauramos las copias de los registros
movf BkStatus,W ;Restaura las copias de registros
movwf STATUS ;registro de estado
movf BkW,W ;registro W
return
; --------------------------------------------------------------------------------------
SSP_Handler ; Este manejador controla cada evento SSP (I2C) acontecido.
; El código que se muestra abajo chequea 5 posibles estados.
; Cada uno de los 5 estados SSP son identificados haciendo
; XOR de los bits del registro SSPSTAT con mascaras de bits
; predeterminadas. Una vez que el estado ha sido identificado
; se llevan a cabo las acciones pertinentes. Los estados
; indefinidos son considerados como estados de error.
; State 1: Operación de escritura I2C, ultimo byte era de dirección.
; SSPSTAT bits: S = 1, D_A = 0, R_W = 0, BF = 1
; State 2: Operación de escritura I2C, ultimo byte era de datos.
; SSPSTAT bits: S = 1, D_A = 1, R_W = 0, BF = 1
; State 3: Operación de lectura I2C, ultimo byte era de dirección.
; SSPSTAT bits: S = 1, D_A = 0, R_W = 1, BF = 0
; State 4: Operación de lectura I2C, ultimo byte era de datos.
; SSPSTAT bits: S = 1, D_A = 1, R_W = 1, BF = 0
; State 5: Reset lógico del Slave I2C por NACK del master.
; SSPSTAT bits: S = 1, D_A = 1, R_W = 0, BF = 0
; --------------------------------------------------------------------------------------
banksel SSPSTAT
movf SSPSTAT,W ; Obtiene el valor de SSPSTAT
andlw b'00101101' ; elimina los bits no importantes SSPSTAT.
banksel Temp
movwf Temp ; para chequeo posterior.
State1: ; Operación de escritura, ultimo byte ha sido
movlw b'00001001' ; de dirección, el buffer está lleno.
banksel Temp
xorwf Temp,W ;
btfss STATUS,Z ; Estamos en el primer estado?
goto State2 ; No, checkeamos siguiente estado
call ReadI2C ; SI. Hacemos un read SSPBUF (para vaciar buffer).
; El Hardware se ocupa de mandar Ack
return
State2: ; Operación de escritura, ultimo byte ha sido
movlw b'00101001' ; de datos, el buffer está lleno.
banksel Temp
xorwf Temp,W
btfss STATUS,Z ; Estamos en el segundo estado?
goto State3 ; NO, checkeamos siguiente estado
call ReadI2C ; SI, Tomamos el byte del SSP.
;Aquí tenemos en W el valor del dato recibido
movwf MensajeIn
return
State3: ; Operación de lectura, ultimo byte ha sido
movlw b'00001100' ; de dirección, el buffer está vacío
banksel Temp
xorwf Temp,W
btfss STATUS,Z ; Estamos en el tercer estado?
goto State4 ; NO, checkeamos siguiente estado
;Aquí debemos poner en W el valor del dato a enviar (solicitado por el master)
movf MensajeOut,W
call WriteI2C ; SI, escribimos el byte en SSPBUF
return
State4: ; Operación de lectura, ultimo byte ha sido
movlw b'00101100' ; de datos, el buffer está vacío
banksel Temp
xorwf Temp,W
btfss STATUS,Z ; Estamos en el cuarto estado?
goto State5 ; NO, checkeamos siguiente estado
;Aquí debemos poner en W el valor del dato a enviar (solicitado por el master)
movf MensajeOut,W
call WriteI2C ; SI, escribimos el byte en SSPBUF
return
State5:
movlw b'00101000' ; Se ha recibido un NACK mientras se transmitían...
banksel Temp
xorwf Temp,W ; ..datos al master. Lo lógica del Slave..
btfss STATUS,Z ; ..se resetea en este caso. R_W = 0, D_A = 1
goto I2CErr ; y BF = 0
return ; Si no estamos en State5, entonces es
; que algo fue mal
I2CErr nop ; Algo fue mal
return
;---------------------------------------------------------------------
WriteI2C ;Usada por SSP_Handler para escribir datos en bus I2C
;---------------------------------------------------------------------
banksel SSPCON
movwf SSPBUF ; Escribe el dato en W
bsf SSPCON,CKP ; Libera el reloj
return
;---------------------------------------------------------------------
ReadI2C ;Usada por SSP_Handler para escribir datos en bus I2C
;---------------------------------------------------------------------
banksel SSPBUF
movf SSPBUF,W ; Toma el byte y lo guarda en W
return
END
Falta el programa del Slave 2, pero el mensaje excede de la longitud máxima permitida. Si te hace falta te lo envio...
Espero que puedas ayudarme....