Compare commits

...

7 Commits

Author SHA1 Message Date
DarkZeros 6e912eb6c9
Merge db4eb82826 into 342eb48a49 2023-12-30 14:17:02 +00:00
Daniel Ansorregui db4eb82826 Allow AsyncPowerOn
* The display takes 16ms to power on
  During this time we can render the
  content and finally display it
* The call is optional if we do not call
  it then the old code path is used
* Moved the init to constructor
2023-12-30 13:13:42 +00:00
Daniel Ansorregui 9dee12e87f Refactor DarkBorder
* Allow to be changed dynamically between
  display updated.
2023-12-29 21:55:47 +00:00
Daniel Ansorregui 98948adaa2 No soft reset after reset
* Avoid a redundant reset after a reset
  The display is always reset after a hard reset
  and there is no need to re-reset it
2023-12-29 21:55:47 +00:00
Daniel Ansorregui 921c24667b Set boosters with less time
* Reduces 220ms the display update
  -80ms the power on of the display
  -140ms the display partial update
- Note: This may have side effects, but I saw none
  tested on Watchy v1.0, display should be same on
  other Watchy boards
2023-12-29 21:55:47 +00:00
Daniel Ansorregui 869c08e240 Refactor DisplayFullInit
* It makes more sense to put it in the Display class
* The reset should be 2ms, 10ms is worst case
* Also there was a disable call based on display
  that makes more sense to put in the default
  boot switch statement
2023-12-29 21:55:47 +00:00
Daniel Ansorregui f51d7e2e72 Move Display specifics
* Move BusyCallback
* Move constructor pins
2023-12-29 14:06:20 +00:00
4 changed files with 98 additions and 42 deletions

View File

@ -15,11 +15,41 @@
// Link: https://github.com/sqfmi/Watchy
#include "Display.h"
#include "config.h"
WatchyDisplay::WatchyDisplay(int16_t cs, int16_t dc, int16_t rst, int16_t busy) :
GxEPD2_EPD(cs, dc, rst, busy, HIGH, 10000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate)
RTC_DATA_ATTR bool displayFullInit = true;
void WatchyDisplay::busyCallback(const void *) {
gpio_wakeup_enable((gpio_num_t)DISPLAY_BUSY, GPIO_INTR_LOW_LEVEL);
esp_sleep_enable_gpio_wakeup();
esp_light_sleep_start();
}
WatchyDisplay::WatchyDisplay() :
GxEPD2_EPD(DISPLAY_CS, DISPLAY_DC, DISPLAY_RES, DISPLAY_BUSY, HIGH, 10000000, WIDTH, HEIGHT, panel, hasColor, hasPartialUpdate, hasFastPartialUpdate)
{
selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0)); // Set SPI to 20Mhz (default is 4Mhz)
// Setup callback and SPI by default
selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0));
setBusyCallback(busyCallback);
// Watchy default initialization
init(0, displayFullInit, 2, true);
}
void WatchyDisplay::asyncPowerOn() {
// This is expensive if unused
if (!waitingPowerOn && !_hibernating) {
_InitDisplay();
_PowerOnAsync();
}
}
void WatchyDisplay::setDarkBorder(bool dark) {
if (_hibernating) return;
darkBorder = dark;
_startTransfer();
_transferCommand(0x3C); // BorderWavefrom
_transfer(dark ? 0x02 : 0x05);
_endTransfer();
}
void WatchyDisplay::clearScreen(uint8_t value)
@ -324,31 +354,52 @@ void WatchyDisplay::_setPartialRamArea(uint16_t x, uint16_t y, uint16_t w, uint1
_endTransfer();
}
void WatchyDisplay::_PowerOnAsync()
{
if (_power_is_on)
return;
_startTransfer();
_transferCommand(0x22);
_transfer(0xf8);
_transferCommand(0x20);
_endTransfer();
waitingPowerOn = true;
_power_is_on = true;
}
void WatchyDisplay::_PowerOn()
{
if (!_power_is_on)
if (waitingPowerOn)
{
_startTransfer();
_transferCommand(0x22);
_transfer(0xf8);
_transferCommand(0x20);
_endTransfer();
waitingPowerOn = false;
_waitWhileBusy("_PowerOn", power_on_time);
}
if (_power_is_on)
return;
_startTransfer();
_transferCommand(0x22);
_transfer(0xf8);
_transferCommand(0x20);
_endTransfer();
_waitWhileBusy("_PowerOn", power_on_time);
_power_is_on = true;
}
void WatchyDisplay::_PowerOff()
{
if (_power_is_on)
if (waitingPowerOn)
{
_startTransfer();
_transferCommand(0x22);
_transfer(0x83);
_transferCommand(0x20);
_endTransfer();
_waitWhileBusy("_PowerOff", power_off_time);
waitingPowerOn = false;
_waitWhileBusy("_PowerOn", power_on_time);
}
if (!_power_is_on)
return;
_startTransfer();
_transferCommand(0x22);
_transfer(0x83);
_transferCommand(0x20);
_endTransfer();
_waitWhileBusy("_PowerOff", power_off_time);
_power_is_on = false;
_using_partial_mode = false;
}
@ -356,20 +407,31 @@ void WatchyDisplay::_PowerOff()
void WatchyDisplay::_InitDisplay()
{
if (_hibernating) _reset();
_writeCommand(0x12); // soft reset
_waitWhileBusy("_SoftReset", 10); // 10ms max according to specs
// No need to soft reset, the Display goes to same state after hard reset
// _writeCommand(0x12); // soft reset
// _waitWhileBusy("_SoftReset", 10); // 10ms max according to specs*/
_startTransfer();
_transferCommand(0x01); // Driver output control
_transfer(0xC7);
_transfer(0x00);
_transfer(0x00);
_transferCommand(0x3C); // BorderWavefrom
_transfer(darkBorder ? 0x02 : 0x05);
if (reduceBoosterTime) {
_transferCommand(0x0C); // BOOSTER_SOFT_START_CONTROL
_transfer(0x80); // Needs to be at least 0x80
_transfer(0x00); // Aggresive lowest possible delay, out of spec (worked ok for me)
_transfer(0x00); // Aggresive lowest possible delay, out of spec (worked ok for me)
_transfer(0x80); // Undocumented booster delay, 0x80 seems ok
}
_transferCommand(0x18); // Read built-in temperature sensor
_transfer(0x80);
_endTransfer();
setDarkBorder(darkBorder);
_setPartialRamArea(0, 0, WIDTH, HEIGHT);
}
@ -395,6 +457,7 @@ void WatchyDisplay::_Update_Full()
_transferCommand(0x20);
_endTransfer();
_waitWhileBusy("_Update_Full", full_refresh_time);
displayFullInit = false;
}
void WatchyDisplay::_Update_Part()

View File

@ -34,7 +34,12 @@ class WatchyDisplay : public GxEPD2_EPD
static const uint16_t full_refresh_time = 2600; // ms, e.g. 2509602us
static const uint16_t partial_refresh_time = 500; // ms, e.g. 457282us
// constructor
WatchyDisplay(int16_t cs, int16_t dc, int16_t rst, int16_t busy);
WatchyDisplay();
void setDarkBorder(bool darkBorder);
void asyncPowerOn();
void _PowerOnAsync();
bool waitingPowerOn = false;
static void busyCallback(const void *);
// methods (virtual)
// Support for Bitmaps (Sprites) to Controller Buffer and to Screen
void clearScreen(uint8_t value = 0xFF); // init controller memory and screen (default white)
@ -69,6 +74,8 @@ class WatchyDisplay : public GxEPD2_EPD
void hibernate(); // turns powerOff() and sets controller to deep sleep for minimum power use, ONLY if wakeable by RST (rst >= 0)
bool darkBorder = false; // adds a dark border outside the normal screen area
static constexpr bool reduceBoosterTime = true; // Saves ~200ms
private:
void _writeScreenBuffer(uint8_t command, uint8_t value);
void _writeImage(uint8_t command, const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false);

View File

@ -2,7 +2,7 @@
WatchyRTC Watchy::RTC;
GxEPD2_BW<WatchyDisplay, WatchyDisplay::HEIGHT> Watchy::display(
WatchyDisplay(DISPLAY_CS, DISPLAY_DC, DISPLAY_RES, DISPLAY_BUSY));
WatchyDisplay{});
RTC_DATA_ATTR int guiState;
RTC_DATA_ATTR int menuIndex;
@ -11,7 +11,6 @@ RTC_DATA_ATTR bool WIFI_CONFIGURED;
RTC_DATA_ATTR bool BLE_CONFIGURED;
RTC_DATA_ATTR weatherData currentWeather;
RTC_DATA_ATTR int weatherIntervalCounter = -1;
RTC_DATA_ATTR bool displayFullInit = true;
RTC_DATA_ATTR long gmtOffset = 0;
RTC_DATA_ATTR bool alreadyInMenu = true;
RTC_DATA_ATTR tmElements_t bootTime;
@ -22,12 +21,6 @@ void Watchy::init(String datetime) {
Wire.begin(SDA, SCL); // init i2c
RTC.init();
// Init the display here for all cases, if unused, it will do nothing
display.epd2.selectSPI(SPI, SPISettings(20000000, MSBFIRST, SPI_MODE0)); // Set SPI to 20Mhz (default is 4Mhz)
display.init(0, displayFullInit, 10,
true); // 10ms by spec, and fast pulldown reset
display.epd2.setBusyCallback(displayBusyCallback);
switch (wakeup_reason) {
case ESP_SLEEP_WAKEUP_EXT0: // RTC Alarm
RTC.read(currentTime);
@ -63,22 +56,14 @@ void Watchy::init(String datetime) {
RTC.read(bootTime);
showWatchFace(false); // full update on reset
vibMotor(75, 4);
// For some reason, seems to be enabled on first boot
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
break;
}
deepSleep();
}
void Watchy::displayBusyCallback(const void *) {
gpio_wakeup_enable((gpio_num_t)DISPLAY_BUSY, GPIO_INTR_LOW_LEVEL);
esp_sleep_enable_gpio_wakeup();
esp_light_sleep_start();
}
void Watchy::deepSleep() {
display.hibernate();
if (displayFullInit) // For some reason, seems to be enabled on first boot
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
displayFullInit = false; // Notify not to init it again
RTC.clearAlarm(); // resets the alarm flag in the RTC
// Set GPIOs 0-39 to input to avoid power leaking out
@ -606,6 +591,8 @@ void Watchy::showAccelerometer() {
void Watchy::showWatchFace(bool partialRefresh) {
display.setFullWindow();
// At this point it is sure we are going to update
display.epd2.asyncPowerOn();
drawWatchFace();
display.display(partialRefresh); // partial refresh
guiState = WATCHFACE_STATE;
@ -834,8 +821,8 @@ void Watchy::setupWifi() {
// turn off radios
WiFi.mode(WIFI_OFF);
btStop();
display.epd2.setBusyCallback(displayBusyCallback); // enable lightsleep on
// busy
// enable lightsleep on busy
display.epd2.setBusyCallback(WatchyDisplay::busyCallback);
guiState = APP_STATE;
}

View File

@ -51,7 +51,6 @@ public:
explicit Watchy(const watchySettings &s) : settings(s) {} // constructor
void init(String datetime = "");
void deepSleep();
static void displayBusyCallback(const void *);
float getBatteryVoltage();
void vibMotor(uint8_t intervalMs = 100, uint8_t length = 20);