200 lines
8.3 KiB
C
200 lines
8.3 KiB
C
#include "gui.h"
|
|
|
|
#include <pico/bootrom.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "ds1302.h"
|
|
#include "graphics.h"
|
|
#include "st7789.h"
|
|
#include "units.h"
|
|
|
|
extern uint16_t ticksPerWindow;
|
|
extern uint totalWheelTicks;
|
|
extern uint wheelTicksFromPowerOn;
|
|
extern DateTime currentDatetime;
|
|
|
|
extern void reset();
|
|
|
|
static struct GUIState guiState;
|
|
|
|
void gui_init() {
|
|
guiState.screen = 0;
|
|
guiState.screenState.mainScreen.displayMode = DISPLAY_MODE_DISTANCE_FROM_POWER_ON;
|
|
guiState.screenState.mainScreen.displayModeChanged = true;
|
|
guiState.drawnTimeTextWidth = 0;
|
|
guiState.drawnTimeHours = 24;
|
|
guiState.drawnTimeMinutes = 60;
|
|
}
|
|
|
|
void on_button_input(short button, enum ButtonInputType type, uint heldButtons) {
|
|
#ifdef LOGGING
|
|
printf("button %i %s press\n", button, type == BUTTON_SHORT_PRESS ? "short" : "long");
|
|
#endif
|
|
|
|
// handle simultaneous long presses
|
|
if (button == 2 && (heldButtons & (1 << 3)) || button == 3 && (heldButtons & (1 << 2))) {
|
|
reset();
|
|
}
|
|
if (button == 0 && (heldButtons & (1 << 3)) || button == 3 && (heldButtons & (1 << 0))) {
|
|
rom_reset_usb_boot(0, 0);
|
|
}
|
|
|
|
// handle simple short and long presses
|
|
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_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;
|
|
}
|
|
}
|
|
|
|
uint16_t color565_lerp(uint16_t a, uint16_t b, uint alphaPercent) {
|
|
uint alphaComplement = 100 - alphaPercent;
|
|
uint16_t red = ((b >> 11) * alphaPercent + (a >> 11) * alphaComplement) / 100;
|
|
uint16_t green = (((b >> 5) & 0x3F) * alphaPercent + ((a >> 5) & 0x3F) * alphaComplement) / 100;
|
|
uint16_t blue = ((b & 0x1F) * alphaPercent + (a & 0x1F) * alphaComplement) / 100;
|
|
return blue | (green << 5) | (red << 11);
|
|
}
|
|
|
|
// speed is in "2.1" decimal fixed-point, in km/h
|
|
void draw_speed(uint value) {
|
|
enum {
|
|
DIGITS_START_X = 20,
|
|
DIGITS_START_Y = 37,
|
|
DIGIT_WIDTH = 64,
|
|
DIGIT_SPACING = 8,
|
|
};
|
|
|
|
if (value > 999)
|
|
value = 999;
|
|
|
|
const uint16_t speedToColorX[] = {0, 50, 100, 150, 200, 300};
|
|
const uint16_t speedToColorY[] = {0x04ff, 0x27fb, 0x6fe4, 0xdfe4, 0xfe84, 0xfd04};
|
|
const size_t speedToColorPointCount = 6;
|
|
|
|
uint16_t color = speedToColorY[speedToColorPointCount - 1];
|
|
for (int i = 1; i < speedToColorPointCount; ++i) {
|
|
if (value <= speedToColorX[i]) {
|
|
color = color565_lerp(speedToColorY[i - 1], speedToColorY[i],
|
|
(value - speedToColorX[i - 1]) * 100 / (speedToColorX[i] - speedToColorX[i - 1]));
|
|
break;
|
|
}
|
|
}
|
|
|
|
lcd_draw_digit(value / 100, DIGITS_START_X, DIGITS_START_Y, color, 0x528a);
|
|
lcd_draw_digit((value / 10) % 10, DIGITS_START_X + DIGIT_WIDTH + DIGIT_SPACING, DIGITS_START_Y, color, 0x528a);
|
|
lcd_draw_decimal_point(DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING, DIGITS_START_Y + 101, color);
|
|
lcd_draw_digit(value % 10, DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING * 2 + 5, DIGITS_START_Y, color, 0x528a);
|
|
}
|
|
|
|
// distance is in "2.3" decimal fixed-point, in kilometers
|
|
void draw_short_term_distance(uint value) {
|
|
enum {
|
|
DIGITS_START_X = 20,
|
|
DIGITS_START_Y = 37,
|
|
SMALL_DIGITS_Y_OFFSET = 28,
|
|
XSMALL_DIGITS_Y_OFFSET = 60,
|
|
DIGIT_WIDTH = 64,
|
|
SMALL_DIGIT_WIDTH = 48,
|
|
XSMALL_DIGIT_WIDTH = 30,
|
|
DIGIT_SPACING = 8,
|
|
SMALL_DIGIT_SPACING = 6,
|
|
XSMALL_DIGIT_SPACING = 4,
|
|
};
|
|
|
|
if (value > 99999)
|
|
value = 99999;
|
|
|
|
lcd_draw_digit(value / 10000, DIGITS_START_X, DIGITS_START_Y, 0xffff, 0x528a);
|
|
lcd_draw_digit((value / 1000) % 10, DIGITS_START_X + DIGIT_WIDTH + DIGIT_SPACING, DIGITS_START_Y, 0xffff, 0x528a);
|
|
lcd_draw_decimal_point(DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING, DIGITS_START_Y + 101, 0xffff);
|
|
lcd_draw_small_digit((value / 100) % 10, DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING * 2 + 10,
|
|
DIGITS_START_Y + SMALL_DIGITS_Y_OFFSET, 0xffff, 0x528a);
|
|
lcd_draw_small_digit((value / 10) % 10,
|
|
DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING * 2 + SMALL_DIGIT_WIDTH + SMALL_DIGIT_SPACING +
|
|
10,
|
|
DIGITS_START_Y + SMALL_DIGITS_Y_OFFSET, 0xffff, 0x528a);
|
|
lcd_draw_xsmall_digit(value % 10,
|
|
DIGITS_START_X + DIGIT_WIDTH * 2 + DIGIT_SPACING * 2 + SMALL_DIGIT_WIDTH * 2 +
|
|
SMALL_DIGIT_SPACING * 2 + 10,
|
|
DIGITS_START_Y + XSMALL_DIGITS_Y_OFFSET, 0xffff, 0x528a);
|
|
}
|
|
|
|
// long term distance is in "4.2" decimal fixed-point, in kilometers
|
|
void draw_long_term_distance(uint value) {
|
|
enum {
|
|
DIGITS_START_X = 15,
|
|
DIGITS_START_Y = 50,
|
|
SMALL_DIGIT_WIDTH = 48,
|
|
XSMALL_DIGITS_Y_OFFSET = 32,
|
|
XSMALL_DIGIT_WIDTH = 30,
|
|
SMALL_DIGIT_SPACING = 6,
|
|
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);
|
|
}
|
|
|
|
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);
|
|
guiState.screenState.mainScreen.displayModeChanged = false;
|
|
}
|
|
|
|
if (guiState.screenState.mainScreen.displayMode == DISPLAY_MODE_SPEED) {
|
|
uint convertedSpeed = ticksPerWindow * cmphPerTpw / 10000;
|
|
draw_speed(convertedSpeed);
|
|
lcd_write_string("Скорость", 10, 0, 0xffff, 0x0000);
|
|
lcd_write_string("км/ч", 241, 126, 0xffff, 0x0000);
|
|
} 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("Текущий путь", 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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|