269 lines
8.2 KiB
C
269 lines
8.2 KiB
C
#pragma once
|
|
|
|
#include <string.h>
|
|
|
|
#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);
|
|
} |