• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Can't get I2C slave to work well woth pic24 and MplabX

Status
Not open for further replies.

SUPER COPTER

New Member
Hi, i'm trying to implement an I2C slave on a pic24 and i'm able toget some communication but cant get usable data out of it.

I've tried 2 ways:

1.-Generating I2C slave code with MCC: I used the generated MCC which emulates an I2C EEPROM. I'm able to write the simulated EEPROM Memory and read it back with an Arduino. I have 2 problems with this method. First, i cant get the data reliably from the EEPROM buffer (64 byte array). there seems to be an I2C pointer set function for taking out the data from the array. I used this function to get the data and print it over serial, but when i try to process the data (compare bytes and that kinf of stuff this doesn´t work as expected. I think i might be doing stuff that interrputs the I2C transacion maybe, but i'm not able to (again) get the status back from the funcion. This variable are defined as "static" so i'm guessing they are expected to be used outside the functions in the user code.

The second problem is probably related with the first one (I don't know how to use the given code), when i get to print the data sent by the master to the UART, it prints once, or sometimes twice... then it prints garbage, sometimes it prints again part of the message and finally (sometimes) i read back the memory (with the arduino) and it is blank (maybe it's a problem with the arduino?).

this is my main code:

Code:
#include "mcc_generated_files/system.h"

#define DELAY1 1995
#define DELAY2 5

int main(void)
{
    SYSTEM_Initialize();
    T1CON = 0x8030; //inicializa timer1 (deberia ir en su propia lib))

    while (1)
    {
        LEDBlink();

        uint8_t *tst = I2C1_ReadPointerGet();
        UARTSendString(tst);
        UART3_Write('\n');
        UART3_Write('\r');

       
    }

    return 1;
}


/* Send a string, until '\0' is read, up to max_length characters */
void UARTSendString(const char* str, const uint8_t max_length) {
    int i;
    for (i=0 ; i<max_length && str[i]!='\0' ; i++) {
        UART3_Write(str[i]);
    }
}

void LEDBlink(void){
    LATB = 0xFFFF;
    TMR1 = 0;
    while (TMR1 < DELAY2)
    {
    }
    LATB = 0x0000;
    TMR1 = 0;
    while (TMR1 < DELAY1)
    {
    }
}
This is the I2C.c file generated by MCC:

Code:
/**
  I2C1 Generated Driver File

  @Company
    Microchip Technology Inc.

  @File Name
    i2c1.c

  @Summary
    This is the generated header file for the i2c1 driver using PIC24 / dsPIC33 / PIC32MM MCUs

  @Description
    This header file provides APIs for driver for i2c1.
    Generation Information :
        Product Revision : PIC24 / dsPIC33 / PIC32MM MCUs - 1.75.1
        Device : PIC24FJ256GA110

    The generated drivers are tested against the following:
        Compiler : XC16 v1.35
        MPLAB : MPLAB X v5.05
*/

/*
    (c) 2016 Microchip Technology Inc. and its subsidiaries. You may use this
    software and any derivatives exclusively with Microchip products.

    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, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION
    WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.

    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.

    MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE
    TERMS.
*/

#include "i2c1.h"

/**
 Section: Data Types
*/

/**
  I2C Slave Driver State Enumeration

  @Summary
    Defines the different states of the i2c slave.

  @Description
    This defines the different states that the i2c slave
    used to process transactions on the i2c bus.
*/
typedef enum
{
    S_SLAVE_IDLE,
    S_SLAVE_RECEIVE_MODE,
    S_SLAVE_TRANSMIT_MODE,
    S_SLAVE_LOW_BYTE_ADDRESS_DETECT,

} I2C_SLAVE_STATES;

/**
 Section: Macro Definitions
*/
/* defined for I2C1 */
#define I2C1_TRANSMIT_REG I2C1TRN // Defines the transmit register used to send data.
#define I2C1_RECEIVE_REG I2C1RCV // Defines the receive register used to receive data.

#define I2C1_MASK_REG I2C1MSK // Defines the address mask register.
#define I2C1_ADDRESS_REG I2C1ADD // Defines the address register. 

// The following control bits are used in the I2C state machine to manage
// the I2C module and determine next states.
#define I2C1_GENERAL_CALL_ENABLE_BIT I2C1CONbits.GCEN // I2C General Call enable control bit.
#define I2C1_10_BIT_ADDRESS_ENABLE_BIT I2C1CONbits.A10M // I2C Address Mode (7 or 10 bit address) control bit.
#define I2C1_RELEASE_SCL_CLOCK_CONTROL_BIT I2C1CONbits.SCLREL // I2C clock stretch/release control bit.

// The following status bits are used in the I2C state machine to determine
// the next states.

#define I2C1_READ_NOT_WRITE_STATUS_BIT I2C1STATbits.R_W // I2C current transaction read/write status bit.
#define I2C1_DATA_NOT_ADDRESS_STATUS_BIT I2C1STATbits.D_A // I2C last byte receive was data/address status bit.
#define I2C1_RECEIVE_OVERFLOW_STATUS_BIT I2C1STATbits.I2COV // I2C receive buffer overflow status bit.
#define I2C1_GENERAL_CALL_ADDRESS_STATUS_BIT I2C1STATbits.GCSTAT // I2C General Call status bit.
#define I2C1_ACKNOWLEDGE_STATUS_BIT I2C1STATbits.ACKSTAT // I2C ACK status bit.

/**
 Section: Local Functions
*/

inline void __attribute__ ((always_inline)) I2C1_TransmitProcess(void);
inline void __attribute__ ((always_inline)) I2C1_ReceiveProcess(void);

/**
 Section: Local Variables
*/

static I2C_SLAVE_STATES i2c1_slave_state;
static uint8_t *p_i2c1_write_pointer;
static uint8_t *p_i2c1_read_pointer;

/**
  Prototype: void I2C1_Initialize(void)
  Input: none
  Output: none
  Description: I2C1_Initialize is an
                    initialization routine that takes inputs from the GUI.
  Comment: 
  Usage: I2C1_Initialize();
*/
void I2C1_Initialize(void)
{

    // initialize the hardware
    // ACKEN disabled; STREN disabled; GCEN disabled; SMEN disabled; DISSLW enabled; I2CSIDL disabled; ACKDT Sends ACK; SCLREL Holds; RSEN disabled; IPMIEN disabled; A10M 7 Bit; PEN disabled; RCEN disabled; SEN disabled; I2CEN enabled; 
    I2C1CON = 0x8000;
    // P disabled; S disabled; I2COV disabled; IWCOL disabled; 
    I2C1STAT = 0x00;
    // I2CADD 102; 
    I2C1_SlaveAddressSet(0x66);
    // AMSK 0; 
    I2C1_SlaveAddressMaskSet(0x00);

    // make sure this is set first
    i2c1_slave_state = S_SLAVE_IDLE;
    
    I2C1_ReadPointerSet(NULL);
    I2C1_WritePointerSet(NULL);
    
    /* SI2C1 - I2C1 Slave Events */
    // clear the master interrupt flag
    IFS1bits.SI2C1IF = 0;
    // enable the master interrupt
    IEC1bits.SI2C1IE = 1;
    
}


void __attribute__ ( ( interrupt, no_auto_psv ) ) _SI2C1Interrupt ( void )
{

    static bool prior_address_match = false;
    static bool not_busy = true;
    uint8_t dummy;

    // NOTE: The slave driver will always acknowledge 
    // any address match.

    switch (i2c1_slave_state)
    {
        case S_SLAVE_IDLE:
        case S_SLAVE_RECEIVE_MODE:

            /* When at S_SLAVE_RECEIVE_MODE this mode there
               will be two types of incoming transactions:
               1. Data sent by master
               2. A restart or start detection

               But from the point of view of the firmware, there is
               no difference between S_SLAVE_IDLE and S_SLAVE_RECEIVE_MODE
               states, since the types of incoming transactions will be
               the same so we share the code here.
             */

            if (
                    // case of 7-bit address detected
                    ( (I2C1_10_BIT_ADDRESS_ENABLE_BIT == 0) &&
                        (I2C1_DATA_NOT_ADDRESS_STATUS_BIT == 0)
                    )
                    ||
                    // case of general address call detected
                    ( (I2C1_GENERAL_CALL_ENABLE_BIT == 1) &&
                        (I2C1_GENERAL_CALL_ADDRESS_STATUS_BIT == 1)
                    )
                )
            {
                            
                if (I2C1_READ_NOT_WRITE_STATUS_BIT == 0)
                {
                    // it is a write, go to receive mode 

                    not_busy = I2C1_StatusCallback(I2C1_SLAVE_RECEIVE_REQUEST_DETECTED);
                    i2c1_slave_state = S_SLAVE_RECEIVE_MODE;

                    if (not_busy == true)
                    {
                        // read the receive register only when
                        // we are ready for the next transaction.
                        // this one is a dummy read
                        dummy = I2C1_RECEIVE_REG;
                    }
                }
                else
                {
                    // it is a read, go to transmit mode 

                    I2C1_StatusCallback(I2C1_SLAVE_TRANSMIT_REQUEST_DETECTED);

                    // during this portion, the master is expecting the
                    // slave for a reply. So the returned status of
                    // the callback at this point cannot be used to 
                    // delay the reply if needed.
                    // In other words, the slave has to reply to the master.
                    // Therefore, the transmit will be performed.
                    
                    I2C1_TransmitProcess();
                    i2c1_slave_state = S_SLAVE_TRANSMIT_MODE;
                }

            }

            else if
               (
                    // case of 10-bit high address detected
                    ( (I2C1_10_BIT_ADDRESS_ENABLE_BIT == 1) &&
                        (I2C1_DATA_NOT_ADDRESS_STATUS_BIT == 0)
                    )
               )
            {
                if (I2C1_READ_NOT_WRITE_STATUS_BIT == 0)
                {
                    // it is the detection of high byte address of 
                    // 10-bit address, go to detection of low byte address
                    prior_address_match = false;
                    i2c1_slave_state = S_SLAVE_LOW_BYTE_ADDRESS_DETECT;

                }
                else // if (I2C1_READ_NOT_WRITE_STATUS_BIT == 1)
                {
                    if (prior_address_match == true)
                    {
                        // it is the detection of high byte
                        // address of 10-bit address, but the next
                        // transaction is read transaction (so it
                        // is a restart).
                        // set the transmit register with the data
                        // to transmit then go to transmit mode

                        I2C1_StatusCallback(I2C1_SLAVE_TRANSMIT_REQUEST_DETECTED);

                        // during this portion, the master is expecting the
                        // slave for a reply. So the returned status of
                        // the callback at this point cannot be used to
                        // delay the reply if needed.
                        // In other words, the slave has to reply to the master.
                        // Therefore, the transmit will be performed.

                        I2C1_TransmitProcess();
                        i2c1_slave_state = S_SLAVE_TRANSMIT_MODE;
                    }
                    else
                    {
                        // it is the detection of high byte address of
                        // 10-bit address, but next transaction is a write.
                        // go to detection of low byte address
                        prior_address_match = false;
                        i2c1_slave_state = S_SLAVE_LOW_BYTE_ADDRESS_DETECT;

                    }
                }

                // dummy read is needed
                dummy = I2C1_RECEIVE_REG;
            }

            // this if statement is to make sure we only save incoming
            // data when we are truly in receiving mode
            if (i2c1_slave_state == S_SLAVE_RECEIVE_MODE)
            {
                // case of data received
                if (I2C1_DATA_NOT_ADDRESS_STATUS_BIT == 1)
                {
                    // check if we are overflowing the receive buffer
                    if (I2C1_RECEIVE_OVERFLOW_STATUS_BIT != 1)
                    {
                        I2C1_ReceiveProcess();
                        not_busy = I2C1_StatusCallback(I2C1_SLAVE_RECEIVED_DATA_DETECTED);
                    }
                    else
                    {
                        // overflow detected!
                        // read the buffer to reset the buffer full flag
                        // and clear the overflow bit
                        // then do nothing so the master
                        // will resend the data
                        dummy = I2C1_RECEIVE_REG;
                        I2C1_RECEIVE_OVERFLOW_STATUS_BIT = 0;
                    }
                }
            }

            break;

        case S_SLAVE_LOW_BYTE_ADDRESS_DETECT:
            // Note that this state will only get
            // executed when 10-bit address is set

            // we send receive request but we do not actually know
            // if the next one is a data from master since the 
            // next one can be a restart with a transmit request.
            // When that happens, the next state will take care of it.
            // This is just the nature of i2c bus protocol.
            not_busy = I2C1_StatusCallback(I2C1_SLAVE_10BIT_RECEIVE_REQUEST_DETECTED);

            // set this flag to indicate we have
            // full 10-bit address detection
            prior_address_match = true;

            if (not_busy)
            {
                // dummy read is needed
                dummy = I2C1_RECEIVE_REG;
            }

            i2c1_slave_state = S_SLAVE_RECEIVE_MODE;

            break;

        case S_SLAVE_TRANSMIT_MODE:

            // this is the state where an ACK or NACK is expected
            // to occur after the slave has placed data to the
            // transmit register.

            // if the transaction was ACK'ed, more data needs to be sent
            // if the transaction was NACK'ed then we don't need to send
            // more data
            if (I2C1_ACKNOWLEDGE_STATUS_BIT == 0)
            {
                // prepare next data
                I2C1_StatusCallback(I2C1_SLAVE_TRANSMIT_REQUEST_DETECTED);

                // transmit more data
                I2C1_TransmitProcess();
                
            }
            else //if (I2C1_ACKNOWLEDGE_STATUS_BIT == 1)
            {
                // no more data to be sent so we go to idle state
                i2c1_slave_state = S_SLAVE_IDLE;
            }
            break;


        default:
            // should never happen, if we ever get here stay here forever
            while(1);
            break;
    }


    // clear the slave interrupt flag
    IFS1bits.SI2C1IF = 0;

}

void I2C1_ReadPointerSet(uint8_t *p)
{
    p_i2c1_read_pointer = p;
}

void I2C1_WritePointerSet(uint8_t *p)
{
    p_i2c1_write_pointer = p;
}

uint8_t *I2C1_ReadPointerGet(void)
{
    return (p_i2c1_read_pointer);
}

uint8_t *I2C1_WritePointerGet(void)
{
    return (p_i2c1_write_pointer);
}

void I2C1_SlaveAddressMaskSet(
                                uint16_t mask)
{
    I2C1_MASK_REG = mask;
}

void I2C1_SlaveAddressSet(
                                uint16_t address)
{
    if (address > 0x7F)
    {
        // use 10 bit address
        I2C1_10_BIT_ADDRESS_ENABLE_BIT = true;
    }
    else
    {
        // use 7 bit address
        I2C1_10_BIT_ADDRESS_ENABLE_BIT = false;
    }
    i2c1_slave_state = S_SLAVE_IDLE;
    I2C1_ADDRESS_REG = address;

}

inline void __attribute__ ((always_inline)) I2C1_TransmitProcess(void)
{
    // get the data to be transmitted

    // sanity check (to avoid stress)
    if (p_i2c1_read_pointer == NULL)
        return;

    I2C1_TRANSMIT_REG = *p_i2c1_read_pointer;

    // set the SCL clock to be released
    I2C1_RELEASE_SCL_CLOCK_CONTROL_BIT = 1;

}

inline void __attribute__ ((always_inline)) I2C1_ReceiveProcess(void)
{ 
    // store the received data 
    
    // sanity check (to avoid stress)
    if (p_i2c1_write_pointer == NULL)
        return;

    *p_i2c1_write_pointer = I2C1_RECEIVE_REG;

}

/* Note: This is an example of the I2C1_StatusCallback()
         implementation. This is an emulated EEPROM Memory
         configured to act as a I2C Slave Device.
         For specific slave device implementation, remove
         or modify this function to the specific slave device
         behavior.
*/

static uint8_t i2c1_slaveWriteData = 0xAA;

bool I2C1_StatusCallback(I2C1_SLAVE_DRIVER_STATUS status)
{

    // this emulates the slave device memory where data written to slave
    // is placed and data read from slave is taken
    static uint8_t EMULATE_EEPROM_Memory[64] =
            {
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
            };

    static uint16_t address, addrByteCount;
    static bool addressState = true;

    switch (status)
    {
        case I2C1_SLAVE_TRANSMIT_REQUEST_DETECTED:
            // Receive the data if valid
            I2C1_ReceiveProcess();
            // set up the slave driver buffer transmit pointer
            I2C1_ReadPointerSet(&EMULATE_EEPROM_Memory[address]);
            address++;

            break;

        case I2C1_SLAVE_RECEIVE_REQUEST_DETECTED:

            addrByteCount = 0;
            addressState = true;

            // set up the slave driver buffer receive pointer
            I2C1_WritePointerSet(&i2c1_slaveWriteData);

            break;

        case I2C1_SLAVE_RECEIVED_DATA_DETECTED:

            if (addressState == true)
            {
                // get the address of the memory being written
                if (addrByteCount == 0)
                {
                    address = (i2c1_slaveWriteData << 8) & 0xFF00;
                    addrByteCount++;
                }
                else if (addrByteCount == 1)
                {
                    address = address | i2c1_slaveWriteData;
                    addrByteCount = 0;
                    addressState = false;
                }
            }
            else // if (addressState == false)
            {
                // set the memory with the received data
                EMULATE_EEPROM_Memory[address] = i2c1_slaveWriteData;
                address++;
            }

            break;

        case I2C1_SLAVE_10BIT_RECEIVE_REQUEST_DETECTED:

            // do something here when 10-bit address is detected

            // 10-bit address is detected

            break;

        default:
            break;

    }

    return true;
}
and the .h respective file:

Code:
/**
  I2C1 Generated Driver API Header File

  @Company
    Microchip Technology Inc.

  @File Name
    i2c1.h

  @Summary
    This is the generated header file for the I2C1 driver using PIC24 / dsPIC33 / PIC32MM MCUs

  @Description
    This header file provides APIs for driver for I2C1.
    Generation Information :
        Product Revision : PIC24 / dsPIC33 / PIC32MM MCUs - 1.75.1
        Device : PIC24FJ256GA110

    The generated drivers are tested against the following:
        Compiler : XC16 v1.35
        MPLAB : MPLAB X v5.05
*/

/*
    (c) 2016 Microchip Technology Inc. and its subsidiaries. You may use this
    software and any derivatives exclusively with Microchip products.

    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, OR ITS INTERACTION WITH MICROCHIP PRODUCTS, COMBINATION
    WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.

    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.

    MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE
    TERMS.
*/

#ifndef _I2C1_H
#define _I2C1_H

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

#ifdef __cplusplus // Provide C++ Compatibility

    extern "C" {

#endif


/**
  I2C Slave Driver Status

  @Summary
    Defines the different status that the slave driver has
    detected over the i2c bus.

  @Description
    This defines the different status that the slave driver has
    detected over the i2c bus. The status is passed to the
    I2C1_StatusCallback() callback function that is implemented by
    the user of the slave driver as a parameter to inform the user
    that there was a change in the status of the driver due to
    transactions on the i2c bus. User of the slave driver can use these
    to manage the read or write buffers.

 */

typedef enum
{
    /* This state indicates that the slave driver has detected a transmit request from the master.
       The next transaction will be a read transaction. Application should prime the transmit
       buffer with the valid data to be sent to the master using the: I2C1_ReadPointerSet()
       
     */
    I2C1_SLAVE_TRANSMIT_REQUEST_DETECTED,
            
    /* This state indicates that the slave driver has detected a reception request from the master.
       The next transaction will be a write transaction. Application should set up the receive 
       buffer by setting the location of the receive buffer using the: I2C1_WritePointerSet()
     */
    I2C1_SLAVE_RECEIVE_REQUEST_DETECTED,

    /* This state indicates that the slave driver has received data from the master.
       Application can use this status to process the received data set up the receive
       buffer in anticipation of next reception in case the master sends more data. 
       See I2C1_SLAVE_RECEIVE_REQUEST_DETECTED.
     */
    I2C1_SLAVE_RECEIVED_DATA_DETECTED,
            
    /* This state indicates that the slave driver has detected the most significant
       byte of the 10-bit slave address. Another transaction with the least 
       significant byte of the 10-bit address with a read/write request will
       be detected next. This second detection is automatically done by the 
       driver which may return one of the following:
           a. I2C1_SLAVE_RECEIVE_REQUEST_DETECTED
           b. I2C1_SLAVE_TRANSMIT_REQUEST_DETECTED
           c. I2C1_SLAVE_10BIT_RECEIVE_REQUEST_DETECTED
       a or b is detected depending on the read/write bit of the received 
       control data. If c is detected next, the master decided to resend
       the most significant address. 
     */
    I2C1_SLAVE_10BIT_RECEIVE_REQUEST_DETECTED,

} I2C1_SLAVE_DRIVER_STATUS;

#define I2C1_SLAVE_DEFAULT_ADDRESS 99

/**
    @Summary
        Initializes and enables the i2c slave instance : 1

    @Description
        This routine initializes the i2c slave driver instance for : 1
        index, making it ready for clients to open and use it.

    @Preconditions
        None

    @Param
        None

    @Returns
        None

    @Example
        <code>
            
            uint8_t SlaveReadBuffer;
            uint8_t SlaveWriteBuffer;

            // initialize the i2c slave driver 
            I2C1_Initialize();
    
            // set up the slave driver
 
            // initialize the location of the read buffer
            I2C1_ReadPointerSet(SlaveReadBuffer);
            // initialize the location of the write buffer
            I2C1_WritePointerSet(SlaveWriteBuffer);
  
        </code>
*/

void I2C1_Initialize(void);


/**
    @Summary
        This function sets the slave address mask.

    @Description
        This function sets the 10-bit slave address mask to be able to
        respond to multiple addresses. This function should be called
        after the initialization of the module.

    @Preconditions
        None

    @Param
        mask - The address mask to be used when filtering
            addresses from the i2c master transactions.

    @Returns
        None

    @Example
        <code>
            Refer to I2C1_SlaveAddressSet() for an example 
        </code>

*/

void I2C1_SlaveAddressMaskSet(
                                uint16_t mask);

/**
    @Summary
        This function sets the slave address.

    @Description
        This function sets the 10-bit slave address to be used by the
        module when filtering transactions from the i2c masters in the
        bus. The function analyzes the given address and decides if
        the 10-bit or 7-bit mode will be enabled. Once the function
        returns, the given address is set for the slave module.

        This function should be called after the initialization of
        the module.

        When changing the slave address the module must be idle.

    @Preconditions
        None

    @Param
        address - The address to be used to determine if the transaction
            is intended for this slave module.

    @Returns
        None

    @Example
        <code>
            // initialize the i2c slave driver 
            I2C1_Initialize();
 
            // set the slave address and address mask if the default
            // values set in the initialize is not the desired values.
            I2C1_SlaveAddressMaskSet(0x0xF);
            I2C1_SlaveAddressSet(0x3C);
 
        </code>

*/

void I2C1_SlaveAddressSet(
                                uint16_t address);


/**
    @Summary
        This function sets the read pointer for the slave driver.

    @Description
        This function sets the read pointer that the driver will
        need to retrieve data that will be transmitted to the master
        whenever the master requests a read.

    @Preconditions
        None

    @Param
        *p - The pointer to the read buffer, that will be used to transmit
             data to the requesting i2c master.

    @Returns
        None

    @Example
        <code>
            Refer to I2C1_Initialize() for an example 
        </code>

*/

void I2C1_ReadPointerSet(uint8_t *p);

/**
    @Summary
        This function sets the write pointer for the slave driver.

    @Description
        This function sets the write pointer that the driver will
        need to save data that will be received from the master
        whenever the master requests a write.

    @Preconditions
        None

    @Param
        *p - The pointer to the read buffer, that will be used to transmit
             data to the requesting i2c master.

    @Returns
        None

    @Example
        <code>
            Refer to I2C1_Initialize() for an example 
        </code>

*/

void I2C1_WritePointerSet(uint8_t *p);

/**
    @Summary
        This function returns the current read pointer used by the
        slave driver.

    @Description
        This function returns the current read pointer used by the
        slave driver. As the operation executes, the pointer is
        incremented. User of the slave driver can use this function
        to check on the current address that the pointer is pointing to.

    @Preconditions
        None

    @Param
        None

    @Returns
        The current pointer used to transmit data to the requesting
        i2c master

    @Example
        <code>
            uint8_t *pReadBuffer;
 
            pReadBuffer = I2C1_ReadPointerGet();
        </code>

*/

uint8_t *I2C1_ReadPointerGet(void);

/**
    @Summary
        This function returns the current write pointer used by the
        slave driver.

    @Description
        This function returns the current write pointer used by the
        slave driver. As the operation executes, the pointer is
        incremented. User of the slave driver can use this function
        to check on the current address that the pointer is pointing to.

    @Preconditions
        None

    @Param
        None

    @Returns
        The current pointer used to save received data from the requesting
        i2c master

    @Example
        <code>
            uint8_t *pWriteBuffer;
 
            pWriteBuffer = I2C1_WritePointerGet();
 
        </code>

*/

uint8_t *I2C1_WritePointerGet(void);

/**
   @Summary
        This application implemented function passes the
        status of the i2c slave driver.

    @Description
        This application implemented function passes the
        status of the i2c slave driver. The status passed
        can be used by the application to manage the read and
        write buffers. This function is called by the slave
        driver everytime there is an event in the i2c bus that caused
        an interrupt in the i2c slave module to be asserted.

        The function will return boolean. The value of the return
        will determine if the next received byte will be ack'ed or 
        nack'ed. By doing that, the application can have the 
        ability to delay incoming transactions. When a false is
        returned, the next transactions will be nack'ed forcing the 
        master to restart the transactions. 
 
        Returning a false will give the application a chance to
        prepare for the next transactions (for example: a read
        requests from the master) or process the recently received
        data.

    @Preconditions
        None

    @Param
        status - The latest status of the slave driver due
            to an event in the i2c bus.

    @Returns
        True, if the application is ready for the next transaction.
        False, if the application is not ready for the next transaction.

    @Example
        <code>

            // Example implementation of the callback

            static uint8_t i2c1_slaveWriteData = 0xAA;

            bool I2C1_StatusCallback(I2C1_SLAVE_DRIVER_STATUS status)
            {

                // this emulates the slave device memory where data written to slave
                // is placed and data read from slave is taken
                static uint8_t EMULATE_EEPROM_Memory[64] =
                        {
                            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                        };

                static uint16_t address, addrByteCount;
                static bool addressState = true;

                switch (status)
                {
                    case I2C1_SLAVE_TRANSMIT_REQUEST_DETECTED:

                        // set up the slave driver buffer transmit pointer
                        I2C1_ReadPointerSet(&EMULATE_EEPROM_Memory[address]);
                        address++;

                        break;

                    case I2C1_SLAVE_RECEIVE_REQUEST_DETECTED:

                        addrByteCount = 0;
                        addressState = true;

                        // set up the slave driver buffer receive pointer
                        I2C1_WritePointerSet(&i2c1_slaveWriteData);

                        break;

                    case I2C1_SLAVE_RECEIVED_DATA_DETECTED:

                        if (addressState == true)
                        {
                            // get the address of the memory being written
                            if (addrByteCount == 0)
                            {
                                address = (i2c1_slaveWriteData << 8) & 0xFF00;
                                addrByteCount++;
                            }
                            else if (addrByteCount == 1)
                            {
                                address = address | i2c1_slaveWriteData;
                                addrByteCount = 0;
                                addressState = false;
                            }
                        }
                        else // if (addressState == false)
                        {
                            // set the memory with the received data
                            EMULATE_EEPROM_Memory[address] = i2c1_slaveWriteData;
                        }

                        break;

                    case I2C1_SLAVE_10BIT_RECEIVE_REQUEST_DETECTED:

                        // do something here when 10-bit address is detected

                        // 10-bit address is detected

                        break;

                    default:
                        break;

                }

                return true;
            }
   
        </code>

 */

#pragma message "I2C1_StatusCallback() is an Application implemented function. If this function is already implemented, you can turn off this message by deleting or commenting out this message."
bool I2C1_StatusCallback(I2C1_SLAVE_DRIVER_STATUS status);

#ifdef __cplusplus // Provide C++ Compatibility

    }

#endif

#endif // _I2C1_H




2. Building the libraries from scratch: I've written some code (obviously it's incomplete, it was just for testing purpose). and i'm able to receive data (without adress, as in the EEPROM implementation) and i'm able to get the data, but it's duplicate. I would prefer to use the first method, but whatever works first is good for me. here's the code:

Main file:

Code:
#include "xc.h"
#include "p24FJ256GA110.h"
#include <ctype.h>
#include <stdbool.h>
#include <stdint.h>
#include "uart3.h"
#include "globales.h"

#define DELAY1 1995
#define DELAY2 5


// CONFIG3
#pragma config WPFP = WPFP511 // Write Protection Flash Page Segment Boundary->Highest Page (same as page 170)
#pragma config WPDIS = WPDIS // Segment Write Protection Disable bit->Segmented code protection disabled
#pragma config WPCFG = WPCFGDIS // Configuration Word Code Page Protection Select bit->Last page(at the top of program memory) and Flash configuration words are not protected
#pragma config WPEND = WPENDMEM // Segment Write Protection End Page Select bit->Write Protect from WPFP to the last page of memory

// CONFIG2
#pragma config POSCMOD = HS // Primary Oscillator Select->HS oscillator mode selected
#pragma config I2C2SEL = PRI // I2C2 Pin Select bit->Use Default SCL2/SDA2 pins for I2C2
#pragma config IOL1WAY = ON // IOLOCK One-Way Set Enable bit->Write RP Registers Once
#pragma config OSCIOFNC = ON // Primary Oscillator Output Function->OSCO functions as port I/O (RC15)
#pragma config FCKSM = CSDCMD // Clock Switching and Monitor->Both Clock Switching and Fail-safe Clock Monitor are disabled
#pragma config FNOSC = PRI // Oscillator Select->Primary oscillator (XT, HS, EC)
#pragma config IESO = ON // Internal External Switch Over Mode->IESO mode (Two-speed start-up) enabled

// CONFIG1
#pragma config WDTPS = PS32768 // Watchdog Timer Postscaler->1:32768
#pragma config FWPSA = PR128 // WDT Prescaler->Prescaler ratio of 1:128
#pragma config WINDIS = OFF // Watchdog Timer Window->Standard Watchdog Timer is enabled,(Windowed-mode is disabled)
#pragma config FWDTEN = ON // Watchdog Timer Enable->Watchdog Timer is enabled
#pragma config ICS = PGx2 // Comm Channel Select->Emulator functions are shared with PGEC2/PGED2
#pragma config BKBUG = OFF // Background Debug->Device resets into Operational mode
#pragma config GWRP = OFF // General Code Segment Write Protect->Writes to program memory are allowed
#pragma config GCP = OFF // General Code Segment Code Protect->Code protection is disabled
#pragma config JTAGEN = OFF // JTAG Port Enable->JTAG port is disabled

char text[100];


 int main (void) {
    T1CON = 0x8030; //inicializa timer1 
    LATB = 0xFFFF;
    TRISB = 0x0000;
    // RCDIV FRC/2; DOZE 1:8; DOZEN disabled; ROI disabled; 
    CLKDIV = 0x3100;
    // TUN Center frequency; 
    OSCTUN = 0x0000;
    // ROEN disabled; ROSEL disabled; RODIV Base clock value; ROSSLP disabled; 
    REFOCON = 0x0000;
    // SICI: SI2C1 - I2C1 Slave Events. Priority: 1
    IPC4bits.SI2C1IP = 1;
    
    RPINR17bits.U3RXR = 0x0010; //RF3->UART3:U3RX;
    RPOR8bits.RP17R = 0x001C; //RF5->UART3:U3TX;
    
    UART3_Initialize();
    I2C1_Initialize();
    
    TMR1=0;
    int ind=0;
    while(1){
        //
        LATB = 0xFFFF;
        TMR1 = 0;
        while (TMR1 < DELAY2)
        {
        }
        LATB = 0x0000;
        TMR1 = 0;
        while (TMR1 < DELAY1)
        {
        }
        //UART3_Write("H");
        //UARTSendString("Supercopter",11);


        if(ind>=sizeof(MemoryMap)){
            ind=0;
        }
        
        if(i2crcvFlag == 1){
            LATB = 1;
            //for(ind=0;ind>=sizeof(MemoryMap);ind++){
                //sprintf(text, "Index: %d, data: %c, hex: 0x%02X", ind, MemoryMap[ind], MemoryMap[ind]);
                UARTSendString(MemoryMap,64);
                UART3_Write('\n');
                UART3_Write('\r');
            //}
            i2crcvFlag = 0; 
        }
            
    }
 }


and the i2c library (basically i editeed the interrupt routine):

Code:
#include "I2C_Lib.h"
#include "globales.h"

int i2crcvFlag;
uint8_t MemoryMap[64];

int WriteIndex;
int dummy;

typedef enum
{
    S_SLAVE_IDLE,
    S_SLAVE_RECEIVE_MODE,
    S_SLAVE_TRANSMIT_MODE,
    S_SLAVE_LOW_BYTE_ADDRESS_DETECT,

} I2C_SLAVE_STATES;

/**
 Section: Macro Definitions
*/
/* defined for I2C1 */
#define I2C1_TRANSMIT_REG I2C1TRN // Defines the transmit register used to send data.
#define I2C1_RECEIVE_REG I2C1RCV // Defines the receive register used to receive data.

#define I2C1_MASK_REG I2C1MSK // Defines the address mask register.
#define I2C1_ADDRESS_REG I2C1ADD // Defines the address register. 

// The following control bits are used in the I2C state machine to manage
// the I2C module and determine next states.
#define I2C1_GENERAL_CALL_ENABLE_BIT I2C1CONbits.GCEN // I2C General Call enable control bit.
#define I2C1_10_BIT_ADDRESS_ENABLE_BIT I2C1CONbits.A10M // I2C Address Mode (7 or 10 bit address) control bit.
#define I2C1_RELEASE_SCL_CLOCK_CONTROL_BIT I2C1CONbits.SCLREL // I2C clock stretch/release control bit.

// The following status bits are used in the I2C state machine to determine
// the next states.

#define I2C1_READ_NOT_WRITE_STATUS_BIT I2C1STATbits.R_W // I2C current transaction read/write status bit.
#define I2C1_DATA_NOT_ADDRESS_STATUS_BIT I2C1STATbits.D_A // I2C last byte receive was data/address status bit.
#define I2C1_RECEIVE_OVERFLOW_STATUS_BIT I2C1STATbits.I2COV // I2C receive buffer overflow status bit.
#define I2C1_GENERAL_CALL_ADDRESS_STATUS_BIT I2C1STATbits.GCSTAT // I2C General Call status bit.
#define I2C1_ACKNOWLEDGE_STATUS_BIT I2C1STATbits.ACKSTAT // I2C ACK status bit.

inline void __attribute__ ((always_inline)) I2C1_TransmitProcess(void);
inline void __attribute__ ((always_inline)) I2C1_ReceiveProcess(void);

/**
 Section: Local Variables
*/

static I2C_SLAVE_STATES i2c1_slave_state;
static uint8_t *p_i2c1_write_pointer;
static uint8_t *p_i2c1_read_pointer;

void I2C1_ReadPointerSet(uint8_t *p)
{
    p_i2c1_read_pointer = p;
}

void I2C1_WritePointerSet(uint8_t *p)
{
    p_i2c1_write_pointer = p;
}

uint8_t *I2C1_ReadPointerGet(void)
{
    return (p_i2c1_read_pointer);
}

uint8_t *I2C1_WritePointerGet(void)
{
    return (p_i2c1_write_pointer);
}

void I2C1_Initialize(void)
{

    // initialize the hardware
    // ACKEN disabled; STREN disabled; GCEN disabled; SMEN disabled; DISSLW enabled; I2CSIDL disabled; ACKDT Sends ACK; SCLREL Holds; RSEN disabled; IPMIEN disabled; A10M 7 Bit; PEN disabled; RCEN disabled; SEN disabled; I2CEN enabled; 
    I2C1CON = 0x8000;
    // P disabled; S disabled; I2COV disabled; IWCOL disabled; 
    I2C1STAT = 0x00;
    // I2CADD 102; 
    I2C1_SlaveAddressSet(0x66);
    // AMSK 0; 
    I2C1_SlaveAddressMaskSet(0x00);

    // make sure this is set first
    i2c1_slave_state = S_SLAVE_IDLE;
    
    I2C1_ReadPointerSet(NULL);
    I2C1_WritePointerSet(NULL);
    
    /* SI2C1 - I2C1 Slave Events */
    // clear the master interrupt flag
    IFS1bits.SI2C1IF = 0;
    // enable the master interrupt
    IEC1bits.SI2C1IE = 1;
    
    WriteIndex=0;
}

void __attribute__ ( ( interrupt, no_auto_psv ) ) _SI2C1Interrupt ( void )
{
    if(WriteIndex>=63){
        WriteIndex = 0;
        i2crcvFlag = 1;
    }

    if(!I2C1_DATA_NOT_ADDRESS_STATUS_BIT ){
        if (I2C1_RECEIVE_OVERFLOW_STATUS_BIT != 1){
            MemoryMap[WriteIndex] = (unsigned char)I2C1RCV;
            WriteIndex++;
            IFS1bits.SI2C1IF = 0;
    }
        else{
            dummy = I2C1_RECEIVE_REG;
            I2C1_RECEIVE_OVERFLOW_STATUS_BIT = 0; 
        }
            
    }
    else
        I2C1RCV = NULL;

}

void I2C1_SlaveAddressSet(
                                uint16_t address)
{
    if (address > 0x7F)
    {
        // use 10 bit address
        I2C1_10_BIT_ADDRESS_ENABLE_BIT = true;
    }
    else
    {
        // use 7 bit address
        I2C1_10_BIT_ADDRESS_ENABLE_BIT = false;
    }
    i2c1_slave_state = S_SLAVE_IDLE;
    I2C1_ADDRESS_REG = address;

}

void I2C1_SlaveAddressMaskSet(
                                uint16_t mask)
{
    I2C1_MASK_REG = mask;
}


inline void __attribute__ ((always_inline)) I2C1_TransmitProcess(void)
{
    // get the data to be transmitted

    // sanity check (to avoid stress)
    if (p_i2c1_read_pointer == NULL)
        return;

    I2C1_TRANSMIT_REG = *p_i2c1_read_pointer;

    // set the SCL clock to be released
    I2C1_RELEASE_SCL_CLOCK_CONTROL_BIT = 1;

}

inline void __attribute__ ((always_inline)) I2C1_ReceiveProcess(void)
{ 
    // store the received data 
    
    // sanity check (to avoid stress)
    if (p_i2c1_write_pointer == NULL)
        return;

    *p_i2c1_write_pointer = I2C1_RECEIVE_REG;

}
 
Status
Not open for further replies.

Latest threads

EE World Online Articles

Loading
Top