From 30ece44ea4bedf7591dd4718d30b841f68e91869 Mon Sep 17 00:00:00 2001 From: Sheikah <> Date: Sun, 5 Feb 2023 00:52:46 +0800 Subject: [PATCH] WIP, ui_menu --- .vscode/settings.json | 5 +- Core/App/Graphic/bmfont.c | 12 +-- Core/App/Graphic/bmfont.h | 6 +- Core/App/Graphic/monoimg.c | 18 +++- Core/App/Graphic/monoimg.h | 4 +- Core/App/Graphic/ui.c | 11 ++- Core/App/Graphic/ui_menu.c | 175 ++++++++++++++++++++++++++++++++++ Core/App/Graphic/ui_menu.h | 5 + Core/App/Graphic/ui_utils.c | 60 ++++++++++++ Core/App/Graphic/ui_utils.h | 41 ++++++++ Core/App/Input/keypad.c | 7 ++ Core/App/Input/keypad.h | 3 +- Core/App/app.c | 48 +++++++++- Core/App/global.c | 2 +- Core/Hardware/ST7735/st7735.c | 99 ++++++++++--------- Core/Hardware/ST7735/st7735.h | 5 + Makefile | 9 +- cmd.jexe => flash.jexe | 0 flash.sh | 3 - reset.jexe | 7 ++ 20 files changed, 443 insertions(+), 77 deletions(-) create mode 100644 Core/App/Graphic/ui_menu.c create mode 100644 Core/App/Graphic/ui_menu.h create mode 100644 Core/App/Graphic/ui_utils.c create mode 100644 Core/App/Graphic/ui_utils.h rename cmd.jexe => flash.jexe (100%) delete mode 100755 flash.sh create mode 100644 reset.jexe diff --git a/.vscode/settings.json b/.vscode/settings.json index 798b888..4e01abc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,9 @@ "type_traits": "c", "gpio.h": "c", "stdint.h": "c", - "time.h": "c" + "time.h": "c", + "st7735.h": "c", + "stdbool.h": "c", + "bmfont.h": "c" } } \ No newline at end of file diff --git a/Core/App/Graphic/bmfont.c b/Core/App/Graphic/bmfont.c index 1a93e2a..93255fb 100644 --- a/Core/App/Graphic/bmfont.c +++ b/Core/App/Graphic/bmfont.c @@ -23,7 +23,7 @@ typedef struct { int16_t char_y; uint32_t count; uint32_t unicode; - uint8_t *utf8_text; + const uint8_t *utf8_text; uint8_t last_char_width; uint8_t is_looping; } bmf_LoopState; @@ -34,7 +34,7 @@ void place_next_char(bmf_BitmapFont *font, bmf_LoopState *state) { // get unicode char uint32_t b_off = state->byte_offset; uint32_t b_lim = state->byte_limit; - uint8_t *utf8_text = state->utf8_text; + const uint8_t *utf8_text = state->utf8_text; if (b_off >= b_lim) { state->is_looping = 0; state->unicode = bmf_ASCII_EOF; @@ -77,7 +77,7 @@ void place_next_char(bmf_BitmapFont *font, bmf_LoopState *state) { state->char_y += font->char_height; state->char_x = state->start_x; } - if (state->height_limit > 0 && (state->char_y + font->char_height - state->start_y > state->width_limit)) { + if (state->height_limit > 0 && (state->char_y + font->char_height - state->start_y > state->height_limit)) { if (unicode == bmf_ASCII_N) { state->byte_offset = b_off; state->unicode = unicode; @@ -94,7 +94,7 @@ void place_next_char(bmf_BitmapFont *font, bmf_LoopState *state) { return; } -uint16_t bmf_get_text_width(bmf_BitmapFont *font, uint8_t *text, uint32_t bytes_len) { +uint16_t bmf_get_text_width(bmf_BitmapFont *font, const uint8_t *text, uint32_t bytes_len) { uint16_t total_width = 0; uint32_t b_off = 0; while (b_off < bytes_len) { @@ -135,7 +135,7 @@ uint16_t bmf_get_text_width(bmf_BitmapFont *font, uint8_t *text, uint32_t bytes_ return total_width; } -uint32_t bmf_draw_text(bmf_BitmapFont *font, uint8_t *text, uint32_t bytes_len, uint16_t x, uint16_t y, uint16_t width_limit, uint16_t height_limit, uint16_t color) { +uint32_t bmf_draw_text(bmf_BitmapFont *font, const uint8_t *text, uint32_t bytes_len, uint16_t x, uint16_t y, uint16_t width_limit, uint16_t height_limit, uint16_t color) { bmf_LoopState state_obj = {bytes_len, 0, x, y, width_limit, height_limit, x, y, 0, 0, text, 0, 1}; bmf_LoopState *state = &state_obj; bmf_FunctionGetCharImage get_char_image = font->get_char_image; @@ -178,7 +178,7 @@ uint32_t bmf_draw_text(bmf_BitmapFont *font, uint8_t *text, uint32_t bytes_len, return state_obj.byte_offset; } -uint32_t bmf_get_text_offset(bmf_BitmapFont *font, uint8_t *text, uint32_t bytes_len, uint16_t width_limit, uint16_t height_limit) { +uint32_t bmf_get_text_offset(bmf_BitmapFont *font, const uint8_t *text, uint32_t bytes_len, uint16_t width_limit, uint16_t height_limit) { bmf_LoopState state_obj = {bytes_len, 0, 0, 0, width_limit, height_limit, 0, 0, 0, 0, text, 0, 1}; bmf_LoopState *state = &state_obj; while (state_obj.is_looping) { diff --git a/Core/App/Graphic/bmfont.h b/Core/App/Graphic/bmfont.h index 6304c28..f583550 100644 --- a/Core/App/Graphic/bmfont.h +++ b/Core/App/Graphic/bmfont.h @@ -14,6 +14,6 @@ typedef struct bmf_BitmapFont { const uint8_t *ascii_width; // size [94] } bmf_BitmapFont; -uint16_t bmf_get_text_width(bmf_BitmapFont *font, uint8_t *text, uint32_t bytes_len); -uint32_t bmf_draw_text(bmf_BitmapFont *font, uint8_t *text, uint32_t bytes_len, uint16_t x, uint16_t y, uint16_t width_limit, uint16_t height_limit, uint16_t color); -uint32_t bmf_get_text_offset(bmf_BitmapFont *font, uint8_t *text, uint32_t bytes_len, uint16_t width_limit, uint16_t height_limit); +uint16_t bmf_get_text_width(bmf_BitmapFont *font, const uint8_t *text, uint32_t bytes_len); +uint32_t bmf_draw_text(bmf_BitmapFont *font, const uint8_t *text, uint32_t bytes_len, uint16_t x, uint16_t y, uint16_t width_limit, uint16_t height_limit, uint16_t color); +uint32_t bmf_get_text_offset(bmf_BitmapFont *font, const uint8_t *text, uint32_t bytes_len, uint16_t width_limit, uint16_t height_limit); diff --git a/Core/App/Graphic/monoimg.c b/Core/App/Graphic/monoimg.c index c450c3e..33e935b 100644 --- a/Core/App/Graphic/monoimg.c +++ b/Core/App/Graphic/monoimg.c @@ -1,5 +1,6 @@ #include #include "monoimg.h" +#include "st7735.h" #define max(a,b) ((a) >= (b) ? (a) : (b)) #define min(a,b) ((a) <= (b) ? (a) : (b)) @@ -23,7 +24,7 @@ uint8_t mimg_get_pixel(const uint8_t *img, uint16_t x, uint16_t y) { return mimg_get_pixel_unsafe(img, (uint8_t)x, (uint8_t)y); } -void mimg_draw(mimg_FunctionSetPixel set_pixel, uint16_t x, uint16_t y, uint16_t color, const uint8_t *img, mimg_Area area) { +void mimg_draw(uint16_t x, uint16_t y, uint16_t color, const uint8_t *img, mimg_Area area) { uint8_t width_s1 = img[0]; // width - 1 uint8_t height_s1 = img[1]; // height - 1 uint8_t ix = min(area.x, width_s1); @@ -34,13 +35,13 @@ void mimg_draw(mimg_FunctionSetPixel set_pixel, uint16_t x, uint16_t y, uint16_t for (off_y = 0; off_y < ih; off_y ++) { for (off_x = 0; off_x < iw; off_x ++) { if (mimg_get_pixel_unsafe(img, ix + off_x, iy + off_y)) { - set_pixel(x + off_x, y + off_y, color); + ST7735_DrawPixel(x + off_x, y + off_y, color); } } } } -void mimg_draw_with_bg(mimg_FunctionSetPixel set_pixel, uint16_t x, uint16_t y, uint16_t color, uint16_t bg_color, const uint8_t *img, mimg_Area area) { +void mimg_draw_with_bg(uint16_t x, uint16_t y, uint16_t color, uint16_t bg_color, const uint8_t *img, mimg_Area area) { uint8_t width_s1 = img[0]; // width - 1 uint8_t height_s1 = img[1]; // height - 1 uint8_t ix = min(area.x, width_s1); @@ -48,15 +49,22 @@ void mimg_draw_with_bg(mimg_FunctionSetPixel set_pixel, uint16_t x, uint16_t y, uint8_t iw = min(area.w, width_s1 - ix + 1); uint8_t ih = min(area.h, height_s1 - iy + 1); uint8_t off_x, off_y; + ST7735_Select(); + ST7735_SetAddressWindow(x, y, x + iw - 1, y + ih - 1); + ST7735_WriteCommand(ST7735_RAMWR); + HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_SET); + uint8_t data_color[] = {color >> 8, color & 0xFF}; + uint8_t data_bg_color[] = {bg_color >> 8, bg_color & 0xFF}; for (off_y = 0; off_y < ih; off_y ++) { for (off_x = 0; off_x < iw; off_x ++) { if (mimg_get_pixel_unsafe(img, ix + off_x, iy + off_y)) { - set_pixel(x + off_x, y + off_y, color); + HAL_SPI_Transmit(&ST7735_SPI_PORT, data_color, sizeof(data_color), HAL_MAX_DELAY); } else { - set_pixel(x + off_x, y + off_y, bg_color); + HAL_SPI_Transmit(&ST7735_SPI_PORT, data_bg_color, sizeof(data_bg_color), HAL_MAX_DELAY); } } } + ST7735_Unselect(); } mimg_Area mimg_get_tile_area(const uint8_t *img, uint8_t cols, uint8_t rows, uint8_t index) { diff --git a/Core/App/Graphic/monoimg.h b/Core/App/Graphic/monoimg.h index 0754bc2..a38e8b9 100644 --- a/Core/App/Graphic/monoimg.h +++ b/Core/App/Graphic/monoimg.h @@ -14,7 +14,7 @@ typedef struct mimg_Area /** get a pixel from img, return 1 or 0 */ uint8_t mimg_get_pixel(const uint8_t *img, uint16_t x, uint16_t y); /** draw a part of img {ix, iy, iw, ih} , at (x, y) with color */ -void mimg_draw(mimg_FunctionSetPixel set_pixel, uint16_t x, uint16_t y, uint16_t color, const uint8_t *img, mimg_Area area); -void mimg_draw_with_bg(mimg_FunctionSetPixel set_pixel, uint16_t x, uint16_t y, uint16_t color, uint16_t bg_color, const uint8_t *img, mimg_Area area); +void mimg_draw(uint16_t x, uint16_t y, uint16_t color, const uint8_t *img, mimg_Area area); +void mimg_draw_with_bg(uint16_t x, uint16_t y, uint16_t color, uint16_t bg_color, const uint8_t *img, mimg_Area area); /** calc tile area */ mimg_Area mimg_get_tile_area(const uint8_t *img, uint8_t cols, uint8_t rows, uint8_t index); diff --git a/Core/App/Graphic/ui.c b/Core/App/Graphic/ui.c index 7ed34b5..2e82de0 100644 --- a/Core/App/Graphic/ui.c +++ b/Core/App/Graphic/ui.c @@ -8,6 +8,7 @@ #include "monoimg.h" #include "bmfont.h" #include "asciifont.h" +#include "ui_utils.h" void ui_text_number18x32(uint32_t num, uint16_t x, uint16_t y, uint16_t color) { uint32_t tmp = num; @@ -21,12 +22,12 @@ void ui_text_number18x32(uint32_t num, uint16_t x, uint16_t y, uint16_t color) { div = div / 10; if (div == 0) { mimg_Area area = mimg_get_tile_area(IMG_DIGI_18_32, 10, 1, 0); - mimg_draw(ST7735_DrawPixel, x, y, color, IMG_DIGI_18_32, area); + mimg_draw(x, y, color, IMG_DIGI_18_32, area); } else { while (div > 0) { tmp = (num / div) % 10; mimg_Area area = mimg_get_tile_area(IMG_DIGI_18_32, 10, 1, tmp); - mimg_draw(ST7735_DrawPixel, x, y, color, IMG_DIGI_18_32, area); + mimg_draw(x, y, color, IMG_DIGI_18_32, area); x = x + 18; div = div / 10; } @@ -45,12 +46,12 @@ void ui_text_number18x32_with_bg(uint32_t num, uint16_t x, uint16_t y, uint16_t div = div / 10; if (div == 0) { mimg_Area area = mimg_get_tile_area(IMG_DIGI_18_32, 10, 1, 0); - mimg_draw_with_bg(ST7735_DrawPixel, x, y, color, bg_color, IMG_DIGI_18_32, area); + mimg_draw_with_bg(x, y, color, bg_color, IMG_DIGI_18_32, area); } else { while (div > 0) { tmp = (num / div) % 10; mimg_Area area = mimg_get_tile_area(IMG_DIGI_18_32, 10, 1, tmp); - mimg_draw_with_bg(ST7735_DrawPixel, x, y, color, bg_color, IMG_DIGI_18_32, area); + mimg_draw_with_bg(x, y, color, bg_color, IMG_DIGI_18_32, area); x = x + 18; div = div / 10; } @@ -205,7 +206,7 @@ void icon_stereo() { if (global_data.flag & G_FL_MONO) { ST7735_FillRectangle(8, 25, area.w, area.h, ST7735_BLACK); } else { - mimg_draw_with_bg(ST7735_DrawPixel, 8, 25, ST7735_YELLOW, ST7735_BLACK, IMG_STEREO, area); + mimg_draw_with_bg(8, 25, ST7735_YELLOW, ST7735_BLACK, IMG_STEREO, area); } } diff --git a/Core/App/Graphic/ui_menu.c b/Core/App/Graphic/ui_menu.c new file mode 100644 index 0000000..943e93a --- /dev/null +++ b/Core/App/Graphic/ui_menu.c @@ -0,0 +1,175 @@ +#include +#include "keypad.h" +#include "st7735.h" +#include "asciifont.h" +#include "ui_menu.h" +#include "ui_utils.h" + +#define LIST_STYLE_SELECTED_COLOR_BG ST7735_COLOR565(0x40, 0x40, 0x40) +#define LIST_STYLE_OFFSET_LEFT 32 +#define UI_HALF_Y (UI_Y + (UI_H / 2)) +#define LIST_PAGE_SIZE (UI_H / font_unifont_8x16->char_height) +#define LIST_PAGE_SIZE_HALF ((LIST_PAGE_SIZE + 1) / 2) +#define LIST_ITEM_LEVEL(p) ((p < LIST_PAGE_SIZE_HALF) ? (p) : (LIST_PAGE_SIZE - p - 1)) +#define UI_LIST_OFFSET_X(p) (UI_X + (LIST_STYLE_OFFSET_LEFT * LIST_ITEM_LEVEL(p) / (LIST_PAGE_SIZE_HALF - 1)) + 8) +#define UI_LIST_OFFSET_Y(p) (UI_Y + (font_unifont_8x16->char_height * p)) +#define UI_LIST_WIDTH(p) (UI_W - (LIST_STYLE_OFFSET_LEFT * LIST_ITEM_LEVEL(p) / (LIST_PAGE_SIZE_HALF - 1)) - 8) + +#define U8_RESET 255 +/* +0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 +8 +*/ +static uint16_t get_item_count(uiu_GetStrItemFunc get_item) { + uint16_t len = 0; + while (get_item(len).len > 0) { + len ++; + } + return len; +} + +static void render_page_number(uint8_t curr, uint8_t total) { + uint8_t number_str[4] = {0, 0, 0, 0}; + uint8_t scale = 100; + uint8_t num_pos = 0; + while (scale > 0) { + uint8_t num = (curr / scale) % 10; + scale /= 10; + if (num == 0 && num_pos == 0) continue; + number_str[num_pos] = '0' + num; + num_pos ++; + } + uiu_text_area(font_quan_8x8, number_str, 4, + UI_X, UI_HALF_Y - 1 - font_quan_8x8->char_height - 1, + font_quan_8x8->char_width * 3, font_quan_8x8->char_height, + uiu_ALIGN_HCENTER | uiu_ALIGN_VCENTER, ST7735_YELLOW, ST7735_BLACK + ); + number_str[0] = 0; + number_str[1] = 0; + number_str[2] = 0; + number_str[3] = 0; + scale = 100; + num_pos = 0; + while (scale > 0) { + uint8_t num = (total / scale) % 10; + scale /= 10; + if (num == 0 && num_pos == 0) continue; + number_str[num_pos] = '0' + num; + num_pos ++; + } + uiu_text_area(font_quan_8x8, number_str, 4, + UI_X, UI_HALF_Y + 2, + font_quan_8x8->char_width * 3, font_quan_8x8->char_height, + uiu_ALIGN_HCENTER | uiu_ALIGN_VCENTER, ST7735_YELLOW, ST7735_BLACK + ); +} + +IndexResult iui_list_select(const uint8_t *title, uint32_t len, uiu_GetStrItemFunc get_item, uint16_t init_index) { + uint16_t item_count = get_item_count(get_item); + uint16_t pointer = init_index % item_count; + uint8_t tmp_pointer = U8_RESET; + uint8_t last_pointer = U8_RESET; + uint16_t page_start = (pointer / LIST_PAGE_SIZE) * LIST_PAGE_SIZE; + printf("item count: %u\n", item_count); + // clear + ST7735_FillScreen(ST7735_BLACK); + // render title + uiu_title(title, len); + // render line + ST7735_DrawLine(UI_X, UI_Y + font_unifont_8x16->char_height, LIST_STYLE_OFFSET_LEFT - 1, UI_HALF_Y - 1, ST7735_YELLOW); + ST7735_DrawLine(UI_X, UI_Y + UI_H - font_unifont_8x16->char_height - 1, LIST_STYLE_OFFSET_LEFT - 1, UI_HALF_Y, ST7735_YELLOW); + ST7735_FillRectangle(UI_X, UI_HALF_Y - 1, LIST_STYLE_OFFSET_LEFT - 1, 1, ST7735_YELLOW); + while (1) { + uint8_t event = kp_query(); + uint8_t et = kp_Type(event); + uint8_t ev = kp_Value(event); + // process event + if (et == kp_ROTATE_RIGHT || et == kp_ROTATE_LEFT) { + if (et == kp_ROTATE_RIGHT) { + pointer += ev; + } else { + pointer += (0x7FFF / item_count) * item_count; + pointer -= ev; + } + pointer %= item_count; + if (((pointer / LIST_PAGE_SIZE) * LIST_PAGE_SIZE) != page_start) { + last_pointer = U8_RESET; + page_start = ((pointer / LIST_PAGE_SIZE) * LIST_PAGE_SIZE); + } + tmp_pointer = U8_RESET; + printf("Move list pointer to %u\n", pointer); + } else if (et == kp_SHORT_CLICK) { + if (ev == kp_KEYENCODER) { + discare_kp_events(); + printf("Confirm list item %u\n", pointer); + IndexResult ret = {pointer, 1}; + return ret; + } + } else if (et == kp_LONG_PRESS) { + if (ev == kp_KEYENCODER) { + discare_kp_events(); + printf("Cancel list item %u\n", pointer); + IndexResult ret = {pointer, 0}; + return ret; + } + } else if (et != kp_NOP) { + printf("Unhandled event: %u %u\n", et, ev); + } + // render items + if (tmp_pointer == U8_RESET) { + // need render + if (last_pointer == U8_RESET) { + // render all + render_page_number((pointer / LIST_PAGE_SIZE) + 1, (item_count + LIST_PAGE_SIZE - 1) / LIST_PAGE_SIZE); + for (tmp_pointer = 0; tmp_pointer < LIST_PAGE_SIZE; tmp_pointer ++) { + uint16_t current = page_start + tmp_pointer; + if (current >= item_count) { + uiu_text_area(font_unifont_8x16, u8str(" "), 1, + UI_LIST_OFFSET_X(tmp_pointer), UI_LIST_OFFSET_Y(tmp_pointer), + UI_LIST_WIDTH(tmp_pointer), font_unifont_8x16->char_height, + uiu_ALIGN_HCENTER | uiu_ALIGN_VCENTER, ST7735_WHITE, ST7735_BLACK + ); + continue; + } + StrItem item = get_item(current); + if (current == pointer) { + uiu_text_area(font_unifont_8x16, item.text, item.len, + UI_LIST_OFFSET_X(tmp_pointer), UI_LIST_OFFSET_Y(tmp_pointer), + UI_LIST_WIDTH(tmp_pointer), font_unifont_8x16->char_height, + uiu_ALIGN_HCENTER | uiu_ALIGN_VCENTER, ST7735_GREEN, LIST_STYLE_SELECTED_COLOR_BG + ); + last_pointer = tmp_pointer; + } else { + uiu_text_area(font_unifont_8x16, item.text, item.len, + UI_LIST_OFFSET_X(tmp_pointer), UI_LIST_OFFSET_Y(tmp_pointer), + UI_LIST_WIDTH(tmp_pointer), font_unifont_8x16->char_height, + uiu_ALIGN_HCENTER | uiu_ALIGN_VCENTER, ST7735_WHITE, ST7735_BLACK + ); + } + } + } else { + StrItem item = get_item(page_start + last_pointer); + uiu_text_area(font_unifont_8x16, item.text, item.len, + UI_LIST_OFFSET_X(last_pointer), UI_LIST_OFFSET_Y(last_pointer), + UI_LIST_WIDTH(last_pointer), font_unifont_8x16->char_height, + uiu_ALIGN_HCENTER | uiu_ALIGN_VCENTER, ST7735_WHITE, ST7735_BLACK + ); + last_pointer = pointer - page_start; + StrItem item_curr = get_item(pointer); + uiu_text_area(font_unifont_8x16, item_curr.text, item_curr.len, + UI_LIST_OFFSET_X(last_pointer), UI_LIST_OFFSET_Y(last_pointer), + UI_LIST_WIDTH(last_pointer), font_unifont_8x16->char_height, + uiu_ALIGN_HCENTER | uiu_ALIGN_VCENTER, ST7735_GREEN, LIST_STYLE_SELECTED_COLOR_BG + ); + } + tmp_pointer = 0; + } + } +} diff --git a/Core/App/Graphic/ui_menu.h b/Core/App/Graphic/ui_menu.h new file mode 100644 index 0000000..1315270 --- /dev/null +++ b/Core/App/Graphic/ui_menu.h @@ -0,0 +1,5 @@ +#pragma once +#include +#include "ui_utils.h" + +IndexResult iui_list_select(const uint8_t *title, uint32_t len, uiu_GetStrItemFunc get_item, uint16_t init_index); diff --git a/Core/App/Graphic/ui_utils.c b/Core/App/Graphic/ui_utils.c new file mode 100644 index 0000000..b3f5bab --- /dev/null +++ b/Core/App/Graphic/ui_utils.c @@ -0,0 +1,60 @@ +#include +#include "ui_utils.h" +#include "st7735.h" +#include "bmfont.h" +#include "asciifont.h" + +#define MAX_LINES 16 +#define TITLE_COLOR_BG ST7735_COLOR565(0x20, 0x20, 0x20) + +const StrItem NULL_STR_ITEM = { NULL, 0 }; + +void uiu_text_area(bmf_BitmapFont *font, const uint8_t *text, uint32_t len, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t align, uint16_t color, uint16_t bg_color) { + // calc line width + uint16_t lws[MAX_LINES]; + uint16_t gws[MAX_LINES]; + uint16_t max_width = 0; + uint8_t lines = 0; + uint16_t p_off = 0; + while ((p_off < len) && ((lines + 1) * font->char_height <= h) && (lines < MAX_LINES)) { + uint16_t fitlen = bmf_get_text_offset(font, text + p_off, len - p_off, w, font->char_height); + if (fitlen == 0) { + break; + } + lws[lines] = fitlen; + gws[lines] = bmf_get_text_width(font, text + p_off, fitlen); + max_width = (gws[lines] > max_width) ? gws[lines] : max_width; + lines ++; + p_off += fitlen; + } + // draw lines + uint16_t off_x = 0; + uint16_t off_y = 0; + if (align & uiu_ALIGN_VBOTTOM) { + off_y = y + (h - (lines * font->char_height)); + }else if (align & uiu_ALIGN_VCENTER) { + off_y = y + ((h - (lines * font->char_height)) / 2); + }else { + off_y = y; + } + uint8_t cur_lines; + p_off = 0; + ST7735_FillRectangle(x, y, w, h, bg_color); + for (cur_lines = 0; cur_lines < lines; cur_lines ++) { + if (align & uiu_ALIGN_HRIGHT) { + off_x = x + (w - gws[cur_lines]); + } else if (align & uiu_ALIGN_HCENTER) { + off_x = x + ((w - gws[cur_lines]) / 2); + } else { + off_x = x; + } + bmf_draw_text(font, text + p_off, lws[cur_lines], off_x, off_y, gws[cur_lines], font->char_height, color); + off_y += font->char_height; + p_off += lws[cur_lines]; + } +} + +void uiu_title(const uint8_t *text, uint32_t len) { + ST7735_FillRectangle(0, 0, ST7735_WIDTH, 16, TITLE_COLOR_BG); + uiu_text_area(font_unifont_8x16, text, len, 0, 0, ST7735_WIDTH, 16, uiu_ALIGN_HCENTER | uiu_ALIGN_VCENTER, ST7735_YELLOW, TITLE_COLOR_BG); +} diff --git a/Core/App/Graphic/ui_utils.h b/Core/App/Graphic/ui_utils.h new file mode 100644 index 0000000..015f0f7 --- /dev/null +++ b/Core/App/Graphic/ui_utils.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include "bmfont.h" +#include "st7735.h" + +#define uiu_ALIGN_HLEFT 0b00000001 +#define uiu_ALIGN_HCENTER 0b00000010 +#define uiu_ALIGN_HRIGHT 0b00000100 +#define uiu_ALIGN_VTOP 0b00010000 +#define uiu_ALIGN_VCENTER 0b00100000 +#define uiu_ALIGN_VBOTTOM 0b01000000 + +#define UI_X 0 +#define UI_Y 16 +#define UI_W (ST7735_WIDTH) +#define UI_H (ST7735_HEIGHT - 16) + +typedef struct +{ + const uint8_t *text; + const uint32_t len; +} StrItem; + +typedef struct +{ + uint16_t index; + uint8_t confirmed; +} IndexResult; + + +extern const StrItem NULL_STR_ITEM; + +/** get string item in the list. + * @param index item index + * @return STR_ITEM, if STR_ITEM.len == 0 means item not exist. + */ +typedef StrItem (*uiu_GetStrItemFunc)(uint16_t index); + +void uiu_text_area(bmf_BitmapFont *font, const uint8_t *text, uint32_t len, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t align, uint16_t color, uint16_t bg_color); +void uiu_title(const uint8_t *text, uint32_t len); diff --git a/Core/App/Input/keypad.c b/Core/App/Input/keypad.c index 48500a5..de29430 100644 --- a/Core/App/Input/keypad.c +++ b/Core/App/Input/keypad.c @@ -92,3 +92,10 @@ uint8_t kp_query() { } return K_EVENT(kp_NOP, 0); } + +void discare_kp_events() { + uint8_t event = kp_query(); + while (kp_Type(event) != kp_NOP) { + event = kp_query(); + } +} diff --git a/Core/App/Input/keypad.h b/Core/App/Input/keypad.h index f8787c5..7fdb7cb 100644 --- a/Core/App/Input/keypad.h +++ b/Core/App/Input/keypad.h @@ -19,7 +19,8 @@ #define kp_Type(x) (x & 0b1111) #define kp_Value(x) (x >> 4) -#define kp_LONG_PRESS_TIMEOUT_MS 250 +#define kp_LONG_PRESS_TIMEOUT_MS 500 // query and return keypad event uint8_t kp_query(); +void discare_kp_events(void); diff --git a/Core/App/app.c b/Core/App/app.c index 21a551c..ad07bb6 100644 --- a/Core/App/app.c +++ b/Core/App/app.c @@ -6,6 +6,39 @@ #include "time.h" #include "kt0915.h" +/** test */ +#include "ui_menu.h" +#include "ui_utils.h" + +static const uint8_t *item_text = u8str("qwertyuiop"); +static StrItem app_get_item(uint16_t index) { + if (index >= 10) { + return NULL_STR_ITEM; + } + StrItem item = { item_text, 1 }; + item.text += index; + return item; +} + +void test() { + ST7735_FillScreen(ST7735_BLACK); + printf("test start.\n"); + while (1) { + IndexResult res = iui_list_select(u8str("Kobold's Menu"), 33, app_get_item, 0); + printf("You Selected '%s'\n", app_get_item(res.index).text); + if (res.confirmed) { + printf("and you confirmed it.\n"); + } else { + printf("but you canceled it.\n"); + } + } + // end + printf("test end.\n"); + while (1) { + } +} +/** test end */ + void __limit_freq_range() { uint16_t limit_min = (global_data.rf_mode == G_RF_MODE_AM) ? G_AM_FREQ_MIN : G_FM_FREQ_MIN; uint16_t limit_max = (global_data.rf_mode == G_RF_MODE_AM) ? G_AM_FREQ_MAX : G_FM_FREQ_MAX; @@ -16,6 +49,14 @@ void __limit_freq_range() { } } +void __ensure_mute() { + if (global_data.flag & G_FL_MUTE) { + KT0915_setAudioMute(1); + } else { + KT0915_setAudioMute(0); + } +} + void __send_freq() { if (global_data.rf_mode == G_RF_MODE_AM) { KT0915_setFrequency(global_data.freq); @@ -35,12 +76,14 @@ void __switch_to_fm() { KT0915_setFM(G_FM_FREQ_MIN * 50ul, G_FM_FREQ_MAX * 50ul, G_FM_FREQ_MIN * 50ul, 50u); printf("Switched to FM Mode\n"); __send_freq(); + __ensure_mute(); } void __switch_to_am() { KT0915_setAM(G_AM_FREQ_MIN, G_AM_FREQ_MAX, G_AM_FREQ_MIN, 1, 0); printf("Switched to AM Mode\n"); __send_freq(); + __ensure_mute(); } uint16_t __read_rssi() { @@ -78,8 +121,9 @@ void app_init() { printf("\n====start====\n"); target_time_stamp = ticks_add(ticks_ms(), 500); kp_query(); - __init_kt0915(); - ui_screen_main(); + // __init_kt0915(); + // ui_screen_main(); + test(); } void app_main_loop() { diff --git a/Core/App/global.c b/Core/App/global.c index d3208b2..4672831 100644 --- a/Core/App/global.c +++ b/Core/App/global.c @@ -2,7 +2,7 @@ #include "global.h" GlobalData global_data = { - 0b0000000000000000, //flag + 0b0000000000000001, //flag // 880 * 2, // freq 893 * 2, // freq G_RF_MODE_FM, //rf_mode diff --git a/Core/Hardware/ST7735/st7735.c b/Core/Hardware/ST7735/st7735.c index cddd189..117cfe1 100644 --- a/Core/Hardware/ST7735/st7735.c +++ b/Core/Hardware/ST7735/st7735.c @@ -77,7 +77,7 @@ static const uint8_t ST7735_DISPON, DELAY, // 4: Main screen turn on, no args w/delay 100}; // 100 ms delay -static void ST7735_Select() +void ST7735_Select() { HAL_GPIO_WritePin(ST7735_CS_GPIO_Port, ST7735_CS_Pin, GPIO_PIN_RESET); } @@ -87,21 +87,21 @@ void ST7735_Unselect() HAL_GPIO_WritePin(ST7735_CS_GPIO_Port, ST7735_CS_Pin, GPIO_PIN_SET); } -static void ST7735_Reset() +void ST7735_Reset() { HAL_GPIO_WritePin(ST7735_RES_GPIO_Port, ST7735_RES_Pin, GPIO_PIN_RESET); HAL_Delay(5); HAL_GPIO_WritePin(ST7735_RES_GPIO_Port, ST7735_RES_Pin, GPIO_PIN_SET); } -static void ST7735_WriteCommand(uint8_t cmd) +void ST7735_WriteCommand(uint8_t cmd) { HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(&ST7735_SPI_PORT, &cmd, sizeof(cmd), HAL_MAX_DELAY); // SPI_Write(&cmd, sizeof(cmd)); } -static void ST7735_WriteData(uint8_t *buff, size_t buff_size) +void ST7735_WriteData(uint8_t *buff, size_t buff_size) { HAL_GPIO_WritePin(ST7735_DC_GPIO_Port, ST7735_DC_Pin, GPIO_PIN_SET); HAL_SPI_Transmit(&ST7735_SPI_PORT, buff, buff_size, HAL_MAX_DELAY); @@ -139,7 +139,7 @@ static void ST7735_ExecuteCommandList(const uint8_t *addr) } } -static void ST7735_SetAddressWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) +void ST7735_SetAddressWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { // column address set ST7735_WriteCommand(ST7735_CASET); @@ -178,54 +178,61 @@ void ST7735_DrawPixel(uint16_t x, uint16_t y, uint16_t color) //画点 ST7735_Unselect(); } -void ST7735_DrawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,uint16_t color) //画线 +void ST7735_DrawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2,uint16_t color) { - int16_t temp; - int16_t steep = abs(y1 - y0) > abs(x1 - x0); - if (steep) { - temp = x0; - x0 = y0; - y0 = temp; - - temp = x1; - x0 = y1; - y1 = temp; - } - - if (x0 > x1) { - temp = x1; - x1 = x0; - x0 = temp; - - temp = y1; - y1 = y0; - y0 = temp; - } - - int16_t dx, dy; - dx = x1 - x0; - dy = abs(y1 - y0); - - int16_t err = dx / 2; - int16_t ystep; - - if (y0 < y1) { - ystep = 1; + int16_t dx = x2 - x1; + int16_t sx; + if (dx > 0) { + sx = 1; } else { - ystep = -1; + dx = -dx; + sx = -1; } - - for (; x0 <= x1; x0++) { + int16_t dy = y2 - y1; + int16_t sy; + if (dy > 0) { + sy = 1; + } else { + dy = -dy; + sy = -1; + } + uint8_t steep; + if (dy > dx) { + int16_t temp; + temp = x1; + x1 = y1; + y1 = temp; + temp = dx; + dx = dy; + dy = temp; + temp = sx; + sx = sy; + sy = temp; + steep = 1; + } else { + steep = 0; + } + int16_t e = 2 * dy - dx; + int16_t i; + for (i = 0; i < dx; ++i) { if (steep) { - ST7735_DrawPixel(y0, x0, color); + if (0 <= y1 && y1 < ST7735_WIDTH && 0 <= x1 && x1 < ST7735_HEIGHT) { + ST7735_DrawPixel(y1, x1, color); + } } else { - ST7735_DrawPixel(x0, y0, color); + if (0 <= x1 && x1 < ST7735_WIDTH && 0 <= y1 && y1 < ST7735_HEIGHT) { + ST7735_DrawPixel(x1, y1, color); + } } - err -= dy; - if (err < 0) { - y0 += ystep; - err += dx; + while (e >= 0) { + y1 += sy; + e -= 2 * dx; } + x1 += sx; + e += 2 * dy; + } + if (0 <= x2 && x2 < ST7735_WIDTH && 0 <= y2 && y2 < ST7735_HEIGHT) { + ST7735_DrawPixel(x2, y2, color); } } diff --git a/Core/Hardware/ST7735/st7735.h b/Core/Hardware/ST7735/st7735.h index 1667246..ea4398c 100644 --- a/Core/Hardware/ST7735/st7735.h +++ b/Core/Hardware/ST7735/st7735.h @@ -237,6 +237,11 @@ extern SPI_HandleTypeDef ST7735_SPI_PORT; // call before initializing any SPI devices void ST7735_Unselect(void); +void ST7735_Select(void); +void ST7735_Reset(void); +void ST7735_WriteCommand(uint8_t cmd); +void ST7735_WriteData(uint8_t *buff, size_t buff_size); +void ST7735_SetAddressWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1); void ST7735_Init(void); void ST7735_DrawPixel(uint16_t x, uint16_t y, uint16_t color); diff --git a/Makefile b/Makefile index c598e99..5be539a 100644 --- a/Makefile +++ b/Makefile @@ -76,6 +76,8 @@ Core/Hardware/ST7735/st7735.c \ Core/Hardware/KT0915/kt0915.c \ Core/Hardware/SWI2C/i2c_sw.c \ Core/App/Graphic/monoimg.c \ +Core/App/Graphic/ui_menu.c \ +Core/App/Graphic/ui_utils.c \ Core/App/Graphic/ui.c \ Core/App/Graphic/bmfont.c \ Core/App/Graphic/asciifont.c \ @@ -155,7 +157,7 @@ C_INCLUDES = \ # compile gcc flags ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections -CFLAGS += $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections +CFLAGS += $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections -fstack-usage ifeq ($(DEBUG), 1) CFLAGS += -g -gdwarf-2 @@ -221,7 +223,10 @@ clean: # flash ####################################### flash: - -JLinkExe cmd.jexe + -JLinkExe flash.jexe + +reset: + -JLinkExe reset.jexe ####################################### # dependencies diff --git a/cmd.jexe b/flash.jexe similarity index 100% rename from cmd.jexe rename to flash.jexe diff --git a/flash.sh b/flash.sh deleted file mode 100755 index 9e3740e..0000000 --- a/flash.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -JLinkExe cmd.jexe diff --git a/reset.jexe b/reset.jexe new file mode 100644 index 0000000..4d076c6 --- /dev/null +++ b/reset.jexe @@ -0,0 +1,7 @@ +Device STM32G030C8 +SelectInterface SWD +Speed auto +Connect +Reset +Go +EXit \ No newline at end of file