RadioLib/src/Module.cpp

729 wiersze
18 KiB
C++
Czysty Zwykły widok Historia

2018-03-05 16:08:42 +00:00
#include "Module.h"
2021-11-14 10:33:35 +00:00
#if defined(RADIOLIB_BUILD_ARDUINO)
// we need this to emulate tone() on mbed Arduino boards
#if defined(RADIOLIB_MBED_TONE_OVERRIDE)
#include "mbed.h"
mbed::PwmOut *pwmPin = NULL;
#endif
[MOD] Remove constexpr usage This was introduced when STM32WL support was added. Using constexpr for the END_OF_MODE_TABLE constant allows it to be initialized in the class declaration, but this needs C++11. This moves the initialization out of the class declaration to the .cpp file, which does not need constexpr. It also lets STM32WLx::END_OF_MODE_TABLE define its value directly (instead of aliasing Module::END_OF_MODE_TABLE) to prevent reduce runtime overhead (see below). The downside of this change is that the value of the END_OF_MODE_TABLE is no longer visible in other compilation units and thus cannot be inlined into the rfswitch_table (if used). For example, on STM32, this means that instead of having a pre-cooked rfswitch_table that lives in the .rodata section (so can be read directly from flash), the table lives in RAM and is initialized at runtime (the actual modes and pins are copied from flash to RAM by the standard startup loop that copies all of the .data section, and the END_OF_MODE_TABLE value is copied by a bit of new generated code). This means a little more runtime overhead, but the cost is mostly in RAM size (80 bytes for the SMT32WL sketches, 16 per mode plus 16 for the END_OF_MODE_TABLE). In a first attempt at this commit, STM32WLx::END_OF_MODE_TABLE was still initialized using the Module::END_OF_MODE_TABLE value, but since the latter is also not available at compiletime, this meant initialization of the former also needed to happen at runtime, adding even more code overhead (and possibly leading to ordering issues as well). To avoid this, the STM32WLx::END_OF_MODE_TABLE initialization now just duplicates that of Module::END_OF_MODE_TABLE. On AVR, the impact is not so much: Since AVR cannot address flash directly, the table was already copied from flash to RAM at startup, so the extra RAM usage is just 4 bytes because END_OF_MODE_TABLE is now also present in RAM, to be copied into rfswitch_table at startup. Options for avoiding this overhead (not implemented in this commit) could be (in no particular order): 1. Use a macro instead of a constant. Downside is that these cannot be scoped inside the Module/STM32WLx classes like now, so this requires changes to sketches that use a rfswitch_table (and reduced scoping and using macros adds more opportunity for conflicts and weird errors). 2. Apply the change in this commit only when C++11 is not available. Downside is that the initialization value of these constants must be duplicated in the .h and .cpp file for C++ and older versions respectively. 3. Let sketches just use `{Module::MODE_END_OF_TABLE, {}}` explicitly instead of `Module::END_OF_MODE_TABLE`. Downside of this is that this requires sketches to be modified and that it lets the sketch encode more of the table structure, potentially making future API changes harder (but it probably does not really matter in practice). 4. Turn END_OF_MODE_TABLE into a static method, which *can* then be defined in the class declaration and inlined. The method can then be conditionally marked as constexpr, which allows C++11 compilers to completely resolve the rfswitch_table value at compiletime, producing a binary identical to before this commit. When constexpr is omitted (e.g. on older compilers), some runtime overhead is added (pretty much the same as the result from this commit). Downside is that sketches must be modified, and the `END_OF_MODE_TABLE` "constant" must now be called, e.g. `END_OF_MODE_TABLE()` which might be a bit unexpected syntax.
2023-02-03 09:05:22 +00:00
const Module::RfSwitchMode_t Module::END_OF_MODE_TABLE = {Module::MODE_END_OF_TABLE, {}};
2020-07-04 14:05:56 +00:00
Module::Module(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE gpio):
_cs(cs),
_irq(irq),
_rst(rst),
2021-11-14 10:33:35 +00:00
_gpio(gpio)
2020-07-04 14:05:56 +00:00
{
_spi = &RADIOLIB_DEFAULT_SPI;
_initInterface = true;
2021-11-14 10:33:35 +00:00
// this is Arduino build, pre-set callbacks
setCb_pinMode(::pinMode);
setCb_digitalRead(::digitalRead);
setCb_digitalWrite(::digitalWrite);
#if !defined(RADIOLIB_TONE_UNSUPPORTED)
setCb_tone(::tone);
setCb_noTone(::noTone);
#endif
setCb_attachInterrupt(::attachInterrupt);
setCb_detachInterrupt(::detachInterrupt);
#if !defined(RADIOLIB_YIELD_UNSUPPORTED)
setCb_yield(::yield);
#endif
setCb_delay(::delay);
setCb_delayMicroseconds(::delayMicroseconds);
setCb_millis(::millis);
setCb_micros(::micros);
2022-08-07 07:57:41 +00:00
setCb_pulseIn(::pulseIn);
2021-11-14 10:33:35 +00:00
setCb_SPIbegin(&Module::SPIbegin);
setCb_SPIbeginTransaction(&Module::beginTransaction);
setCb_SPItransfer(&Module::transfer);
setCb_SPIendTransaction(&Module::endTransaction);
setCb_SPIend(&Module::end);
2018-07-11 16:15:54 +00:00
}
2020-07-04 14:05:56 +00:00
Module::Module(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE gpio, SPIClass& spi, SPISettings spiSettings):
_cs(cs),
_irq(irq),
_rst(rst),
2021-11-14 10:33:35 +00:00
_gpio(gpio),
2020-07-04 14:05:56 +00:00
_spiSettings(spiSettings)
{
_spi = &spi;
_initInterface = false;
2021-11-14 10:33:35 +00:00
// this is Arduino build, pre-set callbacks
setCb_pinMode(::pinMode);
setCb_digitalRead(::digitalRead);
setCb_digitalWrite(::digitalWrite);
#if !defined(RADIOLIB_TONE_UNSUPPORTED)
setCb_tone(::tone);
setCb_noTone(::noTone);
#endif
setCb_attachInterrupt(::attachInterrupt);
setCb_detachInterrupt(::detachInterrupt);
#if !defined(RADIOLIB_YIELD_UNSUPPORTED)
setCb_yield(::yield);
#endif
setCb_delay(::delay);
setCb_delayMicroseconds(::delayMicroseconds);
setCb_millis(::millis);
setCb_micros(::micros);
2022-08-07 07:57:41 +00:00
setCb_pulseIn(::pulseIn);
2021-11-14 10:33:35 +00:00
setCb_SPIbegin(&Module::SPIbegin);
setCb_SPIbeginTransaction(&Module::beginTransaction);
setCb_SPItransfer(&Module::transfer);
setCb_SPIendTransaction(&Module::endTransaction);
setCb_SPIend(&Module::end);
}
2021-11-14 10:33:35 +00:00
#else
2021-11-14 10:33:35 +00:00
Module::Module(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE gpio):
2020-07-04 14:05:56 +00:00
_cs(cs),
_irq(irq),
_rst(rst),
2021-11-14 10:33:35 +00:00
_gpio(gpio)
2020-07-04 14:05:56 +00:00
{
2021-11-14 10:33:35 +00:00
// not an Arduino build, it's up to the user to set all callbacks
}
2019-05-13 13:03:09 +00:00
#endif
2018-03-05 16:08:42 +00:00
Module::Module(const Module& mod) {
*this = mod;
}
Module& Module::operator=(const Module& mod) {
this->SPIreadCommand = mod.SPIreadCommand;
this->SPIwriteCommand = mod.SPIwriteCommand;
this->_cs = mod.getCs();
this->_irq = mod.getIrq();
this->_rst = mod.getRst();
2021-11-14 10:33:35 +00:00
this->_gpio = mod.getGpio();
return(*this);
}
2021-11-14 10:33:35 +00:00
void Module::init() {
this->pinMode(_cs, OUTPUT);
this->digitalWrite(_cs, HIGH);
#if defined(RADIOLIB_BUILD_ARDUINO)
2021-11-14 10:33:35 +00:00
if(_initInterface) {
(this->*cb_SPIbegin)();
2018-03-05 16:08:42 +00:00
}
#endif
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)
#if defined(RADIOLIB_BUILD_ARDUINO)
if(!_initInterface) {
return;
}
2021-11-14 10:33:35 +00:00
if(_spi != nullptr) {
this->SPIend();
2018-03-05 16:08:42 +00:00
}
#endif
2018-03-05 16:08:42 +00:00
}
2018-07-23 09:19:34 +00:00
int16_t Module::SPIgetRegValue(uint8_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);
}
2021-04-15 17:34:53 +00:00
int16_t Module::SPIsetRegValue(uint8_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 defined(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)
2021-11-14 10:33:35 +00:00
uint32_t start = this->micros();
uint8_t readValue = 0x00;
2021-11-14 10:33:35 +00:00
while(this->micros() - start < (checkInterval * 1000)) {
readValue = SPIreadRegister(reg);
2021-04-15 17:34:53 +00:00
if((readValue & checkMask) == (newValue & checkMask)) {
// check passed, we can stop the loop
2021-11-14 10:33:35 +00:00
return(RADIOLIB_ERR_NONE);
}
2018-10-31 16:44:47 +00:00
}
2019-05-13 13:03:09 +00:00
// check failed, print debug info
RADIOLIB_DEBUG_PRINTLN();
RADIOLIB_DEBUG_PRINT(F("address:\t0x"));
RADIOLIB_DEBUG_PRINTLN(reg, HEX);
RADIOLIB_DEBUG_PRINT(F("bits:\t\t"));
RADIOLIB_DEBUG_PRINT(msb);
RADIOLIB_DEBUG_PRINT(' ');
RADIOLIB_DEBUG_PRINTLN(lsb);
RADIOLIB_DEBUG_PRINT(F("value:\t\t0b"));
RADIOLIB_DEBUG_PRINTLN(value, BIN);
RADIOLIB_DEBUG_PRINT(F("current:\t0b"));
RADIOLIB_DEBUG_PRINTLN(currentValue, BIN);
RADIOLIB_DEBUG_PRINT(F("mask:\t\t0b"));
RADIOLIB_DEBUG_PRINTLN(mask, BIN);
RADIOLIB_DEBUG_PRINT(F("new:\t\t0b"));
RADIOLIB_DEBUG_PRINTLN(newValue, BIN);
RADIOLIB_DEBUG_PRINT(F("read:\t\t0b"));
RADIOLIB_DEBUG_PRINTLN(readValue, BIN);
RADIOLIB_DEBUG_PRINTLN();
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(uint8_t reg, uint8_t numBytes, uint8_t* inBytes) {
2019-03-22 18:01:56 +00:00
SPItransfer(SPIreadCommand, reg, NULL, inBytes, numBytes);
2018-03-05 16:08:42 +00:00
}
uint8_t Module::SPIreadRegister(uint8_t reg) {
2019-11-23 09:10:53 +00:00
uint8_t resp = 0;
2019-03-22 18:01:56 +00:00
SPItransfer(SPIreadCommand, reg, NULL, &resp, 1);
return(resp);
2018-03-05 16:08:42 +00:00
}
void Module::SPIwriteRegisterBurst(uint8_t reg, uint8_t* data, uint8_t numBytes) {
2019-03-22 18:01:56 +00:00
SPItransfer(SPIwriteCommand, reg, data, NULL, numBytes);
2018-03-05 16:08:42 +00:00
}
void Module::SPIwriteRegister(uint8_t reg, uint8_t data) {
2019-03-22 18:01:56 +00:00
SPItransfer(SPIwriteCommand, reg, &data, NULL, 1);
}
void Module::SPItransfer(uint8_t cmd, uint8_t reg, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes) {
// start SPI transaction
2021-11-14 10:33:35 +00:00
this->SPIbeginTransaction();
2019-05-13 13:03:09 +00:00
2019-03-22 18:01:56 +00:00
// pull CS low
2021-11-14 10:33:35 +00:00
this->digitalWrite(_cs, LOW);
2019-05-13 13:03:09 +00:00
2019-03-22 18:01:56 +00:00
// send SPI register address with access command
2021-11-14 10:33:35 +00:00
this->SPItransfer(reg | cmd);
#if defined(RADIOLIB_VERBOSE)
2020-01-06 16:20:18 +00:00
if(cmd == SPIwriteCommand) {
RADIOLIB_VERBOSE_PRINT('W');
} else if(cmd == SPIreadCommand) {
RADIOLIB_VERBOSE_PRINT('R');
}
RADIOLIB_VERBOSE_PRINT('\t')
RADIOLIB_VERBOSE_PRINT(reg, HEX);
RADIOLIB_VERBOSE_PRINT('\t');
#endif
2019-05-13 13:03:09 +00:00
2019-03-22 18:01:56 +00:00
// send data or get response
if(cmd == SPIwriteCommand) {
2020-07-04 11:43:39 +00:00
if(dataOut != NULL) {
for(size_t n = 0; n < numBytes; n++) {
2021-11-14 10:33:35 +00:00
this->SPItransfer(dataOut[n]);
2020-07-04 11:43:39 +00:00
RADIOLIB_VERBOSE_PRINT(dataOut[n], HEX);
RADIOLIB_VERBOSE_PRINT('\t');
}
2019-03-22 18:01:56 +00:00
}
} else if (cmd == SPIreadCommand) {
2020-07-04 11:43:39 +00:00
if(dataIn != NULL) {
for(size_t n = 0; n < numBytes; n++) {
2021-11-14 10:33:35 +00:00
dataIn[n] = this->SPItransfer(0x00);
2020-07-04 11:43:39 +00:00
RADIOLIB_VERBOSE_PRINT(dataIn[n], HEX);
RADIOLIB_VERBOSE_PRINT('\t');
}
2019-03-22 18:01:56 +00:00
}
}
2019-09-28 08:30:50 +00:00
RADIOLIB_VERBOSE_PRINTLN();
2019-05-13 13:03:09 +00:00
2019-03-22 18:01:56 +00:00
// release CS
2021-11-14 10:33:35 +00:00
this->digitalWrite(_cs, HIGH);
2019-05-13 13:03:09 +00:00
2019-03-22 18:01:56 +00:00
// end SPI transaction
2021-11-14 10:33:35 +00:00
this->SPIendTransaction();
2018-03-05 16:08:42 +00:00
}
2019-12-01 07:12:04 +00:00
2023-02-19 11:32:17 +00:00
int16_t Module::SPItransferStream(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes, bool waitForGpio, uint32_t timeout) {
#if defined(RADIOLIB_VERBOSE)
uint8_t debugBuff[RADIOLIB_STATIC_ARRAY_SIZE];
#endif
// pull NSS low
this->digitalWrite(this->getCs(), LOW);
// ensure GPIO is low
uint32_t start = this->millis();
while(this->digitalRead(this->getGpio())) {
this->yield();
if(this->millis() - start >= timeout) {
this->digitalWrite(this->getCs(), HIGH);
return(RADIOLIB_ERR_SPI_CMD_TIMEOUT);
}
}
// start transfer
this->SPIbeginTransaction();
// send command byte(s)
for(uint8_t n = 0; n < cmdLen; n++) {
this->SPItransfer(cmd[n]);
}
// variable to save error during SPI transfer
int16_t state = RADIOLIB_ERR_NONE;
// send/receive all bytes
if(write) {
for(uint8_t n = 0; n < numBytes; n++) {
// send byte
uint8_t in = this->SPItransfer(dataOut[n]);
#if defined(RADIOLIB_VERBOSE)
debugBuff[n] = in;
#endif
// check status
if(this->SPIparseStatusCb != nullptr) {
state = this->SPIparseStatusCb(in);
}
}
} else {
// skip the first byte for read-type commands (status-only)
uint8_t in = this->SPItransfer(this->SPIreadCommand);
#if defined(RADIOLIB_VERBOSE)
debugBuff[0] = in;
#endif
// check status
if(this->SPIparseStatusCb != nullptr) {
state = this->SPIparseStatusCb(in);
} else {
state = RADIOLIB_ERR_NONE;
}
// read the data
if(state == RADIOLIB_ERR_NONE) {
for(uint8_t n = 0; n < numBytes; n++) {
dataIn[n] = this->SPItransfer(this->SPIreadCommand);
}
}
}
// stop transfer
this->SPIendTransaction();
this->digitalWrite(this->getCs(), HIGH);
// wait for GPIO to go high and then low
if(waitForGpio) {
this->delayMicroseconds(1);
uint32_t start = this->millis();
while(this->digitalRead(this->getGpio())) {
this->yield();
if(this->millis() - start >= timeout) {
state = RADIOLIB_ERR_SPI_CMD_TIMEOUT;
break;
}
}
}
// print debug output
#if defined(RADIOLIB_VERBOSE)
// print command byte(s)
RADIOLIB_VERBOSE_PRINT("CMD\t");
for(uint8_t n = 0; n < cmdLen; n++) {
RADIOLIB_VERBOSE_PRINT(cmd[n], HEX);
RADIOLIB_VERBOSE_PRINT('\t');
}
RADIOLIB_VERBOSE_PRINTLN();
// print data bytes
RADIOLIB_VERBOSE_PRINT("DAT");
if(write) {
RADIOLIB_VERBOSE_PRINT("W\t");
for(uint8_t n = 0; n < numBytes; n++) {
RADIOLIB_VERBOSE_PRINT(dataOut[n], HEX);
RADIOLIB_VERBOSE_PRINT('\t');
RADIOLIB_VERBOSE_PRINT(debugBuff[n], HEX);
RADIOLIB_VERBOSE_PRINT('\t');
}
RADIOLIB_VERBOSE_PRINTLN();
} else {
RADIOLIB_VERBOSE_PRINT("R\t");
// skip the first byte for read-type commands (status-only)
RADIOLIB_VERBOSE_PRINT(this->SPIreadCommand, HEX);
RADIOLIB_VERBOSE_PRINT('\t');
RADIOLIB_VERBOSE_PRINT(debugBuff[0], HEX);
RADIOLIB_VERBOSE_PRINT('\t')
for(uint8_t n = 0; n < numBytes; n++) {
RADIOLIB_VERBOSE_PRINT(this->SPIreadCommand, HEX);
RADIOLIB_VERBOSE_PRINT('\t');
RADIOLIB_VERBOSE_PRINT(dataIn[n], HEX);
RADIOLIB_VERBOSE_PRINT('\t');
}
RADIOLIB_VERBOSE_PRINTLN();
}
RADIOLIB_VERBOSE_PRINTLN();
#endif
return(state);
}
void Module::waitForMicroseconds(uint32_t start, uint32_t len) {
#if defined(RADIOLIB_INTERRUPT_TIMING)
(void)start;
if((this->TimerSetupCb != nullptr) && (len != this->_prevTimingLen)) {
_prevTimingLen = len;
this->TimerSetupCb(len);
}
this->TimerFlag = false;
while(!this->TimerFlag) {
this->yield();
}
#else
while(this->micros() - start < len) {
this->yield();
}
#endif
}
2020-03-27 13:10:45 +00:00
void Module::pinMode(RADIOLIB_PIN_TYPE pin, RADIOLIB_PIN_MODE mode) {
2021-11-14 10:33:35 +00:00
if((pin == RADIOLIB_NC) || (cb_pinMode == nullptr)) {
return;
}
2021-11-14 10:33:35 +00:00
cb_pinMode(pin, mode);
}
2020-03-27 13:10:45 +00:00
void Module::digitalWrite(RADIOLIB_PIN_TYPE pin, RADIOLIB_PIN_STATUS value) {
2021-11-14 10:33:35 +00:00
if((pin == RADIOLIB_NC) || (cb_digitalWrite == nullptr)) {
return;
2019-12-01 07:12:04 +00:00
}
2021-11-14 10:33:35 +00:00
cb_digitalWrite(pin, value);
2019-12-01 07:12:04 +00:00
}
2020-03-27 13:10:45 +00:00
RADIOLIB_PIN_STATUS Module::digitalRead(RADIOLIB_PIN_TYPE pin) {
2021-11-14 10:33:35 +00:00
if((pin == RADIOLIB_NC) || (cb_digitalRead == nullptr)) {
return((RADIOLIB_PIN_STATUS)0);
2020-03-27 13:10:45 +00:00
}
2021-11-14 10:33:35 +00:00
return(cb_digitalRead(pin));
2020-03-27 13:10:45 +00:00
}
2020-04-30 15:07:28 +00:00
2022-08-09 18:20:19 +00:00
#if defined(ESP32)
// we need to cache the previous tone value for emulation on ESP32
int32_t prev = -1;
#endif
2021-11-14 10:33:35 +00:00
void Module::tone(RADIOLIB_PIN_TYPE pin, uint16_t value, uint32_t duration) {
#if !defined(RADIOLIB_TONE_UNSUPPORTED)
if((pin == RADIOLIB_NC) || (cb_tone == nullptr)) {
2020-10-28 10:24:05 +00:00
return;
2020-04-30 15:07:28 +00:00
}
2021-11-14 10:33:35 +00:00
cb_tone(pin, value, duration);
2020-10-28 10:24:05 +00:00
#else
2021-11-14 10:33:35 +00:00
if(pin == RADIOLIB_NC) {
return;
}
2020-10-28 10:24:05 +00:00
#if defined(ESP32)
// ESP32 tone() emulation
(void)duration;
2022-08-09 18:20:19 +00:00
if(prev == -1) {
ledcAttachPin(pin, RADIOLIB_TONE_ESP32_CHANNEL);
}
if(prev != value) {
ledcWriteTone(RADIOLIB_TONE_ESP32_CHANNEL, value);
}
prev = value;
#elif defined(RADIOLIB_MBED_TONE_OVERRIDE)
// better tone for mbed OS boards
(void)duration;
if(!pwmPin) {
pwmPin = new mbed::PwmOut(digitalPinToPinName(pin));
}
pwmPin->period(1.0 / value);
pwmPin->write(0.5);
2021-11-14 10:33:35 +00:00
#else
(void)value;
(void)duration;
2020-10-28 10:24:05 +00:00
#endif
2020-04-30 15:07:28 +00:00
#endif
}
void Module::noTone(RADIOLIB_PIN_TYPE pin) {
2021-11-14 10:33:35 +00:00
#if !defined(RADIOLIB_TONE_UNSUPPORTED)
if((pin == RADIOLIB_NC) || (cb_noTone == nullptr)) {
2020-10-28 10:24:05 +00:00
return;
2020-04-30 15:07:28 +00:00
}
2021-11-14 10:33:35 +00:00
#if defined(ARDUINO_ARCH_STM32)
cb_noTone(pin, false);
2020-10-28 10:24:05 +00:00
#else
2021-11-14 10:33:35 +00:00
cb_noTone(pin);
2020-10-28 10:24:05 +00:00
#endif
2021-11-14 10:33:35 +00:00
#else
if(pin == RADIOLIB_NC) {
return;
}
#if defined(ESP32)
// ESP32 tone() emulation
ledcDetachPin(pin);
ledcWrite(RADIOLIB_TONE_ESP32_CHANNEL, 0);
2022-08-09 18:20:19 +00:00
prev = -1;
#elif defined(RADIOLIB_MBED_TONE_OVERRIDE)
// better tone for mbed OS boards
(void)pin;
pwmPin->suspend();
2021-11-14 10:33:35 +00:00
#endif
2020-04-30 15:07:28 +00:00
#endif
}
2020-06-18 14:31:38 +00:00
void Module::attachInterrupt(RADIOLIB_PIN_TYPE interruptNum, void (*userFunc)(void), RADIOLIB_INTERRUPT_STATUS mode) {
2021-11-14 10:33:35 +00:00
if((interruptNum == RADIOLIB_NC) || (cb_attachInterrupt == nullptr)) {
return;
}
cb_attachInterrupt(interruptNum, userFunc, mode);
}
void Module::detachInterrupt(RADIOLIB_PIN_TYPE interruptNum) {
2021-11-14 10:33:35 +00:00
if((interruptNum == RADIOLIB_NC) || (cb_detachInterrupt == nullptr)) {
return;
}
cb_detachInterrupt(interruptNum);
}
void Module::yield() {
2021-11-14 10:33:35 +00:00
if(cb_yield == nullptr) {
return;
}
#if !defined(RADIOLIB_YIELD_UNSUPPORTED)
2021-11-14 10:33:35 +00:00
cb_yield();
2021-10-27 19:15:46 +00:00
#endif
}
void Module::delay(uint32_t ms) {
2021-11-14 10:33:35 +00:00
if(cb_delay == nullptr) {
return;
}
cb_delay(ms);
}
void Module::delayMicroseconds(uint32_t us) {
2021-11-14 10:33:35 +00:00
if(cb_delayMicroseconds == nullptr) {
return;
}
cb_delayMicroseconds(us);
}
uint32_t Module::millis() {
2021-11-14 10:33:35 +00:00
if(cb_millis == nullptr) {
return(0);
}
return(cb_millis());
}
uint32_t Module::micros() {
2021-11-14 10:33:35 +00:00
if(cb_micros == nullptr) {
return(0);
}
return(cb_micros());
}
uint32_t Module::pulseIn(RADIOLIB_PIN_TYPE pin, RADIOLIB_PIN_STATUS state, uint32_t timeout) {
if(cb_pulseIn == nullptr) {
return(0);
}
return(cb_pulseIn(pin, state, timeout));
}
2021-11-14 10:33:35 +00:00
void Module::begin() {
#if defined(RADIOLIB_BUILD_ARDUINO)
2021-11-14 10:33:35 +00:00
if(cb_SPIbegin == nullptr) {
return;
}
(this->*cb_SPIbegin)();
#endif
2021-11-14 10:33:35 +00:00
}
void Module::beginTransaction() {
#if defined(RADIOLIB_BUILD_ARDUINO)
2021-11-14 10:33:35 +00:00
if(cb_SPIbeginTransaction == nullptr) {
return;
}
(this->*cb_SPIbeginTransaction)();
#endif
2021-11-14 10:33:35 +00:00
}
uint8_t Module::transfer(uint8_t b) {
#if defined(RADIOLIB_BUILD_ARDUINO)
2021-11-14 10:33:35 +00:00
if(cb_SPItransfer == nullptr) {
return(0xFF);
}
return((this->*cb_SPItransfer)(b));
#endif
2021-11-14 10:33:35 +00:00
}
void Module::endTransaction() {
#if defined(RADIOLIB_BUILD_ARDUINO)
2021-11-14 10:33:35 +00:00
if(cb_SPIendTransaction == nullptr) {
return;
}
(this->*cb_SPIendTransaction)();
#endif
2021-11-14 10:33:35 +00:00
}
void Module::end() {
#if defined(RADIOLIB_BUILD_ARDUINO)
2021-11-14 10:33:35 +00:00
if(cb_SPIend == nullptr) {
return;
}
(this->*cb_SPIend)();
#endif
2021-11-14 10:33:35 +00:00
}
#if defined(RADIOLIB_BUILD_ARDUINO)
void Module::SPIbegin() {
_spi->begin();
}
#endif
2021-11-14 10:33:35 +00:00
void Module::SPIbeginTransaction() {
#if defined(RADIOLIB_BUILD_ARDUINO)
2021-11-14 10:33:35 +00:00
_spi->beginTransaction(_spiSettings);
#endif
2021-11-14 10:33:35 +00:00
}
uint8_t Module::SPItransfer(uint8_t b) {
#if defined(RADIOLIB_BUILD_ARDUINO)
2021-11-14 10:33:35 +00:00
return(_spi->transfer(b));
#endif
2021-11-14 10:33:35 +00:00
}
void Module::SPIendTransaction() {
#if defined(RADIOLIB_BUILD_ARDUINO)
2021-11-14 10:33:35 +00:00
_spi->endTransaction();
#endif
2021-11-14 10:33:35 +00:00
}
#if defined(RADIOLIB_BUILD_ARDUINO)
2021-11-14 10:33:35 +00:00
void Module::SPIend() {
_spi->end();
}
#endif
2021-06-14 18:59:16 +00:00
uint8_t Module::flipBits(uint8_t b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
uint16_t Module::flipBits16(uint16_t i) {
i = (i & 0xFF00) >> 8 | (i & 0x00FF) << 8;
i = (i & 0xF0F0) >> 4 | (i & 0x0F0F) << 4;
i = (i & 0xCCCC) >> 2 | (i & 0x3333) << 2;
i = (i & 0xAAAA) >> 1 | (i & 0x5555) << 1;
return i;
}
2022-07-03 09:05:56 +00:00
void Module::hexdump(uint8_t* data, size_t len) {
size_t rem_len = len;
for(size_t i = 0; i < len; i+=16) {
2022-07-03 09:05:56 +00:00
char str[80];
sprintf(str, "%07x ", i);
size_t line_len = 16;
if(rem_len < line_len) {
line_len = rem_len;
}
for(size_t j = 0; j < line_len; j++) {
2022-07-03 09:05:56 +00:00
sprintf(&str[8 + j*3], "%02x ", data[i+j]);
}
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] = ' ';
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 = '.';
}
sprintf(&str[58 + j], "%c", c);
}
for(size_t j = line_len; j < 16; j++) {
sprintf(&str[58 + j], " ");
}
2022-07-03 09:05:56 +00:00
RADIOLIB_DEBUG_PRINTLN(str);
rem_len -= 16;
2022-07-03 09:05:56 +00:00
}
}
2022-07-04 13:30:37 +00:00
void Module::regdump(uint8_t start, uint8_t len) {
#if defined(RADIOLIB_STATIC_ONLY)
uint8_t buff[RADIOLIB_STATIC_ARRAY_SIZE];
#else
uint8_t* buff = new uint8_t[len];
#endif
SPIreadRegisterBurst(start, len, buff);
hexdump(buff, len);
#if !defined(RADIOLIB_STATIC_ONLY)
delete[] buff;
#endif
}
2020-06-18 14:31:38 +00:00
void Module::setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE 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 RADIOLIB_PIN_TYPE pins[] = {
rxEn, txEn, RADIOLIB_NC,
};
// This must be static, since setRfSwitchTable stores a reference.
static constexpr RfSwitchMode_t table[] = {
{MODE_IDLE, {LOW, LOW}},
{MODE_RX, {HIGH, LOW}},
{MODE_TX, {LOW, HIGH}},
END_OF_MODE_TABLE,
};
setRfSwitchTable(pins, table);
}
void Module::setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[3], const RfSwitchMode_t table[]) {
memcpy(_rfSwitchPins, pins, sizeof(_rfSwitchPins));
_rfSwitchTable = table;
for(size_t i = 0; i < RFSWITCH_MAX_PINS; i++)
this->pinMode(pins[i], OUTPUT);
}
const Module::RfSwitchMode_t *Module::findRfSwitchMode(uint8_t mode) const {
const RfSwitchMode_t *row = _rfSwitchTable;
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
[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 RADIOLIB_PIN_STATUS *value = &row->values[0];
for(size_t i = 0; i < RFSWITCH_MAX_PINS; i++) {
RADIOLIB_PIN_TYPE pin = _rfSwitchPins[i];
if (pin != RADIOLIB_NC)
this->digitalWrite(pin, *value);
++value;
}
2020-06-18 14:31:38 +00:00
}