view esp/update_lcd_block.h @ 21:a90cb6927c7d default tip

fix countdown queries. Display "now" instead of "In -0.4 hours"
author drewp@bigasterisk.com
date Sat, 07 Sep 2024 17:47:36 -0700
parents 47795c3121f1
children
line wrap: on
line source

#include <string>

#include "esphome.h"
// some from esphome; other code from
// https://github.com/lvgl/lvgl_esp32_drivers/blob/master/lvgl_tft/ili9481.c
// #include "esphome/components/ili9xxx/ili9xxx_display.h"

static const char *const TAG = "ili9481direct";

namespace esphome {
namespace ili9xxx {

/* MIPI DCS Type1  */
#define ILI9481_CMD_NOP 0x00
#define ILI9481_CMD_SOFTWARE_RESET 0x01
#define ILI9481_CMD_READ_DISP_POWER_MODE 0x0A
#define ILI9481_CMD_READ_DISP_MADCTRL 0x0B  // bits 7:3 only
#define ILI9481_CMD_READ_DISP_PIXEL_FORMAT 0x0C
#define ILI9481_CMD_READ_DISP_IMAGE_MODE 0x0D
#define ILI9481_CMD_READ_DISP_SIGNAL_MODE 0x0E
#define ILI9481_CMD_READ_DISP_SELF_DIAGNOSTIC 0x0F  // bits 7:6 only
#define ILI9481_CMD_ENTER_SLEEP_MODE 0x10
#define ILI9481_CMD_SLEEP_OUT 0x11
#define ILI9481_CMD_PARTIAL_MODE_ON 0x12
#define ILI9481_CMD_NORMAL_DISP_MODE_ON 0x13
#define ILI9481_CMD_DISP_INVERSION_OFF 0x20
#define ILI9481_CMD_DISP_INVERSION_ON 0x21
#define ILI9481_CMD_DISPLAY_OFF 0x28
#define ILI9481_CMD_DISPLAY_ON 0x29
#define ILI9481_CMD_COLUMN_ADDRESS_SET 0x2A
#define ILI9481_CMD_PAGE_ADDRESS_SET 0x2B
#define ILI9481_CMD_MEMORY_WRITE 0x2C
#define ILI9481_CMD_MEMORY_READ 0x2E
#define ILI9481_CMD_PARTIAL_AREA 0x30
#define ILI9481_CMD_VERT_SCROLL_DEFINITION 0x33
#define ILI9481_CMD_TEARING_EFFECT_LINE_OFF 0x34
#define ILI9481_CMD_TEARING_EFFECT_LINE_ON 0x35
#define ILI9481_CMD_MEMORY_ACCESS_CONTROL 0x36  // bits 7:3,1:0 only
#define ILI9481_CMD_VERT_SCROLL_START_ADDRESS 0x37
#define ILI9481_CMD_IDLE_MODE_OFF 0x38
#define ILI9481_CMD_IDLE_MODE_ON 0x39
#define ILI9481_CMD_COLMOD_PIXEL_FORMAT_SET 0x3A
#define ILI9481_CMD_WRITE_MEMORY_CONTINUE 0x3C
#define ILI9481_CMD_READ_MEMORY_CONTINUE 0x3E
#define ILI9481_CMD_SET_TEAR_SCANLINE 0x44
#define ILI9481_CMD_GET_SCANLINE 0x45

#define ILI9481_DDB_START 0xA1
#define ILI9481_DDB_CONTINUE 0xA8

/* other */
#define ILI9481_CMD_ACCESS_PROTECT 0xB0
#define ILI9481_CMD_LOW_POWER_CONTROL 0xB1
#define ILI9481_CMD_FRAME_MEMORY_ACCESS 0xB3
#define ILI9481_CMD_DISPLAY_MODE 0xB4
#define ILI9481_CMD_DEVICE_CODE 0xBF

#define ILI9481_CMD_PANEL_DRIVE 0xC0
#define ILI9481_CMD_DISP_TIMING_NORMAL 0xC1
#define ILI9481_CMD_DISP_TIMING_PARTIAL 0xC2
#define ILI9481_CMD_DISP_TIMING_IDLE 0xC3
#define ILI9481_CMD_FRAME_RATE 0xC5
#define ILI9481_CMD_INTERFACE_CONTROL 0xC6
#define ILI9481_CMD_GAMMA_SETTING 0xC8

#define ILI9481_CMD_POWER_SETTING 0xD0
#define ILI9481_CMD_VCOM_CONTROL 0xD1
#define ILI9481_CMD_POWER_CONTROL_NORMAL 0xD2
#define ILI9481_CMD_POWER_CONTROL_IDEL 0xD3
#define ILI9481_CMD_POWER_CONTROL_PARTIAL 0xD4

#define ILI9481_CMD_NVMEM_WRITE 0xE0
#define ILI9481_CMD_NVMEM_PROTECTION_KEY 0xE1
#define ILI9481_CMD_NVMEM_STATUS_READ 0xE2
#define ILI9481_CMD_NVMEM_PROTECTION 0xE3

typedef struct {
  uint8_t cmd;
  uint8_t data[16];
  uint8_t databytes;  // No of data in data; bit 7 = delay after set; 0xFF = end
                      // of cmds.
} lcd_init_cmd_t;

lcd_init_cmd_t ili_init_cmds[] = {
    {ILI9481_CMD_SLEEP_OUT, {0x00}, 0x80},

    // {ILI9481_CMD_POWER_SETTING, {0x07, 0x42, 0x18}, 3},  // from lvgl code
    {ILI9481_CMD_POWER_SETTING, {0x07, 0x41, 0x1D}, 3},  // from esphome code

    // {ILI9481_CMD_VCOM_CONTROL, {0x00, 0x07, 0x10}, 3},  // from lvgl code
    {ILI9481_CMD_VCOM_CONTROL, {0x00, 0x1c, 0x1f}, 3},  // from esphome code

    // {ILI9481_CMD_POWER_CONTROL_NORMAL, {0x01, 0x02}, 2},  // from lvgl code
    {ILI9481_CMD_POWER_CONTROL_NORMAL, {0x01, 0x11}, 2},  // from esphome code

    {ILI9481_CMD_PANEL_DRIVE, {0x10, 0x3B, 0x00, 0x02, 0x11}, 5},
    {ILI9481_CMD_FRAME_RATE, {0x03}, 1},
    {ILI9481_CMD_FRAME_MEMORY_ACCESS, {0x0, 0x0, 0x0, 0x0}, 4},
    //{ILI9481_CMD_DISP_TIMING_NORMAL, {0x10, 0x10, 0x22}, 3},
    {ILI9481_CMD_GAMMA_SETTING,
     {0x00, 0x32, 0x36, 0x45, 0x06, 0x16, 0x37, 0x75, 0x77, 0x54, 0x0C, 0x00},
     12},

#define ILI9481_ADDR_PAGE_ADDR_ORDER_B_TO_T 0x80
#define ILI9481_ADDR_COL_ADDR_ORDER_R_TO_L 0x40
#define ILI9481_ADDR_PAGE_COLUMN_ORDER_REVERSE 0x20
#define ILI9481_ADDR_LCD_REFRESH_BOT_TO_TOP 0x10
#define ILI9481_ADDR_BGR_ORDER 0x08
#define ILI9481_ADDR_HFLIP 0x02
#define ILI9481_ADDR_VFLIP 0x01

    {ILI9481_CMD_MEMORY_ACCESS_CONTROL,
     {ILI9481_ADDR_BGR_ORDER |
      ILI9481_ADDR_HFLIP |
      0},
     1},

    {ILI9481_CMD_COLMOD_PIXEL_FORMAT_SET, {0x66}, 1},
    // {ILI9481_CMD_COLMOD_PIXEL_FORMAT_SET, {0x55}, 1},

    {ILI9481_CMD_DISP_INVERSION_ON, {}, 0x80},
    {ILI9481_CMD_NORMAL_DISP_MODE_ON, {}, 0x80},
    {ILI9481_CMD_DISPLAY_ON, {}, 0x80},
    {0, {0}, 0xff},
};

class ILI9481Direct
    : public Component,
      public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
                            spi::CLOCK_PHASE_LEADING,
                            // spi::DATA_RATE_40MHZ
                            spi::DATA_RATE_10MHZ> {
 public:
  ILI9481Direct(esphome::spi::SPIComponent *spi) { spi_ = spi; }
  esphome::spi::SPIComponent *spi_;
  int width_ = 320;
  int height_ = 480;
  esp32::ESP32InternalGPIOPin *reset_pin_;
  esp32::ESP32InternalGPIOPin *dc_pin_;
  esp32::ESP32InternalGPIOPin *cs_pin_;

  void setup() override {
    this->setup_pins_();
    this->init_lcd_();
  }
  void loop() override {}

  void setup_pins_() {
    dc_pin_ = new esp32::ESP32InternalGPIOPin();
    dc_pin_->set_pin(::GPIO_NUM_22);
    dc_pin_->set_inverted(false);
    dc_pin_->set_drive_strength(::GPIO_DRIVE_CAP_2);
    dc_pin_->set_flags(gpio::Flags::FLAG_OUTPUT);

    dc_pin_->setup();  // OUTPUT
    dc_pin_->digital_write(false);

    reset_pin_ = new esp32::ESP32InternalGPIOPin();
    reset_pin_->set_pin(::GPIO_NUM_25);
    reset_pin_->set_inverted(false);
    reset_pin_->set_drive_strength(::GPIO_DRIVE_CAP_2);
    reset_pin_->set_flags(gpio::Flags::FLAG_OUTPUT);

    reset_pin_->setup();  // OUTPUT
    reset_pin_->digital_write(true);

    cs_pin_ = new esp32::ESP32InternalGPIOPin();
    cs_pin_->set_pin(::GPIO_NUM_27);
    cs_pin_->set_inverted(false);
    cs_pin_->set_drive_strength(::GPIO_DRIVE_CAP_2);
    cs_pin_->set_flags(gpio::Flags::FLAG_OUTPUT);

    set_cs_pin(cs_pin_);
    set_spi_parent(spi_);
    spi_setup();
    reset_();
  }

  void init_lcd_() {
    command(ILI9481_CMD_SOFTWARE_RESET);
    delay(150);

    uint16_t cmd = 0;
    while (ili_init_cmds[cmd].databytes != 0xff) {
      command(ili_init_cmds[cmd].cmd);
      {
        start_data_();
        write_array(ili_init_cmds[cmd].data,
                    ili_init_cmds[cmd].databytes & 0x1F);
        end_data_();
      }
      if (ili_init_cmds[cmd].databytes & 0x80) {
        delay(150);
      }
      cmd++;
    }
    ESP_LOGI(TAG, "done init cmds");
  }

  void blit(uint16_t xp, uint16_t yp, uint16_t w, uint16_t h,
            const unsigned char *&buf) {
    set_addr_window_(xp, yp, xp + w - 1, yp + h - 1);

    this->command(ILI9481_CMD_MEMORY_WRITE);
    {
      this->start_data_();
      this->write_array(buf, static_cast<size_t>(h * w * 3));
      this->end_data_();
    }
  }

  void set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
    command(ILI9481_CMD_COLUMN_ADDRESS_SET);
    {
      start_data_();
      write_byte(x1 >> 8);
      write_byte(x1 & 0xFF);
      write_byte(x2 >> 8);
      write_byte(x2 & 0xFF);
      end_data_();
    }
    command(ILI9481_CMD_PAGE_ADDRESS_SET);
    {
      start_data_();
      write_byte(y1 >> 8);
      write_byte(y1 & 0xFF);
      write_byte(y2 >> 8);
      write_byte(y2 & 0xFF);
      end_data_();
    }
  }
  void command(uint8_t value) {
    this->start_command_();
    this->write_byte(value);
    this->end_command_();
  }

  void start_command_() {
    this->dc_pin_->digital_write(false);
    this->enable();
  }
  void end_command_() { this->disable(); }

  void start_data_() {
    this->dc_pin_->digital_write(true);
    this->enable();
  }
  void end_data_() { this->disable(); }

  void reset_() {
    if (this->reset_pin_ != nullptr) {
      this->reset_pin_->digital_write(false);
      delay(20);
      this->reset_pin_->digital_write(true);
      delay(20);
    }
  }
};

void update_lcd_block(ILI9481Direct *lcd, std::string &x) {
#if 0
  std::string localPayload = x;
  const unsigned char *buf = reinterpret_cast<const unsigned char*>(localPayload.data());
#else
  const unsigned char *buf = reinterpret_cast<const unsigned char *>(x.data());
#endif

  uint16_t seq, xp, yp, w, h;
#define readu16()                          \
  (static_cast<const uint16_t>(*(buf++)) | \
   (static_cast<const uint16_t>(*(buf++)) << 8))
  seq = readu16();
  xp = readu16();
  yp = readu16();
  w = readu16();
  h = readu16();
  char seqDia[] = "           ";
  seqDia[seq % 10] = '%';
  ESP_LOGD("dbg",
           "seq=%hu%s xp=%hu, yp=%hu, w=%hu, h=%hu, full payload=%d bytes", seq,
           seqDia, xp, yp, w, h, x.size());

  lcd->blit(xp, yp, w, h, buf);
}
}  // namespace ili9xxx
}  // namespace esphome