Factor out bus configuration

This commit is contained in:
Sassa NF
2025-01-26 22:33:37 +00:00
parent c7727fb527
commit 2b1da5ae4a
11 changed files with 453 additions and 61 deletions

92
lib/comms/bus.cpp Normal file
View File

@@ -0,0 +1,92 @@
#include "bus.h"
#include <Wire.h>
bool initUARTs(Config &config)
{
if (config.uart0.enabled)
{
Uart0.end();
Uart0.begin(config.uart0.clock_freq, SERIAL_8N1, config.uart0.rx,
config.uart0.tx);
}
if (config.uart1.enabled)
{
Uart1.end();
Uart1.begin(config.uart1.clock_freq, SERIAL_8N1, config.uart1.rx,
config.uart1.tx);
}
return true;
}
SPIClass &hspi = *(new SPIClass(HSPI)); // not usable until initSPIs
bool initSPIs(Config &config)
{
if (config.spi1.enabled)
{
delete (&hspi);
hspi = *(new SPIClass(config.spi1.bus_num));
if (config.spi1.clock_freq > 0)
{
hspi.setFrequency(config.spi1.clock_freq);
}
// if all the pins are -1, then will use the default for SPI bus_num
hspi.begin(config.spi1.clk, config.spi1.miso, config.spi1.mosi);
Serial.printf("Initialized SPI%d: SC:%d MISO:%d MOSI:%d clock:%d\n",
(int)config.spi1.bus_num, (int)config.spi1.clk,
(int)config.spi1.miso, (int)config.spi1.mosi,
(int)config.spi1.clock_freq);
}
return true;
}
uint8_t _scanSupportedDevicesOnWire(TwoWire &w, int bus_num);
uint8_t wireDevices;
uint8_t wire1Devices;
bool initWires(Config &config)
{
wireDevices = _scanSupportedDevicesOnWire(Wire, 0);
if (config.wire1.enabled)
{
#if SOC_I2C_NUM > 1
// if you want to use default pins, configure -1
// if you want to use default clock speed, configure 0
if (!Wire1.begin(config.wire1.sda, config.wire1.scl, config.wire1.clock_freq))
{
Serial.println("Failed to initialize Wire1");
return false;
}
wire1Devices = _scanSupportedDevicesOnWire(Wire1, 1);
#endif
}
return true;
}
uint8_t _scanSupportedDevicesOnWire(TwoWire &w, int bus_num)
{
uint8_t res = 0;
for (int i = 0; known_i2c_devices[i].address > 0; i++)
{
w.beginTransmission(known_i2c_devices[i].address);
delay(2);
if (w.endTransmission() == 0)
{
Serial.printf("Found supported device on Wire%d: %s(%X)\n", bus_num,
known_i2c_devices[i].name.c_str(),
(int)known_i2c_devices[i].address);
res |= 1 << i;
}
}
return res;
}

39
lib/comms/bus.h Normal file
View File

@@ -0,0 +1,39 @@
#pragma once
#include <config.h>
struct
{
String name;
uint8_t address;
} known_i2c_devices[] = {{"HMC5883L", 0x1e}, {"QMC5883L", 0x0d}, {" last record ", 0}};
enum I2CDevices
{
// powers of 2
HMC5883L = 1,
QMC5883L = 2
};
extern uint8_t wireDevices;
extern uint8_t wire1Devices;
extern SPIClass &hspi;
// abstract away a reference to Serial vs Serial0 vs Serial1, so it compiles
#ifndef ARDUINO_USB_CDC_ON_BOOT
#define Uart0 Serial
#else
#define Uart0 Serial0
#endif
#if SOC_UART_NUM > 1
#define Uart1 Serial1
#else
#define Uart1 Uart0
#endif
bool initSPIs(Config &config);
bool initUARTs(Config &config);
bool initWires(Config &config);

View File

@@ -86,9 +86,8 @@ bool Comms::initComms(Config &c)
if (c.listen_on_serial0.equalsIgnoreCase("readline"))
{
// comms using readline plaintext protocol
Comms0 = new ReadlineComms("UART0", SERIAL0);
SERIAL0.onReceive(_onReceive0, false);
SERIAL0.begin(115200);
Comms0 = new ReadlineComms("UART0", Uart0);
Uart0.onReceive(_onReceive0, false);
Serial.println("Initialized communications on Serial0 using readline protocol");
}
@@ -102,9 +101,8 @@ bool Comms::initComms(Config &c)
if (c.listen_on_serial1.equalsIgnoreCase("readline"))
{
// comms using readline plaintext protocol
Comms1 = new ReadlineComms("UART1", Serial1);
Serial1.onReceive(_onReceive1, false);
Serial1.begin(115200);
Comms1 = new ReadlineComms("UART1", Uart1);
Uart1.onReceive(_onReceive1, false);
Serial.println("Initialized communications on Serial1 using readline protocol");
}

View File

@@ -5,14 +5,9 @@
#include <LoRaBoards.h>
#include <LiLyGo.h>
#include <bus.h>
#include <config.h>
#ifndef ARDUINO_USB_CDC_ON_BOOT
#define SERIAL0 Serial
#else
#define SERIAL0 Serial0
#endif
#ifndef SCAN_MAX_RESULT_KHZ_SCALE
// kHz scale: round frequency, so it fits into 2 bytes
// 2500000 / 40 = 62500, scale 40 fits 2.5GHz into two bytes
@@ -135,7 +130,7 @@ struct Comms
struct NoopComms : Comms
{
NoopComms() : Comms("no-op", SERIAL0) {};
NoopComms() : Comms("no-op", Uart0) {};
virtual bool send(Message &) { return true; };
virtual void _onReceive() {};

View File

@@ -170,6 +170,30 @@ bool Config::updateConfig(String key, String value)
return true;
}
if (key.equalsIgnoreCase("uart0"))
{
uart0 = BusConfig::configure(value);
return true;
}
if (key.equalsIgnoreCase("uart1"))
{
uart1 = BusConfig::configure(value);
return true;
}
if (key.equalsIgnoreCase("spi1"))
{
spi1 = BusConfig::configure(value);
return true;
}
if (key.equalsIgnoreCase("wire1"))
{
wire1 = BusConfig::configure(value);
return true;
}
UPDATE_BOOL(is_host, key, value);
UPDATE_BOOL(lora_enabled, key, value);
@@ -521,6 +545,11 @@ bool Config::write_config(const char *path)
f.println("lora_enabled = " + getConfig("lora_enabled"));
f.println("uart0 = " + getConfig("uart0"));
f.println("uart1 = " + getConfig("uart1"));
f.println("spi1 = " + getConfig("spi1"));
f.println("wire1 = " + getConfig("wire1"));
f.close();
return true;
}
@@ -572,6 +601,26 @@ String Config::getConfig(String key)
return String(is_host ? "true" : "false");
}
if (key.equalsIgnoreCase("uart0"))
{
return uart0.toStr();
}
if (key.equalsIgnoreCase("uart1"))
{
return uart1.toStr();
}
if (key.equalsIgnoreCase("spi1"))
{
return spi1.toStr();
}
if (key.equalsIgnoreCase("wire1"))
{
return wire1.toStr();
}
return "";
}
@@ -642,3 +691,127 @@ ParseResult parse_config_line(String ln)
return ParseResult(k, v);
}
BusConfig BusConfig::configure(String cfg)
{
BusConfig c;
if (cfg.equalsIgnoreCase("none"))
{
return c;
}
int begin = 0;
int end, i;
while ((i = findSepa(cfg, ",", begin, end)) >= 0)
{
String param = cfg.substring(begin, end);
begin = i;
int j = param.indexOf(":");
if (j < 0)
{
Serial.printf("Expected ':' to be present in '%s' - ignoring config\n",
param);
continue;
}
String k = param.substring(0, j);
param = param.substring(j + 1);
k.toLowerCase();
if (k.equals("enabled"))
{
c.enabled = !param.equalsIgnoreCase("false");
continue;
}
if (c.bus_type == SPI && k.equals("clk") || c.bus_type == WIRE && k.equals("scl"))
{
c.clk = param.toInt();
continue;
}
if (c.bus_type == SERIAL && k.equals("tx") ||
c.bus_type == SPI && k.equals("mosi") ||
c.bus_type == WIRE && k.equals("sda"))
{
c.tx = param.toInt();
continue;
}
if (c.bus_type == SERIAL && k.equals("rx") ||
c.bus_type == SPI && k.equals("miso"))
{
c.rx = param.toInt();
continue;
}
if (c.bus_type != NONE)
{
Serial.printf("Unknown key: '%s'; ignoring config\n", k.c_str());
continue;
}
if (k.equals("none") || k.length() == 0)
{
continue;
}
c.enabled = true;
char bus_type = k.charAt(0);
switch (bus_type)
{
case 'u':
c.bus_type = UART;
break;
case 's':
c.bus_type = SPI;
break;
case 'w':
c.bus_type = WIRE;
break;
default:
c.bus_type = NONE;
c.enabled = false;
}
c.bus_num = k.substring(1).toInt();
c.clock_freq = param.toInt();
}
return c;
}
String BusConfig::toStr()
{
if (bus_type == NONE)
{
return "none";
}
String ret = String(bus_type == UART ? "u"
: bus_type == SPI ? "s"
: bus_type == WIRE ? "w"
: "none") +
String(bus_num);
ret += ":" + String(clock_freq) + (enabled ? "" : ",enabled:false");
switch (bus_type)
{
case UART:
ret += ",rx:" + String(rx) + ",tx:" + String(tx);
break;
case SPI:
ret += ",clk:" + String(clk) + ",mosi:" + String(mosi) + ",miso:" + String(miso);
break;
case WIRE:
ret += ",scl:" + String(scl) + ",sda:" + String(sda);
break;
default:
ret = "none";
}
return ret;
}

View File

@@ -53,6 +53,49 @@ struct LoRaConfig
LoRaConfig *configureLora(String cfg);
struct BusConfig
{
enum
{
NONE, // No bus
UART, // Serial
SPI,
WIRE // I2C
} bus_type;
int8_t bus_num;
bool enabled;
uint32_t clock_freq;
union
{
int8_t scl;
int8_t clk;
};
union
{
int8_t sda;
int8_t mosi;
int8_t tx;
};
union
{
int8_t miso;
int8_t rx;
};
BusConfig()
: bus_type(NONE), bus_num(-1), enabled(false), clock_freq(115200), clk(-1),
rx(-1), tx(-1) {};
static BusConfig configure(String cfg);
String toStr();
};
#ifndef FREQ_RX
#define FREQ_RX 866
#endif
@@ -84,6 +127,22 @@ LoRaConfig *configureLora(String cfg);
#define DEFAULT_LORA_SF 7
#endif
#ifndef DEFAULT_UART0
#define DEFAULT_UART0 "none"
#endif
#ifndef DEFAULT_UART1
#define DEFAULT_UART1 "u1:115200"
#endif
#ifndef DEFAULT_SPI1
#define DEFAULT_SPI1 "none"
#endif
#ifndef DEFAULT_WIRE1
#define DEFAULT_WIRE1 "none"
#endif
#define CREATE_MISSING_CONFIG true
struct Config
{
@@ -100,6 +159,11 @@ struct Config
LoRaConfig *rx_lora;
LoRaConfig *tx_lora;
BusConfig uart0;
BusConfig uart1;
BusConfig spi1;
BusConfig wire1;
bool is_host;
bool lora_enabled;
@@ -110,6 +174,10 @@ struct Config
listen_on_serial0(String("none")), listen_on_serial1(String("readline")),
listen_on_usb(String("readline")), rx_lora(configureLora(DEFAULT_RX)),
tx_lora(configureLora(DEFAULT_TX)), is_host(DEFAULT_IS_LORA_HOST),
uart0(BusConfig::configure(DEFAULT_UART0)),
uart1(BusConfig::configure(DEFAULT_UART1)),
spi1(BusConfig::configure(DEFAULT_SPI1)),
wire1(BusConfig::configure(DEFAULT_WIRE1)),
lora_enabled(DEFAULT_LORA_ENABLED) {};
bool write_config(const char *path);

View File

@@ -1,5 +1,4 @@
#include "heading.h"
#include <Wire.h>
/*
* QMC5883L Registers:
@@ -62,11 +61,6 @@ bool QMC5883LCompass::begin()
{
_lastErr = CompassStatus::COMPASS_UNINITIALIZED;
if (!Wire1.begin(46, 42)) /* SDA, SCL */
{
return false;
}
String err = selfTest();
if (!err.startsWith("OK\n"))
{
@@ -77,16 +71,17 @@ bool QMC5883LCompass::begin()
return true;
}
uint8_t _write_register(uint8_t addr, uint8_t reg, uint8_t value, bool skipValue = false)
uint8_t _write_register(TwoWire &wire, uint8_t addr, uint8_t reg, uint8_t value,
bool skipValue = false)
{
Wire1.beginTransmission(addr);
size_t s = Wire1.write(reg);
wire.beginTransmission(addr);
size_t s = wire.write(reg);
if (s == 1 && !skipValue)
{
s = Wire1.write(value);
s = wire.write(value);
}
size_t s1 = Wire1.endTransmission();
size_t s1 = wire.endTransmission();
if (s != 1 && s1 == 0)
{
return 1; // "data too long to fit in transmit buffer"
@@ -95,30 +90,31 @@ uint8_t _write_register(uint8_t addr, uint8_t reg, uint8_t value, bool skipValue
return s1;
}
int8_t _read_registers(uint8_t addr, uint8_t reg, uint8_t *v, size_t sz,
int8_t _read_registers(TwoWire &wire, uint8_t addr, uint8_t reg, uint8_t *v, size_t sz,
bool skipRegister = false)
{
if (!skipRegister)
{
uint8_t s = _write_register(addr, reg, 0, true);
uint8_t s = _write_register(wire, addr, reg, 0, true);
if (s != 0)
{
return s;
}
}
uint8_t r = Wire1.requestFrom(addr, sz);
uint8_t r = wire.requestFrom(addr, sz);
for (int i = 0; i < r; i++, v++)
{
*v = Wire1.read();
*v = wire.read();
}
return r - sz;
}
uint8_t _read_register(uint8_t addr, uint8_t reg, uint8_t &v, bool skipRegister = false)
uint8_t _read_register(TwoWire &wire, uint8_t addr, uint8_t reg, uint8_t &v,
bool skipRegister = false)
{
uint8_t r = _read_registers(addr, reg, &v, 1, skipRegister);
uint8_t r = _read_registers(wire, addr, reg, &v, 1, skipRegister);
if (r != 0)
{
return 1;
@@ -127,10 +123,10 @@ uint8_t _read_register(uint8_t addr, uint8_t reg, uint8_t &v, bool skipRegister
return 0;
}
int8_t _read_xyz(CompassXYZ &xyz)
int8_t _read_xyz(TwoWire &wire, CompassXYZ &xyz)
{
xyz.status = 0;
size_t s = _read_register(QMC5883_ADDR, QMC5883_STATUS_REG, xyz.status);
size_t s = _read_register(wire, QMC5883_ADDR, QMC5883_STATUS_REG, xyz.status);
if (s != 0)
{
return s;
@@ -139,7 +135,7 @@ int8_t _read_xyz(CompassXYZ &xyz)
if ((xyz.status & QMC5883_STATUS_DRDY) == 0)
{
delay(10);
s = _read_register(QMC5883_ADDR, QMC5883_STATUS_REG, xyz.status);
s = _read_register(wire, QMC5883_ADDR, QMC5883_STATUS_REG, xyz.status);
if (s != 0)
{
return s;
@@ -148,7 +144,7 @@ int8_t _read_xyz(CompassXYZ &xyz)
int16_t mags[3];
int8_t r = _read_registers(QMC5883_ADDR, 0, (uint8_t *)&mags, 6);
int8_t r = _read_registers(wire, QMC5883_ADDR, 0, (uint8_t *)&mags, 6);
xyz.x = mags[0];
xyz.y = mags[1];
xyz.z = mags[1];
@@ -160,8 +156,8 @@ uint8_t QMC5883LCompass::setMode(CompassMode m)
{
if (m == CompassMode::COMPASS_IDLE)
{
uint8_t s = _write_register(QMC5883_ADDR, QMC5883_FBR_REG, 0);
s |= _write_register(QMC5883_ADDR, QMC5883_CTR_REG, 0);
uint8_t s = _write_register(wire, QMC5883_ADDR, QMC5883_FBR_REG, 0);
s |= _write_register(wire, QMC5883_ADDR, QMC5883_CTR_REG, 0);
return s;
}
@@ -185,13 +181,14 @@ uint8_t QMC5883LCompass::setMode(CompassMode m)
return 1;
}
uint8_t s = _write_register(QMC5883_ADDR, QMC5883_FBR_REG, 1); // set/reset period
uint8_t s =
_write_register(wire, QMC5883_ADDR, QMC5883_FBR_REG, 1); // set/reset period
if (s != 0)
{
return s;
}
return _write_register(QMC5883_ADDR, QMC5883_CTR_REG,
return _write_register(wire, QMC5883_ADDR, QMC5883_CTR_REG,
(osr << 6) | (rng << 4) | (odr << 2) | mode);
}
@@ -211,7 +208,7 @@ String QMC5883LCompass::selfTest()
for (int i = 0; i < 100; i++)
{
CompassXYZ xyz;
int8_t r = _read_xyz(xyz);
int8_t r = _read_xyz(wire, xyz);
if (r < 0)
{
errors = true;
@@ -238,7 +235,7 @@ String QMC5883LCompass::selfTest()
return res;
}
int8_t QMC5883LCompass::readXYZ() { return _read_xyz(xyz); }
int8_t QMC5883LCompass::readXYZ() { return _read_xyz(wire, xyz); }
int64_t Compass::lastRead()
{

View File

@@ -1,5 +1,6 @@
#pragma once
#include <WString.h>
#include <Wire.h>
#include <cstdint>
struct HeadingSensor
@@ -57,7 +58,8 @@ struct Compass : HeadingSensor
struct QMC5883LCompass : Compass
{
QMC5883LCompass() : Compass() {}
TwoWire &wire;
QMC5883LCompass(TwoWire &wire) : Compass(), wire(wire) {}
bool begin() override;
String selfTest() override;

View File

@@ -18,10 +18,10 @@
#ifndef HELTEC_NO_RADIO_INSTANCE
#ifndef ARDUINO_heltec_wifi_32_lora_V3
// Assume MISO and MOSI being wrong when not using Heltec's board definition
// and use hspi to make it work anyway. See heltec_setup() for the actual SPI setup.
// and use vspi to make it work anyway. See heltec_setup() for the actual SPI setup.
#include <SPI.h>
extern SPIClass *hspi;
#define RADIO_MODULE_INIT() new Module(SS, DIO1, RST_LoRa, BUSY_LoRa, *hspi);
extern SPIClass *vspi;
#define RADIO_MODULE_INIT() new Module(SS, DIO1, RST_LoRa, BUSY_LoRa, *vspi);
#else // ARDUINO_heltec_wifi_32_lora_V3
#endif // end ARDUINO_heltec_wifi_32_lora_V3
#endif // end HELTEC_NO_RADIO_INSTANCE

View File

@@ -77,7 +77,7 @@ void heltec_setup()
#ifdef HELTEC
#ifndef ARDUINO_heltec_wifi_32_lora_V3
hspi->begin(SCK, MISO, MOSI, SS);
vspi->begin(SCK, MISO, MOSI, SS);
#endif
#endif
#ifndef HELTEC_NO_DISPLAY_INSTANCE
@@ -90,7 +90,7 @@ void heltec_setup()
#ifdef HELTEC
#ifndef ARDUINO_heltec_wifi_32_lora_V3
SPIClass hspi = new SPIClass(2);
SPIClass vspi = new SPIClass(2);
#endif
#endif

View File

@@ -139,6 +139,7 @@ void sendBTData(float heading, float rssi)
#include <LiLyGo.h>
#endif // end LILYGO
#include <bus.h>
#include <heading.h>
DroneHeading droneHeading;
@@ -1413,6 +1414,31 @@ void setup(void)
wf_start = millis();
config = Config::init();
#if defined(HAS_SDCARD)
SD.end();
SDCardSPI.end(); // end SPI before other uses, eg radio2 over SPI
#endif
pinMode(LED, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(REB_PIN, OUTPUT);
heltec_setup();
if (!initUARTs(config))
{
Serial.println("Failed to initialize UARTs");
}
if (!initSPIs(config))
{
Serial.println("Failed to initialize SPIs");
}
if (!initWires(config))
{
Serial.println("Failed to initialize I2Cs");
}
r.comms_initialized = Comms::initComms(config);
if (r.comms_initialized)
{
@@ -1423,11 +1449,6 @@ void setup(void)
Serial.println("Comms did not initialize");
}
pinMode(LED, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(REB_PIN, OUTPUT);
heltec_setup();
#ifdef JOYSTICK_ENABLED
calibrate_joy();
pinMode(JOY_BTN_PIN, INPUT_PULLUP);
@@ -1653,20 +1674,27 @@ void setup(void)
#endif
compass = new QMC5883LCompass();
if (!compass->begin())
if (wireDevices & QMC5883L || wire1Devices & QMC5883L)
{
Serial.println("Failed to initialize Compass");
compass = new QMC5883LCompass(wireDevices & QMC5883L ? Wire : Wire1);
}
String err = compass->selfTest();
if (err.startsWith("OK\n"))
if (compass)
{
Serial.printf("Compass self-test passed: %s\n", err.c_str());
}
else
{
Serial.printf("Compass self-sets failed: %s\n", err.c_str());
if (!compass->begin())
{
Serial.println("Failed to initialize Compass");
}
String err = compass->selfTest();
if (err.startsWith("OK\n"))
{
Serial.printf("Compass self-test passed: %s\n", err.c_str());
}
else
{
Serial.printf("Compass self-sets failed: %s\n", err.c_str());
}
}
#ifdef UPTIME_CLOCK