]> git.earman.xyz Git - sensor-watch.git/commitdiff
display glass autodetection
authorJoey Castillo <joeycastillo@utexas.edu>
Sat, 8 Mar 2025 18:43:11 +0000 (13:43 -0500)
committerJoey Castillo <joeycastillo@utexas.edu>
Sat, 8 Mar 2025 18:43:11 +0000 (13:43 -0500)
Makefile
gossamer
littlefs
tinyusb
watch-faces/complication/advanced_alarm_face.c
watch-faces/demo/all_segments_face.c
watch-library/hardware/watch/watch_slcd.c
watch-library/shared/watch/watch_common_display.c
watch-library/shared/watch/watch_common_display.h
watch-library/shared/watch/watch_slcd.h

index 9768975ae75bdb23d283c6772e50e9aa6ab5381a..a0d56d1a89b200c25d92e3c3f76b5274a71400ed 100644 (file)
--- 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
index ac3923bce85cd8ff2d44239b62f05a83f3580c4b..f323fa77643d0a52943c86ea8fc17d2d423cd931 160000 (submodule)
--- a/gossamer
+++ b/gossamer
@@ -1 +1 @@
-Subproject commit ac3923bce85cd8ff2d44239b62f05a83f3580c4b
+Subproject commit f323fa77643d0a52943c86ea8fc17d2d423cd931
index d01280e64934a09ba16cac60cf9d3a37e228bb66..0494ce7169f06a734a7bd7585f49a9fa91fa7318 160000 (submodule)
--- a/littlefs
+++ b/littlefs
@@ -1 +1 @@
-Subproject commit d01280e64934a09ba16cac60cf9d3a37e228bb66
+Subproject commit 0494ce7169f06a734a7bd7585f49a9fa91fa7318
diff --git a/tinyusb b/tinyusb
index a8903d3152cbaa93d4d9d7e347dd09529d4ca887..29ffd57237554b1f2339af543e3789ae04d3b29b 160000 (submodule)
--- a/tinyusb
+++ b/tinyusb
@@ -1 +1 @@
-Subproject commit a8903d3152cbaa93d4d9d7e347dd09529d4ca887
+Subproject commit 29ffd57237554b1f2339af543e3789ae04d3b29b
index a8662c83ff2b843ef4ae22ed1f0e2a5ee839f400..39739bc4e5b1bbacd464426f4d70a7fda3158f4b 100644 (file)
@@ -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;
+        }
     }
 }
 
index cc2be420962758fcadfd679e8bb3b13c971e2fe4..9364af302b676429e809556bde11f4d620d09c30 100644 (file)
@@ -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);
index 04895f472c5a7ec1f698544c117709fbf5dcdbec..5379a5d7a66dda417a91fd2190195f99e55e34d2 100644 (file)
@@ -26,6 +26,8 @@
 #include "watch_slcd.h"
 #include "watch_common_display.h"
 #include "slcd.h"
+#include "tc.h"
+#include "adc.h"
 
  //////////////////////////////////////////////////////////////////////////////////////////
 // Segmented Display
 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;
-    }
-    watch_set_indicator(indicator);
+    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);
+        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
+        slcd_disable();
+        slcd_set_blink_enabled(false);
+        slcd_configure_blink(false, mask, 0, 0);
+        slcd_set_blink_enabled(true);
+        slcd_enable();
+    }
 }
 
 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);
+    if (_installed_display == WATCH_LCD_TYPE_CUSTOM) {
+        // on pro LCD, we just show the sleep indicator
+        watch_set_indicator(WATCH_INDICATOR_SLEEP);
+    } else {
+        // on classic LCD we do the "tick/tock" animation
+        watch_display_character(' ', 8);
+        watch_display_character(' ', 9);
 
-    slcd_disable();
-    slcd_set_frame_counter_enabled(1, false);
-    slcd_set_circular_shift_animation_enabled(false);
+        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);
+        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();
-#endif
+        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);
+    }
 }
index 312705cdf8b9d6045101e5cddef4ecd458b81a8c..f4133c45786ff30cec27d4a4d113c11704c82d99 100644 (file)
 #include <stdlib.h>
 #include <math.h>
 
-#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
-    }
-    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
+        // 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 == 0) {
-        watch_clear_pixel(0, 15); // clear funky ninth segment
+
+    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 == 'I') character = 'l'; // uppercase I only works in position 0
+        segmap = Classic_LCD_Display_Mapping[position];
+        segdata = Classic_LCD_Character_Set[character - 0x20];
     }
-#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
index eb404224583fa64c4976500e5da03752f8afc02b..2f8c6d9a496e655fe272d64f8fc10a2b6d7b3e4a 100644 (file)
@@ -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);
index 1c69d2fbe4249d8a28d765e96871620b9512f43c..76ba34ee5fdce2920edb7151d64845c64f70e12e 100644 (file)
@@ -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.
   */