Rev 237 | Blame | Last modification | View Log | RSS feed
#include "defines.h"
#include "I2C1.h"
static I2C1_DATA *i2c_data_p;
// Initialize the data structures, should be called once before any I2C routines are called
void I2C1_Init(I2C1_DATA *data, uint8_t speed, uint8_t address) {
i2c_data_p = data;
i2c_data_p->buffer_in_len = 0;
i2c_data_p->buffer_in_read_ind = 0;
i2c_data_p->buffer_in_write_ind = 0;
i2c_data_p->buffer_out_ind = 0;
i2c_data_p->buffer_out_len = 0;
i2c_data_p->operating_state = I2C1_IDLE;
i2c_data_p->return_status = 0;
i2c_data_p->slave_in_last_byte = 0;
i2c_data_p->slave_sending_data = 0;
i2c_data_p->master_dest_addr = 0;
i2c_data_p->master_status = I2C1_MASTER_IDLE;
INTDisableInterrupts();
// Enable the I2C module and set the clock stretch enable bit
// Note: Automatically overrides any other pin settings
I2C1CONSET = 0x00008040;
I2C1ADD = address;
if (speed == 0x01) {
I2C1BRG = 0x0BE; // Operate at 200kHZ (80MHz)
I2C1CONbits.DISSLW = 0; // Slew rate control enabled
} else if (speed == 0x02) {
I2C1BRG = 0x05A; // Operate at 400kHZ (80MHz)
I2C1CONbits.DISSLW = 1; // Slew rate control disabled
} else if (speed == 0x03) {
I2C1BRG = 0x020; // Operate at 1MHz (80MHz)
I2C1CONbits.DISSLW = 1; // Slew rate control disabled
} else {
I2C1BRG = 0x186; // Operate at 100kHZ (80MHz)
I2C1CONbits.DISSLW = 0; // Slew rate control enabled
}
IFS0CLR = 0xE0000000; // Clear any existing events
IPC6CLR = 0x00001F00; // Reset priority levels
IPC6SET = 0x00001600; // Set IPL=6, Subpriority 2
IEC0SET = 0xE0000000; // Enable I2C1 interrupts
INTEnableInterrupts();
}
// Sends length number of bytes in msg to specified address (no R/W bit)
// Will return status I2C1_SEND_OK or I2C1_SEND_FAIL
void I2C1_Master_Send(uint8_t address, uint8_t *msg, uint32_t length) {
uint32_t i;
if (length == 0)
return;
// Copy message to send into buffer and save length/address
for (i = 0; i < length; i++) {
i2c_data_p->buffer_in[i] = msg[i];
}
i2c_data_p->buffer_in_len = length;
i2c_data_p->master_dest_addr = address;
i2c_data_p->buffer_in_read_ind = 0;
i2c_data_p->buffer_in_write_ind = 0;
// Change status to 'next' operation
i2c_data_p->operating_state = I2C1_SEND_ADDR;
i2c_data_p->master_status = I2C1_MASTER_SEND;
// Generate start condition
I2C1CONbits.SEN = 1;
}
// Reads length number of bytes from address (no R/W bit)
// Will return status I2C1_RECV_OK or I2C1_RECV_FAIL
void I2C1_Master_Recv(uint8_t address, uint32_t length) {
if (length == 0)
return;
// Save length and address to get data from
i2c_data_p->buffer_in_len = length;
i2c_data_p->master_dest_addr = address;
i2c_data_p->buffer_in_read_ind = 0;
i2c_data_p->buffer_in_write_ind = 0;
// Change status to 'next' operation
i2c_data_p->operating_state = I2C1_SEND_ADDR;
i2c_data_p->master_status = I2C1_MASTER_RECV;
// Generate start condition
I2C1CONbits.SEN = 1;
}
// Writes msg to address then reads length number of bytes from address
// Will return status I2C1_SEND_FAIL or I2C1_RECV_FAIL or I2C1_RECV_OK
void I2C1_Master_Restart(uint8_t address, uint8_t msg, uint32_t length) {
uint8_t c;
if (length == 0) {
c = msg;
I2C1_Master_Send(address, &c, 1);
return;
}
// Save length and address to get data from
i2c_data_p->buffer_in[0] = msg;
i2c_data_p->buffer_in_len = length;
i2c_data_p->master_dest_addr = address;
i2c_data_p->buffer_in_read_ind = 0;
i2c_data_p->buffer_in_write_ind = 0;
// Change status to 'next' operation
i2c_data_p->operating_state = I2C1_SEND_ADDR;
i2c_data_p->master_status = I2C1_MASTER_RESTART;
// Generate start condition
I2C1CONbits.SEN = 1;
}
void __ISR(_I2C_1_VECTOR, ipl5) __I2C_1_Interrupt_Handler(void) {
// Bus collision event
if (IFS0bits.I2C1BIF) {
// TODO: Handle bus collision events here
IFS0CLR = 0x20000000;
}
// Slave event
if (IFS0bits.I2C1SIF) {
I2C1_Interrupt_Slave();
IFS0CLR = 0x40000000;
}
// Master event
if (IFS0bits.I2C1MIF) {
I2C1_Interrupt_Master();
IFS0CLR = 0x80000000;
}
}
// An internal subroutine used in the master version of the i2c_interrupt_handler
void I2C1_Interrupt_Master() {
/* The PIC32 family has different master interrupts than the PIC8 family
* Master mode operations that generate a slave interrupt are:
* 1. Start condition
* 2. Repeated start sequence
* 3. Stop condition
* 4. Data transfer byte received
* 5. During a send ACK or NACK sequence to slave
* 6. Data transfer byte transmitted
* 7. During a slave-detected stop
*/
if (I2C1STATbits.IWCOL == 1) {
// TODO: Handle write collisions
I2C1STATbits.IWCOL = 0;
}
// If we are in the middle of sending data
if (i2c_data_p->master_status == I2C1_MASTER_SEND) {
switch (i2c_data_p->operating_state) {
case I2C1_IDLE:
break;
case I2C1_SEND_ADDR:
// Send the address with read bit set
i2c_data_p->operating_state = I2C1_CHECK_ACK_SEND;
I2C1TRN = (i2c_data_p->master_dest_addr << 1) | 0x0;
break;
case I2C1_CHECK_ACK_SEND:
// Check if ACK is received or not
if (!I2C1STATbits.ACKSTAT) {
// If an ACK is received, send next byte of data
if (i2c_data_p->buffer_in_read_ind < i2c_data_p->buffer_in_len) {
I2C1TRN = i2c_data_p->buffer_in[i2c_data_p->buffer_in_read_ind];
i2c_data_p->buffer_in_read_ind++;
} else {
// If no more data is to be sent, send stop bit
i2c_data_p->operating_state = I2C1_STOPPED;
I2C1CONbits.PEN = 1;
i2c_data_p->return_status = I2C1_SEND_OK;
}
} else {
// If a NACK is received, stop transmission and send error
i2c_data_p->operating_state = I2C1_STOPPED;
I2C1CONbits.PEN = 1;
i2c_data_p->return_status = I2C1_SEND_FAIL;
}
break;
case I2C1_STOPPED:
i2c_data_p->operating_state = I2C1_IDLE;
i2c_data_p->master_status = I2C1_MASTER_IDLE;
break;
}
// If we are in the middle of receiving data
} else if (i2c_data_p->master_status == I2C1_MASTER_RECV) {
switch (i2c_data_p->operating_state) {
case I2C1_IDLE:
break;
case I2C1_SEND_ADDR:
// Send address with write bit set
i2c_data_p->operating_state = I2C1_CHECK_ACK_RECV;
I2C1TRN = (i2c_data_p->master_dest_addr << 1) | 0x1;
break;
case I2C1_CHECK_ACK_RECV:
// Check if ACK is received
if (!I2C1STATbits.ACKSTAT) {
// If an ACK is received, set module to receive 1 byte of data
i2c_data_p->operating_state = I2C1_RCV_DATA;
I2C1CONbits.RCEN = 1;
} else {
// If a NACK is received, stop transmission and send error
i2c_data_p->operating_state = I2C1_STOPPED;
I2C1CONbits.PEN = 1;
i2c_data_p->return_status = I2C1_RECV_FAIL;
}
break;
case I2C1_RCV_DATA:
// On receive, save byte into buffer
// TODO: Handle possible I2C buffer overflow
i2c_data_p->buffer_in[i2c_data_p->buffer_in_write_ind] = I2C1RCV;
i2c_data_p->buffer_in_write_ind++;
if (i2c_data_p->buffer_in_write_ind < i2c_data_p->buffer_in_len) {
// If we still need to read, send an ACK to the slave
i2c_data_p->operating_state = I2C1_REQ_DATA;
I2C1CONbits.ACKDT = 0; // ACK
I2C1CONbits.ACKEN = 1;
} else {
// If we are done reading, send an NACK to the slave
i2c_data_p->operating_state = I2C1_SEND_STOP;
I2C1CONbits.ACKDT = 1; // NACK
I2C1CONbits.ACKEN = 1;
}
break;
case I2C1_REQ_DATA:
// Set module to receive one byte of data
i2c_data_p->operating_state = I2C1_RCV_DATA;
I2C1CONbits.RCEN = 1;
break;
case I2C1_SEND_STOP:
// Send the stop bit
i2c_data_p->operating_state = I2C1_STOPPED;
I2C1CONbits.PEN = 1;
i2c_data_p->return_status = I2C1_RECV_OK;
break;
case I2C1_STOPPED:
i2c_data_p->operating_state = I2C1_IDLE;
i2c_data_p->master_status = I2C1_MASTER_IDLE;
break;
}
} else if (i2c_data_p->master_status == I2C1_MASTER_RESTART) {
switch (i2c_data_p->operating_state) {
case I2C1_IDLE:
break;
case I2C1_SEND_ADDR:
// Send the address with read bit set
i2c_data_p->operating_state = I2C1_CHECK_ACK_SEND;
I2C1TRN = (i2c_data_p->master_dest_addr << 1) | 0x0;
break;
case I2C1_CHECK_ACK_SEND:
// Check if ACK is received or not
if (!I2C1STATbits.ACKSTAT) {
// If an ACK is received, send first byte of data
uint8_t to_send = i2c_data_p->buffer_in[0];
I2C1TRN = to_send;
i2c_data_p->operating_state = I2C1_CHECK_ACK_RESTART;
} else {
// If a NACK is received, stop transmission and send error
i2c_data_p->operating_state = I2C1_STOPPED;
I2C1CONbits.PEN = 1;
i2c_data_p->return_status = I2C1_SEND_FAIL;
}
break;
case I2C1_CHECK_ACK_RESTART:
if (!I2C1STATbits.ACKSTAT) {
I2C1CONbits.RSEN = 1;
i2c_data_p->operating_state = I2C1_SEND_ADDR_2;
} else {
// If a NACK is received, stop transmission and send error
i2c_data_p->operating_state = I2C1_STOPPED;
I2C1CONbits.PEN = 1;
i2c_data_p->return_status = I2C1_SEND_FAIL;
}
break;
case I2C1_SEND_ADDR_2:
// Send the address with read bit set
i2c_data_p->operating_state = I2C1_CHECK_ACK_RECV;
I2C1TRN = (i2c_data_p->master_dest_addr << 1) | 0x1;
break;
case I2C1_CHECK_ACK_RECV:
// Check if ACK is received
if (!I2C1STATbits.ACKSTAT) {
// If an ACK is received, set module to receive 1 byte of data
i2c_data_p->operating_state = I2C1_RCV_DATA;
I2C1CONbits.RCEN = 1;
} else {
// If a NACK is received, stop transmission and send error
i2c_data_p->operating_state = I2C1_STOPPED;
I2C1CONbits.PEN = 1;
i2c_data_p->return_status = I2C1_RECV_FAIL;
}
break;
case I2C1_RCV_DATA:
// On receive, save byte into buffer
// TODO: Handle possible I2C buffer overflow
i2c_data_p->buffer_in[i2c_data_p->buffer_in_write_ind] = I2C1RCV;
i2c_data_p->buffer_in_write_ind++;
if (i2c_data_p->buffer_in_write_ind < i2c_data_p->buffer_in_len) {
// If we still need to read, send an ACK to the slave
i2c_data_p->operating_state = I2C1_REQ_DATA;
I2C1CONbits.ACKDT = 0; // ACK
I2C1CONbits.ACKEN = 1;
} else {
// If we are done reading, send an NACK to the slave
i2c_data_p->operating_state = I2C1_SEND_STOP;
I2C1CONbits.ACKDT = 1; // NACK
I2C1CONbits.ACKEN = 1;
}
break;
case I2C1_REQ_DATA:
// Set module to receive one byte of data
i2c_data_p->operating_state = I2C1_RCV_DATA;
I2C1CONbits.RCEN = 1;
break;
case I2C1_SEND_STOP:
// Send the stop bit
i2c_data_p->operating_state = I2C1_STOPPED;
I2C1CONbits.PEN = 1;
i2c_data_p->return_status = I2C1_RECV_OK;
break;
case I2C1_STOPPED:
i2c_data_p->operating_state = I2C1_IDLE;
i2c_data_p->master_status = I2C1_MASTER_IDLE;
break;
}
}
}
void I2C1_Interrupt_Slave() {
// !!WARNING!! THIS CODE DOES -NOT- HAVE ANY ERROR HANDLING !!
// TODO: Add error handling to this interrupt function
/* The PIC32 family has different slave interrupts than the PIC8 family
* Slave mode operations that generate a slave interrupt are:
* 1. Detection of a valid device address (including general call)
* 2. Reception of data
* 3. Request to transmit data
*/
uint8_t received_data;
uint8_t data_read_from_buffer = 0;
uint8_t data_written_to_buffer = 0;
uint8_t overrun_error = 0;
// Clear SSPOV (overflow bit)
if (I2C1STATbits.I2COV == 1) {
I2C1STATbits.I2COV = 0;
overrun_error = 1;
i2c_data_p->return_status = I2C1_ERR_OVERRUN;
}
// Read SPPxBUF if it is full
if (I2C1STATbits.RBF == 1) {
received_data = I2C1RCV;
data_read_from_buffer = 1;
}
if (!overrun_error) {
if (I2C1STATbits.R_W == 0) {
// Slave is receiving data
i2c_data_p->buffer_in[i2c_data_p->buffer_in_write_ind] = received_data;
if (i2c_data_p->buffer_in_write_ind == MAXI2C1BUF - 1) {
i2c_data_p->buffer_in_write_ind = 0;
} else {
i2c_data_p->buffer_in_write_ind++;
}
if (i2c_data_p->buffer_in_len < MAXI2C1BUF - 1) {
i2c_data_p->buffer_in_len++;
}
i2c_data_p->slave_in_last_byte = received_data;
i2c_data_p->return_status = I2C1_RECV_OK;
} else {
// Slave is returning data
if (!i2c_data_p->slave_sending_data) {
// If we are not currently sending data, figure out what to reply with
if (I2C1_Process_Request(i2c_data_p->slave_in_last_byte)) {
// Data exists to be returned, send first byte
I2C1TRN = i2c_data_p->buffer_out[0];
data_written_to_buffer = 1;
i2c_data_p->buffer_out_ind = 1;
i2c_data_p->slave_sending_data = 1;
} else {
// Unknown request, fill rest of request with 0s
I2C1TRN = 0x0;
data_written_to_buffer = 1;
i2c_data_p->slave_sending_data = 0;
i2c_data_p->return_status = I2C1_SEND_FAIL;
}
} else {
// Sending remaining data back to master
if (i2c_data_p->buffer_out_ind < i2c_data_p->buffer_out_len) {
I2C1TRN = i2c_data_p->buffer_out[i2c_data_p->buffer_out_ind];
data_written_to_buffer = 1;
i2c_data_p->buffer_out_ind++;
} else {
// Nothing left to send, fill rest of request with 0s
I2C1TRN = 0x0;
data_written_to_buffer = 1;
i2c_data_p->slave_sending_data = 0;
i2c_data_p->return_status = I2C1_SEND_OK;
}
}
}
}
// Release the clock stretching bit (if we should)
if (data_read_from_buffer || data_written_to_buffer) {
// Release the clock
if (I2C1CONbits.SCLREL == 0) {
I2C1CONbits.SCLREL = 1;
}
}
}
/* Returns 0 if I2C module is currently busy, otherwise returns status code */
uint8_t I2C1_Get_Status() {
if (i2c_data_p->master_status == I2C1_MASTER_IDLE &&
i2c_data_p->operating_state == I2C1_IDLE &&
I2C1STATbits.TBF == 0) {
return i2c_data_p->return_status;
} else {
return 0;
}
}
uint8_t I2C1_Buffer_Len() {
return i2c_data_p->buffer_in_len;
}
/* Returns 0 if I2C module is currently busy, otherwise returns buffer length */
uint8_t I2C1_Read_Buffer(uint8_t *buffer) {
uint32_t i = 0;
while (i2c_data_p->buffer_in_len != 0) {
buffer[i] = i2c_data_p->buffer_in[i2c_data_p->buffer_in_read_ind];
i++;
if (i2c_data_p->buffer_in_read_ind == MAXI2C1BUF-1) {
i2c_data_p->buffer_in_read_ind = 0;
} else {
i2c_data_p->buffer_in_read_ind++;
}
i2c_data_p->buffer_in_len--;
}
return i;
}
/* Put data to be returned here */
uint8_t I2C1_Process_Request(uint8_t c) {
uint8_t ret = 0;
switch (c) {
case 0x01:
i2c_data_p->buffer_out[0] = 0x12;
i2c_data_p->buffer_out_len = 1;
ret = 1;
break;
case 0x02:
i2c_data_p->buffer_out[0] = 0x34;
i2c_data_p->buffer_out[1] = 0x56;
i2c_data_p->buffer_out_len = 2;
ret = 1;
break;
}
return ret;
}