From 6586bcfa693ce66243988a8e9690839fc2c59a66 Mon Sep 17 00:00:00 2001 From: Slavasil Date: Sun, 13 Sep 2026 22:47:07 +0300 Subject: [PATCH] fix font; add separate counter for distance from power-on --- font_ubuntu.h | 8 ++--- graphics.c | 8 ++--- graphics.h | 14 +++++--- main.c | 93 +++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 93 insertions(+), 30 deletions(-) diff --git a/font_ubuntu.h b/font_ubuntu.h index 2dd871c..d4c3fd7 100644 --- a/font_ubuntu.h +++ b/font_ubuntu.h @@ -326,9 +326,9 @@ const char GLYPH_CYRILLIC_SMALL_LETTER_CHE[] = {224, 224, 0, 0, 0, 0, 0, 0, const char GLYPH_CYRILLIC_SMALL_LETTER_SHA[] = {224, 224, 0, 0, 0, 0, 0, 0, 224, 224, 0, 0, 0, 0, 0, 224, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 7, 7, 6, 6, 6, 6, 6, 6, 7, 7, 6, 6, 6, 6, 6, 7}; -const char GLYPH_CYRILLIC_SMALL_LETTER_SHCHA[] = {224, 224, 0, 0, 0, 0, 0, 0, 224, 224, 0, 0, 0, 0, 0, 224, - 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, - 7, 7, 6, 6, 6, 6, 6, 6, 7, 7, 6, 6, 6, 6, 6, 7}; +const char GLYPH_CYRILLIC_SMALL_LETTER_SHCHA[] = { + 224, 224, 0, 0, 0, 0, 0, 0, 224, 224, 0, 0, 0, 0, 0, 224, 224, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, + 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 7, 7, 6, 6, 6, 6, 6, 6, 7, 7, 6, 6, 6, 6, 6, 7, 7, 126, 126, 0}; const char GLYPH_CYRILLIC_SMALL_LETTER_HARD_SIGN[] = {96, 96, 96, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 12, 12, 12, 12, 12, 12, 28, 24, 248, 240, 0, 0, 0, 7, 7, 6, 6, 6, 6, 6, 6, 6, 3, 3, 1}; @@ -351,7 +351,7 @@ const char GLYPH_WIDTH[143] = { 13, 9, 16, 17, 14, 13, 15, 12, 11, 13, 14, 2, 11, 14, 12, 16, 15, 16, 13, 16, 14, 12, 14, 14, 16, 17, 14, 14, 13, 6, 11, 6, 13, 13, 5, 11, 12, 10, 12, 13, 8, 12, 11, 2, 4, 12, 4, 16, 11, 13, 12, 12, 8, 10, 8, 11, 12, 18, 13, 12, 10, 7, 2, 7, 14, 12, 11, 18, 12, 17, 13, 15, 15, 14, 16, 14, 14, 17, 16, 12, 16, 16, 17, 16, 12, 13, 16, - 14, 13, 10, 9, 15, 13, 18, 11, 12, 12, 12, 14, 15, 11, 11, 10, 16, 12, 10, 16, 16, 15, 16, 10, 10, 16, 11}; + 14, 13, 10, 9, 15, 13, 18, 11, 12, 12, 12, 14, 15, 11, 11, 10, 16, 12, 10, 16, 20, 15, 16, 10, 10, 16, 11}; const char *GLYPH_ADDR[143] = { GLYPH_EXCLAMATION_MARK, diff --git a/graphics.c b/graphics.c index b5e2d4c..ec52eb7 100644 --- a/graphics.c +++ b/graphics.c @@ -111,8 +111,8 @@ void lcd_draw_vertical_xsmall_digit_segment(uint16_t x, uint16_t y, uint16_t col } } -const uint8_t DIGIT_MASKS[10] = {0b1110111, 0b0100100, 0b1011101, 0b1101101, 0b0101110, - 0b1101011, 0b1111011, 0b0100101, 0b1111111, 0b1101111}; +static const uint8_t DIGIT_MASKS[10] = {0b1110111, 0b0100100, 0b1011101, 0b1101101, 0b0101110, + 0b1101011, 0b1111011, 0b0100101, 0b1111111, 0b1101111}; void lcd_draw_digit(uint8_t digit, uint16_t x, uint16_t y, uint16_t color, uint16_t bgColor) { const uint16_t SEGMENT_OFFSETS[7][2] = {{9, 0}, {0, 9}, {49, 9}, {9, 49}, {0, 58}, {49, 58}, {9, 99}}; @@ -199,7 +199,7 @@ void lcd_write_string(const char *string, uint16_t x, uint16_t y, uint16_t color st7789_write(glyphPixels, 24 * (glyphWidth + LETTER_SPACING) * sizeof(uint16_t)); x += glyphWidth + LETTER_SPACING; } else { - lcd_fill_rectangle(x, y, 12, 24, bgColor); + _fill_rectangle(x, y, 12, 24, bgColor); x += 12; } } @@ -215,7 +215,7 @@ int lcd_string_width(const char *string) { } else { int glyphIndex = glyph_index_for_code_point(character); if (glyphIndex != -1) { - width += GLYPH_WIDTH[glyphIndex] + 1; + width += GLYPH_WIDTH[glyphIndex] + LETTER_SPACING; } else { width += 12; } diff --git a/graphics.h b/graphics.h index aed81bd..bbe082d 100644 --- a/graphics.h +++ b/graphics.h @@ -29,6 +29,14 @@ static inline void _draw_v_strip(uint16_t x, uint16_t y, uint16_t len, uint16_t } } +static inline void _fill_rectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) { + st7789_caset(x, x + w - 1); + st7789_raset(y, y + h - 1); + for (uint16_t i = 0; i < w * h; ++i) { + st7789_put(color); + } +} + inline void lcd_set_pixel(uint16_t x, uint16_t y, uint16_t color) { x += LCD_OFFSET_X; y += LCD_OFFSET_Y; @@ -39,11 +47,7 @@ inline void lcd_set_pixel(uint16_t x, uint16_t y, uint16_t color) { inline void lcd_fill_rectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) { x += LCD_OFFSET_X; y += LCD_OFFSET_Y; - st7789_caset(x, x + w - 1); - st7789_raset(y, y + h - 1); - for (uint16_t i = 0; i < w * h; ++i) { - st7789_put(color); - } + _fill_rectangle(x, y, w, h, color); } void lcd_draw_horizontal_digit_segment(uint16_t x, uint16_t y, uint16_t color); diff --git a/main.c b/main.c index b00a94d..8d9a97e 100644 --- a/main.c +++ b/main.c @@ -37,7 +37,8 @@ enum GUIScreen { enum DisplayMode { DISPLAY_MODE_SPEED, - DISPLAY_MODE_DISTANCE, + DISPLAY_MODE_DISTANCE_FROM_POWER_ON, + DISPLAY_MODE_TOTAL_DISTANCE, }; struct GUIState { @@ -51,6 +52,9 @@ struct GUIState { //... } settingsScreen; } screenState; + uint8_t drawnTimeHours; + uint8_t drawnTimeMinutes; + uint16_t drawnTimeTextWidth; }; void on_gpio_interrupt(uint pin, uint32_t eventMask); @@ -112,7 +116,9 @@ uint16_t ticksPerWindow = 0; // critical_section_t intervalWheelTicksCritSec; repeating_timer_t speedMeasureTimer; -uint totalTicks = 0; +// odometer counters +uint totalWheelTicks = 0; +uint wheelTicksFromPowerOn = 0; DateTime currentDatetime; @@ -289,7 +295,8 @@ int64_t hall_sensor_debounce_callback(alarm_id_t alarmId, void *userData) { absolute_time_t now = get_absolute_time(); lastWheelTickTime = now; ++intervalWheelTicks[currentInterval]; - ++totalTicks; + ++totalWheelTicks; + ++wheelTicksFromPowerOn; gpio_put(25, !gpio_get(25)); hallSensorPrevState = hallSensorLastState; @@ -338,7 +345,9 @@ void on_button_short_press(uint button) { if (button == 3 && guiState.screen == MAIN_SCREEN) { uint oldDispMode = guiState.screenState.mainScreen.displayMode; if (oldDispMode == DISPLAY_MODE_SPEED) - guiState.screenState.mainScreen.displayMode = DISPLAY_MODE_DISTANCE; + guiState.screenState.mainScreen.displayMode = DISPLAY_MODE_DISTANCE_FROM_POWER_ON; + else if (oldDispMode == DISPLAY_MODE_DISTANCE_FROM_POWER_ON) + guiState.screenState.mainScreen.displayMode = DISPLAY_MODE_TOTAL_DISTANCE; else guiState.screenState.mainScreen.displayMode = DISPLAY_MODE_SPEED; guiState.screenState.mainScreen.displayModeChanged = true; @@ -457,10 +466,46 @@ void draw_short_term_distance(uint value) { #undef DIGITS_START_Y #undef DIGIT_WIDTH #undef DIGIT_SPACING +#undef XSMALL_DIGITS_Y_OFFSET +} + +// long term distance is in "4.2" decimal fixed-point, in kilometers +void draw_long_term_distance(uint value) { +#define DIGITS_START_X 15 +#define DIGITS_START_Y 50 +#define XSMALL_DIGITS_Y_OFFSET 32 +#define XSMALL_DIGIT_WIDTH 30 +#define XSMALL_DIGIT_SPACING 4 + + if (value > 999999) + value = 999999; + + lcd_draw_small_digit(value / 100000, DIGITS_START_X, DIGITS_START_Y, 0xffff, 0x528a); + lcd_draw_small_digit((value / 10000) % 10, DIGITS_START_X + SMALL_DIGIT_WIDTH + SMALL_DIGIT_SPACING, DIGITS_START_Y, + 0xffff, 0x528a); + lcd_draw_small_digit((value / 1000) % 10, DIGITS_START_X + SMALL_DIGIT_WIDTH * 2 + SMALL_DIGIT_SPACING * 2, + DIGITS_START_Y, 0xffff, 0x528a); + lcd_draw_small_digit((value / 100) % 10, DIGITS_START_X + SMALL_DIGIT_WIDTH * 3 + SMALL_DIGIT_SPACING * 3, + DIGITS_START_Y, 0xffff, 0x528a); + lcd_draw_decimal_point(DIGITS_START_X + SMALL_DIGIT_WIDTH * 4 + SMALL_DIGIT_SPACING * 4 - 6, DIGITS_START_Y + 76, + 0xffff); + lcd_draw_xsmall_digit((value / 10) % 10, DIGITS_START_X + SMALL_DIGIT_WIDTH * 4 + SMALL_DIGIT_SPACING * 4 + 10, + DIGITS_START_Y + XSMALL_DIGITS_Y_OFFSET, 0xffff, 0x528a); + lcd_draw_xsmall_digit(value % 10, + DIGITS_START_X + SMALL_DIGIT_WIDTH * 4 + SMALL_DIGIT_SPACING * 4 + XSMALL_DIGIT_WIDTH + + XSMALL_DIGIT_SPACING + 10, + DIGITS_START_Y + XSMALL_DIGITS_Y_OFFSET, 0xffff, 0x528a); + +#undef DIGITS_START_X +#undef DIGITS_START_Y +#undef XSMALL_DIGITS_Y_OFFSET +#undef XSMALL_DIGIT_WIDTH +#undef XSMALL_DIGIT_SPACING } void update_lcd() { if (guiState.screen == MAIN_SCREEN) { + bool forceRedrawClock = guiState.screenState.mainScreen.displayModeChanged; if (guiState.screenState.mainScreen.displayModeChanged) { // lcd_fill_rectangle(49, 27, 213, 113, 0x0000); st7789_fill(0x0000); @@ -470,29 +515,43 @@ void update_lcd() { if (guiState.screenState.mainScreen.displayMode == DISPLAY_MODE_SPEED) { uint convertedSpeed = ticksPerWindow * cmphPerTpw / 10000; draw_speed(convertedSpeed); - lcd_write_string("Скорость", 20, 0, 0xffff, 0x0000); + lcd_write_string("Скорость", 10, 0, 0xffff, 0x0000); lcd_write_string("км/ч", 241, 126, 0xffff, 0x0000); - } else if (guiState.screenState.mainScreen.displayMode == DISPLAY_MODE_DISTANCE) { - uint convertedDistance = totalTicks * cmPerTick / 100; + } else if (guiState.screenState.mainScreen.displayMode == DISPLAY_MODE_DISTANCE_FROM_POWER_ON) { + uint convertedDistance = wheelTicksFromPowerOn * cmPerTick / 100; draw_short_term_distance(convertedDistance); - lcd_write_string("Расстояние", 20, 0, 0xffff, 0x0000); + lcd_write_string("Текущий путь", 10, 0, 0xffff, 0x0000); + lcd_write_string("км", 280, 37, 0xffff, 0x0000); + } else if (guiState.screenState.mainScreen.displayMode == DISPLAY_MODE_TOTAL_DISTANCE) { + uint convertedDistance = totalWheelTicks * cmPerTick / 1000; + draw_long_term_distance(convertedDistance); + lcd_write_string("Общий путь", 10, 0, 0xffff, 0x0000); lcd_write_string("км", 280, 37, 0xffff, 0x0000); } - char timeDisplay[6]; - memset(timeDisplay, ' ', sizeof(timeDisplay)); - timeDisplay[5] = 0; - sprintf(timeDisplay, "%i:%02i", currentDatetime.hour, currentDatetime.minute); - lcd_write_string(timeDisplay, 320 - 20 - lcd_string_width(timeDisplay), 0, 0xffff, 0x0000); - lcd_fill_rectangle(320 - 160, 0, 140 - lcd_string_width(timeDisplay), 24, 0x0000); + if (forceRedrawClock || guiState.drawnTimeHours != currentDatetime.hour || + guiState.drawnTimeMinutes != currentDatetime.minute) { + lcd_fill_rectangle(305 - guiState.drawnTimeTextWidth, 0, guiState.drawnTimeTextWidth, 24, 0x0000); + guiState.drawnTimeHours = currentDatetime.hour; + guiState.drawnTimeMinutes = currentDatetime.minute; + char timeDisplay[6]; + memset(timeDisplay, ' ', sizeof(timeDisplay)); + timeDisplay[5] = 0; + sprintf(timeDisplay, "%i:%02i", currentDatetime.hour, currentDatetime.minute); + lcd_write_string(timeDisplay, 305 - lcd_string_width(timeDisplay), 0, 0xffff, 0x0000); + guiState.drawnTimeTextWidth = lcd_string_width(timeDisplay); + } } else if (guiState.screen == SETTINGS_SCREEN) { } } void gui_init(struct GUIState *state) { state->screen = 0; - state->screenState.mainScreen.displayMode = DISPLAY_MODE_DISTANCE; + state->screenState.mainScreen.displayMode = DISPLAY_MODE_DISTANCE_FROM_POWER_ON; state->screenState.mainScreen.displayModeChanged = true; + state->drawnTimeTextWidth = 0; + state->drawnTimeHours = 24; + state->drawnTimeMinutes = 60; } void init_button_states() { @@ -516,7 +575,7 @@ void save_to_rtc() { static uint savedDistance; uint8_t buffer[31]; memset(buffer, 0, 31); - uint newDistance = totalTicks * cmPerTick / 10; + uint newDistance = totalWheelTicks * cmPerTick / 10; if (savedBefore && newDistance == savedDistance) { printf("skipped saving\n"); return; @@ -588,7 +647,7 @@ bool load_from_rtc() { } uint savedDistance = *(uint *)(buffer + 0); - totalTicks = savedDistance * 10 / cmPerTick; + totalWheelTicks = savedDistance * 10 / cmPerTick; #ifdef LOGGING printf("successfully loaded data from RTC memory\n"); #endif