Rev 157 | Blame | Last modification | View Log | Download | RSS feed
#include <xc.h>#include <stdio.h>#include <string.h>#include "defines.h"#include "base_I2C.h"#include "base_UART.h"static I2C_DATA *i2c_data_p;// Set up the data structures for the base_I2C.code// Should be called once before any i2c routines are calledvoid I2C_Init(I2C_DATA *data) {i2c_data_p = data;i2c_data_p->buffer_in_len = 0;i2c_data_p->buffer_in_len_tmp = 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_mode = 0;i2c_data_p->operating_state = I2C_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 = I2C_MASTER_IDLE;// Enable I2C interruptPIE1bits.SSPIE = 1;}// Setup the PIC to operate as a master.void I2C_Configure_Master(char speed) {i2c_data_p->operating_mode = I2C_MODE_MASTER;I2C_CLK_TRIS = 1;I2C_DAT_TRIS = 1;SSPSTAT = 0x0;SSPCON1 = 0x0;SSPCON2 = 0x0;SSPCON1bits.SSPM = 0x8; // I2C Master Modeif (speed) {SSPADD = 0x74; // Operate at 100KHz (48MHz)} else {SSPADD = 0x1A; // Operate at 400KHz (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(char address, char length, char *msg) {char 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 = I2C_SEND_ADDR;i2c_data_p->master_status = I2C_MASTER_SEND;// Generate start conditionSSPCON2bits.SEN = 1;}// Reads length number of bytes from address (no R/W bit)void I2C_Master_Recv(char address, char 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 = I2C_SEND_ADDR;i2c_data_p->master_status = I2C_MASTER_RECV;// Generate start conditionSSPCON2bits.SEN = 1;}// Writes msg to address then reads length number of bytes from addressvoid I2C_Master_Restart(char address, char msg, char length) {char c;if (length == 0) {c = msg;I2C_Master_Send(address, 1, &c);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 = I2C_SEND_ADDR;i2c_data_p->master_status = I2C_MASTER_RESTART;// 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(char addr) {i2c_data_p->operating_mode = I2C_MODE_SLAVE;// Ensure the two lines are set for input (we are a slave)I2C_CLK_TRIS = 1;I2C_DAT_TRIS = 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_data_p->operating_mode == I2C_MODE_MASTER) {I2C_Interrupt_Master();} else if (i2c_data_p->operating_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_data_p->master_status == I2C_MASTER_SEND) {switch (i2c_data_p->operating_state) {case I2C_IDLE:break;case I2C_SEND_ADDR:// Send the address with read bit seti2c_data_p->operating_state = I2C_CHECK_ACK_SEND;SSPBUF = (i2c_data_p->master_dest_addr << 1) | 0x0;break;case I2C_CHECK_ACK_SEND:// Check if ACK is received or notif (!SSPCON2bits.ACKSTAT) {// If an ACK is received, send next byte of dataif (i2c_data_p->buffer_in_read_ind < i2c_data_p->buffer_in_len) {SSPBUF = 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 = I2C_IDLE;SSPCON2bits.PEN = 1;i2c_data_p->master_status = I2C_MASTER_IDLE;i2c_data_p->return_status = I2C_SEND_OK;}} else {// If a NACK is received, stop transmission and send errori2c_data_p->operating_state = I2C_IDLE;SSPCON2bits.PEN = 1;i2c_data_p->master_status = I2C_MASTER_IDLE;i2c_data_p->return_status = I2C_SEND_FAIL;}break;}// If we are in the middle of receiving data} else if (i2c_data_p->master_status == I2C_MASTER_RECV) {switch (i2c_data_p->operating_state) {case I2C_IDLE:break;case I2C_SEND_ADDR:// Send address with write bit seti2c_data_p->operating_state = I2C_CHECK_ACK_RECV;SSPBUF = (i2c_data_p->master_dest_addr << 1) | 0x1;break;case I2C_CHECK_ACK_RECV:// Check if ACK is receivedif (!SSPCON2bits.ACKSTAT) {// If an ACK is received, set module to receive 1 byte of datai2c_data_p->operating_state = I2C_RCV_DATA;SSPCON2bits.RCEN = 1;} else {// If a NACK is received, stop transmission and send errori2c_data_p->operating_state = I2C_IDLE;SSPCON2bits.PEN = 1;i2c_data_p->master_status = I2C_MASTER_IDLE;i2c_data_p->return_status = I2C_RECV_FAIL;}break;case I2C_RCV_DATA:// On receive, save byte into buffer// TODO: Handle I2C buffer overflowi2c_data_p->buffer_in[i2c_data_p->buffer_in_write_ind] = SSPBUF;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 = I2C_REQ_DATA;SSPCON2bits.ACKDT = 0; // ACKSSPCON2bits.ACKEN = 1;} else {// If we are done reading, send an NACK to the slavei2c_data_p->operating_state = I2C_SEND_STOP;SSPCON2bits.ACKDT = 1; // NACKSSPCON2bits.ACKEN = 1;}break;case I2C_REQ_DATA:// Set module to receive one byte of datai2c_data_p->operating_state = I2C_RCV_DATA;SSPCON2bits.RCEN = 1;break;case I2C_SEND_STOP:// Send the stop bit and copy message to send to Main()i2c_data_p->operating_state = I2C_IDLE;SSPCON2bits.PEN = 1;i2c_data_p->master_status = I2C_MASTER_IDLE;i2c_data_p->return_status = I2C_RECV_OK;break;}} else if (i2c_data_p->master_status == I2C_MASTER_RESTART) {switch (i2c_data_p->operating_state) {case I2C_IDLE:break;case I2C_SEND_ADDR:// Send the address with read bit seti2c_data_p->operating_state = I2C_CHECK_ACK_SEND;SSPBUF = (i2c_data_p->master_dest_addr << 1) | 0x0;break;case I2C_CHECK_ACK_SEND:// Check if ACK is received or notif (!SSPCON2bits.ACKSTAT) {// If an ACK is received, send first byte of dataSSPBUF = i2c_data_p->buffer_in[0];i2c_data_p->operating_state = I2C_CHECK_ACK_RESTART;} else {// If a NACK is received, stop transmission and send errori2c_data_p->operating_state = I2C_IDLE;SSPCON2bits.PEN = 1;i2c_data_p->master_status = I2C_MASTER_IDLE;i2c_data_p->return_status = I2C_SEND_FAIL;}break;case I2C_CHECK_ACK_RESTART:if (!SSPCON2bits.ACKSTAT) {SSPCON2bits.RSEN = 1;i2c_data_p->operating_state = I2C_SEND_ADDR_2;} else {// If a NACK is received, stop transmission and send errori2c_data_p->operating_state = I2C_IDLE;SSPCON2bits.PEN = 1;i2c_data_p->master_status = I2C_MASTER_IDLE;i2c_data_p->return_status = I2C_SEND_FAIL;}break;case I2C_SEND_ADDR_2:// Send the address with read bit seti2c_data_p->operating_state = I2C_CHECK_ACK_RECV;SSPBUF = (i2c_data_p->master_dest_addr << 1) | 0x1;break;case I2C_CHECK_ACK_RECV:// Check if ACK is receivedif (!SSPCON2bits.ACKSTAT) {// If an ACK is received, set module to receive 1 byte of datai2c_data_p->operating_state = I2C_RCV_DATA;SSPCON2bits.RCEN = 1;} else {// If a NACK is received, stop transmission and send errori2c_data_p->operating_state = I2C_IDLE;SSPCON2bits.PEN = 1;i2c_data_p->master_status = I2C_MASTER_IDLE;i2c_data_p->return_status = I2C_RECV_FAIL;}break;case I2C_RCV_DATA:// On receive, save byte into buffer// TODO: Handle I2C buffer overflowi2c_data_p->buffer_in[i2c_data_p->buffer_in_write_ind] = SSPBUF;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 = I2C_REQ_DATA;SSPCON2bits.ACKDT = 0; // ACKSSPCON2bits.ACKEN = 1;} else {// If we are done reading, send an NACK to the slavei2c_data_p->operating_state = I2C_SEND_STOP;SSPCON2bits.ACKDT = 1; // NACKSSPCON2bits.ACKEN = 1;}break;case I2C_REQ_DATA:// Set module to receive one byte of datai2c_data_p->operating_state = I2C_RCV_DATA;SSPCON2bits.RCEN = 1;break;case I2C_SEND_STOP:// Send the stop bit and copy message to send to Main()i2c_data_p->operating_state = I2C_IDLE;SSPCON2bits.PEN = 1;i2c_data_p->master_status = I2C_MASTER_IDLE;i2c_data_p->return_status = I2C_RECV_OK;break;}}}void I2C_Interrupt_Slave() {char received_data;char data_read_from_buffer = 0;char data_written_to_buffer = 0;char overrun_error = 0;char output[64];// Clear SSPOV (overflow bit)if (SSPCON1bits.SSPOV == 1) {sprintf(output, "I2C: (ERROR) overflow detectedr\r\n");DBG_PRINT_I2C(output, strlen(output));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_data_p->operating_state = I2C_IDLE;overrun_error = 1;i2c_data_p->return_status = I2C_ERR_OVERRUN;}// Read SPPxBUF if it is fullif (SSPSTATbits.BF == 1) {received_data = SSPBUF;// DBG_PRINT_I2C("I2C: data read from buffer: %x\r\n", SSPBUF);data_read_from_buffer = 1;}if (!overrun_error) {switch (i2c_data_p->operating_state) {case I2C_IDLE:{// Ignore anything except a startif (SSPSTATbits.S == 1) {i2c_data_p->buffer_in_len_tmp = 0;i2c_data_p->operating_state = I2C_STARTED;// if (data_read_from_buffer) {// 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_data_p->operating_state = I2C_IDLE;// i2c_data_p->return_status = I2C_ERR_NOADDR;// } else {// // Determine if we are sending or receiving data// if (SSPSTATbits.R_W == 1) {// i2c_data_p->operating_state = I2C_SEND_DATA;// } else {// i2c_data_p->operating_state = I2C_RCV_DATA;// }// }// } else {// i2c_data_p->operating_state = I2C_STARTED;// }}break;}case I2C_STARTED:{// In this case, we expect either an address or a stop bitif (SSPSTATbits.P == 1) {// Return to idle modei2c_data_p->operating_state = I2C_IDLE;} else if (data_read_from_buffer) {if (SSPSTATbits.D_A == 0) {// Address receivedif (SSPSTATbits.R_W == 0) {// Slave write modei2c_data_p->operating_state = I2C_RCV_DATA;} else {// Slave read modei2c_data_p->operating_state = I2C_SEND_DATA;// Process the first byte immediatly if sending datagoto send;}} else {sprintf(output, "I2C: (ERROR) no data recieved\r\n");DBG_PRINT_I2C(output, strlen(output));i2c_data_p->operating_state = I2C_IDLE;i2c_data_p->return_status = I2C_ERR_NODATA;}}break;}send:case I2C_SEND_DATA:{if (!i2c_data_p->slave_sending_data) {// If we are not currently sending data, figure out what to reply withif (I2C_Process_Send(i2c_data_p->slave_in_last_byte)) {// Data exists to be returned, send first byteSSPBUF = i2c_data_p->buffer_out[0];i2c_data_p->buffer_out_ind = 1;i2c_data_p->slave_sending_data = 1;data_written_to_buffer = 1;} else {// Unknown requesti2c_data_p->slave_sending_data = 0;i2c_data_p->operating_state = I2C_IDLE;}} else {// Sending remaining data back to masterif (i2c_data_p->buffer_out_ind < i2c_data_p->buffer_out_len) {SSPBUF = i2c_data_p->buffer_out[i2c_data_p->buffer_out_ind];i2c_data_p->buffer_out_ind++;data_written_to_buffer = 1;} else {// Nothing left to sendi2c_data_p->slave_sending_data = 0;i2c_data_p->operating_state = 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) {// Stop bit detected, we need to check to see if we also read dataif (data_read_from_buffer) {if (SSPSTATbits.D_A == 1) {// Data received with stop bit// TODO: Handle I2C buffer overflowi2c_data_p->buffer_in[i2c_data_p->buffer_in_write_ind] = received_data;if (i2c_data_p->buffer_in_write_ind == MAXI2CBUF-1) {i2c_data_p->buffer_in_write_ind = 0;} else {i2c_data_p->buffer_in_write_ind++;}i2c_data_p->buffer_in_len_tmp++;// Save the last byte receivedi2c_data_p->slave_in_last_byte = received_data;i2c_data_p->return_status = I2C_DATA_AVAL;} else {sprintf(output, "I2C: (ERROR) no data recieved\r\n");DBG_PRINT_I2C(output, strlen(output));i2c_data_p->operating_state = I2C_IDLE;i2c_data_p->return_status = I2C_ERR_NODATA;}}i2c_data_p->buffer_in_len += i2c_data_p->buffer_in_len_tmp;i2c_data_p->operating_state = I2C_IDLE;} else if (data_read_from_buffer) {if (SSPSTATbits.D_A == 1) {// Data receivedi2c_data_p->buffer_in[i2c_data_p->buffer_in_write_ind] = received_data;if (i2c_data_p->buffer_in_write_ind == MAXI2CBUF-1) {i2c_data_p->buffer_in_write_ind = 0;} else {i2c_data_p->buffer_in_write_ind++;}i2c_data_p->buffer_in_len_tmp++;// Save the last byte receivedi2c_data_p->slave_in_last_byte = received_data;i2c_data_p->return_status = I2C_DATA_AVAL;} else {// Restart bit detectedif (SSPSTATbits.R_W == 1) {i2c_data_p->buffer_in_len += i2c_data_p->buffer_in_len_tmp;i2c_data_p->operating_state = I2C_SEND_DATA;// Process the first byte immediatly if sending datagoto send;} else {// Bad to recv an address again, we aren't readysprintf(output, "I2C: (ERROR) no data recieved\r\n");DBG_PRINT_I2C(output, strlen(output));i2c_data_p->operating_state = I2C_IDLE;i2c_data_p->return_status = I2C_ERR_NODATA;}}}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;}}}/* Returns 0 if I2C module is currently busy, otherwise returns status code */char I2C_Get_Status() {if (i2c_data_p->operating_mode == I2C_MODE_MASTER) {if (i2c_data_p->master_status != I2C_MASTER_IDLE || i2c_data_p->buffer_in_len == 0) {return 0;} else {return i2c_data_p->return_status;}} else {if (i2c_data_p->operating_state != I2C_IDLE || i2c_data_p->buffer_in_len == 0) {return 0;} else {return i2c_data_p->return_status;}}}char I2C_Buffer_Len() {return i2c_data_p->buffer_in_len;}/* Returns 0 if I2C module is currently busy, otherwise returns buffer length */char I2C_Read_Buffer(char *buffer) {char 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 == MAXI2CBUF-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 */char I2C_Process_Send(char c) {char ret = 0;switch (c) {case 0xAA:i2c_data_p->buffer_out[0] = 'A';i2c_data_p->buffer_out_len = 1;ret = 1;break;case 0xBB:i2c_data_p->buffer_out[0] = '1';i2c_data_p->buffer_out[1] = '2';i2c_data_p->buffer_out_len = 2;ret = 1;break;}return ret;}