/** MSSP2_I2C Generated Driver File @Company Microchip Technology Inc. @File Name mssp2_i2c.c @Summary This is the generated source file for the MSSP2_I2C driver using PIC24 / dsPIC33 / PIC32MM MCUs @Description This source file provides APIs for driver for MSSP2_I2C. Generation Information : Product Revision : PIC24 / dsPIC33 / PIC32MM MCUs - 1.171.4 Device : PIC24FV16KM202 The generated drivers are tested against the following: Compiler : XC16 v2.10 MPLAB : MPLAB X v6.05 */ /* (c) 2020 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 "mssp2_i2c.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; /** I2C uint16_t union @Summary Defines the data type to easily manage the slave addresses. @Description This defines the data type to easily swap between lsb and msb of the slave address when doing 10-bit addressing. */ typedef union { uint8_t uint168BitValue[2]; uint16_t uint16Value; }UINT16_UNION; /** Section: Macro Definitions */ /* defined for MSSP2_I2C */ #define MSSP2_I2C_TRANSMIT_REG SSP2BUF // Defines the transmit register used to send data. #define MSSP2_I2C_RECEIVE_REG SSP2BUF // Defines the receive register used to receive data. #define MSSP2_I2C_MASK_REG SSP2MSK // Defines the address mask register. #define MSSP2_I2C_ADDRESS_REG SSP2ADD // 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 MSSP2_I2C_SERIAL_PORT_ENABLE_BIT SSP2CON1bits.SSPEN // I2C port enable control bit. #define MSSP2_I2C_MODE_SELECT_BITS SSP2CON1bits.SSPM // I2C Slave Mode control bit. #define MSSP2_I2C_RELEASE_SCL_CLOCK_CONTROL_BIT SSP2CON1bits.CKP // I2C clock stretch/release control bit. #define MSSP2_I2C_RECEIVE_OVERFLOW_STATUS_BIT SSP2CON1bits.SSPOV // I2C receive buffer overflow status bit. #define MSSP2_I2C_ACKNOWLEDGE_STATUS_BIT SSP2CON2bits.ACKSTAT // I2C ACK status bit. // The following status bits are used in the I2C state machine to determine // the next states. #define MSSP2_I2C_READ_NOT_WRITE_STATUS_BIT SSP2STATbits.R_NOT_W // I2C current transaction read/write status bit. #define MSSP2_I2C_DATA_NOT_ADDRESS_STATUS_BIT SSP2STATbits.D_NOT_A // I2C last byte receive was data/address status bit. /** Section: Local Functions */ inline void __attribute__ ((always_inline)) MSSP2_I2C_TransmitProcess(void); inline void __attribute__ ((always_inline)) MSSP2_I2C_ReceiveProcess(void); /** Section: Local Variables */ static I2C_SLAVE_STATES mssp2_i2c_slave_state; static uint8_t *p_mssp2_i2c_write_pointer; static uint8_t *p_mssp2_i2c_read_pointer; static UINT16_UNION mssp2_i2c_slave_address; uint8_t yeah; #define ARRAY_CNT 6 // Number of bytes in array uint8_t slaveAddress = 0x30; // 7-bit slave address uint8_t index = 0; // Array pointer uint8_t I2C_timeout = 0; //Number of timer iterations since clear uint8_t temp = 0; // Temp register uint8_t regAdd = 1; // First data byte was reg add uint8_t i2cArray[ARRAY_CNT] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /** Prototype: void MSSP2_I2C_Initialize(void) Input: none Output: none Description: MSSP2_I2C_Initialize is an initialization routine that takes inputs from the GUI. Usage: MSSP2_I2C_Initialize(); */ void MSSP2_I2C_Initialize(void) { // initialize the state mssp2_i2c_slave_state = S_SLAVE_IDLE; MSSP2_I2C_ReadPointerSet(NULL); MSSP2_I2C_WritePointerSet(NULL); // initialize the hardware // SMP Standard Speed; CKE Idle to Active; SSP2STAT = 0x80; // SSPEN enabled; WCOL no_collision; CKP Clock Stretch; SSPM 7 Bit; SSPOV no_overflow; SSP2CON1 = 0x2E & 0x26; // ACKEN disabled; GCEN disabled; PEN disabled; ACKDT acknowledge; RSEN disabled; RCEN disabled; SEN enabled; SSP2CON2 = 0x01; // SBCDE disabled; BOEN enabled; SCIE disabled; PCIE disabled; DHEN disabled; SDAHT 300ns; AHEN disabled; SSP2CON3 = 0x18 | 0x01; // AMSK 0; SSP2MSK = 0x00; // Set up the slave address: SSPADD 7; //MSSP2_I2C_SlaveAddressSet(0x07); SSP2ADD = 0x30; IFS3bits.BCL2IF = 0; // Clear Bus Collision IF /* MSSP2 - I2C/SPI Interrupt */ // clear the master interrupt flag IFS3bits.SSP2IF = 0; // enable the master interrupt IEC3bits.SSP2IE = 1; } void __attribute__((interrupt, no_auto_psv)) _MSSP2Interrupt(void) { if (IFS3bits.SSP2IF) // Check for SSPIF { if (SSP2STATbits.R_NOT_W == 1) // Master read (slave transmit) { temp = SSP2BUF; SSP2BUF = i2cArray[1]; // Load array value SSP2CON1bits.CKP = 1; // Release clock stretch } if (SSP2STATbits.R_NOT_W == 0) // Master write (slave receive) { I2C_timeout = 0x00; //Reset our counter when a message is received. if (SSP2STATbits.D_NOT_A == 0) // Last byte was an address { _LATB7 = !_LATB7; regAdd = 1; // Next byte register address temp = SSP2BUF; // Clear BF SSP2CON1bits.CKP = 1; // Release clock stretch } if (SSP2STATbits.D_NOT_A == 1) // Last byte was data { if (regAdd == 1) // Last byte was register add { index = SSP2BUF; // Load register address regAdd = 0; // Next byte will be true data } else { if (index < ARRAY_CNT) // Within boundaries? { i2cArray[index] = SSP2BUF; // Yes, read SSP1BUF } else { temp = SSP2BUF; // No, discard data } } SSP2CON1bits.CKP = 1; // Release clock stretch } } } if ((IFS3bits.BCL2IF == 1) || (SSP2CON1bits.SSPOV == 1)) { temp = SSP2BUF; // Clear BF IFS3bits.BCL2IF = 0; // Clear BCLIF SSP2CON1bits.SSPOV = 0; SSP2CON1bits.CKP = 1; // Release clock stretching } IFS3bits.SSP2IF = 0; // Clear SSP1IF _LATB8 = !SSP2CON1bits.SSPOV; } uint8_t getTimeoutCounter() { return I2C_timeout; } void incrementTimeoutCounter() { I2C_timeout++; } uint8_t getI2CArray(uint8_t index) { if(index < 5)return i2cArray[index]; else return 0; } void MSSP2_I2C_ReadPointerSet(uint8_t *p) { p_mssp2_i2c_read_pointer = p; } void MSSP2_I2C_WritePointerSet(uint8_t *p) { p_mssp2_i2c_write_pointer = p; } uint8_t *MSSP2_I2C_ReadPointerGet(void) { return (p_mssp2_i2c_read_pointer); } uint8_t *MSSP2_I2C_WritePointerGet(void) { return (p_mssp2_i2c_write_pointer); } void MSSP2_I2C_SlaveAddressMaskSet( uint16_t mask) { MSSP2_I2C_MASK_REG = mask; } void MSSP2_I2C_SlaveAddressSet( uint16_t address) { UINT16_UNION add; add.uint16Value = address; if (address > 0xFF) { // use 10 bit address add.uint168BitValue[1] = (0xF0 | ((add.uint168BitValue[1] << 1) & 0x06)); MSSP2_I2C_ADDRESS_REG = add.uint168BitValue[1]; } else { // use 7 bit address MSSP2_I2C_ADDRESS_REG = add.uint168BitValue[0]; } mssp2_i2c_slave_address.uint16Value = add.uint16Value; } inline void __attribute__ ((always_inline)) MSSP2_I2C_TransmitProcess(void) { // get the data to be transmitted // sanity check (to avoid stress) if (p_mssp2_i2c_read_pointer == NULL) return; //MSSP2_I2C_TRANSMIT_REG = *p_mssp2_i2c_read_pointer; MSSP2_I2C_TRANSMIT_REG = yeah; // set the SCL clock to be released MSSP2_I2C_RELEASE_SCL_CLOCK_CONTROL_BIT = 1; } inline void __attribute__ ((always_inline)) MSSP2_I2C_ReceiveProcess(void) { // store the received data // sanity check (to avoid stress) if (p_mssp2_i2c_write_pointer == NULL) return; *p_mssp2_i2c_write_pointer = MSSP2_I2C_RECEIVE_REG; } /* Note: This is an example of the MSSP2_I2C_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 mssp2_i2c_slaveWriteData = 0xAA; bool MSSP2_I2C_StatusCallback(MSSP2_I2C_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[5] = { 0x30, 0x31, 0x32, 0x33, 0x34 }; static uint16_t address = 0; static bool addressState = true; switch (status) { case MSSP2_I2C_SLAVE_TRANSMIT_REQUEST_DETECTED: // set up the slave driver buffer transmit pointer //MSSP2_I2C_ReadPointerSet(&EMULATE_EEPROM_Memory[address]); //address++; //if(address > 5) address = 0; SSP2BUF = yeah; break; case MSSP2_I2C_SLAVE_RECEIVE_REQUEST_DETECTED: addressState = true; // set up the slave driver buffer receive pointer MSSP2_I2C_WritePointerSet(&mssp2_i2c_slaveWriteData); break; case MSSP2_I2C_SLAVE_RECEIVED_DATA_DETECTED: /* if (addressState == true) { address = mssp2_i2c_slaveWriteData; addressState = false; if(address > 31) _LATB6=0; else _LATB6=1; } else // if (addressState == false) { // set the memory with the received data EMULATE_EEPROM_Memory[address] = mssp2_i2c_slaveWriteData; }*/ yeah = SSP2BUF; if(yeah > 100) _LATB6=0; else _LATB6=1; break; case MSSP2_I2C_SLAVE_10BIT_RECEIVE_REQUEST_DETECTED: // do something here when 10-bit address is detected // 10-bit address is detected break; default: break; } return true; }