Issue with implementing TWI/I2C Master Read with the ATMega4809

Go To Last Post
5 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have been working for about a week on interfacing I2C for the ATMega4809 as a master with a variety of slave devices. So far, I have been able to successfully implement Master Write functionality, however I keep getting stuck on Master Read. Specifically,  I am unable to to successfully read from a slave device. I am familiar with the I2C protocol and have been able to successfully implement both Read and Write on Arduino. I have also isolated the problem with a logic analyzer to the point where the address and read op are acknowledged, but then after that, no valid data is sent. This leads me to believe that the issue is specific to the ATMega4809 itself and maybe I am missing something in regards to the implementation of the various registers and their intricacies. I have attached my Master Initialization and Master Read functions below. Any help would be greatly appreciated.

//TWI Master Initialization
void TWI0_init(){
   TWI0.MBAUD = (uint8_t)TWI0_BAUD(100000, 1); //Set the MBAUD rate

   TWI0.MCTRLB |=  TWI_FLUSH_bm;           // Clear TWI state

   TWI0.MCTRLA &= ~TWI_TIMEOUT_gm;         // Disable Timeout for TWI operation

   TWI0.MCTRLA |=  TWI_ENABLE_bm           //enables as master
                | TWI_WIEN_bm              //enables write interrupt
                | TWI_SMEN_bm              //enables smart mode
                | TWI_RIEN_bm              //enables read interrupt
                | TWI_QCEN_bm;             //enables quick command

   TWI0.MSTATUS |= TWI_BUSSTATE_IDLE_gc;   //initially sets the bus state to idle
}


//TWI Master Read Function
void TWI0_read(uint8_t address, uint8_t* buffer, uint8_t length){

   //write slave address and write op code as read
   TWI0.MADDR = (address << 1) | READ_OP;

   //wait for ACK or NACK from slave
   while(!(TWI0.MSTATUS & TWI_WIF_bm) && !(TWI0.MSTATUS & TWI_RIF_bm)){;}


   //makes sure a ACK was received
   if(!(TWI0.MSTATUS & TWI_RXACK_bm)){

       //clears the interrupt bits
       TWI0.MSTATUS |= (TWI_RIF_bm | TWI_WIF_bm);

       //holds the location of the buffer
       uint8_t buffLoc = 0;

       //reads in data until buffer is full
       while(length){

           if (length == 1){
               TWI0.MCTRLB |= TWI_ACKACT_NACK_gc; // Next byte will be last to be received, setup NACK
           }
           else{
               TWI0.MCTRLB &= ~(1 << TWI_ACKACT_bp); // More bytes to receive, setup ACK
           }

           if (--length) {
               buffer[buffLoc] = TWI0.MDATA;
               buffLoc++;
               TWI0.MCTRLB |= TWI_MCMD_RECVTRANS_gc;

               while(!(TWI0.MSTATUS & TWI_WIF_bm) && !(TWI0.MSTATUS & TWI_RIF_bm)){;}

               TWI0.MSTATUS |= (TWI_RIF_bm | TWI_WIF_bm);
           }
           else{
               buffer[buffLoc] = TWI0.MDATA;
               buffLoc++;
               TWI0.MCTRLB |= TWI_ACKACT_NACK_gc;
               TWI0.MCTRLB |= TWI_MCMD_STOP_gc;

           }
       }
   }
   else{
       TWI0.MSTATUS |= TWI_BUSSTATE_IDLE_gc; //resets the bus state
       TWI0_sendStop(); //sends stop bit
   }

}

 

This topic has a solution.

Pete

Last Edited: Thu. Jun 20, 2019 - 05:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I haven't seen your code in detail yet, but at least I use EEP-ROM, RTC, LCD, etc. with a lot of mega4809 (or similar chips) and there is no problem with their operation.

 

One point of concern is the clearing of the flag.
This instruction clears even unintended bits for flags that are cleared by writing "1".

       //clears the interrupt bits
       TWI0.MSTATUS |= (TWI_RIF_bm | TWI_WIF_bm);

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for the reply. Are there any other pieces of code that would help? I actually took that line of code from the AVR generated code for the 4809 Master Read function within the ISR they wrote.

Pete

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I got at Atmel START to see what the current i2c_master.c is.
I didn't understand the intent of this coder, but NOTE looks like an excuse. It feels like a very unfriendly thing to me.

void I2C_0_master_isr(void)
{
    TWI0.MSTATUS |= (TWI_RIF_bm | TWI_WIF_bm);

    // NOTE: We are ignoring the Write Collision flag.

    // Address phase received NACK from slave, override next state
    if (I2C_0_status.addressNACKCheck && (TWI0.MSTATUS & TWI_RXACK_bm)) {
        I2C_0_status.state = I2C_ADDRESS_NACK; // State Override
    }

    // Bus arbitration lost to another master, override next state
    if (TWI0.MSTATUS & TWI_ARBLOST_bm) {
        I2C_0_status.state = I2C_BUS_COLLISION; // State Override
    }

    // Bus error, override next state
    if (TWI0.MSTATUS & TWI_BUSERR_bm) {
        I2C_0_status.state = I2C_BUS_ERROR; // State Override
    }

    I2C_0_status.state = I2C_0_fsmStateTable[I2C_0_status.state]();
}

 

Here is the code I actually use:
This is a reduction of the code that old AtmelSTART gave me
I do not recommend it to others because it is my own, but please refer to it as a sample that is actually working.

/* I2C Driver for AVR0/1 */
#include "i2c.h"
#include <avr/interrupt.h>

////////////////////////////////////////////////////////////////
// variable
////////////////////////////////////////////////////////////////
static volatile bool ready = true;
static volatile uint8_t result;         // Communication result
static volatile uint8_t *p_now, *p_end; // data pointer

////////////////////////////////////////////////////////////////
// Check ready
////////////////////////////////////////////////////////////////
bool i2c_isready(void){
    return ready;
}

////////////////////////////////////////////////////////////////
// Check result
////////////////////////////////////////////////////////////////
uint8_t i2c_isresult(void){
    return result;
}

////////////////////////////////////////////////////////////////
// I2C Communication start
//  i2c_adr = device_adr + (R/W)bit
////////////////////////////////////////////////////////////////
bool i2c_trx(uint8_t i2c_adr, void* buf_adr, uint8_t length){
    if (!ready) return false;   ready = false;
    result = TWIM_RESULT_UNKNOWN;
    p_now = buf_adr;
    p_end = buf_adr + length;
    TWI0.MADDR = i2c_adr;
    return true;
}

////////////////////////////////////////////////////////////////
// I2C Interrupt
////////////////////////////////////////////////////////////////
ISR(TWI0_TWIM_vect){
    uint8_t currentStatus = TWI0.MSTATUS;
    uint8_t *pn = (uint8_t*)p_now;

    // If arbitration lost
    if (currentStatus & TWI_ARBLOST_bm) {
        TWI0.MSTATUS |= TWI_ARBLOST_bm | TWI_BUSERR_bm | TWI_WIF_bm;
        result = TWIM_RESULT_ARBITRATION_LOST;
        ready = true;
    }

    // If  bus error
    if (currentStatus & TWI_BUSERR_bm) {
        TWI0.MSTATUS |= TWI_ARBLOST_bm | TWI_BUSERR_bm | TWI_WIF_bm;
        result = TWIM_RESULT_BUS_ERROR;
        ready = true;
    }

    // If master write
    else if (currentStatus & TWI_WIF_bm) {
        // Is NAK
        if (currentStatus & TWI_RXACK_bm) {
            TWI0.MCTRLB = TWI_MCMD_STOP_gc;
            result = TWIM_RESULT_NACK_RECEIVED;
            ready = true;
        }

        // Send next
        else if (pn < p_end) {
            TWI0.MDATA = *pn;
            p_now = ++pn;
        }

        // Send completely
        else {
            TWI0.MCTRLB = TWI_MCMD_STOP_gc;
            result = TWIM_RESULT_OK;
            ready = true;
        }
    }

    // If master read
    else if (currentStatus & TWI_RIF_bm) {
        // Is NAK
        if (currentStatus & TWI_RXACK_bm) {
            TWI0.MCTRLB = TWI_MCMD_STOP_gc;
            result = TWIM_RESULT_NACK_RECEIVED;
            ready = true;
        }

        // Read data
        *pn = TWI0.MDATA;
        p_now = ++pn;

        // Receive next
        if (pn < p_end) {
            TWI0.MCTRLB = TWI_MCMD_RECVTRANS_gc;
        }

        // Receive completely
        else {
            TWI0.MCTRLB =   TWI_ACKACT_bm | TWI_MCMD_STOP_gc;
            result = TWIM_RESULT_OK;
            ready = true;
        }
    }

    /* If unexpected state */
    else {
        result = TWIM_RESULT_FAIL;
        ready = true;
    }
}

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you so much, this helped a ton!

Pete