This repository has been archived on 2025-02-01. You can view files and clone it, but cannot push or open issues or pull requests.
reprapfirmware-dc42/Libraries/Wire/Wire.cpp
David Crocker 13ad2ba6e4 Switched to new build system
Switched to the new build system that doesn't need the Eclipse Arduino
plugin and uses the CoreDuet project instead of Arduino to provide
hardware support
2016-03-09 16:38:18 +00:00

384 lines
9.9 KiB
C++

/*
* TwoWire.h - TWI/I2C library for Arduino Due
* Copyright (c) 2011 Cristian Maglie <c.maglie@bug.st>.
* All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
extern "C" {
#include <string.h>
}
#include "Wire.h"
static inline bool TWI_FailedAcknowledge(Twi *pTwi) {
return pTwi->TWI_SR & TWI_SR_NACK;
}
static inline bool TWI_WaitTransferComplete(Twi *_twi, uint32_t _timeout) {
while (!TWI_TransferComplete(_twi)) {
if (TWI_FailedAcknowledge(_twi))
return false;
if (--_timeout == 0)
return false;
}
return true;
}
static inline bool TWI_WaitByteSent(Twi *_twi, uint32_t _timeout) {
while (!TWI_ByteSent(_twi)) {
if (TWI_FailedAcknowledge(_twi))
return false;
if (--_timeout == 0)
return false;
}
return true;
}
static inline bool TWI_WaitByteReceived(Twi *_twi, uint32_t _timeout) {
while (!TWI_ByteReceived(_twi)) {
if (TWI_FailedAcknowledge(_twi))
return false;
if (--_timeout == 0)
return false;
}
return true;
}
static inline bool TWI_STATUS_SVREAD(uint32_t status) {
return (status & TWI_SR_SVREAD) == TWI_SR_SVREAD;
}
static inline bool TWI_STATUS_SVACC(uint32_t status) {
return (status & TWI_SR_SVACC) == TWI_SR_SVACC;
}
static inline bool TWI_STATUS_GACC(uint32_t status) {
return (status & TWI_SR_GACC) == TWI_SR_GACC;
}
static inline bool TWI_STATUS_EOSACC(uint32_t status) {
return (status & TWI_SR_EOSACC) == TWI_SR_EOSACC;
}
static inline bool TWI_STATUS_NACK(uint32_t status) {
return (status & TWI_SR_NACK) == TWI_SR_NACK;
}
TwoWire::TwoWire(Twi *_twi, void(*_beginCb)(void)) :
rxBufferIndex(0), rxBufferLength(0), txAddress(0), txBufferLength(0),
srvBufferIndex(0), srvBufferLength(0), onBeginCallback(_beginCb), twi(_twi),
status(UNINITIALIZED)
{
}
void TwoWire::begin(void) {
if (onBeginCallback)
onBeginCallback();
// Disable PDC channel
twi->TWI_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
TWI_ConfigureMaster(twi, TWI_CLOCK, VARIANT_MCK);
status = MASTER_IDLE;
}
void TwoWire::begin(uint8_t address) {
if (onBeginCallback)
onBeginCallback();
// Disable PDC channel
twi->TWI_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
TWI_ConfigureSlave(twi, address);
status = SLAVE_IDLE;
TWI_EnableIt(twi, TWI_IER_SVACC);
//| TWI_IER_RXRDY | TWI_IER_TXRDY | TWI_IER_TXCOMP);
}
void TwoWire::begin(int address) {
begin((uint8_t) address);
}
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) {
if (quantity > BUFFER_LENGTH)
quantity = BUFFER_LENGTH;
// perform blocking read into buffer
int readed = 0;
TWI_StartRead(twi, address, 0, 0);
do {
// Stop condition must be set during the reception of last byte
if (readed + 1 == quantity)
TWI_SendSTOPCondition( twi);
TWI_WaitByteReceived(twi, RECV_TIMEOUT);
rxBuffer[readed++] = TWI_ReadByte(twi);
} while (readed < quantity);
TWI_WaitTransferComplete(twi, RECV_TIMEOUT);
// set rx buffer iterator vars
rxBufferIndex = 0;
rxBufferLength = readed;
return readed;
}
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) {
return requestFrom((uint8_t) address, (uint8_t) quantity, (uint8_t) true);
}
uint8_t TwoWire::requestFrom(int address, int quantity) {
return requestFrom((uint8_t) address, (uint8_t) quantity, (uint8_t) true);
}
uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) {
return requestFrom((uint8_t) address, (uint8_t) quantity, (uint8_t) sendStop);
}
void TwoWire::beginTransmission(uint8_t address) {
status = MASTER_SEND;
// save address of target and empty buffer
txAddress = address;
txBufferLength = 0;
}
void TwoWire::beginTransmission(int address) {
beginTransmission((uint8_t) address);
}
//
// Originally, 'endTransmission' was an f(void) function.
// It has been modified to take one parameter indicating
// whether or not a STOP should be performed on the bus.
// Calling endTransmission(false) allows a sketch to
// perform a repeated start.
//
// WARNING: Nothing in the library keeps track of whether
// the bus tenure has been properly ended with a STOP. It
// is very possible to leave the bus in a hung state if
// no call to endTransmission(true) is made. Some I2C
// devices will behave oddly if they do not see a STOP.
//
uint8_t TwoWire::endTransmission(uint8_t sendStop) {
// transmit buffer (blocking)
TWI_StartWrite(twi, txAddress, 0, 0, txBuffer[0]);
TWI_WaitByteSent(twi, XMIT_TIMEOUT);
int sent = 1;
while (sent < txBufferLength) {
TWI_WriteByte(twi, txBuffer[sent++]);
TWI_WaitByteSent(twi, XMIT_TIMEOUT);
}
TWI_Stop( twi);
TWI_WaitTransferComplete(twi, XMIT_TIMEOUT);
// empty buffer
txBufferLength = 0;
status = MASTER_IDLE;
return sent;
}
// This provides backwards compatibility with the original
// definition, and expected behaviour, of endTransmission
//
uint8_t TwoWire::endTransmission(void)
{
return endTransmission(true);
}
size_t TwoWire::write(uint8_t data) {
if (status == MASTER_SEND) {
if (txBufferLength >= BUFFER_LENGTH)
return 0;
txBuffer[txBufferLength++] = data;
return 1;
} else {
if (srvBufferLength >= BUFFER_LENGTH)
return 0;
srvBuffer[srvBufferLength++] = data;
return 1;
}
}
size_t TwoWire::write(const uint8_t *data, size_t quantity) {
if (status == MASTER_SEND) {
for (size_t i = 0; i < quantity; ++i) {
if (txBufferLength >= BUFFER_LENGTH)
return i;
txBuffer[txBufferLength++] = data[i];
}
} else {
for (size_t i = 0; i < quantity; ++i) {
if (srvBufferLength >= BUFFER_LENGTH)
return i;
srvBuffer[srvBufferLength++] = data[i];
}
}
return quantity;
}
int TwoWire::available(void) {
return rxBufferLength - rxBufferIndex;
}
int TwoWire::read(void) {
if (rxBufferIndex < rxBufferLength)
return rxBuffer[rxBufferIndex++];
return -1;
}
int TwoWire::peek(void) {
if (rxBufferIndex < rxBufferLength)
return rxBuffer[rxBufferIndex];
return -1;
}
void TwoWire::flush(void) {
// Do nothing, use endTransmission(..) to force
// data transfer.
}
void TwoWire::onReceive(void(*function)(int)) {
onReceiveCallback = function;
}
void TwoWire::onRequest(void(*function)(void)) {
onRequestCallback = function;
}
void TwoWire::onService(void) {
// Retrieve interrupt status
uint32_t sr = TWI_GetStatus(twi);
if (status == SLAVE_IDLE && TWI_STATUS_SVACC(sr)) {
TWI_DisableIt(twi, TWI_IDR_SVACC);
TWI_EnableIt(twi, TWI_IER_RXRDY | TWI_IER_GACC | TWI_IER_NACK
| TWI_IER_EOSACC | TWI_IER_SCL_WS | TWI_IER_TXCOMP);
srvBufferLength = 0;
srvBufferIndex = 0;
// Detect if we should go into RECV or SEND status
// SVREAD==1 means *master* reading -> SLAVE_SEND
if (!TWI_STATUS_SVREAD(sr)) {
status = SLAVE_RECV;
} else {
status = SLAVE_SEND;
// Alert calling program to generate a response ASAP
if (onRequestCallback)
onRequestCallback();
else
// create a default 1-byte response
write((uint8_t) 0);
}
}
if (status != SLAVE_IDLE) {
if (TWI_STATUS_TXCOMP(sr) && TWI_STATUS_EOSACC(sr)) {
if (status == SLAVE_RECV && onReceiveCallback) {
// Copy data into rxBuffer
// (allows to receive another packet while the
// user program reads actual data)
for (uint8_t i = 0; i < srvBufferLength; ++i)
rxBuffer[i] = srvBuffer[i];
rxBufferIndex = 0;
rxBufferLength = srvBufferLength;
// Alert calling program
onReceiveCallback( rxBufferLength);
}
// Transfer completed
TWI_EnableIt(twi, TWI_SR_SVACC);
TWI_DisableIt(twi, TWI_IDR_RXRDY | TWI_IDR_GACC | TWI_IDR_NACK
| TWI_IDR_EOSACC | TWI_IDR_SCL_WS | TWI_IER_TXCOMP);
status = SLAVE_IDLE;
}
}
if (status == SLAVE_RECV) {
if (TWI_STATUS_RXRDY(sr)) {
if (srvBufferLength < BUFFER_LENGTH)
srvBuffer[srvBufferLength++] = TWI_ReadByte(twi);
}
}
if (status == SLAVE_SEND) {
if (TWI_STATUS_TXRDY(sr) && !TWI_STATUS_NACK(sr)) {
uint8_t c = 'x';
if (srvBufferIndex < srvBufferLength)
c = srvBuffer[srvBufferIndex++];
TWI_WriteByte(twi, c);
}
}
}
#if WIRE_INTERFACES_COUNT > 0
static void Wire_Init(void) {
pmc_enable_periph_clk(WIRE_INTERFACE_ID);
PIO_Configure(
g_APinDescription[PIN_WIRE_SDA].pPort,
g_APinDescription[PIN_WIRE_SDA].ulPinType,
g_APinDescription[PIN_WIRE_SDA].ulPin,
g_APinDescription[PIN_WIRE_SDA].ulPinConfiguration);
PIO_Configure(
g_APinDescription[PIN_WIRE_SCL].pPort,
g_APinDescription[PIN_WIRE_SCL].ulPinType,
g_APinDescription[PIN_WIRE_SCL].ulPin,
g_APinDescription[PIN_WIRE_SCL].ulPinConfiguration);
NVIC_DisableIRQ(TWI1_IRQn);
NVIC_ClearPendingIRQ(TWI1_IRQn);
NVIC_SetPriority(TWI1_IRQn, 6);
NVIC_EnableIRQ(TWI1_IRQn);
}
TwoWire Wire = TwoWire(WIRE_INTERFACE, Wire_Init);
void WIRE_ISR_HANDLER(void) {
Wire.onService();
}
#endif
#if WIRE_INTERFACES_COUNT > 1
static void Wire1_Init(void) {
pmc_enable_periph_clk(WIRE1_INTERFACE_ID);
PIO_Configure(
g_APinDescription[PIN_WIRE1_SDA].pPort,
g_APinDescription[PIN_WIRE1_SDA].ulPinType,
g_APinDescription[PIN_WIRE1_SDA].ulPin,
g_APinDescription[PIN_WIRE1_SDA].ulPinConfiguration);
PIO_Configure(
g_APinDescription[PIN_WIRE1_SCL].pPort,
g_APinDescription[PIN_WIRE1_SCL].ulPinType,
g_APinDescription[PIN_WIRE1_SCL].ulPin,
g_APinDescription[PIN_WIRE1_SCL].ulPinConfiguration);
NVIC_DisableIRQ(TWI0_IRQn);
NVIC_ClearPendingIRQ(TWI0_IRQn);
NVIC_SetPriority(TWI0_IRQn, 6);
NVIC_EnableIRQ(TWI0_IRQn);
}
TwoWire Wire1 = TwoWire(WIRE1_INTERFACE, Wire1_Init);
void WIRE1_ISR_HANDLER(void) {
Wire1.onService();
}
#endif