#pragma once #include #include "driver/gpio.h" #include "driver/i2c.h" #include "esp_err.h" #include "esp_log.h" #include "freertos/task.h" #include "sdkconfig.h" // generated by "make menuconfig" #define CH1116_TAG "CH1116" #define SDA_PIN GPIO_NUM_4 #define SCL_PIN GPIO_NUM_5 #define CH1116_WIDTH 128 #define CH1116_HEIGHT 64 #define CH1116_ADDRESS 0x3C // 011110+SA0 - 0x3C or 0x3D // CH1116 Control #define CH1116_CONTROL_BYTE_COMMAND_SINGLE 0x80 #define CH1116_CONTROL_BYTE_COMMAND_STREAM 0x00 #define CH1116_CONTROL_BYTE_DATA_SINGLE 0xC0 #define CH1116_CONTROL_BYTE_DATA_STREAM 0x40 // CH1116 Config Commands #define CH1116_DISPLAY_OFF 0xAE // #13 #define CH1116_LOWER_COLUMN_ADDRESS 0x02 // #1 0x02-0x0F #define CH1116_HIGHER_COLUMN_ADDRESS 0x10 // #2 0x10-0x1F #define CH1116_DISPLAY_START_LINE 0x40 // #3 #define CH1116_PAGE_ADDRESS 0xB0 // #18 0xB0-0xB7 #define CH1116_CONTRACT_CONTROL 0x81 // #10 #define CH1116_CONTRACT_CONTROL_128 0xCF // #10 #define CH1116_SEGMENT_REMAP 0xA1 // #12 ADC=1 (right) #define CH1116_NORMAL_DISPLAY 0xA6 // #14 #define CH1116_MULTIPLEX_RATIO 0xA8 // #15 #define CH1116_MULTIPLEX_RATIO_1_64 0x3F // VCC Generated by Internal DC/DC Circuit #define CH1116_CHARGE_PUMP_ENABLE 0xAD // #16 #define CH1116_CHARGE_PUMP_ENABLE_INTERNAL_VCC 0x8B // #16 #define CH1116_CHARGE_PUMP_VOLTAGE_9V 0x33 // #8 #define CH1116_COM_SCAN_DIRECTION 0xC8 // #19 #define CH1116_DISPLAY_OFFSET 0xD3 // #20 #define CH1116_DISPLAY_OFFSET_0 0x00 // #20 #define CH1116_SET_OSC_DIVISION 0xD5 // #21 #define CH1116_SET_OSC_DIVISION_1 0x80 // #21 #define CH1116_SET_PRE_CHARGE_PERIOD 0xD9 // #22 #define CH1116_SET_PRE_CHARGE_PERIOD_1 0x1F // #22 #define CH1116_SET_COM_PINS 0xDA // #23 #define CH1116_SET_COM_PINS_1 0x12 // #23 #define CH1116_SET_VCOMH 0xDB // #24 #define CH1116_SET_VCOMH_1 0x40 // #24 #define CH1116_DISPLAY_ON 0xAF // #13 void ch1116_init() { esp_err_t err; i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (CH1116_ADDRESS << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_COMMAND_STREAM, true); i2c_master_write_byte(cmd, CH1116_DISPLAY_OFF, true); i2c_master_write_byte(cmd, CH1116_LOWER_COLUMN_ADDRESS, true); i2c_master_write_byte(cmd, CH1116_HIGHER_COLUMN_ADDRESS, true); i2c_master_write_byte(cmd, CH1116_DISPLAY_START_LINE, true); i2c_master_write_byte(cmd, CH1116_PAGE_ADDRESS, true); i2c_master_write_byte(cmd, CH1116_CONTRACT_CONTROL, true); i2c_master_write_byte(cmd, CH1116_CONTRACT_CONTROL_128, true); i2c_master_write_byte(cmd, CH1116_SEGMENT_REMAP, true); i2c_master_write_byte(cmd, CH1116_NORMAL_DISPLAY, true); i2c_master_write_byte(cmd, CH1116_MULTIPLEX_RATIO, true); i2c_master_write_byte(cmd, CH1116_MULTIPLEX_RATIO_1_64, true); i2c_master_write_byte(cmd, CH1116_CHARGE_PUMP_ENABLE, true); i2c_master_write_byte(cmd, CH1116_CHARGE_PUMP_ENABLE_INTERNAL_VCC, true); i2c_master_write_byte(cmd, CH1116_CHARGE_PUMP_VOLTAGE_9V, true); i2c_master_write_byte(cmd, CH1116_COM_SCAN_DIRECTION, true); i2c_master_write_byte(cmd, CH1116_DISPLAY_OFFSET, true); i2c_master_write_byte(cmd, CH1116_DISPLAY_OFFSET_0, true); i2c_master_write_byte(cmd, CH1116_SET_OSC_DIVISION, true); i2c_master_write_byte(cmd, CH1116_SET_OSC_DIVISION_1, true); i2c_master_write_byte(cmd, CH1116_SET_PRE_CHARGE_PERIOD, true); i2c_master_write_byte(cmd, CH1116_SET_PRE_CHARGE_PERIOD_1, true); i2c_master_write_byte(cmd, CH1116_SET_COM_PINS, true); i2c_master_write_byte(cmd, CH1116_SET_COM_PINS_1, true); i2c_master_write_byte(cmd, CH1116_SET_VCOMH, true); i2c_master_write_byte(cmd, CH1116_SET_VCOMH_1, true); i2c_master_write_byte(cmd, CH1116_DISPLAY_ON, true); i2c_master_stop(cmd); err = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS); if (err == ESP_OK) { ESP_LOGI(CH1116_TAG, "OLED configured successfully"); } else { ESP_LOGE(CH1116_TAG, "OLED configuration failed. code: %s", esp_err_to_name(err)); } i2c_cmd_link_delete(cmd); } void task_ch1116_display_pattern(void *ignore) { i2c_cmd_handle_t cmd; for (uint8_t i = 0; i < 8; i++) { cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (CH1116_ADDRESS << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_COMMAND_SINGLE, true); i2c_master_write_byte(cmd, 0xB0 | i, true); i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_DATA_STREAM, true); for (uint8_t j = 0; j < 132; j++) { i2c_master_write_byte(cmd, 0xFF >> (j % 8), true); } i2c_master_stop(cmd); i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); } } void task_ch1116_display_clear(void *ignore) { i2c_cmd_handle_t cmd; uint8_t zero[132]; memset(zero, 0, 132); for (uint8_t i = 0; i < 8; i++) { cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (CH1116_ADDRESS << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_COMMAND_SINGLE, true); i2c_master_write_byte(cmd, 0xB0 | i, true); i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_DATA_STREAM, true); i2c_master_write(cmd, zero, 132, true); i2c_master_stop(cmd); i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); } cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_COMMAND_STREAM, true); i2c_master_write_byte(cmd, 0x00, true); // reset column i2c_master_write_byte(cmd, 0x10, true); i2c_master_write_byte(cmd, 0xB0, true); // reset page i2c_master_stop(cmd); i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); } static esp_err_t ch1116_draw_bitmap(int x_start, int y_start, int x_end, int y_end, const void *color_data) { assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position"); // one page contains 8 rows (COMs) uint8_t page_start = y_start / 8; uint8_t page_end = y_end / 8; // define an area of frame memory where MCU can access esp_err_t err; i2c_cmd_handle_t cmd; uint8_t *color_data_ptr = (uint8_t *)color_data; uint16_t page_data_size = (x_end - x_start + 1); // ESP_LOGI(CH1116_TAG, "y_start: %d, y_end: %d, page_start: %d, page_end: // %d", // y_start, y_end, page_start, page_end); // ESP_LOGI(CH1116_TAG, "x_start: %d, x_end: %d, page_data_size: %d", x_start, // x_end, page_data_size); for (int page = page_start; page <= page_end; page++) { cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (CH1116_ADDRESS << 1) | I2C_MASTER_WRITE, true); // set cursor position i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_COMMAND_STREAM, true); i2c_master_write_byte(cmd, CH1116_LOWER_COLUMN_ADDRESS | (x_start & 0x0f), true); i2c_master_write_byte(cmd, CH1116_HIGHER_COLUMN_ADDRESS | (x_start >> 4), true); i2c_master_write_byte(cmd, CH1116_PAGE_ADDRESS | (page & 0x0f), true); i2c_master_stop(cmd); err = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS); if (err != ESP_OK) { i2c_cmd_link_delete(cmd); return err; } i2c_cmd_link_delete(cmd); // write page data cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (CH1116_ADDRESS << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_DATA_STREAM, true); i2c_master_write(cmd, color_data_ptr, page_data_size, true); i2c_master_stop(cmd); err = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS); // ESP_LOG_BUFFER_HEXDUMP(CH1116_TAG, color_data_ptr, page_data_size, // ESP_LOG_INFO); color_data_ptr += page_data_size; if (err != ESP_OK) { i2c_cmd_link_delete(cmd); return err; } i2c_cmd_link_delete(cmd); } return ESP_OK; } void ch1116_main(void) { ch1116_init(); task_ch1116_display_pattern(NULL); // vTaskDelay(1000 / portTICK_PERIOD_MS); // task_ch1116_display_clear(NULL); }