Rev 281 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | 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 calledvoid 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 interruptPIE1bits.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 Modeif (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/addressfor (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' operationi2c_data.operating_state = I2C_SEND_ADDR;i2c_data.master_status = I2C_MASTER_SEND;// Generate start conditionSSP1CON2bits.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 fromi2c_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' operationi2c_data.operating_state = I2C_SEND_ADDR;i2c_data.master_status = I2C_MASTER_RECV;// Generate start conditionSSP1CON2bits.SEN = 1;}// Writes msg to address then reads length number of bytes from addressvoid 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 fromi2c_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' operationi2c_data.operating_state = I2C_SEND_ADDR;i2c_data.master_status = I2C_MASTER_RESTART;// Generate start conditionSSP1CON2bits.SEN = 1;}void I2C1_Interrupt_Handler() {// If we are in the middle of sending dataif (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 seti2c_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 notif (!SSP1CON2bits.ACKSTAT) {// If an ACK is received, send next byte of dataif (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 biti2c_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 errori2c_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 seti2c_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 receivedif (!SSP1CON2bits.ACKSTAT) {// If an ACK is received, set module to receive 1 byte of datai2c_data.operating_state = I2C_RCV_DATA;SSP1CON2bits.RCEN = 1;} else {// If a NACK is received, stop transmission and send errori2c_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 overflowi2c_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 slavei2c_data.operating_state = I2C_REQ_DATA;SSP1CON2bits.ACKDT = 0; // ACKSSP1CON2bits.ACKEN = 1;} else {// If we are done reading, send an NACK to the slavei2c_data.operating_state = I2C_SEND_STOP;SSP1CON2bits.ACKDT = 1; // NACKSSP1CON2bits.ACKEN = 1;}break;case I2C_REQ_DATA:// Set module to receive one byte of datai2c_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 seti2c_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 notif (!SSP1CON2bits.ACKSTAT) {// If an ACK is received, send first byte of dataSSP1BUF = i2c_data.buffer_in[0];i2c_data.operating_state = I2C_CHECK_ACK_RESTART;} else {// If a NACK is received, stop transmission and send errori2c_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 errori2c_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 seti2c_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 receivedif (!SSP1CON2bits.ACKSTAT) {// If an ACK is received, set module to receive 1 byte of datai2c_data.operating_state = I2C_RCV_DATA;SSP1CON2bits.RCEN = 1;} else {// If a NACK is received, stop transmission and send errori2c_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 overflowi2c_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 slavei2c_data.operating_state = I2C_REQ_DATA;SSP1CON2bits.ACKDT = 0; // ACKSSP1CON2bits.ACKEN = 1;} else {// If we are done reading, send an NACK to the slavei2c_data.operating_state = I2C_SEND_STOP;SSP1CON2bits.ACKDT = 1; // NACKSSP1CON2bits.ACKEN = 1;}break;case I2C_REQ_DATA:// Set module to receive one byte of datai2c_data.operating_state = I2C_RCV_DATA;SSP1CON2bits.RCEN = 1;break;case I2C_SEND_STOP:// Send the stop biti2c_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;}