Rev 281 | Blame | Last modification | View Log | RSS feed
#include "defines.h"
#include "I2C1.h"
extern I2C1_DATA i2c_data;
// Set up the data structures for the base_I2C.code
// Should be called once before any i2c routines are called
void I2C1_Init(void) {
I2C_1_CLK_TRIS = 1;
I2C_1_DAT_TRIS = 1;
i2c_data.buffer_in_len = 0;
i2c_data.buffer_in_read_ind = 0;
i2c_data.buffer_in_write_ind = 0;
i2c_data.operating_state = I2C_IDLE;
i2c_data.return_status = 0;
i2c_data.master_dest_addr = 0;
i2c_data.master_status = I2C_MASTER_IDLE;
// Enable I2C interrupt
PIE1bits.SSP1IE = 1;
}
// Setup the PIC to operate as a master.
void I2C1_Configure_Master(uint8_t speed) {
SSP1STAT = 0x0;
SSP1CON1 = 0x0;
SSP1CON2 = 0x0;
SSP1CON3 = 0x0;
SSP1CON1bits.SSPM = 0x8; // I2C Master Mode
if (speed == 0x01) {
SSP1ADD = 0x13; // Operate at 400KHz (32MHz)
SSP1STATbits.SMP = 1; // Disable Slew Rate Control
} else if (speed == 0x02) {
SSP1ADD = 0x07; // Operate at 1Mhz (32Mhz)
SSP1STATbits.SMP = 1; // Disable Slew Rate Control
} else {
SSP1ADD = 0x4F; // Operate at 100KHz (32MHz)
SSP1STATbits.SMP = 0; // Enable Slew Rate Control
}
SSP1CON1bits.SSPEN = 1; // Enable MSSP1 Module
}
// Sends length number of bytes in msg to specified address (no R/W bit)
void I2C1_Master_Send(uint8_t address, uint8_t length, uint8_t *msg) {
if (length == 0)
return;
// Copy message to send into buffer and save length/address
for (uint8_t i = 0; i < length; i++) {
i2c_data.buffer_in[i] = msg[i];
}
i2c_data.buffer_in_len = length;
i2c_data.master_dest_addr = address;
i2c_data.buffer_in_read_ind = 0;
i2c_data.buffer_in_write_ind = 0;
// Change status to 'next' operation
i2c_data.operating_state = I2C_SEND_ADDR;
i2c_data.master_status = I2C_MASTER_SEND;
// Generate start condition
SSP1CON2bits.SEN = 1;
}
// Reads length number of bytes from address (no R/W bit)
void I2C1_Master_Recv(uint8_t address, uint8_t length) {
if (length == 0)
return;
// Save length and address to get data from
i2c_data.buffer_in_len = length;
i2c_data.master_dest_addr = address;
i2c_data.buffer_in_read_ind = 0;
i2c_data.buffer_in_write_ind = 0;
// Change status to 'next' operation
i2c_data.operating_state = I2C_SEND_ADDR;
i2c_data.master_status = I2C_MASTER_RECV;
// Generate start condition
SSP1CON2bits.SEN = 1;
}
// Writes msg to address then reads length number of bytes from address
void I2C1_Master_Restart(uint8_t address, uint8_t msg, uint8_t length) {
uint8_t c;
if (length == 0) {
c = msg;
I2C1_Master_Send(address, 1, &c);
return;
}
// Save length and address to get data from
i2c_data.buffer_in[0] = msg;
i2c_data.buffer_in_len = length;
i2c_data.master_dest_addr = address;
i2c_data.buffer_in_read_ind = 0;
i2c_data.buffer_in_write_ind = 0;
// Change status to 'next' operation
i2c_data.operating_state = I2C_SEND_ADDR;
i2c_data.master_status = I2C_MASTER_RESTART;
// Generate start condition
SSP1CON2bits.SEN = 1;
}
void I2C1_Interrupt_Handler() {
// If we are in the middle of sending data
if (i2c_data.master_status == I2C_MASTER_SEND) {
switch (i2c_data.operating_state) {
case I2C_IDLE:
break;
case I2C_SEND_ADDR:
// Send the address with read bit set
i2c_data.operating_state = I2C_CHECK_ACK_SEND;
SSP1BUF = (i2c_data.master_dest_addr << 1) | 0x0;
break;
case I2C_CHECK_ACK_SEND:
// Check if ACK is received or not
if (!SSP1CON2bits.ACKSTAT) {
// If an ACK is received, send next byte of data
if (i2c_data.buffer_in_read_ind < i2c_data.buffer_in_len) {
SSP1BUF = i2c_data.buffer_in[i2c_data.buffer_in_read_ind];
i2c_data.buffer_in_read_ind++;
} else {
// If no more data is to be sent, send stop bit
i2c_data.operating_state = I2C_IDLE;
SSP1CON2bits.PEN = 1;
i2c_data.master_status = I2C_MASTER_IDLE;
i2c_data.return_status = I2C_SEND_OK;
}
} else {
// If a NACK is received, stop transmission and send error
i2c_data.operating_state = I2C_IDLE;
SSP1CON2bits.PEN = 1;
i2c_data.master_status = I2C_MASTER_IDLE;
i2c_data.return_status = I2C_SEND_FAIL;
}
break;
}
// If we are in the middle of receiving data
} else if (i2c_data.master_status == I2C_MASTER_RECV) {
switch (i2c_data.operating_state) {
case I2C_IDLE:
break;
case I2C_SEND_ADDR:
// Send address with write bit set
i2c_data.operating_state = I2C_CHECK_ACK_RECV;
uint8_t tmp = (i2c_data.master_dest_addr << 1);
tmp |= 0x01;
SSP1BUF = tmp;
break;
case I2C_CHECK_ACK_RECV:
// Check if ACK is received
if (!SSP1CON2bits.ACKSTAT) {
// If an ACK is received, set module to receive 1 byte of data
i2c_data.operating_state = I2C_RCV_DATA;
SSP1CON2bits.RCEN = 1;
} else {
// If a NACK is received, stop transmission and send error
i2c_data.operating_state = I2C_IDLE;
SSP1CON2bits.PEN = 1;
i2c_data.master_status = I2C_MASTER_IDLE;
i2c_data.return_status = I2C_RECV_FAIL;
}
break;
case I2C_RCV_DATA:
// On receive, save byte into buffer
// TODO: Handle I2C buffer overflow
i2c_data.buffer_in[i2c_data.buffer_in_write_ind] = SSP1BUF;
i2c_data.buffer_in_write_ind++;
if (i2c_data.buffer_in_write_ind < i2c_data.buffer_in_len) {
// If we still need to read, send an ACK to the slave
i2c_data.operating_state = I2C_REQ_DATA;
SSP1CON2bits.ACKDT = 0; // ACK
SSP1CON2bits.ACKEN = 1;
} else {
// If we are done reading, send an NACK to the slave
i2c_data.operating_state = I2C_SEND_STOP;
SSP1CON2bits.ACKDT = 1; // NACK
SSP1CON2bits.ACKEN = 1;
}
break;
case I2C_REQ_DATA:
// Set module to receive one byte of data
i2c_data.operating_state = I2C_RCV_DATA;
SSP1CON2bits.RCEN = 1;
break;
case I2C_SEND_STOP:
// Send the stop bit and copy message to send to Main()
i2c_data.operating_state = I2C_IDLE;
SSP1CON2bits.PEN = 1;
i2c_data.master_status = I2C_MASTER_IDLE;
i2c_data.return_status = I2C_RECV_OK;
break;
}
} else if (i2c_data.master_status == I2C_MASTER_RESTART) {
switch (i2c_data.operating_state) {
case I2C_IDLE:
break;
case I2C_SEND_ADDR:
// Send the address with read bit set
i2c_data.operating_state = I2C_CHECK_ACK_SEND;
SSP1BUF = (i2c_data.master_dest_addr << 1) | 0x0;
break;
case I2C_CHECK_ACK_SEND:
// Check if ACK is received or not
if (!SSP1CON2bits.ACKSTAT) {
// If an ACK is received, send first byte of data
SSP1BUF = i2c_data.buffer_in[0];
i2c_data.operating_state = I2C_CHECK_ACK_RESTART;
} else {
// If a NACK is received, stop transmission and send error
i2c_data.operating_state = I2C_IDLE;
SSP1CON2bits.PEN = 1;
i2c_data.master_status = I2C_MASTER_IDLE;
i2c_data.return_status = I2C_SEND_FAIL;
}
break;
case I2C_CHECK_ACK_RESTART:
if (!SSP1CON2bits.ACKSTAT) {
SSP1CON2bits.RSEN = 1;
i2c_data.operating_state = I2C_SEND_ADDR_2;
} else {
// If a NACK is received, stop transmission and send error
i2c_data.operating_state = I2C_IDLE;
SSP1CON2bits.PEN = 1;
i2c_data.master_status = I2C_MASTER_IDLE;
i2c_data.return_status = I2C_SEND_FAIL;
}
break;
case I2C_SEND_ADDR_2:
// Send the address with read bit set
i2c_data.operating_state = I2C_CHECK_ACK_RECV;
uint8_t tmp = (i2c_data.master_dest_addr << 1);
tmp |= 0x01;
SSP1BUF = tmp;
break;
case I2C_CHECK_ACK_RECV:
// Check if ACK is received
if (!SSP1CON2bits.ACKSTAT) {
// If an ACK is received, set module to receive 1 byte of data
i2c_data.operating_state = I2C_RCV_DATA;
SSP1CON2bits.RCEN = 1;
} else {
// If a NACK is received, stop transmission and send error
i2c_data.operating_state = I2C_IDLE;
SSP1CON2bits.PEN = 1;
i2c_data.master_status = I2C_MASTER_IDLE;
i2c_data.return_status = I2C_RECV_FAIL;
}
break;
case I2C_RCV_DATA:
// On receive, save byte into buffer
// TODO: Handle I2C buffer overflow
i2c_data.buffer_in[i2c_data.buffer_in_write_ind] = SSP1BUF;
i2c_data.buffer_in_write_ind++;
if (i2c_data.buffer_in_write_ind < i2c_data.buffer_in_len) {
// If we still need to read, send an ACK to the slave
i2c_data.operating_state = I2C_REQ_DATA;
SSP1CON2bits.ACKDT = 0; // ACK
SSP1CON2bits.ACKEN = 1;
} else {
// If we are done reading, send an NACK to the slave
i2c_data.operating_state = I2C_SEND_STOP;
SSP1CON2bits.ACKDT = 1; // NACK
SSP1CON2bits.ACKEN = 1;
}
break;
case I2C_REQ_DATA:
// Set module to receive one byte of data
i2c_data.operating_state = I2C_RCV_DATA;
SSP1CON2bits.RCEN = 1;
break;
case I2C_SEND_STOP:
// Send the stop bit
i2c_data.operating_state = I2C_IDLE;
SSP1CON2bits.PEN = 1;
i2c_data.master_status = I2C_MASTER_IDLE;
i2c_data.return_status = I2C_RECV_OK;
break;
}
}
}
/* Returns 0 if I2C module is currently busy, otherwise returns status code */
uint8_t I2C1_Get_Status() {
// if (i2c_data.operating_mode == I2C_MODE_MASTER) {
if (i2c_data.master_status != I2C_MASTER_IDLE || i2c_data.buffer_in_len == 0) {
return 0;
} else {
return i2c_data.return_status;
}
}
uint8_t I2C1_Buffer_Len() {
return i2c_data.buffer_in_len;
}
/* Returns 0 if I2C module is currently busy, otherwise returns buffer length */
uint8_t I2C1_Read_Buffer(uint8_t *buffer) {
uint8_t i = 0;
while (i2c_data.buffer_in_len != 0) {
buffer[i] = i2c_data.buffer_in[i2c_data.buffer_in_read_ind];
i++;
if (i2c_data.buffer_in_read_ind == MAXI2C1BUF-1) {
i2c_data.buffer_in_read_ind = 0;
} else {
i2c_data.buffer_in_read_ind++;
}
i2c_data.buffer_in_len--;
}
return i;
}