I2C: comunicacion entre dos pics. Master solicita datos...

Hola,
Estoy programando (en CCS) dos pics que se comunican entre ellos por i2c (16f88 y 16f873a). Uno de ellos (pic16f873a) controla un modulo LCD de 20x4.
Envio datos de master a slave continuamente y el slave los recibe pero quiero poder controlar la secuencia dado que el pic principal tiene mas tareas a parte de atender al bus I2C, y si creara una interrupcion esta estaria funcionando siempre, y si por otro lado leyera el bus I2C en un momento, probablemente el emisor no estuviera sincronizado (si no recibe bit de ack no deberia enviar nada, pero no se como funciona en realidad).

Actualmente el master tiene un adc y envia el dato a el slave que es el pic16f873a, querria cambiarlo y que el pic16f873a fuera el master y el pic16f88 el esclavo, pero tendria que gestionar el envio de datos para que fuera el master el que enviara una peticion a el esclavo y entonces este le enviara el dato, y no se como hacerlo.

--------------------------------------------------------------------------------------------
He probado leyendo i2c_isr_state() para ver si en algun momento dejaba de emitir el master pero esta siempre emitiendo (en el lcd tengo puesto el estado y el dato y el estado va alternando entre 0,1 y 2. Y el dato es adress, dato1 y dato2).

Tambien he probado con la interrupcion #INT_SSP
void ssp_interupt ()
pero como no controlo en envio en el master (siempre envia) esta siempre activada en el esclavo...
Igualmente habia visto que la comunicacion se finalizaba escribiendo de slave a master i2c_write(0) pero no obtengo los resultados esperados...

Espero que alguien pueda explicarme el proceso. No he visto mucha informacion en cuanto a controlar en envio y una peticion por parte del master, solo el ejemplo que viene en ccs sobre el control de una memoria i2c pero no lo entiendo cuando trabaja con los registros...

Por otro lado, tengo otra duda, y es como enviar un dato de 10bits (del adc) a traves de i2c que envia (creo) solo 8bit. Habia pensado en enviar dos nibbles pero (a demas de que no me acuerdo de la instruccion para separar el dato y luego juntarlo) debe haber una forma mas sencilla... De hecho hace algunas semanas consegui enviar el dato y me funcionaba, pero modifique el programa y no me acuerdo de que hice...

Gracias
 
Como norma general (http://www.best-microcontroller-projects.com/i2c-tutorial.html):
1. Send the START bit (S).
2. Send the slave address (ADDR).
3. Send the Read(R)-1 / Write(W)-0 bit.
4. Wait for/Send an acknowledge bit (A).
5. Send/Receive the data byte (8 bits) (DATA).
6. Expect/Send acknowledge bit (A).
7. Send the STOP bit (P).

Traduciendo a css, seria:
1. I2c_start();
2. i2c_write(slaveaddress);
3. i2c_write(0); // para escribir en el bus
i2c_write(1); //para leer del bus
4. //No se si esta incluido en I2c_read(), si es otra funcion o si hay que leer i2c_isr_state();.
5. data=I2c_read(); //Para leer un dato (habiendo enviado i2c_write(1) )
I2c_write(data); //Para escribir un dato (habiendo enviado i2c_write(0) )
6. //No se si esta incluido en I2c_read(), en i2c_stop(), si es otra funcion o si hay que leer i2c_isr_state();.
7. i2c_stop();

Esta bien el codigo?
Como vería en el esclavo que el master esta pidiéndole un dato? entraría automáticamente en la interrupción void ssp_interupt () al enviar el master su direccion? seria entonces cuando se pone
state = i2c_isr_state();
if(state < 0x80) //Master esta enviando datos
{
if(state == 0) //no entiendo que es lo que hace aqui. Pata recibir dato ya esta despues if(state == 0x80)
{
dato = i2c_read();
}
if(state == 1) //El primer byte es la direccion
{
address = i2c_read();
}
if(state == 2) //El segundo byte es el dato
{
buffer[address] = i2c_read();
}
}

if(state == 0x80) //master pide un dato
{
i2c_write(d);
}
-----------------------------------------------
El dato que lees o recibes, ¿tiene que ser variable byte o int8? he probado con float e int16 y luego en printf poniendo %s, %f, %d... y obtengo diferentes resultados (ninguno satisfactorio, a no ser que envie char para cada numero)...

El pic 16f873a como master por hardware y el 16f88 como esclavo por hardware estan bien? segun he visto en los datasheets soportan estos modos, y para poder usar interrupciones necesito que sean modos hardware.
 
Última edición:
El codigo del esclavo es este, lo unico que hace el main es poner "ready" al encender y luego "while" en la pantalla (para avisar de que no esta dentro de la interrupcion) y enviar un caracter por i2c (por si acaso el master recive algo.. xd), pero todabia no estoy probando el envio, primero quiero recibir datos en el esclavo. He comprobado que la interrupcion funciona solo cuando el master envia su direccion (0xa0), asi que la interrupcion funciona bien, pero luego en state siempre es "00" y en data "a1" (ambos en hexadecimal), con lo que no funciona la rutina de comprobar si es mayor o menos de 0x80 y si esta enviando o recibiendo etc... (en el codigo que muestro esta borrado). A veces modificando cosas he conseguido que varien estos datos, pero funcionan mal otras cosas... por ejemplo a veces funciona si pongo "#INT_SSP NOCLEAR" y luego al inicio de la interrupcion pongo "clear ssp_interrupt" (me parece que era esta la sintaxis, lo borre hace tiempo (horas xd))

codigo slave:
#include <16F88.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOPUT //No Power Up Timer
#FUSES MCLR //Master Clear pin enabled
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOPROTECT //Code not protected from reading
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES RESERVED //Used to set the reserved FUSE bits
#use delay(clock=20000000)
#include <flex_lcd_cF88.c>

#define I2C_SDA PIN_B1
#define I2C_SCL PIN_B4
#use i2c(slave,slow,sda=I2C_SDA,scl=I2C_SCL,address=0xA0,force_hw)

BYTE address, ad;

#INT_SSP
void i2c_isr()
{

printf(LCD_PUTC, "\fI2Cisr:");
delay_ms(1000);

BYTE state;

state = i2c_isr_state();
ad = i2c_read();

printf(LCD_PUTC, "\fstate:%x\n",state);
printf(LCD_PUTC, "data:%x",ad);
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
lcd_putc("\f");
}


void main()
{
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);

lcd_init();
lcd_putc("\f");
lcd_putc("\fReady...\n");
delay_ms(2000);
lcd_putc("\f");
while (TRUE)
{
lcd_putc("\f");
lcd_putc("while");
i2c_write('s');
}
}

En el codigo master, lo unico que hace es enviar un caracter y luego leer otro. No hace ninguna de las dos cosas XD. En "#use_i2c" si pongo "force_hw" se bloquea a veces (se queda la pantalla en blanco o con todo encendido (cuadrados))... ahora lo estoy utilizando y no se bloquea.
codigo master:
#include <16F873A.h>
#device adc=8

#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#FUSES RESERVED //Used to set the reserved FUSE bits

#use delay(clock=20000000)

#include "C:\Users\Dani\Desktop\exm\css\f873lcd.h"
#include <flex_lcd_cf873.c>
#define I2C_SCL PIN_C3
#define I2C_SDA PIN_C4
#use i2c(master,sda=I2C_SDA,scl=I2C_SCL)

INT i;
float data,data2;
float state;
int dat1, dat2;

void main()
{
lcd_init();
while(TRUE)
{
i2c_start();
i2c_write(0xA0);
delay_ms(100);
i2c_write(0x00);
delay_ms(100);
i2c_write('H');
i2c_stop();
lcd_putc("H");
i2c_start();
i2c_write(0xA0);
i2c_write(0x00);
i2c_start();
i2c_write(0xA1);
data = i2c_read(0);
i2c_stop();
printf(lcd_putc," data:%c,%x \n", data,data);
delay_ms(1500);
lcd_putc("\f");
delay_ms(1000);
}
}
 
Última edición:
Alguien puede ayudarme?
Tengo la impresion de que uno de mis principales problemas es de configuracion de algun parametro o de limitaciones del compilador, porque me funcionan de diferente forma la comunicacion i2c segun si pongo force_hw o force_sw o nada, o si digo que borre el estado de las interrupciones... tambien he visto que algunas veces desactivan las interrupciones al entrar en la inturrepcion y las activan al salir.
Ahora tengo los dos con force_hw y no parece enviar nada el uno al otro...
 
Adjunto una imagen de mi osciloscopio donde se puede ver en la parte superior la trama i2c completa y debajo la primera palabra ampliada (corresponde a la direccion (7bit), el bit de R/W (1bit) y el ACK (1bit)).

Ambos pics (maestro y esclavo) tienen configurado force_hw.

En el master tengo esto en main (he probado tambien quitando los retardos entre instrucciones i2c pero estos creo que estan bien. He comprobado con el osciloscopio lo que dura cada trama i2c y me parece adecuado):
while(1)
{
delay_ms(200);
lcd_putc("i2cstart");

i2c_start();
delay_us(8);
i2c_write(0xA0);
delay_us(8);
i2c_write(5);
delay_us(8);
i2c_write(12);
delay_us(8);
i2c_stop();

delay_ms(50);
printf(LCD_PUTC, "\fdelay ");
delay_ms(1000);
lcd_putc(" 1");
delay_ms(1000);
lcd_putc("2");
delay_ms(1000);
lcd_putc("3");
delay_ms(1000);
lcd_putc("4");
delay_ms(1000);
lcd_putc("5");
delay_ms(1000);
}

En el esclavo tengo el programa que hay en muchos ejemplos que discrimina el status (si es 00, 0x80...).
Empieza asi:
#INT_SSP //NOCLEAR
void i2c_isr()
{
....
}

El master envia el dato y cuenta 5 segundos. El esclavo parpadea el caracter '*' (es lo que hace el main) y muestra en la pantalla '1', que corresponde a el state 00, donde el master envia la direccion del esclavo.
El problema que tengo es que no se porque no avanza. Si en vez de leer en la interrupcion la desactivo y leo en el main el state avanza indefinidamente hasta que se bloquea y bloquea al master. Observando en el osciloscopio deja data a 0v en vez de dejarlo a nivel alto (nunca termina el envio de i2c).
El status debería cambiar automaticamente al haber usado read_i2c(), no?
En cuanto a la señal, el master envia el reloj y los datos, para que el esclavo envie un ack o informacion tiene que usar i2c_start/i2c_write()/i2c_stop()? crea el esclavo el reloj tambien?
 

Adjuntos

  • fffffle1.jpg
    fffffle1.jpg
    46.9 KB · Visitas: 47
Tenia desactualizada la version del compilador ccs. He actualizado a la 4.120 (no es la ultima pero es la ultima que he encontrado... la ultima es la 4.123). He compilado nuevamente los hex y los he flasheado y creo que no ha cambiado nada... probare mas cosas
A parte de recibir datos incorrectos y de que cuando el master envia una secuencia de clock para recibir datos, data esta a nivel alto (lo he visto con el osciloscopio) y siempre me indica ff; el problema que tengo es que state esta siempre a 00.
 
Probando con el pickit serial analyzer me comunico correctamente con el master pero no lo consigo con el esclavo. He probado con el 16f88, con el 16f873 y con el 16f886 como esclavos y ninguno funciona bien. Puede ser un error del compilador (ccs)? pensaba en cambiarme de lenguaje pero me da muchisima pereza porque utilizo librerias modificadas para controlar los lcd y en hitec que es al que me iba a pasar no he encontrado como hacerlas... a demas ccd es muy sencillo (alto nivel) y me evita complicaciones, claro que si luego el interpretado esta mal (como puede que me pase ahora) me lia mucho mas...
 
Envio tres bytes y los recivo en el slave, el slave envia dos y los recivo en el master, pero pasado un rato, el slave pasa el status a 00 otra vez, mirando con el osciloscopio scl se queda a 5V y sda a 0v, y el master se queda bloqueado antes de i2c_start (el bus esta ocupado) y en cuanto reinicio o apago el esclavo responde inmediatamente.

codigo master
#include <16f873a.h>
#device adc=8
#FUSES HS //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#use delay(clock=20000000)

#define I2C_SCL PIN_C3
#define I2C_SDA PIN_C4
#use i2c(master,sda=PIN_C4,scl=PIN_C3,force_hw) //873 slow,force_hw
#include <flex_lcd_cf873.c> //873


INT i;
byte data,data0,data1;
float data2;
float state;
int dat1, dat2,slaveaddress1;
char voltageac[]="1234";

void main()
{
lcd_init();
int ad,a,b,c,d,e,f,zz;
lcd_putc("\f");
lcd_putc("ready...");
delay_ms(2000);
lcd_putc("\f");
lcd_putc("start");
ad=5;
while(1)
{
delay_ms(6);
lcd_putc("i2cstart");

i2c_start();
i2c_write(0xA0);
delay_us(10);
i2c_write(0xb7);
delay_us(10);
i2c_write(0xb8);
delay_us(10);
i2c_stop();
delay_us(3);
i2c_start();
i2c_write(0xA1);
data0 = i2c_read();
data1 = i2c_read();
data = i2c_read(0);
delay_us(10);
i2c_stop ();

delay_ms(10);
printf(LCD_PUTC, " %x,%x,%x",data0,data1,data);
delay_ms(700);
printf(LCD_PUTC, "\f");
}
}



codigo slave
#include <16F88.h>
#FUSES HS
#byte SSPCON1=0xFC6

#FUSES NOPUT //No Power Up Timer
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected

#use delay(clock=20000000)


#include <flex_lcd_cX.c> //88
#use i2c(slave,sda=PIN_B1,scl=PIN_B4,address=0xa0,slow,force_hw)

int i,j,x;
byte state,address,y,z,buffer[],send_buffer[],rcv_buffer[],r0,r1,r2,r00;
int rcv_buf[0x10];

#INT_SSP //NOCLEAR
void i2c_isr() //void ssp_interupt()
{
state = i2c_isr_state();
if(state >= 0x80)
{
if (state==0x80)
{
i2c_write(0x01);
}
if (state==0x81)
{
i2c_write(0x02);
}
if (state==0x82)
{
i2c_write(0x03);
}
if (state>0x82)
{
i2c_write(0);
}
}
if(state < 0x80)
{
r00=i2c_read();

if (state==0x00)
{
r0=r00;
}
if (state==0x01)
{
r1=r00;
}
else if (state > 1)
{
rcv_buf[state-2]=r00;
}
}
}
void main()
{
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
lcd_init();
lcd_gotoxy(1,1);
byte a,b,c,d,e;
x=0;
while(1)
{
lcd_gotoxy(1,1);
lcd_putc("*");
printf(LCD_PUTC, ":%x,%x,%x\n",r0,r1,state);
printf(LCD_PUTC, ":%x,%x,%x,%x",rcv_buf[0],rcv_buf[1],rcv_buf[2],rcv_buf[3]);
delay_ms(80);
lcd_gotoxy(1,1);
lcd_putc(" ");
delay_ms(50);
}
}

Si conecto el pickit serial analyzer al master este envia los datos correctamente, si lo hago al slave (con el pickit como master) funcionan todas las funciones (receive, write, read) hasta un momento, pasados unos minutos, en que deja de hacerlo y el bus se pone en el mismo estado de error que el descrito antes.
 
Última edición:
activando watchdog
se recupera del error (scl=1, sda=0) y sigue funcionando con normalidad, hasta el siguiente error (que se reinicia nuevamente y sigue funcionando bien...). El error salta en un instante aleatorio y, usando el watchdog hay veces que el error dura varios segundos hasta que se corrige (otras lo hace inmediatemente).
El uso de watchdog es exclusivo de programas terminados con problemas puntuales no contemplados, así que debería solucionar el problema mejor...

He probado con diferentes resistencias pullup, con diferentes configuraciones de fuses, con diferentes pics, he comprobado que la tension de alimentacion es estable...
Seguiré probando. No se cual puede ser el error...
 
he sacado el esclavo a una protoboard (lo tenia en la placa final) y sigue pasando lo mismo, asi que descarto error de conexionado, aunque tanto en la protoboard como en la placa, los puertos que no utilizo estan al aire y no he encontrado en el datasheet nada que indique que hay que ponerles resistencias de pull down y no se si no habra que hacerlo... el datasheet habla de las resistencias pull up que incluye en puerto b pero no habla de los demas.

Adjunto una imagen de la secuencia temporal despues del encendido. La primera transmision la efectua correctamente y despues la hace cada 1'5 segundos (practicamente es como si enviara la informacion, dejara 1'5s de delay y luego pusiera i2c_stop, aunque no sube por i2c_stop, sino porque el watchdog ha hecho un reset).
 

Adjuntos

  • dehfile0.jpg
    dehfile0.jpg
    39.3 KB · Visitas: 16
Hola..es la desventaja del C..no se puede entender las funciones de libreria sino se entiende el ejemplo que trae la ayuda o bien no funciona esta uno al horno..quedan 2: o se traduce el .list que genera al compilar o usas #asm /#endasm y haces la transmisión en asm. una vez hice una comunicación entre 2 pics pero ahora no la encuentro veré si te la busco mañana mas descansado bye
 
Atrás
Arriba