TODOPIC

Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: micro_pepe en 13 de Abril de 2011, 19:22:19

Título: Libreria EEPROM en HiTech
Publicado por: micro_pepe en 13 de Abril de 2011, 19:22:19
Estoy intentando hacer una libreria para una 24LC16 en HiTech, pero no consigo hacerla funcionar bien. Estoy usando las funciones del ejemplo que viene en el compilador, y lo que hago basicamente es lo mismo que en la libreria de la 2416 del CCS.

Os dejo el codigo, espero ayuda.

Código: [Seleccionar]
#include <htc.h>
#include "i2c.c"
//---------------------------------------------
void init_ext_eeprom() {
SCL_DIR = 1;//Pone pin como entrada.
SDA_DIR = 1;
}
//--------------------------------------------
unsigned char ext_eeprom_ready() {
   unsigned char ack;
   i2c_Restart();            // If the write command is acknowledged,
   ack = i2c_SendAddress(0x50,0);// then the device is ready.
   i2c_Stop();
   return !ack;
}
//--------------------------------------------
void write_ext_eeprom(unsigned char address, unsigned char data) {
   while(!ext_eeprom_ready());
   i2c_Restart();
   i2c_SendAddress(0x50,0);
   i2c_SendByte(address);
   i2c_SendByte(data);
   i2c_Stop();
}
//---------------------------------------------
int  read_ext_eeprom(unsigned char address) {
   int data;

   while(!ext_eeprom_ready());
   i2c_Restart();
   i2c_SendAddress(0x50,0);//Escribir
   i2c_SendByte(address);
   i2c_Restart();
   i2c_SendAddress(0x50,1);//Leer.
   data=i2c_ReadByte();
   i2c_Stop();
   return(data);
}

Título: Re: Libreria EEPROM en HiTech
Publicado por: AngelGris en 13 de Abril de 2011, 22:11:18
  ¿Y cuál es el problema que estás teniendo? ¿Estás intentando utilizar el I2C por HardWare o por SoftWare?
Título: Re: Libreria EEPROM en HiTech
Publicado por: micro_pepe en 14 de Abril de 2011, 06:38:39
Pues el problema es que no leo el dato que escribo previamente. La secuencia que obtengo en Proteus es esta:

Código: [Seleccionar]
S 50 P
S 50 A 02 A P
S 50 P
S 50 A 03 Sr 51 N Sr P

Y el codigo de prueba:

Código: [Seleccionar]
init_ext_eeprom();
write_ext_eeprom(1, 0x22);
datoAux = read_ext_eeprom(1);
GotoxyLcd(1,1);
WriteStrLcd("dato: ");
itoa(texto,datoAux,10);
WriteStrLcd(texto);

Como se puede ver, intento leer y escribir en la posicion 1, y en la segunda secuencia se puede ver que escribe un 2. En la ultima secuencia escribe un 3, cuando deberia ser un 1.

Estoy usando los pines que el pic tiene para el I2C, pero no sé si la comunicación es por hardware o por software, como dije uso las librerias para el I2C que trae el compilador (están en los adjuntos).
Título: Re: Libreria EEPROM en HiTech
Publicado por: micro_pepe en 14 de Abril de 2011, 09:11:28
Bueno, de esta otra forma, al menos la escritura parece correcta. Ahora si no me equivoco solo falla la lectura del dato, lee 0xFF en lugar de 0x22.

Código: [Seleccionar]
#include <htc.h>
#include "i2c.c"
//---------------------------------------------
void init_ext_eeprom() {
SCL_DIR = 1;//Pone pin como entrada.
SDA_DIR = 1;
}
//--------------------------------------------
unsigned char ext_eeprom_ready() {
   unsigned char ack;
   i2c_Restart();            // If the write command is acknowledged,
   ack = i2c_SendAddress(0x50,0);// then the device is ready.
   i2c_Stop();
   return !ack;
}
//--------------------------------------------
void write_ext_eeprom(unsigned char address, unsigned char data) {
    while(!ext_eeprom_ready());
    i2c_WriteTo(0x50);
i2c_PutByte(address);
i2c_PutByte(data);
i2c_Stop();
}
//---------------------------------------------
int  read_ext_eeprom(unsigned char address) {
    int data;

    while(!ext_eeprom_ready());
    i2c_WriteTo(0x50);
i2c_PutByte(address);
i2c_ReadFrom(0x50);
data = i2c_GetByte(I2C_LAST);
i2c_Stop();
return(data);
}

Y las secuencias en proteus:

Código: [Seleccionar]
S 50 P
S 50 N 01 N 22 N Sr P
S 50 P
S 50 N 01 N Sr 51 N FF N Sr P
Título: Re: Libreria EEPROM en HiTech
Publicado por: Suky en 14 de Abril de 2011, 09:28:58
Podrías verificar si no existe alguna función que indique que el proceso de envío/recepción de datos ha finalizado y luego leer? Puede que las funciones solo escriban sobre SSPBUF.

Ahora, esto porqué?

return !ack;  y while(!ext_eeprom_ready()); . Negado y negado..
Título: Re: Libreria EEPROM en HiTech
Publicado por: AngelGris en 14 de Abril de 2011, 09:47:45
  Prestale atención a lo que indica Suky, y que incluso queda de manifiesto con lo mostrado por el I2C monitor, ya que se aprecia claramente que no hay respuesta del dispositivo.

Código: C
  1. S 50 P
  2. S 50 N 01 N 22 N Sr P
  3. S 50 P
  4. S 50 N 01 N Sr 51 N FF N Sr P
Título: Re: Libreria EEPROM en HiTech
Publicado por: AngelGris en 14 de Abril de 2011, 11:20:19
  micro_pepe, ¿Por qué hacés esto i2c_SendAddress(0x50,0);? Según el datasheet que estoy consultando, al address de una 24LC16 es 1010 + 3 bits de bloque interno + el bit r/w. Entonces para llamar al dispositivo y queriendo manejar el primer bloque para escritura tendríamos que enviar 0xA0 y no 0x50
Título: Re: Libreria EEPROM en HiTech
Publicado por: micro_pepe en 14 de Abril de 2011, 12:21:28
  micro_pepe, ¿Por qué hacés esto i2c_SendAddress(0x50,0);? Según el datasheet que estoy consultando, al address de una 24LC16 es 1010 + 3 bits de bloque interno + el bit r/w. Entonces para llamar al dispositivo y queriendo manejar el primer bloque para escritura tendríamos que enviar 0xA0 y no 0x50

Pues hago eso porque las direcciones de los dispositivos I2C son de 7 bit, y luego a parte el bit que indica si se escribe o se lee, 1010000 = 0x50. Esto lo aprendí con el Arduino: http://www.todopic.com.ar/foros/index.php?topic=32811.0

De todas formas probé con 0xA0. Tendré en cuenta lo que me decís en post anteriores.

Saludos.
Título: Re: Libreria EEPROM en HiTech
Publicado por: Suky en 14 de Abril de 2011, 12:26:39
Lo que pasa que la direccion I2C es 1010A2A1A0, y si los Ax está a GND la dirección de 7-bits es 0x50, que ubicada correctamente en el byte junto al bit R/W es 0xA0 o 0xA1 como dice AngelGris  ;-)



Saludos!
Título: Re: Libreria EEPROM en HiTech
Publicado por: micro_pepe en 14 de Abril de 2011, 13:41:20
Bueno, creo que algo he avanzado, la secuencia es esta, ahora si responde la memoria (supongo que la 'A' es el ack de respuesta):

Código: [Seleccionar]
S A0 A 01 A 22 A Sr P
S A0 A 01 A Sr A1 A FF N Sr P

Código: [Seleccionar]
//--------------------------------------------
void write_ext_eeprom(unsigned char address, unsigned char data) {
    while(i2c_WriteTo(0xA0));
while(i2c_PutByte(address));
while(i2c_PutByte(data));
i2c_SendAcknowledge(1);
while(!i2c_ReadAcknowledge());
i2c_Stop();
}
//---------------------------------------------
unsigned char  read_ext_eeprom(unsigned char address) {
    unsigned char data;

while(i2c_WriteTo(0xA0));
while(i2c_PutByte(address));
while(i2c_ReadFrom(0xA1));
data = i2c_GetByte(I2C_LAST);
i2c_Stop();
return(data);
}
Título: Re: Libreria EEPROM en HiTech
Publicado por: AngelGris en 14 de Abril de 2011, 13:58:19
  Bien, y si, ese A es de ack.

  Ahora solo falta que se grabe el dato. Podrías probar de dejar un delay de unos 5mS a 10mS entre que intentas escribir en la memoria y luego intentar la lectura.
Título: Re: Libreria EEPROM en HiTech
Publicado por: micro_pepe en 14 de Abril de 2011, 20:10:18
  Bien, y si, ese A es de ack.

  Ahora solo falta que se grabe el dato. Podrías probar de dejar un delay de unos 5mS a 10mS entre que intentas escribir en la memoria y luego intentar la lectura.

Pues lo del retardo no funciona  :? :? he probado otras cosas y no he conseguido nada.
Título: Re: Libreria EEPROM en HiTech
Publicado por: AngelGris en 14 de Abril de 2011, 22:08:27
  Yo hice un programa con unas rutinas propias de I2C e implementando el delay (según la simulación) sí me graba el dato...

Acá está el programa que estoy simulando

Código: C
  1. #include <htc.h>
  2.  
  3.   __CONFIG (HS & WDTDIS & LVPDIS);
  4.  
  5. #define PIC_CLK    10000000
  6. #define _XTAL_FREQ 10000000
  7. #define I2C_BAUD   100000
  8.  
  9. #include "HardI2c.c"
  10. #include "Def16f87xa.h"
  11.  
  12. void main(void)
  13. {
  14.   char DatoLeido;
  15.  
  16.   setup_adc (ADC_OFF);
  17.   setup_adc_ports (NO_ANALOG);
  18.   CMCON = 7;
  19.   TRISB = 0;
  20.   TRISC = 0;
  21.   setup_i2c (I2C_MASTER);
  22.   start_i2c();
  23.   if (write_i2c(0xA0) == ACK)  // envio la direccion de la memoria
  24.   {
  25.     write_i2c(0x00);           // envio en que direccion quiero escribir
  26.     write_i2c(0x10);           // envio el dato a escribir
  27.   }
  28.   stop_i2c();
  29.   __delay_ms(10);              // espero a que termine de grabar
  30.   start_i2c();
  31.   write_i2c(0xA0);             // envio la direccion de la memoria
  32.   write_i2c(0x00);             // envio la direccion a leer
  33.   rstart_i2c();                // envio el Repeated Start
  34.   if (write_i2c(0xA1) == ACK) DatoLeido = read_i2c(NOACK);
  35.  // envio la direccion de la memoria para leer y si hay respuesta leo el dato y envio NOACK.
  36.   stop_i2c();
  37.  
  38.   while (1);
  39. }
Título: Re: Libreria EEPROM en HiTech
Publicado por: micro_pepe en 16 de Abril de 2011, 16:59:53
Pues no consigo hacerlo funcionar, lo que si está claro es que no graba el dato, si prviamente en real grabo la memoria, los datos grabados los lee correctamente.

Saludos.
Título: Re: Libreria EEPROM en HiTech
Publicado por: AngelGris en 16 de Abril de 2011, 19:48:03
  Te cuento mi experiencia con mis propias rutinas.
  Antes intentaba hacer polling sobre el ACK -como indica el datasheet de la memoria- para saber cuando terminaba el proceso de escritura y luego intentar la lectura, pues bien, siempre fallaba la lectura. Luego puse el delay posterior a la grabación y allí si pude leer. Pero no entendía el porque del fallo en el polling, debuggeando el programa comprendí que en mi función "write_ic2" luego de llenar el registro SSPBUF con el dato que quería enviar salía de la misma retornando el bit ACKSTAT.

Código: C
  1. char write_i2c (unsigned char data)
  2.   {
  3.     while (( SSPCON2 & 0x1F ) || RW );
  4.     SSPBUF = data;
  5.     return ACKSTAT;
  6.   }

  Pero el error está en que se está saliendo antes de que haya terminado la transferencia y por lo tanto el valor de retorno no es el correcto. La solución fue no salir de la rutina hasta asegurarse que el bit RW se haga 0 (porque dicho bit se pone en 1 cuando hay transferencia sobre el bus).
  El cambio fue muy sencillo, quedando la rutina de esta manera

Código: C
  1. char write_i2c (unsigned char data)
  2.   {
  3.     while (( SSPCON2 & 0x1F ) || RW );
  4.     SSPBUF = data;
  5.     while (RW == 1);
  6.     return ACKSTAT;
  7.   }


  Y así ya me deja hacer polling sobre el bit ACK para saber cuando termina el proceso de escritura de la memoria.
Título: Re: Libreria EEPROM en HiTech
Publicado por: micro_pepe en 18 de Abril de 2011, 17:51:13
AngelGris, por lo que veo lo estás haciendo por hardware, yo los codigos que he puesto son por software.

He intentado también hacerlo por hardware, pasando a c el ejemplo del libro de Angulo, pero ni siquiera saca un dato bién por el puerto. Este es otro codigo que traduje de un ejemplo en asm:

Código: [Seleccionar]
//--------------------------------------------------------------------------------------
/*
Function: I2CInit
 Return:
 Arguments:
 Description: Initialize I2C in master mode, Sets the required baudrate
 */
void I2CInit(void){
         TRISC3 = 1;      /* SDA and SCL as input pin */
         TRISC4 = 1;      /* these pins can be configured either i/p or o/p */
SSPCON = 0x28; // seteo como MASTER
SSPSTAT=0b10000000;
SSPEN = 1; // módulo SSP habilitado.
SSPADD = 19; // Baud Rate Generator: 100 kHz fosc 8MHz.
/* SSPEN = 1, I2C Master mode, clock = FOSC/(4 * (SSPADD + 1)) */
}
//--------------------------------------------------------------------------------------
/*
Function: I2CStart
 Return:
 Arguments:
 Description: Send a start condition on I2C Bus
 */
void I2CStart(){
        // start condition MASTER
SEN = 1; // Start Condition Enabled
while (SSPIF==0) // mientras no se levante la bandera que indica el fin de
continue; // envío de la start condition no hago nada.
SSPIF=0; // la bandera se debe bajar por software.
}
//--------------------------------------------------------------------------------------
/*
Function: I2CStop
 Return:
 Arguments:
 Description: Send a stop condition on I2C Bus
 */
void I2CStop(){
// stop condition MASTER
PEN = 1; // Stop Condition Enabled
while (SSPIF==0) // mientras no se levante la bandera que indica el fin de
continue; // envío de la stop condition no hago nada.
SSPIF=0;
}
//--------------------------------------------------------------------------------------
/*
Function: I2CSend
 Return:
 Arguments: dat - 8-bit data to be sent on bus
            data can be either address/data byte
 Description: Send 8-bit data on I2C bus
 */
signed char I2CSend(unsigned char dat){
SSPBUF = dat; // envío dato.
while (SSPIF==0) // mientras no se levante la bandera que indica el fin de
continue; // envío de la dirección no hago nada.
SSPIF=0; // la bandera se debe bajar por software.

// luego del envío chequeo el error viendo el ACKSTAT
if (ACKSTAT != 0)
return -1;// ocurrió un error…
return 1;

}
//--------------------------------------------------------------------------------------
/*
Function: I2CRead
 Return:    8-bit data read from I2C bus
 Arguments:
 Description: read 8-bit data from I2C bus
 */
unsigned char I2CRead(void){
unsigned char dato;
RCEN = 1; // Recepción habilitada, para recibir cada dato.

// Recepción de un dato.
while (SSPIF==0) // mientras no se levante la bandera que indica el fin de
continue; // recepción de un dato no hago nada.
SSPIF=0; // la bandera se debe bajar por software.
dato = SSPBUF; // el dato recibido esta en el SSPBUF.

// Envío el ACK FINAL.
ACKDT = 1;
ACKEN = 1;
while (SSPIF==0) // mientras no se levante la bandera que indica el fin de
continue; // envío del ACK FINAL no hago nada.
SSPIF=0; // la bandera se debe bajar por software.
return dato;
}
//--------------------------------------------------------------------------------------

Saludos.
Título: Re: Libreria EEPROM en HiTech
Publicado por: AngelGris en 18 de Abril de 2011, 19:47:51
  Tené en cuenta que la librería que trae HiTech puede funcionar tanto por SoftWare como por HardWare.

  Esto es una parte de dicha librería.

Código: C
  1. /* Uncomment the next line to use the PIC's SSP Module*/
  2. #define I2C_MODULE 1
  3.  
  4. #ifdef I2C_MODULE
  5. /* I2C module uses PORT C */
  6. #define SCL             RC3             /* clock on port C bit 2 */
  7. #define SCL_DIR         TRISC3
  8. #define SDA     RC4                     /* data on port C bit 1 */
  9. #define SDA_DIR         TRISC4
  10. #define I2CTRIS TRISC
  11. #define MASTER_MODE     0B1011          /* I2C firmware controlled Master Mode (slave idle) */
  12. #define SSPMode(val)   SSPCON &=0xF0; SSPCON|=(val & 0xf)

  Tus rutinas parecen estar bien excepto la de lectura ya que siempre se envía un NOACK, pero en ocasiones que se necesiten leer mas de un byte, es necesario poder enviar ACK.

  Esta es la librería que yo uso......

Código: C
  1. /**************************************************************************
  2. *      Libreria para protocolo I2C Master por HardWare para HiTech        *
  3. ***************************************************************************
  4.  
  5. ***************************************************************************
  6. *                                                                         *
  7. * Antes de incluir este archivo en nuestro programa hay que definir       *
  8. * algunos parametros. Estos son                                           *
  9. *                                                                         *
  10. * PIC_CLK        (se usa para calcular el valor de SSPADD)                *    
  11. * I2C_BAUD       (velocidad de transferencia)                             *
  12. *                                                                         *
  13. ***************************************************************************
  14.  
  15. ***************************************************************************
  16. *                                                                         *
  17. * En esta libreria se encuentran las siguientes funciones                 *
  18. *                                                                         *
  19. * setup_i2c(char)               (inicializa el puerto I2C)                *
  20. * start_i2c()                   (envia start, inicia transmision)         *
  21. * rstar_i2c()                   (envia restart, reinicia transmision)     *
  22. * stop_i2c()                    (envia stop, termina transmision)         *
  23. * write_i2c(char)               (envia un byte)                           *
  24. * read_i2c(char)                (recibe un byte y envia ack o noack)      *
  25. *                                                                         *
  26. *  La funcion write_i2c() devuelve 0 si el dispositivo responde o 1 si el *
  27. * dispositivo no responde.                                                *
  28. *                                                                         *
  29. *  A la funcion read_i2c() hay que pasarle 0 si se quiere responder al    *
  30. * disipositivo o 1 si no se quiere responder                              *
  31. *                                                                         *
  32. *  En esta libreria estan definidos los parametros I2C_MASTER, I2C_SLAVE, *
  33. * I2C_10BIT_ADDRESS. Eston son utilizados en la funcion setup_i2c(char)   *
  34. *                                                                         *
  35. *  Tambien estan definidos ACK y NOACK que son los utilizados para        *
  36. * responder o no al dispositivo                                           *
  37. *                                                                         *
  38. ***************************************************************************
  39.  
  40. * Ejemplo de configuracion y utilizacion
  41.  
  42.  #define PIC_CLK   10000000   // cristal 10MHz
  43.  #define I2C_BAUD  100000     // velocidad 100 KHz
  44.  #include "HardI2c.c"
  45.  
  46.  void main (void);
  47.  {
  48.    unsigned char respuesta;
  49.    unsigned char dato;  
  50.  
  51.    ......
  52.    ......
  53.    ......
  54.    setup_i2c (I2C_MASTER);
  55.    start_i2c();
  56.    respuesta = write_i2c (0b10101110);    // comunicacion para escribir en una 24C02C
  57.    if (respuesta == ACK)
  58.    {
  59.      write_i2c (0x00);                    // indico la direccion a escribir
  60.      write_i2c (0xF5);                    // escribo el dato F5
  61.      write_i2c (0x06);                    // escribo el dato 06
  62.    }
  63.    stop_i2c();
  64.  
  65.    // colocar un delay necesario para que termine de grabar la memoria
  66.  
  67.    start_i2c();
  68.    respuesta = write_i2c (0b10101110);    // comunicacion para escribir en una 24C02C
  69.    if (respuesta == ACK)
  70.    {
  71.      write_i2c (0x00);                    // indico la direccion de memoria
  72.      rstart_i2c();                       // reinicio la transmisión
  73.      respuesta = write_i2c (0b10101111);  // comunicacion para leer en una 24C02C
  74.      if (respuesta == ACK)
  75.      {
  76.        dato = read_i2c (ACK);             // leo el dato y respondo
  77.        dato = read_i2c (NOACK);           // leo el dato y no respondo
  78.      }
  79.    }
  80.    stop_i2c();
  81.  }
  82.  
  83. */
  84.  
  85. #if defined (PIC_CLK) && defined (I2C_BAUD)
  86.  
  87.   #define I2C_MASTER          0x38
  88.   #define I2C_SLAVE           0x26
  89.   #define I2C_10BIT_ADDRESS   0x01
  90.  
  91.   #ifndef ACK
  92.     #define ACK   0
  93.   #endif
  94.   #ifndef NOACK
  95.     #define NOACK 1
  96.   #endif
  97.  
  98.  
  99.   void setup_i2c (char x)
  100.   {
  101.     TRISC3 = 1;
  102.     TRISC4 = 1;
  103.     SSPSTAT = 0;
  104.     SSPCON = x;
  105.     SSPCON2 = 0;
  106.     if (SSPM3 == 1) SSPADD = (unsigned char)((PIC_CLK/(4*I2C_BAUD)) - 1);
  107.   }
  108.  
  109.   char start_i2c (void)
  110.   {
  111.     while (( SSPCON2 & 0x1F ) || RW );
  112.     SEN = 1;
  113.     return !BCLIF;
  114.   }
  115.  
  116.   void rstart_i2c (void)
  117.   {
  118.     while (( SSPCON2 & 0x1F ) || RW );
  119.     RSEN = 1;
  120.   }
  121.  
  122.   void stop_i2c (void)
  123.   {
  124.     while (( SSPCON2 & 0x1F ) || RW );
  125.     PEN = 1;
  126.   }
  127.  
  128.   char write_i2c (unsigned char data)
  129.   {
  130.     while (( SSPCON2 & 0x1F ) || RW );
  131.     SSPBUF = data;
  132.     while (RW == 1);
  133.     return ACKSTAT;
  134.   }
  135.  
  136.   unsigned char read_i2c (unsigned char respuesta)
  137.   {
  138.     unsigned char dato;
  139.  
  140.     while (( SSPCON2 & 0x1F ) || RW );
  141.     RCEN = 1;
  142.     while (( SSPCON2 & 0x1F ) || RW );
  143.     dato = SSPBUF;
  144.     while (( SSPCON2 & 0x1F ) || RW );
  145.     ACKDT = respuesta;
  146.     ACKEN = 1;
  147.     return dato;
  148.   }
  149. #else
  150.   #error Faltan definir parametros
  151. #endif
Título: Re: Libreria EEPROM en HiTech
Publicado por: micro_pepe en 19 de Abril de 2011, 09:00:27
 
  Esta es la librería que yo uso......


No entiendo lo que está pasando, he probado a hacer un nuevo proyecto de prueba con el mismo codigo que pusiste, y en la simulación no escribe ninguna trama, tan solo una ristra de "????????" Adjunto lo que estoy haciendo, pediria por favor que alguien lo simule.

Saludos.
Título: Re: Libreria EEPROM en HiTech
Publicado por: AngelGris en 19 de Abril de 2011, 09:47:08
  Acabo de probar tu programa y esquema y funcionó bien, pero haciendo los siguientes cambios

Las resistencias de pullup del bus las dejé de 4.7K y las puse como digitales en lugar de analógicas, ya que eso suele generar bastantes problemas en el Proteus, de hecho yo suelo utilizar el componente PULLUP para esas resistencias.
Título: Re: Libreria EEPROM en HiTech
Publicado por: micro_pepe en 19 de Abril de 2011, 16:37:00
  Acabo de probar tu programa y esquema y funcionó bien, pero haciendo los siguientes cambios

Las resistencias de pullup del bus las dejé de 4.7K y las puse como digitales en lugar de analógicas, ya que eso suele generar bastantes problemas en el Proteus, de hecho yo suelo utilizar el componente PULLUP para esas resistencias.

Muchas gracias, efectivamente era eso, no conocia esas resistencias, con las analogicas hasta ahora me habian simulado bien los circuitos.

Saludos.