Compare commits

...

5 Commits

Author SHA1 Message Date
imi415 cdd4834ea6
Major structure change. 2022-01-23 16:02:31 +08:00
imi415 3d497800bb
Added JLX LCD driver. 2022-01-22 20:14:30 +08:00
imi415 3f1f782faa
ELW1501AA: Added partial mode. 2022-01-21 23:31:21 +08:00
imi415 4ab907c903
Added clang-format. 2022-01-21 22:44:31 +08:00
imi415 e4d597c5bf
Added ELW1501AA OLED driver. 2022-01-21 22:43:33 +08:00
17 changed files with 495 additions and 9 deletions

11
.clang-format Normal file
View File

@ -0,0 +1,11 @@
BasedOnStyle: Google
IndentWidth: 4
AlignConsecutiveMacros: AcrossEmptyLines
AlignConsecutiveDeclarations: true
AlignConsecutiveAssignments: AcrossEmptyLinesAndComments
BreakBeforeBraces: Custom
BraceWrapping:
AfterEnum: false
AfterStruct: false
SplitEmptyFunction: false
ColumnLimit: 120

View File

@ -4,13 +4,25 @@ project(epd-spi)
set(EPD_SOURCES
"src/epd_common.c"
"src/epd_panel_wfh0420cz35.c"
"src/epd_panel_gdew042t2.c"
"src/driver/oled_ssd1327.c"
"src/panel/epd_wfh0420cz35.c"
"src/panel/epd_gdew042t2.c"
"src/panel/lcd_jlx256128g_920.c"
"src/panel/oled_elw1501aa.c"
"src/panel/oled_zjy150s700.c"
)
set(EPD_INCLUDES
"include"
)
set(EPD_PRIVATE_INCLUDES
"include/epd-spi"
"include/private"
)
add_library(${PROJECT_NAME} ${EPD_SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC ${EPD_INCLUDES})
target_include_directories(${PROJECT_NAME}
PUBLIC ${EPD_INCLUDES}
PRIVATE ${EPD_PRIVATE_INCLUDES}
)

View File

@ -0,0 +1,6 @@
#ifndef DRIVER_SSD1327_H
#define DRIVER_SSD1327_H
#endif

View File

@ -1,7 +1,7 @@
#ifndef EPD_GDEW042T2_H
#define EPD_GDEW042T2_H
#include "epd_common.h"
#include "epd-spi/epd_common.h"
/**
* @brief This display module has UC8176 controller IC,

View File

@ -1,7 +1,7 @@
#ifndef EPD_WFH0420CZ35_H
#define EPD_WFH0420CZ35_h
#include "epd_common.h"
#include "epd-spi/epd_common.h"
/**
* @brief This display module has UC8176 controller IC, with BWR LUT stored in OTP.

View File

@ -0,0 +1,34 @@
#ifndef LCD_PANEL_JLX256128G_920_H
#define LCD_PANEL_JLX256128G_920_H
#include "epd-spi/epd_common.h"
/**
* @brief JLX256128G-920 LCD
* Drive/Controller: Sitronix ST75256
* Resolution: 256x128@2bpp(4 grayscale)
* Link: https://item.taobao.com/item.htm?id=595485128701
* Datasheet: https://pan.baidu.com/s/1XVAYUiLdCP7uPqK4mrkUxQ Pass: e87n
*/
typedef enum {
LCD_JLX256128G_MODE_BW = 0x10U,
LCD_JLX256128G_MODE_GS = 0x11U,
} lcd_jlx256128g_mode_t;
typedef struct {
lcd_jlx256128g_mode_t mode;
uint16_t contrast;
} lcd_jlx256128g_config_t;
typedef struct {
epd_cb_t cb;
void *user_data;
lcd_jlx256128g_config_t config;
} lcd_jlx256128g_t;
epd_ret_t lcd_jlx256128g_init(lcd_jlx256128g_t *lcd);
epd_ret_t lcd_jlx256128g_power(lcd_jlx256128g_t *lcd, uint8_t on);
epd_ret_t lcd_jlx256128g_upload(lcd_jlx256128g_t *lcd, epd_coord_t *coord, uint8_t *data);
#endif

View File

@ -0,0 +1,23 @@
#ifndef OLED_PANEL_ELW1501AA_H
#define OLED_PANEL_ELW1501AA_H
#include "epd-spi/epd_common.h"
/**
* @brief Futaba ELW1501AA(R) PMOLED
* Driver/Controller: Solomon Systech SSD1327
* Resolution: 128x128@4bpp(16 grayscale)
* Link: https://www.futaba.com/oled/
* Datasheet: https://www.futaba.com/wp-content/uploads/2021/08/ELW1501AA.pdf
*/
typedef struct {
epd_cb_t cb;
void *user_data;
} oled_elw1501aa_t;
epd_ret_t oled_elw1501aa_init(oled_elw1501aa_t *elw);
epd_ret_t oled_elw1501aa_upload(oled_elw1501aa_t *oled, epd_coord_t *coord, uint8_t *data);
epd_ret_t oled_elw1501aa_power(oled_elw1501aa_t *elw, uint8_t on);
#endif

View File

@ -0,0 +1,23 @@
#ifndef OLED_PANEL_ZJY150S700_H
#define OLED_PANEL_ZJY150S700_H
#include "epd-spi/epd_common.h"
/**
* @brief Futaba zjy150s700(R) PMOLED
* Driver/Controller: Solomon Systech SSD1327
* Resolution: 128x128@4bpp(16 grayscale)
* Link: https://www.futaba.com/oled/
* Datasheet: https://www.futaba.com/wp-content/uploads/2021/08/zjy150s700.pdf
*/
typedef struct {
epd_cb_t cb;
void *user_data;
} oled_zjy150s700_t;
epd_ret_t oled_zjy150s700_init(oled_zjy150s700_t *elw);
epd_ret_t oled_zjy150s700_upload(oled_zjy150s700_t *oled, epd_coord_t *coord, uint8_t *data);
epd_ret_t oled_zjy150s700_power(oled_zjy150s700_t *elw, uint8_t on);
#endif

View File

@ -1,7 +1,7 @@
#ifndef EPD_PRIVATE_H
#define EPD_PRIVATE_H
#include "epd_common.h"
#include "epd-spi/epd_common.h"
#define EPD_ASSERT(x) if(!(x)) for(;;) { /* ABORT. */}
#define EPD_ERROR_CHECK(x) if(x != EPD_OK) return EPD_FAIL

View File

@ -0,0 +1,2 @@
#include "driver/oled_ssd1327.h"

View File

@ -1,6 +1,6 @@
#include "epd_panel_gdew042t2.h"
#include "panel/epd_gdew042t2.h"
#include "epd_panel_gdew042t2_lut.h"
#include "panel/epd_gdew042t2_lut.h"
#include "epd_private.h"
#define DTM1 0x10

View File

@ -1,4 +1,4 @@
#include "epd_panel_wfh0420cz35.h"
#include "panel/epd_wfh0420cz35.h"
#include "epd_private.h"

View File

@ -0,0 +1,132 @@
#include "panel/lcd_jlx256128g_920.h"
#include "epd_private.h"
#define PANEL_X_OFFSET 0
#define PANEL_Y_OFFSET 32
static uint8_t lcd_jlx256128g_init_sequence[] = {
0x00, 0x30, // Extension command EXT[1:0] = 0,0
0x00, 0x94, // Set power save mode, SLP = 0
0x00, 0x31, // Extension command EXT[1:0] = 0,1
0x01, 0xD7, 0x9F, // Set auto-read instruction, XARD = 1
0x03, 0x32, 0x00, 0x01, 0x03, // 32 - Set analog circuit, BE[1:0] = 0,1; BS[2:0] = 0,1,1
0x10, 0x20, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D,
0x10, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F, // 31 - Set grey scale level
0x04, 0xF0, 0x0D, 0x0D, 0x0D, 0x0D, // Frame rate, 0x0C - 69.0Hz
0x00, 0x30, // Extension command EXT[1:0] = 0,0
0x02, 0x75, 0x00, 0x14, // 7 - Set page address, YS = 0x00, YE = 0x14
0x02, 0x15, 0x00, 0xFF, // 8 - Set column address, XS = 0x00, XE = 0xFF
0x01, 0xBC, 0x00, // 9 - Data scan direction, MV = 0, MX = 0, MY = 0
0x03, 0xCA, 0x00, 0x9F, 0x20, // 5 - Display control, CLD = 0, DT = 0x7F, LF[4:0] = 16, FL = 0
0x01, 0xF0, 0x10, // 28 - Display mode: DM = 0(Mono)
0x02, 0x81, 0x36, 0x04, // 21 - Set VOP, VOP = 0x136
0x01, 0x20, 0x0B, // 20 - Power control, VB = 1, VF = 1, VR = 1
};
static epd_ret_t lcd_jlx256128g_reset(lcd_jlx256128g_t *lcd) {
EPD_ERROR_CHECK(lcd->cb.reset_cb(lcd->user_data));
return EPD_OK;
}
static epd_ret_t lcd_jlx256128g_window(lcd_jlx256128g_t *lcd, epd_coord_t *coord) {
uint8_t pixel_per_byte = 4;
if (lcd->config.mode == LCD_JLX256128G_MODE_BW) {
pixel_per_byte = 8;
}
if (coord->y_start % pixel_per_byte != 0 || coord->y_end % pixel_per_byte != pixel_per_byte - 1) {
return EPD_FAIL;
}
if (coord->x_end > 255 || coord->x_start > coord->x_end) {
return EPD_FAIL;
}
if (coord->y_end > 127 || coord->y_start > coord->y_end) {
return EPD_FAIL;
}
uint8_t page_start = (coord->y_start + PANEL_Y_OFFSET) / pixel_per_byte;
uint8_t page_end = (coord->y_end + PANEL_Y_OFFSET) / pixel_per_byte;
uint8_t col_cmd[] = {0x15, coord->x_start + PANEL_X_OFFSET, coord->x_end + PANEL_X_OFFSET};
uint8_t page_cmd[] = {0x75, page_start, page_end};
EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, col_cmd, 0x03));
EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, page_cmd, 0x03));
return EPD_OK;
}
static epd_ret_t lcd_jlx256128g_config(lcd_jlx256128g_t *lcd) {
uint8_t contrast_command[] = {0x81, lcd->config.contrast & 0x3F, (lcd->config.contrast >> 6U) & 0x07};
uint8_t mode_command[] = {0xF0, lcd->config.mode};
EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, contrast_command, 0x03));
EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, mode_command, 0x02));
epd_coord_t coord = {
.x_start = 0,
.x_end = 255,
.y_start = 0,
.y_end = 127,
};
EPD_ERROR_CHECK(lcd_jlx256128g_window(lcd, &coord));
return EPD_OK;
}
static inline uint32_t lcd_jlx256128g_data_size(lcd_jlx256128g_t *lcd, epd_coord_t *coord) {
uint8_t pix_per_byte = 8;
if (lcd->config.mode == LCD_JLX256128G_MODE_GS) {
pix_per_byte = 4;
}
return (coord->y_end - coord->y_start + 1) * (coord->x_end - coord->x_start + 1) / pix_per_byte;
}
epd_ret_t lcd_jlx256128g_init(lcd_jlx256128g_t *lcd) {
EPD_ERROR_CHECK(lcd_jlx256128g_reset(lcd));
EPD_ERROR_CHECK(epd_common_execute_sequence(&lcd->cb, lcd->user_data, lcd_jlx256128g_init_sequence,
sizeof(lcd_jlx256128g_init_sequence)));
EPD_ERROR_CHECK(lcd_jlx256128g_config(lcd));
EPD_ERROR_CHECK(lcd_jlx256128g_power(lcd, 1));
return EPD_OK;
}
epd_ret_t lcd_jlx256128g_power(lcd_jlx256128g_t *lcd, uint8_t on) {
uint8_t tx_buf[] = {0xAF};
if (!on) tx_buf[0] = 0xAE;
EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x01));
return EPD_OK;
}
epd_ret_t lcd_jlx256128g_upload(lcd_jlx256128g_t *lcd, epd_coord_t *coord, uint8_t *data) {
uint32_t data_size = (lcd->config.mode == LCD_JLX256128G_MODE_GS) ? 8192 : 4096;
if (coord != NULL) {
EPD_ERROR_CHECK(lcd_jlx256128g_window(lcd, coord));
data_size = lcd_jlx256128g_data_size(lcd, coord);
}
uint8_t dtm_cmd[] = {0x5C};
EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, dtm_cmd, 0x01));
EPD_ERROR_CHECK(lcd->cb.write_data_cb(lcd->user_data, data, data_size));
if (coord != NULL) {
epd_coord_t new_coord = {
.x_start = 0,
.x_end = 255,
.y_start = 0,
.y_end = 127,
};
EPD_ERROR_CHECK(lcd_jlx256128g_window(lcd, &new_coord));
}
return EPD_OK;
}

120
src/panel/oled_elw1501aa.c Normal file
View File

@ -0,0 +1,120 @@
#include "panel/oled_elw1501aa.h"
#include "epd_private.h"
static uint8_t elw1501aa_init_sequence[] = {
0x01, 0x81, 0x9C, // Set contrast control
0x01, 0xA0, 0x53, // Set segment re-map
0x01, 0xA1, 0x00, // Set display start line
0x01, 0xA2, 0x00, // Set display offset
0x00, 0xA4, // Set normal display mode
0x01, 0xA8, 0x7F, // Set MUX ratio
0x01, 0xAB, 0x01, // Set enable internal VDD regulator mode
0x01, 0xB1, 0x31, // Set phase length of phase 1, phase 2
0x01, 0xB3, 0xF1, // Set ratio of dividing frequency & oscillation frequency
0x00, 0xB9, // Set gray scale table
0x01, 0xBC, 0x07, // Set pre-charge voltage
0x01, 0xBE, 0x07, // Set voltage VCOMH
0x02, 0x15, 0x00, 0x3F, // Set column address
0x02, 0x75, 0x00, 0x7F, // Set row address
};
static epd_ret_t oled_elw1501aa_reset(oled_elw1501aa_t *oled) {
if (oled->cb.reset_cb != NULL) {
return oled->cb.reset_cb(oled->user_data);
}
return EPD_OK;
}
static epd_ret_t oled_elw1501aa_window(oled_elw1501aa_t *oled, epd_coord_t *coord) {
if (coord->x_start % 2 != 0 || coord->x_end % 2 == 0) {
return EPD_FAIL;
}
if (coord->x_end > 127 || coord->x_start > coord->x_end) {
return EPD_FAIL;
}
if (coord->y_end > 127 || coord->y_start > coord->y_end) {
return EPD_FAIL;
}
uint8_t col_start = coord->x_start / 2;
uint8_t col_end = coord->x_end / 2;
uint8_t col_cmd_buf[3] = {0x15, col_start, col_end};
uint8_t row_cmd_buf[3] = {0x75, coord->y_start, coord->y_end};
EPD_ERROR_CHECK(oled->cb.write_command_cb(oled->user_data, col_cmd_buf, 3));
EPD_ERROR_CHECK(oled->cb.write_command_cb(oled->user_data, row_cmd_buf, 3));
return EPD_OK;
}
static inline uint32_t oled_elw1501aa_data_size(epd_coord_t *coord) {
return (coord->x_end - coord->x_start + 1) * (coord->y_end - coord->y_start + 1) / 2;
}
/**
* @brief Initialize OLED panel
* As OLEDs and LCDs need to be driven constantly, also with
* less overhead and higher data rate, initialization sequence
* are moved to this seperate function. Call this function before
* sending framebuffer data.
* @param oled pointer to oled_elw1501aa_t
* @return epd_ret_t EPD_OK for success, EPD_FAIL for error.
*/
epd_ret_t oled_elw1501aa_init(oled_elw1501aa_t *oled) {
EPD_ERROR_CHECK(oled_elw1501aa_reset(oled));
EPD_ERROR_CHECK(epd_common_execute_sequence(&oled->cb, oled->user_data, elw1501aa_init_sequence,
sizeof(elw1501aa_init_sequence)));
EPD_ERROR_CHECK(oled_elw1501aa_power(oled, 1));
return EPD_OK;
}
/**
* @brief Upload frame buffer to screen.
*
* @param oled pointer to oled_elw1501aa_t
* @param coord pointer to epd_coord_t, can be NULL to upload full frame.
* @param data array pointer to new pixel data.
* @return epd_ret_t EPD_OK for success, EPD_FAIL for error.
*/
epd_ret_t oled_elw1501aa_upload(oled_elw1501aa_t *oled, epd_coord_t *coord, uint8_t *data) {
uint32_t data_size = 8192;
if (coord != NULL) {
EPD_ERROR_CHECK(oled_elw1501aa_window(oled, coord));
data_size = oled_elw1501aa_data_size(coord);
}
EPD_ERROR_CHECK(oled->cb.write_data_cb(oled->user_data, data, data_size));
if (coord != NULL) {
epd_coord_t new_coord = {
.x_start = 0,
.x_end = 127,
.y_start = 0,
.y_end = 127,
};
EPD_ERROR_CHECK(oled_elw1501aa_window(oled, &new_coord));
}
return EPD_OK;
}
/**
* @brief Control the OLED drivers.
* LCDs and OLEDs needs to be constant driven in order to
* display a stable image, turn the drivers and source/gate scan on or off.
*
* @param oled pointer to oled_elw1501aa_t
* @param on 0:off, anything else: on
* @return epd_ret_t EPD_OK for success, EPD_FAIL for error.
*/
epd_ret_t oled_elw1501aa_power(oled_elw1501aa_t *oled, uint8_t on) {
uint8_t cmd[2] = {0xAE, 0x00};
if (on) cmd[0] = 0xAF;
EPD_ERROR_CHECK(oled->cb.write_command_cb(oled->user_data, cmd, 1));
return EPD_OK;
}

123
src/panel/oled_zjy150s700.c Normal file
View File

@ -0,0 +1,123 @@
#include "panel/oled_zjy150s700.h"
#include "epd_private.h"
static uint8_t zjy150s700_init_sequence[] = {
0x01, 0x81, 0x77, // Set contrast control
0x01, 0xA0, 0x53, // Set segment re-map
0x01, 0xA1, 0x00, // Set display start line
0x01, 0xA2, 0x00, // Set display offset
0x00, 0xA4, // Set normal display mode
0x01, 0xA8, 0x7F, // Set MUX ratio
0x01, 0xAB, 0x01, // Set enable internal VDD regulator mode
0x01, 0xB1, 0x31, // Set phase length of phase 1, phase 2
0x01, 0xB3, 0xB1, // Set ratio of dividing frequency & oscillation frequency
0x01, 0xB5, 0x03, // Set GPIO pin output high (External boost regulator EN)
0x01, 0xB6, 0x0D, // Set pre-charge period
0x00, 0xB9, // Set gray scale table
0x01, 0xBC, 0x07, // Set pre-charge voltage
0x01, 0xBE, 0x07, // Set voltage VCOMH
0x01, 0xD5, 0x02, // Set second precharge enable
0x02, 0x15, 0x00, 0x3F, // Set column address
0x02, 0x75, 0x00, 0x7F, // Set row address
};
static epd_ret_t oled_zjy150s700_reset(oled_zjy150s700_t *oled) {
if (oled->cb.reset_cb != NULL) {
return oled->cb.reset_cb(oled->user_data);
}
return EPD_OK;
}
static epd_ret_t oled_zjy150s700_window(oled_zjy150s700_t *oled, epd_coord_t *coord) {
if (coord->x_start % 2 != 0 || coord->x_end % 2 == 0) {
return EPD_FAIL;
}
if (coord->x_end > 127 || coord->x_start > coord->x_end) {
return EPD_FAIL;
}
if (coord->y_end > 127 || coord->y_start > coord->y_end) {
return EPD_FAIL;
}
uint8_t col_start = coord->x_start / 2;
uint8_t col_end = coord->x_end / 2;
uint8_t col_cmd_buf[3] = {0x15, col_start, col_end};
uint8_t row_cmd_buf[3] = {0x75, coord->y_start, coord->y_end};
EPD_ERROR_CHECK(oled->cb.write_command_cb(oled->user_data, col_cmd_buf, 3));
EPD_ERROR_CHECK(oled->cb.write_command_cb(oled->user_data, row_cmd_buf, 3));
return EPD_OK;
}
static inline uint32_t oled_zjy150s700_data_size(epd_coord_t *coord) {
return (coord->x_end - coord->x_start + 1) * (coord->y_end - coord->y_start + 1) / 2;
}
/**
* @brief Initialize OLED panel
* As OLEDs and LCDs need to be driven constantly, also with
* less overhead and higher data rate, initialization sequence
* are moved to this seperate function. Call this function before
* sending framebuffer data.
* @param oled pointer to oled_zjy150s700_t
* @return epd_ret_t EPD_OK for success, EPD_FAIL for error.
*/
epd_ret_t oled_zjy150s700_init(oled_zjy150s700_t *oled) {
EPD_ERROR_CHECK(oled_zjy150s700_reset(oled));
EPD_ERROR_CHECK(epd_common_execute_sequence(&oled->cb, oled->user_data, zjy150s700_init_sequence,
sizeof(zjy150s700_init_sequence)));
EPD_ERROR_CHECK(oled_zjy150s700_power(oled, 1));
return EPD_OK;
}
/**
* @brief Upload frame buffer to screen.
*
* @param oled pointer to oled_zjy150s700_t
* @param coord pointer to epd_coord_t, can be NULL to upload full frame.
* @param data array pointer to new pixel data.
* @return epd_ret_t EPD_OK for success, EPD_FAIL for error.
*/
epd_ret_t oled_zjy150s700_upload(oled_zjy150s700_t *oled, epd_coord_t *coord, uint8_t *data) {
uint32_t data_size = 8192;
if (coord != NULL) {
EPD_ERROR_CHECK(oled_zjy150s700_window(oled, coord));
data_size = oled_zjy150s700_data_size(coord);
}
EPD_ERROR_CHECK(oled->cb.write_data_cb(oled->user_data, data, data_size));
if (coord != NULL) {
epd_coord_t new_coord = {
.x_start = 0,
.x_end = 127,
.y_start = 0,
.y_end = 127,
};
EPD_ERROR_CHECK(oled_zjy150s700_window(oled, &new_coord));
}
return EPD_OK;
}
/**
* @brief Control the OLED drivers.
* LCDs and OLEDs needs to be constant driven in order to
* display a stable image, turn the drivers and source/gate scan on or off.
*
* @param oled pointer to oled_zjy150s700_t
* @param on 0:off, anything else: on
* @return epd_ret_t EPD_OK for success, EPD_FAIL for error.
*/
epd_ret_t oled_zjy150s700_power(oled_zjy150s700_t *oled, uint8_t on) {
uint8_t cmd[2] = {0xAE, 0x00};
if (on) cmd[0] = 0xAF;
EPD_ERROR_CHECK(oled->cb.write_command_cb(oled->user_data, cmd, 1));
return EPD_OK;
}