diff --git a/Makefile b/Makefile index 9768975a..a0d56d1a 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,9 @@ GOSSAMER_PATH=gossamer # Which board are we building for? BOARD=sensorwatch_pro -# Which screen are we building for? -DISPLAY=CLASSIC +# Sensor Watch will detect the display, unless you are debugging over USB. +# If you need to force a specific display, set this to the type you want, CLASSIC or CUSTOM +# FORCE_DISPLAY_TYPE=CUSTOM # Which sensor board? SENSOR=NONE diff --git a/gossamer b/gossamer index ac3923bc..f323fa77 160000 --- a/gossamer +++ b/gossamer @@ -1 +1 @@ -Subproject commit ac3923bce85cd8ff2d44239b62f05a83f3580c4b +Subproject commit f323fa77643d0a52943c86ea8fc17d2d423cd931 diff --git a/littlefs b/littlefs index d01280e6..0494ce71 160000 --- a/littlefs +++ b/littlefs @@ -1 +1 @@ -Subproject commit d01280e64934a09ba16cac60cf9d3a37e228bb66 +Subproject commit 0494ce7169f06a734a7bd7585f49a9fa91fa7318 diff --git a/tinyusb b/tinyusb index a8903d31..29ffd572 160000 --- a/tinyusb +++ b/tinyusb @@ -1 +1 @@ -Subproject commit a8903d3152cbaa93d4d9d7e347dd09529d4ca887 +Subproject commit 29ffd57237554b1f2339af543e3789ae04d3b29b diff --git a/watch-faces/complication/advanced_alarm_face.c b/watch-faces/complication/advanced_alarm_face.c index a8662c83..39739bc4 100644 --- a/watch-faces/complication/advanced_alarm_face.c +++ b/watch-faces/complication/advanced_alarm_face.c @@ -46,11 +46,8 @@ static const uint8_t _blink_idx2[ALARM_SETTING_STATES] = {3, 1, 5, 7, 8, 9}; static const watch_buzzer_note_t _buzzer_notes[3] = {BUZZER_NOTE_B6, BUZZER_NOTE_C8, BUZZER_NOTE_A8}; // Volume is indicated by the three segments 5D, 5G and 5A -#ifdef USE_CUSTOM_LCD -static const uint8_t _buzzer_segdata[3][2] = {{1, 5}, {2, 5}, {3, 10}}; -#else -static const uint8_t _buzzer_segdata[3][2] = {{0, 3}, {1, 3}, {2, 2}}; -#endif +// This mapping is for classic LCD; if custom LCD is in use, we change it in the setup function. +static uint8_t _buzzer_segdata[3][2] = {{0, 3}, {1, 3}, {2, 2}}; static int8_t _wait_ticks; @@ -222,6 +219,15 @@ void advanced_alarm_face_setup(uint8_t watch_face_index, void **context_ptr) { } state->alarm_handled_minute = -1; _wait_ticks = -1; + + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + _buzzer_segdata[0][0] = 1; + _buzzer_segdata[0][1] = 5; + _buzzer_segdata[1][0] = 2; + _buzzer_segdata[1][1] = 5; + _buzzer_segdata[2][0] = 3; + _buzzer_segdata[2][1] = 10; + } } } diff --git a/watch-faces/demo/all_segments_face.c b/watch-faces/demo/all_segments_face.c index cc2be420..9364af30 100644 --- a/watch-faces/demo/all_segments_face.c +++ b/watch-faces/demo/all_segments_face.c @@ -33,14 +33,15 @@ void all_segments_face_setup(uint8_t watch_face_index, void ** context_ptr) { } void all_segments_face_activate(void *context) { -#ifdef USE_CUSTOM_LCD - uint8_t num_com = 4; -#else + (void) context; + watch_lcd_type_t lcd_type = watch_get_lcd_type(); uint8_t num_com = 3; -#endif uint8_t num_seg = 27 - num_com; - (void) context; + if (lcd_type == WATCH_LCD_TYPE_CUSTOM) { + num_com = 4; + } + for (int com = 0; com < num_com; com++) { for (int seg = 0; seg < num_seg; seg++) { watch_set_pixel(com, seg); diff --git a/watch-library/hardware/watch/watch_slcd.c b/watch-library/hardware/watch/watch_slcd.c index 04895f47..5379a5d7 100644 --- a/watch-library/hardware/watch/watch_slcd.c +++ b/watch-library/hardware/watch/watch_slcd.c @@ -26,6 +26,8 @@ #include "watch_slcd.h" #include "watch_common_display.h" #include "slcd.h" +#include "tc.h" +#include "adc.h" ////////////////////////////////////////////////////////////////////////////////////////// // Segmented Display @@ -33,7 +35,96 @@ static uint16_t _slcd_framerate = 0; static uint16_t _slcd_fc_min_ms_bypass = 0; +static watch_lcd_type_t _installed_display = WATCH_LCD_TYPE_UNKNOWN; + +void watch_discover_lcd_type(void) { + #if defined(FORCE_CUSTOM_LCD_TYPE) + _installed_display = WATCH_LCD_TYPE_CUSTOM; + goto valid_display_detected; + #elif defined(FORCE_CLASSIC_LCD_TYPE) + _installed_display = WATCH_LCD_TYPE_CLASSIC; + goto valid_display_detected; + #endif + + // Don't bother detecting the LCD type if we're plugged into USB. + if (usb_is_enabled()) return; + + uint16_t slcd3, slcd4; + int difference; + uint8_t valid_frames_classic = 0; + uint8_t valid_frames_custom = 0; + const uint16_t lo_threshold = 12000; + const uint16_t hi_threshold = 32000; + + // taking advantage of the fact that LCD segments are like little capacitors, + // we're going to introduce an alternating voltage on SLCD2, or COM2. + HAL_GPIO_SLCD2_out(); + + // Then we're going to read the voltage on SLCD3 and SLCD4: + // * On classic LCD, we will expect to see the voltage change on SLCD3 (SEG0) and SLCD4 (SEG1) + // * On custom LCD, we will expect to see the voltage change on SLCD4 (SEG1) but not SLCD3 (COM4) + adc_init(); + adc_enable(); + HAL_GPIO_SLCD3_pmuxen(HAL_GPIO_PMUX_ADC); + HAL_GPIO_SLCD4_pmuxen(HAL_GPIO_PMUX_ADC); + + /// TODO: Remove this and the watch_set_led_off below; it's here for testing (people will see red if their LCD was undetectable) + watch_set_led_red(); + + while(1) { + HAL_GPIO_SLCD2_set(); + slcd3 = adc_get_analog_value(HAL_GPIO_SLCD3_pin()); + slcd4 = adc_get_analog_value(HAL_GPIO_SLCD4_pin()); + difference = abs((int)slcd4 - (int)slcd3); + // printf("1, slcd3: %d, slcd4: %d, diff: %d\n", slcd3, slcd4, difference); + if (slcd3 > hi_threshold && slcd4 > hi_threshold && abs(difference) < 1000) { + valid_frames_classic++; + } else if (slcd4 > hi_threshold && abs(difference) > 5000) { + valid_frames_custom++; + } + delay_ms(4); + + HAL_GPIO_SLCD2_clr(); + slcd3 = adc_get_analog_value(HAL_GPIO_SLCD3_pin()); + slcd4 = adc_get_analog_value(HAL_GPIO_SLCD4_pin()); + difference = (int)slcd4 - (int)slcd3; + // printf("0, slcd3: %d, slcd4: %d, diff: %d\n", slcd3, slcd4, difference); + if (slcd3 < lo_threshold && slcd4 < lo_threshold && abs(difference) < 100) { + valid_frames_classic++; + } else if (slcd4 < lo_threshold && abs(difference) > 5000) { + valid_frames_custom++; + } + + if (valid_frames_classic > 16 || valid_frames_custom > 16) { + break; + } + delay_ms(4); + } + + watch_set_led_off(); + + HAL_GPIO_SLCD2_off(); + HAL_GPIO_SLCD3_off(); + HAL_GPIO_SLCD4_off(); + adc_disable(); + + if (valid_frames_classic > 16) { + _installed_display = WATCH_LCD_TYPE_CLASSIC; + } else if (valid_frames_custom > 16) { + _installed_display = WATCH_LCD_TYPE_CUSTOM; + } + +valid_display_detected: + _watch_update_indicator_segments(); +} + +watch_lcd_type_t watch_get_lcd_type(void) { + return _installed_display; +} + void watch_enable_display(void) { + watch_discover_lcd_type(); + HAL_GPIO_SLCD0_pmuxen(HAL_GPIO_PMUX_B); HAL_GPIO_SLCD1_pmuxen(HAL_GPIO_PMUX_B); HAL_GPIO_SLCD2_pmuxen(HAL_GPIO_PMUX_B); @@ -62,26 +153,28 @@ void watch_enable_display(void) { HAL_GPIO_SLCD25_pmuxen(HAL_GPIO_PMUX_B); HAL_GPIO_SLCD26_pmuxen(HAL_GPIO_PMUX_B); -#ifdef USE_CUSTOM_LCD - // Original famous Casio LCD: 1/3 bias, 1/4 duty with a frame rate of 32 Hz - slcd_init(LCD_PIN_ENABLE, SLCD_BIAS_THIRD, SLCD_DUTY_4_COMMON, SLCD_CLOCKSOURCE_XOSC, SLCD_PRESCALER_DIV64, SLCD_CLOCKDIV_4); - // exact frame rate is: 32768 / (4 * 64 * 4) ≈ 32 Hz - _slcd_framerate = 32; -#else - // Original famous Casio LCD: 1/3 bias, 1/3 duty with a frame rate of ~34 Hz - slcd_init(LCD_PIN_ENABLE, SLCD_BIAS_THIRD, SLCD_DUTY_3_COMMON, SLCD_CLOCKSOURCE_XOSC, SLCD_PRESCALER_DIV64, SLCD_CLOCKDIV_5); - // exact frame rate is: 32768 / (3 * 64 * 5) ≈ 34.13 Hz - _slcd_framerate = 34; -#endif + if (_installed_display == WATCH_LCD_TYPE_CUSTOM) { + // Custom LCD: 1/3 bias, 1/4 duty with a frame rate of 32 Hz + slcd_init(LCD_PIN_ENABLE, SLCD_BIAS_THIRD, SLCD_DUTY_4_COMMON, SLCD_CLOCKSOURCE_XOSC, SLCD_PRESCALER_DIV64, SLCD_CLOCKDIV_4); + // exact frame rate is: 32768 / (4 * 64 * 4) ≈ 32 Hz + _slcd_framerate = 32; + } else { + // Original famous Casio LCD: 1/3 bias, 1/3 duty with a frame rate of ~34 Hz + slcd_init(LCD_PIN_ENABLE, SLCD_BIAS_THIRD, SLCD_DUTY_3_COMMON, SLCD_CLOCKSOURCE_XOSC, SLCD_PRESCALER_DIV64, SLCD_CLOCKDIV_5); + // exact frame rate is: 32768 / (3 * 64 * 5) ≈ 34.13 Hz + _slcd_framerate = 34; + } // calculate the smallest duration we can time before we have to engage the frame counter prescaler bypass _slcd_fc_min_ms_bypass = 32 * (1000 / _slcd_framerate); slcd_clear(); -#ifdef USE_CUSTOM_LCD - slcd_set_contrast(6); -#else - slcd_set_contrast(9); -#endif + + if (_installed_display == WATCH_LCD_TYPE_CUSTOM) { + slcd_set_contrast(6); + } else { + slcd_set_contrast(9); + } + slcd_enable(); } @@ -118,43 +211,41 @@ void watch_start_character_blink(char character, uint32_t duration) { } void watch_start_indicator_blink_if_possible(watch_indicator_t indicator, uint32_t duration) { -#ifdef USE_CUSTOM_LCD - uint8_t mask = 0; - switch (indicator) { - case WATCH_INDICATOR_COLON: - mask = 0b0001; - break; - case WATCH_INDICATOR_LAP: - mask = 0b0010; - break; - case WATCH_INDICATOR_BATTERY: - mask = 0b0100; - break; - case WATCH_INDICATOR_SLEEP: - mask = 0b1000; - break; - default: - return; + if (_installed_display == WATCH_LCD_TYPE_CUSTOM) { + // Indicators can only blink on the custom LCD. + uint8_t mask = 0; + switch (indicator) { + case WATCH_INDICATOR_COLON: + mask = 0b0001; + break; + case WATCH_INDICATOR_LAP: + mask = 0b0010; + break; + case WATCH_INDICATOR_BATTERY: + mask = 0b0100; + break; + case WATCH_INDICATOR_SLEEP: + mask = 0b1000; + break; + default: + return; + } + watch_set_indicator(indicator); + + if (duration <= _slcd_fc_min_ms_bypass) { + slcd_configure_frame_counter(0, (duration / (1000 / _slcd_framerate)) - 1, false); + } else { + slcd_configure_frame_counter(0, ((duration / (1000 / _slcd_framerate)) / 8 - 1), true); + } + slcd_set_frame_counter_enabled(0, true); + + + slcd_disable(); + slcd_set_blink_enabled(false); + slcd_configure_blink(false, mask, 0, 0); + slcd_set_blink_enabled(true); + slcd_enable(); } - watch_set_indicator(indicator); - - if (duration <= _slcd_fc_min_ms_bypass) { - slcd_configure_frame_counter(0, (duration / (1000 / _slcd_framerate)) - 1, false); - } else { - slcd_configure_frame_counter(0, ((duration / (1000 / _slcd_framerate)) / 8 - 1), true); - } - slcd_set_frame_counter_enabled(0, true); - - - slcd_disable(); - slcd_set_blink_enabled(false); - slcd_configure_blink(false, mask, 0, 0); - slcd_set_blink_enabled(true); - slcd_enable(); -#else - (void) indicator; - (void) duration; -#endif } void watch_stop_blink(void) { @@ -163,28 +254,29 @@ void watch_stop_blink(void) { } void watch_start_sleep_animation(uint32_t duration) { -#ifdef USE_CUSTOM_LCD - (void) duration; - watch_set_indicator(WATCH_INDICATOR_SLEEP); -#else - watch_display_character(' ', 8); - watch_display_character(' ', 9); - - slcd_disable(); - slcd_set_frame_counter_enabled(1, false); - slcd_set_circular_shift_animation_enabled(false); - - if (duration <= _slcd_fc_min_ms_bypass) { - slcd_configure_frame_counter(1, (duration / (1000 / _slcd_framerate)) - 1, false); + if (_installed_display == WATCH_LCD_TYPE_CUSTOM) { + // on pro LCD, we just show the sleep indicator + watch_set_indicator(WATCH_INDICATOR_SLEEP); } else { - slcd_configure_frame_counter(1, ((duration / (1000 / _slcd_framerate)) / 8 - 1), true); - } - slcd_set_frame_counter_enabled(1, true); + // on classic LCD we do the "tick/tock" animation + watch_display_character(' ', 8); + watch_display_character(' ', 9); - slcd_configure_circular_shift_animation(0b00000001, 1, SLCD_CSRSHIFT_LEFT, 1); - slcd_set_circular_shift_animation_enabled(true); - slcd_enable(); -#endif + slcd_disable(); + slcd_set_frame_counter_enabled(1, false); + slcd_set_circular_shift_animation_enabled(false); + + if (duration <= _slcd_fc_min_ms_bypass) { + slcd_configure_frame_counter(1, (duration / (1000 / _slcd_framerate)) - 1, false); + } else { + slcd_configure_frame_counter(1, ((duration / (1000 / _slcd_framerate)) / 8 - 1), true); + } + slcd_set_frame_counter_enabled(1, true); + + slcd_configure_circular_shift_animation(0b00000001, 1, SLCD_CSRSHIFT_LEFT, 1); + slcd_set_circular_shift_animation_enabled(true); + slcd_enable(); + } } bool watch_sleep_animation_is_running(void) { @@ -193,10 +285,10 @@ bool watch_sleep_animation_is_running(void) { } void watch_stop_sleep_animation(void) { -#ifdef USE_CUSTOM_LCD - watch_clear_indicator(WATCH_INDICATOR_SLEEP); -#else - slcd_set_circular_shift_animation_enabled(false); - watch_display_character(' ', 8); -#endif + if (_installed_display == WATCH_LCD_TYPE_CUSTOM) { + watch_clear_indicator(WATCH_INDICATOR_SLEEP); + } else { + slcd_set_circular_shift_animation_enabled(false); + watch_display_character(' ', 8); + } } diff --git a/watch-library/shared/watch/watch_common_display.c b/watch-library/shared/watch/watch_common_display.c index 312705cd..f4133c45 100644 --- a/watch-library/shared/watch/watch_common_display.c +++ b/watch-library/shared/watch/watch_common_display.c @@ -29,75 +29,63 @@ #include #include -#ifdef USE_CUSTOM_LCD -static const uint32_t IndicatorSegments[] = { - SLCD_SEGID(0, 21), // WATCH_INDICATOR_SIGNAL - SLCD_SEGID(1, 21), // WATCH_INDICATOR_BELL - SLCD_SEGID(3, 21), // WATCH_INDICATOR_PM - SLCD_SEGID(2, 21), // WATCH_INDICATOR_24H - SLCD_SEGID(1, 0), // WATCH_INDICATOR_LAP - SLCD_SEGID(2, 0), // WATCH_INDICATOR_BATTERY - SLCD_SEGID(3, 0), // WATCH_INDICATOR_SLEEP -}; -#else -static const uint32_t IndicatorSegments[] = { - SLCD_SEGID(0, 17), // WATCH_INDICATOR_SIGNAL - SLCD_SEGID(0, 16), // WATCH_INDICATOR_BELL - SLCD_SEGID(2, 17), // WATCH_INDICATOR_PM - SLCD_SEGID(2, 16), // WATCH_INDICATOR_24H - SLCD_SEGID(1, 10), // WATCH_INDICATOR_LAP - - // Aliases for indicators unavailable on the original F-91W LCD - SLCD_SEGID(1, 10), // WATCH_INDICATOR_BATTERY (same as LAP) - SLCD_SEGID(4, 0), // WATCH_INDICATOR_SLEEP (does not exist, will set in SDATAL4 which is harmless) -}; - -#endif +uint32_t IndicatorSegments[7] = {0}; void watch_display_character(uint8_t character, uint8_t position) { -#ifdef USE_CUSTOM_LCD - if (character == 'R') character = 'r'; // We can't display uppercase R on this display. - else if (character == 'T' && position > 1 && position < 8) character = 't'; // lowercase t is the only option for these positions -#else - // special cases for positions 4 and 6 - if (position == 4 || position == 6) { - if (character == '7') character = '&'; // "lowercase" 7 - else if (character == 'A') character = 'a'; // A needs to be lowercase - else if (character == 'o') character = 'O'; // O needs to be uppercase - else if (character == 'L') character = '!'; // L needs to be in top half - else if (character == 'M' || character == 'm' || character == 'N') character = 'n'; // M and uppercase N need to be lowercase n - else if (character == 'c') character = 'C'; // C needs to be uppercase - else if (character == 'J') character = 'j'; // same - else if (character == 'v' || character == 'V' || character == 'U' || character == 'W' || character == 'w') character = 'u'; // bottom segment duplicated, so show in top half + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + if (character == 'R') character = 'r'; // We can't display uppercase R on this display. + else if (character == 'T' && position > 1 && position < 8) character = 't'; // lowercase t is the only option for these positions } else { - if (character == 'u') character = 'v'; // we can use the bottom segment; move to lower half - else if (character == 'j') character = 'J'; // same but just display a normal J - else if (character == '.') character = '_'; // we can use the bottom segment; make dot an underscore + // special cases for positions 4 and 6 + if (position == 4 || position == 6) { + if (character == '7') character = '&'; // "lowercase" 7 + else if (character == 'A') character = 'a'; // A needs to be lowercase + else if (character == 'o') character = 'O'; // O needs to be uppercase + else if (character == 'L') character = '!'; // L needs to be in top half + else if (character == 'M' || character == 'm' || character == 'N') character = 'n'; // M and uppercase N need to be lowercase n + else if (character == 'c') character = 'C'; // C needs to be uppercase + else if (character == 'J') character = 'j'; // same + else if (character == 'v' || character == 'V' || character == 'U' || character == 'W' || character == 'w') character = 'u'; // bottom segment duplicated, so show in top half + } else { + if (character == 'u') character = 'v'; // we can use the bottom segment; move to lower half + else if (character == 'j') character = 'J'; // same but just display a normal J + else if (character == '.') character = '_'; // we can use the bottom segment; make dot an underscore + } + if (position > 1) { + if (character == 'T') character = 't'; // uppercase T only works in positions 0 and 1 + } + if (position == 1) { + if (character == 'a') character = 'A'; // A needs to be uppercase + else if (character == 'o') character = 'O'; // O needs to be uppercase + else if (character == 'i') character = 'l'; // I needs to be uppercase (use an l, it looks the same) + else if (character == 'n') character = 'N'; // N needs to be uppercase + else if (character == 'r') character = 'R'; // R needs to be uppercase + else if (character == 'd') character = 'D'; // D needs to be uppercase + else if (character == 'v' || character == 'V' || character == 'u') character = 'U'; // side segments shared, make uppercase + else if (character == 'b') character = 'B'; // B needs to be uppercase + else if (character == 'c') character = 'C'; // C needs to be uppercase + } else { + if (character == 'R') character = 'r'; // R needs to be lowercase almost everywhere + } + if (position == 0) { + watch_clear_pixel(0, 15); // clear funky ninth segment + } else { + if (character == 'I') character = 'l'; // uppercase I only works in position 0 + } } - if (position > 1) { - if (character == 'T') character = 't'; // uppercase T only works in positions 0 and 1 - } - if (position == 1) { - if (character == 'a') character = 'A'; // A needs to be uppercase - else if (character == 'o') character = 'O'; // O needs to be uppercase - else if (character == 'i') character = 'l'; // I needs to be uppercase (use an l, it looks the same) - else if (character == 'n') character = 'N'; // N needs to be uppercase - else if (character == 'r') character = 'R'; // R needs to be uppercase - else if (character == 'd') character = 'D'; // D needs to be uppercase - else if (character == 'v' || character == 'V' || character == 'u') character = 'U'; // side segments shared, make uppercase - else if (character == 'b') character = 'B'; // B needs to be uppercase - else if (character == 'c') character = 'C'; // C needs to be uppercase + + digit_mapping_t segmap; + uint8_t segdata; + + /// TODO: This could be optimized by doing this check once and setting a pointer in watch_discover_lcd_type. + + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + segmap = Custom_LCD_Display_Mapping[position]; + segdata = Custom_LCD_Character_Set[character - 0x20]; } else { - if (character == 'R') character = 'r'; // R needs to be lowercase almost everywhere + segmap = Classic_LCD_Display_Mapping[position]; + segdata = Classic_LCD_Character_Set[character - 0x20]; } - if (position == 0) { - watch_clear_pixel(0, 15); // clear funky ninth segment - } else { - if (character == 'I') character = 'l'; // uppercase I only works in position 0 - } -#endif - digit_mapping_t segmap = Watch_Display_Mapping[position]; - uint8_t segdata = Watch_Character_Set[character - 0x20]; for (int i = 0; i < 8; i++) { if (segmap.segment[i].value == segment_does_not_exist) { @@ -126,8 +114,18 @@ void watch_display_character(uint8_t character, uint8_t position) { void watch_display_character_lp_seconds(uint8_t character, uint8_t position) { // Will only work for digits and for positions 8 and 9 - but less code & checks to reduce power consumption - digit_mapping_t segmap = Watch_Display_Mapping[position]; - uint8_t segdata = Watch_Character_Set[character - 0x20]; + digit_mapping_t segmap; + uint8_t segdata; + + /// TODO: See optimization note above. + + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + segmap = Custom_LCD_Display_Mapping[position]; + segdata = Custom_LCD_Character_Set[character - 0x20]; + } else { + segmap = Classic_LCD_Display_Mapping[position]; + segdata = Classic_LCD_Character_Set[character - 0x20]; + } for (int i = 0; i < 8; i++) { if (segmap.segment[i].value == segment_does_not_exist) { @@ -175,15 +173,15 @@ void watch_display_text(watch_position_t location, const char *string) { break; case WATCH_POSITION_BOTTOM: { - #ifdef USE_CUSTOM_LCD - watch_clear_pixel(0, 22); - #endif + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + watch_clear_pixel(0, 22); + } int i = 0; while (string[i] != 0) { watch_display_character(string[i], 4 + i); i++; } - } + } break; case WATCH_POSITION_HOURS: watch_display_character(string[0], 4); @@ -209,67 +207,64 @@ void watch_display_text(watch_position_t location, const char *string) { #pragma GCC diagnostic ignored "-Wdeprecated-declarations" watch_display_string(string, 0); #pragma GCC diagnostic pop - #ifdef USE_CUSTOM_LCD - watch_display_character(' ', 10); - #endif + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + watch_display_character(' ', 10); + } } } void watch_display_text_with_fallback(watch_position_t location, const char *string, const char *fallback) { -#ifdef USE_CUSTOM_LCD - (void)fallback; - - switch (location) { - case WATCH_POSITION_TOP: - for (size_t i = 0; i < strlen(string); i++) { - if (i < 2) watch_display_character(string[i], i); - else if (i == 2) watch_display_character(string[i], 10); - else if (i < 5) watch_display_character(string[i], i - 1); - else break; - } - break; - case WATCH_POSITION_TOP_LEFT: - watch_display_character(string[0], 0); - if (string[1]) { - watch_display_character(string[1], 1); - } else { - return; - } - if (string[2]) { - // position 3 is at index 10 in the display mapping - watch_display_character(string[2], 10); - } - break; - case WATCH_POSITION_BOTTOM: - { - watch_clear_pixel(0, 22); - int i = 0; - int offset = 0; - size_t len = strlen(string); - if (len == 7 && string[0] == '1') { - watch_set_pixel(0, 22); - offset = 1; - i++; - } - while (string[i] != 0) { - if (4 + i - offset == 10) break; - watch_display_character(string[i], 4 + i - offset); - i++; + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + switch (location) { + case WATCH_POSITION_TOP: + for (size_t i = 0; i < strlen(string); i++) { + if (i < 2) watch_display_character(string[i], i); + else if (i == 2) watch_display_character(string[i], 10); + else if (i < 5) watch_display_character(string[i], i - 1); + else break; + } + break; + case WATCH_POSITION_TOP_LEFT: + watch_display_character(string[0], 0); + if (string[1]) { + watch_display_character(string[1], 1); + } else { + return; + } + if (string[2]) { + // position 3 is at index 10 in the display mapping + watch_display_character(string[2], 10); + } + break; + case WATCH_POSITION_BOTTOM: + { + watch_clear_pixel(0, 22); + int i = 0; + int offset = 0; + size_t len = strlen(string); + if (len == 7 && string[0] == '1') { + watch_set_pixel(0, 22); + offset = 1; + i++; + } + while (string[i] != 0) { + if (4 + i - offset == 10) break; + watch_display_character(string[i], 4 + i - offset); + i++; + } } + break; + case WATCH_POSITION_TOP_RIGHT: + case WATCH_POSITION_HOURS: + case WATCH_POSITION_MINUTES: + case WATCH_POSITION_SECONDS: + case WATCH_POSITION_FULL: + watch_display_text(location, string); + break; } - break; - case WATCH_POSITION_TOP_RIGHT: - case WATCH_POSITION_HOURS: - case WATCH_POSITION_MINUTES: - case WATCH_POSITION_SECONDS: - case WATCH_POSITION_FULL: - watch_display_text(location, string); - break; + } else { + watch_display_text(location, fallback); } -#else - (void)string; - watch_display_text(location, fallback); -#endif } void watch_display_float_with_best_effort(float value, const char *units) { @@ -320,31 +315,31 @@ void watch_display_float_with_best_effort(float value, const char *units) { } void watch_set_colon(void) { -#ifdef USE_CUSTOM_LCD - watch_set_pixel(0, 0); -#else - watch_set_pixel(1, 16); -#endif + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + watch_set_pixel(0, 0); + } else { + watch_set_pixel(1, 16); + } } void watch_clear_colon(void) { -#ifdef USE_CUSTOM_LCD - watch_clear_pixel(0, 0); -#else - watch_clear_pixel(1, 16); -#endif + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + watch_clear_pixel(0, 0); + } else { + watch_clear_pixel(1, 16); + } } void watch_set_decimal_if_available(void) { -#ifdef USE_CUSTOM_LCD - watch_set_pixel(0, 14); -#endif + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + watch_set_pixel(0, 14); + } } void watch_clear_decimal_if_available(void) { -#ifdef USE_CUSTOM_LCD - watch_clear_pixel(0, 14); -#endif + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + watch_clear_pixel(0, 14); + } } void watch_set_indicator(watch_indicator_t indicator) { @@ -371,3 +366,24 @@ void watch_clear_all_indicators(void) { watch_clear_indicator(WATCH_INDICATOR_BATTERY); watch_clear_indicator(WATCH_INDICATOR_SLEEP); } + +void _watch_update_indicator_segments(void) { + if (watch_get_lcd_type() == WATCH_LCD_TYPE_CUSTOM) { + IndicatorSegments[0] = SLCD_SEGID(0, 21); // WATCH_INDICATOR_SIGNAL + IndicatorSegments[1] = SLCD_SEGID(1, 21); // WATCH_INDICATOR_BELL + IndicatorSegments[2] = SLCD_SEGID(3, 21); // WATCH_INDICATOR_PM + IndicatorSegments[3] = SLCD_SEGID(2, 21); // WATCH_INDICATOR_24H + IndicatorSegments[4] = SLCD_SEGID(1, 0); // WATCH_INDICATOR_LAP + IndicatorSegments[5] = SLCD_SEGID(2, 0); // WATCH_INDICATOR_BATTERY + IndicatorSegments[6] = SLCD_SEGID(3, 0); // WATCH_INDICATOR_SLEEP + } else { + IndicatorSegments[0] = SLCD_SEGID(0, 17); // WATCH_INDICATOR_SIGNAL + IndicatorSegments[1] = SLCD_SEGID(0, 16); // WATCH_INDICATOR_BELL + IndicatorSegments[2] = SLCD_SEGID(2, 17); // WATCH_INDICATOR_PM + IndicatorSegments[3] = SLCD_SEGID(2, 16); // WATCH_INDICATOR_24H + IndicatorSegments[4] = SLCD_SEGID(1, 10); // WATCH_INDICATOR_LAP + // Aliases for indicators unavailable on the original F-91W LCD + IndicatorSegments[5] = SLCD_SEGID(1, 10); // WATCH_INDICATOR_BATTERY (same as LAP) + IndicatorSegments[6] = SLCD_SEGID(4, 0); // WATCH_INDICATOR_SLEEP (does not exist, will set in SDATAL4 which is harmless) + } +} \ No newline at end of file diff --git a/watch-library/shared/watch/watch_common_display.h b/watch-library/shared/watch/watch_common_display.h index eb404224..2f8c6d9a 100644 --- a/watch-library/shared/watch/watch_common_display.h +++ b/watch-library/shared/watch/watch_common_display.h @@ -42,11 +42,10 @@ typedef union digit_mapping_t { uint64_t value; } digit_mapping_t; -#ifdef USE_CUSTOM_LCD // Custom extended LCD // Character set is slightly different since we don't have to work around as much stuff. -static const uint8_t Watch_Character_Set[] = +static const uint8_t Custom_LCD_Character_Set[] = { 0b00000000, // [space] 0b00000000, // ! (unused) @@ -145,7 +144,7 @@ static const uint8_t Watch_Character_Set[] = 0b00000001, // ~ }; -static const digit_mapping_t Watch_Display_Mapping[] = { +static const digit_mapping_t Custom_LCD_Display_Mapping[] = { { .segment = { { .address = { .com = 0, .seg = 19 } }, // 0A @@ -281,10 +280,8 @@ static const digit_mapping_t Watch_Display_Mapping[] = { }, }; -#else - // Original famous Casio LCD -static const uint8_t Watch_Character_Set[] = +static const uint8_t Classic_LCD_Character_Set[] = { 0b00000000, // [space] 0b01100000, // ! (L in the top half for positions 4 and 6) @@ -383,7 +380,7 @@ static const uint8_t Watch_Character_Set[] = 0b00000001, // ~ }; -static const digit_mapping_t Watch_Display_Mapping[] = { +static const digit_mapping_t Classic_LCD_Display_Mapping[] = { // Positions 0 and 1 are the Weekday or Mode digits { .segment = { @@ -508,7 +505,8 @@ static const digit_mapping_t Watch_Display_Mapping[] = { }, }, }; -#endif void watch_display_character(uint8_t character, uint8_t position); void watch_display_character_lp_seconds(uint8_t character, uint8_t position); + +void _watch_update_indicator_segments(void); diff --git a/watch-library/shared/watch/watch_slcd.h b/watch-library/shared/watch/watch_slcd.h index 1c69d2fb..76ba34ee 100644 --- a/watch-library/shared/watch/watch_slcd.h +++ b/watch-library/shared/watch/watch_slcd.h @@ -74,6 +74,25 @@ typedef enum { WATCH_POSITION_SECONDS, ///< Display 2 characters in the seconds portion of the main line. } watch_position_t; +/// an enum describing the possible LCD types +typedef enum { + WATCH_LCD_TYPE_UNKNOWN = 0, ///< Value at boot: unknown LCD + WATCH_LCD_TYPE_CLASSIC = 0b10101001, ///< The original famous F-91W LCD + WATCH_LCD_TYPE_CUSTOM = 0b01010110, ///< The custom Oddly Specific LCD +} watch_lcd_type_t; + +/** @brief Determines the type of LCD being used by the watch. + * @return 0 for the original F-91W LCD, or 1 for the new custom LCD. + */ + void watch_discover_lcd_type(void); + +/** + * @brief Gets the type of LCD being used by the watch. + * @return The type of LCD in use, or WATCH_LCD_TYPE_UNKNOWN if the display is unknown. + * The display type will be unknown if the watch is plugged into USB. + */ + watch_lcd_type_t watch_get_lcd_type(void); + /** @brief Enables the Segment LCD display. * Call this before attempting to set pixels or display strings. */