Added ST7302 Reflective LCD driver.

This commit is contained in:
imi415 2021-03-20 20:05:37 +09:00
parent de00659a94
commit a16a03dd1c
14 changed files with 380 additions and 40 deletions

View File

@ -7,7 +7,9 @@ set(CMAKE_C_STANDARD 11)
set(SOURCES
main.c
st75256_impl.c
st7302_impl.c
st75256_lvgl_impl.c
st7302_lvgl_impl.c
user_tasks.c
fonts/jf_dot_jiskan24_2000.c
fonts/jf_dot_jiskan16.c
@ -15,6 +17,7 @@ set(SOURCES
tasks/task_lvgl.c
tasks/task_hello.c
lib/st75256_lcd/st75256_lcd.c
lib/st7302_lcd/st7302_lcd.c
lib/LittleVGL/src/lv_themes/lv_theme.c
lib/LittleVGL/src/lv_themes/lv_theme_empty.c
lib/LittleVGL/src/lv_themes/lv_theme_material.c
@ -131,6 +134,7 @@ set(SOURCES
include_directories(
include
lib/st75256_lcd
lib/st7302_lcd
lib/LittleVGL
)

View File

@ -2,7 +2,7 @@
#define __CONFIG_H
#ifndef CONFIG_SPIDEV_FILENAME
#define CONFIG_SPIDEV_FILENAME "/dev/spidev0.0"
#define CONFIG_SPIDEV_FILENAME "/dev/spidev0.1"
#endif
#ifndef CONFIG_GPIO_CONSUMER
@ -14,11 +14,19 @@
#endif
#ifndef CONFIG_RES_PIN
#define CONFIG_RES_PIN 27
#define CONFIG_RES_PIN 23
#endif
#ifndef CONFIG_DC_PIN
#define CONFIG_DC_PIN 25
#define CONFIG_DC_PIN 24
#endif
#ifndef CONFIG_DISPLAY_OFFSET_X
#define CONFIG_DISPLAY_OFFSET_X 5
#endif
#ifndef CONFIG_DISPLAY_OFFSET_Y
#define CONFIG_DISPLAY_OFFSET_Y 0
#endif
#endif

View File

@ -20,8 +20,8 @@
*====================*/
/* Maximal horizontal and vertical resolution to support by the library.*/
#define LV_HOR_RES_MAX (256)
#define LV_VER_RES_MAX (128)
#define LV_HOR_RES_MAX (250)
#define LV_VER_RES_MAX (122)
/* Color depth:
* - 1: 1 byte per pixel
@ -311,7 +311,7 @@ typedef void * lv_indev_drv_user_data_t; /*Type of user data in the i
*===============*/
/*1: Enable the log module*/
#define LV_USE_LOG 0
#define LV_USE_LOG 1
#if LV_USE_LOG
/* How important log should be added:
* LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
@ -324,7 +324,7 @@ typedef void * lv_indev_drv_user_data_t; /*Type of user data in the i
/* 1: Print the log with 'printf';
* 0: user need to register a callback with `lv_log_register_print_cb`*/
# define LV_LOG_PRINTF 0
# define LV_LOG_PRINTF 1
#endif /*LV_USE_LOG*/
/*=================

32
include/st7302_impl.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef __ST7302_IMPL_H
#define __ST7302_IMPL_H
// SPIDEV
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
// LIBGPIOD
#include <gpiod.h>
// USER
#include "st7302_lcd.h"
#include "config.h"
typedef struct {
int spi_fd;
struct gpiod_chip *gpio_chip;
struct gpiod_line *gpio_line_res;
struct gpiod_line *gpio_line_dc;
} _st7302_impl_t;
_st7302_impl_t *_st7302_impl_init(void);
int _st7302_impl_deinit(_st7302_impl_t *handle);
st7302_ret_t _st7302_impl_write_cmd(void *handle, uint8_t *cmd, uint8_t len);
st7302_ret_t _st7302_impl_write_data(void *handle, uint8_t *data, uint16_t len);
st7302_ret_t _st7302_impl_reset(void *handle);
st7302_ret_t _st7302_impl_delay(void *handle, uint32_t usec);
#endif

View File

@ -0,0 +1,12 @@
#ifndef __ST7302_LVGL_IMPL_H
#define __ST7302_LVGL_IMPL_H
#include "st7302_lcd.h"
#include "lvgl.h"
void _st7302_lv_impl_rounder(lv_disp_drv_t *disp_drv, lv_area_t *area);
void _st7302_lv_impl_set_px(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa);
void _st7302_lv_impl_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
#endif

View File

@ -1,5 +1,5 @@
#ifndef __lcd_impl_IMPL_H
#define __lcd_impl_IMPL_H
#ifndef __ST75256_IMPL_H
#define __ST75256_IMPL_H
// SPIDEV
#include <fcntl.h>
@ -22,11 +22,11 @@ typedef struct {
struct gpiod_line *gpio_line_dc;
} _st75256_impl_t;
_st75256_impl_t *_lcd_impl_init(void);
int _lcd_impl_deinit(_st75256_impl_t *handle);
st75256_ret_t _lcd_impl_write_cmd(void *handle, uint8_t *cmd, uint8_t len);
st75256_ret_t _lcd_impl_write_data(void *handle, uint8_t *data, uint16_t len);
st75256_ret_t _lcd_impl_reset(void *handle);
st75256_ret_t _lcd_impl_delay(void *handle, uint32_t usec);
_st75256_impl_t *_st75256_impl_init(void);
int _st75256_impl_deinit(_st75256_impl_t *handle);
st75256_ret_t _st75256_impl_write_cmd(void *handle, uint8_t *cmd, uint8_t len);
st75256_ret_t _st75256_impl_write_data(void *handle, uint8_t *data, uint16_t len);
st75256_ret_t _st75256_impl_reset(void *handle);
st75256_ret_t _st75256_impl_delay(void *handle, uint32_t usec);
#endif

0
lib/CMakeLists.txt Normal file
View File

View File

@ -0,0 +1,65 @@
#include "st7302_lcd.h"
uint8_t st7302_gd_unknown_init_sequence[] = {
0x01, 0xEB, 0x02, // Enable OTP
0x01, 0xD7, 0x68, // OTP Load Control
0x01, 0xD1, 0x01, // Auto Power Control
0x01, 0xC0, 0x40, // Gate Voltage setting
0x06, 0xC1, 0x22, 0x28, 0x28, 0x22, 0x14, 0x00, // VSH Setting
0x04, 0xC2, 0x00, 0x00, 0x00, 0x00, // VSL Setting
0x01, 0xCB, 0x0E, // VCOMH Setting
0x01, 0xB3, 0x94, // VCOM EQ Enable
0x0A, 0xB4, 0xE5, 0x66, 0x85, 0xFF, 0xFF, 0x52, 0x85, 0xFF, 0xFF, 0x52, // Gate EQ
0x02, 0xC7, 0xA6, 0xE9, // OSC Setting
0x01, 0xB0, 0x3F, // Duty Setting
0x02, 0xB2, 0x01, 0x05, // Frame Rate Setting
0x01, 0x36, 0x20, // Memory Access Mode
0x01, 0x3A, 0x01, // Data Format
0x01, 0xB9, 0x23, // Source Setting
0x01, 0xB8, 0x09, // Panel Setting
0x02, 0x2A, 0x19, 0x23, // Column Address Setting
0x02, 0x2B, 0x00, 0x7C, // Row Address Setting
};
st7302_ret_t _st7302_init_seq(st7302_t *lcd) {
uint16_t i = 0;
while(i < sizeof(ST7302_PANEL_SELECTION)) {
ST7302_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, &ST7302_PANEL_SELECTION[i + 1], ST7302_PANEL_SELECTION[i] + 1));
i += ST7302_PANEL_SELECTION[i] + 2;
}
return ST7302_OK;
}
st7302_ret_t st7302_init(st7302_t *lcd) {
ST7302_ERROR_CHECK(_st7302_init_seq(lcd));
uint8_t command[3] = {0xB2, 0x01, 0x05};
command[0] = 0x11;
ST7302_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, command, 0x01)); // Sleep Out
ST7302_ERROR_CHECK(lcd->cb.delay_cb(lcd->user_data, 100 * 1000));
command[0] = 0x29;
ST7302_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, command, 0x01)); // Display ON
command[0] = 0x38;
ST7302_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, command, 0x01)); // LPM ON
return ST7302_OK;
}
st7302_ret_t st7302_upload(st7302_t *lcd, uint8_t col_start, uint8_t col_end, uint8_t row_start, uint8_t row_end, uint8_t *data) {
uint8_t command[3] = { 0x2A, col_start, col_end };
ST7302_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, command, 0x03)); // Column address set
command[0] = 0x2B;
command[1] = row_start;
command[2] = row_end;
ST7302_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, command, 0x03)); // Row address set
command[0] = 0x2C; // Write DATA
ST7302_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, command, 0x01));
uint16_t data_len = (col_end - col_start + 1) * (row_end - row_start + 1) * 3;
ST7302_ERROR_CHECK(lcd->cb.write_data_cb(lcd->user_data, data, data_len));
}

View File

@ -0,0 +1,30 @@
#ifndef __ST7302_LCD_H
#define __ST7302_LCD_H
#include <stdint.h>
#define ST7302_PANEL_SELECTION st7302_gd_unknown_init_sequence
typedef enum {
ST7302_OK,
ST7302_ERROR
} st7302_ret_t;
typedef struct {
st7302_ret_t (*reset_cb)(void *handle);
st7302_ret_t (*delay_cb)(void *handle, uint32_t usec);
st7302_ret_t (*write_cmd_cb)(void *handle, uint8_t *cmd, uint8_t len);
st7302_ret_t (*write_data_cb)(void *handle, uint8_t *data, uint16_t len);
} st7302_cb_t;
typedef struct {
void *user_data;
st7302_cb_t cb;
} st7302_t;
#define ST7302_ERROR_CHECK(x) if(x != ST7302_OK) return ST7302_ERROR
st7302_ret_t st7302_init(st7302_t *lcd);
st7302_ret_t st7302_upload(st7302_t *lcd, uint8_t col_start, uint8_t col_end, uint8_t row_start, uint8_t row_end, uint8_t *data);
#endif

153
st7302_impl.c Normal file
View File

@ -0,0 +1,153 @@
#include "st7302_impl.h"
#define SPIDEV_MAX_LEN 4096
_st7302_impl_t *_st7302_impl_init(void) {
int spi_fd = open(CONFIG_SPIDEV_FILENAME, O_RDWR | O_SYNC);
if(spi_fd < 0) return NULL;
uint32_t spi_mode = 0; // Nothing to be set in default mode.
int ret = ioctl(spi_fd, SPI_IOC_WR_MODE32, &spi_mode);
if(ret == -1) goto spi_err_out;
ret = ioctl(spi_fd, SPI_IOC_RD_MODE32, &spi_mode);
if(ret == -1) goto spi_err_out;
uint32_t spi_word_len = 8;
ret= ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_word_len);
if(ret == -1) goto spi_err_out;
ret = ioctl(spi_fd, SPI_IOC_RD_BITS_PER_WORD, &spi_word_len);
if(ret == -1) goto spi_err_out;
uint32_t spi_speed = 16000000; // 16MHz
ret = ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed);
if(ret == -1) goto spi_err_out;
ret = ioctl(spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, &spi_speed);
if(ret == -1) goto spi_err_out;
_st7302_impl_t *impl = malloc(sizeof(_st7302_impl_t));
if(impl == NULL) goto spi_err_out;
impl->spi_fd = spi_fd;
impl->gpio_chip = gpiod_chip_open(CONFIG_GPIO_CHIP);
if(impl->gpio_chip == NULL) goto impl_err_out;
impl->gpio_line_res = gpiod_chip_get_line(impl->gpio_chip, CONFIG_RES_PIN);
if(impl->gpio_line_res == NULL) goto gpiod_err_out;
impl->gpio_line_dc = gpiod_chip_get_line(impl->gpio_chip, CONFIG_DC_PIN);
if(impl->gpio_line_dc == NULL) goto gpiod_err_out;
ret = gpiod_line_request_output(impl->gpio_line_dc, CONFIG_GPIO_CONSUMER, 1);
if(ret != 0) goto gpiod_err_out;
ret = gpiod_line_request_output(impl->gpio_line_res, CONFIG_GPIO_CONSUMER, 1);
if(ret != 0) goto gpiod_err_out;
return impl;
gpiod_err_out:
gpiod_chip_close(impl->gpio_chip);
impl_err_out:
free(impl);
spi_err_out:
close(spi_fd);
return NULL;
}
int _st7302_impl_deinit(_st7302_impl_t *handle) {
close(handle->spi_fd);
gpiod_chip_close(handle->gpio_chip);
free(handle);
return 0;
}
st7302_ret_t _st7302_impl_write_cmd(void *handle, uint8_t *cmd, uint8_t len) {
_st7302_impl_t *impl = handle;
int ret = gpiod_line_set_value(impl->gpio_line_dc, 0);
if(ret) return ST7302_ERROR;
struct spi_ioc_transfer cmd_tr = {
.tx_buf = (unsigned long)cmd,
.rx_buf = (unsigned long)NULL,
.len = 1,
.bits_per_word = 8
};
ret = ioctl(impl->spi_fd, SPI_IOC_MESSAGE(1), &cmd_tr);
if(ret < 1) return ST7302_ERROR;
if(len > 1) {
ret = gpiod_line_set_value(impl->gpio_line_dc, 1); // Set DC pin
if(ret) return ST7302_ERROR;
cmd_tr.tx_buf = (unsigned long)&cmd[1]; //2nd transfer
cmd_tr.len = len - 1;
ret = ioctl(impl->spi_fd, SPI_IOC_MESSAGE(1), &cmd_tr);
if(ret < len - 1) return ST7302_ERROR;
}
return ST7302_OK;
}
st7302_ret_t _st7302_impl_write_data(void *handle, uint8_t *data, uint16_t len) {
_st7302_impl_t *impl = handle;
int ret = gpiod_line_set_value(impl->gpio_line_dc, 1);
struct spi_ioc_transfer data_tr = {
.tx_buf = (unsigned long)data,
.rx_buf = (unsigned long)NULL,
.len = len,
.bits_per_word = 8
};
uint32_t transfer_times = (len / SPIDEV_MAX_LEN) + ((len % SPIDEV_MAX_LEN != 0) ? 1 : 0); // Linux SPI can transfer some bytes each time.
for(uint32_t i = 0; i < transfer_times; i++) {
if(i == transfer_times - 1) {
data_tr.len = len - SPIDEV_MAX_LEN * i;
}
else {
data_tr.len = SPIDEV_MAX_LEN;
}
data_tr.tx_buf = (unsigned long)(&data[SPIDEV_MAX_LEN * i]);
ret = ioctl(impl->spi_fd, SPI_IOC_MESSAGE(1), &data_tr);
if(ret < data_tr.len) return ST7302_ERROR;
}
return ST7302_OK;
}
st7302_ret_t _st7302_impl_reset(void *handle) {
_st7302_impl_t *impl = handle;
usleep(10 * 1000);
int ret = gpiod_line_set_value(impl->gpio_line_res, 0);
if(ret) return ST7302_ERROR;
usleep(10 * 1000);
ret = gpiod_line_set_value(impl->gpio_line_res, 1);
if(ret) return ST7302_ERROR;
usleep(10 * 1000);
return ST7302_OK;
}
st7302_ret_t _st7302_impl_delay(void *handle, uint32_t usec) {
usleep(usec);
return ST7302_OK;
}

38
st7302_lvgl_impl.c Normal file
View File

@ -0,0 +1,38 @@
#include "st7302_lvgl_impl.h"
#include "config.h"
void _st7302_lv_impl_rounder(lv_disp_drv_t *disp_drv, lv_area_t *area) {
st7302_t *lcd = disp_drv->user_data;
area->x1 = (area->x1 / 2) * 2;
area->x2 = (area->x2 / 2) * 2 + 1;
area->y1 = (area->y1 / 12) * 12;
area->y2 = (area->y2 / 12) * 12 + 11;
}
void _st7302_lv_impl_set_px(lv_disp_drv_t *disp_drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa) {
st7302_t *lcd = disp_drv->user_data;
uint16_t byte_index = (buf_w / 2 * 3) * (y / 12) + (x / 2) * 3 + ((y % 12) / 4);
uint8_t bit_index = (3 - (y % 4)) * 2 + (x % 2);
if(color.full) {
buf[byte_index] |= 1U << bit_index;
}
else {
buf[byte_index] &= ~(1U << bit_index);
}
}
void _st7302_lv_impl_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
st7302_t *lcd = disp_drv->user_data;
uint8_t column_start = 20 + CONFIG_DISPLAY_OFFSET_X + area->y1 / 12;
uint8_t column_end = 20 + CONFIG_DISPLAY_OFFSET_X + area->y2 / 12;
uint8_t row_start = CONFIG_DISPLAY_OFFSET_Y + area->x1 / 2;
uint8_t row_end = CONFIG_DISPLAY_OFFSET_Y + area->x2 / 2;
st7302_upload(lcd, column_start, column_end, row_start, row_end, (uint8_t *)color_p);
lv_disp_flush_ready(disp_drv);
}

View File

@ -2,7 +2,7 @@
#define SPIDEV_MAX_LEN 4096
_st75256_impl_t *_lcd_impl_init(void) {
_st75256_impl_t *_st75256_impl_init(void) {
int spi_fd = open(CONFIG_SPIDEV_FILENAME, O_RDWR | O_SYNC);
if(spi_fd < 0) return NULL;
@ -63,14 +63,14 @@ spi_err_out:
return NULL;
}
int _lcd_impl_deinit(_st75256_impl_t *handle) {
int _st75256_impl_deinit(_st75256_impl_t *handle) {
close(handle->spi_fd);
gpiod_chip_close(handle->gpio_chip);
free(handle);
return 0;
}
st75256_ret_t _lcd_impl_write_cmd(void *handle, uint8_t *cmd, uint8_t len) {
st75256_ret_t _st75256_impl_write_cmd(void *handle, uint8_t *cmd, uint8_t len) {
_st75256_impl_t *impl = handle;
int ret = gpiod_line_set_value(impl->gpio_line_dc, 0);
@ -99,7 +99,7 @@ st75256_ret_t _lcd_impl_write_cmd(void *handle, uint8_t *cmd, uint8_t len) {
return ST75256_OK;
}
st75256_ret_t _lcd_impl_write_data(void *handle, uint8_t *data, uint16_t len) {
st75256_ret_t _st75256_impl_write_data(void *handle, uint8_t *data, uint16_t len) {
_st75256_impl_t *impl = handle;
int ret = gpiod_line_set_value(impl->gpio_line_dc, 1);
@ -129,7 +129,7 @@ st75256_ret_t _lcd_impl_write_data(void *handle, uint8_t *data, uint16_t len) {
return ST75256_OK;
}
st75256_ret_t _lcd_impl_reset(void *handle) {
st75256_ret_t _st75256_impl_reset(void *handle) {
_st75256_impl_t *impl = handle;
usleep(10 * 1000);
@ -147,7 +147,7 @@ st75256_ret_t _lcd_impl_reset(void *handle) {
return ST75256_OK;
}
st75256_ret_t _lcd_impl_delay(void *handle, uint32_t usec) {
st75256_ret_t _st75256_impl_delay(void *handle, uint32_t usec) {
usleep(usec);
return ST75256_OK;
}

View File

@ -23,13 +23,13 @@ void *hello_thread(void *arguments) {
lv_style_t hello_label_style;
lv_style_init(&hello_label_style);
lv_style_set_text_font(&hello_label_style, LV_STATE_DEFAULT, &jf_dot_jiskan16);
lv_style_set_text_font(&hello_label_style, LV_STATE_DEFAULT, &jf_dot_jiskan24_2000);
lv_obj_t * hello_label = lv_label_create(lv_scr_act(), NULL);
lv_label_set_long_mode(hello_label, LV_LABEL_LONG_SROLL_CIRC); /*Circular scroll*/
lv_label_set_anim_speed(hello_label, 4);
lv_obj_set_width(hello_label, 256);
lv_label_set_text(hello_label, "JFドットjiskan16 - 電子情報技術産業協会 / 安岡孝一氏 / 今村俊幸氏 / 花高信哉");
lv_label_set_anim_speed(hello_label, 1);
lv_obj_set_width(hello_label, 250);
lv_label_set_text(hello_label, "JFドットjiskan24-2000 電子情報技術産業協会 / 矢木達也");
lv_obj_add_style(hello_label, LV_LABEL_PART_MAIN, &hello_label_style);
lv_obj_align(hello_label, NULL, LV_ALIGN_CENTER, 0, 0);

View File

@ -4,8 +4,8 @@
#include "lvgl.h"
#include "st75256_impl.h"
#include "st75256_lvgl_impl.h"
#include "st7302_impl.h"
#include "st7302_lvgl_impl.h"
#include "user_tasks.h"
@ -13,17 +13,17 @@ extern uint8_t g_running;
pthread_mutex_t g_lvgl_mutex = PTHREAD_MUTEX_INITIALIZER;
st75256_t g_lcd = {
st7302_t g_lcd = {
.user_data = NULL,
.cb = {
.write_cmd_cb = _lcd_impl_write_cmd,
.write_data_cb = _lcd_impl_write_data,
.reset_cb = _lcd_impl_reset,
.delay_cb = _lcd_impl_delay
.write_cmd_cb = _st7302_impl_write_cmd,
.write_data_cb = _st7302_impl_write_data,
.reset_cb = _st7302_impl_reset,
.delay_cb = _st7302_impl_delay
}
};
#define LCD_BUF_SIZE (256 * 10)
#define LCD_BUF_SIZE (250 * 24)
lv_disp_buf_t g_lcd_disp_buf;
lv_color_t g_lcd_buf[LCD_BUF_SIZE];
@ -31,12 +31,10 @@ lv_color_t g_lcd_buf[LCD_BUF_SIZE];
int lvgl_task_init(void) {
int ret;
g_lcd.user_data = _lcd_impl_init();
g_lcd.user_data = _st7302_impl_init();
if(g_lcd.user_data == NULL) return -1;
st75256_init(&g_lcd);
st75256_set_contrast(&g_lcd, 310);
st75256_set_mode(&g_lcd, ST75256_GREY);
st7302_init(&g_lcd);
lv_init();
@ -45,9 +43,9 @@ int lvgl_task_init(void) {
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.buffer = &g_lcd_disp_buf;
disp_drv.set_px_cb = _st75256_lv_impl_set_px;
disp_drv.flush_cb = _st75256_lv_impl_flush;
disp_drv.rounder_cb = _st75256_lv_impl_rounder;
disp_drv.set_px_cb = _st7302_lv_impl_set_px;
disp_drv.flush_cb = _st7302_lv_impl_flush;
disp_drv.rounder_cb = _st7302_lv_impl_rounder;
disp_drv.user_data = &g_lcd;
lv_disp_drv_register(&disp_drv);