#include "ygg_sh1106.h" #include "lvgl.h" #include "i2cdev.h" #include "esp_log.h" #include "esp_lcd_io_i2c.h" #include "esp_lcd_panel_ops.h" #include "esp_timer.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_sh1106.h" #define I2C_HOST I2C_NUM_0 #define EXAMPLE_I2C_MASTER_FREQ_HZ 400000 #define EXAMPLE_LCD_V_RES 64 #define EXAMPLE_LCD_H_RES 128 #define EXAMPLE_LVGL_PALETTE_SIZE 8 #define SH1106_I2C_SDA_GPIO 19 #define SH1106_I2C_SCL_GPIO 23 static uint8_t oled_buffer[EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES / 8]; static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) { esp_lcd_panel_handle_t panel_handle = lv_display_get_user_data(disp); // This is necessary because LVGL reserves 2 x 4 bytes in the buffer, as these are assumed to be used as a palette. Skip the palette here // More information about the monochrome, please refer to https://docs.lvgl.io/9.2/porting/display.html#monochrome-displays px_map += EXAMPLE_LVGL_PALETTE_SIZE; uint16_t hor_res = lv_display_get_physical_horizontal_resolution(disp); int x1 = area->x1; int x2 = area->x2; int y1 = area->y1; int y2 = area->y2; for (int y = y1; y <= y2; y++) { for (int x = x1; x <= x2; x++) { /* The order of bits is MSB first MSB LSB bits 7 6 5 4 3 2 1 0 pixels 0 1 2 3 4 5 6 7 Left Right */ bool chroma_color = (px_map[(hor_res >> 3) * y + (x >> 3)] & 1 << (7 - x % 8)); /* Write to the buffer as required for the display. * It writes only 1-bit for monochrome displays mapped vertically.*/ uint8_t *buf = oled_buffer + hor_res * (y >> 3) + (x); if (chroma_color) { (*buf) &= ~(1 << (y % 8)); } else { (*buf) |= (1 << (y % 8)); } } } // pass the draw buffer to the driver esp_lcd_panel_draw_bitmap(panel_handle, x1, y1, x2 + 1, y2 + 1, oled_buffer); lv_display_flush_ready(disp); } lv_display_t* ygg_sh1106_init() { //ESP_ERROR_CHECK(i2cdev_init()); static i2c_dev_t oled_i2c_dev; memset(&oled_i2c_dev, 0, sizeof(oled_i2c_dev)); oled_i2c_dev.port = I2C_HOST; // numer portu I2C oled_i2c_dev.addr = SH1106_I2C_ADDR; // adres OLED-a (0x3C) oled_i2c_dev.cfg.sda_io_num = SH1106_I2C_SDA_GPIO; // SDA z "idf menuconfig" oled_i2c_dev.cfg.scl_io_num = SH1106_I2C_SCL_GPIO; // SCL z "idf menuconfig" oled_i2c_dev.cfg.master.clk_speed = EXAMPLE_I2C_MASTER_FREQ_HZ; oled_i2c_dev.cfg.sda_pullup_en = true; // OLED zwykle nie ma pull-upów oled_i2c_dev.cfg.scl_pullup_en = true; // Rejestracja urządzenia + utworzenie mutexu. // Przy pierwszym urządzeniu na danym porcie i2cdev tworzy magistralę // (i2c_new_master_bus) z podanymi pinami SDA/SCL. ESP_ERROR_CHECK(i2c_dev_create_mutex(&oled_i2c_dev)); // Opcjonalnie: sprawdź, czy OLED odpowiada na magistrali. ESP_ERROR_CHECK(i2c_dev_check_present(&oled_i2c_dev)); // Pobierz współdzielony uchwyt magistrali zarządzanej przez i2cdev, // żeby przekazać go do natywnego komponentu esp_lcd. // Dzięki temu inne sensory na i2cdev i wyświetlacz na esp_lcd // bezpiecznie dzielą tę samą magistralę I2C. // UWAGA: uchwytem zarządza i2cdev — NIE wołaj na nim i2c_del_master_bus(). i2c_master_bus_handle_t i2c_bus_handle = NULL; ESP_ERROR_CHECK(i2cdev_get_shared_handle(I2C_HOST, (void **)&i2c_bus_handle)); // Create the i2c io handle esp_lcd_panel_io_handle_t io_handle = NULL; esp_lcd_panel_io_i2c_config_t io_config = ESP_SH1106_DEFAULT_IO_CONFIG; ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_bus_handle, &io_config, &io_handle)); /* SCREEN CONFIGURATION */ // sh1106 panel configuration (most of the values are not used, but must be set to avoid cpp warnings) esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = -1, // sh1106 does not have a reset pin, so set to -1 .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // not even used, but must be set to avoid cpp warnings .data_endian = LCD_RGB_DATA_ENDIAN_LITTLE, // not even used, but must be set to avoid cpp warnings .bits_per_pixel = SH1106_PIXELS_PER_BYTE / 8, // bpp = 1 (monochrome, that's important) .flags = { .reset_active_high = false, // not even used, but must be set to avoid cpp warnings }, .vendor_config = NULL, // no need for custom vendor config, not implemented }; // Create the panel handle from the sh1106 driver esp_lcd_panel_handle_t panel_handle = NULL; ESP_ERROR_CHECK(esp_lcd_new_panel_sh1106(io_handle, &panel_config, &panel_handle)); // Reset the screen (no reset pin, so it's a no-op here, optional) ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); // Initialize the screen (this one isn't optional at all!) ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); // Turn on the screen (Easier to see something, right?) ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); void *buf = NULL; size_t draw_buffer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES / 8 + EXAMPLE_LVGL_PALETTE_SIZE; buf = heap_caps_calloc(1, draw_buffer_sz, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); lv_init(); lv_display_t *display = lv_display_create(EXAMPLE_LCD_H_RES, EXAMPLE_LCD_V_RES); lv_display_set_user_data(display, panel_handle); lv_display_set_color_format(display, LV_COLOR_FORMAT_I1); lv_display_set_buffers(display, buf, NULL, draw_buffer_sz, LV_DISPLAY_RENDER_MODE_FULL); lv_display_set_flush_cb(display, example_lvgl_flush_cb); lv_obj_t *scr = lv_display_get_screen_active(display); return display; } void ygg_set_display_label(lv_display_t *display, lv_obj_t *label, const char *str) { lv_label_set_text(label, str); /* Size of the screen (if you use rotation 90 or 270, please use lv_display_get_vertical_resolution) */ lv_obj_set_width(label, lv_display_get_horizontal_resolution(display)); lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0); lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 0); }