How to handle read data with MCC generated i2c driver?

1.5k views Asked by At

I'm trying to use new (?) i2c driver for PIC18F45Q10. I don't understand how can I use it without modification. I just want to set my own function with state machine as callback to process incoming bytes.

I'm sending 2 bytes from Linux machine to microcontroller.

i2cset 1 0x12 0x01 0x02

Where:

  • 1 is Linux I2C bus number
  • 0x12 is 7-bit i2c address (my microcontroller)
  • 0x01 is first byte of data
  • 0x02 is second byte of data

My problem is that I2C2_SlaveRdCallBack(); is never being called (because rx buffer is always empty).

I2C configuration in MCC seems to be fine, because when I put a breakpoint at if(I2C2_SlaveIsRxBufFull()) line - it stops there and I see that i2c2RdData variable contains first byte of data. Linux machine can discover my microcontroller with i2cdetect command (i2cdetect sends all possible device addresses and when it receives ACK - it shows device as discovered).

How to use this driver? Is this code supposed to be working, or just some scaffolding / sample?

Main ISR handler from this driver:

static void I2C2_Isr() 
{ 
    I2C2_SlaveClearIrq();

    // read SSPBUF to clear BF
    i2c2RdData = I2C2_SlaveGetRxData(); //  <- but there is my first ! (it reads hardware register)

    if(I2C2_SlaveIsRead())
    {
        i2c2State = I2C2_TX;
    }
    else
    {
        i2c2State = I2C2_RX;
    }
    
    switch(i2c2State)
    {
        case I2C2_TX:
            if(!I2C2_SlaveIsWriteCollision())
            {
                I2C2_SlaveWrCallBack();
            }
            else
            {
                I2C2_SlaveWrColCallBack();
                I2C2_SlaveRestart();
            }
            i2c2NextState = I2C2_ADDR;
            break;
           
        case I2C2_RX:
            if (I2C2_SlaveIsData())              <--    I have problem here
            {
                if(I2C2_SlaveIsRxBufFull())      <--    when the buffer is not full 
                { 
                    I2C2_SlaveRdCallBack();      <--    read callback is not called
                }
            }
            else
            {
                I2C2_SlaveAddrCallBack();       
                i2c2NextState = I2C2_ADDR;
            }
            break;
        default:          
            break;
    }
    i2c2State = i2c2NextState;
    I2C2_SlaveReleaseClock();
}

Full driver code:

/**
  I2C2 Generated Driver File

  @Company
    Microchip Technology Inc.

  @File Name
    i2c2.c

  @Summary
    This is the generated driver implementation file for the I2C2 driver using PIC10 / PIC12 / PIC16 / PIC18 MCUs

  @Description
    This header file provides implementations for driver APIs for I2C2.
    Generation Information :
        Product Revision  :  PIC10 / PIC12 / PIC16 / PIC18 MCUs - 1.78.1
        Device            :  PIC18F45Q10
        Driver Version    :  1.0.0
    The generated drivers are tested against the following:
        Compiler          :  XC8 2.10 and above or later
        MPLAB             :  MPLAB X 5.30
*/

/*
    (c) 2018 Microchip Technology Inc. and its subsidiaries. 
    
    Subject to your compliance with these terms, you may use Microchip software and any 
    derivatives exclusively with Microchip products. It is your responsibility to comply with third party 
    license terms applicable to your use of third party software (including open source software) that 
    may accompany Microchip software.
    
    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER 
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY 
    IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS 
    FOR A PARTICULAR PURPOSE.
    
    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, 
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND 
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP 
    HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO 
    THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL 
    CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT 
    OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS 
    SOFTWARE.
*/

#include "i2c2_slave.h"
#include <xc.h>

#define I2C2_SLAVE_ADDRESS      18
#define I2C2_SLAVE_MASK         127

/**
 Section: Global Variables
 */
typedef enum 
{
    I2C2_ADDR, 
    I2C2_TX, 
    I2C2_RX
} i2c2_state_t;

static void I2C2_Isr(void);
static void I2C2_SlaveDefRdInterruptHandler(void);
static void I2C2_SlaveDefWrInterruptHandler(void);
static void I2C2_SlaveDefAddrInterruptHandler(void);
static void I2C2_SlaveDefWrColInterruptHandler(void);
static void I2C2_SlaveDefBusColInterruptHandler(void);

static void I2C2_SlaveRdCallBack(void);
static void I2C2_SlaveWrCallBack(void);
static void I2C2_SlaveAddrCallBack(void);
static void I2C2_SlaveWrColCallBack(void);
static void I2C2_SlaveBusColCallBack(void);

static inline bool I2C2_SlaveOpen();
static inline void I2C2_SlaveClose();
static inline void I2C2_SlaveSetSlaveAddr(uint8_t slaveAddr);
static inline void I2C2_SlaveSetSlaveMask(uint8_t maskAddr);
static inline void I2C2_SlaveEnableIrq(void);
static inline bool I2C2_SlaveIsAddr(void);
static inline bool I2C2_SlaveIsRead(void);
static inline void I2C2_SlaveClearBuff(void);
static inline void I2C2_SlaveClearIrq(void);
static inline void I2C2_SlaveReleaseClock(void);
static inline bool I2C2_SlaveIsWriteCollision(void);
static inline bool I2C2_SlaveIsTxBufEmpty(void);
static inline bool I2C2_SlaveIsData(void);
static inline void I2C2_SlaveRestart(void);
static inline bool I2C2_SlaveIsRxBufFull(void);
static inline void I2C2_SlaveSendTxData(uint8_t data);
static inline uint8_t I2C2_SlaveGetRxData(void);
static inline uint8_t I2C2_SlaveGetAddr(void);
static inline void I2C2_SlaveSendAck(void);
static inline void I2C2_SlaveSendNack(void);


static volatile i2c2_state_t i2c2State = I2C2_ADDR;
static volatile i2c2_state_t i2c2NextState = I2C2_ADDR;
volatile uint8_t i2c2WrData;
volatile uint8_t i2c2RdData;
volatile uint8_t i2c2SlaveAddr;

void I2C2_Initialize()
{
    SSP2STAT  = 0x40;
    SSP2CON1 |= 0x06;
    SSP2CON2  = 0x10;
    SSP2CON1bits.SSPEN = 0;
}

void I2C2_Open() 
{
    I2C2_SlaveOpen();
    I2C2_SlaveSetSlaveAddr(I2C2_SLAVE_ADDRESS << 1);
    I2C2_SlaveSetSlaveMask(I2C2_SLAVE_MASK);
    I2C2_SlaveSetIsrHandler(I2C2_Isr);
    I2C2_SlaveSetBusColIntHandler(I2C2_SlaveDefBusColInterruptHandler);
    I2C2_SlaveSetWriteIntHandler(I2C2_SlaveDefWrInterruptHandler);
    I2C2_SlaveSetReadIntHandler(I2C2_SlaveDefRdInterruptHandler);
    I2C2_SlaveSetAddrIntHandler(I2C2_SlaveDefAddrInterruptHandler);
    I2C2_SlaveSetWrColIntHandler(I2C2_SlaveDefWrColInterruptHandler);
    I2C2_SlaveEnableIrq();    
}

void I2C2_Close() 
{
    I2C2_SlaveClose();
}

uint8_t I2C2_Read()
{
   return I2C2_SlaveGetRxData();
}

void I2C2_Write(uint8_t data)
{
    I2C2_SlaveSendTxData(data);
}

void I2C2_Enable()
{
    I2C2_Initialize();
}

void I2C2_SendAck()
{
    I2C2_SlaveSendAck();
}

void I2C2_SendNack()
{
    I2C2_SlaveSendNack();
}

static void I2C2_Isr() 
{ 
    I2C2_SlaveClearIrq();

    // read SSPBUF to clear BF
    i2c2RdData = I2C2_SlaveGetRxData();

    if(I2C2_SlaveIsRead())
    {
        i2c2State = I2C2_TX;
    }
    else
    {
        i2c2State = I2C2_RX;
    }
    
    switch(i2c2State)
    {
        case I2C2_TX:
            if(!I2C2_SlaveIsWriteCollision())
            {
                I2C2_SlaveWrCallBack();
            }
            else
            {
                I2C2_SlaveWrColCallBack();
                I2C2_SlaveRestart();
            }
            i2c2NextState = I2C2_ADDR;
            break;
           
        case I2C2_RX:
            if (I2C2_SlaveIsData()) 
            {
                if(I2C2_SlaveIsRxBufFull())
                { 
                    I2C2_SlaveRdCallBack();
                }
            }
            else
            {
                I2C2_SlaveAddrCallBack();
                i2c2NextState = I2C2_ADDR;
            }
            break;
        default:          
            break;
    }
    i2c2State = i2c2NextState;
    I2C2_SlaveReleaseClock();
}

// Common Event Interrupt Handlers
void I2C2_SlaveSetIsrHandler(interruptHandler handler)
{
    MSSP2_InterruptHandler = handler;
}

// Read Event Interrupt Handlers
void I2C2_SlaveSetReadIntHandler(interruptHandler handler) {
    I2C2_SlaveRdInterruptHandler = handler;
}

static void I2C2_SlaveRdCallBack() {
    // Add your custom callback code here
    if (I2C2_SlaveRdInterruptHandler) 
    {
        I2C2_SlaveRdInterruptHandler();
    }
}

static void I2C2_SlaveDefRdInterruptHandler() {
    i2c2RdData = I2C2_SlaveGetRxData();
}

// Write Event Interrupt Handlers
void I2C2_SlaveSetWriteIntHandler(interruptHandler handler) {
    I2C2_SlaveWrInterruptHandler = handler;
}

static void I2C2_SlaveWrCallBack() {
    // Add your custom callback code here
    if (I2C2_SlaveWrInterruptHandler) 
    {
        I2C2_SlaveWrInterruptHandler();
    }
}

static void I2C2_SlaveDefWrInterruptHandler() {
    I2C2_SlaveSendTxData(i2c2WrData);
}

// ADDRESS Event Interrupt Handlers
void I2C2_SlaveSetAddrIntHandler(interruptHandler handler){
    I2C2_SlaveAddrInterruptHandler = handler;
}

static void I2C2_SlaveAddrCallBack() {
    // Add your custom callback code here
    if (I2C2_SlaveAddrInterruptHandler) {
        I2C2_SlaveAddrInterruptHandler();
    }
}

static void I2C2_SlaveDefAddrInterruptHandler() {
    i2c2SlaveAddr = I2C2_SlaveGetAddr();
}

// Write Collision Event Interrupt Handlers
void I2C2_SlaveSetWrColIntHandler(interruptHandler handler){
    I2C2_SlaveWrColInterruptHandler = handler;
}

static void  I2C2_SlaveWrColCallBack() {
    // Add your custom callback code here
    if ( I2C2_SlaveWrColInterruptHandler) 
    {
         I2C2_SlaveWrColInterruptHandler();
    }
}

static void I2C2_SlaveDefWrColInterruptHandler() {
}

// Bus Collision Event Interrupt Handlers
void I2C2_SlaveSetBusColIntHandler(interruptHandler handler){
    I2C2_SlaveBusColInterruptHandler = handler;
}

static void  I2C2_SlaveBusColCallBack() {
    // Add your custom callback code here
    if ( I2C2_SlaveBusColInterruptHandler) 
    {
         I2C2_SlaveBusColInterruptHandler();
    }
}

static void I2C2_SlaveDefBusColInterruptHandler() {
}

static inline bool I2C2_SlaveOpen()
{
    if(!SSP2CON1bits.SSPEN)
    {      
        SSP2STAT  = 0x40;
        SSP2CON1 |= 0x06;
        SSP2CON2  = 0x10;
        SSP2CON1bits.SSPEN = 1;
        return true;
    }
    return false;
}

static inline void I2C2_SlaveClose()
{
    SSP2STAT  = 0x40;
    SSP2CON1 |= 0x06;
    SSP2CON2  = 0x10;
    SSP2CON1bits.SSPEN = 0;
}

static inline void I2C2_SlaveSetSlaveAddr(uint8_t slaveAddr)
{
    SSP2ADD = slaveAddr;
}

static inline void I2C2_SlaveSetSlaveMask(uint8_t maskAddr)
{
    SSP2MSK = maskAddr;
}

static inline void I2C2_SlaveEnableIrq()
{
    PIE3bits.SSP2IE = 1;
}

static inline bool I2C2_SlaveIsAddr()
{
    return !(SSP2STATbits.D_nA);
}

static inline bool I2C2_SlaveIsRead()
{
    return (SSP2STATbits.R_nW);
}

static inline void I2C2_SlaveClearIrq()
{
    PIR3bits.SSP2IF = 0;
}

static inline void I2C2_SlaveReleaseClock()
{
    SSP2CON1bits.CKP = 1;
}

static inline bool I2C2_SlaveIsWriteCollision()
{
    return SSP2CON1bits.WCOL;
}

static inline bool I2C2_SlaveIsData()
{
    return SSP2STATbits.D_nA;
}

static inline void I2C2_SlaveRestart(void)
{
    SSP2CON2bits.RSEN = 1;
}

static inline bool I2C2_SlaveIsTxBufEmpty()
{
    return !SSP2STATbits.BF;
}

static inline bool I2C2_SlaveIsRxBufFull()
{
    return SSP2STATbits.BF;
}

static inline void I2C2_SlaveSendTxData(uint8_t data)
{
    SSP2BUF = data;
}

static inline uint8_t I2C2_SlaveGetRxData()
{
    return SSP2BUF;
}

static inline uint8_t I2C2_SlaveGetAddr()
{
    return SSP2ADD;
}

static inline void I2C2_SlaveSendAck()
{
    SSP2CON2bits.ACKDT = 0;
    SSP2CON2bits.ACKEN = 1;
}

static inline void I2C2_SlaveSendNack()
{
    SSP2CON2bits.ACKDT = 1;
    SSP2CON2bits.ACKEN = 1;
}

Header file:

/**
  I2C2 Generated Driver API Header File

  @Company
    Microchip Technology Inc.

  @File Name
    i2c2_slave.h

  @Summary
    This is the generated header file for the I2C2 driver using PIC10 / PIC12 / PIC16 / PIC18 MCUs

  @Description
    This header file provides APIs for driver for I2C2.
    Generation Information :
        Product Revision  :  PIC10 / PIC12 / PIC16 / PIC18 MCUs - 1.78.1
        Device            :  PIC18F45Q10
        Driver Version    :  1.0.0
    The generated drivers are tested against the following:
        Compiler          :  XC8 2.10 and above or later
        MPLAB             :  MPLAB X 5.30
*/

/*
    (c) 2018 Microchip Technology Inc. and its subsidiaries. 
    
    Subject to your compliance with these terms, you may use Microchip software and any 
    derivatives exclusively with Microchip products. It is your responsibility to comply with third party 
    license terms applicable to your use of third party software (including open source software) that 
    may accompany Microchip software.
    
    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER 
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY 
    IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS 
    FOR A PARTICULAR PURPOSE.
    
    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, 
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND 
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP 
    HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO 
    THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL 
    CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT 
    OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS 
    SOFTWARE.
*/

#ifndef I2C2_SLAVE_H
#define I2C2_SLAVE_H

#include <stdbool.h>
#include <stdint.h>

typedef void (*interruptHandler)(void);
/**
 * \brief Initialize I2C2 interface
 * If module is configured to disabled state, the clock to the I2C2 is disabled
 * if this is supported by the device's clock system.
 *
 * \return None
 */
void I2C2_Initialize(void);

/**
 * \brief Open the I2C2 for communication. Enables the module if disabled.
 *
 * \return Nothing
 */
void I2C2_Open(void);

/**
 * \brief Close the I2C2 for communication. Disables the module if enabled.
 * Disables address recognition.
 *
 * \return Nothing
 */
void I2C2_Close(void);

/**
 * \brief Read data from I2C2 communication. 
 *
 * \return Read Data
 */
uint8_t I2C2_Read(void);

/**
 * \brief Write data over the communication. 
 *
 * \return None
 */
void I2C2_Write(uint8_t data);

/**
 * \brief Enable the communication by initialization of hardware 
 *
 * \return None
 */
void I2C2_Enable(void);

/**
 * \brief Send the Ack Signal to Master 
 *
 * \return None
 */
void I2C2_SendAck(void);

/**
 * \brief Send the Nack Signal to Master 
 *
 * \return None
 */
void I2C2_SendNack(void);

/**
 * \brief The function called by the I2C2 Irq handler.
 * Can be called in a polling loop in a polled driver.
 *
 * \return Nothing
 */
void I2C2_SlaveSetIsrHandler(interruptHandler handler);
void I2C2_SlaveSetAddrIntHandler(interruptHandler handler);
void I2C2_SlaveSetReadIntHandler(interruptHandler handler);
void I2C2_SlaveSetWriteIntHandler(interruptHandler handler);
void I2C2_SlaveSetBusColIntHandler(interruptHandler handler);
void I2C2_SlaveSetWrColIntHandler(interruptHandler handler);

void (*MSSP2_InterruptHandler)(void);
void (*I2C2_SlaveRdInterruptHandler)(void);
void (*I2C2_SlaveWrInterruptHandler)(void);
void (*I2C2_SlaveAddrInterruptHandler)(void);
void (*I2C2_SlaveBusColInterruptHandler)(void);
void (*I2C2_SlaveWrColInterruptHandler)(void); 

#endif /* I2C2_SLAVE_H */
1

There are 1 answers

0
cuong hoang On

You shoud not do this line in "I2C2_Isr()"

  i2c2RdData = I2C2_SlaveGetRxData(); //   <- but there is my first ! 
                                        (it reads hardware register)

It will copy data from hardware register then this register will be empty. So that why your " I2C2_SlaveRdCallBack();" is never be called.

This default handle will be auto call.

static void I2C2_SlaveDefRdInterruptHandler() {
i2c2RdData = I2C2_SlaveGetRxData();
} 

So if you want to get data then copy to your variable, you should be do here

static void I2C2_SlaveRdCallBack() {
// Add your custom callback code here
if (I2C2_SlaveRdInterruptHandler) 
{
    I2C2_SlaveRdInterruptHandler();
}

// Copy i2c data to your variable here
}

Sorry for my poor English