Blame | Last modification | View Log | Download | RSS feed
#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 calledvoid 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 ModeSSPADD = 0x77; // Operate at 100KHz (48MHz)SSPSTATbits.SMP = 1; // Disable Slew Rate ControlSSPCON1bits.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/addressfor (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' operationi2c_pdata->status = I2C_SEND_ADDR;i2c_pdata->master_state = I2C_MASTER_SEND;// Generate start conditionSSPCON2bits.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 fromi2c_pdata->buflen = length;i2c_pdata->master_dest_addr = address;i2c_pdata->bufind = 0;// Change status to 'next' operationi2c_pdata->status = I2C_SEND_ADDR;i2c_pdata->master_state = I2C_MASTER_RECV;// Generate start conditionSSPCON2bits.SEN = 1;}// Setup the PIC to operate as a slave. The address must not include the R/W bitvoid 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 addressSSPSTAT = 0x0;SSPCON1 = 0x0;SSPCON2 = 0x0;SSPCON1bits.SSPM = 0xE; // Enable Slave 7-bit w/ start/stop interruptsSSPSTATbits.SMP = 1; // Slew OffSSPCON2bits.SEN = 1; // Enable clock-stretchingSSPCON1bits.SSPEN = 1; // Enable MSSP Module}void i2c_interrupt_handler() {// Call interrupt depending on which mode we are operating inif (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_handlervoid i2c_interrupt_master() {// If we are in the middle of sending dataif (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 seti2c_pdata->status = I2C_CHECK_ACK;SSPBUF = (i2c_pdata->master_dest_addr << 1) | 0x0;break;case I2C_CHECK_ACK:// Check if ACK is received or notif (!SSPCON2bits.ACKSTAT) {// If an ACK is received, send next byte of dataif (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 biti2c_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 errori2c_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 seti2c_pdata->status = I2C_CHECK_ACK;SSPBUF = (i2c_pdata->master_dest_addr << 1) | 0x1;break;case I2C_CHECK_ACK:// Check if ACK is receivedif (!SSPCON2bits.ACKSTAT) {// If an ACK is received, set module to receive 1 byte of datai2c_pdata->status = I2C_RCV_DATA;SSPCON2bits.RCEN = 1;} else {// If a NACK is received, stop transmission and send errori2c_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 bufferi2c_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 slavei2c_pdata->status = I2C_REQ_DATA;SSPCON2bits.ACKDT = 0; // ACKSSPCON2bits.ACKEN = 1;} else {// If we are done reading, send an NACK to the slavei2c_pdata->status = I2C_SEND_STOP;SSPCON2bits.ACKDT = 1; // NACKSSPCON2bits.ACKEN = 1;}break;case I2C_REQ_DATA:// Set module to receive one byte of datai2c_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_handlervoid 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 addressif (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 addressi2c_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 messagei2c_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 fullif (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 startif (SSPSTATbits.S == 1) {i2c_handle_start(data_read_from_buffer);// If we see a slave read, then we need to handle it hereif (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 bitif (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 modei2c_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 writei2c_pdata->status = I2C_RCV_DATA;} else { // Slave readi2c_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 sendif (!i2c_pdata->slave_sending_data) {// Check the message type of the next message in queuemsgtype = MQ_peek_FromMainToHigh();if (msgtype != MSGTYPE_I2C_REPLY || msgtype == 0) {// If the message queue is empty or to another interrupt processor, return 0xFFDBG_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 dataDBG_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 bytesif (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 sendi2c_pdata->slave_sending_data = 0;i2c_pdata->slave_sending_blank_data = 0;i2c_pdata->status = I2C_IDLE;}} else {// Otherwise keep sending back the requested dataif (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 sendi2c_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 datai2c_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 readyDBG_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 clockif (SSPCON1bits.CKP == 0) {SSPCON1bits.CKP = 1;}}// Must check if the message is too longif ((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;}}