3 * Copyright (c) 2020 Project CHIP Authors
4 * Copyright (c) 2018 Nest Labs, Inc.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
23 * This file implements helper APIs for the M5Stack's display
29 #include "driver/ledc.h"
31 #include "esp_system.h"
32 #include "freertos/FreeRTOS.h"
33 #include "freertos/task.h"
34 #include "freertos/timers.h"
38 #if CONFIG_HAVE_DISPLAY
40 // Brightness picked such that it's easy for cameras to focus on
41 #define DEFAULT_BRIGHTNESS_PERCENT 10
43 // 8MHz is the recommended SPI speed to init the driver with
44 // It later gets set to the preconfigured defaults within the driver
45 #define TFT_SPI_CLOCK_INIT_HZ 8000000
47 // The frequency used by the ledc timer
48 // value chosen to eliminate flicker
49 #define LEDC_PWM_HZ 1000
51 // with a duty resolution of LEDC_TIMER_8_BIT
52 // the highest possible brightness value is 255
53 #define BRIGHTNESS_MAX 255
54 // The M5Stack's backlight is on Channel 7
55 #define BACKLIGHT_CHANNEL LEDC_CHANNEL_7
57 extern const char * TAG;
59 uint16_t DisplayHeight = 0;
60 uint16_t DisplayWidth = 0;
64 #if CONFIG_DISPLAY_AUTO_OFF
65 // FreeRTOS timer used to turn the display off after a short while
66 TimerHandle_t displayTimer = NULL;
69 #if CONFIG_DISPLAY_AUTO_OFF
70 static void TimerCallback(TimerHandle_t xTimer);
72 static void SetupBrightnessControl();
73 static void SetBrightness(uint16_t brightness_percent);
75 esp_err_t InitDisplay()
78 spi_lobo_device_handle_t spi;
80 // configured based on the display driver's examples
81 spi_lobo_bus_config_t buscfg;
82 memset((void *) &buscfg, 0, sizeof(buscfg));
83 buscfg.miso_io_num = PIN_NUM_MISO; // set SPI MISO pin
84 buscfg.mosi_io_num = PIN_NUM_MOSI; // set SPI MOSI pin
85 buscfg.sclk_io_num = PIN_NUM_CLK; // set SPI CLK pin
86 buscfg.quadwp_io_num = -1;
87 buscfg.quadhd_io_num = -1;
89 spi_lobo_device_interface_config_t devcfg;
90 memset((void *) &devcfg, 0, sizeof(devcfg));
91 devcfg.clock_speed_hz = TFT_SPI_CLOCK_INIT_HZ;
92 devcfg.mode = 0; // SPI mode 0
93 devcfg.spics_io_num = -1; // we will use external CS pin
94 devcfg.spics_ext_io_num = PIN_NUM_CS; // external CS pin
95 devcfg.flags = LB_SPI_DEVICE_HALFDUPLEX; // ALWAYS SET to HALF DUPLEX MODE!! for display spi
96 tft_max_rdclock = TFT_SPI_CLOCK_INIT_HZ;
98 // Initialize all pins used by display driver.
101 // Initialize SPI bus and add a device for the display.
102 err = spi_lobo_bus_add_device(TFT_HSPI_HOST, &buscfg, &devcfg, &spi);
106 // Configure the display to use the new SPI device.
109 err = spi_lobo_device_select(spi, 1);
112 err = spi_lobo_device_deselect(spi);
116 // Initialize the display driver.
119 // Detect maximum read speed and set it.
120 tft_max_rdclock = find_rd_speed();
122 // Set the SPI clock speed overriding the initialized 8MHz speed
123 spi_lobo_set_speed(spi, DEFAULT_SPI_CLOCK);
125 TFT_setGammaCurve(0);
126 TFT_setRotation(LANDSCAPE);
129 DisplayWidth = (uint16_t)(1 + tft_dispWin.x2 - tft_dispWin.x1);
130 DisplayHeight = (uint16_t)(1 + tft_dispWin.y2 - tft_dispWin.y1);
132 ESP_LOGI(TAG, "Display initialized (height %u, width %u)", DisplayHeight, DisplayWidth);
134 TFT_invertDisplay(INVERT_DISPLAY);
136 // prepare the display for brightness control
137 SetupBrightnessControl();
139 #if CONFIG_DISPLAY_AUTO_OFF
140 displayTimer = xTimerCreate("DisplayTimer", pdMS_TO_TICKS(DISPLAY_TIMEOUT_MS), false, NULL, TimerCallback);
142 // lower the brightness of the screen
148 void SetBrightness(uint16_t brightness_percent)
150 uint16_t brightness = (brightness_percent * BRIGHTNESS_MAX) / 100;
151 if (ledc_set_duty(LEDC_HIGH_SPEED_MODE, BACKLIGHT_CHANNEL, brightness) ||
152 ledc_update_duty(LEDC_HIGH_SPEED_MODE, BACKLIGHT_CHANNEL))
154 ESP_LOGE(TAG, "Failed to set display brightness...");
162 SetBrightness(DEFAULT_BRIGHTNESS_PERCENT);
163 #if CONFIG_DISPLAY_AUTO_OFF
164 xTimerStart(displayTimer, 0);
165 ESP_LOGI(TAG, "Display awake but will switch off automatically in %d seconds", DISPLAY_TIMEOUT_MS / 1000);
175 void ClearRect(uint16_t x_percent_start, uint16_t y_percent_start, uint16_t x_percent_end, uint16_t y_percent_end)
177 if (x_percent_end < x_percent_start)
179 x_percent_end = x_percent_start;
181 if (y_percent_end < y_percent_start)
183 y_percent_end = y_percent_start;
185 uint16_t start_x = (DisplayWidth * x_percent_start) / 100;
186 uint16_t start_y = (DisplayHeight * y_percent_start) / 100;
187 uint16_t end_x = (DisplayWidth * x_percent_end) / 100;
188 uint16_t end_y = (DisplayHeight * y_percent_end) / 100;
189 TFT_fillRect(start_x, start_y, end_x, end_y, TFT_BLACK);
192 void DisplayStatusMessage(char * msg, uint16_t vpos)
194 TFT_setFont(SMALL_FONT, NULL);
196 uint16_t msgY = (DisplayHeight * vpos) / 100;
197 TFT_print(msg, msgX, msgY);
200 void TimerCallback(TimerHandle_t xTimer)
202 ESP_LOGI(TAG, "Display going to sleep...");
207 void SetupBrightnessControl()
209 ledc_timer_config_t ledc_timer;
210 memset(&ledc_timer, 0, sizeof(ledc_timer));
212 ledc_timer.duty_resolution = LEDC_TIMER_8_BIT; // resolution of PWM duty
213 ledc_timer.freq_hz = LEDC_PWM_HZ; // frequency of PWM signal
214 ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE; // timer mode
215 ledc_timer.timer_num = LEDC_TIMER_0; // timer index
216 ledc_timer_config(&ledc_timer);
218 ledc_timer_set(LEDC_HIGH_SPEED_MODE, LEDC_TIMER_0, LEDC_PWM_HZ, LEDC_TIMER_8_BIT, LEDC_REF_TICK);
220 ledc_channel_config_t ledc_channel;
221 memset(&ledc_channel, 0, sizeof(ledc_channel));
222 ledc_channel.channel = BACKLIGHT_CHANNEL;
223 ledc_channel.duty = BRIGHTNESS_MAX;
224 ledc_channel.gpio_num = PIN_NUM_BCKL;
225 ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;
226 ledc_channel.timer_sel = LEDC_TIMER_0;
227 ledc_channel_config(&ledc_channel);
230 #endif // CONFIG_HAVE_DISPLAY