Hola a todos.
Llevo bastante tiempo consultando muchos temas de los distintos foros y hasta ahora con su lectura ha sido más que suficiente para avanzar en el conocimiento de la programación de los PICs.
Digo hasta ahora porque me encuentro trabado en un problema que no consigo resolver y en el que espero que puedan ayudarme.
Programo con CCS 4.130 y el montaje en cuestión lo estoy simulando en PROTEUS 7.10.
Trato de comunicar mediante el protocolo I2C un 18f4550 (maestro) y un 18f46k22 (esclavo). En la línea hay también un RTC ds3232. El maestro le envía al esclavo una orden para que realice la lectura de una señal exterior a través de una entrada analógica y después lee el valor que el esclavo ha guardado en una posición de memoria.
Inicialmente tenía el proyecto montado con un 18f4550 (maestro), un 18f4620 y el DS3232. Todos funcionaba perfectamente, pero por otras cuestiones necesitaba un esclavo que tuviera dos módulos MSSP, por lo que cambié al 18f46k22 y dejó de funcionar al adaptar el programa a este PIC.
El problema que se me presenta es que la comunicación I2C se interrumpe cuando están los tres elementos, justo en el momento en el que el maestro trata de leer el DS3232. Si comento las líneas relativas al DS3232 la comunicación maestro-esclavo, esclavo-maestro es correcta. Si desconecto el pin SCL del esclavo, la comunicación con el reloj es correcta y sus datos se setean y presentan en el LCD sin problemas.
Entiendo que se trata de algún parámetro de configuración del 18f46k22 que no tengo bien pero no consigo dar con el. Llevo casi un mes consultando webs y estudiándome el datasheet sin éxito. He buscado también a ver si existe alguna errata para el 18f46k22.
He probado también a cambiar el DS323 por el DS1307 sin éxito.
Voy a tratar de adjuntarles los ficheros correspondientes y la simulación en Proteus por si pueden pegarle un vistazo y ayudarme a resolver este enigma.
Agradezco cualquier comentario / crítica constructiva sobre mi trabajo.
Este es el fichero del master:
//////////////////////////////////////////////////////////////////////////////////////////
// JOSE R TORRES
// 18/04/13
//
// Programa: PRUEBA DE COMUNICACION BIDIRECCIONAL I2C
//
//
// Version: 18F46K22-18F4550_MODEL_MST
//
// Dispositivo: PIC 18f4550 Compilador: CCS vs4.130
// simulador: Proteus 7.10
//
//
//
//////////////////////////////////////////////////////////////////////////////////////////
#include <18F46K22-18F4550_MODEL_MST.h>
#include "ds3232.c"
#include "LCD420JRTC.c"
#define DEBUG_DLG //Comentando esta linea se omiten los avisos de DEBUG_DLG
//////////////////////////////////////////////////////////////////////////////////////////
// RUTINAS //
// //
void leer_presion();
void lcd_saludo_inicial();
void lcd_escribe_hora_fecha();
void envio_I2C (direccion_envio,posicion_envio,dato_envio);
int8 lectura_I2C (direccion_leido,posicion_leido);
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// VARIABLES //
//
int check=0; //Variable para señalizar el estado del chequeo
int8 direccion=0;//Dirección del esclavo en la cual se lee o escribe...
int8 dato=0;//...el dato...
int8 posicion=0;//...en la correspondiente posición, según la siguiente distribución:
/******************************TABLA DE POSICIONES/DATOS*********************************/
/*POSICION DATO DESCRIPCION
0 0 Ordena....
0 2 Ordena leer ADC2 (MANÓMETRO)
0 3 Orden de chequeo de válvulas
3 byte alto de la lectura del manómetro (enteros)
4 byte bajo de la lectura del manómetro (decimales)
*****************************************************************************************/
float lect_pres=0;//Valor compuesto con 2 decimales
int8 dato_leido=0;
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// RUTINA DE CONFIGURACION //
// //
void setup()
{
setup_oscillator(OSC_8MHZ);
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_wdt(WDT_OFF);
lcd_init();
ds3232_init();
setup_comparator(NC_NC_NC_NC);// This device COMP currently not supported by the PICWizard
enable_interrupts(GLOBAL);
}//fin del setup
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// RUTINA PRINCIPAL //
// //
void main()
{
setup(); //inicializa el hardware
lcd_saludo_inicial();
escribir_ds3232(dir_hor,10);
escribir_ds3232(dir_min,33);
escribir_ds3232(dir_seg,33);
escribir_ds3232(dir_dia,31);
escribir_ds3232(dir_mes,03);
escribir_ds3232(dir_anio,13);
leer_ds3232();
#ifdef DEBUG_DLG
fprintf(DEBUG_COM_MST,"Estado inicial.\r");
lcd_gotoxy(1,3);//columna 1, fila 3
printf(lcd_putc,"Estado inicial.\r");
#endif
while(1)
{
leer_presion();
lcd_escribe_hora_fecha();
delay_ms(1000);
}//fin del while(1)
}//fin del main
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// RUTINA DE SALUDO LCD //
// Al arrancar el sistema presenta el mensaje de saludo y la version del SOFT //
void lcd_saludo_inicial(void)
{
while (check==0)
{
lcd_putc("\f");
lcd_putc("MONITOR DE PRESION "); //Escribo en la primera fila
delay_ms(500); //Espero medio segundo
lcd_gotoxy(1,2); //columna 1, fila 2
lcd_putc( "JRTC V1.0"); //Escribo en la segunda fila
delay_ms(500); //Espero medio segundo
lcd_putc( "\f"); //Limpio pantalla
check=1;
}//fin del while (check==0)
}//fin del void lcd_saludo_inicial(void)
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// RUTINA PARA ESCRIBIR FECHA Y HORA //
// //
void lcd_escribe_hora_fecha(void)
{
leer_ds3232();
lcd_gotoxy(1,1);//columna 1, fila 1
printf(lcd_putc,"%02u:%02u:%02u %02u/%02u/%02u\r ",reg_ds3232[dir_hor],
reg_ds3232[dir_min],reg_ds3232[dir_seg],reg_ds3232[dir_dia],reg_ds3232[dir_mes],
reg_ds3232[dir_anio]);
}
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// ENVIA ORDEN DE LECTURA DE CANALES ADC Y RECIBE VALORES //
//Envío al esclavo la orden de realizar una lectura del manómetro para luego enviarme el//
//dato. Para ello envío el valor 2 en la posición 0. Cuando el esclavo recibe esta orden//
//lee el canal AN1. Después le envío la orden de lectura de la posición correspondiente//
//(3 para el byte alto de la lectura del manómetro (enteros) y 4 para el byte bajo de la//
//lectura del manómetro (decimales)). //
void leer_presion()
{
signed int16 lect_pres_int=0;
signed int8 lect_pres_int_alta=0, lect_pres_int_baja=0;//Descomposición en dos bytes del
//...dato recibido del ADC del esclavo correspondiente a la lectura del manómetro.
direccion=dir_18F46k22SLV;
posicion=0;
dato=2;
envio_I2C(direccion,posicion,dato);
delay_ms(200);//Este valor es critico para evitar que se envíe eldato varias veces 30/03/13
/**********************COMPONE EL VALOR DE PRESION DE 2 BYTES A FLOAT *******************/
posicion=3;//le doy la posicion del buffer en el que está el byte alto del dato(enteros)
lect_pres_int_alta=lectura_I2C(direccion, posicion);//Traigo la parte alta del dato
posicion=4;//le doy la posicion del buffer en el que está el byte bajo del dato(decimales)
lect_pres_int_baja=lectura_I2C(direccion, posicion);//Traigo la parte baja del dato
lect_pres_int=make16(lect_pres_int_alta,lect_pres_int_baja);//Compongo el int16 con
//...los dos bytes.
lect_pres=(float)lect_pres_int/100;//Convierto en decimal el dato.
/****************************************************************************************/
#ifdef DEBUG_DLG
fprintf(DEBUG_COM_MST,"Presion: %02.2f\r",lect_pres);
lcd_gotoxy(1,2);//columna 1, fila 2
printf(lcd_putc,"Presion: %02.2f\r",lect_pres);
#endif
}//fin del leer_presion()
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// RUTINA DE ENVIO DE FUNCIONES POR I2C A ESCLAVO //
// //
void envio_I2C (byte direccion_enviado, byte posicion_enviado, byte dato_enviado)
{
i2c_start(); //Comienzo de la comunicación I2C ...
i2c_write(direccion_enviado); //...con la dirección del PIC esclavo...
delay_us(3);
i2c_write(posicion_enviado); // Posición donde se guardara el dato transmitido
delay_us(3);
i2c_write(dato_enviado); // Dato a transmitir
i2c_stop();//Finalización de la transmisión
delay_ms(10);
#ifdef DEBUG_DLG
fprintf(DEBUG_COM_MST,"Orden enviada. Direccion= %02X Posicion= %u Dato= %u
%02u/%02u/%02u %02u:%02u:%02u\r", direccion,posicion,dato,reg_ds3232[dir_dia],
reg_ds3232[dir_mes],reg_ds3232[dir_anio],reg_ds3232[dir_hor],reg_ds3232[dir_min],
reg_ds3232[dir_seg]);
#endif
}
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// RUTINA DE LECTURA DE DATOS POR I2C DEL ESCLAVO //
// //
int8 lectura_I2C (int8 direccion_leido, int8 posicion_leido)
{
int8 dato_leido=0;
i2c_start(); // Comienzo de la comunicación
i2c_write(direccion_leido); // Dirección del esclavo en el bus I2C
i2c_write(posicion_leido); // Posición de donde se leerá el dato en el esclavo
i2c_start(); // Reinicio
i2c_write(direccion_leido+1); // Dirección del esclavo en modo lectura
delay_ms(10);
dato_leido=i2c_read(0); // Lectura del dato. 0 indicates do not ack.
i2c_stop(); // Fin comunicación
delay_ms(50);
return (dato_leido);
}
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
Con su cabecera:
#include <18F4550.h>
#device adc=8
#FUSES HSPLL //JRTC 17/03/13
#FUSES PLL5 //JRTC 17/03/13
#FUSES USBDIV //JRTC 17/03/13
#FUSES CPUDIV1 //JRTC 17/03/13
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOBROWNOUT //No brownout reset
#FUSES BORV20
#FUSES NOPUT //No Power Up Timer
#FUSES NOCPD //No EE protection
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT //Program memory not write protected
#FUSES NOWRTD //Data EEPROM not write protected
#FUSES NOIESO //Internal External Switch Over mode disabled
#FUSES NOFCMEN //Fail-safe clock monitor disabled
#FUSES NOPBADEN //PORTB pins are configured as digital I/O on RESET
#FUSES NOWRTC //configuration not registers write protected
#FUSES NOWRTB //Boot block not write protected
#FUSES NOEBTR //Memory not protected from table reads
#FUSES NOEBTRB //Boot block not protected from table reads
#FUSES NOCPB //No Boot Block code protection
#FUSES LPT1OSC //Timer1 configured for low-power operation
#FUSES MCLR //Master Clear pin enabled
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#byte TRISA=0x92
#byte TRISB=0x93
#byte TRISC=0x94
#use delay(clock=8000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,bits=8,stream=DEBUG_COM_MST)
#use I2C(MASTER, SLOW,SDA=PIN_B0, SCL=PIN_B1,FORCE_HW)
#define dir_18F46k22SLV 0xA0
ESCLAVO
//////////////////////////////////////////////////////////////////////////////////////////
// JOSE R TORRES
// 18/04/13
//
// Programa: PRUEBA DE COMUNICACION BIDIRECCIONAL I2C
//
//
// Version: 18F46K22-18F4550_MODEL_SLV
//
// Dispositivo: PIC 18f46K22 Compilador: CCS vs4.130
// simulador: Proteus 7.10
//
//
//
//////////////////////////////////////////////////////////////////////////////////////////
#include <18F46K22-18F4550_MODEL_SLV.h>
#define DEBUG_DLG//Comentando esta linea se omiten los avisos de DEBUG_DLG
//////////////////////////////////////////////////////////////////////////////////////////
// RUTINAS //
// //
void lee_manometro(void);
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// VARIABLES //
//
int16 ecu=0; //Resultado de la ecuación de conversión de la tensión leida.
float pres_volt;
float pres_kpa, pres_atm, pres_psi; // Lectura temporal guardada.Fundamental que sea float
signed int16 pres_kpa_int;//Variable para convertir el dato float en int16
signed int8 pres_kpa_int_alta, pres_kpa_int_baja;//Variable para descomponer el dato en dos bytes
typedef enum {nada, leer_posicion,leer_dato} ESTADO_I2C;//Define el tipo ESTADO_I2C que...
//...es una enumeración de constantes enteras (nada=0,leer_posicion=1,leer_dato=2)
ESTADO_I2C fEstado;//Crea la variable fEstado del tipo ESTADO_I2C...
int8 posicion;
signed int8 buffer[10];
int envio=0;
/****************************************************************************************/
/********************** FUNCIÓN INTERRUPCIÓN POR RECEPCION I2C **************************/
#INT_SSP
void ssp_interupt ()
{
int8 recibido; //es donde se recibe el byte que manda el maestro
fEstado = i2c_isr_state(); //estado del bus tras la interrupción
if(fEstado==0x80)// Si no hay dato recibido=>el master requiere lectura del esclavo
{
i2c_write (buffer[posicion]);
output_high (VALVIB);
output_low (VALVOF);
output_low (VALVOB);
#ifdef DEBUG_DLG
fprintf (DEBUG_COM_SLV,">>Dato enviado al master Pos.%u Dato= %u %X\r" posicion,
buffer[posicion], buffer[posicion]);
#endif
}
if (fEstado<0x80)//maestro está enviando datos
{
recibido = i2c_read(); //... lo lee
if (fEstado == nada)//Información recibida es aviso de que va mandar algo master
{
fEstado = leer_posicion; //Siguiente información será la posición donde guardar dato
output_high (VALVOF);
output_low (VALVIB);
output_low (VALVOB);
}
else if (fEstado == leer_posicion) //Información recibida corresponde a la posicion
{
posicion = recibido; //Se guarda posición
fEstado = leer_dato; //Siguiente información recibida será el dato
output_high (VALVOB);
output_low (VALVIB);
output_low (VALVOF);
}
else if (fEstado == leer_dato) //Información recibida corresponde al dato
{
buffer[posicion] = recibido; //Se guarda dato
fEstado = nada; //Siguiente información será aviso de nuevo envío del master
envio=1;
}
}
/****************************************************************************************/
// DESCRIPCION DE LOS VALORES QUE PUEDE ADOPTAR I2C_ISR_STATE //
// 0 Dirección coincidente con un R/W a cero //
// 1 a 0x7f El master ha escrito un datos, se debe utilizar I2C_READ(). //
// 0X80 Indica dirección coincidente con un R/W a uno, responder con un I2C_WRITE().//
// 0x81 a 0xff Transmisión terminada y reconocida, se responde con un I2C_WRITE(). //
/****************************************************************************************/
}
//////////////////////////////////////////////////////////////////////////////////////////
// RUTINA DE CONFIGURACION //
// //
void setup()
{
setup_oscillator(OSC_8MHZ);
TRISA=0b00000010; //Configuro pines del puerto A como I (1) / O (0)
//para que funcione el módulo I2C por HardWare, los pines C3 y C4 deben ser configurados como entrada
TRISC=0b00011000;//RC3 Y RC4 COMO ENTRADAS
setup_adc(ADC_CLOCK_INTERNAL); //Reloj del adc interno.
setup_adc_ports(sAN0|sAN1|sAN2|VSS_VDD);
setup_timer_3(T3_DISABLED | T3_DIV_BY_1);
setup_timer_4(T4_DISABLED,0,1);
setup_timer_5(T5_DISABLED | T5_DIV_BY_1);
setup_timer_6(T6_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);// This device COMP currently not supported by the PICWizard
enable_interrupts(INT_SSP);
enable_interrupts(global);
}//fin del setup
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// RUTINA PRINCIPAL //
// //
void main()
{
setup(); //inicializa el hardware
output_high (VALV09);
output_high (VALV12);
delay_ms(200);
output_low (VALV09);
output_low(VALV12);
fEstado = nada;
for (posicion=0;posicion<10;posicion++)
buffer[posicion] = 0x00;//Reinicializa las variables
#ifdef DEBUG_DLG
fprintf(DEBUG_COM_SLV,"Estado inicial.\r");
#endif
while(1)
{
while (envio==1)
{
#ifdef DEBUG_DLG
fprintf(DEBUG_COM_SLV, "<< Dato recibido del master Pos.%u Dato= %u\r",
posicion, buffer[posicion]);
#endif
envio=0;
}
lee_manometro();
}//fin del while(1)
}//fin del main
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// RUTINA PARA LEER ADC DEL MANOMETRO //
// //
void lee_manometro()
{
//disable_interrupts(INT_SSP);
if (buffer[0]==2)//Si le digo que lea del canal AN1
{
set_adc_channel(1); //Elijo el canal a leer
delay_us(20); //Espero 20 microsegundos para que se elija bien el canal
ecu=read_adc();
pres_volt=5.0*ecu/1024.0; //lectura de presion en voltios (1024.0 conversor 10 bits)
pres_kpa = ((pres_volt-4.6)/0.03826)-1.5; //Presión en KpA con MPXV4115/MPXV6115
//pres_kpa = ((pres_volt+0.475)/0.045)-1.5; //Presión en KpA con MPX4115
pres_atm = pres_kpa * 0.0098692; //Presión en Atm
pres_psi = pres_kpa * 0.1450377; //Presión en Psi
/************FORMULA PARA CONVERSION DE VOLTS EN PRESION (DATASHEET)*********************/
//Nominal Transfer Value MPX4115: Vout = VS ((P x 0.009) - 0.095)±
//(Pressure Error x Temp. Factor x 0.009 x VS)
//VS = 5.1 V ± 0.25 Vdc P=((Vout + 0.475)/0.045)-1.5 (para el rango entre 0 y 85ºC)
//Nominal Transfer Value MPXV4115/MPXV6115: Vout = VS (P x 0.007652 )+ 0.92)±
//(Pressure Error x Temp. Factor x 0.007652 x VS)
//VS = 5 V ± 0.25 Vdc P=((Vout - 4.6)/0.03826)-1.5 (para el rango entre 0 y 85ºC)
//Vout= 0,2 a 4,6 V
/***************CORRECCION DEL ERROR EN FUNCION DE LA TEMP (DATASHEET)*******************/
/*Como la temperatura de trabajo siempre va a estar entre 0 y 85ºC omito la función que
calcula el factor de corrección en función de la temperatura y utilizo siempre -1.5
if (temp>=0 && temp<=85) TF= 1.0;//Ver datasheet
else TF=3.0;
ERROR = TF * 1.5; //Cálculo del error de presion con la temperatura
presion = presion - ERROR; //Presion en KPa*
/*******************DESCOMPONE EL VALOR DE PRESION FLOAT A 2 BYTES***********************/
pres_kpa = pres_kpa*100; //Al multiplicar por 100 incluyo dos decimales...
//...en la parte entera del dato.
pres_kpa_int=(int16)pres_kpa; //Pongo en pres_kpa_int la parte entera de...
//...pres_kpa (incluyendo los dos decimales)
pres_kpa_int_baja=make8(pres_kpa_int,0);//Así obtengo la palabra baja (parte decimal)
//...de la variable "pres_kpa_int"...
pres_kpa_int_alta=make8(pres_kpa_int,1);//Así obtengo la palabra alta (parte entera)
//...de la variable "pres_kpa_int"...
/****************************************************************************************/
#ifdef DEBUG_DLG
fprintf(DEBUG_COM_SLV, "Lectura del adc(ecu): %Lu\r", ecu);
fprintf(DEBUG_COM_SLV,"Lectura del volt: %1.4f\r", pres_volt);
fprintf(DEBUG_COM_SLV,"Lectura del KPa: %2.4f %LX\r", pres_kpa, pres_kpa_int);
#endif
buffer[3]=pres_kpa_int_alta;
buffer[4]= pres_kpa_int_baja;
buffer[0]=0;
}//FIN DEL if (buffer[8]==1)
//enable_interrupts(INT_SSP);
}
// //
// //
//////////////////////////////////////////////////////////////////////////////////////////
y su cabecera:
#include <18F45K22.h>
#device adc=10
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO//Internal RC Osc no CLKOUT.
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOPBADEN //PORTB pins are configured as digital I/O on RESET
#FUSES NOMCLR //Master Clear pin disabled
#use delay(clock=8000000)
// PIC 18F45k22 Register/Bit definitions
#byte TRISA=0x92
#byte TRISB=0x93
#byte TRISC=0x94
#byte TRISD=0x95
#byte TRISE=0x96
#use I2C(SLAVE,i2c1,SDA=PIN_C4, SCL=PIN_C3, address=0xA0, FORCE_HW )
#use rs232(baud=9600,parity=N,xmit=PIN_D6,bits=8,stream=DEBUG_COM_SLV)
#define VALV09 PIN_B0//Válvula nº 9
#define VALV10 PIN_B1//Válvula nº 10
#define VALV11 PIN_B2//Válvula nº 11
#define VALV12 PIN_B3//Válvula nº 12
#define VALVIB PIN_B4//Válvula INPUT BOMBA
#define VALVIF PIN_B5//Válvula INPUT FUM
#define VALVOB PIN_B6//Válvula OUTPUT BOMBA
#define VALVOF PIN_B7//Válvula OUTPUT FUM