La comunicación I2C no se iniciará a menos que se vuelva a insertar la resistencia de extracción SDA

4

Estoy trabajando en un proyecto en el que un microprocesador PIC12LF1552 usa I2C para hablar con un sensor de temperatura TMP75. Como dice el título, un problema muy extraño sigue sucediendo.

Descripción general

Cada vez que enciendo el micro, el SDA (línea de datos) y SCL (línea de reloj) permanecen bajos. Después del período de espera de 18 segundos en el código, ambas líneas pasan al estado alto cuando la función I2C_init inicia el bus I2C. En este punto, sé que el micro debería estar escribiendo en el registro de configuración en el sensor de temperatura. El bucle infinito destella un LED cada vez que el micro escribe en el sensor de temperatura.

El problema es que las líneas I2C se mantendrán atascadas a menos que elimine la resistencia de recuperación SDA y luego la vuelva a colocar. Una vez que se vuelve a colocar la extracción SDA, el alcance captura la comunicación I2C perfectamente normal, y todo funciona bien . (Ver abajo)

Captura de pantalla de Scope 4.7k pullups

Capturadepantalladelalcance1kpullups

Resistencias Pullup

Estoy usando resistencias pullup de 1k ahora, sin embargo, en la captura de pantalla anterior estaba usando 4.7k, que es demasiado grande. Con resistencias de 1 k, la línea de reloj y la línea de datos son mucho más cuadradas y de aspecto adecuado. Con el debilitamiento interno débil en el micro habilitado, la comunicación I2C no funcionaría en absoluto.

Información adicional

He verificado que todos los bits del comando de escritura I2C que se está enviando son correctos. También he usado una Raspberry Pi para leer y escribir correctamente en el TMP75.

La velocidad del bus I2C establecida en el micro es 100kHz. El TMP75 puede manejar hasta 400 kHz, por lo que esto no es un problema.

Reinsertar el pullup SDA es lo que permite que ocurra la comunicación normal I2C. Sin embargo, reinsertar el pullup de SCL mientras el I2C está "atascado" no soluciona el problema.

He preguntado a varios profesores en mi universidad sobre este problema, pero ninguno de ellos encontró una solución.

Pregunta

Por lo tanto, mi pregunta es: ¿Qué podría estar causando la necesidad de reinsertar la recuperación de SDA para que I2C comience a comunicarse?

Código de referencia

main.c

#include <xc.h>
#include "i2c.h"

//PIC12LF1552 Configuration Bit Settings
// 'C' source line config statements

#define _XTAL_FREQ 16000000
#define I2C_SLAVE 0x48

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = ON         // Flash Program Memory Code Protection (Program memory code protection is enabled)
#pragma config BOREN = OFF       // 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)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#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 LPBOR = OFF      // Low-Power Brown Out Reset (Low-Power BOR is disabled)
#pragma config LVP = ON         // Low-Voltage Programming Enable (Low-voltage programming enabled)

// Send a command to I2C chip
void i2c_write_command(unsigned char address, unsigned char command)
{
i2c_Start();                        // Send Start
i2c_Address(I2C_SLAVE, I2C_WRITE);  // Send slave address - write operation
i2c_Write(address);                 // Send register address 
i2c_Write(command);                 // Send register value (command)
i2c_Stop();                         // Send Stop
}

// Read a char from I2C chip
unsigned char i2c_read_command(unsigned char address)
{
unsigned char read_byte;
// Read one byte
i2c_Start();                        // send Start
i2c_Address(I2C_SLAVE, I2C_WRITE);  // Send slave address - write operation
i2c_Write(address);                 // Set register for read
i2c_Restart();                      // Restart
i2c_Address(I2C_SLAVE, I2C_READ);   // Send slave address - read operation  
read_byte = i2c_Read(0);            // Read one byte
                      // If more than one byte to be read, (0) should
                      // be on last byte only
                      // e.g.3 bytes= i2c_Read(1); i2c_Read(1); i2c_Read(0);
i2c_Stop();                         // send Stop
return read_byte;                   // return byte. 
                                    // If reading more than one byte
                                    // store in an array
}

void main(void) {

unsigned char temp1;
int i=0;

OSCCON = 0b01111010;                // set internal osc to 16MHz
TRISA = 0b000000;                   // set ports to outputs
PORTA = 0b000000;
//OPTION_REG = 0b01011000;            //or hex 0x58, enable pull up resistors
//WPUA1 = 1;                          //enable individual pull ups
//WPUA2 = 1;

__delay_ms(9000);
__delay_ms(9000);

i2c_Init();                         // Start I2C as Master 100KHz
i2c_write_command(0x1,0x78);        // Write configuration to register
//temp1 = i2c_read_command(0x0);
//__delay_ms(5000);

while(1)
{
    RA4 = 1;
    __delay_ms(500);
    RA4 = 0;
    __delay_ms(500);
    i2c_write_command(0x01,0x78);
}
return;
}

i2c.h EDIT: ACTUALIZADO

#define I2C_WRITE 0
#define I2C_READ 1

// Initialise MSSP port. (12F1822 - other devices may differ)
void i2c_Init(void){

   // Initialise I2C MSSP
   // Master 100KHz
   TRISA1=1;                // set SCL and SDA pins as inputs
   TRISA2=1;

   SSPCON1 = 0b00101000;    // I2C enabled, Master mode
   SSPCON2 = 0x00;      // I2C Master mode, clock = FOSC/(4 * (SSPADD + 1)) 
   SSPADD = 39;         // 100Khz @ 16Mhz Fosc
   SSPSTAT = 0b11000000;    // Slew rate disabled

}

// i2c_Wait - wait for I2C transfer to finish
void i2c_Wait(void)
{
    while ( ( SSP1CON2 & 0x1F ) || ( SSPSTAT & 0x04 ) );
}

// i2c_Start - Start I2C communication
void i2c_Start(void)
{
    //i2c_Wait();
    SEN=1;  //Indicates that start bit has been detected last
    while(SEN);
}

// i2c_Restart - Re-Start I2C communication
void i2c_Restart(void){
    //i2c_Wait();
    RSEN=1; //Enables receiver mode for I2C master mode only 
    while(RSEN);
}

// i2c_Stop - Stop I2C communication
void i2c_Stop(void)
{
    //i2c_Wait();
    PEN=1;  //Initiate stop condition on SDA and SCL pins, auto cleared by hardware
    while(PEN);
}

// i2c_Write - Sends one byte of data
void i2c_Write(unsigned char data)
{
    SSPBUF = data;  //MSSP1 Receive Buffer/Transmit Register
    while(BF);       // wait till complete data is sent from buffer 
    i2c_Wait();
}

// i2c_Address - Sends Slave Address and Read/Write mode
// mode is either I2C_WRITE or I2C_READ
void i2c_Address(unsigned char address, unsigned char mode)
{
    unsigned char l_address;

    l_address=address<<1;
    l_address+=mode;
    SSPBUF = l_address;
    while(BF);       // wait till complete data is sent from buffer
    i2c_Wait();
}

// i2c_Read - Reads a byte from Slave device
unsigned char i2c_Read(unsigned char ack)
{
    // Read data from slave
    // ack should be 1 if there is going to be more data read
    // ack should be 0 if this is the last byte of data read
    unsigned char i2cReadData;

    //i2c_Wait();
    RCEN=1;
    while(!BF);      // wait for buffer full //i2c_Wait();
    i2cReadData = SSPBUF;
    i2c_Wait();
    if ( ack ) ACKDT=0;         // Ack
    else       ACKDT=1;         // NAck
    ACKEN=1;                    // send acknowledge sequence

    return( i2cReadData );
}

Gracias por tu tiempo.

EDITAR: Actualizado i2c.h EDIT2: Captura de pantalla cargada con 1k resistencias pullup.

    
pregunta Rdd

3 respuestas

2

Bueno, esto podría no ser una respuesta completa, pero resuelve el problema. La solución a este problema fue alternar manualmente la línea SDA después de que inicialice el bus i2c. Aquí está el código que agregué después de la llamada a i2c_init ().

TRISA2 = 0;
RA2 = 1;
RA2 = 0;
__delay_ms(10);
RA2 = 1;
TRISA2 = 1;

Por lo tanto, al cambiar manualmente la línea SDA, el problema se soluciona. Ahora puedo leer y escribir en el sensor de temperatura TMP75. Gracias por toda la ayuda a todos.

    
respondido por el Rdd
3

Su problema es que ha rociado comandos i2c_wait () a través de su código.

Este es un microcontrolador, es simple, hace exactamente lo que quieres. El estado no necesita ser revisado constantemente. Solo debe llamar a i2c_wait () después de interactuar con SSPBUF. Mira esto enlace

Al mantener SCL alto y alternar SDA (quitar y reemplazar la resistencia), está enviando un bit de inicio seguido de un bit de parada. No está claro por qué esto corrige los registros que está verificando, ya que Chris Stratton sugirió que podría eliminar los registros si realmente le importara.

    
respondido por el lod
1

De la hoja de datos para su sensor de temperatura:

  

7.3.2.8 Función de tiempo de espera El TMP175 restablece la interfaz en serie si SCL > o SDA se mantiene bajo durante 54 ms (típico) entre un START y   Condición de parada El TMP175 libera el bus si se baja y   espera una condición de inicio. Para evitar activar el time-out.   Función, se debe mantener una velocidad de comunicación de al menos 1 kHz.   para la frecuencia de operación SCL.

Tal vez su sensor esté agarrando el autobús y no lo suelte, y su manipulación de levantamiento es suficiente para que se suelte.

En cualquier caso, es hora de arremangarse y hacer una depuración seria.

Una buena manera de depurar este tipo de cosas es con un alcance con un módulo de activación en serie avanzado para que pueda activar un patrón para buscar ese retraso de 54 ms.

Alternativamente, en este punto, hay poca excusa para no saber dónde o cómo se cuelga tu programa. Al menos alternar un bit digital en main para convencerse de que el bucle while no se está ejecutando. Una vez que haya terminado, y descubra que no lo está, entonces comience a activar los bits cuando ingrese a una subrutina y apáguelo cuando la abandone. En poco tiempo, al menos debes saber DÓNDE estás colgando. Después de eso, active un bit digital que sirva como un disparador de alcance para la sección de comunicación con la que está teniendo problemas, de modo que pueda obtener una imagen muy clara de lo que está pasando.

Quizás aún mejor, use sus temporizadores para agotar el tiempo de espera de cada subrutina de comunicación, y salga con un código de error. Puede que aparezca el código de error en un DIO.

    
respondido por el Scott Seidman

Lea otras preguntas en las etiquetas