272 lines
11 KiB
C++
272 lines
11 KiB
C++
/** @file i2c.cpp
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include <xc.h>
|
|
#include "i2c.hpp"
|
|
/**
|
|
* @fn i2c::i2c
|
|
* @param speed the requested I2C bus speed
|
|
* @param pclk the speed of the peripheral clock
|
|
*
|
|
* This constructor takes the requested bus speed and the peripheral
|
|
* clock speed, and calculates the appropriate baud rate divisor for
|
|
* I2C2BRG. Then it enables the I2C module.
|
|
*/
|
|
i2c::i2c(uint32_t speed, uint32_t pclk) {
|
|
double baud = (((1.0 / (2.0 * (double) speed)) - 104E-9) * (double) pclk) - 2.0;
|
|
int i_baud = (int) (baud + 0.5); // cast/round/truncate/whatever
|
|
I2C2BRG = i_baud; // set baud rate
|
|
I2C2CONbits.ON = 1; // enable I2C module
|
|
//I2C2CONbits.I2CEN = 1;
|
|
}
|
|
/**
|
|
* @fn i2c::transact
|
|
* @brief Function for all I2C transactions
|
|
* @param addr the 7-bit peripheral address
|
|
* @param send buffer for data to be sent, can be NULL
|
|
* @param recv buffer for data to be received, can be NULL
|
|
* @param size number of bytes to send to received
|
|
* @param mode which transactMode to use
|
|
* @param memAddr optional memory address, only used when communicating with
|
|
* EEPROMs, can be NULL if not needed. This is a pointer to the variable
|
|
* holding the memory address. This is done so that it can be NULL
|
|
* when not needed.
|
|
* @return a status code, not fully implemented
|
|
*
|
|
* Note that the address needs to be sent in 7-bit mode
|
|
* Note also that null pointers can be used when a mode
|
|
* doesn't require use of one of the pointers
|
|
* Mode options:
|
|
* 0: send single byte
|
|
* 1: send multiple bytes
|
|
* 2: receive single byte
|
|
* 3: receive multiple bytes
|
|
* 4: test mode
|
|
*
|
|
* Status byte codes:
|
|
* 1: NACK on address
|
|
* 2: NACK on data
|
|
*/
|
|
uint8_t i2c::transact(uint8_t addr, uint8_t *send, uint8_t *recv, \
|
|
uint8_t size, transactMode mode, uint16_t *memAddr) {
|
|
|
|
uint8_t fullAddress, status = 0;
|
|
uint8_t loopVar;
|
|
const int max = 255;
|
|
const int prime = 317;
|
|
switch (mode) {
|
|
case SINGLE_SEND: // Single send
|
|
fullAddress = addr << 1; // we are writing, thus R/W = 0
|
|
while (I2C2STATbits.TRSTAT); // wait for any transmission to finish
|
|
I2C2CONbits.SEN = 1; // send a start
|
|
while (I2C2CONbits.SEN); // wait for start to complete
|
|
I2C2TRN = fullAddress; // send address
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
if (I2C2STATbits.ACKSTAT == 0) {
|
|
// acknowledge received
|
|
} else {
|
|
status |= 1; // NACK on address
|
|
}
|
|
if(memAddr != NULL) {
|
|
// Send memory address
|
|
I2C2TRN = (uint8_t)(*memAddr >> 8);
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
I2C2TRN = (uint8_t)(*memAddr & 0x00FF);
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
}
|
|
if(send != NULL) {
|
|
// Now send the actual data
|
|
I2C2TRN = *send; // load buffer with data
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
if (I2C2STATbits.ACKSTAT == 0) {
|
|
// acknowledge received
|
|
} else {
|
|
status |= 2; // NACK on data
|
|
}
|
|
} else {
|
|
status |= NULL_ERROR;
|
|
}
|
|
I2C2CONbits.PEN = 1; // send STOP
|
|
while (I2C2CONbits.PEN); // wait for stop to complete
|
|
break;
|
|
case MULTIPLE_SEND: // Multiple send
|
|
fullAddress = addr << 1; // we are writing, thus R/W = 0
|
|
while (I2C2STATbits.TRSTAT); // wait for any transmission to finish
|
|
I2C2CONbits.SEN = 1; // send a start
|
|
while (I2C2CONbits.SEN); // wait for start to complete
|
|
I2C2TRN = fullAddress; // send address
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
if (I2C2STATbits.ACKSTAT == 0) {
|
|
// acknowledge received
|
|
} else {
|
|
status |= 1; // NACK on address
|
|
}
|
|
if(memAddr != NULL) {
|
|
// Send memory address
|
|
I2C2TRN = (uint8_t)(*memAddr >> 8);
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
I2C2TRN = (uint8_t)(*memAddr & 0x00FF);
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
}
|
|
if(send != NULL) { // guard against NULL pointer
|
|
// Now send the actual data
|
|
for (loopVar = size; loopVar > 0; loopVar--) { // do until size hits zero
|
|
I2C2TRN = *send; // load buffer with (next) data
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
if (I2C2STATbits.ACKSTAT == 0) {
|
|
// acknowledge received
|
|
} else {
|
|
status |= 2; // NACK on data
|
|
}
|
|
send++; // good old pointer increment... hope it works!
|
|
}
|
|
} else {
|
|
status |= NULL_ERROR;
|
|
}
|
|
I2C2CONbits.PEN = 1; // send STOP
|
|
while (I2C2CONbits.PEN); // wait for stop to complete
|
|
|
|
break;
|
|
case SINGLE_RECV: // Single receive
|
|
if(memAddr != NULL || send != NULL) {
|
|
fullAddress = addr << 1; // we are writing, thus R/W = 0
|
|
while (I2C2STATbits.TRSTAT); // wait for any transmission to finish
|
|
I2C2CONbits.SEN = 1; // send a start
|
|
while (I2C2CONbits.SEN); // wait for start to complete
|
|
I2C2TRN = fullAddress; // send address
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
if (I2C2STATbits.ACKSTAT == 0) {
|
|
// acknowledge received
|
|
} else {
|
|
status |= 1; // NACK on address
|
|
}
|
|
}
|
|
if(memAddr != NULL) {
|
|
// Send memory address
|
|
I2C2TRN = (uint8_t)(*memAddr >> 8);
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
I2C2TRN = (uint8_t)(*memAddr & 0x00FF);
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
}
|
|
// Now send the actual data
|
|
if(send != NULL) {
|
|
I2C2TRN = *send; // load buffer with data
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
if (I2C2STATbits.ACKSTAT == 0) {
|
|
// acknowledge received
|
|
} else {
|
|
status |= DATA_NACK; // NACK on data
|
|
}
|
|
}
|
|
if(recv != NULL) { // guard against NULL pointers
|
|
// RECEIVE SECTION
|
|
I2C2CONbits.RSEN = 1; // send restart
|
|
while (I2C2CONbits.RSEN); // wait for restart
|
|
fullAddress = (addr << 1) | 0x01; // address is now for reading
|
|
I2C2TRN = fullAddress; // send address
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
if (I2C2STATbits.ACKSTAT == 0) {
|
|
// acknowledge received
|
|
} else {
|
|
status |= ADDR_NACK; // NACK on address
|
|
}
|
|
I2C2CONbits.RCEN = 1; // enable receiever
|
|
while (!I2C2STATbits.RBF); // wait for 8 bits to be receieved
|
|
*recv = I2C2RCV;
|
|
I2C2CONbits.ACKDT = 1; // change to NACK
|
|
I2C2CONbits.ACKEN = 1; // send ACKDT
|
|
while (I2C2CONbits.ACKEN); // wait for NACK to send
|
|
} else {
|
|
status |= NULL_ERROR;
|
|
}
|
|
|
|
I2C2CONbits.PEN = 1; // send STOP
|
|
while (I2C2CONbits.PEN); // wait for stop to complete
|
|
break;
|
|
case MULTIPLE_RECV: // Multiple receive
|
|
if(memAddr != NULL || send != NULL) { // only send the write address if needed
|
|
fullAddress = addr << 1; // we are writing, thus R/W = 0
|
|
while (I2C2STATbits.TRSTAT); // wait for any transmission to finish
|
|
I2C2CONbits.SEN = 1; // send a start
|
|
while (I2C2CONbits.SEN); // wait for start to complete
|
|
I2C2TRN = fullAddress; // send address
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
if (I2C2STATbits.ACKSTAT == 0) {
|
|
// acknowledge received
|
|
} else {
|
|
status |= 1; // NACK on address
|
|
}
|
|
}
|
|
if(memAddr != NULL) {
|
|
// Send memory address
|
|
I2C2TRN = (uint8_t)(*memAddr >> 8);
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
I2C2TRN = (uint8_t)(*memAddr & 0x00FF);
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
}
|
|
if(send != NULL) {
|
|
// Now send the actual data
|
|
I2C2TRN = *send; // load buffer with data
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
if (I2C2STATbits.ACKSTAT == 0) {
|
|
// acknowledge received
|
|
} else {
|
|
status |= 2; // NACK on data
|
|
}
|
|
}
|
|
if(recv != NULL) {
|
|
// RECEIVE SECTION
|
|
I2C2CONbits.RSEN = 1; // send restart
|
|
while (I2C2CONbits.RSEN); // wait for restart
|
|
fullAddress = (addr << 1) | 0x01; // address is now for reading
|
|
I2C2TRN = fullAddress; // send address
|
|
while (I2C2STATbits.TRSTAT); // wait for transmission
|
|
if (I2C2STATbits.ACKSTAT == 0) {
|
|
// acknowledge received
|
|
} else {
|
|
status |= 1; // NACK on address
|
|
}
|
|
for (loopVar = size; loopVar > 0; loopVar--) {
|
|
I2C2CONbits.RCEN = 1; // enable receiever
|
|
while (!I2C2STATbits.RBF); // wait for 8 bits to be receieved
|
|
*recv = I2C2RCV;
|
|
recv++;
|
|
if (loopVar > 1) {
|
|
I2C2CONbits.ACKDT = 0; // ACK
|
|
I2C2CONbits.ACKEN = 1;
|
|
while (I2C2CONbits.ACKEN); // wait for ACK
|
|
} else {
|
|
I2C2CONbits.ACKDT = 1; // change to NACK
|
|
I2C2CONbits.ACKEN = 1; // send ACKDT
|
|
while (I2C2CONbits.ACKEN); // wait for NACK to send
|
|
}
|
|
}
|
|
} else {
|
|
status |= NULL_ERROR;
|
|
}
|
|
I2C2CONbits.PEN = 1; // send STOP
|
|
while (I2C2CONbits.PEN); // wait for stop to complete
|
|
break;
|
|
case TEST_MODE: // infinite test mode
|
|
do {
|
|
for (int i = 0; i < max; ++i) {
|
|
uint8_t c = (int) (i * prime) % max;
|
|
I2C2CONbits.SEN = 1;
|
|
while (I2C2CONbits.SEN);
|
|
I2C2TRN = c;
|
|
while (I2C2STATbits.TBF); // wait for transmission
|
|
I2C2CONbits.PEN = 1;
|
|
while (I2C2CONbits.PEN);
|
|
}
|
|
} while (size--);
|
|
break;
|
|
default:
|
|
status |= 0x04;
|
|
break;
|
|
}
|
|
return status;
|
|
} |