Rationalize pin selection using readable template

Use a constexpr template to calculate the valid pins for different IO
hardware.  This lets us have an easily readable list of pin numbers that
we can adjust/check.
parent 4c76137f
......@@ -60,6 +60,13 @@ unsigned long millis();
#include "SerialUSB.h"
#include "SerialUART.h"
#include "RP2040.h"
// Template which will evaluate at *compile time* to a single 32b number
// with the specified bits set.
template <size_t N>
constexpr uint32_t __bitset(const int (&a)[N], size_t i = 0U) {
return i < N ? (1L << a[i]) | __bitset(a, i+1) : 0;
}
#endif
......
......@@ -30,26 +30,31 @@
extern void serialEvent1() __attribute__((weak));
extern void serialEvent2() __attribute__((weak));
bool SerialUART::setPinout(pin_size_t tx, pin_size_t rx) {
const uint32_t uart_tx[2] = { 0b000010001000000000001000100000, 0b100000000000100010000000000010 };
const uint32_t uart_rx[2] = { 0b000001000100000000000100010000, 0b010000000000010001000000000001 };
if ( ((1 << tx) & uart_tx[uart_get_index(_uart)]) &&
((1 << rx) & uart_rx[uart_get_index(_uart)]) ) {
if (_running) {
pinMode(_tx, INPUT);
pinMode(_rx, INPUT);
}
_tx = tx;
bool SerialUART::setRX(pin_size_t rx) {
constexpr uint32_t valid[2] = { __bitset({1, 13, 17, 29}) /* UART0 */,
__bitset({5, 9, 21, 25}) /* UART1 */};
if (_running) {
return false;
} else if ((1 << rx) & valid[uart_get_index(_uart)]) {
_rx = rx;
if (_running) {
gpio_set_function(_tx, GPIO_FUNC_UART);
gpio_set_function(_rx, GPIO_FUNC_UART);
}
return true;
} else {
return false;
}
return false;
}
bool SerialUART::setTX(pin_size_t tx) {
constexpr uint32_t valid[2] = { __bitset({0, 12, 16, 28}) /* UART0 */,
__bitset({4, 8, 20, 24}) /* UART1 */};
if (_running) {
return false;
} else if ((1 << tx) & valid[uart_get_index(_uart)]) {
_tx = tx;
return true;
} else {
return false;
}
}
void SerialUART::begin(unsigned long baud, uint16_t config) {
_baud = baud;
......
......@@ -31,7 +31,10 @@ class SerialUART : public HardwareSerial {
public:
SerialUART(uart_inst_t *uart, pin_size_t tx, pin_size_t rx) { _uart = uart; _tx = tx; _rx = rx; }
bool setPinout(pin_size_t tx, pin_size_t rx);
// Select the pinout. Call before .begin()
bool setRX(pin_size_t pin);
bool setTX(pin_size_t pin);
bool setPinout(pin_size_t tx, pin_size_t rx) { bool ret = setRX(rx); ret &= setTX(tx); return ret; }
void begin(unsigned long baud = 115200) override { begin(baud, SERIAL_8N1); };
void begin(unsigned long baud, uint16_t config) override;
......
......@@ -24,6 +24,7 @@
SPIClassRP2040::SPIClassRP2040(spi_inst_t *spi, pin_size_t rx, pin_size_t cs, pin_size_t sck, pin_size_t tx) {
_spi = spi;
_running = false;
_initted = false;
_spis = SPISettings();
_RX = rx;
......@@ -143,42 +144,57 @@ void SPIClassRP2040::endTransaction(void) {
}
bool SPIClassRP2040::setRX(pin_size_t pin) {
const uint32_t valid[2] = { 0b10001000000000001000100000000000, 0b00000000100010000000000010001000 };
if ( (1 << pin) & valid[spi_get_index(_spi)] ) {
constexpr uint32_t valid[2] = { __bitset({0, 4, 16, 20}) /* SPI0 */,
__bitset({8, 12, 24, 28}) /* SPI1 */};
if (_running) {
return false;
} else if ((1 << pin) & valid[spi_get_index(_spi)]) {
_RX = pin;
return true;
} else {
return false;
}
return false;
}
bool SPIClassRP2040::setCS(pin_size_t pin) {
const uint32_t valid[2] = { 0b01000100000000000100010000000000, 0b00000000010001000000000001000100 };
if ( (1 << pin) & valid[spi_get_index(_spi)] ) {
constexpr uint32_t valid[2] = { __bitset({1, 5, 17, 21}) /* SPI0 */,
__bitset({9, 13, 25, 29}) /* SPI1 */};
if (_running) {
return false;
} else if ((1 << pin) & valid[spi_get_index(_spi)]) {
_CS = pin;
return true;
} else {
return false;
}
return false;
}
bool SPIClassRP2040::setSCK(pin_size_t pin) {
const uint32_t valid[2] = { 0b00100010000000000010001000000000, 0b00000000001000100000000000100010 };
if ( (1 << pin) & valid[spi_get_index(_spi)] ) {
constexpr uint32_t valid[2] = { __bitset({2, 6, 18, 22}) /* SPI0 */,
__bitset({10, 14, 26}) /* SPI1 */};
if (_running) {
return false;
} else if ((1 << pin) & valid[spi_get_index(_spi)]) {
_SCK = pin;
return true;
} else {
return false;
}
return false;
}
bool SPIClassRP2040::setTX(pin_size_t pin) {
const uint32_t valid[2] = { 0b00010001000000000001000100000000, 0b00000000000100010000000000010001 };
if ( (1 << pin) & valid[spi_get_index(_spi)] ) {
constexpr uint32_t valid[2] = { __bitset({3, 7, 19, 23}) /* SPI0 */,
__bitset({11, 15, 27}) /* SPI1 */};
if (_running) {
return false;
} else if ((1 << pin) & valid[spi_get_index(_spi)]) {
_TX = pin;
return true;
} else {
return false;
}
return false;
}
void SPIClassRP2040::begin(bool hwCS) {
DEBUGSPI("SPI::begin(%d), rx=%d, cs=%d, sck=%d, tx=%d\n", hwCS, _RX, _CS, _SCK, _TX);
gpio_set_function(_RX, GPIO_FUNC_SPI);
......
......@@ -73,7 +73,8 @@ private:
SPISettings _spis;
pin_size_t _RX, _TX, _SCK, _CS;
bool _hwCS;
bool _initted;
bool _running; // SPI port active
bool _initted; // Transaction begun
};
extern SPIClassRP2040 SPI;
......
......@@ -31,25 +31,35 @@ TwoWire::TwoWire(i2c_inst_t *i2c, pin_size_t sda, pin_size_t scl) {
_scl = scl;
_i2c = i2c;
_clkHz = TWI_CLOCK;
_begun = false;
_running = false;
_txBegun = false;
_buffLen = 0;
}
bool TwoWire::setSDA(pin_size_t pin) {
if (sdaAllowed(pin)) {
constexpr uint32_t valid[2] = { __bitset({0, 4, 8, 12, 16, 20, 24, 28}) /* I2C0 */,
__bitset({2, 6, 10, 14, 18, 22, 26}) /* I2C1 */};
if (_running) {
return false;
} else if ((1 << pin) & valid[i2c_hw_index(_i2c)]) {
_sda = pin;
return true;
} else {
return false;
}
return false;
}
bool TwoWire::setSCL(pin_size_t pin) {
if (sclAllowed(pin)) {
constexpr uint32_t valid[2] = { __bitset({1, 5, 9, 13, 17, 21, 25, 29}) /* I2C0 */,
__bitset({3, 7, 11, 15, 19, 23, 27}) /* I2C1 */};
if (_running) {
return false;
} else if ((1 << pin) & valid[i2c_hw_index(_i2c)]) {
_scl = pin;
return true;
} else {
return false;
}
return false;
}
void TwoWire::setClock(uint32_t hz) {
......@@ -58,7 +68,7 @@ void TwoWire::setClock(uint32_t hz) {
// Master mode
void TwoWire::begin() {
if (_begun) {
if (_running) {
// ERROR
return;
}
......@@ -70,7 +80,7 @@ void TwoWire::begin() {
gpio_set_function(_scl, GPIO_FUNC_I2C);
gpio_pull_up(_scl);
_begun = true;
_running = true;
_txBegun = false;
_buffLen = 0;
}
......@@ -80,7 +90,7 @@ void TwoWire::begin(uint8_t addr) {
// Slave mode isn't documented in the SDK, need to twiddle raw registers
// and use bare interrupts. TODO to implement, for now.
#if 0
if (_begun) {
if (_running) {
// ERROR
return;
}
......@@ -95,17 +105,17 @@ void TwoWire::begin(uint8_t addr) {
}
void TwoWire::end() {
if (!_begun) {
if (!_running) {
// ERROR
return;
}
i2c_deinit(_i2c);
_begun = false;
_running = false;
_txBegun = false;
}
void TwoWire::beginTransmission(uint8_t addr) {
if (!_begun || _txBegun) {
if (!_running || _txBegun) {
// ERROR
return;
}
......@@ -114,56 +124,8 @@ void TwoWire::beginTransmission(uint8_t addr) {
_txBegun = true;
}
bool TwoWire::sdaAllowed(pin_size_t pin) {
switch (i2c_hw_index(_i2c)) {
case 0:
switch (pin) {
case 0:
case 4:
case 16:
case 20:
return true;
}
break;
case 1:
switch (pin) {
case 8:
case 12:
case 24:
case 28:
return true;
}
break;
}
return false;
}
bool TwoWire::sclAllowed(pin_size_t pin) {
switch (i2c_hw_index(_i2c)) {
case 0:
switch (pin) {
case 1:
case 5:
case 17:
case 21:
return true;
}
break;
case 1:
switch (pin) {
case 9:
case 13:
case 25:
case 29:
return true;
}
break;
}
return false;
}
size_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit) {
if (!_begun || _txBegun || !quantity || (quantity > sizeof(_buff))) {
if (!_running || _txBegun || !quantity || (quantity > sizeof(_buff))) {
return 0;
}
......@@ -184,7 +146,7 @@ size_t TwoWire::requestFrom(uint8_t address, size_t quantity) {
// 3 : NACK on transmit of data
// 4 : Other error
uint8_t TwoWire::endTransmission(bool stopBit) {
if (!_begun || !_txBegun || !_buffLen) {
if (!_running || !_txBegun || !_buffLen) {
return 4;
}
auto len = _buffLen;
......@@ -199,7 +161,7 @@ uint8_t TwoWire::endTransmission() {
}
size_t TwoWire::write(uint8_t ucData) {
if (!_begun || !_txBegun || (_buffLen == sizeof(_buff))) {
if (!_running || !_txBegun || (_buffLen == sizeof(_buff))) {
return 0;
}
_buff[_buffLen++] = ucData;
......@@ -217,7 +179,7 @@ size_t TwoWire::write(const uint8_t *data, size_t quantity) {
}
int TwoWire::available(void) {
return _begun ? _buffLen - _buffOff : 0;
return _running ? _buffLen - _buffOff : 0;
}
int TwoWire::read(void) {
......
......@@ -46,8 +46,10 @@ public:
// Shut down the I2C interface
void end();
// Select IO pins to use. Call before ::begin()
bool setSDA(pin_size_t sda);
bool setSCL(pin_size_t scl);
void setClock(uint32_t freqHz) override;
void beginTransmission(uint8_t);
......@@ -74,16 +76,12 @@ public:
using Print::write;
private:
// Verifies the passed in pin can be used by this I2C device
bool sdaAllowed(pin_size_t pin);
bool sclAllowed(pin_size_t pin);
i2c_inst_t *_i2c;
pin_size_t _sda;
pin_size_t _scl;
int _clkHz;
bool _begun;
bool _running;
bool _slave;
uint8_t _addr;
bool _txBegun;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment