RadioLib/src/Module.cpp

614 wiersze
20 KiB
C++
Czysty Zwykły widok Historia

#include "Module.h"
// the following is probably only needed on non-Arduino builds
2023-04-11 02:51:29 +00:00
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
2018-03-05 16:08:42 +00:00
2023-11-27 18:38:10 +00:00
#if RADIOLIB_DEBUG
// needed for debug print
#include <stdarg.h>
#endif
2021-11-14 10:33:35 +00:00
#if defined(RADIOLIB_BUILD_ARDUINO)
2023-04-11 02:51:29 +00:00
#include "ArduinoHal.h"
Module::Module(uint32_t cs, uint32_t irq, uint32_t rst, uint32_t gpio) : csPin(cs), irqPin(irq), rstPin(rst), gpioPin(gpio) {
this->hal = new ArduinoHal();
2018-07-11 16:15:54 +00:00
}
Module::Module(uint32_t cs, uint32_t irq, uint32_t rst, uint32_t gpio, SPIClass& spi, SPISettings spiSettings) : csPin(cs), irqPin(irq), rstPin(rst), gpioPin(gpio) {
2023-04-11 02:51:29 +00:00
this->hal = new ArduinoHal(spi, spiSettings);
2021-11-14 10:33:35 +00:00
}
#endif
2018-03-05 16:08:42 +00:00
Module::Module(RadioLibHal *hal, uint32_t cs, uint32_t irq, uint32_t rst, uint32_t gpio) : csPin(cs), irqPin(irq), rstPin(rst), gpioPin(gpio) {
2023-04-11 02:51:29 +00:00
this->hal = hal;
}
Module::Module(const Module& mod) {
*this = mod;
}
Module& Module::operator=(const Module& mod) {
memcpy((void*)&mod.spiConfig, &this->spiConfig, sizeof(SPIConfig_t));
this->csPin = mod.csPin;
this->irqPin = mod.irqPin;
this->rstPin = mod.rstPin;
this->gpioPin = mod.gpioPin;
return(*this);
}
2024-04-07 16:34:31 +00:00
static volatile const char info[] = RADIOLIB_INFO;
2021-11-14 10:33:35 +00:00
void Module::init() {
2023-04-11 02:51:29 +00:00
this->hal->init();
this->hal->pinMode(csPin, this->hal->GpioModeOutput);
this->hal->digitalWrite(csPin, this->hal->GpioLevelHigh);
RADIOLIB_DEBUG_BASIC_PRINTLN(RADIOLIB_INFO);
2018-03-05 16:08:42 +00:00
}
2021-11-14 10:33:35 +00:00
void Module::term() {
// stop hardware interfaces (if they were initialized by the library)
2023-04-11 02:51:29 +00:00
this->hal->term();
2018-03-05 16:08:42 +00:00
}
int16_t Module::SPIgetRegValue(uint32_t reg, uint8_t msb, uint8_t lsb) {
2018-03-05 16:08:42 +00:00
if((msb > 7) || (lsb > 7) || (lsb > msb)) {
2021-11-14 10:33:35 +00:00
return(RADIOLIB_ERR_INVALID_BIT_RANGE);
2018-03-05 16:08:42 +00:00
}
2019-05-13 13:03:09 +00:00
2018-03-05 16:08:42 +00:00
uint8_t rawValue = SPIreadRegister(reg);
uint8_t maskedValue = rawValue & ((0b11111111 << lsb) & (0b11111111 >> (7 - msb)));
return(maskedValue);
}
int16_t Module::SPIsetRegValue(uint32_t reg, uint8_t value, uint8_t msb, uint8_t lsb, uint8_t checkInterval, uint8_t checkMask) {
2018-07-23 09:19:34 +00:00
if((msb > 7) || (lsb > 7) || (lsb > msb)) {
2021-11-14 10:33:35 +00:00
return(RADIOLIB_ERR_INVALID_BIT_RANGE);
2018-07-23 09:19:34 +00:00
}
2019-05-13 13:03:09 +00:00
2018-07-23 09:19:34 +00:00
uint8_t currentValue = SPIreadRegister(reg);
uint8_t mask = ~((0b11111111 << (msb + 1)) | (0b11111111 >> (8 - lsb)));
uint8_t newValue = (currentValue & ~mask) | (value & mask);
SPIwriteRegister(reg, newValue);
2019-05-13 13:03:09 +00:00
#if RADIOLIB_SPI_PARANOID
// check register value each millisecond until check interval is reached
// some registers need a bit of time to process the change (e.g. SX127X_REG_OP_MODE)
Use RadioLibTime_t (aka unsigned long) when dealing with millis() and micros() (#1075) * Use unsigned long when dealing with millis() and micros(). Although sizeof(uint32_t) == sizeof(unsigned long) on Arduino, this is not the case on 64-bit Linux, where sizeof(unsigned long) == sizeof(uint64_t). Most timestamp arithmetic and comparisons have been left alone, to reduce code churn. This is fine, as uint32_t is perfectly wide to store most timestamp deltas this library will deal with, and C will promote the integer rather than do a narrowing conversion. The real problem arises with narrowing conversions being done by assuming timestamps are 32-bit. No functional changes intended for platforms where sizeof(uint32_t) == sizeof(unsigned long) (so most 8/16/32-bit platforms). Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com> * Change most timestamps to use RadioLibTime_t. This makes it obvious what is and isn't a timestamp. Not everything has been converted; anything dealing with protocol and chip-level timestamps has been left alone on purpose, to make it clear that these functions do require 32-bit timestamps. No functional changes intended on platforms where sizeof(uint32_t) == sizeof(unsigned long). Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com> * Use uint32_t internally in getTimeOnAir. We need to not overflow the integers with the shifts and multiplications, so this is correct behaviour. Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com> --------- Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com>
2024-04-25 19:50:58 +00:00
RadioLibTime_t start = this->hal->micros();
2024-05-01 14:51:01 +00:00
#if RADIOLIB_DEBUG_SPI
uint8_t readValue = 0x00;
2024-05-01 14:51:01 +00:00
#endif
2023-04-11 02:51:29 +00:00
while(this->hal->micros() - start < (checkInterval * 1000)) {
2024-05-01 14:51:01 +00:00
uint8_t val = SPIreadRegister(reg);
if((val & checkMask) == (newValue & checkMask)) {
// check passed, we can stop the loop
2021-11-14 10:33:35 +00:00
return(RADIOLIB_ERR_NONE);
}
2024-05-01 14:51:01 +00:00
#if RADIOLIB_DEBUG_SPI
readValue = val;
#endif
2018-10-31 16:44:47 +00:00
}
2019-05-13 13:03:09 +00:00
// check failed, print debug info
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINTLN();
RADIOLIB_DEBUG_SPI_PRINTLN("address:\t0x%X", reg);
RADIOLIB_DEBUG_SPI_PRINTLN("bits:\t\t%d %d", msb, lsb);
RADIOLIB_DEBUG_SPI_PRINTLN("value:\t\t0x%X", value);
RADIOLIB_DEBUG_SPI_PRINTLN("current:\t0x%X", currentValue);
RADIOLIB_DEBUG_SPI_PRINTLN("mask:\t\t0x%X", mask);
RADIOLIB_DEBUG_SPI_PRINTLN("new:\t\t0x%X", newValue);
RADIOLIB_DEBUG_SPI_PRINTLN("read:\t\t0x%X", readValue);
2021-11-14 10:33:35 +00:00
return(RADIOLIB_ERR_SPI_WRITE_FAILED);
#else
2021-11-14 10:33:35 +00:00
return(RADIOLIB_ERR_NONE);
#endif
2018-07-23 09:19:34 +00:00
}
void Module::SPIreadRegisterBurst(uint32_t reg, size_t numBytes, uint8_t* inBytes) {
if(!this->spiConfig.stream) {
SPItransfer(this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ], reg, NULL, inBytes, numBytes);
} else {
uint8_t cmd[6];
uint8_t* cmdPtr = cmd;
2024-04-14 18:22:16 +00:00
for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) {
*(cmdPtr++) = (this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] >> 8*i) & 0xFF;
}
2024-04-07 16:34:31 +00:00
for(int8_t i = (int8_t)((this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8) - 1); i >= 0; i--) {
*(cmdPtr++) = (reg >> 8*i) & 0xFF;
}
SPItransferStream(cmd, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 + this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8, false, NULL, inBytes, numBytes, true, RADIOLIB_MODULE_SPI_TIMEOUT);
}
2018-03-05 16:08:42 +00:00
}
uint8_t Module::SPIreadRegister(uint32_t reg) {
2019-11-23 09:10:53 +00:00
uint8_t resp = 0;
if(!spiConfig.stream) {
SPItransfer(this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ], reg, NULL, &resp, 1);
} else {
uint8_t cmd[6];
uint8_t* cmdPtr = cmd;
2024-04-14 18:22:16 +00:00
for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) {
*(cmdPtr++) = (this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] >> 8*i) & 0xFF;
}
2024-04-07 16:34:31 +00:00
for(int8_t i = (int8_t)((this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8) - 1); i >= 0; i--) {
*(cmdPtr++) = (reg >> 8*i) & 0xFF;
}
SPItransferStream(cmd, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 + this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8, false, NULL, &resp, 1, true, RADIOLIB_MODULE_SPI_TIMEOUT);
}
2019-03-22 18:01:56 +00:00
return(resp);
2018-03-05 16:08:42 +00:00
}
void Module::SPIwriteRegisterBurst(uint32_t reg, uint8_t* data, size_t numBytes) {
if(!spiConfig.stream) {
SPItransfer(spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE], reg, data, NULL, numBytes);
} else {
uint8_t cmd[6];
uint8_t* cmdPtr = cmd;
2024-04-14 18:22:16 +00:00
for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) {
*(cmdPtr++) = (this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] >> 8*i) & 0xFF;
}
2024-04-07 16:34:31 +00:00
for(int8_t i = (int8_t)((this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8) - 1); i >= 0; i--) {
*(cmdPtr++) = (reg >> 8*i) & 0xFF;
}
SPItransferStream(cmd, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 + this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8, true, data, NULL, numBytes, true, RADIOLIB_MODULE_SPI_TIMEOUT);
}
2018-03-05 16:08:42 +00:00
}
void Module::SPIwriteRegister(uint32_t reg, uint8_t data) {
if(!spiConfig.stream) {
SPItransfer(spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE], reg, &data, NULL, 1);
} else {
uint8_t cmd[6];
uint8_t* cmdPtr = cmd;
2024-04-14 18:22:16 +00:00
for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) {
*(cmdPtr++) = (this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] >> 8*i) & 0xFF;
}
2024-04-07 16:34:31 +00:00
for(int8_t i = (int8_t)((this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8) - 1); i >= 0; i--) {
*(cmdPtr++) = (reg >> 8*i) & 0xFF;
}
SPItransferStream(cmd, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 + this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8, true, &data, NULL, 1, true, RADIOLIB_MODULE_SPI_TIMEOUT);
}
2019-03-22 18:01:56 +00:00
}
void Module::SPItransfer(uint16_t cmd, uint32_t reg, uint8_t* dataOut, uint8_t* dataIn, size_t numBytes) {
2023-06-26 17:36:45 +00:00
// prepare the buffers
size_t buffLen = this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 + this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8 + numBytes;
2023-11-27 18:38:10 +00:00
#if RADIOLIB_STATIC_ONLY
2023-06-26 17:36:45 +00:00
uint8_t buffOut[RADIOLIB_STATIC_ARRAY_SIZE];
uint8_t buffIn[RADIOLIB_STATIC_ARRAY_SIZE];
#else
uint8_t* buffOut = new uint8_t[buffLen];
uint8_t* buffIn = new uint8_t[buffLen];
#endif
uint8_t* buffOutPtr = buffOut;
2019-05-13 13:03:09 +00:00
2023-06-26 17:36:45 +00:00
// copy the command
// TODO properly handle variable commands and addresses
if(this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] <= 8) {
2023-06-26 17:36:45 +00:00
*(buffOutPtr++) = reg | cmd;
} else {
2023-06-26 17:36:45 +00:00
*(buffOutPtr++) = (reg >> 8) | cmd;
*(buffOutPtr++) = reg & 0xFF;
}
// copy the data
if(cmd == spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE]) {
2023-06-26 17:36:45 +00:00
memcpy(buffOutPtr, dataOut, numBytes);
} else {
memset(buffOutPtr, this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP], numBytes);
2023-06-26 17:36:45 +00:00
}
// do the transfer
this->hal->spiBeginTransaction();
this->hal->digitalWrite(this->csPin, this->hal->GpioLevelLow);
2023-06-26 17:36:45 +00:00
this->hal->spiTransfer(buffOut, buffLen, buffIn);
this->hal->digitalWrite(this->csPin, this->hal->GpioLevelHigh);
this->hal->spiEndTransaction();
2023-06-26 17:36:45 +00:00
// copy the data
if(cmd == spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ]) {
memcpy(dataIn, &buffIn[this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8], numBytes);
}
2023-06-26 17:36:45 +00:00
// print debug information
2024-03-10 10:07:23 +00:00
#if RADIOLIB_DEBUG_SPI
2023-06-26 17:36:45 +00:00
uint8_t* debugBuffPtr = NULL;
if(cmd == spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE]) {
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINT("W\t%X\t", reg);
debugBuffPtr = &buffOut[this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8];
} else if(cmd == spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ]) {
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINT("R\t%X\t", reg);
debugBuffPtr = &buffIn[this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8];
2019-03-22 18:01:56 +00:00
}
2023-06-26 17:36:45 +00:00
for(size_t n = 0; n < numBytes; n++) {
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINT_NOTAG("%X\t", debugBuffPtr[n]);
2019-03-22 18:01:56 +00:00
}
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINTLN_NOTAG();
2023-06-26 17:36:45 +00:00
#endif
2019-05-13 13:03:09 +00:00
2023-11-27 18:38:10 +00:00
#if !RADIOLIB_STATIC_ONLY
2023-06-26 17:36:45 +00:00
delete[] buffOut;
delete[] buffIn;
#endif
2018-03-05 16:08:42 +00:00
}
2019-12-01 07:12:04 +00:00
int16_t Module::SPIreadStream(uint16_t cmd, uint8_t* data, size_t numBytes, bool waitForGpio, bool verify) {
uint8_t cmdBuf[2];
uint8_t* cmdPtr = cmdBuf;
2024-04-14 18:22:16 +00:00
for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) {
*(cmdPtr++) = (cmd >> 8*i) & 0xFF;
}
return(this->SPIreadStream(cmdBuf, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8, data, numBytes, waitForGpio, verify));
2023-02-19 15:59:03 +00:00
}
2023-02-25 12:20:30 +00:00
int16_t Module::SPIreadStream(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, size_t numBytes, bool waitForGpio, bool verify) {
2023-02-19 15:59:03 +00:00
// send the command
int16_t state = this->SPItransferStream(cmd, cmdLen, false, NULL, data, numBytes, waitForGpio, RADIOLIB_MODULE_SPI_TIMEOUT);
2023-02-19 15:59:03 +00:00
RADIOLIB_ASSERT(state);
#if !RADIOLIB_SPI_PARANOID
(void)verify;
return(RADIOLIB_ERR_NONE);
#else
2023-02-19 15:59:03 +00:00
// check the status
if(verify && (this->spiConfig.checkStatusCb != nullptr)) {
state = this->spiConfig.checkStatusCb(this);
2023-02-19 15:59:03 +00:00
}
return(state);
#endif
2023-02-19 15:59:03 +00:00
}
int16_t Module::SPIwriteStream(uint16_t cmd, uint8_t* data, size_t numBytes, bool waitForGpio, bool verify) {
uint8_t cmdBuf[2];
uint8_t* cmdPtr = cmdBuf;
2024-04-14 18:22:16 +00:00
for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) {
*(cmdPtr++) = (cmd >> 8*i) & 0xFF;
}
return(this->SPIwriteStream(cmdBuf, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8, data, numBytes, waitForGpio, verify));
2023-02-19 15:59:03 +00:00
}
2023-02-25 12:20:30 +00:00
int16_t Module::SPIwriteStream(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, size_t numBytes, bool waitForGpio, bool verify) {
2023-02-19 15:59:03 +00:00
// send the command
int16_t state = this->SPItransferStream(cmd, cmdLen, true, data, NULL, numBytes, waitForGpio, RADIOLIB_MODULE_SPI_TIMEOUT);
2023-02-19 15:59:03 +00:00
RADIOLIB_ASSERT(state);
#if !RADIOLIB_SPI_PARANOID
(void)verify;
return(RADIOLIB_ERR_NONE);
#else
2023-02-19 15:59:03 +00:00
// check the status
if(verify && (this->spiConfig.checkStatusCb != nullptr)) {
state = this->spiConfig.checkStatusCb(this);
2023-02-19 15:59:03 +00:00
}
return(state);
#endif
2023-02-19 15:59:03 +00:00
}
int16_t Module::SPIcheckStream() {
int16_t state = RADIOLIB_ERR_NONE;
#if RADIOLIB_SPI_PARANOID
2023-02-19 15:59:03 +00:00
// get the status
uint8_t spiStatus = 0;
uint8_t cmdBuf[2];
uint8_t* cmdPtr = cmdBuf;
2024-04-14 18:22:16 +00:00
for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) {
*(cmdPtr++) = ( this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] >> 8*i) & 0xFF;
}
state = this->SPItransferStream(cmdBuf, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8, false, NULL, &spiStatus, 1, true, RADIOLIB_MODULE_SPI_TIMEOUT);
2023-02-19 15:59:03 +00:00
RADIOLIB_ASSERT(state);
// translate to RadioLib status code
if(this->spiConfig.parseStatusCb != nullptr) {
this->spiConfig.err = this->spiConfig.parseStatusCb(spiStatus);
2023-02-19 15:59:03 +00:00
}
#endif
return(state);
}
2024-05-05 18:47:09 +00:00
int16_t Module::SPItransferStream(const uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* dataOut, uint8_t* dataIn, size_t numBytes, bool waitForGpio, RadioLibTime_t timeout) {
2024-05-01 14:51:01 +00:00
// prepare the output buffer
2023-06-26 17:36:45 +00:00
size_t buffLen = cmdLen + numBytes;
if(!write) {
buffLen += (this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] / 8);
2023-06-26 17:36:45 +00:00
}
2023-11-27 18:38:10 +00:00
#if RADIOLIB_STATIC_ONLY
2023-06-26 17:36:45 +00:00
uint8_t buffOut[RADIOLIB_STATIC_ARRAY_SIZE];
#else
uint8_t* buffOut = new uint8_t[buffLen];
2023-02-19 11:32:17 +00:00
#endif
2023-06-26 17:36:45 +00:00
uint8_t* buffOutPtr = buffOut;
// copy the command
for(uint8_t n = 0; n < cmdLen; n++) {
*(buffOutPtr++) = cmd[n];
}
// copy the data
if(write) {
memcpy(buffOutPtr, dataOut, numBytes);
} else {
memset(buffOutPtr, this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP], numBytes + (this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] / 8));
2023-06-26 17:36:45 +00:00
}
2023-02-19 11:32:17 +00:00
// ensure GPIO is low
if(this->gpioPin == RADIOLIB_NC) {
this->hal->delay(50);
} else {
Use RadioLibTime_t (aka unsigned long) when dealing with millis() and micros() (#1075) * Use unsigned long when dealing with millis() and micros(). Although sizeof(uint32_t) == sizeof(unsigned long) on Arduino, this is not the case on 64-bit Linux, where sizeof(unsigned long) == sizeof(uint64_t). Most timestamp arithmetic and comparisons have been left alone, to reduce code churn. This is fine, as uint32_t is perfectly wide to store most timestamp deltas this library will deal with, and C will promote the integer rather than do a narrowing conversion. The real problem arises with narrowing conversions being done by assuming timestamps are 32-bit. No functional changes intended for platforms where sizeof(uint32_t) == sizeof(unsigned long) (so most 8/16/32-bit platforms). Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com> * Change most timestamps to use RadioLibTime_t. This makes it obvious what is and isn't a timestamp. Not everything has been converted; anything dealing with protocol and chip-level timestamps has been left alone on purpose, to make it clear that these functions do require 32-bit timestamps. No functional changes intended on platforms where sizeof(uint32_t) == sizeof(unsigned long). Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com> * Use uint32_t internally in getTimeOnAir. We need to not overflow the integers with the shifts and multiplications, so this is correct behaviour. Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com> --------- Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com>
2024-04-25 19:50:58 +00:00
RadioLibTime_t start = this->hal->millis();
while(this->hal->digitalRead(this->gpioPin)) {
this->hal->yield();
if(this->hal->millis() - start >= timeout) {
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_BASIC_PRINTLN("GPIO pre-transfer timeout, is it connected?");
2023-11-27 18:38:10 +00:00
#if !RADIOLIB_STATIC_ONLY
2023-06-26 17:36:45 +00:00
delete[] buffOut;
#endif
return(RADIOLIB_ERR_SPI_CMD_TIMEOUT);
}
2023-02-19 11:32:17 +00:00
}
}
2024-05-01 14:51:01 +00:00
// prepare the input buffer
#if RADIOLIB_STATIC_ONLY
uint8_t buffIn[RADIOLIB_STATIC_ARRAY_SIZE];
#else
uint8_t* buffIn = new uint8_t[buffLen];
#endif
2023-06-26 17:36:45 +00:00
// do the transfer
2023-04-11 02:51:29 +00:00
this->hal->spiBeginTransaction();
this->hal->digitalWrite(this->csPin, this->hal->GpioLevelLow);
2023-06-26 17:36:45 +00:00
this->hal->spiTransfer(buffOut, buffLen, buffIn);
this->hal->digitalWrite(this->csPin, this->hal->GpioLevelHigh);
this->hal->spiEndTransaction();
2023-02-19 11:32:17 +00:00
// wait for GPIO to go high and then low
if(waitForGpio) {
if(this->gpioPin == RADIOLIB_NC) {
this->hal->delay(1);
} else {
this->hal->delayMicroseconds(1);
Use RadioLibTime_t (aka unsigned long) when dealing with millis() and micros() (#1075) * Use unsigned long when dealing with millis() and micros(). Although sizeof(uint32_t) == sizeof(unsigned long) on Arduino, this is not the case on 64-bit Linux, where sizeof(unsigned long) == sizeof(uint64_t). Most timestamp arithmetic and comparisons have been left alone, to reduce code churn. This is fine, as uint32_t is perfectly wide to store most timestamp deltas this library will deal with, and C will promote the integer rather than do a narrowing conversion. The real problem arises with narrowing conversions being done by assuming timestamps are 32-bit. No functional changes intended for platforms where sizeof(uint32_t) == sizeof(unsigned long) (so most 8/16/32-bit platforms). Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com> * Change most timestamps to use RadioLibTime_t. This makes it obvious what is and isn't a timestamp. Not everything has been converted; anything dealing with protocol and chip-level timestamps has been left alone on purpose, to make it clear that these functions do require 32-bit timestamps. No functional changes intended on platforms where sizeof(uint32_t) == sizeof(unsigned long). Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com> * Use uint32_t internally in getTimeOnAir. We need to not overflow the integers with the shifts and multiplications, so this is correct behaviour. Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com> --------- Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com>
2024-04-25 19:50:58 +00:00
RadioLibTime_t start = this->hal->millis();
while(this->hal->digitalRead(this->gpioPin)) {
this->hal->yield();
if(this->hal->millis() - start >= timeout) {
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_BASIC_PRINTLN("GPIO post-transfer timeout, is it connected?");
2023-11-27 18:38:10 +00:00
#if !RADIOLIB_STATIC_ONLY
2023-06-26 17:36:45 +00:00
delete[] buffOut;
delete[] buffIn;
#endif
return(RADIOLIB_ERR_SPI_CMD_TIMEOUT);
}
2023-02-19 11:32:17 +00:00
}
}
}
2023-06-26 17:36:45 +00:00
// parse status
int16_t state = RADIOLIB_ERR_NONE;
if((this->spiConfig.parseStatusCb != nullptr) && (numBytes > 0)) {
state = this->spiConfig.parseStatusCb(buffIn[this->spiConfig.statusPos]);
2023-06-26 17:36:45 +00:00
}
// copy the data
if(!write) {
// skip the status bytes if present
memcpy(dataIn, &buffIn[cmdLen + (this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] / 8)], numBytes);
2023-06-26 17:36:45 +00:00
}
// print debug information
2024-03-10 10:07:23 +00:00
#if RADIOLIB_DEBUG_SPI
2023-02-19 11:32:17 +00:00
// print command byte(s)
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINT("CMD");
2023-06-26 17:36:45 +00:00
if(write) {
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINT_NOTAG("W\t");
2023-06-26 17:36:45 +00:00
} else {
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINT_NOTAG("R\t");
2023-06-26 17:36:45 +00:00
}
size_t n = 0;
for(; n < cmdLen; n++) {
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINT_NOTAG("%X\t", cmd[n]);
2023-02-19 11:32:17 +00:00
}
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINTLN_NOTAG();
2023-02-19 11:32:17 +00:00
// print data bytes
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINT("SI\t");
for(n = 0; n < cmdLen; n++) {
RADIOLIB_DEBUG_SPI_PRINT_NOTAG("\t");
}
2023-06-26 17:36:45 +00:00
for(; n < buffLen; n++) {
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINT_NOTAG("%X\t", buffOut[n]);
2023-06-26 17:36:45 +00:00
}
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINTLN_NOTAG();
RADIOLIB_DEBUG_SPI_PRINT("SO\t");
for(n = 0; n < buffLen; n++) {
RADIOLIB_DEBUG_SPI_PRINT_NOTAG("%X\t", buffIn[n]);
2023-02-19 11:32:17 +00:00
}
2024-03-10 10:07:23 +00:00
RADIOLIB_DEBUG_SPI_PRINTLN_NOTAG();
2023-02-19 11:32:17 +00:00
#endif
2023-11-27 18:38:10 +00:00
#if !RADIOLIB_STATIC_ONLY
2023-06-26 17:36:45 +00:00
delete[] buffOut;
delete[] buffIn;
#endif
2023-02-19 11:32:17 +00:00
return(state);
}
Use RadioLibTime_t (aka unsigned long) when dealing with millis() and micros() (#1075) * Use unsigned long when dealing with millis() and micros(). Although sizeof(uint32_t) == sizeof(unsigned long) on Arduino, this is not the case on 64-bit Linux, where sizeof(unsigned long) == sizeof(uint64_t). Most timestamp arithmetic and comparisons have been left alone, to reduce code churn. This is fine, as uint32_t is perfectly wide to store most timestamp deltas this library will deal with, and C will promote the integer rather than do a narrowing conversion. The real problem arises with narrowing conversions being done by assuming timestamps are 32-bit. No functional changes intended for platforms where sizeof(uint32_t) == sizeof(unsigned long) (so most 8/16/32-bit platforms). Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com> * Change most timestamps to use RadioLibTime_t. This makes it obvious what is and isn't a timestamp. Not everything has been converted; anything dealing with protocol and chip-level timestamps has been left alone on purpose, to make it clear that these functions do require 32-bit timestamps. No functional changes intended on platforms where sizeof(uint32_t) == sizeof(unsigned long). Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com> * Use uint32_t internally in getTimeOnAir. We need to not overflow the integers with the shifts and multiplications, so this is correct behaviour. Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com> --------- Signed-off-by: Elizabeth Myers <elizabeth.jennifer.myers@gmail.com>
2024-04-25 19:50:58 +00:00
void Module::waitForMicroseconds(RadioLibTime_t start, RadioLibTime_t len) {
2023-11-27 18:38:10 +00:00
#if RADIOLIB_INTERRUPT_TIMING
(void)start;
if((this->TimerSetupCb != nullptr) && (len != this->prevTimingLen)) {
prevTimingLen = len;
this->TimerSetupCb(len);
}
this->TimerFlag = false;
while(!this->TimerFlag) {
2023-04-11 02:51:29 +00:00
this->hal->yield();
2021-11-14 10:33:35 +00:00
}
#else
2023-04-11 02:51:29 +00:00
while(this->hal->micros() - start < len) {
this->hal->yield();
2021-11-14 10:33:35 +00:00
}
#endif
2021-11-14 10:33:35 +00:00
}
2023-05-12 18:55:51 +00:00
uint32_t Module::reflect(uint32_t in, uint8_t bits) {
uint32_t res = 0;
for(uint8_t i = 0; i < bits; i++) {
res |= (((in & ((uint32_t)1 << i)) >> i) << (bits - i - 1));
}
return(res);
2021-06-14 18:59:16 +00:00
}
2023-11-27 18:38:10 +00:00
#if RADIOLIB_DEBUG
void Module::hexdump(const char* level, uint8_t* data, size_t len, uint32_t offset, uint8_t width, bool be) {
size_t rem_len = len;
for(size_t i = 0; i < len; i+=16) {
char str[120];
2024-05-03 19:54:18 +00:00
sprintf(str, "%07" PRIx32 " ", (uint32_t)i+offset);
size_t line_len = 16;
if(rem_len < line_len) {
line_len = rem_len;
}
for(size_t j = 0; j < line_len; j+=width) {
2023-02-25 12:20:30 +00:00
if(width > 1) {
int m = 0;
int step = width/2;
if(be) {
step *= -1;
}
for(int32_t k = width - 1; k >= -width + 1; k+=step) {
sprintf(&str[8 + (j+m)*3], "%02x ", data[i+j+k+m]);
m++;
}
} else {
sprintf(&str[8 + (j)*3], "%02x ", data[i+j]);
}
2022-07-03 09:05:56 +00:00
}
for(size_t j = line_len; j < 16; j++) {
sprintf(&str[8 + j*3], " ");
}
2022-07-03 09:05:56 +00:00
str[56] = '|';
str[57] = ' ';
// at this point we need to start escaping "%" characters
char* strPtr = &str[58];
for(size_t j = 0; j < line_len; j++) {
2022-07-03 09:05:56 +00:00
char c = data[i+j];
if((c < ' ') || (c > '~')) {
c = '.';
} else if(c == '%') {
*strPtr++ = '%';
2022-07-03 09:05:56 +00:00
}
sprintf(strPtr++, "%c", c);
2022-07-03 09:05:56 +00:00
}
for(size_t j = line_len; j < 16; j++) {
sprintf(strPtr++, " ");
}
if(level) {
RADIOLIB_DEBUG_PRINT(level);
}
2023-04-09 21:47:44 +00:00
RADIOLIB_DEBUG_PRINT(str);
RADIOLIB_DEBUG_PRINTLN();
rem_len -= 16;
2022-07-03 09:05:56 +00:00
}
}
void Module::regdump(const char* level, uint16_t start, size_t len) {
2023-11-27 18:38:10 +00:00
#if RADIOLIB_STATIC_ONLY
2022-07-04 13:30:37 +00:00
uint8_t buff[RADIOLIB_STATIC_ARRAY_SIZE];
#else
uint8_t* buff = new uint8_t[len];
#endif
SPIreadRegisterBurst(start, len, buff);
hexdump(level, buff, len, start);
2023-11-27 18:38:10 +00:00
#if !RADIOLIB_STATIC_ONLY
2022-07-04 13:30:37 +00:00
delete[] buff;
#endif
}
#endif
2022-07-04 13:30:37 +00:00
2023-11-27 18:38:10 +00:00
#if RADIOLIB_DEBUG && defined(RADIOLIB_BUILD_ARDUINO)
2023-04-10 12:51:07 +00:00
// https://github.com/esp8266/Arduino/blob/65579d29081cb8501e4d7f786747bf12e7b37da2/cores/esp8266/Print.cpp#L50
size_t Module::serialPrintf(const char* format, ...) {
va_list arg;
va_start(arg, format);
char temp[64];
char* buffer = temp;
size_t len = vsnprintf(temp, sizeof(temp), format, arg);
va_end(arg);
if (len > sizeof(temp) - 1) {
buffer = new char[len + 1];
if (!buffer) {
return 0;
}
va_start(arg, format);
vsnprintf(buffer, len + 1, format, arg);
va_end(arg);
}
2024-05-01 14:51:01 +00:00
len = RADIOLIB_DEBUG_PORT.write(reinterpret_cast<const uint8_t*>(buffer), len);
2023-04-10 12:51:07 +00:00
if (buffer != temp) {
delete[] buffer;
}
return len;
}
#endif
void Module::setRfSwitchPins(uint32_t rxEn, uint32_t txEn) {
[MOD] Generalize rfswitch pin handling This defines operation modes (IDLE, RX and TX) and allows defining up to to three pins to be controlled. For each mode a value can be specified for each pin a table. Compared to the previous handling, this: - Allows up to three pins instead of only two. - Gives more control over output pin values (e.g. to simply change polarity or support more complex control logic). In addition, the modes are treated as opaque by the Module code, allowing radio classes to define their own modes if needed. Some notes regarding the implementation: - The number of pins is limited at three, since most boards seem to need only two pins and only the Nucleo STM32WL55 board needs three. If more pins are needed in the future, the setRfSwitchTable() can be overloaded to accept either a 3-element or e.g. 4-element pins array, to allow new and old code to work as-is. Note that there is a RFSWITCH_MAX_PINS constant defined, but it is not recommended for sketches to use this constant when defining a rfswitch pins array, to prevent issues when this value is ever increased and such an array gets extra zero elements (that will be interpreted as pin 0). Note that this is not a problem for the RfSwitchMode_t values array, since any extra values in there will only be used if a valid pin was set in the pins array. - The pins array is passed by reference, so the compiler complains if the array passed is not the expected size. Since a reference to an array without a length is not supported (at least not by the gcc 7 used by the AVR core - gcc 10 for STM32 seems to accept it), the table array is passed as a pointer instead (but because arrays and pointers are reasonably interchangeable, the caller does not see the difference). - The existing setRfSwitchPins() method is still supported as before. Internally it creates a table with the right values and pins and passes those to setRfSwitchTable. - For easier review, this commit does not modify all calls to setRfSwitchState() in all radio modules yet, but has a compatibility wrapper to delay this change until the next commit. Similarly, the setRfSwitchTable() method is now defined on Module only, a wrapper for it will be defined in all radios that already have the setRfSwitchPins() wrapper in another commit. - To allow future radios to define any number of modes, the modes table does not have a fixed length, but instead is terminated by a special value. This is a bit fragile (if the terminator is omitted, the code will read past the end of the array), but rather flexible. One alternative to this approach would be to make setRfSwitchTable a template that deduces the array size from a template argument and then stores the size explicitly, but using templates probably reduces code clarity.
2022-12-06 16:52:18 +00:00
// This can be on the stack, setRfSwitchTable copies the contents
const uint32_t pins[] = {
[MOD] Generalize rfswitch pin handling This defines operation modes (IDLE, RX and TX) and allows defining up to to three pins to be controlled. For each mode a value can be specified for each pin a table. Compared to the previous handling, this: - Allows up to three pins instead of only two. - Gives more control over output pin values (e.g. to simply change polarity or support more complex control logic). In addition, the modes are treated as opaque by the Module code, allowing radio classes to define their own modes if needed. Some notes regarding the implementation: - The number of pins is limited at three, since most boards seem to need only two pins and only the Nucleo STM32WL55 board needs three. If more pins are needed in the future, the setRfSwitchTable() can be overloaded to accept either a 3-element or e.g. 4-element pins array, to allow new and old code to work as-is. Note that there is a RFSWITCH_MAX_PINS constant defined, but it is not recommended for sketches to use this constant when defining a rfswitch pins array, to prevent issues when this value is ever increased and such an array gets extra zero elements (that will be interpreted as pin 0). Note that this is not a problem for the RfSwitchMode_t values array, since any extra values in there will only be used if a valid pin was set in the pins array. - The pins array is passed by reference, so the compiler complains if the array passed is not the expected size. Since a reference to an array without a length is not supported (at least not by the gcc 7 used by the AVR core - gcc 10 for STM32 seems to accept it), the table array is passed as a pointer instead (but because arrays and pointers are reasonably interchangeable, the caller does not see the difference). - The existing setRfSwitchPins() method is still supported as before. Internally it creates a table with the right values and pins and passes those to setRfSwitchTable. - For easier review, this commit does not modify all calls to setRfSwitchState() in all radio modules yet, but has a compatibility wrapper to delay this change until the next commit. Similarly, the setRfSwitchTable() method is now defined on Module only, a wrapper for it will be defined in all radios that already have the setRfSwitchPins() wrapper in another commit. - To allow future radios to define any number of modes, the modes table does not have a fixed length, but instead is terminated by a special value. This is a bit fragile (if the terminator is omitted, the code will read past the end of the array), but rather flexible. One alternative to this approach would be to make setRfSwitchTable a template that deduces the array size from a template argument and then stores the size explicitly, but using templates probably reduces code clarity.
2022-12-06 16:52:18 +00:00
rxEn, txEn, RADIOLIB_NC,
};
[MOD] Generalize rfswitch pin handling This defines operation modes (IDLE, RX and TX) and allows defining up to to three pins to be controlled. For each mode a value can be specified for each pin a table. Compared to the previous handling, this: - Allows up to three pins instead of only two. - Gives more control over output pin values (e.g. to simply change polarity or support more complex control logic). In addition, the modes are treated as opaque by the Module code, allowing radio classes to define their own modes if needed. Some notes regarding the implementation: - The number of pins is limited at three, since most boards seem to need only two pins and only the Nucleo STM32WL55 board needs three. If more pins are needed in the future, the setRfSwitchTable() can be overloaded to accept either a 3-element or e.g. 4-element pins array, to allow new and old code to work as-is. Note that there is a RFSWITCH_MAX_PINS constant defined, but it is not recommended for sketches to use this constant when defining a rfswitch pins array, to prevent issues when this value is ever increased and such an array gets extra zero elements (that will be interpreted as pin 0). Note that this is not a problem for the RfSwitchMode_t values array, since any extra values in there will only be used if a valid pin was set in the pins array. - The pins array is passed by reference, so the compiler complains if the array passed is not the expected size. Since a reference to an array without a length is not supported (at least not by the gcc 7 used by the AVR core - gcc 10 for STM32 seems to accept it), the table array is passed as a pointer instead (but because arrays and pointers are reasonably interchangeable, the caller does not see the difference). - The existing setRfSwitchPins() method is still supported as before. Internally it creates a table with the right values and pins and passes those to setRfSwitchTable. - For easier review, this commit does not modify all calls to setRfSwitchState() in all radio modules yet, but has a compatibility wrapper to delay this change until the next commit. Similarly, the setRfSwitchTable() method is now defined on Module only, a wrapper for it will be defined in all radios that already have the setRfSwitchPins() wrapper in another commit. - To allow future radios to define any number of modes, the modes table does not have a fixed length, but instead is terminated by a special value. This is a bit fragile (if the terminator is omitted, the code will read past the end of the array), but rather flexible. One alternative to this approach would be to make setRfSwitchTable a template that deduces the array size from a template argument and then stores the size explicitly, but using templates probably reduces code clarity.
2022-12-06 16:52:18 +00:00
// This must be static, since setRfSwitchTable stores a reference.
2023-04-11 02:51:29 +00:00
static const RfSwitchMode_t table[] = {
{ MODE_IDLE, {this->hal->GpioLevelLow, this->hal->GpioLevelLow} },
{ MODE_RX, {this->hal->GpioLevelHigh, this->hal->GpioLevelLow} },
{ MODE_TX, {this->hal->GpioLevelLow, this->hal->GpioLevelHigh} },
[MOD] Generalize rfswitch pin handling This defines operation modes (IDLE, RX and TX) and allows defining up to to three pins to be controlled. For each mode a value can be specified for each pin a table. Compared to the previous handling, this: - Allows up to three pins instead of only two. - Gives more control over output pin values (e.g. to simply change polarity or support more complex control logic). In addition, the modes are treated as opaque by the Module code, allowing radio classes to define their own modes if needed. Some notes regarding the implementation: - The number of pins is limited at three, since most boards seem to need only two pins and only the Nucleo STM32WL55 board needs three. If more pins are needed in the future, the setRfSwitchTable() can be overloaded to accept either a 3-element or e.g. 4-element pins array, to allow new and old code to work as-is. Note that there is a RFSWITCH_MAX_PINS constant defined, but it is not recommended for sketches to use this constant when defining a rfswitch pins array, to prevent issues when this value is ever increased and such an array gets extra zero elements (that will be interpreted as pin 0). Note that this is not a problem for the RfSwitchMode_t values array, since any extra values in there will only be used if a valid pin was set in the pins array. - The pins array is passed by reference, so the compiler complains if the array passed is not the expected size. Since a reference to an array without a length is not supported (at least not by the gcc 7 used by the AVR core - gcc 10 for STM32 seems to accept it), the table array is passed as a pointer instead (but because arrays and pointers are reasonably interchangeable, the caller does not see the difference). - The existing setRfSwitchPins() method is still supported as before. Internally it creates a table with the right values and pins and passes those to setRfSwitchTable. - For easier review, this commit does not modify all calls to setRfSwitchState() in all radio modules yet, but has a compatibility wrapper to delay this change until the next commit. Similarly, the setRfSwitchTable() method is now defined on Module only, a wrapper for it will be defined in all radios that already have the setRfSwitchPins() wrapper in another commit. - To allow future radios to define any number of modes, the modes table does not have a fixed length, but instead is terminated by a special value. This is a bit fragile (if the terminator is omitted, the code will read past the end of the array), but rather flexible. One alternative to this approach would be to make setRfSwitchTable a template that deduces the array size from a template argument and then stores the size explicitly, but using templates probably reduces code clarity.
2022-12-06 16:52:18 +00:00
END_OF_MODE_TABLE,
};
setRfSwitchTable(pins, table);
}
void Module::setRfSwitchTable(const uint32_t (&pins)[3], const RfSwitchMode_t table[]) {
memcpy(this->rfSwitchPins, pins, sizeof(this->rfSwitchPins));
this->rfSwitchTable = table;
[MOD] Generalize rfswitch pin handling This defines operation modes (IDLE, RX and TX) and allows defining up to to three pins to be controlled. For each mode a value can be specified for each pin a table. Compared to the previous handling, this: - Allows up to three pins instead of only two. - Gives more control over output pin values (e.g. to simply change polarity or support more complex control logic). In addition, the modes are treated as opaque by the Module code, allowing radio classes to define their own modes if needed. Some notes regarding the implementation: - The number of pins is limited at three, since most boards seem to need only two pins and only the Nucleo STM32WL55 board needs three. If more pins are needed in the future, the setRfSwitchTable() can be overloaded to accept either a 3-element or e.g. 4-element pins array, to allow new and old code to work as-is. Note that there is a RFSWITCH_MAX_PINS constant defined, but it is not recommended for sketches to use this constant when defining a rfswitch pins array, to prevent issues when this value is ever increased and such an array gets extra zero elements (that will be interpreted as pin 0). Note that this is not a problem for the RfSwitchMode_t values array, since any extra values in there will only be used if a valid pin was set in the pins array. - The pins array is passed by reference, so the compiler complains if the array passed is not the expected size. Since a reference to an array without a length is not supported (at least not by the gcc 7 used by the AVR core - gcc 10 for STM32 seems to accept it), the table array is passed as a pointer instead (but because arrays and pointers are reasonably interchangeable, the caller does not see the difference). - The existing setRfSwitchPins() method is still supported as before. Internally it creates a table with the right values and pins and passes those to setRfSwitchTable. - For easier review, this commit does not modify all calls to setRfSwitchState() in all radio modules yet, but has a compatibility wrapper to delay this change until the next commit. Similarly, the setRfSwitchTable() method is now defined on Module only, a wrapper for it will be defined in all radios that already have the setRfSwitchPins() wrapper in another commit. - To allow future radios to define any number of modes, the modes table does not have a fixed length, but instead is terminated by a special value. This is a bit fragile (if the terminator is omitted, the code will read past the end of the array), but rather flexible. One alternative to this approach would be to make setRfSwitchTable a template that deduces the array size from a template argument and then stores the size explicitly, but using templates probably reduces code clarity.
2022-12-06 16:52:18 +00:00
for(size_t i = 0; i < RFSWITCH_MAX_PINS; i++)
2023-04-11 02:51:29 +00:00
this->hal->pinMode(pins[i], this->hal->GpioModeOutput);
[MOD] Generalize rfswitch pin handling This defines operation modes (IDLE, RX and TX) and allows defining up to to three pins to be controlled. For each mode a value can be specified for each pin a table. Compared to the previous handling, this: - Allows up to three pins instead of only two. - Gives more control over output pin values (e.g. to simply change polarity or support more complex control logic). In addition, the modes are treated as opaque by the Module code, allowing radio classes to define their own modes if needed. Some notes regarding the implementation: - The number of pins is limited at three, since most boards seem to need only two pins and only the Nucleo STM32WL55 board needs three. If more pins are needed in the future, the setRfSwitchTable() can be overloaded to accept either a 3-element or e.g. 4-element pins array, to allow new and old code to work as-is. Note that there is a RFSWITCH_MAX_PINS constant defined, but it is not recommended for sketches to use this constant when defining a rfswitch pins array, to prevent issues when this value is ever increased and such an array gets extra zero elements (that will be interpreted as pin 0). Note that this is not a problem for the RfSwitchMode_t values array, since any extra values in there will only be used if a valid pin was set in the pins array. - The pins array is passed by reference, so the compiler complains if the array passed is not the expected size. Since a reference to an array without a length is not supported (at least not by the gcc 7 used by the AVR core - gcc 10 for STM32 seems to accept it), the table array is passed as a pointer instead (but because arrays and pointers are reasonably interchangeable, the caller does not see the difference). - The existing setRfSwitchPins() method is still supported as before. Internally it creates a table with the right values and pins and passes those to setRfSwitchTable. - For easier review, this commit does not modify all calls to setRfSwitchState() in all radio modules yet, but has a compatibility wrapper to delay this change until the next commit. Similarly, the setRfSwitchTable() method is now defined on Module only, a wrapper for it will be defined in all radios that already have the setRfSwitchPins() wrapper in another commit. - To allow future radios to define any number of modes, the modes table does not have a fixed length, but instead is terminated by a special value. This is a bit fragile (if the terminator is omitted, the code will read past the end of the array), but rather flexible. One alternative to this approach would be to make setRfSwitchTable a template that deduces the array size from a template argument and then stores the size explicitly, but using templates probably reduces code clarity.
2022-12-06 16:52:18 +00:00
}
const Module::RfSwitchMode_t *Module::findRfSwitchMode(uint8_t mode) const {
const RfSwitchMode_t *row = this->rfSwitchTable;
[MOD] Generalize rfswitch pin handling This defines operation modes (IDLE, RX and TX) and allows defining up to to three pins to be controlled. For each mode a value can be specified for each pin a table. Compared to the previous handling, this: - Allows up to three pins instead of only two. - Gives more control over output pin values (e.g. to simply change polarity or support more complex control logic). In addition, the modes are treated as opaque by the Module code, allowing radio classes to define their own modes if needed. Some notes regarding the implementation: - The number of pins is limited at three, since most boards seem to need only two pins and only the Nucleo STM32WL55 board needs three. If more pins are needed in the future, the setRfSwitchTable() can be overloaded to accept either a 3-element or e.g. 4-element pins array, to allow new and old code to work as-is. Note that there is a RFSWITCH_MAX_PINS constant defined, but it is not recommended for sketches to use this constant when defining a rfswitch pins array, to prevent issues when this value is ever increased and such an array gets extra zero elements (that will be interpreted as pin 0). Note that this is not a problem for the RfSwitchMode_t values array, since any extra values in there will only be used if a valid pin was set in the pins array. - The pins array is passed by reference, so the compiler complains if the array passed is not the expected size. Since a reference to an array without a length is not supported (at least not by the gcc 7 used by the AVR core - gcc 10 for STM32 seems to accept it), the table array is passed as a pointer instead (but because arrays and pointers are reasonably interchangeable, the caller does not see the difference). - The existing setRfSwitchPins() method is still supported as before. Internally it creates a table with the right values and pins and passes those to setRfSwitchTable. - For easier review, this commit does not modify all calls to setRfSwitchState() in all radio modules yet, but has a compatibility wrapper to delay this change until the next commit. Similarly, the setRfSwitchTable() method is now defined on Module only, a wrapper for it will be defined in all radios that already have the setRfSwitchPins() wrapper in another commit. - To allow future radios to define any number of modes, the modes table does not have a fixed length, but instead is terminated by a special value. This is a bit fragile (if the terminator is omitted, the code will read past the end of the array), but rather flexible. One alternative to this approach would be to make setRfSwitchTable a template that deduces the array size from a template argument and then stores the size explicitly, but using templates probably reduces code clarity.
2022-12-06 16:52:18 +00:00
while (row && row->mode != MODE_END_OF_TABLE) {
if (row->mode == mode)
return row;
++row;
}
return nullptr;
2020-06-18 14:31:38 +00:00
}
[MOD] Generalize rfswitch pin handling This defines operation modes (IDLE, RX and TX) and allows defining up to to three pins to be controlled. For each mode a value can be specified for each pin a table. Compared to the previous handling, this: - Allows up to three pins instead of only two. - Gives more control over output pin values (e.g. to simply change polarity or support more complex control logic). In addition, the modes are treated as opaque by the Module code, allowing radio classes to define their own modes if needed. Some notes regarding the implementation: - The number of pins is limited at three, since most boards seem to need only two pins and only the Nucleo STM32WL55 board needs three. If more pins are needed in the future, the setRfSwitchTable() can be overloaded to accept either a 3-element or e.g. 4-element pins array, to allow new and old code to work as-is. Note that there is a RFSWITCH_MAX_PINS constant defined, but it is not recommended for sketches to use this constant when defining a rfswitch pins array, to prevent issues when this value is ever increased and such an array gets extra zero elements (that will be interpreted as pin 0). Note that this is not a problem for the RfSwitchMode_t values array, since any extra values in there will only be used if a valid pin was set in the pins array. - The pins array is passed by reference, so the compiler complains if the array passed is not the expected size. Since a reference to an array without a length is not supported (at least not by the gcc 7 used by the AVR core - gcc 10 for STM32 seems to accept it), the table array is passed as a pointer instead (but because arrays and pointers are reasonably interchangeable, the caller does not see the difference). - The existing setRfSwitchPins() method is still supported as before. Internally it creates a table with the right values and pins and passes those to setRfSwitchTable. - For easier review, this commit does not modify all calls to setRfSwitchState() in all radio modules yet, but has a compatibility wrapper to delay this change until the next commit. Similarly, the setRfSwitchTable() method is now defined on Module only, a wrapper for it will be defined in all radios that already have the setRfSwitchPins() wrapper in another commit. - To allow future radios to define any number of modes, the modes table does not have a fixed length, but instead is terminated by a special value. This is a bit fragile (if the terminator is omitted, the code will read past the end of the array), but rather flexible. One alternative to this approach would be to make setRfSwitchTable a template that deduces the array size from a template argument and then stores the size explicitly, but using templates probably reduces code clarity.
2022-12-06 16:52:18 +00:00
void Module::setRfSwitchState(uint8_t mode) {
const RfSwitchMode_t *row = findRfSwitchMode(mode);
if(!row) {
// RF switch control is disabled or does not have this mode
2020-06-18 14:31:38 +00:00
return;
}
// set pins
const uint32_t *value = &row->values[0];
[MOD] Generalize rfswitch pin handling This defines operation modes (IDLE, RX and TX) and allows defining up to to three pins to be controlled. For each mode a value can be specified for each pin a table. Compared to the previous handling, this: - Allows up to three pins instead of only two. - Gives more control over output pin values (e.g. to simply change polarity or support more complex control logic). In addition, the modes are treated as opaque by the Module code, allowing radio classes to define their own modes if needed. Some notes regarding the implementation: - The number of pins is limited at three, since most boards seem to need only two pins and only the Nucleo STM32WL55 board needs three. If more pins are needed in the future, the setRfSwitchTable() can be overloaded to accept either a 3-element or e.g. 4-element pins array, to allow new and old code to work as-is. Note that there is a RFSWITCH_MAX_PINS constant defined, but it is not recommended for sketches to use this constant when defining a rfswitch pins array, to prevent issues when this value is ever increased and such an array gets extra zero elements (that will be interpreted as pin 0). Note that this is not a problem for the RfSwitchMode_t values array, since any extra values in there will only be used if a valid pin was set in the pins array. - The pins array is passed by reference, so the compiler complains if the array passed is not the expected size. Since a reference to an array without a length is not supported (at least not by the gcc 7 used by the AVR core - gcc 10 for STM32 seems to accept it), the table array is passed as a pointer instead (but because arrays and pointers are reasonably interchangeable, the caller does not see the difference). - The existing setRfSwitchPins() method is still supported as before. Internally it creates a table with the right values and pins and passes those to setRfSwitchTable. - For easier review, this commit does not modify all calls to setRfSwitchState() in all radio modules yet, but has a compatibility wrapper to delay this change until the next commit. Similarly, the setRfSwitchTable() method is now defined on Module only, a wrapper for it will be defined in all radios that already have the setRfSwitchPins() wrapper in another commit. - To allow future radios to define any number of modes, the modes table does not have a fixed length, but instead is terminated by a special value. This is a bit fragile (if the terminator is omitted, the code will read past the end of the array), but rather flexible. One alternative to this approach would be to make setRfSwitchTable a template that deduces the array size from a template argument and then stores the size explicitly, but using templates probably reduces code clarity.
2022-12-06 16:52:18 +00:00
for(size_t i = 0; i < RFSWITCH_MAX_PINS; i++) {
uint32_t pin = this->rfSwitchPins[i];
[MOD] Generalize rfswitch pin handling This defines operation modes (IDLE, RX and TX) and allows defining up to to three pins to be controlled. For each mode a value can be specified for each pin a table. Compared to the previous handling, this: - Allows up to three pins instead of only two. - Gives more control over output pin values (e.g. to simply change polarity or support more complex control logic). In addition, the modes are treated as opaque by the Module code, allowing radio classes to define their own modes if needed. Some notes regarding the implementation: - The number of pins is limited at three, since most boards seem to need only two pins and only the Nucleo STM32WL55 board needs three. If more pins are needed in the future, the setRfSwitchTable() can be overloaded to accept either a 3-element or e.g. 4-element pins array, to allow new and old code to work as-is. Note that there is a RFSWITCH_MAX_PINS constant defined, but it is not recommended for sketches to use this constant when defining a rfswitch pins array, to prevent issues when this value is ever increased and such an array gets extra zero elements (that will be interpreted as pin 0). Note that this is not a problem for the RfSwitchMode_t values array, since any extra values in there will only be used if a valid pin was set in the pins array. - The pins array is passed by reference, so the compiler complains if the array passed is not the expected size. Since a reference to an array without a length is not supported (at least not by the gcc 7 used by the AVR core - gcc 10 for STM32 seems to accept it), the table array is passed as a pointer instead (but because arrays and pointers are reasonably interchangeable, the caller does not see the difference). - The existing setRfSwitchPins() method is still supported as before. Internally it creates a table with the right values and pins and passes those to setRfSwitchTable. - For easier review, this commit does not modify all calls to setRfSwitchState() in all radio modules yet, but has a compatibility wrapper to delay this change until the next commit. Similarly, the setRfSwitchTable() method is now defined on Module only, a wrapper for it will be defined in all radios that already have the setRfSwitchPins() wrapper in another commit. - To allow future radios to define any number of modes, the modes table does not have a fixed length, but instead is terminated by a special value. This is a bit fragile (if the terminator is omitted, the code will read past the end of the array), but rather flexible. One alternative to this approach would be to make setRfSwitchTable a template that deduces the array size from a template argument and then stores the size explicitly, but using templates probably reduces code clarity.
2022-12-06 16:52:18 +00:00
if (pin != RADIOLIB_NC)
2023-04-11 02:51:29 +00:00
this->hal->digitalWrite(pin, *value);
[MOD] Generalize rfswitch pin handling This defines operation modes (IDLE, RX and TX) and allows defining up to to three pins to be controlled. For each mode a value can be specified for each pin a table. Compared to the previous handling, this: - Allows up to three pins instead of only two. - Gives more control over output pin values (e.g. to simply change polarity or support more complex control logic). In addition, the modes are treated as opaque by the Module code, allowing radio classes to define their own modes if needed. Some notes regarding the implementation: - The number of pins is limited at three, since most boards seem to need only two pins and only the Nucleo STM32WL55 board needs three. If more pins are needed in the future, the setRfSwitchTable() can be overloaded to accept either a 3-element or e.g. 4-element pins array, to allow new and old code to work as-is. Note that there is a RFSWITCH_MAX_PINS constant defined, but it is not recommended for sketches to use this constant when defining a rfswitch pins array, to prevent issues when this value is ever increased and such an array gets extra zero elements (that will be interpreted as pin 0). Note that this is not a problem for the RfSwitchMode_t values array, since any extra values in there will only be used if a valid pin was set in the pins array. - The pins array is passed by reference, so the compiler complains if the array passed is not the expected size. Since a reference to an array without a length is not supported (at least not by the gcc 7 used by the AVR core - gcc 10 for STM32 seems to accept it), the table array is passed as a pointer instead (but because arrays and pointers are reasonably interchangeable, the caller does not see the difference). - The existing setRfSwitchPins() method is still supported as before. Internally it creates a table with the right values and pins and passes those to setRfSwitchTable. - For easier review, this commit does not modify all calls to setRfSwitchState() in all radio modules yet, but has a compatibility wrapper to delay this change until the next commit. Similarly, the setRfSwitchTable() method is now defined on Module only, a wrapper for it will be defined in all radios that already have the setRfSwitchPins() wrapper in another commit. - To allow future radios to define any number of modes, the modes table does not have a fixed length, but instead is terminated by a special value. This is a bit fragile (if the terminator is omitted, the code will read past the end of the array), but rather flexible. One alternative to this approach would be to make setRfSwitchTable a template that deduces the array size from a template argument and then stores the size explicitly, but using templates probably reduces code clarity.
2022-12-06 16:52:18 +00:00
++value;
}
2020-06-18 14:31:38 +00:00
}