diff --git a/I2CManager_SAMD.h b/I2CManager_SAMD.h new file mode 100644 index 0000000..aeabd6c --- /dev/null +++ b/I2CManager_SAMD.h @@ -0,0 +1,188 @@ +/* + * © 2021, Neil McKechnie. All rights reserved. + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +#ifndef I2CMANAGER_SAMD_H +#define I2CMANAGER_SAMD_H + +#include +#include "I2CManager.h" + +//#include +//#include + +#if defined(I2C_USE_INTERRUPTS) && defined(ARDUINO_SAMD_ZERO) +// PMA - IRQ handler, based on SERCOM3 being used for I2C, as per Arduino Zero & Sparkfun SAMD21 +// TODO: test +void SERCOM3_Handler() { + I2CManagerClass::handleInterrupt(); +} +#endif + +/*************************************************************************** + * Set I2C clock speed register. + ***************************************************************************/ +void I2CManagerClass::I2C_setClock(unsigned long i2cClockSpeed) { + unsigned long temp = ((F_CPU / i2cClockSpeed) - 16) / 2; + for (uint8_t preScaler = 0; preScaler<=3; preScaler++) { + if (temp <= 255) { + TWBR = temp; + TWSR = (TWSR & 0xfc) | preScaler; + return; + } else + temp /= 4; + } + // Set slowest speed ~= 500 bits/sec + TWBR = 255; + TWSR |= 0x03; +} + +/*************************************************************************** + * Initialise I2C registers. + ***************************************************************************/ +void I2CManagerClass::I2C_init() +{ + // PMA - broadly we do the following + initialise the clock + initialise the NVIC + software reset the I2C for the sercom + set master mode + do we need smart mode and quick command?? + configure interrupt handlers + enable interrupts + set default baud rate + set SDA/SCL pins as outputs and enable pullups +} + +/*************************************************************************** + * Initiate a start bit for transmission. + ***************************************************************************/ +void I2CManagerClass::I2C_sendStart() { + bytesToSend = currentRequest->writeLen; + bytesToReceive = currentRequest->readLen; + // We may have initiated a stop bit before this without waiting for it. + // Wait for stop bit to be sent before sending start. + while (TWCR & (1<writeBuffer + (txCount++)); + else + TWDR = currentRequest->writeBuffer[txCount++]; + bytesToSend--; + TWCR = (1< 0) { + currentRequest->readBuffer[rxCount++] = TWDR; + bytesToReceive--; + } + /* fallthrough */ + case TWI_MRX_ADR_ACK: // SLA+R has been sent and ACK received + if (bytesToReceive <= 1) { + TWCR = (1< 0) { + currentRequest->readBuffer[rxCount++] = TWDR; + bytesToReceive--; + } + TWCR = (1<i2cAddress << 1) | 1; // SLA+R + else + TWDR = (currentRequest->i2cAddress << 1) | 0; // SLA+W + TWCR = (1<