0,0 → 1,448 |
#include "msg_queues.h" |
#include "maindefs.h" |
//#include <i2c.h> |
#include "i2c.h" |
|
static I2C_DATA *i2c_pdata; |
|
// Set up the data structures for the i2c code |
// Should be called once before any i2c routines are called |
void i2c_init(I2C_DATA *data) { |
i2c_pdata = data; |
i2c_pdata->buflen = 0; |
i2c_pdata->slave_event_count = 0; |
i2c_pdata->status = I2C_IDLE; |
i2c_pdata->slave_error_count = 0; |
i2c_pdata->bufind = 0; |
i2c_pdata->buflen = 0; |
i2c_pdata->slave_in_last_byte = 0; |
i2c_pdata->slave_sending_data = 0; |
i2c_pdata->slave_sending_blank_data = 0; |
i2c_pdata->mode = 0; |
i2c_pdata->master_dest_addr = 0; |
i2c_pdata->master_state = I2C_MASTER_IDLE; |
} |
|
// Setup the PIC to operate as a master. |
void i2c_configure_master() { |
i2c_pdata->mode = I2C_MODE_MASTER; |
|
TRISCbits.TRISC3 = 1; |
TRISCbits.TRISC4 = 1; |
|
SSPSTAT = 0x0; |
SSPCON1 = 0x0; |
SSPCON2 = 0x0; |
SSPCON1bits.SSPM = 0x8; // I2C Master Mode |
SSPADD = 0x77; // Operate at 100KHz (48MHz) |
SSPSTATbits.SMP = 1; // Disable Slew Rate Control |
SSPCON1bits.SSPEN = 1; // Enable MSSP Module |
} |
|
// Sends length number of bytes in msg to specified address (no R/W bit) |
void i2c_master_send(unsigned char address, unsigned char length, unsigned char *msg) { |
int i; |
if (length == 0) |
return; |
|
// Copy message to send into buffer and save length/address |
for (i = 0; i < length; i++) { |
i2c_pdata->buffer[i] = msg[i]; |
} |
i2c_pdata->buflen = length; |
i2c_pdata->master_dest_addr = address; |
i2c_pdata->bufind = 0; |
|
// Change status to 'next' operation |
i2c_pdata->status = I2C_SEND_ADDR; |
i2c_pdata->master_state = I2C_MASTER_SEND; |
|
// Generate start condition |
SSPCON2bits.SEN = 1; |
} |
|
// Reads length number of bytes from address (no R/W bit) |
void i2c_master_recv(unsigned char address, unsigned char length) { |
if (length == 0) |
return; |
|
// Save length and address to get data from |
i2c_pdata->buflen = length; |
i2c_pdata->master_dest_addr = address; |
i2c_pdata->bufind = 0; |
|
// Change status to 'next' operation |
i2c_pdata->status = I2C_SEND_ADDR; |
i2c_pdata->master_state = I2C_MASTER_RECV; |
|
// Generate start condition |
SSPCON2bits.SEN = 1; |
} |
|
// Setup the PIC to operate as a slave. The address must not include the R/W bit |
void i2c_configure_slave(unsigned char addr) { |
i2c_pdata->mode = I2C_MODE_SLAVE; |
|
// Ensure the two lines are set for input (we are a slave) |
TRISCbits.TRISC3 = 1; |
TRISCbits.TRISC4 = 1; |
|
SSPADD = addr << 1; // Set the slave address |
|
SSPSTAT = 0x0; |
SSPCON1 = 0x0; |
SSPCON2 = 0x0; |
SSPCON1bits.SSPM = 0xE; // Enable Slave 7-bit w/ start/stop interrupts |
SSPSTATbits.SMP = 1; // Slew Off |
SSPCON2bits.SEN = 1; // Enable clock-stretching |
SSPCON1bits.SSPEN = 1; // Enable MSSP Module |
} |
|
void i2c_interrupt_handler() { |
// Call interrupt depending on which mode we are operating in |
if (i2c_pdata->mode == I2C_MODE_MASTER) { |
i2c_interrupt_master(); |
} else if (i2c_pdata->mode == I2C_MODE_SLAVE) { |
i2c_interrupt_slave(); |
} |
} |
|
// An internal subroutine used in the master version of the i2c_interrupt_handler |
void i2c_interrupt_master() { |
// If we are in the middle of sending data |
if (i2c_pdata->master_state == I2C_MASTER_SEND) { |
switch (i2c_pdata->status) { |
case I2C_IDLE: |
break; |
case I2C_SEND_ADDR: |
// Send the address with read bit set |
i2c_pdata->status = I2C_CHECK_ACK; |
SSPBUF = (i2c_pdata->master_dest_addr << 1) | 0x0; |
break; |
case I2C_CHECK_ACK: |
// Check if ACK is received or not |
if (!SSPCON2bits.ACKSTAT) { |
// If an ACK is received, send next byte of data |
if (i2c_pdata->bufind < i2c_pdata->buflen) { |
SSPBUF = i2c_pdata->buffer[i2c_pdata->bufind]; |
i2c_pdata->bufind++; |
} else { |
// If no more data is to be sent, send stop bit |
i2c_pdata->status = I2C_IDLE; |
SSPCON2bits.PEN = 1; |
i2c_pdata->master_state = I2C_MASTER_IDLE; |
MQ_sendmsg_ToMainFromHigh(0, MSGTYPE_I2C_MASTER_SEND_COMPLETE, (void *) 0); |
} |
} else { |
// If a NACK is received, stop transmission and send error |
i2c_pdata->status = I2C_IDLE; |
SSPCON2bits.PEN = 1; |
i2c_pdata->master_state = I2C_MASTER_IDLE; |
MQ_sendmsg_ToMainFromHigh(0, MSGTYPE_I2C_MASTER_SEND_FAILED, (void *) 0); |
} |
break; |
} |
// If we are in the middle of receiving data |
} else if (i2c_pdata->master_state == I2C_MASTER_RECV) { |
switch (i2c_pdata->status) { |
case I2C_IDLE: |
break; |
case I2C_SEND_ADDR: |
// Send address with write bit set |
i2c_pdata->status = I2C_CHECK_ACK; |
SSPBUF = (i2c_pdata->master_dest_addr << 1) | 0x1; |
break; |
case I2C_CHECK_ACK: |
// Check if ACK is received |
if (!SSPCON2bits.ACKSTAT) { |
// If an ACK is received, set module to receive 1 byte of data |
i2c_pdata->status = I2C_RCV_DATA; |
SSPCON2bits.RCEN = 1; |
} else { |
// If a NACK is received, stop transmission and send error |
i2c_pdata->status = I2C_IDLE; |
SSPCON2bits.PEN = 1; |
i2c_pdata->master_state = I2C_MASTER_IDLE; |
MQ_sendmsg_ToMainFromHigh(0, MSGTYPE_I2C_MASTER_RECV_FAILED, (void *) 0); |
} |
break; |
case I2C_RCV_DATA: |
// On receive, save byte into buffer |
i2c_pdata->buffer[i2c_pdata->bufind] = SSPBUF; |
i2c_pdata->bufind++; |
if (i2c_pdata->bufind < i2c_pdata->buflen) { |
// If we still need to read, send an ACK to the slave |
i2c_pdata->status = I2C_REQ_DATA; |
SSPCON2bits.ACKDT = 0; // ACK |
SSPCON2bits.ACKEN = 1; |
} else { |
// If we are done reading, send an NACK to the slave |
i2c_pdata->status = I2C_SEND_STOP; |
SSPCON2bits.ACKDT = 1; // NACK |
SSPCON2bits.ACKEN = 1; |
} |
break; |
case I2C_REQ_DATA: |
// Set module to receive one byte of data |
i2c_pdata->status = I2C_RCV_DATA; |
SSPCON2bits.RCEN = 1; |
break; |
case I2C_SEND_STOP: |
// Send the stop bit and copy message to send to Main() |
i2c_pdata->status = I2C_IDLE; |
SSPCON2bits.PEN = 1; |
i2c_pdata->master_state = I2C_MASTER_IDLE; |
MQ_sendmsg_ToMainFromHigh(i2c_pdata->buflen, MSGTYPE_I2C_MASTER_RECV_COMPLETE, (void *) i2c_pdata->buffer); |
break; |
} |
} |
} |
|
// An internal subroutine used in the slave version of the i2c_interrupt_handler |
void i2c_handle_start(unsigned char data_read) { |
i2c_pdata->slave_event_count = 1; |
i2c_pdata->buflen = 0; |
|
// Check to see if we also got the address |
if (data_read) { |
if (SSPSTATbits.D_A == 1) { |
DBG_PRINT_I2C("I2C Start: (ERROR) no address recieved\r\n"); |
// This is bad because we got data and we wanted an address |
i2c_pdata->status = I2C_IDLE; |
i2c_pdata->slave_error_count++; |
i2c_pdata->slave_error_code = I2C_ERR_NOADDR; |
} else { |
if (SSPSTATbits.R_W == 1) { |
i2c_pdata->status = I2C_SEND_DATA; |
} else { |
i2c_pdata->status = I2C_RCV_DATA; |
} |
} |
} else { |
i2c_pdata->status = I2C_STARTED; |
} |
} |
|
void i2c_interrupt_slave() { |
unsigned char i2c_data; |
unsigned char data_read_from_buffer = 0; |
unsigned char data_written_to_buffer = 0; |
unsigned char msg_send_data_to_main = 0; |
unsigned char overrun_error = 0; |
unsigned char error_buf[3]; |
unsigned char msgtype = 0; |
|
// Clear SSPOV (overflow bit) |
if (SSPCON1bits.SSPOV == 1) { |
DBG_PRINT_I2C("I2C: overflow detected\r\n"); |
SSPCON1bits.SSPOV = 0; |
// We failed to read the buffer in time, so we know we |
// can't properly receive this message, just put us in the |
// a state where we are looking for a new message |
i2c_pdata->status = I2C_IDLE; |
overrun_error = 1; |
i2c_pdata->slave_error_count++; |
i2c_pdata->slave_error_code = I2C_ERR_OVERRUN; |
} |
|
// Read SPPxBUF if it is full |
if (SSPSTATbits.BF == 1) { |
i2c_data = SSPBUF; |
DBG_PRINT_I2C("I2C: data read from buffer: %x\r\n", SSPBUF); |
data_read_from_buffer = 1; |
} |
|
if (!overrun_error) { |
switch (i2c_pdata->status) { |
case I2C_IDLE: |
{ |
// Ignore anything except a start |
if (SSPSTATbits.S == 1) { |
i2c_handle_start(data_read_from_buffer); |
// If we see a slave read, then we need to handle it here |
if (i2c_pdata->status == I2C_SEND_DATA) { |
// Return the first byte (message id) |
SSPBUF = 0x3; |
} |
} |
break; |
} |
case I2C_STARTED: |
{ |
// In this case, we expect either an address or a stop bit |
if (SSPSTATbits.P == 1) { |
// We need to check to see if we also read an address (a message of length 0) |
i2c_pdata->slave_event_count++; |
if (data_read_from_buffer) { |
if (SSPSTATbits.D_A == 0) { |
msg_send_data_to_main = 1; |
} else { |
DBG_PRINT_I2C("I2C: (ERROR) no data recieved\r\n"); |
i2c_pdata->slave_error_count++; |
i2c_pdata->slave_error_code = I2C_ERR_NODATA; |
} |
} |
// Return to idle mode |
i2c_pdata->status = I2C_IDLE; |
} else if (data_read_from_buffer) { |
i2c_pdata->slave_event_count++; |
if (SSPSTATbits.D_A == 0) { |
if (SSPSTATbits.R_W == 0) { // Slave write |
i2c_pdata->status = I2C_RCV_DATA; |
} else { // Slave read |
i2c_pdata->status = I2C_SEND_DATA; |
// Return the first byte (message id) |
SSPBUF = 0x3; |
} |
} else { |
DBG_PRINT_I2C("I2C: (ERROR) no data recieved\r\n"); |
i2c_pdata->slave_error_count++; |
i2c_pdata->status = I2C_IDLE; |
i2c_pdata->slave_error_code = I2C_ERR_NODATA; |
} |
} |
break; |
} |
case I2C_SEND_DATA: |
{ |
// If we arnt current in the middle of sending data, check to see |
// if there is a message in the queue to send |
if (!i2c_pdata->slave_sending_data) { |
// Check the message type of the next message in queue |
msgtype = MQ_peek_FromMainToHigh(); |
if (msgtype != MSGTYPE_I2C_REPLY || msgtype == 0) { |
// If the message queue is empty or to another interrupt processor, return 0xFF |
DBG_PRINT_I2C("I2C: Returning 0xFF [%d:%d]\r\n", 0, i2c_pdata->slave_in_last_byte-1); |
SSPBUF = 0xFF; |
i2c_pdata->bufind = 1; |
i2c_pdata->slave_sending_data = 1; |
i2c_pdata->slave_sending_blank_data = 1; |
data_written_to_buffer = 1; |
} else { |
i2c_pdata->buflen = MQ_recvmsg_FromMainToHigh(MSGLEN, (unsigned char *)i2c_pdata->slave_outbufmsgtype, (void *) i2c_pdata->buffer); |
// DBG_PRINT_I2C("%x\r\n",i2c_ptr->buffer[0]); |
// DBG_PRINT_I2C("I2C: buffer Message Length: %d\r\n",i2c_ptr->outbuflen); |
if (i2c_pdata->buflen > 0) { |
// Otherwise return the first byte of data |
DBG_PRINT_I2C("I2C: Returning %x [%d,%d]\r\n", i2c_pdata->buffer[0], 0, i2c_pdata->buflen-1); |
SSPBUF = i2c_pdata->buffer[0]; |
i2c_pdata->bufind = 1; |
i2c_pdata->slave_sending_data = 1; |
data_written_to_buffer = 1; |
} else { |
DBG_PRINT_I2C("I2C: (ERROR) Unexpected msg in queue, type = %x\r\n", i2c_pdata->slave_outbufmsgtype); |
} |
} |
} else if (i2c_pdata->slave_sending_blank_data) { |
// If we are currently sending 0xFFs back, keep sending for the requested number of bytes |
if (i2c_pdata->bufind < i2c_pdata->slave_in_last_byte) { |
DBG_PRINT_I2C("I2C: Returning 0xFF [%d:%d]\r\n", i2c_pdata->bufind, i2c_pdata->slave_in_last_byte-1); |
SSPBUF = 0xFF; |
i2c_pdata->bufind++; |
data_written_to_buffer = 1; |
} else { |
// We have nothing left to send |
i2c_pdata->slave_sending_data = 0; |
i2c_pdata->slave_sending_blank_data = 0; |
i2c_pdata->status = I2C_IDLE; |
} |
} else { |
// Otherwise keep sending back the requested data |
if (i2c_pdata->bufind < i2c_pdata->buflen) { |
DBG_PRINT_I2C("I2C: Returning %x [%d,%d]\r\n", i2c_pdata->buffer[i2c_pdata->bufind], i2c_pdata->bufind, i2c_pdata->buflen-1); |
SSPBUF = i2c_pdata->buffer[i2c_pdata->bufind]; |
i2c_pdata->bufind++; |
data_written_to_buffer = 1; |
} else { |
// We have nothing left to send |
i2c_pdata->slave_sending_data = 0; |
i2c_pdata->status = I2C_IDLE; |
} |
} |
break; |
} |
case I2C_RCV_DATA: |
{ |
// We expect either data or a stop bit or a (if a restart, an addr) |
if (SSPSTATbits.P == 1) { |
// We need to check to see if we also read data |
i2c_pdata->slave_event_count++; |
if (data_read_from_buffer) { |
if (SSPSTATbits.D_A == 1) { |
i2c_pdata->buffer[i2c_pdata->buflen] = i2c_data; |
i2c_pdata->buflen++; |
msg_send_data_to_main = 1; |
} else { |
DBG_PRINT_I2C("I2C: (ERROR) no data recieved\r\n"); |
i2c_pdata->slave_error_count++; |
i2c_pdata->slave_error_code = I2C_ERR_NODATA; |
i2c_pdata->status = I2C_IDLE; |
} |
} else { |
msg_send_data_to_main = 1; |
} |
i2c_pdata->status = I2C_IDLE; |
} else if (data_read_from_buffer) { |
i2c_pdata->slave_event_count++; |
if (SSPSTATbits.D_A == 1) { |
i2c_pdata->buffer[i2c_pdata->buflen] = i2c_data; |
i2c_pdata->buflen++; |
} else /* a restart */ { |
if (SSPSTATbits.R_W == 1) { |
i2c_pdata->status = I2C_SEND_DATA; |
msg_send_data_to_main = 1; |
// Return the first byte (message id) |
SSPBUF = 0x3; |
|
} else { // Bad to recv an address again, we aren't ready |
DBG_PRINT_I2C("I2C: (ERROR) no data recieved\r\n"); |
i2c_pdata->slave_error_count++; |
i2c_pdata->slave_error_code = I2C_ERR_NODATA; |
i2c_pdata->status = I2C_IDLE; |
} |
} |
} |
break; |
} |
} |
} |
|
// Release the clock stretching bit (if we should) |
if (data_read_from_buffer || data_written_to_buffer) { |
// Release the clock |
if (SSPCON1bits.CKP == 0) { |
SSPCON1bits.CKP = 1; |
} |
} |
|
// Must check if the message is too long |
if ((i2c_pdata->buflen > MAXI2CBUF - 2) && (!msg_send_data_to_main)) { |
DBG_PRINT_I2C("I2C: (ERROR) message too long\r\n"); |
i2c_pdata->status = I2C_IDLE; |
i2c_pdata->slave_error_count++; |
i2c_pdata->slave_error_code = I2C_ERR_MSGTOOLONG; |
} |
|
if (msg_send_data_to_main) { |
DBG_PRINT_I2C("I2C: sending message to main()\r\n"); |
i2c_pdata->slave_in_last_byte = i2c_pdata->buffer[i2c_pdata->buflen-1]; |
i2c_pdata->buffer[i2c_pdata->buflen] = i2c_pdata->slave_event_count; |
MQ_sendmsg_ToMainFromHigh(i2c_pdata->buflen + 1, MSGTYPE_I2C_DATA, (void *) i2c_pdata->buffer); |
i2c_pdata->buflen = 0; |
} else if (i2c_pdata->slave_error_count >= I2C_ERR_THRESHOLD) { |
DBG_PRINT_I2C("I2C: (ERROR) error threshold passed\r\n"); |
error_buf[0] = i2c_pdata->slave_error_count; |
error_buf[1] = i2c_pdata->slave_error_code; |
error_buf[2] = i2c_pdata->slave_event_count; |
MQ_sendmsg_ToMainFromHigh(sizeof (unsigned char) *3, MSGTYPE_I2C_DBG, (void *) error_buf); |
i2c_pdata->slave_error_count = 0; |
} |
} |
|
unsigned char i2c_master_busy() { |
if (i2c_pdata->master_state == I2C_MASTER_IDLE) { |
return 0; |
} else { |
return 1; |
} |
} |