Si además quieres enviarnos un Artículo para el Blog y redes sociales, pulsa el siguiente botón:
Hola a tod@s
Estoy intentando que se comuniquen dos 16F876A mediante I2C (un maestro y un esclavo) en C (CCS) ¿algun@ sabe cómo manejarse con las funciones?... por ejemplo... para enviar un dato al esclavo, según el protocolo I2C debería...
1º) Enviar un pulso de inicio (supuestamente con: i2c_start())
2º) Enviar la dirección a escribir (supongo que con: i2c_write(DIRECCION))
3º) Enviar el bit de escritura (y eso cómo lo hago? :S )
4º) Recibo el pulso de reconocimiento (lo recibo con i2c_write()?... cómo?... si eso ocurre en el paso 2º y el pulso ACK debo recibirlo en el 4º paso? O_o )
5º) Enviar el dato (supongo i2c_write())
6º) Recibir pulso ACK (en este caso es comprensible que me lo pueda dar i2c_write pues no hay bit de lectura escritura de por medio)
7º) Enviar bit de stop (con i2c_stop)
Tengo también una duda sobre el registro sspadd que contiene la dirección del esclavo en modo de 7 bits. Si no recuerdo mal, se comparan los 7 bits más altos de SSPBUF con los correspondientes de SSPADD en modo esclavo... si quisiera meter el bit de lectura/escritura en modo maestro... debería incluirlo en la dirección enviada en el paso 2º?... es decir... el dato de dirección serían los 7 bits de más peso y el octavo sería el de lectura escritura (usando algo así: i2c_write(DIRECCION + BIT);)
En fin... tengo un liiio :S . Agradezco culaquier sugerencia/comentario.
GRACIAS
Mosvack, ya sabes mucho mas que yo, pero el tema me interesa mucho... Animo en tu investigacion, postea tus progresos, que siempre son una ayuda para inutilillos como yo
Hola Roboter@s!
Parece que he logrado desenmarañar un poco cómo funciona el I2C programado usando CCS...
Conecté dos PIC16F876A vía I2C, con sus resistencias de pullup, y usé el puerto B de cada uno como "display" para mostrar los datos enviados y recibidos, además de los generados. Los dos PICs tenían la patita A0 analógica, y mediante un potenciómetro (cada uno el suyo), podía variar el valor binario reflejado en el puerto B de cada uno. Además, en el puerto C y A disponía de algunas líneas para encencer otros leds para indicar si se usaban o no ciertas funciones, y líneas para dar órdenes...
Así el maestro tenía una línea para cambiar lo mostrado en el puerto B, de tal forma que podía mostrar el dato del potenciómetro, o bien el último dato recibido del esclavo. El esclavo por su parte podía hacer lo mismo respecto al maestro, es decir, podía mostrar el valor de su potenciómetro, o el último dato recibido por el maestro. El maestro disponía además de una línea para dar la orden de enviar el dato que provenía de su potenciómetro al esclavo, y otra para decirle al esclavo que le mandase el valor de su potenciómetro. En resumen:
Maestro:
A0 = Captura analógica
A1 = Mostrar dato captura/Mostrar último dato recibido
A2 = Leer dato del esclavo
A3 = Escribir dato en el esclavo.
Esclavo:
A0= Captura analógica
A1= Mostrar dato captura/ Mostrar último dato recibido
Realicé varias pruebas y al final logré que el esclavo recibiera el dato del maestro, pero no a la inversa. Me sorprendió comprobar que cuando el maestro enviaba la dirección al esclavo en una lectura, no se recibía el bit ACK, y no sólo eso, sino que el esclavo no disparaba la interrupción por actividad en el puerto I2C. Esto lo sé porque coloqué funciones que encendían ciertos leds que coloqué en las patitas libres de los PIC. De esto deduje que el disparo de la rutina de interrupción en el esclavo no se produce por las mismas causas que se produce en un PIC (como por ejemplo, al detectar la condición de inicio...) pues de ser así, se dispararía la rutina tanto en los envios del maestro como en las recepciones...
Probé a cambiarle al esclavo la dirección que originalmente le había puesto (A0) por A1. Soprendentemente obtuve el mismo resultado (es decir, funcionan las escrituras en el esclavo, pero no las lecturas del esclavo). Digo sorprendentemente porque, a pesar de que sabía que la dirección era de 7 bits, y que el último bit no se transmitía, esperaba que quizá la causa del fallo estuviera en que el PIC esclavo comparaba su dirección con el byte de dirección completo (incluyendo el bit de R/W#), por lo que esperaba que ahora funcionasen las lecturas y no las escrituras.
Cambié la dirección del esclavo a A2, y en este caso no funcionó ni la lectura ni la escritura....
Probé a inhibir la activación del permiso de recepción (RCEN) en el código y obtuve un resultado sorprendente: en este caso se disparaba la interrupción del esclavo tanto en la lectura como en la escritura, si bien la lectura funcionaba mal...
(fragmento de código)
if(input(PIN_A2))
{
// Rutina de petición de dato
// bit_set(sspcon2,3); <- ACTIVACIÓN DE RCEN INHIBIDA!!
i2c_start();
output_bit(PIN_A5,i2c_write(DIREC)); // Ilumina A5 en caso de no recibir ACK
valor = i2c_read(); // Dato recibido del esclavo
i2c_stop();
delay_ms(1000); // Retardo para no enviar varias peticiones al pulsar
}
if(input(PIN_A3))
{
// Rutina de envío de dato
bit_clear(sspcon2,3);
i2c_start();
output_bit(PIN_A5,i2c_write(DIREC));
output_bit(PIN_C0,i2c_write(captura)); // Dato
i2c_stop();
delay_ms(1000); // Retardo para no enviar varias peticiones
}
(antes del main() se definió : #BYTE sspcon2 = 0x91 )
De todo esto deduzco:
El registro SPADD que guarda la dirección es comparado al completo, tomando como cero el valor del bit de menos peso, con el primer byte transferido, incluyendo el byte de R/W#. Si las direcciones coinciden, se dispara la interrupción. En este caso, se debe leer el SSPBUF (con i2c_read()), a fin de permitir la llegada del próximo (o próximos) byte(s).
Por tanto, si queremos leer algo del esclavo seguramente debemos enviar un byte a modo de orden para indicar al esclavo que a continuación debe enviarnos los bytes... es decir... el bit R/W# no sirve para nada, salvo para impedir que el esclavo responda xD
Probaré esto último que he dicho y os contaré qué tal me ha ido. Agradecería cualquier comentario (si es que alguien no ha caido sobre el teclado, antes de llegar aquí, presa del sueño) o pregunta sobre el I2C, que siempre me ayudará a aclararme.
GRACIAS 😀
Mi unico comentario es que me has dejado "acojonao" con todo lo que has conseguido en menos de dos días... creo que tengo cosas interesantes para poner en marcha tus ideas... que tal mandar datos por USB a un 18F2550, y que este retransmita a los dispositivos I2C... puede ser un proyecto de Domótica interesante... y... mmm... mmm... ¡¡¡Qué guapo!!! Si a las Skylamps® se las dotase de I2C, se podrian hacer efectos de luz muy guapos en la Campus 2007...
🙁 nada... no se me ocurre nada de nada. Probé a enviar el byte de "orden" pero no sirvió para nada... Creo que ocurre lo siguiente:
* Si se envía una petición de escritura sobre el esclavo: Todo de maravilla... el esclavo recibe el pulso de start, luego la dirección de 7 bits + el bit de R/W#, que en este caso es cero... El esclavo reconoce la dirección, genera el ACK# y entra en la función de interrupción ... todas las escrituras que haga el maestro a partir de ahí sobre el esclavo funcionan correctamente...
* Si se envía una petición de lectura sobre el esclavo: el maestro envía el pulso de start, se envía la dirección + el bit R/W# que vale 1... El esclavo ... no reconoce la dirección, pues ni genera el pulso de ACK# para el maestro, ni entra en la función de interrupción... Cualquier intento de lectura por parte del maestro sólo logra leer "1" (es decir ... paquetes de 11111111) y ello seguramente se debe a que el esclavo no escribe nada sobre la línea SDA que como está a nivel alto por el pullup, pues sólo lee "1".
Es muy posible que el esclavo no pueda escribir en la línea SDA si el bit R/W# del registro SSPSTAT no se carga con un 1, y esto lo hace al recibir el 8º bit del byte de dirección... ¿qué ocurre pues?... pues que como no se dispara la interrupción ni se genera pulso de ACK# (igual que si hubiese enviado una dirección distinta a la del esclavo) si este bit vale 1, no tengo forma de descubrir un intento de lectura por parte del maestro, y por tanto no sé cuándo puedo escribir en la línea SDA... o sea, la pescadilla que se muerde la cola... :S
En teoría, la función de interrupción por actividad I2C debería activarse en idénticas condiciones tanto en la lectura como en la escritura por parte del maestro... pero ya he comprobado que no ocurre así. En la escritura sobre el esclavo, éste responde como cabe esperar, entrando en la función de interrupción al detectar que la dirección transmitida por el bus es igual que la propia (almacenada en SSPADD), pero al colocar a 1 el bit R/W# para producir una lectura (Esto se hace poniendo RCEN=1 en el registro SSPCON2) el esclavo actúa como si no reconociera la dirección transmitica, esto es, no genera el pulso ACK# y no entra en la función de interrupción.
Por tanto, me da la impresión de que "con un bug hemos topado"... o eso, o no sé cómo hacerlo....
Nuevamente agradecería cualquier comentario/sugerencia. GRACIAS
P.D: Mif es un proyecto genial... pero me parece que al final habrá que manejar el I2C en ASM xD