Atmega328P and RaspberryPi TWI issue

1 post / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi!

I have strange problems in I2C bus when using i2c-tools with the Raspberry Pi and an Atmega328P. The code shown simply uses an FSM which does some actions in the bus, sending the data from a vector dataByteLog[], and simply set the pointer cnt1 according to what is received from the RPi. The data for the cnt1 is receved, the byte pointed is returned and sent on TWI. But the first byte always returns the correct value in OR with the 0x80. So, if I need to receive 0x02, I receive 0x82. Performing the operation as 16bit words, the first byte is incorrect with th 0x80, the second is correct.Basically, the bug is that there is this 0x80 value, or one most significant bit which is sent wrongly always high at the first byte transfer.

Moreover, everything is ok with the hardware, the board is using the RPi pullups and is working a 3.3V with common GND and it is ok on the oscilloscope. But I don't have a DSO to analyze the actual data.

This is the main code:

/* address 0xD0 */
i2c_init(RTC_PROTOCOL_DEVICE_ADDRESS, I2C_SLAVE_INIT_MODE);

sei();
while(1)
{

};

 

This is the I2C init code, with the variables used (I am not posting the complete struct, but only the relevant data):

/* Current bus status defines */
#define I2C_MASTER_START_STATE 0x08
#define I2C_MASTER_RESTART_STATE 0x10
#define I2C_MASTER_TX_ADX_ACK_NOT_RECEIVED_STATE 0x20
#define I2C_MASTER_TX_ADX_ACK_RECEIVED_STATE 0x18
#define I2C_MASTER_RX_ADX_ACK_RECEIVED_STATE 0x40
#define I2C_MASTER_RX_ADX_ACK_NOT_RECEIVED_STATE 0x48
#define I2C_MASTER_END_RX_DATA_STATE 0x58
#define I2C_MASTER_RX_DATA_STATE 0x50
#define I2C_MASTER_TX_ACK_RECEIVED_STATE 0x28
#define I2C_MASTER_TX_ACK_NOT_RECEIVED_STATE 0x30
#define I2C_SLAVE_RX_ADX_STATE 0x60
#define I2C_SLAVE_RX_DATA_ACK_TX_STATE 0x80
#define I2C_SLAVE_RX_DATA_NACK_TX_STATE 0x88
#define I2C_SLAVE_TX_ADX_STATE 0xA8
#define I2C_SLAVE_TX_DATA_ACK_RX_STATE 0xB8
#define I2C_SLAVE_TX_DATA_NACK_RX_STATE 0xC0
#define I2C_SLAVE_END_TX_DATA_ACK_RX_STATE 0xC8
#define I2C_SLAVE_STOP_RESTART_STATE 0xA0

static volatile uint8_t dataLogByte[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x03, 0x00, 0x00};
static volatile uint8_t cnt1 = 0;
static volatile uint8_t dataLogBytew[288];
static volatile uint8_t cnt2 = 0;
I2C_DATA i2cData;

void i2c_init(uint8_t speed_adx, uint8_t mode)
{
/** Or speed, or address, depending if master or slave, respectively */
if (mode == I2C_MASTER_INIT_MODE)
{
    i2cData.baud = (uint16_t)speed_adx;
    TWCR = 0;
    i2c_baudSelector(i2cData.baud);
    TWCR |= (1<<TWIE); /* Int enabled */
    TWCR |= (1<<TWEN); /* enabled */
    /*By writing the TWEA bit to zero, the device can be virtually     disconnected from the 2-wire Serial
    Bus temporarily. Address recognition can then be resumed by writing the TWEA bit to one
    again.
     */
    i2cData.mode = I2C_MASTER_INIT_MODE;
}
else if (mode == I2C_SLAVE_INIT_MODE)
{
    TWAR =  (uint8_t)speed_adx;
    TWCR = 0b01000101;
    i2cData.mode = I2C_SLAVE_MODE;
    i2cData.index = 0;
    i2cData.buffLenRx = I2C_BUFF_LEN;
    i2cData.buffLenTx = I2C_BUFF_LEN;
}
else
{

}

i2cData.busy = 0;
}

 


 

This is the I2C interrupt code:

 

uint8_t i2c_byteHandler_ISR(void)
{

uint8_t state;
i2cData.status = TWSR;
state = (i2cData.status & I2C_STATUS_MASK);

switch (state)
{
    case I2C_MASTER_START_STATE:
    break;

    case I2C_MASTER_RESTART_STATE:
    break;

    case I2C_SLAVE_RX_ADX_STATE:
    cnt1 = 0;
    break;

    case I2C_SLAVE_RX_DATA_ACK_TX_STATE:
        cnt1 = TWDR;
        i2cData.sendNack = 0;
    break;

    case I2C_SLAVE_RX_DATA_NACK_TX_STATE:
    /* next: I2C_SLAVE_STOP_RESTART_STATE */
    break;

    case I2C_SLAVE_STOP_RESTART_STATE:
    i2cData.sendNack = 0;
    break;

    case I2C_SLAVE_TX_ADX_STATE:
    i2cData.sendNack = 0;
    TWDR = dataLogByte[cnt1++];
    break;

    case I2C_SLAVE_TX_DATA_ACK_RX_STATE:
    i2cData.sendNack = 0;
    TWDR = dataLogByte[cnt1++];
    break;

    case I2C_SLAVE_END_TX_DATA_ACK_RX_STATE:
    i2cData.sendNack = 0;
    break;

    case I2C_SLAVE_TX_DATA_NACK_RX_STATE:
    /* No more transfers. reactivate adx recognition */
    i2cData.sendNack = 0;
    cnt1 = 0;
    break;

    case I2C_MASTER_TX_ADX_ACK_NOT_RECEIVED_STATE:
    break;

    case I2C_MASTER_TX_ADX_ACK_RECEIVED_STATE:
    break;

    case I2C_MASTER_RX_ADX_ACK_RECEIVED_STATE:
    break;

    case I2C_MASTER_RX_ADX_ACK_NOT_RECEIVED_STATE:
    break;

    case I2C_MASTER_END_RX_DATA_STATE:
    break;

    case I2C_MASTER_RX_DATA_STATE:
    break;

    case I2C_MASTER_TX_ACK_RECEIVED_STATE:
    break;

    case I2C_MASTER_TX_ACK_NOT_RECEIVED_STATE:
    break;

    default:
    i2cData.stopReq = 1;
    break;
}
i2c_clearInt();
dataLogBytew[cnt2++] = state;
return state;

 

 

This is the clearInt executealways at the end of the ISR:

 

static void i2c_clearInt(void)
{
if (i2cData.reStartReq == 1)
{
    /* As start, but with no clear_int */
    /*en ack active again */
    TWCR = 0b10100101;
    i2cData.reStartReq = 0;
}
else if (i2cData.stopReq == 1)
{
    /*en ack not active again */
    TWCR = 0b11010101;
    i2cData.busy = 0;
    i2cData.stopReq = 0;
}
else
{
    /* Normal */
    if (i2cData.sendNack)
    TWCR = 0b10000101;
    else
    TWCR = 0b11000101;

}
}

 

So, in the RPi I use the sudo i2cget -f -y 1 0x68 0x01

sudo i2cget -f -y 1 0x68 0x01

and I will get 0x82 from the Atmel. I use the 

i2cget -f -y 1 0x68 0x01 w

 to retrieve two consecutive bytes from the Atmel, and I will get 0x0382 in one 16-bit word, instead 0x0302. Where 0x03 is correct, and 0x82 is not because should be 0x02. I am really stucked. This code was working in another complex project, but then I modified something outside the TWI driver, and some bugs arise. So I have investigated and foud that there is some basically wrong with this implementation.

Last Edited: Sat. Oct 1, 2016 - 08:54 PM