mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-01 02:11:57 +00:00
perf: unused TinyUSB classes off on nRF52, SSD1306 sends only changed blocks, ESP32 loop yields when idle
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -381,6 +381,11 @@ void loop() {
|
||||
if (!the_mesh.hasPendingWork()) {
|
||||
#if defined(NRF52_PLATFORM)
|
||||
board.sleep(0); // nrf ignores seconds param, sleeps whenever possible
|
||||
#elif defined(ESP32) && !defined(UI_LOOP_IDLES)
|
||||
// Arduino's loop task never yields: without this it spins the CPU flat
|
||||
// out. One tick lets the idle task halt the core (WAITI) until the next
|
||||
// interrupt or tick -- radio, buttons and serial still get a pass a ms.
|
||||
vTaskDelay(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
#include "../ui-core/UiCoreHost.h"
|
||||
#include "../ui-core/ScreenLock.h"
|
||||
|
||||
// loop() waits on its own until something is due (lvport::idle, woken by a
|
||||
// frame from the app): main.cpp adds no wait of its own.
|
||||
#define UI_LOOP_IDLES 1
|
||||
|
||||
class UiCore;
|
||||
class NearbyModel;
|
||||
struct UiEvent;
|
||||
|
||||
@@ -93,6 +93,14 @@ build_flags = ${arduino_base.build_flags}
|
||||
-D LFS_NO_ASSERT=1
|
||||
-D EXTRAFS=1
|
||||
-D USE_CC310_HW_CRYPTO=1
|
||||
; USB is the serial port only: TinyUSB's other device classes (on by default
|
||||
; in the core's tusb_config_nrf.h) cost ~2.4 KB of RAM for nothing.
|
||||
-D CFG_TUD_MSC=0
|
||||
-D CFG_TUD_HID=0
|
||||
-D CFG_TUD_MIDI=0
|
||||
-D CFG_TUD_VENDOR=0
|
||||
-D CFG_TUD_VIDEO=0
|
||||
-D CFG_TUD_VIDEO_STREAMING=0
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
https://github.com/oltaco/CustomLFS#0.2.3
|
||||
|
||||
@@ -17,6 +17,7 @@ bool SSD1306Display::begin() {
|
||||
#ifdef DISPLAY_ROTATION
|
||||
display.setRotation(DISPLAY_ROTATION);
|
||||
#endif
|
||||
_force_redraw = true;
|
||||
return display.begin(SSD1306_SWITCHCAPVCC, DISPLAY_ADDRESS, true, false) && i2c_probe(Wire, DISPLAY_ADDRESS);
|
||||
}
|
||||
|
||||
@@ -27,6 +28,7 @@ void SSD1306Display::turnOn() {
|
||||
if (_peripher_power) begin(); // re-init display after power was cut
|
||||
}
|
||||
display.ssd1306_command(SSD1306_DISPLAYON);
|
||||
_force_redraw = true; // panel was off -- guarantee the next endFrame() flushes
|
||||
}
|
||||
|
||||
void SSD1306Display::turnOff() {
|
||||
@@ -45,6 +47,7 @@ void SSD1306Display::turnOff() {
|
||||
void SSD1306Display::clear() {
|
||||
display.clearDisplay();
|
||||
display.display();
|
||||
_force_redraw = true; // next endFrame() must flush even if its hash matches a pre-clear frame
|
||||
}
|
||||
|
||||
void SSD1306Display::startFrame(Color bkg) {
|
||||
@@ -107,6 +110,49 @@ uint16_t SSD1306Display::getTextWidth(const char* str) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void SSD1306Paged::sendBlock(uint8_t page, uint8_t col, uint8_t len) {
|
||||
const uint8_t cmd[] = { SSD1306_PAGEADDR, page, page, SSD1306_COLUMNADDR, col, (uint8_t)(col + len - 1) };
|
||||
const uint8_t* ptr = buffer + (uint16_t)page * WIDTH + col;
|
||||
ssd1306_commandList(cmd, sizeof(cmd));
|
||||
while (len) { // 32 data bytes a transfer fit every core's Wire buffer
|
||||
uint8_t n = len < 32 ? len : 32;
|
||||
wire->beginTransmission(i2caddr);
|
||||
wire->write((uint8_t)0x40);
|
||||
wire->write(ptr, n);
|
||||
wire->endTransmission();
|
||||
ptr += n; len -= n;
|
||||
}
|
||||
}
|
||||
|
||||
void SSD1306Display::endFrame() {
|
||||
display.display();
|
||||
const uint8_t* buf = display.getBuffer();
|
||||
const uint8_t per_page = 128 / BLOCK_W;
|
||||
bool any = false;
|
||||
bool dirty[BLOCKS];
|
||||
for (uint8_t i = 0; i < BLOCKS; i++) {
|
||||
const uint8_t* q = buf + (uint16_t)i * BLOCK_W; // blocks run along each page in buffer order
|
||||
uint32_t h = 2166136261u;
|
||||
for (uint8_t j = 0; j < BLOCK_W; j++) { h ^= q[j]; h *= 16777619u; }
|
||||
dirty[i] = _force_redraw || h != _block_hash[i];
|
||||
_block_hash[i] = h;
|
||||
any |= dirty[i];
|
||||
}
|
||||
if (!any) return;
|
||||
if (_force_redraw || !display.canSendBlocks()) {
|
||||
_force_redraw = false;
|
||||
display.display(); // whole frame; also resets the panel's address window
|
||||
return;
|
||||
}
|
||||
display.blocksBegin();
|
||||
for (uint8_t page = 0; page < 64 / 8; page++) {
|
||||
// runs of neighbouring dirty blocks go out as one transfer
|
||||
for (uint8_t b = 0; b < per_page; ) {
|
||||
if (!dirty[page * per_page + b]) { b++; continue; }
|
||||
uint8_t e = b;
|
||||
while (e + 1 < per_page && dirty[page * per_page + e + 1]) e++;
|
||||
display.sendBlock(page, b * BLOCK_W, (e - b + 1) * BLOCK_W);
|
||||
b = e + 1;
|
||||
}
|
||||
}
|
||||
display.blocksEnd();
|
||||
}
|
||||
|
||||
@@ -15,12 +15,32 @@
|
||||
#define DISPLAY_ADDRESS 0x3C
|
||||
#endif
|
||||
|
||||
// Adafruit's display() always sends the whole frame (1 KB, ~25 ms at 400 kHz),
|
||||
// and startFrame() clears the whole buffer, so every frame went out in full.
|
||||
// As on the SH1106 (SH1106Display.h): a sender for single 32-column blocks.
|
||||
class SSD1306Paged : public Adafruit_SSD1306 {
|
||||
public:
|
||||
using Adafruit_SSD1306::Adafruit_SSD1306;
|
||||
bool canSendBlocks() const { return wire != nullptr; }
|
||||
// A frame's blocks go out between these: the bus clock is set once.
|
||||
void blocksBegin() { wire->setClock(wireClk); }
|
||||
void blocksEnd() { wire->setClock(restoreClk); }
|
||||
void sendBlock(uint8_t page, uint8_t col, uint8_t len);
|
||||
};
|
||||
|
||||
class SSD1306Display : public DisplayDriver {
|
||||
Adafruit_SSD1306 display;
|
||||
SSD1306Paged display;
|
||||
bool _isOn;
|
||||
uint8_t _color;
|
||||
int _text_sz = 1;
|
||||
RefCountedDigitalPin* _peripher_power;
|
||||
// Frame-skip: endFrame() hashes each 32-column block of each 8-px page and
|
||||
// sends only the blocks whose hash changed; _force_redraw sends the frame
|
||||
// after begin()/turnOn()/clear() whole.
|
||||
static const uint8_t BLOCK_W = 32;
|
||||
static const uint8_t BLOCKS = (128 / BLOCK_W) * (64 / 8);
|
||||
uint32_t _block_hash[BLOCKS] = {};
|
||||
bool _force_redraw = true;
|
||||
|
||||
bool i2c_probe(TwoWire& wire, uint8_t addr);
|
||||
#ifdef OLED_MISC_FIXED_FONT
|
||||
|
||||
Reference in New Issue
Block a user