0,0 → 1,447 |
#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) I2C1BRG = 0x05A; // Operate at 400kHZ (80MHz) |
else I2C1BRG = 0x186; // Operate at 100kHZ (80MHz) |
IFS0CLR = 0xE0000000; // Clear any existing events |
IPC6CLR = 0x00001F00; // Reset priority levels |
IPC6SET = 0x00001500; // Set IPL=5, Subpriority 1 |
IEC0SET = 0xE0000000; // Enable I2C1 interrupts |
|
INTEnableInterrupts(); |
} |
|
// Sends length number of bytes in msg to specified address (no R/W bit) |
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) |
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 |
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) { |
// This should be handled at some point |
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 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_IDLE; |
I2C1CONbits.PEN = 1; |
i2c_data_p->master_status = I2C1_MASTER_IDLE; |
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_IDLE; |
I2C1CONbits.PEN = 1; |
i2c_data_p->master_status = I2C1_MASTER_IDLE; |
i2c_data_p->return_status = I2C1_SEND_FAIL; |
} |
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_IDLE; |
I2C1CONbits.PEN = 1; |
i2c_data_p->master_status = I2C1_MASTER_IDLE; |
i2c_data_p->return_status = I2C1_RECV_FAIL; |
} |
break; |
case I2C1_RCV_DATA: |
// On receive, save byte into buffer |
// TODO: Handle 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 and copy message to send to Main() |
i2c_data_p->operating_state = I2C1_IDLE; |
I2C1CONbits.PEN = 1; |
i2c_data_p->master_status = I2C1_MASTER_IDLE; |
i2c_data_p->return_status = I2C1_RECV_OK; |
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 |
I2C1TRN = i2c_data_p->buffer_in[0]; |
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_IDLE; |
I2C1CONbits.PEN = 1; |
i2c_data_p->master_status = I2C1_MASTER_IDLE; |
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_IDLE; |
I2C1CONbits.PEN = 1; |
i2c_data_p->master_status = I2C1_MASTER_IDLE; |
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_IDLE; |
I2C1CONbits.PEN = 1; |
i2c_data_p->master_status = I2C1_MASTER_IDLE; |
i2c_data_p->return_status = I2C1_RECV_FAIL; |
} |
break; |
case I2C1_RCV_DATA: |
// On receive, save byte into buffer |
// TODO: Handle 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 and copy message to send to Main() |
i2c_data_p->operating_state = I2C1_IDLE; |
I2C1CONbits.PEN = 1; |
i2c_data_p->master_status = I2C1_MASTER_IDLE; |
i2c_data_p->return_status = I2C1_RECV_OK; |
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->buffer_in_len == 0) { |
return 0; |
} else { |
return i2c_data_p->return_status; |
} |
} |
|
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; |
} |