Skip to content

Commit

Permalink
Merge branch 'feature/support_rotating_lcd_ssd1306_1' into 'master'
Browse files Browse the repository at this point in the history
Support rotation SSD1306, changed example for using LVGL port component and added SH1107 LCD support.

See merge request espressif/esp-idf!21196
  • Loading branch information
espzav committed Dec 8, 2022
2 parents fb4b18d + 7e661b3 commit 1303a7c
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 88 deletions.
21 changes: 19 additions & 2 deletions components/esp_lcd/src/esp_lcd_panel_ssd1306.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ typedef struct {
int x_gap;
int y_gap;
unsigned int bits_per_pixel;
bool swap_axes;
} ssd1306_panel_t;

esp_err_t esp_lcd_new_panel_ssd1306(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel)
Expand Down Expand Up @@ -143,6 +144,8 @@ static esp_err_t panel_ssd1306_init(esp_lcd_panel_t *panel)
esp_lcd_panel_io_tx_param(io, SSD1306_CMD_SET_CHARGE_PUMP, (uint8_t[]) {
0x14 // enable charge pump
}, 1);
esp_lcd_panel_io_tx_param(io, SSD1306_CMD_MIRROR_X_OFF, NULL, 0);
esp_lcd_panel_io_tx_param(io, SSD1306_CMD_MIRROR_Y_OFF, NULL, 0);
return ESP_OK;
}

Expand All @@ -151,11 +154,22 @@ static esp_err_t panel_ssd1306_draw_bitmap(esp_lcd_panel_t *panel, int x_start,
ssd1306_panel_t *ssd1306 = __containerof(panel, ssd1306_panel_t, base);
assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position");
esp_lcd_panel_io_handle_t io = ssd1306->io;

// adding extra gap
x_start += ssd1306->x_gap;
x_end += ssd1306->x_gap;
y_start += ssd1306->y_gap;
y_end += ssd1306->y_gap;

if (ssd1306->swap_axes) {
int x = x_start;
x_start = y_start;
y_start = x;
x = x_end;
x_end = y_end;
y_end = x;
}

// one page contains 8 rows (COMs)
uint8_t page_start = y_start / 8;
uint8_t page_end = (y_end - 1) / 8;
Expand Down Expand Up @@ -204,15 +218,18 @@ static esp_err_t panel_ssd1306_mirror(esp_lcd_panel_t *panel, bool mirror_x, boo
if (mirror_y) {
command = SSD1306_CMD_MIRROR_Y_ON;
} else {
command = SSD1306_CMD_MIRROR_X_OFF;
command = SSD1306_CMD_MIRROR_Y_OFF;
}
esp_lcd_panel_io_tx_param(io, command, NULL, 0);
return ESP_OK;
}

static esp_err_t panel_ssd1306_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
{
return ESP_ERR_NOT_SUPPORTED;
ssd1306_panel_t *ssd1306 = __containerof(panel, ssd1306_panel_t, base);
ssd1306->swap_axes = swap_axes;

return ESP_OK;
}

static esp_err_t panel_ssd1306_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
Expand Down
16 changes: 16 additions & 0 deletions examples/peripherals/lcd/i2c_oled/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
menu "Example Configuration"

choice EXAMPLE_LCD_CONTROLLER
prompt "LCD controller model"
default EXAMPLE_LCD_CONTROLLER_SSD1306
help
Select LCD controller model

config EXAMPLE_LCD_CONTROLLER_SSD1306
bool "SSD1306"

config EXAMPLE_LCD_CONTROLLER_SH1107
bool "SH1107"
endchoice

endmenu
146 changes: 61 additions & 85 deletions examples/peripherals/lcd/i2c_oled/main/i2c_oled_example_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/i2c.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"
#include "esp_lvgl_port.h"

#if CONFIG_EXAMPLE_LCD_CONTROLLER_SH1107
#include "esp_lcd_sh1107.h"
#else
#include "esp_lcd_panel_vendor.h"
#endif

static const char *TAG = "example";

Expand All @@ -30,64 +36,31 @@ static const char *TAG = "example";
#define EXAMPLE_I2C_HW_ADDR 0x3C

// The pixel number in horizontal and vertical
#if CONFIG_EXAMPLE_LCD_CONTROLLER_SSD1306
#define EXAMPLE_LCD_H_RES 128
#define EXAMPLE_LCD_V_RES 64
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_SH1107
#define EXAMPLE_LCD_H_RES 64
#define EXAMPLE_LCD_V_RES 128
#endif
// Bit number used to represent command and parameter
#define EXAMPLE_LCD_CMD_BITS 8
#define EXAMPLE_LCD_PARAM_BITS 8

#define EXAMPLE_LVGL_TICK_PERIOD_MS 2

extern void example_lvgl_demo_ui(lv_disp_t *disp);

static bool example_notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
/* The LVGL port component calls esp_lcd_panel_draw_bitmap API for send data to the screen. There must be called
lvgl_port_flush_ready(disp) after each transaction to display. The best way is to use on_color_trans_done
callback from esp_lcd IO config structure. In IDF 5.1 and higher, it is solved inside LVGL port component. */
static bool notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
lv_disp_flush_ready(disp_driver);
lv_disp_t * disp = (lv_disp_t *)user_ctx;
lvgl_port_flush_ready(disp);
return false;
}

static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
int offsetx1 = area->x1;
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
// copy a buffer's content to a specific area of the display
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}

static void example_lvgl_set_px_cb(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
lv_color_t color, lv_opa_t opa)
{
uint16_t byte_index = x + (( y >> 3 ) * buf_w);
uint8_t bit_index = y & 0x7;

if ((color.full == 0) && (LV_OPA_TRANSP != opa)) {
buf[byte_index] |= (1 << bit_index);
} else {
buf[byte_index] &= ~(1 << bit_index);
}
}

static void example_lvgl_rounder(lv_disp_drv_t *disp_drv, lv_area_t *area)
{
area->y1 = area->y1 & (~0x7);
area->y2 = area->y2 | 0x7;
}

static void example_increase_lvgl_tick(void *arg)
{
/* Tell LVGL how many milliseconds has elapsed */
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}

void app_main(void)
{
static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
static lv_disp_drv_t disp_drv; // contains callback functions

ESP_LOGI(TAG, "Initialize I2C bus");
i2c_config_t i2c_conf = {
.mode = I2C_MODE_MASTER,
Expand All @@ -105,11 +78,17 @@ void app_main(void)
esp_lcd_panel_io_i2c_config_t io_config = {
.dev_addr = EXAMPLE_I2C_HW_ADDR,
.control_phase_bytes = 1, // According to SSD1306 datasheet
.dc_bit_offset = 6, // According to SSD1306 datasheet
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS, // According to SSD1306 datasheet
.lcd_param_bits = EXAMPLE_LCD_CMD_BITS, // According to SSD1306 datasheet
.on_color_trans_done = example_notify_lvgl_flush_ready,
.user_ctx = &disp_drv,
#if CONFIG_EXAMPLE_LCD_CONTROLLER_SSD1306
.dc_bit_offset = 6, // According to SSD1306 datasheet
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_SH1107
.dc_bit_offset = 0, // According to SH1107 datasheet
.flags =
{
.disable_control_phase = 1,
}
#endif
};
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)I2C_HOST, &io_config, &io_handle));

Expand All @@ -119,51 +98,48 @@ void app_main(void)
.bits_per_pixel = 1,
.reset_gpio_num = EXAMPLE_PIN_NUM_RST,
};
#if CONFIG_EXAMPLE_LCD_CONTROLLER_SSD1306
ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(io_handle, &panel_config, &panel_handle));
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_SH1107
ESP_ERROR_CHECK(esp_lcd_new_panel_sh1107(io_handle, &panel_config, &panel_handle));
#endif

ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));

ESP_LOGI(TAG, "Initialize LVGL library");
lv_init();
// alloc draw buffers used by LVGL
// it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized
lv_color_t *buf1 = malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t));
assert(buf1);
lv_color_t *buf2 = malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t));
assert(buf2);
// initialize LVGL draw buffers
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 20);

ESP_LOGI(TAG, "Register display driver to LVGL");
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = EXAMPLE_LCD_H_RES;
disp_drv.ver_res = EXAMPLE_LCD_V_RES;
disp_drv.flush_cb = example_lvgl_flush_cb;
disp_drv.draw_buf = &disp_buf;
disp_drv.user_data = panel_handle;
disp_drv.rounder_cb = example_lvgl_rounder;
disp_drv.set_px_cb = example_lvgl_set_px_cb;
lv_disp_t *disp = lv_disp_drv_register(&disp_drv);

ESP_LOGI(TAG, "Install LVGL tick timer");
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
const esp_timer_create_args_t lvgl_tick_timer_args = {
.callback = &example_increase_lvgl_tick,
.name = "lvgl_tick"
#if CONFIG_EXAMPLE_LCD_CONTROLLER_SH1107
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
#endif

ESP_LOGI(TAG, "Initialize LVGL");
const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
lvgl_port_init(&lvgl_cfg);

const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = io_handle,
.panel_handle = panel_handle,
.buffer_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES,
.double_buffer = true,
.hres = EXAMPLE_LCD_H_RES,
.vres = EXAMPLE_LCD_V_RES,
.monochrome = true,
.rotation = {
.swap_xy = false,
.mirror_x = false,
.mirror_y = false,
}
};
lv_disp_t * disp = lvgl_port_add_disp(&disp_cfg);
/* Register done callback for IO */
const esp_lcd_panel_io_callbacks_t cbs = {
.on_color_trans_done = notify_lvgl_flush_ready,
};
esp_timer_handle_t lvgl_tick_timer = NULL;
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
esp_lcd_panel_io_register_event_callbacks(io_handle, &cbs, disp);

/* Rotation of the screen */
lv_disp_set_rotation(disp, LV_DISP_ROT_NONE);

ESP_LOGI(TAG, "Display LVGL Scroll Text");
example_lvgl_demo_ui(disp);

while (1) {
// raise the task priority of LVGL and/or reduce the handler period can improve the performance
vTaskDelay(pdMS_TO_TICKS(10));
// The task running lv_timer_handler should have lower priority than that running `lv_tick_inc`
lv_timer_handler();
}
}
2 changes: 2 additions & 0 deletions examples/peripherals/lcd/i2c_oled/main/idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
dependencies:
idf: ">=4.4"
lvgl/lvgl: "~8.2.0"
esp_lcd_sh1107: "^1"
esp_lvgl_port: "^1"
3 changes: 2 additions & 1 deletion examples/peripherals/lcd/i2c_oled/main/lvgl_demo_ui.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ void example_lvgl_demo_ui(lv_disp_t *disp)
lv_obj_t *label = lv_label_create(scr);
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); /* Circular scroll */
lv_label_set_text(label, "Hello Espressif, Hello LVGL.");
lv_obj_set_width(label, 128);
/* Size of the screen (if you use rotation 90 or 270, please set disp->driver->ver_res) */
lv_obj_set_width(label, disp->driver->hor_res);
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 0);
}

0 comments on commit 1303a7c

Please sign in to comment.