Autor Tema: I2C con XC8: Pin SCL como salida?  (Leído 7638 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado Loudness

  • PIC16
  • ***
  • Mensajes: 147
I2C con XC8: Pin SCL como salida?
« en: 30 de Mayo de 2018, 03:43:49 »
Saludos.

Sigo luchando con el C y ahora me estoy metiendo en con el funcionamiento del I2C utilizando las librerias que Microchip proporciona con
la nota de aplicacion AN1488

https://www.microchip.com/wwwAppNotes/AppNotes.aspx?appnote=en560799

El caso es que revisando las librerias me encuentro que en la configuracion de los puertos para I2C pone la patilla SCL como salida  :shock::

Código: [Seleccionar]
//..............................................................................
//   Inits GPIO ports
//..............................................................................
void ini_gpio(void)
{
TRISD=0; // init LEDS (RD0 - RD7 = outputs)
LATD=0;                         // LEDS are OFF

// enable I/O function of the next pins:
LCDSE2bits.SE16=0;              // RC3 (SCL1)
LCDSE2bits.SE17=0;              // RC4 (SDA1)

SDA_TRIS = 1;                   // SDA1 (RC4) = input
SCL_TRIS = 0;                   // SCL1 (RC3) = output ???
SCL=1;                          // SCL is high
}

Por lo que yo tenia entendido tanto SDA como SCL tienen que estar como entradas.... :?:

Como se deben configurar SDA y SCL?

Desconectado Robert76

  • PIC24F
  • *****
  • Mensajes: 577
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #1 en: 30 de Mayo de 2018, 07:17:17 »
Hola, el pin SCL seguramente trabaja cómo salida permanente, ya que sincroniza el tráfico de datos que entran y salen del PIC.
En CCS, la primera vez que usé I2C. Ni siquiera declare cómo entradas o salidas a éstos pines, sólo defini cuál es cuál en el puerto más cómodo.
Ahora el pin SDA, trabaja cómo entrada-salida. En función de los datos. La misma librería se encarga de conmutar ésta función.
Lo que sí recuerda que debe haber  resistencias en modo pull-up en las líneas SCL y SDA.

Desconectado Loudness

  • PIC16
  • ***
  • Mensajes: 147
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #2 en: 30 de Mayo de 2018, 08:48:00 »
Gracias por la respuesta Robert76

En CCS, la primera vez que usé I2C. Ni siquiera declare cómo entradas o salidas a éstos pines

Interesante. Cuando he usado el I2C en el pic16F877A siempre los ponia como salidas.
Creo recordar que me daba problemas si no lo hacia...creo, no recuerdo bien :lol:

De todas maneras no he probado las librerias y no se si funcionan o no asi que no puedo decir nada. 

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #3 en: 30 de Mayo de 2018, 08:54:05 »
Eso es un I2C por software. Ya que los PIC no poseen open-drain deben hacerlo asi.

Por ser maestro, y como es el unico que deberia manejar el SCL es este mismo. Hay ocasiones que el esclavo puede mantener el clock en bajo si no mal recuerdo, pero usualmente el unico que controla el SCL es el maestro, distinto al SDA, que la informacion puede venir de varios lados.

Personalmente usaria la resistencia de pull-up, y como decis, lo pondria como entrada. Pero por lo visto que es realizado para una memoria EEPROM, y Mirando el datasheet de la memoria me encuentro con esto:

Citar
2.3 Serial Clock (SCL)
This input is used to synchronize the data transfer to and from the device

2.2 Serial Data (SDA)
This is a bidirectional pin used to transfer addresses and data into and out of the device

Te puede explicar el porque decidieron directamente ponerlo como salida.

Desconectado Loudness

  • PIC16
  • ***
  • Mensajes: 147
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #4 en: 30 de Mayo de 2018, 10:25:41 »
Gracias por la respuesta KILLERJC

Ahora mi problema es otro aunque es relacionado con el i2c asi que lo pongo aqui.

Estoy intentando hacer un programa para que grabar datos en una memoria externa y, dejando de lado las librerias de microchip, he cogido un trozo de codigo sacado de este libro

https://www.apress.com/cn/book/9781484232729

y lo he puesto en mi programa modificandolo un poco para usarlo con el PIC16F1947. Aqui lo teneis:

Código: [Seleccionar]
/*
 * File:   main.c
 * Author: Raul
 * PRUEBAS VARIAS RELACIONADAS CON PIC16F1947
 * Created on 2018/05/21, 11:29
 */
// CONFIG1
#pragma config FOSC = HS        // Oscillator Selection->HS Oscillator, High-speed crystal/resonator connected between OSC1 and OSC2 pins
#pragma config WDTE = OFF       // Watchdog Timer Enable->WDT disabled
#pragma config PWRTE = OFF      // Power-up Timer Enable->PWRT disabled
#pragma config MCLRE = ON       // MCLR Pin Function Select->MCLR/VPP pin function is MCLR
#pragma config CP = OFF         // Flash Program Memory Code Protection->Program memory code protection is disabled
#pragma config CPD = OFF        // Data Memory Code Protection->Data memory code protection is disabled
#pragma config BOREN = ON       // Brown-out Reset Enable->Brown-out Reset enabled
#pragma config CLKOUTEN = OFF   // Clock Out Enable->CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin
#pragma config IESO = ON        // Internal/External Switchover->Internal/External Switchover mode is enabled
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable->Fail-Safe Clock Monitor is enabled
// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection->Write protection off
#pragma config VCAPEN = OFF     // Voltage Regulator Capacitor Enable->VCAP pin functionality is disabled
#pragma config PLLEN = OFF      // PLL Enable->4x PLL disabled
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable->Stack Overflow or Underflow will cause a Reset
#pragma config BORV = LO        // Brown-out Reset Voltage Selection->Brown-out Reset Voltage (Vbor), low trip point selected.
#pragma config LVP = OFF        // Low-Voltage Programming Enable->High-voltage on MCLR/VPP must be used for programming


#include <xc.h>
#include <htc.h>
#include <stdio.h>


#define _XTAL_FREQ 16000000             // 16 Mhz

int block_adress = 0x00;
int word_adress = 0x00;
int eeprom_data = 0x09;
int incoming_data;

/*******************************CONFIGURACION I/O*******************************/
void Configuration_IO (void)        //configuracion puertos entrada y salida solo una vez al incio
{
    LATE = 0x00; LATD = 0x00; LATA = 0x00; LATF = 0x00;
    LATB = 0x00; LATG = 0x00; LATC = 0x00;

    TRISE = 0x00; TRISF = 0x00; TRISA = 0xFF; TRISG = 0x00;
    TRISB = 0x0F; TRISC = 0x00; TRISD = 0x00;

    ANSELE = 0x07; ANSELG = 0x1E; ANSELF = 0xFF; ANSELA = 0x2F;

    WPUB = 0x00; WPUG = 0x00;
    OPTION_REGbits.nWPUEN = 1;      //resistencias internas desactivadas
   
    APFCON = 0x00;                  //selecciona salidas especiales en ciertos puertos
     
}
/*******************************INICIAR I2C************************************/
void Configuration_I2C (void)
{
 
    TRISCbits.TRISC4 = 1;
    TRISCbits.TRISC3 = 1;
    LATCbits.LATC3 = 1;
    SSP1STAT = 0x80;
    SSP1CON1 = 0x28;
    SSP1CON3 = 0x00;
    SSP1ADD = 0x27;
}


/*******************************PRINCIPAL**************************************/

void main(void)                      //bucle principal. A partir de while(1) se repite indefinidamente
{
   Configuration_IO();                //esta parte solo hace una vez
   Configuration_I2C();
   while(1){                          //bucle infinito
       //escribir en eeprom externa//
       Send_I2C_StartBit();                    // send start bit
       Send_I2C_ControlByte(0x00,0);  // send control byte with R/W bit set low
       Send_I2C_Data(word_adress);            // send word address
       Send_I2C_Data(eeprom_data);             // send data byte
       Send_I2C_StopBit();                     // send stop bit
       __delay_ms(200);

       
      //leer desde la eeprom externa//
      Send_I2C_StartBit();                    // send start bit
      Send_I2C_ControlByte(0x00,0);  // send control byte with R/W bit set low
      Send_I2C_Data(word_adress);            // send word address
      Send_I2C_StartBit();                    // send start bit
      Send_I2C_ControlByte(0x00,1);  // send control byte with R/W bit set high
      incoming_data = Read_I2C_Data();        // now we read the data coming back from the eeprom
      Send_I2C_NAK();                         // send a the NAK to tell the eeprom we don't want any more data
      Send_I2C_StopBit();                     // and then send the stop bit
      PORTF = incoming_data;     // saca el resultado por el puerto f donde hay conectados leds
      eeprom_data = eeprom_data + 1;//aumenta en uno el dato a escribir y leer
     
      __delay_ms(200);
       
           }
}   

/*************************RUTINAS PARA EL I2C***********************************/

void Send_I2C_Data(unsigned int databyte)
{
    PIR1bits.SSPIF=0;          // clear SSP interrupt bit
    SSPBUF = databyte;          // send databyte
    while(!PIR1bits.SSPIF);    // Wait for interrupt flag to go high indicating transmission is complete

}

unsigned int Read_I2C_Data(void)

{

    PIR1bits.SSPIF=0;          // clear SSP interrupt bit
    SSPCON2bits.RCEN=1;         // set the receive enable bit to initiate a read of 8 bits from the serial eeprom
    while(!PIR1bits.SSPIF);    // Wait for interrupt flag to go high indicating transmission is complete   
    return (SSPBUF);            // Data from eeprom is now in the SSPBUF so return that value

}

void Send_I2C_ControlByte(unsigned int BlockAddress,unsigned int RW_bit)

{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSPBUF = (((0b1010 << 4) | (BlockAddress <<1)) + RW_bit);  // send the control byte
    while(!PIR1bits.SSP1IF);    // Wait for interrupt flag to go high indicating transmission is complete
}

void Send_I2C_StartBit(void)

{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSPCON2bits.SEN=1;          // send start bit
    while(!PIR1bits.SSP1IF);    // Wait for the SSPIF bit to go back high before we load the data buffer
}

void Send_I2C_StopBit(void)

{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSPCON2bits.PEN=1;          // send stop bit
    while(!PIR1bits.SSP1IF);    // Wait for interrupt flag to go high indicating transmission is complete
}

void Send_I2C_ACK(void)

{
   PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
   SSPCON2bits.ACKDT=0;        // clear the Acknowledge Data Bit - this means we are sending an Acknowledge or 'ACK'
   SSPCON2bits.ACKEN=1;        // set the ACK enable bit to initiate transmission of the ACK bit to the serial eeprom
   while(!PIR1bits.SSP1IF);    // Wait for interrupt flag to go high indicating transmission is complete
}

void Send_I2C_NAK(void)

{
    PIR1bits.SSP1IF=0;           // clear SSP interrupt bit
    SSPCON2bits.ACKDT=1;        // set the Acknowledge Data Bit- this means we are sending a No-Ack or 'NAK'
    SSPCON2bits.ACKEN=1;        // set the ACK enable bit to initiate transmission of the ACK bit to the serial eeprom
    while(!PIR1bits.SSP1IF);    // Wait for interrupt flag to go high indicating transmission is complete
}

El caso es que al compilar me sale estos dos errores:


* Error 180530.jpg
(94.47 kB, 702x274 - visto 836 veces)


main.c:106: error: (984) type redeclared
main.c:106: error: (1098) conflicting declarations for variable "Send_I2C_Data" (main.c:105)

Imagino que tiene que ser una variable definida 2 veces de manera diferente o algo asi? pero no lo veo por ningun lado  :?.
Y, desde que estoy con c (no hace mucho) creo ya he visto este error mas veces...

Agraderia cualquier ayuda

Desconectado Robert76

  • PIC24F
  • *****
  • Mensajes: 577
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #5 en: 30 de Mayo de 2018, 11:16:32 »
Yo no utilizo XC8, pero viendo el listado de errores que te arroja, si observas que hay un número entre dobles puntos, cómo por ejem.

main.c :92: <---- parece que está apuntandote adonde debes verificar.

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #6 en: 30 de Mayo de 2018, 11:18:42 »
Uno de mis problema es que no se si los valores de lineas mios son los tuyos... ya sea porque al copiar le pusiste un enter o falta un espacio, etc. Lo que veo en el codigo es que esta faltando la definicion de los prototipos. Me explico con algo simple.. vos pensa que el compilador va de arriba hacia abajo en el .c, Lo primero que tiene es el main, y de golpe le aparece una funcion que se llama Send_I2C_StartBit que nunca la vio.. entonces va generar un error, ni siquiera sabe que es..

Hay 2 soluciones,
- poner todos las funciones de I2C al comienzo del main.c, asi pasa por estas antes de llegar a la funcion main, como tenes las configuraciones de las IO e I2C
- agregarles los prototiṕos al comienzo, es decir agregar:

Código: C
  1. void Send_I2C_StartBit(void);
  2. void Send_I2C_ControlByte(unsigned int BlockAddress,unsigned int RW_bit);
  3. unsigned int Read_I2C_Data(void);
  4. // y todos los demas

Eso creo que solucionaria tu problema.

------------------------------------------------------------------------------------------

Otra cosa, uno de los grandes problemas en sistemas embebidos es que nosotros SI sabemos que cantidad de bits queremos en nuestras variables, y jugamos con el espacio. El problema viene con el uso de distintos compiladores.
CCS tiene un int que es de 8bits, mientras que XC8 el int es de 16 bits. Entonces complica el "paso" de uno a otro. Una forma facil de pasar esto es usar el archivo stdint.h el cual define nuevos valores que son mas claros para el usuario.

uint8_t -- Unsigned entero de 8 bits
uint16_t -- Unsigned entero de 16 bits
int8_t -- entero (con signo) de 8 bits

asi para 32 tambien..
Luego dentro de ese archivo cada compilador lo convierte a su formato, en XC8 vas a tener algo asi:

typedef unsigned char uint8_t

en CCS va a ser unsigned int, pero si vos usas uint8_t vas a saber que si o si es de 8 bits (rara excepcion cuando la arquitectura no lo permite).

Lo nombro porque veo que usas "unsigned int" para solo 8 bits.

---------------------------------------------------------------------

Otra cosa mas... solo es necesario incluir el xc.h nada mas , los demas: stdio.h, htc.h no hace falta para lo que usas.
Y obviamente si usas lo que dije antes necesitas incluir el stdint.h
« Última modificación: 30 de Mayo de 2018, 11:43:46 por KILLERJC »

Desconectado Loudness

  • PIC16
  • ***
  • Mensajes: 147
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #7 en: 30 de Mayo de 2018, 22:09:58 »
Gracias por tu respuesta KILLERJC

Creo que esto tiene que ver con lo que me explicaste aqui

http://www.todopic.com.ar/foros/index.php?topic=48766.0


Hay 2 soluciones,
- poner todos las funciones de I2C al comienzo del main.c, asi pasa por estas antes de llegar a la funcion main, como tenes las configuraciones de las IO e I2C


Puede ser la solucion pero entonces si se hace lo mismo con todas las funciones del pic (i2c, USART, ADC....) vamos a tener un main enorme y que va a ser un lio enorme.
Para ello se utilizan las librerias verdad? :?.

Y las librerias tienen el archivo .c y otro con la declaracion de variables llamado .h para que no salga el error que me esta saliendo a mi. Me equivoco?.

Creo que ya voy entendiendo por donde va el tema :lol:


Otra cosa, uno de los grandes problemas en sistemas embebidos es que nosotros SI sabemos que cantidad de bits queremos en nuestras variables, y jugamos con el espacio. El problema viene con el uso de distintos compiladores.
CCS tiene un int que es de 8bits, mientras que XC8 el int es de 16 bits. Entonces complica el "paso" de uno a otro. Una forma facil de pasar esto es usar el archivo stdint.h el cual define nuevos valores que son mas claros para el usuario.

uint8_t -- Unsigned entero de 8 bits
uint16_t -- Unsigned entero de 16 bits
int8_t -- entero (con signo) de 8 bits

Otra cosa mas... solo es necesario incluir el xc.h nada mas , los demas: stdio.h, htc.h no hace falta para lo que usas.
Y obviamente si usas lo que dije antes necesitas incluir el stdint.h

Al compilar, junto con los errores, tambien me salen montones de warning (en la foto anterior se pueden ver algunos) y creo que van relacionados con el tema de las variables.

Al final compila y los programas (hasta ahora muy sencillos) funcionan asi que no me he puesto demasiado, pero ,dejar warnings sin hacer nada por mucho que compile no me parece buena practica.

Al incluir stdint.h y cambiar "unsigned int" por "uint8_t" parece que algunos warnings han desaparecido...

De momento voy a por el i2c, si es posible quiero hacerlo funcionar hoy mismo :5]

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #8 en: 30 de Mayo de 2018, 23:51:48 »
Citar
Creo que esto tiene que ver con lo que me explicaste aqui

Exacto. En el link había escrito sobre  que en el archivo que contenía las funciones suma y resta, si vas a usarlas dentro, ibas a tener que agregar el .h, y era justamente por el problema que esta teniendo vos ahora.

Citar
Y las librerias tienen el archivo .c y otro con la declaracion de variables llamado .h para que no salga el error que me esta saliendo a mi. Me equivoco?.

Recorda que en los .h NO va nada que ocupe lugar en el micro... es decir no podes definir variables, eso va en el .c, podes definir prototipos de funciones, como van a estar formadas las estructuras y uniones, defines que tengas que usar de forma externa, pero declarar como esta formado no significa que se declare el lugar para la variable. Me remito a un ejemplo:

Código: C
  1. int suma(int a, int b);  // Esto no ocupa lugar, solo le dice al compilador como esta formada la funcion suma, esto puede ir en el .h
  2.  
  3. int suma(int a, int b)           // Esto si ocupa lugar, hay instrucciones, el int c, va a hacer que el micro reserve RAM para eso. Asi que tambien ocupa lugar
  4. {
  5.    int c;
  6.    c = a + b
  7. }

Otra cosa... lo que va en el .h es lo que queres "compartir" con otro archivo. Si de 10 funciones solo 1 se usa de forma externa al archivo (por otro archivo .c) , entonces el .h deberia tener ese solo prototipo de funcion.

Citar
Puede ser la solucion pero entonces si se hace lo mismo con todas las funciones del pic (i2c, USART, ADC....) vamos a tener un main enorme y que va a ser un lio enorme.

Exacto, lo que mas se premia es la legibilidad, y facilidad de entender una funcion (esto no desmerece los comentarios que siempre son bienvenidos), de nada sirve una función de 300 lineas, porque se complica seguirla y a veces con muchos niveles de anidado (y falta de identacion especialmente, si queres malos ejemplos, hay de sobra).
Volviendo al caso que presentas...

Suponete por un momento que no solo uses I2C para la memoria EEPROM, sino que también para un RTC.

Vas a tener un archivo i2c.c que va a tener tus funciones:
Send_I2C_StartBit, Send_I2C_ControlByte, Read_I2C_Data, Send_I2C_Data, Send_I2C_StopBit, Send_I2C_ACK y Send_I2C_NACK.
Tambien vas a tener un i2c.h con los prototipos de esas funciones, para que otros .c puedan usarlas.

Algo a notar es que esas funciones manejan registros del microcontrolador. Y luego te voy a hacer ver el porque es importante esto.

Luego vas a tener un archivo eeprom.c con su correspondiente cabecera y va a ser algo así:

Código: C
  1. #include "i2c.h"
  2.  
  3. void Write_EEPROM(uint8_t word_address,uint8_t eeprom_data)
  4. {
  5.        //escribir en eeprom externa
  6.        Send_I2C_StartBit();                    // send start bit
  7.        Send_I2C_ControlByte(0x00,0);  // send control byte with R/W bit set low
  8.        Send_I2C_Data(word_address);            // send word address
  9.        Send_I2C_Data(eeprom_data);             // send data byte
  10.        Send_I2C_StopBit();                     // send stop bit
  11. }
  12.  
  13. uint8_t Read_EEPROM(uint8_t word_address)
  14. {      
  15.       //leer desde la eeprom externa
  16.       Send_I2C_StartBit();                    // send start bit
  17.       Send_I2C_ControlByte(0x00,0);  // send control byte with R/W bit set low
  18.       Send_I2C_Data(word_address);            // send word address
  19.       Send_I2C_StartBit();                    // send start bit
  20.       Send_I2C_ControlByte(0x00,1);  // send control byte with R/W bit set high
  21.       incoming_data = Read_I2C_Data();        // now we read the data coming back from the eeprom
  22.       Send_I2C_NAK();                         // send a the NAK to tell the eeprom we don't want any more data
  23.       Send_I2C_StopBit();                     // and then send the stop bit
  24.       return incoming_data;
  25. }

Tambien podemos tener nuestro rtc.c con su cabecera tambien:

Código: C
  1. #include "i2c.h"
  2.  
  3. void Write_RTC(uint8_t hour,uint8_t minute,uint8_t second)
  4. {
  5.  
  6.        Send_I2C_StartBit();                    // send start bit
  7.        Send_I2C_ControlByte(0x00,0);  // send control byte with R/W bit set low
  8.        Send_I2C_Data(0x10);            // 0x10 donde esta la hora
  9.        Send_I2C_Data(hour);             // send data byte
  10.        Send_I2C_Data(minute);             // send data byte
  11.        Send_I2C_Data(second);             // send data byte
  12.        Send_I2C_StopBit();                     // send stop bit
  13. }
  14.  
  15. uint8_t Read_RTC_hour(void)
  16. {      
  17.  
  18.       Send_I2C_StartBit();                    // send start bit
  19.       Send_I2C_ControlByte(0x00,0);  // send control byte with R/W bit set low
  20.       Send_I2C_Data(0x10);            // send word address
  21.       Send_I2C_StartBit();                    // send start bit
  22.       Send_I2C_ControlByte(0x00,1);  // send control byte with R/W bit set high
  23.       hour = Read_I2C_Data();        // now we read the data coming back from the eeprom
  24.       Send_I2C_NAK();                         // send a the NAK to tell the eeprom we don't want any more data
  25.       Send_I2C_StopBit();                     // and then send the stop bit
  26.       return hour;
  27. }

Obviamente luego tenes tu main.c por ejemplo en el cual vas a usar estas funciones y vas a incluir los archivos eeprom.h y rtc.h (que no los escribi pero imagino que te daras cuenta como son)

Código: C
  1. #include "eeprom.h"
  2. #include "rtc.h"
  3.  
  4. void main(void)
  5. {
  6.    int hora, temp;
  7.  
  8.    Write_RTC(12,24,36);
  9.    hora = Read_RTC_hour();
  10.  
  11.    Write_EEPROM(0x00,hora);
  12.    temp = Read_EEPROM(0x00);
  13.  
  14.    while(1);
  15. }

Y gracias a la libreria de I2C ahora poseo 3 grandes mejoras que no tenia antes...

1- Mi main es mas legible, se lo que se hace a simple vista. En este caso es el main, pero puede haber sido cualquier otra funcion.

2- Me permite reusar el codigo, se que funciona la parte de I2C, entonces solo debo reusar ese codigo para otras funciones, si tuviera un sensor de temperatura por I2C con solo agregar el include lo podria hacer.

3- Aunque no lo parezca, si vamos a cambiar de microcontrolador, suponete que te vas a un PIC24 y cambian de nombre los registros,tenes que agregar mas cosas, etc, nuestro main.c, eeprom.c, rtc.c NO LOS TENES QUE CAMBIAR. Solo cambias el archivo de I2C.. es decir aislamos lo que es puro C, de lo que es referente al hardware. Entonces para pasar a otro micro lo unico que debes cambiar es el archivo i2c. Proporcionar la misma funcionalidad y todo andando genial como antes, resumen: REUTILIZAR EL CODIGO. (Por supuesto vas a tener una funcion EEPROM_Init el cual configure los pines para I2C llamando a una funcion de i2c.c, inicie algunas variables si es necesario necesarias para la EEPROM, etc).


---------------

PD: Omití por claridad que sean correctas las direcciones de I2C al inicio, pero es para que se entienda algunas ventajas.
Igual el codigo fue un copy/paste, pero fue un ejemplo "sencillo" de porque usarias una libreria.

« Última modificación: 31 de Mayo de 2018, 00:08:27 por KILLERJC »

Desconectado Loudness

  • PIC16
  • ***
  • Mensajes: 147
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #9 en: 31 de Mayo de 2018, 00:54:24 »
KILLERJC ((:-)) ((:-)) ((:-))

Creo que ya lo entendi.

Voy a ponerme e intentar crear una libreria para hacer funcionar algo.

Gracias de nuevo por la ayuda.

PD: dentro de poco me tendreis por aqui jejeje

Desconectado Loudness

  • PIC16
  • ***
  • Mensajes: 147
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #10 en: 03 de Junio de 2018, 23:07:59 »
Aqui estoy de nuevo :lol:

Despues de varios dias con el tema del I2C he escrito un sencillo programa que, utilizando las librerias de mircochip (sacadas de AN1488), hace lo siguiente:

- Aumenta un dato
- Lo escribe en una eeprom externa por I2C
- Lo lee de nuevo
- Saca el resultado por leds conectados al puertoF (PIC16F1947)

Y probando en Proteus (todavia no he probado en protoboard) el programa funciona, es decir, por el puertoF se va aumentando el dato en binario.

Sin embargo, salen montones de warnings (adjunto imagen)que no entiendo muy bien.


* Warnings 180604.JPG
(187.74 kB, 925x527 - visto 862 veces)


Buscando por la web, parece que hay gente que tiene el mismo problema, pero lo soluciona cambiando las resistencias pull-up, algo que a mi o me ha funcionado :?.

De todas maneras, como todavia estoy empezando con el c, creo que mi problema puede ser alguna configuracion del programa
o de los tiempos o algo asi??.

Agradeceria cualquier ayuda.
« Última modificación: 03 de Junio de 2018, 23:14:09 por Loudness »

Desconectado Loudness

  • PIC16
  • ***
  • Mensajes: 147
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #11 en: 03 de Junio de 2018, 23:19:11 »
No adjunte el programa, aqui se lo envio

* main.c
(3.47 kB - descargado 192 veces)

* I2C_EEPROM.c
(11.23 kB - descargado 267 veces)

* I2C_EEPROM.h
(1.66 kB - descargado 175 veces)

* PIC16F1947 I2C.hex
(2.61 kB - descargado 163 veces)



Gracias por adelantado.

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #12 en: 04 de Junio de 2018, 00:54:10 »
Tal ves porque montaste un I2C por software, lo que esta diciendo el warning es que se detecto una transicion muy corta, es decir el pin cambio de estado en muy poco tiempo,, imaginatelo como un muy pequeño pulso. no vi el programa pero tal ves es debido a que esos pines no trabajan como open colector.

Hay otro warning para la señal de stop, que con el oscilador que tengas no cumplas los tiempos exactos, de todas formas yo reemplazaria esas funciones por una que utilice el modulo I2C.
¿Si tenes un modulo, porque complicarse pensandolo en hacer por software?

Desconectado Loudness

  • PIC16
  • ***
  • Mensajes: 147
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #13 en: 04 de Junio de 2018, 01:26:15 »
Gracias por la respuesta.

Tal ves porque montaste un I2C por software, lo que esta diciendo el warning es que se detecto una transicion muy corta, es decir el pin cambio de estado en muy poco tiempo,, imaginatelo como un muy pequeño pulso. no vi el programa pero tal ves es debido a que esos pines no trabajan como open colector.

Me imaginaba que era tema del programa, no siempre se puede dudar del proteus jejeje

de todas formas yo reemplazaria esas funciones por una que utilice el modulo I2C.
¿Si tenes un modulo, porque complicarse pensandolo en hacer por software?

Aqui me mataste porque ya no entendi nada :lol:.
 
Despues de leer libros y consultar diversas fuentes yo pense que esta era la utilizacion del I2C, pero por tus palabras veo que, si el modulo existe (y el pic16f1947 lo tiene), hay una forma menos complicada de utilizarlo?.

Entonces, la pregunta es basica, como se utiliza el modulo I2C?.

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:I2C con XC8: Pin SCL como salida?
« Respuesta #14 en: 04 de Junio de 2018, 10:57:03 »
Tenes 2 formas de realizar el codigo para el I2C, utilizando el modulo (hardware), o hacerlo por software.

Cual es la diferencia? Por software vos sos el encargado de manejar los pines, es como estas haciendo, poner el SCL a 1, o a 0, pasarlo a entrada, controlar los tiempos con NOPs o demoras. Etc. Esto es por software.

Por Hardware (usando el modulo) solo le indicas al modulo que de un Start, y el modulo se encarga de todo lo que hacias antes. es decir controlar SDA, SCL y ademas los tiempos. El unico "extra" que tenes usando el modulo es configurarlo. Y la unica limitante es que solo algunos pines pueden ser I2C, en cambio en software como se hace a mano y controlando el puerto, podes hacerlo con cualquier pin. Al codigo para usarlo por hardware ya lo tenias realizado en un codigo que me pasaste:

Código: C
  1. void Send_I2C_Data(unsigned int databyte)
  2. {
  3.     PIR1bits.SSPIF=0;          // clear SSP interrupt bit
  4.     SSPBUF = databyte;          // send databyte
  5.     while(!PIR1bits.SSPIF);    // Wait for interrupt flag to go high indicating transmission is complete
  6.  
  7. }
  8.  
  9. unsigned int Read_I2C_Data(void)
  10.  
  11. {
  12.  
  13.     PIR1bits.SSPIF=0;          // clear SSP interrupt bit
  14.     SSPCON2bits.RCEN=1;         // set the receive enable bit to initiate a read of 8 bits from the serial eeprom
  15.     while(!PIR1bits.SSPIF);    // Wait for interrupt flag to go high indicating transmission is complete    
  16.     return (SSPBUF);            // Data from eeprom is now in the SSPBUF so return that value
  17.  
  18. void Send_I2C_StartBit(void)
  19.  
  20. {
  21.     PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
  22.     SSPCON2bits.SEN=1;          // send start bit
  23.     while(!PIR1bits.SSP1IF);    // Wait for the SSPIF bit to go back high before we load the data buffer
  24. }
  25.  
  26. void Send_I2C_StopBit(void)
  27.  
  28. {
  29.     PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
  30.     SSPCON2bits.PEN=1;          // send stop bit
  31.     while(!PIR1bits.SSP1IF);    // Wait for interrupt flag to go high indicating transmission is complete
  32. }
  33.  
  34. void Send_I2C_ACK(void)
  35.  
  36. {
  37.    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
  38.    SSPCON2bits.ACKDT=0;        // clear the Acknowledge Data Bit - this means we are sending an Acknowledge or 'ACK'
  39.    SSPCON2bits.ACKEN=1;        // set the ACK enable bit to initiate transmission of the ACK bit to the serial eeprom
  40.    while(!PIR1bits.SSP1IF);    // Wait for interrupt flag to go high indicating transmission is complete
  41. }
  42.  
  43. void Send_I2C_NAK(void)
  44.  
  45. {
  46.     PIR1bits.SSP1IF=0;           // clear SSP interrupt bit
  47.     SSPCON2bits.ACKDT=1;        // set the Acknowledge Data Bit- this means we are sending a No-Ack or 'NAK'
  48.     SSPCON2bits.ACKEN=1;        // set the ACK enable bit to initiate transmission of the ACK bit to the serial eeprom
  49.     while(!PIR1bits.SSP1IF);    // Wait for interrupt flag to go high indicating transmission is complete
  50. }

El modulo del I2C suele esta con el de SPI juntos, y lo llaman MSSPx, Como el PIC posee 2 MSSP, voy a poner la x donde se necesite un 1, o un 2 para identificarlo, Asi que asi lo vas a encontrar en tu datasheet.

Segun el datasheet para configurar el modulo se necesita:
Poner los pines SDAx y SCLx como entradas.
Configurar los registros.

Antes de meternos en los registros, Como deberias saber en I2C hay un master y un slave. Tu micro va a ofciar de Master, por lo tanto vas a controlar el clock. Cuando estas como Maestro tenes que la bandera de interrupcion se activa (ojo la bandera se activa sin necesidad de que vos habilites la interrupcion) cuando ocurren las siguientes posibilidades:

Citar
The following events will cause the SSPx Interrupt Flag bit, SSPxIF, to be set (SSPx interrupt, if enabled):
• Start condition detected
• Stop condition detected
• Data transfer byte transmitted/received
• Acknowledge transmitted/received
• Repeated Start generated

Es decir cuando termina el Start que da el maestro, o el Stop, o termina de transmitir o recibir, cualquiera de esas termina con la bandera poniendose a 1 en maestro. Es por eso que tu codigo lee continuamente el SSPxIF para saber cuando termino.

Primero que nada tenemos que ajustar la velocidad de transmision. Que el I2C define 100Khz/400Khz y otro mas, pero esos son los mas comunes. Vamos a definirlo como 100KHz, segun uno de tus codigos, tenes un cristal de 16Mhz. Segun el datasheet necesitas cargar 0x27 en SSPxADD para tner 100Khz. Este registro tiene doble proposito, cuando es esclavo lo configuras para asignarle una direccion, cuando es maestro para controlar la frecuencia del clock

Ahora los demas registros:
SSPxSTAT = 0x80;  // Desactivo el slew-rate para los 100Khz
SSPxCON1 = 0x38; // activo los pines para I2C, activo la salida de clock, lo configuro como I2C Master
SSPxMSK = 0x00 // Desactivo la comparacion con la direccion, ya que estamos como maestro.

SSPxCON2, tiene los bits que debes activar para inciar un START, para ver el ACK si llego o no, etc.


Respecto al codigo que pasaste antes, tendrias que hacerle unos cambios.. Por ejemplo:

SSPCON2bits.SEN=1;          // send start bit

Ahora va a depender de que modulo uses, si el 1 o el 2.

SSP1CON2bits.SEN=1;          // send start bit
SSP2CON2bits.SEN=1;          // send start bit

Igual la bandera de interrupcion, la del SSP1 esta en PIR1 como tu codigo, y la del SSP2 esta en PIR4.

Hasta te da un "paso a paso" de como hacerlo, claramente vas a ver que cumple con todo lo que tenes que enviar en tu memoria:

Citar
24.6.6.4 Typical transmit sequence:
1. The user generates a Start condition by setting the SEN bit of the SSPxCON2 register.
2. SSPxIF is set by hardware on completion of the Start.
3. SSPxIF is cleared by software.
4. The MSSPx module will wait the required start time before any other operation takes place.
5. The user loads the SSPxBUF with the slave address to transmit.
6. Address is shifted out the SDAx pin until all eight bits are transmitted. Transmission begins as soon as SSPxBUF is written to.
7. The MSSPx module shifts in the ACK bit from the slave device and writes its value into the ACKSTAT bit of the SSPxCON2 register.
8. The MSSPx module generates an interrupt at the end of the ninth clock cycle by setting the SSPxIF bit.
9. The user loads the SSPxBUF with eight bits of data.
10. Data is shifted out the SDAx pin until all eight bits are transmitted.
11. The MSSPx module shifts in the ACK bit from the slave device and writes its value into the ACKSTAT bit of the SSPxCON2 register.
12. Steps 8-11 are repeated for all transmitted data bytes.
13. The user generates a Stop or Restart condition by setting the PEN or RSEN bits of the SSPxCON2 register. Interrupt is generated once the Stop/Restart condition is complete

Lo bueno es que tenes las funciones, tenes masomenos un paso a paso. Solo falta ver que es lo que pide la memoria.

Ejemplo para escribir Necesitas:
- Enviar el Start
- Enviar la direccion de la memoria + R/W , Devuelve un ACK
- Enviar la direccion del contenido, byte alto, Devuelve un ACK
- Enviar la direccion del contenido, byte bajo, Devuelve un ACK
- Enviar el/los datos, Devuelve un ACK
- Generar STOP.

Entonces usando las funciones de arriba, omitimos la lectura de los ACK recibidos de la memoria, si queres asegurarte, tenes que leer el bit ACKSTAT del registro SSPxCON2 para saber si envio un NACK o ACK:

Voy a suponer que, tenemos definidos la direccion de la eeprom y el WRITE/READ con defines, y que se quiere escrbir 0x50, en la direccion 0x123 (Supongamos que llegue ejje )

Código: C
  1. Send_I2C_StartBit();
  2. Send_I2C_Data(DIRECCION_EEPROM || WRITE);
  3. Send_I2C_Data(0x01); //Direccion alta
  4. Send_I2C_Data(0x23); //Direccion baja
  5. Send_I2C_Data(0x50); //Dato
  6. Send_I2C_StopBit()
;

La lectura es un poco mas complicada, pero si ves el grafico de como leer la memoria ,te vas a dar cuenta que tenes que enviar paso a paso y que es lo que recibis. Y para recibir tenes que avisarle al modulo que vas a hacerlo, por eso pone a 1 un bit en la funcion de recepcion. Lo que hace el modulo es encargarse de generar el clk para recibir y una ves completa la recepcion poner a 1 la bandera de interrupcion.
« Última modificación: 04 de Junio de 2018, 11:03:44 por KILLERJC »


 

anything