X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Ftelelco-v3.0%2Fao_lco_v3.c;h=dc6b030edd471561f973fae0902dc6beb15edcf2;hb=HEAD;hp=7c81a2e5babd3ddaf4c1f5c69c00c88469e0ed1b;hpb=f9e319f5a97df8c0950ec7531d0889cf60e53783;p=fw%2Faltos diff --git a/src/telelco-v3.0/ao_lco_v3.c b/src/telelco-v3.0/ao_lco_v3.c index 7c81a2e5..2f4c1dbe 100644 --- a/src/telelco-v3.0/ao_lco_v3.c +++ b/src/telelco-v3.0/ao_lco_v3.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #define WIDTH AO_ST7565_WIDTH #define HEIGHT AO_ST7565_HEIGHT @@ -39,37 +41,58 @@ static struct ao_bitmap fb = { }; static const struct ao_transform logo_transform = { - .x_scale = 48, .x_off = 2, - .y_scale = 48, .y_off = 0, + .x_scale = 40, .x_off = 8, + .y_scale = 40, .y_off = 0, +}; + +static const struct ao_transform show_transform = { + .x_scale = 36, .x_off = 100, + .y_scale = 36, .y_off = 0, }; #define BIG_FONT BitstreamVeraSans_Roman_58_font #define VOLT_FONT BitstreamVeraSans_Roman_58_font -#define CONTRAST_FONT BitstreamVeraSans_Roman_58_font #define SMALL_FONT BitstreamVeraSans_Roman_12_font #define TINY_FONT BitstreamVeraSans_Roman_10_font -#define LOGO_FONT BenguiatGothicStd_Bold_26_font +#define LOGO_FONT BenguiatGothicStd_Bold_24_font #define LABEL_Y (int16_t) (SMALL_FONT.ascent) -#define VALUE_Y (int16_t) (LABEL_Y + BIG_FONT.ascent + 5) -#define BOX_X 2 -#define PAD_X 90 -#define BOX_LABEL_X 30 -#define VOLT_LABEL_X 25 +#define VALUE_Y (int16_t) (LABEL_Y + 5 + BIG_FONT.ascent) + +#define SEP_X 82 +#define SEP_WIDTH 2 + +#define BOX_X (SEP_X / 2) +#define PAD_X ((WIDTH + SEP_X + SEP_WIDTH) / 2) + +#define VALUE_LABEL_X 64 #define RSSI_LABEL_X 15 -#define PAD_LABEL_X 95 -#define SEP_X (PAD_X - 8) + #define SCAN_X (WIDTH - 100) / 2 #define SCAN_Y 50 #define SCAN_HEIGHT 3 +#define SCANNING_X (WIDTH / 2) +#define SCANNING_Y (SCAN_Y - 2) #define FOUND_Y 63 -#define FOUND_X 6 +#define FOUND_X 3 #define FOUND_WIDTH (WIDTH - 6) #define CONTRAST_LABEL_X 37 #define CONTRAST_WIDTH 100 #define CONTRAST_X (WIDTH - CONTRAST_WIDTH) / 2 #define CONTRAST_Y 20 #define CONTRAST_HEIGHT 20 +#define CONTRAST_VALUE_X 64 +#define CONTRAST_VALUE_Y (CONTRAST_Y + CONTRAST_HEIGHT + SMALL_FONT.ascent + 3) +#define BACKLIGHT_LABEL_X 37 +#define BACKLIGHT_WIDTH 100 +#define BACKLIGHT_X (WIDTH - BACKLIGHT_WIDTH) / 2 +#define BACKLIGHT_Y 20 +#define BACKLIGHT_HEIGHT 20 +#define BACKLIGHT_VALUE_X 64 +#define BACKLIGHT_VALUE_Y (BACKLIGHT_Y + BACKLIGHT_HEIGHT + SMALL_FONT.ascent + 3) +#define INFO_FONT TINY_FONT +#define INFO_START_Y ((int16_t) (INFO_FONT.ascent + 2)) +#define INFO_STEP_Y ((int16_t) (INFO_FONT.ascent + 2)) #define AO_LCO_DRAG_RACE_START_TIME AO_SEC_TO_TICKS(5) #define AO_LCO_DRAG_RACE_STOP_TIME AO_SEC_TO_TICKS(2) @@ -79,19 +102,29 @@ static uint8_t ao_lco_select_mode; static uint8_t ao_lco_event_debug; #define PRINTE(...) do { if (!ao_lco_debug && !ao_lco_event_debug) break; printf ("\r%5lu %s: ", (unsigned long) ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0) -#define AO_LCO_SELECT_PAD 0 -#define AO_LCO_SELECT_BOX 1 +#define AO_LCO_SELECT_BOX 0 +#define AO_LCO_SELECT_PAD 1 static uint8_t ao_lco_display_mutex; +static uint8_t ao_sample_data; +static struct ao_data ao_data_cur; + +static void +_ao_center_text(int16_t x, int16_t y, const struct ao_font *font, const char *str) +{ + int16_t width = ao_text_width(font, str); + ao_text(&fb, font, x - width/2, y, str, AO_BLACK, AO_COPY); +} + static void -_ao_lco_show_pad(uint8_t pad) +_ao_lco_show_pad(int8_t pad) { char str[5]; + _ao_center_text(PAD_X, LABEL_Y, &SMALL_FONT, "Pad"); snprintf(str, sizeof(str), "%d", pad); - ao_text(&fb, &BIG_FONT, PAD_X, VALUE_Y, str, AO_BLACK, AO_COPY); - ao_text(&fb, &SMALL_FONT, PAD_LABEL_X, LABEL_Y, "Pad", AO_BLACK, AO_COPY); + _ao_center_text(PAD_X, VALUE_Y, &BIG_FONT, str); } static void @@ -99,42 +132,192 @@ _ao_lco_show_box(int16_t box) { char str[7]; - snprintf(str, sizeof(str), "%2d", box); - ao_text(&fb, &BIG_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY); - ao_text(&fb, &SMALL_FONT, BOX_LABEL_X, LABEL_Y, "Box", AO_BLACK, AO_COPY); + _ao_center_text(BOX_X, LABEL_Y, &SMALL_FONT, "Bank"); + snprintf(str, sizeof(str), "%d", box); + _ao_center_text(BOX_X, VALUE_Y, &BIG_FONT, str); } static void -_ao_lco_show_voltage(uint16_t decivolts, const char *label) +_ao_format_voltage(char *str, size_t size, uint16_t decivolts) { - char str[7]; + snprintf(str, size, "%d.%d", decivolts / 10, decivolts % 10); +} - PRINTD("voltage %d\n", decivolts); - snprintf(str, sizeof(str), "%2d.%d", decivolts / 10, decivolts % 10); - ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, AO_BLACK, AO_COPY); - ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, label, AO_BLACK, AO_COPY); +#if AO_LCO_HAS_CONTRAST +static void +_ao_lco_show_contrast(void) +{ + char buf[8]; + uint8_t brightness = ao_st7565_get_brightness(); + int16_t contrast = (int16_t) (brightness * CONTRAST_WIDTH / AO_LCO_MAX_CONTRAST); + + _ao_center_text(WIDTH/2, LABEL_Y, &SMALL_FONT, "Contrast"); + ao_rect(&fb, CONTRAST_X, CONTRAST_Y, contrast, CONTRAST_HEIGHT, AO_BLACK, AO_COPY); + snprintf(buf, sizeof(buf), "%d %%", brightness * 100 / AO_LCO_MAX_CONTRAST); + _ao_center_text(WIDTH/2, CONTRAST_VALUE_Y, &SMALL_FONT, buf); } +#endif +#if AO_LCO_HAS_BACKLIGHT_UI static void -_ao_lco_batt_voltage(void) +_ao_lco_show_backlight(void) { - struct ao_adc packet; + char buf[8]; + int32_t backlight = ao_lco_get_backlight(); + int16_t value = (int16_t) (backlight * BACKLIGHT_WIDTH / AO_LCO_MAX_BACKLIGHT); + + _ao_center_text(WIDTH/2, LABEL_Y, &SMALL_FONT, "Backlight"); + ao_rect(&fb, BACKLIGHT_X, BACKLIGHT_Y, value, BACKLIGHT_HEIGHT, AO_BLACK, AO_COPY); + snprintf(buf, sizeof(buf), "%ld %%", backlight * 100 / AO_LCO_MAX_BACKLIGHT); + _ao_center_text(WIDTH/2, BACKLIGHT_VALUE_Y, &SMALL_FONT, buf); +} +#endif + +static int16_t info_y; + +static void +_ao_lco_info(const char *format, ...) +{ + va_list a; + char buf[20]; + va_start(a, format); + vsnprintf(buf, sizeof(buf), format, a); + va_end(a); + ao_text(&fb, &INFO_FONT, 0, info_y, buf, AO_BLACK, AO_COPY); + info_y += INFO_STEP_Y; +} + +static void +_ao_lco_show_lco_info(void) +{ + char battery[7]; int16_t decivolt; - ao_adc_single_get(&packet); - decivolt = ao_battery_decivolt(packet.v_batt); - _ao_lco_show_voltage((uint16_t) decivolt, "LCO battery"); - ao_st7565_update(&fb); + ao_logo_poly(&fb, &show_transform, AO_BLACK, AO_COPY); + + decivolt = ao_battery_decivolt(ao_data_cur.adc.v_batt); + _ao_format_voltage(battery, sizeof(battery), (uint16_t) decivolt); + + info_y = INFO_START_Y; + _ao_lco_info("%s", ao_product); + _ao_lco_info("Serial: %d", ao_serial_number); + _ao_lco_info("Battery: %sV", battery); + _ao_lco_info("Version: %s", ao_version); + _ao_lco_info("Callsign: %s", ao_config.callsign); + _ao_lco_info("Frequency: %ld.%03d", + ao_config.frequency / 1000, + (int) (ao_config.frequency % 1000)); +} + +static uint8_t +popcount(uint32_t value) +{ + uint8_t count = 0; + while(value != 0) { + count += value & 1; + value >>= 1; + } + return count; } static void -_ao_lco_show_contrast(void) +_ao_lco_show_pad_info(void) { - uint8_t brightness = ao_st7565_get_brightness(); - int16_t contrast = (int16_t) (brightness * CONTRAST_WIDTH / AO_LCO_MAX_CONTRAST); + char pad_battery[7]; + + ao_logo_poly(&fb, &show_transform, AO_BLACK, AO_COPY); + info_y = INFO_START_Y; + _ao_lco_info("Bank: %d", ao_lco_box); + if (!(ao_lco_valid[ao_lco_box] & AO_LCO_VALID_LAST)) { + _ao_lco_info("Contact lost"); + _ao_lco_info("Last RSSI: %ddBm", ao_radio_cmac_last_rssi); + } else { + _ao_lco_info("Total pads: %d", popcount(ao_pad_query.channels)); + _ao_lco_info("RSSI: %ddBm", ao_radio_cmac_rssi); + _ao_format_voltage(pad_battery, sizeof(pad_battery), ao_pad_query.battery); + _ao_lco_info("Battery: %sV", pad_battery); + _ao_lco_info("Arming switch: %s", ao_pad_query.arm_status ? "On" : "Off"); + } +} - ao_text(&fb, &SMALL_FONT, CONTRAST_LABEL_X, LABEL_Y, "Contrast", AO_BLACK, AO_COPY); - ao_rect(&fb, CONTRAST_X, CONTRAST_Y, contrast, CONTRAST_HEIGHT, AO_BLACK, AO_COPY); +#define AO_LCO_DIM_BACKLIGHT (AO_LCO_MIN_BACKLIGHT + 3 * AO_LCO_BACKLIGHT_STEP) +#define AO_AUTO_BACKLIGHT_RANGE (AO_LCO_MAX_BACKLIGHT - AO_LCO_DIM_BACKLIGHT) +#define AO_AUTO_BACKLIGHT_GAP AO_ADC_MAX / 6 + +static struct { + int16_t v_als; + int32_t backlight; +} ao_lco_backlight_map[] = { + { .v_als = AO_ADC_MAX / 6, .backlight = AO_LCO_DIM_BACKLIGHT }, + { .v_als = AO_ADC_MAX / 3, .backlight = (AO_LCO_MAX_BACKLIGHT - AO_LCO_MIN_BACKLIGHT) / 2 }, + { .v_als = AO_ADC_MAX / 2, .backlight = AO_LCO_MAX_BACKLIGHT }, + { .v_als = AO_ADC_MAX * 3 / 4, .backlight = 0 }, +}; + +#define NUM_BACKLIGHT_MAP sizeof(ao_lco_backlight_map)/sizeof(ao_lco_backlight_map[0]) + +static unsigned ao_backlight_prev = NUM_BACKLIGHT_MAP - 1; + +static void +ao_auto_backlight(int16_t als_min, int16_t als_max) +{ + unsigned ao_backlight; + + PRINTD("ao_auto_backlight min %d max %d\n", als_min, als_max); + ao_backlight = ao_backlight_prev; + while (als_min > ao_lco_backlight_map[ao_backlight].v_als + AO_AUTO_BACKLIGHT_GAP) { + if (ao_backlight == NUM_BACKLIGHT_MAP - 1) + break; + ao_backlight++; + } + while (als_max < ao_lco_backlight_map[ao_backlight].v_als - AO_AUTO_BACKLIGHT_GAP) { + if (ao_backlight == 0) + return; + ao_backlight--; + } + if (ao_backlight != ao_backlight_prev) + { + PRINTD(" set backlight to %ld\n", ao_lco_backlight_map[ao_backlight].backlight); + ao_lco_set_backlight(ao_lco_backlight_map[ao_backlight].backlight); + ao_backlight_prev = ao_backlight; + } +} + +#define AO_LCO_BACKLIGHT_INTERVAL AO_SEC_TO_TICKS(2) + +static void +ao_lco_data(void) +{ + AO_TICK_TYPE backlight_tick = ao_time() + AO_LCO_BACKLIGHT_INTERVAL; + AO_TICK_TYPE now; + int16_t als_min = INT16_MAX; + int16_t als_max = INT16_MIN; + + ao_timer_set_adc_interval(AO_MS_TO_TICKS(100)); + for (;;) { + ao_sleep((void *) &ao_data_head); + + while (ao_sample_data != ao_data_head) { + struct ao_data *ao_data; + + /* Capture a sample */ + ao_data = (struct ao_data *) &ao_data_ring[ao_sample_data]; + + ao_data_cur = *ao_data; + if (ao_data_cur.adc.v_als < als_min) + als_min = ao_data_cur.adc.v_als; + if (ao_data_cur.adc.v_als > als_max) + als_max = ao_data_cur.adc.v_als; + ao_sample_data = ao_data_ring_next(ao_sample_data); + } + now = ao_time(); + if ((AO_TICK_SIGNED) (backlight_tick - now) < 0) { + backlight_tick = now + AO_LCO_BACKLIGHT_INTERVAL; + ao_auto_backlight(als_min, als_max); + als_min = INT16_MAX; + als_max = INT16_MIN; + } + } } void @@ -142,16 +325,31 @@ ao_lco_show(void) { ao_mutex_get(&ao_lco_display_mutex); ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY); - if (ao_lco_box == AO_LCO_LCO_VOLTAGE) { - _ao_lco_batt_voltage(); - } else if (ao_lco_box == AO_LCO_CONTRAST) { + switch (ao_lco_box) { +#if AO_LCO_HAS_CONTRAST + case AO_LCO_CONTRAST: _ao_lco_show_contrast(); - } else if (ao_lco_pad == AO_LCO_PAD_VOLTAGE) { - _ao_lco_show_voltage(ao_pad_query.battery, "Pad battery"); - } else { - _ao_lco_show_pad(ao_lco_pad); - _ao_lco_show_box(ao_lco_box); - ao_rect(&fb, SEP_X, 0, 2, HEIGHT, AO_BLACK, AO_COPY); + break; +#endif +#if AO_LCO_HAS_BACKLIGHT_UI + case AO_LCO_BACKLIGHT: + _ao_lco_show_backlight(); + break; +#endif + case AO_LCO_LCO_INFO: + _ao_lco_show_lco_info(); + break; + default: + switch (ao_lco_pad) { + case AO_LCO_PAD_INFO: + _ao_lco_show_pad_info(); + break; + default: + _ao_lco_show_pad(ao_lco_pad); + _ao_lco_show_box(ao_lco_box); + ao_rect(&fb, SEP_X, 0, SEP_WIDTH, HEIGHT, AO_BLACK, AO_COPY); + } + break; } ao_st7565_update(&fb); ao_mutex_put(&ao_lco_display_mutex); @@ -180,18 +378,37 @@ ao_lco_set_select(void) } +#if AO_LCO_HAS_CONTRAST void -ao_lco_set_contrast(int16_t contrast) +ao_lco_set_contrast(int32_t contrast) { ao_st7565_set_brightness((uint8_t) contrast); } -int16_t +int32_t ao_lco_get_contrast(void) { - return (int16_t) ao_st7565_get_brightness(); + return (int32_t) ao_st7565_get_brightness(); +} +#endif + +#if AO_LCO_HAS_BACKLIGHT +static uint16_t ao_backlight; + +void +ao_lco_set_backlight(int32_t backlight) +{ + ao_backlight = (uint16_t) backlight; + ao_pwm_set(AO_LCD_BL_PWM_CHAN, ao_backlight); } +int32_t +ao_lco_get_backlight(void) +{ + return (int32_t) ao_backlight; +} +#endif + static struct ao_task ao_lco_drag_task; static void @@ -287,23 +504,29 @@ ao_lco_display_test(void) ao_led_on(AO_LEDS_AVAILABLE); ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_BLACK, AO_COPY); ao_st7565_update(&fb); - ao_delay(AO_MS_TO_TICKS(250)); + ao_delay(AO_MS_TO_TICKS(1000)); ao_led_off(AO_LEDS_AVAILABLE); } static struct ao_task ao_lco_input_task; static struct ao_task ao_lco_monitor_task; +static struct ao_task ao_lco_data_task; static struct ao_task ao_lco_arm_warn_task; static struct ao_task ao_lco_igniter_status_task; -static int16_t found_x; +static int16_t found_width; +#define MAX_FOUND 32 +static int16_t found_boxes[MAX_FOUND]; +static uint8_t nfound; void ao_lco_search_start(void) { ao_rect(&fb, 0, 0, WIDTH, HEIGHT, AO_WHITE, AO_COPY); ao_logo(&fb, &logo_transform, &LOGO_FONT, AO_BLACK, AO_COPY); - found_x = FOUND_X; + _ao_center_text(SCANNING_X, SCANNING_Y, &TINY_FONT, "Scanning..."); + found_width = 0; + nfound = 0; } void @@ -318,11 +541,30 @@ void ao_lco_search_box_present(int16_t box) { char str[8]; - if (found_x < FOUND_WIDTH) + int16_t width; + int16_t box_top = FOUND_Y - TINY_FONT.ascent; + int16_t x; + uint8_t n; + + snprintf(str, sizeof(str), "%s%u", nfound ? ", " : "", box); + width = ao_text_width(&TINY_FONT, str); + while (found_width + width > FOUND_WIDTH || nfound == MAX_FOUND) { - snprintf(str, sizeof(str), "%s%02u", found_x ? ", " : "", box); - found_x = ao_text(&fb, &TINY_FONT, found_x, FOUND_Y, str, AO_BLACK, AO_COPY); + snprintf(str, sizeof(str), "%u, ", found_boxes[0]); + found_width -= ao_text_width(&TINY_FONT, str); + memmove(&found_boxes[0], &found_boxes[1], (nfound - 1) * sizeof (int16_t)); + nfound--; + } + found_boxes[nfound++] = box; + + ao_rect(&fb, FOUND_X, FOUND_Y - TINY_FONT.ascent, FOUND_WIDTH, HEIGHT - box_top, AO_WHITE, AO_COPY); + x = FOUND_X; + for (n = 0; n < nfound; n++) { + snprintf(str, sizeof(str), "%s%u", n ? ", " : "", found_boxes[n]); + int16_t next_x = ao_text(&fb, &TINY_FONT, x, FOUND_Y, str, AO_BLACK, AO_COPY); + x = next_x; } + found_width = x - FOUND_X; } void @@ -366,6 +608,7 @@ void ao_lco_init(void) { ao_add_task(&ao_lco_monitor_task, ao_lco_main, "lco monitor"); + ao_add_task(&ao_lco_data_task, ao_lco_data, "lco data"); #if DEBUG ao_cmd_register(&ao_lco_cmds[0]); #endif