AVR puede escribir I2C, pero no puede leer (falla al escribir la dirección de registro)

0

Estoy usando un ATMega328 con un TLE493D (A0) (y manual ) sensor de efecto hall de 3 ejes. He realizado lecturas / escrituras exitosas en otros dispositivos I2C (AT42QT2120), pero tengo problemas con el TLE493. Recibo la confirmación de una escritura I2C exitosa (a través de los bits de estado Oscope y TWI) pero cuando intento leer, la línea I2C mantiene el nivel alto de SDA cuando intento escribir la dirección y, posteriormente, cuando intento leer datos Entonces solo muestra la actividad de un bit y luego la condición de parada.

En cuanto a otro hardware, tengo tapas de desacoplamiento .1uF tanto en el TLE493 como en el ATMega328, el PB1 dispara un LED para el estado (además de servir como un activador de oscope externo) y 4.7k (he probado 2.2k y 10k también) pullups en el I2C. Todo funciona con el regulador de 3.3 V del módulo CP2102 que estoy usando para UART.

Parareferencia,estoesloquepareceunaescritura(0x85pararegistrar0x11):

Ymicódigo(AVRC)(F_CPUSEdefinecomounsímboloenelproyecto):

//#defineF_CPU8000000UL#definewADDR0x6A#definerADDR0x6B#include<avr/io.h>#include<util/delay.h>#include<avr/interrupt.h>#include"my_i2clib.h"
#include "my_usartlib.h"

uint8_t i2c_read(uint8_t addr, uint8_t reg);
uint8_t i2c_write(uint8_t addr, uint8_t reg, uint8_t data);

int main(void){

    usart_init();
    i2c_init();
    DDRB |= (1<<PB1);

    usart_tx_string("Initialized   -   ");
    usart_tx_string("Write status: ");

    PORTB |= (1<<PB1);
    i2c_write(wADDR, 0x11, 0x85);
    _delay_ms(50);
    usart_tx(i2c_write(wADDR, 0x11, 0x85));
    usart_tx('\n');


    usart_tx_string("Read response: "); usart_tx('\n');
    //PORTB |= (1<<PB1);
    usart_tx_val(i2c_read(rADDR, 0x16), 16);
    usart_tx('\n');

    while (1){


    }// end while
}//end main




uint8_t i2c_write(uint8_t addr, uint8_t reg, uint8_t data){
    TWIStart();
    if(TWIGetStatus() != 0x08)
    return 0x53;    //S error

    TWIWrite(addr);
    if(TWIGetStatus() != 0x18)
    return 0x41;    //A error

    TWIWrite(reg);
    if(TWIGetStatus() != 0x28)
    return 0x57;    //W error

    TWIWrite(data);
    if(TWIGetStatus() != 0x28)
    return 0x57;    //W error

    TWIStop();
    return 0x00;    //success
}

uint8_t i2c_read(uint8_t addr, uint8_t reg){
    uint8_t outData = 0x00;
    TWIStart();
    usart_tx_val(TWIGetStatus(), 16); usart_tx('\n');
    TWIWrite(addr);
    //_delay_us(10);
    usart_tx_val(TWIGetStatus(), 16); usart_tx('\n');
    TWIWrite(reg);
    usart_tx_val(TWIGetStatus(), 16); usart_tx('\n');
    outData = TWIReadACK();
    usart_tx_val(TWIGetStatus(), 16); usart_tx('\n');
    TWIStop();
    return outData;
}

Y aquí está el código i2c:

    /*
    My i2c library
    Most code stolen from AVR datasheets / tutorials
    Careful about portability, this is used on ATMega328
*/

#include "my_i2clib.h"
#include <stdint.h>
#include <avr/io.h>

void i2c_init(void)
{
    //set SCL to 200kHz (?)
    // SCL freq = F_CPU / (16 + 2*TWBR) --ignoring prescaler, which should always be 1 (0x00 in register)
    // TWBR = 0.5*(F_CPU / 2*200000) - 16
    TWSR = 0x00;
    TWBR = 0x0C;
    //enable TWI
    TWCR = (1<<TWEN);

}

void TWIStart(void){
    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
    while ((TWCR & (1<<TWINT)) == 0);
}

void TWIStop(void){
    TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}

void TWIWrite(uint8_t u8data){
    TWDR = u8data;
    TWCR = (1<<TWINT)|(1<<TWEN);
    while ((TWCR & (1<<TWINT)) == 0);
}

uint8_t TWIReadACK(void){
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
    while ((TWCR & (1<<TWINT)) == 0);
    return TWDR;
}

uint8_t TWIReadNACK(void){
    TWCR = (1<<TWINT)|(1<<TWEN);
    while ((TWCR & (1<<TWINT)) == 0);
    return TWDR;
}

uint8_t TWIGetStatus(void){
    uint8_t status;
    //mask status
    status = TWSR & 0xF8;
    return status;
}

Como puede ver, leí el byte de estado TWI después de cada comando y obtengo lo siguiente:

La Tabla 22-3 de la hoja de datos de Atmega328 indica que estos códigos se leen de la siguiente manera:

  • 0x08: START transmitido
  • 0x40: Dirección de esclavo + bit de lectura enviado, ACK recibido
  • 0x58: Byte de datos recibido, NACK devuelto
  • 0x50: Byte de datos recibido, ACK devuelto

El 0xFF es el valor de retorno de i2c_read, es decir, los datos que debería haber recibido.

Si hay más información que necesitarías para ayudar, estaré encantada de proporcionarla. Cualquier dirección sería apreciada, he estado en esto por un par de semanas sin suerte.

EDITAR: probando esta nueva secuencia de lectura como se sugiere:

    uint8_t i2c_read(uint8_t addr, uint8_t reg){
    uint8_t outData = 0x00;
    TWIStart();
    //usart_tx(TWIGetStatus()); usart_tx('\n');
    TWIWrite(addr);
    //usart_tx(TWIGetStatus()); usart_tx('\n');
    TWIWrite(reg);
    //usart_tx(TWIGetStatus()); usart_tx('\n');
    TWIStart();
    TWIWrite(addr | 0x01);

    outData = TWIReadNACK();
    TWIStop();
    return outData;
}

Resultados en algún comportamiento extraño. Me cuesta mucho activar el oscope para capturar esta secuencia en particular, pero lo que he visto me muestra que después de un tercer reinicio, SCL se queda atascado. Además, y probablemente posteriormente, las escrituras ya no se procesan (error de direccionamiento informado)

EDIT2: Voy a dejar esto por ahora. He intentado todos los métodos sugeridos, pero también noté que el módulo CP2102 que estoy usando en realidad produce alrededor de 4.1V en el pin etiquetado "3.3V". Tal vez este fue el problema? Hasta que pueda ordenar un regulador adecuado o arreglar este panel de ruptura, este proyecto tiene que estar en espera.

    
pregunta Orotavia

1 respuesta

2

Haces mal la secuencia de registro de lectura. No puede enviar la dirección de registro después de enviar ADDR + R. La secuencia correcta está en las hojas de datos, pero simplemente siguiendo estas líneas: 1) inicio + ADDR_W 2) enviar dirección de registro 3) inicio + ADDR_R 4) recibir datos 5) detener

    
respondido por el Justme

Lea otras preguntas en las etiquetas