This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

SMBus Util on Tiva TM4C123

The Tiva "Utils" directory includes what appears to be a very nice SMBus interface that sits on top of I2C.

I don't understand where to hook in SMBusMasterIntProcess.  The documentation says it is called from the ISR - but which interrupt?    A little example would be great.

  • Hello Dan,

    The function must be called from the corresponding I2C Interrupt routine.

    Regards
    Amit
  • Thank you.  As I looked at the code and re-read the documentation I figured this out.

    I have SMBusMasterBlockRead working.  It does a proper turn-around and reads the correct data.

    However, I am having problems with SMBusMasterByteWordRead.  This seems like it should be very simple.    I put a wrapper around ByteWordRead call to wait for the MasterBusIntProcess to signal completion, just as I did for BlockRead.  The address is written, but the line does not turn around.

    tSMBusStatus SMBDriver::ReadByte( int DevAddr, char Reg, char* value)
    {
    xSemaphoreTake ( ChannelMutex,portMAX_DELAY ); // Lock out other threads.

    char buff [40];

    SMBStatus = SMBusMasterByteWordRead
    (&Context,
    DevAddr,
    (uint8_t const) Reg,
    (uint8_t *) buff,
    1
    );

    if ( SMBStatus == SMBUS_OK )
    {
    // Wait for completion signal from SMBusMasterIntProcess
    xSemaphoreTake( IODoneSignal, portMAX_DELAY );
    *value = buff[0];
    }
    #ifdef DEBUG
    vTaskDelay( 1 );
    #endif
    xSemaphoreGive( ChannelMutex ); // Allow other threads to use device

    return SMBStatus;
    }

    CPU is TM4C123 launchpad.

    Suggestions are appreciated.

  • Hello Dan,

    In receive mode the last bit must be NAKed by the Master to indicate that the transfer is complete. However (thanks to the waveform) the last bit is ACKed and the master expects more data to be read. Now the ACK bit is in control of the Master and it should have released it. That will depend on what has been passed to "context" structure.

    Regards
    Amit
  • Thank you very much for your help.

    I tried ReadBlock again - it generally behaves as I would expect.  Here is the trace:

    It properly turns the line around.  

    I added a queue to record states as it goes through the SMBMasterIntProcess.  I see that it passes through SMBUS_OK 7 times.  That kind of makes sense - one for each data byte moved.

    I am confused as to how to tell when the transaction is complete.  Can I get that from the SMBMasterintProcess?  Or do I have to loop on SMBusStatusGet?

  • Hello Dan,

    You can use either of the two. I would prefer the SMBMastIntProcess callback

    Regards
    Amit
  • Thank you

    With your help, I worked through the various issues and have reliable data transfer.

    I hope that this will help someone else, I have my example code listed below.  

    /*** ReadByte

        @param [in] DevAddr = Address of chip on I2C buss

        @param [in] Reg = the command byte

        @param [in] val = byte read

    */

           

    tSMBusStatus SMBDriver::ReadByte( int DevAddr, char Reg, char* value)

    {

        xSemaphoreTake ( ChannelMutex,portMAX_DELAY );  // Lock out other threads.

        xSemaphoreTake( IODoneSignal, 0 );              // Make sure signal clear

       

        SMBStatus = SMBusMasterByteWordRead

                       (&Context,

                        DevAddr,

                        (uint8_t const) Reg,

                        (uint8_t *) value,

                        1

                       );

            

        if ( SMBStatus == SMBUS_OK )

        {

            // Wait for completion signal from SMBusMasterIntProcess

            xSemaphoreTake( IODoneSignal, portMAX_DELAY );

        }

    #ifdef DEBUG

        vTaskDelay( 1 );

    #endif

       xSemaphoreGive( ChannelMutex );         // Allow other threads to use device

       

        return SMBStatus;  

    }

     

     

     

     

     

    /** SMBIntHandler

        Called from I2CxHandler ISR

        uses FreeRtos signal

        transfer the state to class object,

        wait for transition of SMBusStatus from TRANSFER_IN_PROGRESS to any

        other state ( COMPLETE or errors );

     

        @param [in] pSMB = pointer to SMB object, which contains tSMBus Context

                           and other encapsulated objects

        @return nothing

    */

     

     void SMBIntHandler(SMBDriver* pSMB)

    {

        tSMBusStatus eStatus;

        portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

     

        //

        // Process the interrupt.

        //

        eStatus = SMBusMasterIntProcess(&pSMB->Context);

        pSMB->SMBStatus = eStatus;

     

        // Signal client if any status other than Transfer in Process.  

        tSMBusStatus State = SMBusStatusGet( &pSMB->Context);

        if ( SMBUS_TRANSFER_IN_PROGRESS != State )

        {

            xSemaphoreGiveFromISR( pSMB->IODoneSignal, &xHigherPriorityTaskWoken );

            //

            // If a higher priority task was waiting for this semaphore then this

            // call will make sure it runs immediately after the ISR returns.

            //

            if( xHigherPriorityTaskWoken == pdTRUE)

            {

                portYIELD_FROM_ISR(true);

            }    

        }

    }

     

  • Hello Dan,

    Appreciate the code post, Thanks

    Regards
    Amit
  • Is it possible to support both Master and Slave activity on the same bus?
    Would I create two tSMBus structures? Or simply add the slave controls to the interrupt handler?
  • Hello Dan,

    You would need to have different Structures as the Master and Slave since the Slave can be on another instance.

    Regards
    Amit
  • Thanks. So would I call both MasterIntProcess and SlaveIntProcess - each with their own structure - from the single I2CxInterruptHandler?
  • Hello Dan,

    Yes. You would need to check for the source of interrupt by querying the InterruptStatus for Master and Slave instance. To simplify, you can use another I2C Slave Instance so that the query mechanism does not cause the Interrupt routine to be large to cause processor to spend more time in.

    Regards
    Amit