Si además quieres enviarnos un Artículo para el Blog y redes sociales, pulsa el siguiente botón:
Estoy tratando de comunicar dos PIC16F877 via I2C, utilizando lenguaje C para PIC (PICC), y hasta ahora no lo he logrado. 😕 no encuentro la manera que el PIC maestro se enlace con el PIC esclavo, necesito algún tipo de información, tutorial o enlace de alguna página que pueda ayudarme por favor. 😆
Hola Rick.
Yo tuve grandes problemas con el I2C hace poco. Hice lo siguiente: monté dos 16F876A, uno como maestro y otro como esclavo. Los puertos B de ambos PICs los conecté a 8 Leds cada uno (con sus respectivas resistencias) a fin de mostrar los bytes que se intercambiarían los PICs. ¿Cómo introducir los bytes?... yo opté por tomar un par de potenciómetros y conectarlos entre 0 y 5V... al capturar la señal de tensión analógica, obtenía un valor entre 0 y 255 que mostraba en el puerto B del PIC correspondiente. Además, ambos PICs podían mostrar en su puerto B el último byte recibido o enviado según fuera el caso.... Al final, después de hacer muchas pruebas logré una transmisión bidireccional. Te pego el fragmento de mi diario de trabajo del día en que lo logré a continuación. Si necesitas más aclaraciones, no dudes en preguntar 🙂
_______________________
¡EUREKA! (14/08/2006)
Al fin hemos logrado una transferencia bidireccional. El último escollo consistía en que el bit CKP no podía ser establecido a 1, al enviar el byte al maestro. Ello provocaba que tanto el maestro como el esclavo se quedaran colgados; el maestro por tener la línea CLK a cero, y el esclavo por esperar indefinidamente a recibir el pulso de STOP. Cuando desconectaba la línea de reloj del esclavo, el maestro podía enviar el pulso, si bien al volver a conectar el esclavo, ya no podía. El código fuente del esclavo era el siguiente:
Archivo: I2C_Slave_4.h
#include <16F876A>
#device *=16
#device adc=8
#use delay(clock=10000000)
#fuses NOWDT,HS, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#use i2c(Slave,Slow,sda=PIN_C4,scl=PIN_C3,force_hw,address=0xA0)
Archivo: I2C_Slave_4.c
#include "C:TrabajosElectrónicaPICArchivos CCCS PCWI2C_Slave_4I2C_Slave_4.h"
#include <assert>
#include <ctype>
#include <errno>
#include <float>
#include <limits>
#include <locale>
#include <math>
#include <setjmp>
#include <stdio>
#include <stddef>
#include <stdlib>
#include <stdlibm>
#include <string>
int valor, captura;
#byte sspcon1 = 20
#byte sspadd = 147
#byte sspstat = 148
#byte sspcon2 = 145
#byte sspbuf = 19
#byte status = 3
#byte pir1 = 12
#int_SSP
SSP_isr()
{
int direcc;
output_bit(PIN_A2,1);
while(!bit_test(pir1,3));
bit_clear(pir1,3);
#asm
movf sspbuf,0
movwf direcc
#endasm
if(bit_test(direcc,0) == 0)
{
bit_set(sspcon1,4); // Desbloqueo de reloj...
while(!bit_test(pir1,3));
bit_clear(pir1,3);
#asm
movf sspbuf,0
movwf valor
#endasm
output_bit(PIN_A3,1);
}else{
#asm
movf captura,0
movwf sspbuf
#endasm
bit_set(sspcon1,4);
while(!bit_test(pir1,3));
bit_clear(pir1,3);
output_bit(PIN_A5,1);
}
/* No se recibe pulso de stop... por culpa del CKP... pero tampoco obedece a su puesta a 1 */
bit_set(sspcon1,4); // <- No funciona... con la transferencia... sí con la recepción
if(bit_test(sspcon1,4)) output_bit(PIN_C0,1);
while(!bit_test(sspstat,4)); // En transmisión al maestro... bloquea al esclavo
}
void main()
{
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_INTERNAL);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
set_tris_a(0x03);
set_tris_b(0x00);
set_tris_c(0x18);
delay_ms(1500);
valor = 0;
bit_set(sspcon2,0);
output_bit(PIN_C0,1);
while(1)
{
captura = read_adc();
if(input(PIN_A1))
{
output_b(valor);
output_bit(PIN_A2,0);
output_bit(PIN_A3,0);
output_bit(PIN_C0,0);
output_bit(PIN_A5,0);
}
else {output_b(captura);}
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
}
}
Como se puede apreciar en el código, la secuencia del esclavo es la siguiente:
COMIENZO:
• Se produce la interrupción por coincidencia de direcciones, y el hardware genera el pulso ACK. El bit CKP de SSPCON1 está a cero, congelando la señal de reloj.
• Se lee SSPBUF que contiene la dirección junto con el bit de lectura/escritura. Este bit puede leerse aquí, o bien en el registro SSPSTAT<2>. El hecho de leer SSPBUF tiene la finalidad principal de descargar el buffer de entrada para poner a cero el bit BF del registro SSPSTAT<0>, pues de lo contrario, el buffer se encontraría lleno al enviar el próximo dato. También debemos borrar el bit SSPIF del registro PIR1<3>.
• En función del valor de este bit discriminamos el resto del código a ser ejecutado…
RECEPCIÓN:
• Si el bit R/W# del registro SSPSTAT (o bien el bit de menos peso de la dirección recibida) vale cero, significa que el maestro quiere escribir en el esclavo.
• En este caso ponemos a “1” el bit CKP, con lo que acto seguido recibimos el byte. Cuando esto ocurre, BF=1, y al producir el pulso de reconocimiento (que lo hace el hardware automáticamente) SSPIF=1. En este momento, CKP =0.
• Leemos el byte de SSPBUF, con lo que BF=0. Ponemos SSPIF=0, y CKP=1.
• En ese momento, el maestro manda el pulso de STOP, y finaliza la transferencia.
TRANSMISIÓN:
• En este caso, el bit R/W# = 1.
• Colocamos el byte a enviar al maestro en SSPBUF, por lo que automáticamente BF=1. Ponemos CKP=1 para liberar la línea, y acto seguido se envía el byte al maestro, colocándose BF=0 justo al enviar el último bit. Cuando el maestro lo recibe, SSPIF=1. Si se trata del último byte a enviar al maestro es importante que éste no genere el pulso de reconocimiento, de lo contrario, el esclavo no podrá poner CKP=1, y la transferencia quedará bloqueada (ello es posible que se deba a que para poner CKP=1 en la secuencia de transmisión, previamente se debe llenar SSPBUF con un dato).
• Ponemos SSPIF=0, y esperamos el pulso de STOP, finalizando así la transferencia.
Respecto al código del maestro, tenemos:
Archivo: I2C_Master_4.h
#include <16F876A>
#device *=16
#device adc=8
#use delay(clock=10000000)
#fuses NOWDT,HS, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#use i2c(Master,Slow,sda=PIN_C4,scl=PIN_C3,force_hw)
Archivo: I2C_Master_4.c
#include "C:TrabajosElectrónicaPICArchivos CCCS PCWI2C_Master_4I2C_Master_4.h"
#include <assert>
#include <ctype>
#include <errno>
#include <float>
#include <limits>
#include <locale>
#include <math>
#include <setjmp>
#include <stdio>
#include <stddef>
#include <stdlib>
#include <stdlibm>
#include <string>
#define DIREC 0xA0
#byte sspcon1 = 20
#byte sspadd = 147
#byte sspstat = 148
#byte sspcon2 = 145
void main()
{
int valor, captura;
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_INTERNAL);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
set_tris_a(0x0F);
set_tris_b(0x00);
set_tris_c(0xFE);
valor = 0;
output_bit(PIN_A5,0);
output_bit(PIN_C0,0);
captura = read_adc();
output_b(captura);
delay_ms(1000);
while(1)
{
captura = read_adc();
if(input(PIN_A1))
{
output_b(valor);
output_bit(PIN_A5,0);
output_bit(PIN_C0,0);
}
else {output_b(captura);}
if(input(PIN_A2))
{
// Rutina de petición de dato
//bit_set(sspcon2,3);
i2c_start();
output_bit(PIN_A5,i2c_write(DIREC + 1)); // Dirección con bit de lectura (1)
valor = i2c_read(0); // Dato recibido del esclavo
i2c_stop();
delay_ms(1000); // Retardo para no enviar varias peticiones
}
if(input(PIN_A3))
{
// Rutina de envío de dato
// bit_clear(sspcon2,3);
i2c_start();
output_bit(PIN_A5,i2c_write(DIREC + 0)); // Dirección con bit de escritura (0)
output_bit(PIN_C0,i2c_write(captura)); // Dato
i2c_stop();
delay_ms(1000); // Retardo para no enviar varias peticiones
}
}
}
El código del maestro es transparente. Simplemente se sigue la secuencia empleando las funciones que provee el lenguaje.
_________________
Has colocado las resistencias en las líneas del I2C?
Si estás usando el código que he pegado... asegúrate de que el hardware que uses sea el adecuado para ese código. Fíjate, por ejemplo, que el cristal empleado sea de 10 Mhz... Pequeños detalles de este tipo pueden hacer que no funcione
Hola Mosvack, si ya coloque las resistencias de pullup del pic en los pines RC3/SCL y RC4/SDA de 2K, ya habia leído y descargado tu programa y hoy se lo carge a los PIC's, bueno hasta ahorita estoy en eso. Estoy utilizando el compilador CCS versión 3.249, y tengo una dura en esta linea de tu programa #use i2c(Master,Slow,sda=PIN_C4,scl=PIN_C3,force_hw)
¿que me indica el Sow?
Lo que deberia hacer el programa es leer un Byte por el puertoB del Pic esclavo (introducido por un Dip_Switch), luego ese dato debe ser transmitido al Pic maestro, para ser visualizados mediante unos leds conectados en el puertoB del Pic maestro.. 🙂