diff --git a/CMakeLists.txt b/CMakeLists.txt index ed2deca..59a6fe5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,10 @@ set(CMAKE_C_STANDARD 11) set(SOURCES main.c st75256_impl.c + st75256_lvgl_impl.c user_tasks.c tasks/task_lvgl.c + tasks/task_hello.c lib/st75256_lcd/st75256_lcd.c lib/LittleVGL/src/lv_themes/lv_theme.c lib/LittleVGL/src/lv_themes/lv_theme_empty.c diff --git a/include/lv_conf.h b/include/lv_conf.h index bb07c89..875166a 100644 --- a/include/lv_conf.h +++ b/include/lv_conf.h @@ -53,7 +53,7 @@ /* Dot Per Inch: used to initialize default sizes. * E.g. a button with width = LV_DPI / 2 -> half inch wide * (Not so important, you can adjust it to modify default sizes and spaces)*/ -#define LV_DPI 130 /*[px]*/ +#define LV_DPI 108 /*[px]*/ /* The the real width of the display changes some default values: * default object sizes, layout of examples, etc. @@ -80,7 +80,7 @@ typedef int16_t lv_coord_t; #define LV_MEM_CUSTOM 1 #if LV_MEM_CUSTOM == 0 /* Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/ -# define LV_MEM_SIZE (32U * 1024U) +# define LV_MEM_SIZE (128U * 1024U) /* Compiler prefix for a big array declaration */ # define LV_MEM_ATTR @@ -215,7 +215,7 @@ typedef void * lv_fs_drv_user_data_t; #endif /*1: Add a `user_data` to drivers and objects*/ -#define LV_USE_USER_DATA 0 +#define LV_USE_USER_DATA 1 /*1: Show CPU usage and FPS count in the right bottom corner*/ #define LV_USE_PERF_MONITOR 0 @@ -408,8 +408,8 @@ typedef void * lv_indev_drv_user_data_t; /*Type of user data in the i /*Pixel perfect monospace font * http://pelulamu.net/unscii/ */ -#define LV_FONT_UNSCII_8 0 -#define LV_FONT_UNSCII_16 0 +#define LV_FONT_UNSCII_8 1 +#define LV_FONT_UNSCII_16 1 /* Optionally declare your custom fonts here. * You can use these fonts as default font too @@ -473,14 +473,14 @@ typedef void * lv_font_user_data_t; #define LV_USE_THEME_MONO 1 #define LV_THEME_DEFAULT_INCLUDE /*Include a header for the init. function*/ -#define LV_THEME_DEFAULT_INIT lv_theme_material_init +#define LV_THEME_DEFAULT_INIT lv_theme_mono_init #define LV_THEME_DEFAULT_COLOR_PRIMARY lv_color_hex(0x01a2b1) #define LV_THEME_DEFAULT_COLOR_SECONDARY lv_color_hex(0x44d1b6) -#define LV_THEME_DEFAULT_FLAG LV_THEME_MATERIAL_FLAG_LIGHT -#define LV_THEME_DEFAULT_FONT_SMALL &lv_font_montserrat_14 -#define LV_THEME_DEFAULT_FONT_NORMAL &lv_font_montserrat_14 -#define LV_THEME_DEFAULT_FONT_SUBTITLE &lv_font_montserrat_14 -#define LV_THEME_DEFAULT_FONT_TITLE &lv_font_montserrat_14 +#define LV_THEME_DEFAULT_FLAG 0 +#define LV_THEME_DEFAULT_FONT_SMALL &lv_font_unscii_16 +#define LV_THEME_DEFAULT_FONT_NORMAL &lv_font_unscii_16 +#define LV_THEME_DEFAULT_FONT_SUBTITLE &lv_font_unscii_16 +#define LV_THEME_DEFAULT_FONT_TITLE &lv_font_unscii_16 /*================= * Text settings diff --git a/include/st75256_lvgl_impl.h b/include/st75256_lvgl_impl.h new file mode 100644 index 0000000..56a6161 --- /dev/null +++ b/include/st75256_lvgl_impl.h @@ -0,0 +1,12 @@ +#ifndef __ST75256_LVGL_IMPL_H +#define __ST75256_LVGL_IMPL_H + +#include "st75256_lcd.h" + +#include "lvgl.h" + +void _st75256_lv_impl_rounder(lv_disp_drv_t *disp_drv, lv_area_t *area); +void _st75256_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 _st75256_lv_impl_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p); + +#endif \ No newline at end of file diff --git a/include/user_tasks.h b/include/user_tasks.h index 95008a8..bb12777 100644 --- a/include/user_tasks.h +++ b/include/user_tasks.h @@ -14,4 +14,9 @@ void *lvgl_tick_thread(void *arguments); extern pthread_t lvgl_task_thread_handle; void *lvgl_task_thread(void *arguments); +int hello_task_init(void); +int hello_task_deinit(void); +extern pthread_t hello_thread_handle; +void *hello_thread(void *arguments); + #endif \ No newline at end of file diff --git a/lib/st75256_lcd/st75256_lcd.c b/lib/st75256_lcd/st75256_lcd.c index 86ae3cf..c05d8ad 100644 --- a/lib/st75256_lcd/st75256_lcd.c +++ b/lib/st75256_lcd/st75256_lcd.c @@ -1,20 +1,21 @@ #include "st75256_lcd.h" uint8_t jlx_25664_init_sequence[] = { - 0x00, 0x30, - 0x00, 0x94, - 0x00, 0x31, - 0x01, 0xD7, 0x9F, - 0x03, 0x32, 0x00, 0x01, 0x03, - 0x10, 0x20, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x10, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F, - 0x00, 0x30, - 0x02, 0x75, 0x00, 0x14, - 0x02, 0x15, 0x00, 0xFF, - 0x02, 0xBC, 0x00, 0xA6, - 0x03, 0xCA, 0x00, 0x9F, 0x20, - 0x01, 0xF0, 0x10, - 0x02, 0x81, 0x36, 0x04, - 0x01, 0x20, 0x0B + 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, 0x00, 0x00, 0x00, 0x0B, 0x0B, 0x0B, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, // 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(not divide), DT = 0x9F(128), LF[4:0] = 1,0,0,0,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 }; st75256_ret_t _st75256_hardware_reset(st75256_t *lcd) { @@ -34,8 +35,21 @@ st75256_ret_t _st75256_init_seq(st75256_t *lcd) { } st75256_ret_t _st75256_display_on(st75256_t *lcd) { - uint8_t cmd = 0xAF; + uint8_t cmd = 0x30; ST75256_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, &cmd, 0x01)); + + cmd = 0xAF; + ST75256_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, &cmd, 0x01)); + + return ST75256_OK; +} + +st75256_ret_t _st75256_cursor(st75256_t *lcd, uint8_t column_start, uint8_t column_end, uint8_t page_start, uint8_t page_end) { + uint8_t cmd[] = { 0x30, 0x15, column_start, column_end, 0x75, page_start, page_end }; + ST75256_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, cmd, 0x01)); // Extension command EXT[1:0] = 0,0 + ST75256_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, &cmd[1], 0x03)); + ST75256_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, &cmd[4], 0x03)); + return ST75256_OK; } st75256_ret_t st75256_init(st75256_t *lcd) { @@ -67,6 +81,37 @@ st75256_ret_t st75256_set_mode(st75256_t *lcd, st75256_mode_t mode) { return ST75256_OK; } -st75256_ret_t st75256_cursor(st75256_t *lcd, uint8_t x_start, uint8_t x_end, uint8_t y_start, uint8_t y_end) { - +st75256_ret_t st75256_upload_data(st75256_t *lcd, uint8_t *data, uint8_t x_start, uint8_t x_end, uint8_t y_start, uint8_t y_end) { + uint8_t cmd = 0x30; + ST75256_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, &cmd, 0x01)); // Ext. Command 1 + + uint32_t bytes_to_transfer = 0x00; + uint8_t page_offset = (lcd->mode == ST75256_GREY ? 8 : 4); + uint8_t page_start = page_offset; + uint8_t page_end = page_offset; + + if(lcd->mode == ST75256_GREY) { + if(y_start % 4 != 0 || (y_end + 1) % 4 != 0) { + return ST75256_ERROR; + } + bytes_to_transfer = (x_end - x_start + 1) * (y_end - y_start + 1) / 4; + page_start += y_start / 4; + page_end += y_end / 4; + } + else { + if(y_start % 8 != 0 || (y_end + 1) % 8 != 0) { + return ST75256_ERROR; + } + bytes_to_transfer = (x_end - x_start + 1) * (y_end - y_start + 1) / 8; + page_start += y_start / 8; + page_end += y_end / 8; + } + + ST75256_ERROR_CHECK(_st75256_cursor(lcd, x_start, x_end, page_start, page_end)); + + cmd = 0x5C; + ST75256_ERROR_CHECK(lcd->cb.write_cmd_cb(lcd->user_data, &cmd, 0x01)); // Write data + ST75256_ERROR_CHECK(lcd->cb.write_data_cb(lcd->user_data, data, (uint16_t)bytes_to_transfer)); + + return ST75256_OK; } \ No newline at end of file diff --git a/lib/st75256_lcd/st75256_lcd.h b/lib/st75256_lcd/st75256_lcd.h index 9d07eed..00c6bef 100644 --- a/lib/st75256_lcd/st75256_lcd.h +++ b/lib/st75256_lcd/st75256_lcd.h @@ -33,5 +33,6 @@ typedef struct { st75256_ret_t st75256_init(st75256_t *lcd); st75256_ret_t st75256_set_contrast(st75256_t *lcd, uint16_t contrast); st75256_ret_t st75256_set_mode(st75256_t *lcd, st75256_mode_t mode); +st75256_ret_t st75256_upload_data(st75256_t *lcd, uint8_t *data, uint8_t x_start, uint8_t x_end, uint8_t y_start, uint8_t y_end); #endif \ No newline at end of file diff --git a/lvgl_impl.c b/lvgl_impl.c deleted file mode 100644 index e69de29..0000000 diff --git a/main.c b/main.c index 9db7e84..9e4c0a6 100644 --- a/main.c +++ b/main.c @@ -1,27 +1,9 @@ #include +#include -#include "st75256_lcd.h" -#include "st75256_impl.h" #include "user_tasks.h" -st75256_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 - } -}; - int main() { - g_lcd.user_data = _lcd_impl_init(); - if(g_lcd.user_data == NULL) return -1; - - st75256_init(&g_lcd); - st75256_set_contrast(&g_lcd, 305); - st75256_set_mode(&g_lcd, ST75256_GREY); - user_tasks_init(); user_tasks_loop(); user_tasks_deinit(); diff --git a/st75256_impl.c b/st75256_impl.c index 3d93e2f..0500660 100644 --- a/st75256_impl.c +++ b/st75256_impl.c @@ -1,7 +1,9 @@ #include "st75256_impl.h" +#define SPIDEV_MAX_LEN 4096 + _st75256_impl_t *_lcd_impl_init(void) { - int spi_fd = open(CONFIG_SPIDEV_FILENAME, O_RDWR); + 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. @@ -109,8 +111,20 @@ st75256_ret_t _lcd_impl_write_data(void *handle, uint8_t *data, uint16_t len) { .bits_per_word = 8 }; - ret = ioctl(impl->spi_fd, SPI_IOC_MESSAGE(1), &data_tr); - if(ret < len) return ST75256_ERROR; + 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 ST75256_ERROR; + } return ST75256_OK; } diff --git a/st75256_lvgl_impl.c b/st75256_lvgl_impl.c new file mode 100644 index 0000000..66ea27a --- /dev/null +++ b/st75256_lvgl_impl.c @@ -0,0 +1,48 @@ +#include "st75256_lvgl_impl.h" + +void _st75256_lv_impl_rounder(lv_disp_drv_t *disp_drv, lv_area_t *area) { + st75256_t *lcd = disp_drv->user_data; + if(lcd->mode == ST75256_MONO) { + area->y1 = (area->y1 / 8) * 8; + area->y2 = (area->y2 / 8) * 8 + 7; + } + else { + area->y1 = (area->y1 / 4) * 4; + area->y2 = (area->y2 / 4) * 4 + 3; + } +} + +void _st75256_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) { + st75256_t *lcd = disp_drv->user_data; + + uint16_t byte_index; + uint8_t bit_index; + + if(lcd->mode == ST75256_MONO) { + byte_index = x + (y / 8) * buf_w; + bit_index = 7 - (y & 7); + if(color.full) { + buf[byte_index] |= 1U << bit_index; + } + else { + buf[byte_index] &= ~(1U << bit_index); + } + } + else { + byte_index = x + (y / 4) * buf_w; + bit_index = 6 - ((y & 3) * 2); + + buf[byte_index] &= ~(3U << bit_index); + + if(color.full) { + buf[byte_index] |= (3U << bit_index); + } + } +} + +void _st75256_lv_impl_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { + st75256_t *lcd = disp_drv->user_data; + st75256_upload_data(lcd, (uint8_t *)color_p, area->x1, area->x2, area->y1, area->y2); + lv_disp_flush_ready(disp_drv); + printf("FLUSH %d\n", area->y1); +} \ No newline at end of file diff --git a/tasks/task_hello.c b/tasks/task_hello.c new file mode 100644 index 0000000..5b61012 --- /dev/null +++ b/tasks/task_hello.c @@ -0,0 +1,33 @@ +#include +#include + +#include "lvgl.h" + +#include "user_tasks.h" + +extern uint8_t g_running; +extern pthread_mutex_t g_lvgl_mutex; + +int hello_task_init(void) { + int ret = pthread_create(&hello_thread_handle, NULL, hello_thread, NULL); + ret = pthread_setname_np(hello_thread_handle, "HELLO_THR"); +} + +int hello_task_deinit(void) { + +} + +pthread_t hello_thread_handle; +void *hello_thread(void *arguments) { + pthread_mutex_lock(&g_lvgl_mutex); + lv_obj_t * label2 = lv_label_create(lv_scr_act(), NULL); + lv_label_set_long_mode(label2, LV_LABEL_LONG_SROLL_CIRC); /*Circular scroll*/ + lv_label_set_anim_speed(label2, 3); + lv_obj_set_width(label2, 256); + lv_label_set_text(label2, "It is a circularly scrolling text. "); + lv_obj_align(label2, NULL, LV_ALIGN_IN_TOP_MID, 0, 30); + pthread_mutex_unlock(&g_lvgl_mutex); + while(g_running) { + sleep(1); + } +} \ No newline at end of file diff --git a/tasks/task_lvgl.c b/tasks/task_lvgl.c index 9cb6bcf..cfa28e3 100644 --- a/tasks/task_lvgl.c +++ b/tasks/task_lvgl.c @@ -4,15 +4,54 @@ #include "lvgl.h" +#include "st75256_impl.h" +#include "st75256_lvgl_impl.h" + #include "user_tasks.h" extern uint8_t g_running; pthread_mutex_t g_lvgl_mutex = PTHREAD_MUTEX_INITIALIZER; +st75256_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 + } +}; + +#define LCD_BUF_SIZE (256 * 10) + +lv_disp_buf_t g_lcd_disp_buf; +lv_color_t g_lcd_buf[LCD_BUF_SIZE]; + int lvgl_task_init(void) { int ret; + + g_lcd.user_data = _lcd_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); + lv_init(); + + lv_disp_buf_init(&g_lcd_disp_buf, g_lcd_buf, NULL, LCD_BUF_SIZE); + + 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.user_data = &g_lcd; + + lv_disp_drv_register(&disp_drv); + ret = pthread_create(&lvgl_tick_thread_handle, NULL, lvgl_tick_thread, NULL); if(ret) return ret; ret = pthread_create(&lvgl_task_thread_handle, NULL, lvgl_task_thread, NULL); @@ -29,7 +68,7 @@ int lvgl_task_deinit(void) { pthread_t lvgl_tick_thread_handle; void *lvgl_tick_thread(void *arguments) { while(g_running) { - usleep(30 * 1000); + usleep(29 * 1000); pthread_mutex_lock(&g_lvgl_mutex); lv_tick_inc(30); pthread_mutex_unlock(&g_lvgl_mutex); @@ -39,7 +78,7 @@ void *lvgl_tick_thread(void *arguments) { pthread_t lvgl_task_thread_handle; void *lvgl_task_thread(void *arguments) { while(g_running) { - usleep(10 * 1000); + usleep(17 * 1000); pthread_mutex_lock(&g_lvgl_mutex); lv_task_handler(); pthread_mutex_unlock(&g_lvgl_mutex); diff --git a/user_tasks.c b/user_tasks.c index 414654d..dbb4d4e 100644 --- a/user_tasks.c +++ b/user_tasks.c @@ -19,6 +19,7 @@ void user_tasks_init(void) { } lvgl_task_init(); + hello_task_init(); } void user_tasks_loop(void) { @@ -28,5 +29,6 @@ void user_tasks_loop(void) { } void user_tasks_deinit(void) { + hello_task_deinit(); lvgl_task_deinit(); } \ No newline at end of file