| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * Copyright (c) 2018 Nest Labs, Inc. |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file Display.cpp |
| * |
| * This file implements helper APIs for the M5Stack's display |
| * |
| */ |
| |
| #include <string.h> |
| |
| #include "driver/ledc.h" |
| #include "esp_log.h" |
| #include "esp_system.h" |
| #include "freertos/FreeRTOS.h" |
| #include "freertos/task.h" |
| #include "freertos/timers.h" |
| |
| #include "Display.h" |
| |
| #if CONFIG_HAVE_DISPLAY |
| |
| // Brightness picked such that it's easy for cameras to focus on |
| #define DEFFAULT_BRIGHTNESS_PERCENT 10 |
| |
| // 8MHz is the recommended SPI speed to init the driver with |
| // It later gets set to the preconfigured defaults within the driver |
| #define TFT_SPI_CLOCK_INIT_HZ 8000000 |
| |
| // The frequency used by the ledc timer |
| // value chosen to eliminate flicker |
| #define LEDC_PWM_HZ 1000 |
| |
| // with a duty resolution of LEDC_TIMER_8_BIT |
| // the highest possible brightness value is 255 |
| #define BRIGHTNESS_MAX 255 |
| // The M5Stack's backlight is on Channel 7 |
| #define BACKLIGHT_CHANNEL LEDC_CHANNEL_7 |
| |
| extern const char * TAG; |
| |
| uint16_t DisplayHeight = 0; |
| uint16_t DisplayWidth = 0; |
| |
| #if CONFIG_DISPLAY_AUTO_OFF |
| // FreeRTOS timer used to turn the display off after a short while |
| TimerHandle_t displayTimer = NULL; |
| #endif |
| |
| static void TimerCallback(TimerHandle_t xTimer); |
| static void SetupBrightnessControl(); |
| static void SetBrightness(uint16_t brightness_percent); |
| |
| esp_err_t InitDisplay() |
| { |
| esp_err_t err; |
| spi_lobo_device_handle_t spi; |
| |
| // configured based on the display driver's examples |
| spi_lobo_bus_config_t buscfg; |
| memset((void *) &buscfg, 0, sizeof(buscfg)); |
| buscfg.miso_io_num = PIN_NUM_MISO; // set SPI MISO pin |
| buscfg.mosi_io_num = PIN_NUM_MOSI; // set SPI MOSI pin |
| buscfg.sclk_io_num = PIN_NUM_CLK; // set SPI CLK pin |
| buscfg.quadwp_io_num = -1; |
| buscfg.quadhd_io_num = -1; |
| |
| spi_lobo_device_interface_config_t devcfg; |
| memset((void *) &devcfg, 0, sizeof(devcfg)); |
| devcfg.clock_speed_hz = TFT_SPI_CLOCK_INIT_HZ; |
| devcfg.mode = 0; // SPI mode 0 |
| devcfg.spics_io_num = -1; // we will use external CS pin |
| devcfg.spics_ext_io_num = PIN_NUM_CS; // external CS pin |
| devcfg.flags = LB_SPI_DEVICE_HALFDUPLEX; // ALWAYS SET to HALF DUPLEX MODE!! for display spi |
| tft_max_rdclock = TFT_SPI_CLOCK_INIT_HZ; |
| |
| // Initialize all pins used by display driver. |
| TFT_PinsInit(); |
| |
| // Initialize SPI bus and add a device for the display. |
| err = spi_lobo_bus_add_device(TFT_HSPI_HOST, &buscfg, &devcfg, &spi); |
| if (err != ESP_OK) |
| return err; |
| |
| // Configure the display to use the new SPI device. |
| tft_disp_spi = spi; |
| |
| err = spi_lobo_device_select(spi, 1); |
| if (err != ESP_OK) |
| return err; |
| err = spi_lobo_device_deselect(spi); |
| if (err != ESP_OK) |
| return err; |
| |
| // Initialize the display driver. |
| TFT_display_init(); |
| |
| // Detect maximum read speed and set it. |
| tft_max_rdclock = find_rd_speed(); |
| |
| // Set the SPI clock speed overriding the initialized 8MHz speed |
| spi_lobo_set_speed(spi, DEFAULT_SPI_CLOCK); |
| |
| TFT_setGammaCurve(0); |
| TFT_setRotation(LANDSCAPE); |
| TFT_resetclipwin(); |
| |
| DisplayWidth = (uint16_t)(1 + tft_dispWin.x2 - tft_dispWin.x1); |
| DisplayHeight = (uint16_t)(1 + tft_dispWin.y2 - tft_dispWin.y1); |
| |
| ESP_LOGI(TAG, "Display initialized (height %u, width %u)", DisplayHeight, DisplayWidth); |
| |
| // for some reason this is backwards (turns out this is because of a 2019 update to the m5stack hw) |
| TFT_invertDisplay(INVERT_ON); |
| |
| // prepare the display for brightness control |
| SetupBrightnessControl(); |
| |
| #if CONFIG_DISPLAY_AUTO_OFF |
| displayTimer = xTimerCreate("DisplayTimer", pdMS_TO_TICKS(DISPLAY_TIMEOUT_MS), false, NULL, TimerCallback); |
| #endif |
| // lower the brightness of the screen |
| WakeDisplay(); |
| |
| return err; |
| } |
| |
| void SetBrightness(uint16_t brightness_percent) |
| { |
| uint16_t brightness = (brightness_percent * BRIGHTNESS_MAX) / 100; |
| if (ledc_set_duty(LEDC_HIGH_SPEED_MODE, BACKLIGHT_CHANNEL, brightness) || |
| ledc_update_duty(LEDC_HIGH_SPEED_MODE, BACKLIGHT_CHANNEL)) |
| { |
| ESP_LOGE(TAG, "Failed to set display brightness..."); |
| } |
| } |
| |
| void WakeDisplay() |
| { |
| SetBrightness(DEFFAULT_BRIGHTNESS_PERCENT); |
| #if CONFIG_DISPLAY_AUTO_OFF |
| xTimerStart(displayTimer, 0); |
| ESP_LOGI(TAG, "Display awake but will switch off automatically in %d seconds", DISPLAY_TIMEOUT_MS / 1000); |
| #endif |
| } |
| |
| void ClearDisplay() |
| { |
| ClearRect(); |
| } |
| |
| void ClearRect(uint16_t x_percent_start, uint16_t y_percent_start, uint16_t x_percent_end, uint16_t y_percent_end) |
| { |
| if (x_percent_end < x_percent_start) |
| { |
| x_percent_end = x_percent_start; |
| } |
| if (y_percent_end < y_percent_start) |
| { |
| y_percent_end = y_percent_start; |
| } |
| uint16_t start_x = (DisplayWidth * x_percent_start) / 100; |
| uint16_t start_y = (DisplayHeight * y_percent_start) / 100; |
| uint16_t end_x = (DisplayWidth * x_percent_end) / 100; |
| uint16_t end_y = (DisplayHeight * y_percent_end) / 100; |
| TFT_fillRect(start_x, start_y, end_x, end_y, TFT_BLACK); |
| } |
| |
| void DisplayStatusMessage(char * msg, uint16_t vpos) |
| { |
| TFT_setFont(SMALL_FONT, NULL); |
| uint16_t msgX = 0; |
| uint16_t msgY = (DisplayHeight * vpos) / 100; |
| TFT_print(msg, msgX, msgY); |
| } |
| |
| void TimerCallback(TimerHandle_t xTimer) |
| { |
| ESP_LOGI(TAG, "Display going to sleep..."); |
| SetBrightness(0); |
| } |
| |
| void SetupBrightnessControl() |
| { |
| ledc_timer_config_t ledc_timer; |
| memset(&ledc_timer, 0, sizeof(ledc_timer)); |
| |
| ledc_timer.duty_resolution = LEDC_TIMER_8_BIT; // resolution of PWM duty |
| ledc_timer.freq_hz = LEDC_PWM_HZ; // frequency of PWM signal |
| ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE; // timer mode |
| ledc_timer.timer_num = LEDC_TIMER_0; // timer index |
| ledc_timer_config(&ledc_timer); |
| |
| ledc_timer_set(LEDC_HIGH_SPEED_MODE, LEDC_TIMER_0, LEDC_PWM_HZ, LEDC_TIMER_8_BIT, LEDC_REF_TICK); |
| |
| ledc_channel_config_t ledc_channel; |
| memset(&ledc_channel, 0, sizeof(ledc_channel)); |
| ledc_channel.channel = BACKLIGHT_CHANNEL; |
| ledc_channel.duty = BRIGHTNESS_MAX; |
| ledc_channel.gpio_num = PIN_NUM_BCKL; |
| ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; |
| ledc_channel.timer_sel = LEDC_TIMER_0; |
| ledc_channel_config(&ledc_channel); |
| } |
| |
| #endif // CONFIG_HAVE_DISPLAY |