2020-12-17 06:02:55 +00:00
|
|
|
/**
|
|
|
|
* @file ssd1306.c
|
|
|
|
*
|
2021-01-21 16:54:17 +00:00
|
|
|
* Code from https://github.com/yanbe/ssd1306-esp-idf-i2c.git is used as a starting point,
|
|
|
|
* in addition to code from https://github.com/espressif/esp-iot-solution.
|
2021-02-12 09:39:58 +00:00
|
|
|
*
|
2021-01-21 16:54:17 +00:00
|
|
|
* Definitions are borrowed from:
|
|
|
|
* http://robotcantalk.blogspot.com/2015/03/interfacing-arduino-with-ssd1306-driven.html
|
2021-02-12 09:39:58 +00:00
|
|
|
*
|
2021-01-21 16:54:17 +00:00
|
|
|
* For LVGL the forum has been used, in particular: https://blog.littlevgl.com/2019-05-06/oled
|
2020-12-17 06:02:55 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*********************
|
|
|
|
* INCLUDES
|
|
|
|
*********************/
|
|
|
|
#include "driver/i2c.h"
|
2021-01-22 16:19:06 +00:00
|
|
|
#include "assert.h"
|
2020-12-17 06:02:55 +00:00
|
|
|
|
2021-01-17 05:33:56 +00:00
|
|
|
#include "lvgl_i2c_conf.h"
|
|
|
|
|
2021-01-21 16:54:17 +00:00
|
|
|
#include "ssd1306.h"
|
|
|
|
|
2020-12-17 06:02:55 +00:00
|
|
|
/*********************
|
|
|
|
* DEFINES
|
|
|
|
*********************/
|
|
|
|
#define TAG "SSD1306"
|
|
|
|
|
|
|
|
// SLA (0x3C) + WRITE_MODE (0x00) = 0x78 (0b01111000)
|
|
|
|
#define OLED_I2C_ADDRESS 0x3C
|
|
|
|
#define OLED_WIDTH 128
|
|
|
|
#define OLED_HEIGHT 64
|
|
|
|
#define OLED_COLUMNS 128
|
|
|
|
#define OLED_PAGES 8
|
|
|
|
#define OLED_PIXEL_PER_PAGE 8
|
|
|
|
|
|
|
|
// Control byte
|
|
|
|
#define OLED_CONTROL_BYTE_CMD_SINGLE 0x80
|
|
|
|
#define OLED_CONTROL_BYTE_CMD_STREAM 0x00
|
|
|
|
#define OLED_CONTROL_BYTE_DATA_STREAM 0x40
|
|
|
|
|
|
|
|
// Fundamental commands (pg.28)
|
|
|
|
#define OLED_CMD_SET_CONTRAST 0x81 // follow with 0x7F
|
|
|
|
#define OLED_CMD_DISPLAY_RAM 0xA4
|
|
|
|
#define OLED_CMD_DISPLAY_ALLON 0xA5
|
|
|
|
#define OLED_CMD_DISPLAY_NORMAL 0xA6
|
|
|
|
#define OLED_CMD_DISPLAY_INVERTED 0xA7
|
|
|
|
#define OLED_CMD_DISPLAY_OFF 0xAE
|
|
|
|
#define OLED_CMD_DISPLAY_ON 0xAF
|
|
|
|
|
|
|
|
// Addressing Command Table (pg.30)
|
|
|
|
#define OLED_CMD_SET_MEMORY_ADDR_MODE 0x20 // follow with 0x00 = HORZ mode
|
|
|
|
#define OLED_CMD_SET_COLUMN_RANGE 0x21 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x7F = COL127
|
|
|
|
#define OLED_CMD_SET_PAGE_RANGE 0x22 // can be used only in HORZ/VERT mode - follow with 0x00 and 0x07 = PAGE7
|
|
|
|
|
|
|
|
// Hardware Config (pg.31)
|
|
|
|
#define OLED_CMD_SET_DISPLAY_START_LINE 0x40
|
|
|
|
#define OLED_CMD_SET_SEGMENT_REMAP 0xA1
|
|
|
|
#define OLED_CMD_SET_MUX_RATIO 0xA8 // follow with 0x3F = 64 MUX
|
|
|
|
#define OLED_CMD_SET_COM_SCAN_MODE_NORMAL 0xC0
|
|
|
|
#define OLED_CMD_SET_COM_SCAN_MODE_REMAP 0xC8
|
|
|
|
#define OLED_CMD_SET_DISPLAY_OFFSET 0xD3 // follow with 0x00
|
|
|
|
#define OLED_CMD_SET_COM_PIN_MAP 0xDA // follow with 0x12
|
|
|
|
#define OLED_CMD_NOP 0xE3 // NOP
|
|
|
|
|
|
|
|
// Timing and Driving Scheme (pg.32)
|
|
|
|
#define OLED_CMD_SET_DISPLAY_CLK_DIV 0xD5 // follow with 0x80
|
|
|
|
#define OLED_CMD_SET_PRECHARGE 0xD9 // follow with 0xF1
|
|
|
|
#define OLED_CMD_SET_VCOMH_DESELCT 0xDB // follow with 0x30
|
|
|
|
|
|
|
|
// Charge Pump (pg.62)
|
|
|
|
#define OLED_CMD_SET_CHARGE_PUMP 0x8D // follow with 0x14
|
|
|
|
|
|
|
|
#define OLED_IIC_FREQ_HZ 400000 // I2C colock frequency
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* TYPEDEFS
|
|
|
|
**********************/
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* STATIC PROTOTYPES
|
|
|
|
**********************/
|
2021-01-21 16:54:17 +00:00
|
|
|
static uint8_t send_data(lv_disp_drv_t *disp_drv, void *bytes, size_t bytes_len);
|
|
|
|
static uint8_t send_pixels(lv_disp_drv_t *disp_drv, void *color_buffer, size_t buffer_len);
|
|
|
|
|
2020-12-17 06:02:55 +00:00
|
|
|
/**********************
|
|
|
|
* STATIC VARIABLES
|
|
|
|
**********************/
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* MACROS
|
|
|
|
**********************/
|
|
|
|
|
|
|
|
#define BIT_SET(a,b) ((a) |= (1U<<(b)))
|
|
|
|
#define BIT_CLEAR(a,b) ((a) &= ~(1U<<(b)))
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* GLOBAL FUNCTIONS
|
|
|
|
**********************/
|
2021-01-21 16:57:02 +00:00
|
|
|
void ssd1306_init(void)
|
2020-12-17 06:02:55 +00:00
|
|
|
{
|
2021-01-17 06:25:01 +00:00
|
|
|
uint8_t orientation_1 = 0;
|
|
|
|
uint8_t orientation_2 = 0;
|
2021-01-17 02:37:55 +00:00
|
|
|
|
|
|
|
#if defined (CONFIG_DISPLAY_ORIENTATION_PORTRAIT)
|
2021-01-17 06:25:01 +00:00
|
|
|
orientation_1 = OLED_CMD_SET_SEGMENT_REMAP;
|
|
|
|
orientation_2 = OLED_CMD_SET_COM_SCAN_MODE_REMAP;
|
2021-01-17 02:37:55 +00:00
|
|
|
#elif defined (CONFIG_DISPLAY_ORIENTATION_PORTRAIT_INVERTED)
|
2021-01-17 06:25:01 +00:00
|
|
|
orientation_1 = 0xA0;
|
|
|
|
orientation_2 = OLED_CMD_SET_COM_SCAN_MODE_NORMAL;
|
2021-01-17 02:37:55 +00:00
|
|
|
#else
|
2021-01-17 06:25:01 +00:00
|
|
|
#error "Unsupported orientation"
|
2021-01-17 02:37:55 +00:00
|
|
|
#endif
|
|
|
|
|
2021-01-17 06:25:01 +00:00
|
|
|
uint8_t display_mode = 0;
|
2020-12-17 06:02:55 +00:00
|
|
|
|
2021-02-12 09:39:58 +00:00
|
|
|
#if defined CONFIG_LV_INVERT_COLORS
|
2021-01-17 06:25:01 +00:00
|
|
|
display_mode = OLED_CMD_DISPLAY_INVERTED;
|
2020-12-17 06:02:55 +00:00
|
|
|
#else
|
2021-01-17 06:25:01 +00:00
|
|
|
display_mode = OLED_CMD_DISPLAY_NORMAL;
|
|
|
|
#endif
|
2020-12-17 06:02:55 +00:00
|
|
|
|
2021-01-17 06:25:01 +00:00
|
|
|
uint8_t conf[] = {
|
|
|
|
OLED_CONTROL_BYTE_CMD_STREAM,
|
|
|
|
OLED_CMD_SET_CHARGE_PUMP,
|
|
|
|
0x14,
|
|
|
|
orientation_1,
|
|
|
|
orientation_2,
|
|
|
|
OLED_CMD_SET_CONTRAST,
|
|
|
|
display_mode,
|
|
|
|
0xFF,
|
|
|
|
OLED_CMD_DISPLAY_ON
|
|
|
|
};
|
2021-02-12 09:39:58 +00:00
|
|
|
|
2021-01-22 16:19:06 +00:00
|
|
|
uint8_t err = send_data(NULL, conf, sizeof(conf));
|
|
|
|
assert(0 == err);
|
2020-12-17 06:02:55 +00:00
|
|
|
}
|
|
|
|
|
2021-01-10 02:58:23 +00:00
|
|
|
void ssd1306_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,
|
2021-01-21 16:54:17 +00:00
|
|
|
lv_color_t color, lv_opa_t opa)
|
|
|
|
{
|
2020-12-17 06:02:55 +00:00
|
|
|
uint16_t byte_index = x + (( y>>3 ) * buf_w);
|
|
|
|
uint8_t bit_index = y & 0x7;
|
|
|
|
|
|
|
|
if ((color.full == 0) && (LV_OPA_TRANSP != opa)) {
|
|
|
|
BIT_SET(buf[byte_index], bit_index);
|
|
|
|
} else {
|
|
|
|
BIT_CLEAR(buf[byte_index], bit_index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ssd1306_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
|
|
|
|
{
|
2021-01-17 06:10:12 +00:00
|
|
|
/* Divide by 8 */
|
|
|
|
uint8_t row1 = area->y1 >> 3;
|
|
|
|
uint8_t row2 = area->y2 >> 3;
|
|
|
|
|
|
|
|
uint8_t conf[] = {
|
|
|
|
OLED_CONTROL_BYTE_CMD_STREAM,
|
|
|
|
OLED_CMD_SET_MEMORY_ADDR_MODE,
|
|
|
|
0x00,
|
|
|
|
OLED_CMD_SET_COLUMN_RANGE,
|
|
|
|
(uint8_t) area->x1,
|
|
|
|
(uint8_t) area->x2,
|
|
|
|
OLED_CMD_SET_PAGE_RANGE,
|
|
|
|
row1,
|
|
|
|
row2,
|
|
|
|
};
|
|
|
|
|
2021-01-22 16:19:06 +00:00
|
|
|
uint8_t err = send_data(disp_drv, conf, sizeof(conf));
|
|
|
|
assert(0 == err);
|
|
|
|
err = send_pixels(disp_drv, color_p, OLED_COLUMNS * (1 + row2 - row1));
|
|
|
|
assert(0 == err);
|
2020-12-17 06:02:55 +00:00
|
|
|
|
|
|
|
lv_disp_flush_ready(disp_drv);
|
|
|
|
}
|
|
|
|
|
2021-01-10 02:58:23 +00:00
|
|
|
void ssd1306_rounder(lv_disp_drv_t * disp_drv, lv_area_t *area)
|
2020-12-17 06:02:55 +00:00
|
|
|
{
|
2021-01-17 06:10:12 +00:00
|
|
|
area->x1 = area->x1 & ~(0x07);
|
|
|
|
area->x2 = area->x2 | 0x07;
|
2020-12-17 06:02:55 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 16:57:02 +00:00
|
|
|
void ssd1306_sleep_in(void)
|
2020-12-17 06:02:55 +00:00
|
|
|
{
|
2021-01-17 06:25:01 +00:00
|
|
|
uint8_t conf[] = {
|
|
|
|
OLED_CONTROL_BYTE_CMD_STREAM,
|
|
|
|
OLED_CMD_DISPLAY_OFF
|
|
|
|
};
|
2020-12-17 06:02:55 +00:00
|
|
|
|
2021-01-22 16:19:06 +00:00
|
|
|
uint8_t err = send_data(NULL, conf, sizeof(conf));
|
|
|
|
assert(0 == err);
|
2020-12-17 06:02:55 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 16:57:02 +00:00
|
|
|
void ssd1306_sleep_out(void)
|
2020-12-17 06:02:55 +00:00
|
|
|
{
|
2021-01-17 06:25:01 +00:00
|
|
|
uint8_t conf[] = {
|
|
|
|
OLED_CONTROL_BYTE_CMD_STREAM,
|
|
|
|
OLED_CMD_DISPLAY_ON
|
|
|
|
};
|
|
|
|
|
2021-01-22 16:19:06 +00:00
|
|
|
uint8_t err = send_data(NULL, conf, sizeof(conf));
|
|
|
|
assert(0 == err);
|
2020-12-17 06:02:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**********************
|
|
|
|
* STATIC FUNCTIONS
|
|
|
|
**********************/
|
2021-01-21 16:54:17 +00:00
|
|
|
static uint8_t send_data(lv_disp_drv_t *disp_drv, void *bytes, size_t bytes_len)
|
2021-01-17 06:10:12 +00:00
|
|
|
{
|
|
|
|
(void) disp_drv;
|
2021-01-22 16:12:30 +00:00
|
|
|
esp_err_t err;
|
2020-12-17 06:02:55 +00:00
|
|
|
|
2021-01-17 06:10:12 +00:00
|
|
|
uint8_t *data = (uint8_t *) bytes;
|
|
|
|
|
|
|
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
2021-02-12 09:39:58 +00:00
|
|
|
|
2021-01-17 06:10:12 +00:00
|
|
|
i2c_master_start(cmd);
|
|
|
|
i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
|
|
|
|
|
|
|
|
for (size_t idx = 0; idx < bytes_len; idx++) {
|
|
|
|
i2c_master_write_byte(cmd, data[idx], true);
|
|
|
|
}
|
|
|
|
|
|
|
|
i2c_master_stop(cmd);
|
2021-02-12 09:39:58 +00:00
|
|
|
|
2021-01-22 16:12:30 +00:00
|
|
|
/* Send queued commands */
|
|
|
|
err = i2c_master_cmd_begin(DISP_I2C_PORT, cmd, 10 / portTICK_PERIOD_MS);
|
2021-01-17 06:10:12 +00:00
|
|
|
i2c_cmd_link_delete(cmd);
|
|
|
|
|
2021-01-22 16:12:30 +00:00
|
|
|
return ESP_OK == err ? 0 : 1;
|
2021-01-17 06:10:12 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 16:54:17 +00:00
|
|
|
static uint8_t send_pixels(lv_disp_drv_t *disp_drv, void *color_buffer, size_t buffer_len)
|
2021-01-17 06:10:12 +00:00
|
|
|
{
|
|
|
|
(void) disp_drv;
|
2021-01-22 16:12:30 +00:00
|
|
|
esp_err_t err;
|
2021-02-12 09:39:58 +00:00
|
|
|
|
2021-01-17 06:10:12 +00:00
|
|
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
|
|
|
i2c_master_start(cmd);
|
|
|
|
i2c_master_write_byte(cmd, (OLED_I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true);
|
|
|
|
|
|
|
|
i2c_master_write_byte(cmd, OLED_CONTROL_BYTE_DATA_STREAM, true);
|
|
|
|
i2c_master_write(cmd, (uint8_t *) color_buffer, buffer_len, true);
|
|
|
|
i2c_master_stop(cmd);
|
2021-02-12 09:39:58 +00:00
|
|
|
|
2021-01-22 16:12:30 +00:00
|
|
|
/* Send queued commands */
|
|
|
|
err = i2c_master_cmd_begin(DISP_I2C_PORT, cmd, 10 / portTICK_PERIOD_MS);
|
2021-01-17 06:10:12 +00:00
|
|
|
i2c_cmd_link_delete(cmd);
|
|
|
|
|
2021-01-22 16:12:30 +00:00
|
|
|
return ESP_OK == err ? 0 : 1;
|
2021-01-17 06:10:12 +00:00
|
|
|
}
|