diff --git a/docs/hardware/pinetab/SX1302/DS_SX1302_V1.0.pdf b/docs/hardware/pinetab/SX1302/DS_SX1302_V1.0.pdf new file mode 100644 index 000000000..875008792 Binary files /dev/null and b/docs/hardware/pinetab/SX1302/DS_SX1302_V1.0.pdf differ diff --git a/docs/hardware/pinetab/SX1302/M-GW1302S 用户手册(2)(1)(1).pdf b/docs/hardware/pinetab/SX1302/M-GW1302S 用户手册(2)(1)(1).pdf new file mode 100644 index 000000000..9ad7bbb79 Binary files /dev/null and b/docs/hardware/pinetab/SX1302/M-GW1302S 用户手册(2)(1)(1).pdf differ diff --git a/docs/hardware/pinetab/SX1302/M-GW1302S(射频版)硬件设计手册_V1.1.pdf b/docs/hardware/pinetab/SX1302/M-GW1302S(射频版)硬件设计手册_V1.1.pdf new file mode 100644 index 000000000..bdc8bb250 Binary files /dev/null and b/docs/hardware/pinetab/SX1302/M-GW1302S(射频版)硬件设计手册_V1.1.pdf differ diff --git a/docs/hardware/pinetab/SX1302/M-GW1302(透传版)_硬件设计手册.pdf b/docs/hardware/pinetab/SX1302/M-GW1302(透传版)_硬件设计手册.pdf new file mode 100644 index 000000000..40c23e36c Binary files /dev/null and b/docs/hardware/pinetab/SX1302/M-GW1302(透传版)_硬件设计手册.pdf differ diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp index 04205f498..0205aa66d 100644 --- a/src/RedirectablePrint.cpp +++ b/src/RedirectablePrint.cpp @@ -1,4 +1,5 @@ #include "RedirectablePrint.h" +#include "configuration.h" #include /** @@ -10,4 +11,14 @@ void RedirectablePrint::setDestination(Print *_dest) { assert(_dest); dest = _dest; +} + +size_t RedirectablePrint::write(uint8_t c) +{ +#ifdef SEGGER_STDOUT_CH + SEGGER_RTT_PutCharSkip(SEGGER_STDOUT_CH, c); +#endif + + dest->write(c); + return 1; // We always claim one was written, rather than trusting what the serial port said (which could be zero) } \ No newline at end of file diff --git a/src/RedirectablePrint.h b/src/RedirectablePrint.h index f75aea010..2d525118d 100644 --- a/src/RedirectablePrint.h +++ b/src/RedirectablePrint.h @@ -19,7 +19,7 @@ class RedirectablePrint : public Print */ void setDestination(Print *dest); - virtual size_t write(uint8_t c) { return dest->write(c); } + virtual size_t write(uint8_t c); }; class NoopPrint : public Print diff --git a/src/configuration.h b/src/configuration.h index 5e8964d22..ed851ed8f 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -404,8 +404,11 @@ along with this program. If not, see . // Always include the SEGGER code on NRF52 - because useful for debugging #include "SEGGER_RTT.h" +// The channel we send stdout data to +#define SEGGER_STDOUT_CH 0 + // Debug printing to segger console -#define SEGGER_MSG(...) SEGGER_RTT_printf(0, __VA_ARGS__) +#define SEGGER_MSG(...) SEGGER_RTT_printf(SEGGER_STDOUT_CH, __VA_ARGS__) // If we are not on a NRF52840 (which has built in USB-ACM serial support) and we don't have serial pins hooked up, then we MUST // use SEGGER for debug output diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 3e53d130e..75aa40ae6 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -85,13 +85,16 @@ static uint16_t displayWidth, displayHeight; #define SCREEN_TRANSITION_MSECS 300 #endif -static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +/** + * Draw the icon with extra info printed around the corners + */ +static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { // draw an xbm image. // Please note that everything that should be transitioned // needs to be drawn relative to x and y - // draw centered left to right and centered above the one line of app text + // draw centered icon left to right and centered above the one line of app text display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2, icon_width, icon_height, (const uint8_t *)icon_bits); @@ -101,15 +104,31 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1 display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title); display->setFont(FONT_SMALL); - const char *region = myRegion ? myRegion->name : NULL; - if (region) - display->drawString(x + 0, y + 0, region); + // Draw region in upper left + if (upperMsg) + display->drawString(x + 0, y + 0, upperMsg); + // Draw version in upper right char buf[16]; snprintf(buf, sizeof(buf), "%s", xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf); screen->forceDisplay(); + + // FIXME - draw serial # somewhere? +} + +static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + // Draw region in upper left + const char *region = myRegion ? myRegion->name : NULL; + drawIconScreen(region, display, state, x, y); +} + +/// Used on eink displays while in deep sleep +static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + drawIconScreen("Sleeping...", display, state, x, y); } static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) @@ -600,6 +619,21 @@ Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue cmdQueue.setReader(this); } +/** + * Prepare the display for the unit going to the lowest power mode possible. Most screens will just + * poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code + */ +void Screen::doDeepSleep() +{ +#ifdef HAS_EINK + static FrameCallback sleepFrames[] = {drawSleepScreen}; + static const int sleepFrameCount = sizeof(sleepFrames) / sizeof(sleepFrames[0]); + ui.setFrames(sleepFrames, sleepFrameCount); + ui.update(); +#endif + setOn(false); +} + void Screen::handleSetOn(bool on) { if (!useDisplay) @@ -636,7 +670,7 @@ void Screen::setup() displayWidth = dispdev.width(); displayHeight = dispdev.height(); - ui.setTimePerTransition(SCREEN_TRANSITION_MSECS); + ui.setTimePerTransition(SCREEN_TRANSITION_MSECS); ui.setIndicatorPosition(BOTTOM); // Defines where the first frame is located in the bar. diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 5539dcc76..d0bf62286 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -99,6 +99,12 @@ class Screen : public concurrency::OSThread enqueueCmd(ScreenCmd{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF}); } + /** + * Prepare the display for the unit going to the lowest power mode possible. Most screens will just + * poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code + */ + void doDeepSleep(); + /// Handles a button press. void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); } diff --git a/src/main.cpp b/src/main.cpp index 0f24e1bc4..61bb2be14 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -172,6 +172,10 @@ class ButtonThread : public OSThread { #ifdef BUTTON_PIN userButton = OneButton(BUTTON_PIN, true, true); +#ifdef INPUT_PULLUP_SENSE + // Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did + pinMode(BUTTON_PIN, INPUT_PULLUP_SENSE); +#endif userButton.attachClick(userButtonPressed); userButton.attachDuringLongPress(userButtonPressedLong); userButton.attachDoubleClick(userButtonDoublePressed); @@ -179,6 +183,10 @@ class ButtonThread : public OSThread #endif #ifdef BUTTON_PIN_ALT userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true); +#ifdef INPUT_PULLUP_SENSE + // Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did + pinMode(BUTTON_PIN_ALT, INPUT_PULLUP_SENSE); +#endif userButtonAlt.attachClick(userButtonPressed); userButtonAlt.attachDuringLongPress(userButtonPressedLong); userButtonAlt.attachDoubleClick(userButtonDoublePressed); @@ -233,8 +241,8 @@ RadioInterface *rIf = NULL; void setup() { -#ifdef USE_SEGGER - SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_TRIM); +#ifdef SEGGER_STDOUT_CH + SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, 1024, SEGGER_RTT_MODE_NO_BLOCK_TRIM); #endif // Debug diff --git a/src/nrf52/main-nrf52.cpp b/src/nrf52/main-nrf52.cpp index eec306e52..8b7bb65b9 100644 --- a/src/nrf52/main-nrf52.cpp +++ b/src/nrf52/main-nrf52.cpp @@ -1,6 +1,8 @@ #include "NRF52Bluetooth.h" #include "configuration.h" #include "graphics/TFTDisplay.h" +#include +#include #include #include #include @@ -64,7 +66,7 @@ void setBluetoothEnable(bool on) } } } else { - if(nrf52Bluetooth) + if (nrf52Bluetooth) nrf52Bluetooth->shutdown(); } bleOn = on; @@ -98,7 +100,7 @@ void nrf52Setup() #ifdef BQ25703A_ADDR auto *bq = new BQ25713(); - if(!bq->setup()) + if (!bq->setup()) DEBUG_MSG("ERROR! Charge controller init failed\n"); #endif @@ -114,18 +116,26 @@ void nrf52Setup() void cpuDeepSleep(uint64_t msecToWake) { - DEBUG_MSG("FIXME: implement NRF52 deep sleep enter actions\n"); - // FIXME, configure RTC to wake us - // FIXME, power down SPI, I2C, RAMs + // FIXME, configure RTC or button press to wake us + // FIXME, power down SPI, I2C, RAMs + Wire.end(); + SPI.end(); + Serial.end(); + Serial1.end(); - // FIXME, use system off mode with ram retention for key state? - // FIXME, use non-init RAM per https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled + // FIXME, use system off mode with ram retention for key state? + // FIXME, use non-init RAM per + // https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled - while(1) { - delay(5000); - DEBUG_MSG("."); - } + auto ok = sd_power_system_off(); + if(ok != NRF_SUCCESS) { + DEBUG_MSG("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!\n"); + NRF_POWER->SYSTEMOFF = 1; + } - // FIXME, after wake power up SPI, I2C, RAMs, reinit LORA - DEBUG_MSG("FIXME: implement NRF52 deep sleep wake actions\n"); + // The following code should not be run, because we are off + while (1) { + delay(5000); + DEBUG_MSG("."); + } } \ No newline at end of file diff --git a/src/sleep.cpp b/src/sleep.cpp index e2cafe574..60bf911f2 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -142,14 +142,14 @@ static void waitEnterSleep() void doDeepSleep(uint64_t msecToWake) { - DEBUG_MSG("Entering deep sleep for %llu seconds\n", msecToWake / 1000); + DEBUG_MSG("Entering deep sleep for %lu seconds\n", msecToWake / 1000); // not using wifi yet, but once we are this is needed to shutoff the radio hw // esp_wifi_stop(); waitEnterSleep(); notifyDeepSleep.notifyObservers(NULL); - screen->setOn(false); // datasheet says this will draw only 10ua + screen->doDeepSleep(); // datasheet says this will draw only 10ua nodeDB.saveToDisk(); diff --git a/variants/eink/variant.h b/variants/eink/variant.h index 5ce975fb5..6e3fedd67 100644 --- a/variants/eink/variant.h +++ b/variants/eink/variant.h @@ -243,7 +243,7 @@ External serial flash WP25R1635FZUIL0 #define PIN_SPI_SCK (0 + 19) // To debug via the segger JLINK console rather than the CDC-ACM serial device -#define USE_SEGGER +// #define USE_SEGGER #ifdef __cplusplus } diff --git a/variants/pca10056-rc-clock/variant.h b/variants/pca10056-rc-clock/variant.h index 48dc72df2..9d35325c8 100644 --- a/variants/pca10056-rc-clock/variant.h +++ b/variants/pca10056-rc-clock/variant.h @@ -147,7 +147,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define SX1262_ANT_SW (32 + 10) // P1.10 // To debug via the segger JLINK console rather than the CDC-ACM serial device -#define USE_SEGGER +// #define USE_SEGGER #ifdef __cplusplus } diff --git a/variants/ppr1/variant.h b/variants/ppr1/variant.h index cfefa091b..e6bfd8a03 100644 --- a/variants/ppr1/variant.h +++ b/variants/ppr1/variant.h @@ -168,7 +168,7 @@ static const uint8_t SCK = PIN_SPI_SCK; #define LORA_DISABLE_SENDING // Define this to disable transmission for testing (power testing etc...) // To debug via the segger JLINK console rather than the CDC-ACM serial device -#define USE_SEGGER +// #define USE_SEGGER #ifdef __cplusplus }