Rev 237 | Blame | Last modification | View Log | Download | 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 calledvoid 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 settingsI2C1CONSET = 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 eventsIPC6CLR = 0x00001F00; // Reset priority levelsIPC6SET = 0x00001600; // Set IPL=6, Subpriority 2IEC0SET = 0xE0000000; // Enable I2C1 interruptsINTEnableInterrupts();}// Sends length number of bytes in msg to specified address (no R/W bit)// Will return status I2C1_SEND_OK or I2C1_SEND_FAILvoid 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/addressfor (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' operationi2c_data_p->operating_state = I2C1_SEND_ADDR;i2c_data_p->master_status = I2C1_MASTER_SEND;// Generate start conditionI2C1CONbits.SEN = 1;}// Reads length number of bytes from address (no R/W bit)// Will return status I2C1_RECV_OK or I2C1_RECV_FAILvoid I2C1_Master_Recv(uint8_t address, uint32_t length) {if (length == 0)return;// Save length and address to get data fromi2c_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' operationi2c_data_p->operating_state = I2C1_SEND_ADDR;i2c_data_p->master_status = I2C1_MASTER_RECV;// Generate start conditionI2C1CONbits.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_OKvoid 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 fromi2c_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' operationi2c_data_p->operating_state = I2C1_SEND_ADDR;i2c_data_p->master_status = I2C1_MASTER_RESTART;// Generate start conditionI2C1CONbits.SEN = 1;}void __ISR(_I2C_1_VECTOR, ipl5) __I2C_1_Interrupt_Handler(void) {// Bus collision eventif (IFS0bits.I2C1BIF) {// TODO: Handle bus collision events hereIFS0CLR = 0x20000000;}// Slave eventif (IFS0bits.I2C1SIF) {I2C1_Interrupt_Slave();IFS0CLR = 0x40000000;}// Master eventif (IFS0bits.I2C1MIF) {I2C1_Interrupt_Master();IFS0CLR = 0x80000000;}}// An internal subroutine used in the master version of the i2c_interrupt_handlervoid 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 collisionsI2C1STATbits.IWCOL = 0;}// If we are in the middle of sending dataif (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 seti2c_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 notif (!I2C1STATbits.ACKSTAT) {// If an ACK is received, send next byte of dataif (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 biti2c_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 errori2c_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 seti2c_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 receivedif (!I2C1STATbits.ACKSTAT) {// If an ACK is received, set module to receive 1 byte of datai2c_data_p->operating_state = I2C1_RCV_DATA;I2C1CONbits.RCEN = 1;} else {// If a NACK is received, stop transmission and send errori2c_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 overflowi2c_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 slavei2c_data_p->operating_state = I2C1_REQ_DATA;I2C1CONbits.ACKDT = 0; // ACKI2C1CONbits.ACKEN = 1;} else {// If we are done reading, send an NACK to the slavei2c_data_p->operating_state = I2C1_SEND_STOP;I2C1CONbits.ACKDT = 1; // NACKI2C1CONbits.ACKEN = 1;}break;case I2C1_REQ_DATA:// Set module to receive one byte of datai2c_data_p->operating_state = I2C1_RCV_DATA;I2C1CONbits.RCEN = 1;break;case I2C1_SEND_STOP:// Send the stop biti2c_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 seti2c_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 notif (!I2C1STATbits.ACKSTAT) {// If an ACK is received, send first byte of datauint8_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 errori2c_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 errori2c_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 seti2c_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 receivedif (!I2C1STATbits.ACKSTAT) {// If an ACK is received, set module to receive 1 byte of datai2c_data_p->operating_state = I2C1_RCV_DATA;I2C1CONbits.RCEN = 1;} else {// If a NACK is received, stop transmission and send errori2c_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 overflowi2c_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 slavei2c_data_p->operating_state = I2C1_REQ_DATA;I2C1CONbits.ACKDT = 0; // ACKI2C1CONbits.ACKEN = 1;} else {// If we are done reading, send an NACK to the slavei2c_data_p->operating_state = I2C1_SEND_STOP;I2C1CONbits.ACKDT = 1; // NACKI2C1CONbits.ACKEN = 1;}break;case I2C1_REQ_DATA:// Set module to receive one byte of datai2c_data_p->operating_state = I2C1_RCV_DATA;I2C1CONbits.RCEN = 1;break;case I2C1_SEND_STOP:// Send the stop biti2c_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 fullif (I2C1STATbits.RBF == 1) {received_data = I2C1RCV;data_read_from_buffer = 1;}if (!overrun_error) {if (I2C1STATbits.R_W == 0) {// Slave is receiving datai2c_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 dataif (!i2c_data_p->slave_sending_data) {// If we are not currently sending data, figure out what to reply withif (I2C1_Process_Request(i2c_data_p->slave_in_last_byte)) {// Data exists to be returned, send first byteI2C1TRN = 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 0sI2C1TRN = 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 masterif (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 0sI2C1TRN = 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 clockif (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;}