2023-09-14 00:34:04 -04:00
|
|
|
|
|
|
|
#include "display.hpp"
|
|
|
|
#include "Wire.h"
|
|
|
|
#include "driver/ledc.h"
|
2023-10-22 14:35:49 -04:00
|
|
|
#include "omoteconfig.h"
|
2023-09-14 00:34:04 -04:00
|
|
|
|
2023-10-22 14:35:49 -04:00
|
|
|
std::shared_ptr<Display> Display::getInstance() {
|
|
|
|
if (DisplayAbstract::mInstance == nullptr) {
|
|
|
|
DisplayAbstract::mInstance =
|
|
|
|
std::shared_ptr<Display>(new Display(LCD_BL, LCD_EN));
|
|
|
|
}
|
|
|
|
return std::static_pointer_cast<Display>(mInstance);
|
2023-09-14 00:34:04 -04:00
|
|
|
}
|
|
|
|
|
2023-10-22 14:35:49 -04:00
|
|
|
Display::Display(int backlight_pin, int enable_pin)
|
|
|
|
: DisplayAbstract(), mBacklightPin(backlight_pin), mEnablePin(enable_pin),
|
|
|
|
tft(TFT_eSPI()), touch(Adafruit_FT6206()) {
|
|
|
|
pinMode(mEnablePin, OUTPUT);
|
|
|
|
digitalWrite(mEnablePin, HIGH);
|
|
|
|
pinMode(mBacklightPin, OUTPUT);
|
|
|
|
digitalWrite(mBacklightPin, HIGH);
|
|
|
|
|
|
|
|
setupBacklight(); // This eliminates the flash of the backlight
|
|
|
|
|
|
|
|
// Slowly charge the VSW voltage to prevent a brownout
|
|
|
|
// Workaround for hardware rev 1!
|
|
|
|
for (int i = 0; i < 100; i++) {
|
|
|
|
digitalWrite(this->mEnablePin, HIGH); // LCD Logic off
|
|
|
|
delayMicroseconds(1);
|
|
|
|
digitalWrite(this->mEnablePin, LOW); // LCD Logic on
|
|
|
|
}
|
|
|
|
|
|
|
|
setupTFT();
|
|
|
|
setupTouchScreen();
|
|
|
|
mFadeTaskMutex = xSemaphoreCreateBinary();
|
|
|
|
xSemaphoreGive(mFadeTaskMutex);
|
2023-09-14 00:34:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Display::setupBacklight() {
|
|
|
|
// Configure the backlight PWM
|
|
|
|
// Manual setup because ledcSetup() briefly turns on the backlight
|
|
|
|
ledc_channel_config_t ledc_channel_left;
|
|
|
|
ledc_channel_left.gpio_num = (gpio_num_t)mBacklightPin;
|
|
|
|
ledc_channel_left.speed_mode = LEDC_HIGH_SPEED_MODE;
|
|
|
|
ledc_channel_left.channel = LEDC_CHANNEL_5;
|
|
|
|
ledc_channel_left.intr_type = LEDC_INTR_DISABLE;
|
|
|
|
ledc_channel_left.timer_sel = LEDC_TIMER_1;
|
|
|
|
ledc_channel_left.flags.output_invert = 1; // Can't do this with ledcSetup()
|
|
|
|
ledc_channel_left.duty = 0;
|
|
|
|
ledc_channel_left.hpoint = 0;
|
|
|
|
ledc_timer_config_t ledc_timer;
|
|
|
|
ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;
|
|
|
|
ledc_timer.duty_resolution = LEDC_TIMER_8_BIT;
|
|
|
|
ledc_timer.timer_num = LEDC_TIMER_1;
|
|
|
|
ledc_timer.clk_cfg = LEDC_AUTO_CLK;
|
|
|
|
ledc_timer.freq_hz = 640;
|
|
|
|
ledc_channel_config(&ledc_channel_left);
|
|
|
|
ledc_timer_config(&ledc_timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Display::setupTFT() {
|
|
|
|
delay(100);
|
|
|
|
tft.init();
|
|
|
|
tft.initDMA();
|
|
|
|
tft.setRotation(0);
|
|
|
|
tft.fillScreen(TFT_BLACK);
|
|
|
|
tft.setSwapBytes(true);
|
|
|
|
}
|
|
|
|
|
2023-10-22 14:35:49 -04:00
|
|
|
void Display::setupTouchScreen() {
|
|
|
|
// Configure i2c pins and set frequency to 400kHz
|
|
|
|
Wire.begin(TFT_SDA, TFT_SCL, 400000);
|
|
|
|
touch.begin(128); // Initialize touchscreen and set sensitivity threshold
|
2023-09-14 00:34:04 -04:00
|
|
|
}
|
|
|
|
|
2023-10-22 14:35:49 -04:00
|
|
|
void Display::setBrightness(uint8_t brightness) {
|
2023-09-14 00:34:04 -04:00
|
|
|
mAwakeBrightness = brightness;
|
|
|
|
Serial.print("Set Brightness:");
|
|
|
|
Serial.println(mAwakeBrightness);
|
|
|
|
startFade();
|
|
|
|
}
|
|
|
|
|
2023-10-22 14:35:49 -04:00
|
|
|
uint8_t Display::getBrightness() { return mAwakeBrightness; }
|
2023-09-14 00:34:04 -04:00
|
|
|
|
2023-10-22 14:35:49 -04:00
|
|
|
void Display::setCurrentBrightness(uint8_t brightness) {
|
2023-09-14 00:34:04 -04:00
|
|
|
mBrightness = brightness;
|
|
|
|
auto duty = static_cast<int>(mBrightness);
|
|
|
|
ledcWrite(LCD_BACKLIGHT_LEDC_CHANNEL, duty);
|
|
|
|
// Serial.print("Current Brightness:");
|
|
|
|
// Serial.println(mBrightness);
|
|
|
|
}
|
|
|
|
|
2023-10-22 14:35:49 -04:00
|
|
|
void Display::turnOff() {
|
|
|
|
digitalWrite(this->mBacklightPin, HIGH);
|
|
|
|
digitalWrite(this->mEnablePin, HIGH);
|
|
|
|
pinMode(this->mBacklightPin, INPUT);
|
|
|
|
pinMode(this->mEnablePin, INPUT);
|
|
|
|
gpio_hold_en((gpio_num_t)mBacklightPin);
|
|
|
|
gpio_hold_en((gpio_num_t)mEnablePin);
|
2023-09-14 00:34:04 -04:00
|
|
|
}
|
|
|
|
|
2023-10-22 14:35:49 -04:00
|
|
|
void Display::screenInput(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) {
|
|
|
|
// int16_t touchX, touchY;
|
2023-09-14 00:34:04 -04:00
|
|
|
touchPoint = touch.getPoint();
|
|
|
|
int16_t touchX = touchPoint.x;
|
|
|
|
int16_t touchY = touchPoint.y;
|
|
|
|
bool touched = false;
|
|
|
|
if ((touchX > 0) || (touchY > 0)) {
|
|
|
|
touched = true;
|
2023-10-22 14:35:49 -04:00
|
|
|
mTouchEvent->notify(touchPoint);
|
2023-09-14 00:34:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!touched) {
|
|
|
|
data->state = LV_INDEV_STATE_REL;
|
|
|
|
} else {
|
|
|
|
data->state = LV_INDEV_STATE_PR;
|
|
|
|
|
|
|
|
// Set the coordinates
|
|
|
|
data->point.x = SCREEN_WIDTH - touchX;
|
|
|
|
data->point.y = SCREEN_HEIGHT - touchY;
|
|
|
|
|
|
|
|
// Serial.print( "touchpoint: x" );
|
|
|
|
// Serial.print( touchX );
|
|
|
|
// Serial.print( " y" );
|
|
|
|
// Serial.println( touchY );
|
|
|
|
// tft.drawFastHLine(0, screenHeight - touchY, screenWidth, TFT_RED);
|
|
|
|
// tft.drawFastVLine(screenWidth - touchX, 0, screenHeight, TFT_RED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-22 14:35:49 -04:00
|
|
|
void Display::fadeImpl(void *) {
|
2023-09-14 00:34:04 -04:00
|
|
|
bool fadeDone = false;
|
2023-10-22 14:35:49 -04:00
|
|
|
while (!fadeDone) {
|
2023-09-14 00:34:04 -04:00
|
|
|
fadeDone = getInstance()->fade();
|
2023-10-22 14:35:49 -04:00
|
|
|
vTaskDelay(3 / portTICK_PERIOD_MS); // 3 miliseconds between steps
|
|
|
|
// 0 - 255 will take about .75 seconds to fade up.
|
2023-09-14 00:34:04 -04:00
|
|
|
}
|
2023-10-22 14:35:49 -04:00
|
|
|
|
|
|
|
xSemaphoreTake(getInstance()->mFadeTaskMutex, portMAX_DELAY);
|
2023-09-14 00:34:04 -04:00
|
|
|
getInstance()->mDisplayFadeTask = nullptr;
|
|
|
|
xSemaphoreGive(getInstance()->mFadeTaskMutex);
|
|
|
|
|
|
|
|
vTaskDelete(nullptr); // Delete Fade Task
|
|
|
|
}
|
|
|
|
|
2023-10-22 14:35:49 -04:00
|
|
|
bool Display::fade() {
|
|
|
|
// Early return no fade needed.
|
|
|
|
if (mBrightness == mAwakeBrightness || isAsleep && mBrightness == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool fadeDown = isAsleep || mBrightness > mAwakeBrightness;
|
|
|
|
if (fadeDown) {
|
2023-09-14 00:34:04 -04:00
|
|
|
setCurrentBrightness(mBrightness - 1);
|
|
|
|
auto setPoint = isAsleep ? 0 : mAwakeBrightness;
|
|
|
|
return mBrightness == setPoint;
|
2023-10-22 14:35:49 -04:00
|
|
|
} else {
|
2023-09-14 00:34:04 -04:00
|
|
|
setCurrentBrightness(mBrightness + 1);
|
|
|
|
return mBrightness == mAwakeBrightness;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-22 14:35:49 -04:00
|
|
|
void Display::startFade() {
|
|
|
|
xSemaphoreTake(mFadeTaskMutex, portMAX_DELAY);
|
2023-09-14 00:34:04 -04:00
|
|
|
// Only Create Task if it is needed
|
2023-10-22 14:35:49 -04:00
|
|
|
if (mDisplayFadeTask == nullptr) {
|
|
|
|
xTaskCreate(&Display::fadeImpl, "Display Fade Task", 1024, nullptr, 5,
|
|
|
|
&mDisplayFadeTask);
|
2023-09-14 00:34:04 -04:00
|
|
|
}
|
|
|
|
xSemaphoreGive(mFadeTaskMutex);
|
|
|
|
}
|
|
|
|
|
2023-10-22 14:35:49 -04:00
|
|
|
void Display::flushDisplay(lv_disp_drv_t *disp, const lv_area_t *area,
|
|
|
|
lv_color_t *color_p) {
|
2023-09-14 00:34:04 -04:00
|
|
|
uint32_t w = (area->x2 - area->x1 + 1);
|
|
|
|
uint32_t h = (area->y2 - area->y1 + 1);
|
|
|
|
|
|
|
|
tft.startWrite();
|
|
|
|
tft.setAddrWindow(area->x1, area->y1, w, h);
|
|
|
|
tft.pushPixelsDMA((uint16_t *)&color_p->full, w * h);
|
|
|
|
tft.endWrite();
|
|
|
|
|
|
|
|
lv_disp_flush_ready(disp);
|
|
|
|
}
|