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.