rotl_quotes/i2c.cpp

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;
}