Skip to content

Commit

Permalink
Expose character mapping internals and add invert
Browse files Browse the repository at this point in the history
Allowing the faces to access the internal segment mapping code makes
it easier to have custom characters and have fancy effects.
  • Loading branch information
wryun committed Jul 20, 2024
1 parent 5ec3dca commit e9d9800
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 61 deletions.
4 changes: 2 additions & 2 deletions movement/watch_faces/clock/minute_repeater_decimal_face.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ bool minute_repeater_decimal_face_loop(movement_event_t event, movement_settings

if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
// everything before seconds is the same, don't waste cycles setting those segments.
watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
watch_display_character_lp('0' + date_time.unit.second / 10, 8);
watch_display_character_lp('0' + date_time.unit.second % 10, 9);
break;
} else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
// everything before minutes is the same.
Expand Down
4 changes: 2 additions & 2 deletions movement/watch_faces/clock/repetition_minute_face.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ bool repetition_minute_face_loop(movement_event_t event, movement_settings_t *se

if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
// everything before seconds is the same, don't waste cycles setting those segments.
watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
watch_display_character_lp('0' + date_time.unit.second / 10, 8);
watch_display_character_lp('0' + date_time.unit.second % 10, 9);
break;
} else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
// everything before minutes is the same.
Expand Down
4 changes: 2 additions & 2 deletions movement/watch_faces/clock/simple_clock_bin_led_face.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ bool simple_clock_bin_led_face_loop(movement_event_t event, movement_settings_t

if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
// everything before seconds is the same, don't waste cycles setting those segments.
watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
watch_display_character_lp('0' + date_time.unit.second / 10, 8);
watch_display_character_lp('0' + date_time.unit.second % 10, 9);
break;
} else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
// everything before minutes is the same.
Expand Down
4 changes: 2 additions & 2 deletions movement/watch_faces/clock/simple_clock_face.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting

if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
// everything before seconds is the same, don't waste cycles setting those segments.
watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
watch_display_character_lp('0' + date_time.unit.second / 10, 8);
watch_display_character_lp('0' + date_time.unit.second % 10, 9);
break;
} else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
// everything before minutes is the same.
Expand Down
105 changes: 57 additions & 48 deletions watch-library/shared/watch/watch_private_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ static const uint32_t IndicatorSegments[] = {
SLCD_SEGID(1, 10), // WATCH_INDICATOR_LAP
};

void watch_display_character(uint8_t character, uint8_t position) {
static bool invert = false;

uint8_t watch_convert_char_to_segdata(uint8_t character, uint8_t position) {
// special cases for positions 4 and 6
if (position == 4 || position == 6) {
if (character == '7') character = '&'; // "lowercase" 7
Expand Down Expand Up @@ -64,65 +66,72 @@ void watch_display_character(uint8_t character, uint8_t position) {
} 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 (position != 0) {
if (character == 'I') character = 'l'; // uppercase I only works in position 0
}

uint64_t segmap = Segment_Map[position];
uint64_t segdata = Character_Set[character - 0x20];
return Character_Set[character - 0x20];
}

for (int i = 0; i < 8; i++) {
uint8_t com = (segmap & 0xFF) >> 6;
if (com > 2) {
// COM3 means no segment exists; skip it.
segmap = segmap >> 8;
segdata = segdata >> 1;
continue;
}
uint8_t seg = segmap & 0x3F;

if (segdata & 1)
watch_set_pixel(com, seg);
else
watch_clear_pixel(com, seg);

segmap = segmap >> 8;
segdata = segdata >> 1;
}
uint8_t watch_convert_char_to_segdata_lp(uint8_t character) {
// i.e. remove any special casing cf watch_convert_char_to_segdata
return Character_Set[character - 0x20];
}

void watch_display_character(uint8_t character, uint8_t position) {
watch_display_segdata(watch_convert_char_to_segdata(character, position), position);

// Handle the segments that are not in our usual map at all.
if (position == 0) {
if (character == 'B' || character == 'D' || character == '@') watch_set_pixel(0, 15); // bottom left serif
else watch_clear_pixel(0, 15);

} else if (position == 1) {
if (character == 'T') watch_set_pixel(1, 12); // add descender (left hand side vertical line)

if (character == 'T' && position == 1) watch_set_pixel(1, 12); // add descender
else if (position == 0 && (character == 'B' || character == 'D' || character == '@')) watch_set_pixel(0, 15); // add funky ninth segment
else if (position == 1 && (character == 'B' || character == 'D' || character == '@')) watch_set_pixel(0, 12); // add funky ninth segment
if (character == 'B' || character == 'D' || character == '@') watch_set_pixel(0, 12); // top left serif
else watch_clear_pixel(0, 12);
}
}

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
// Allow us to rotate characters 180 degrees (see watch_private_display.h for bit position diagram).
static const uint8_t vert_invert_map[8] = {
3, 4, 5, 0, 1, 2, 6, 7,
};

void watch_display_segment(uint8_t bit_pos, uint8_t position, bool on) {
uint64_t segmap = Segment_Map[position] >> (8 * bit_pos);
uint8_t com = (segmap & 0xFF) >> 6;

uint64_t segmap = Segment_Map[position];
uint64_t segdata = Character_Set[character - 0x20];
if (com > 2) {
// COM3 means no segment exists; skip it.
return;
}

uint8_t seg = segmap & 0x3F;

if (on) {
watch_set_pixel(com, seg);
} else {
watch_clear_pixel(com, seg);
}
}

void watch_display_segdata(uint8_t segdata, uint8_t position) {
for (int i = 0; i < 8; i++) {
uint8_t com = (segmap & 0xFF) >> 6;
if (com > 2) {
// COM3 means no segment exists; skip it.
segmap = segmap >> 8;
segdata = segdata >> 1;
continue;
}
uint8_t seg = segmap & 0x3F;

if (segdata & 1)
watch_set_pixel(com, seg);
else
watch_clear_pixel(com, seg);

segmap = segmap >> 8;
segdata = segdata >> 1;
uint8_t bit_pos = invert ? vert_invert_map[i] : i;
watch_display_segment(bit_pos, position, (1 << i) & segdata);
}
}

void watch_display_invert(bool inv) {
invert = inv;
}

void watch_display_character_lp(uint8_t character, uint8_t position) {
watch_display_segdata(watch_convert_char_to_segdata_lp(character), position);
}

void watch_display_string(char *string, uint8_t position) {
size_t i = 0;
while(string[i] != 0) {
Expand All @@ -133,7 +142,7 @@ void watch_display_string(char *string, uint8_t position) {
// uncomment this line to see screen output on terminal, i.e.
// FR 29
// 11 50 23
// note that for partial displays (positon > 0) it will only show the characters that were updated.
// note that for partial displays (position > 0) it will only show the characters that were updated.
// printf("________\n %c%c %c%c\n%c%c %c%c %c%c\n--------\n", (position > 0) ? ' ' : string[0], (position > 1) ? ' ' : string[1 - position], (position > 2) ? ' ' : string[2 - position], (position > 3) ? ' ' : string[3 - position], (position > 4) ? ' ' : string[4 - position], (position > 5) ? ' ' : string[5 - position], (position > 6) ? ' ' : string[6 - position], (position > 7) ? ' ' : string[7 - position], (position > 8) ? ' ' : string[8 - position], (position > 9) ? ' ' : string[9 - position]);
}

Expand Down
18 changes: 14 additions & 4 deletions watch-library/shared/watch/watch_private_display.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@
#include "hpl_slcd_config.h"
#include "driver_init.h"

// Bit position -> segment mapping for Character_Set.
// --0--
// | |
// 5 1
// | |
// --6--
// | |
// 4 2
// | |
// --3--
//
// 7 is the middle divider (i.e. only position 0).


static const uint8_t Character_Set[] =
{
0b00000000, //
Expand Down Expand Up @@ -141,8 +155,4 @@ static const uint64_t Segment_Map[] = {

static const uint8_t Num_Chars = 10;

void watch_display_character(uint8_t character, uint8_t position);
void watch_display_character_lp_seconds(uint8_t character, uint8_t position);


#endif
76 changes: 75 additions & 1 deletion watch-library/shared/watch/watch_slcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ void watch_clear_pixel(uint8_t com, uint8_t seg);
void watch_clear_display(void);

/** @brief Displays a string at the given position, starting from the top left. There are ten digits.
A space in any position will clear that digit.
* A space in any position will clear that digit.
* @param string A null-terminated string.
* @param position The position where you wish to start displaying the string. The day of week digits
* are positions 0 and 1; the day of month digits are positions 2 and 3, and the main
Expand All @@ -84,6 +84,79 @@ void watch_clear_display(void);
*/
void watch_display_string(char *string, uint8_t position);

/** @brief Displays a character at the given position, starting from the top left. There are ten digits.
* A space in any position will clear that digit.
* @param character A single character to display.
* @param position The position where you wish to display the character. The day of week digits
* are positions 0 and 1; the day of month digits are positions 2 and 3, and the main
* clock line occupies positions 4-9.
*/
void watch_display_character(uint8_t character, uint8_t position);

/** @brief Displays a character at the given position, starting from the top left. There are ten digits.
* lp refers to 'low-power', as this does _not_ do any special casing to try to handle the pecularities of each position,
* This means it's best used to display numbers in the expected form.
* @param character A single character to display.
* @param position The position where you wish to display the character. The day of week digits
* are positions 0 and 1; the day of month digits are positions 2 and 3, and the main
* clock line occupies positions 4-9.
*/
void watch_display_character_lp(uint8_t character, uint8_t position);

/** @brief Attempt to invert the display (i.e. rotate all characters 180 degrees).
* As with watch_display_character_lp, due to the limitations of the display this works best
* with numbers. This affects all subsequent calls to watch_display_string/character.
* @param inv whether to invert (true) or not invert (false).
*/
void watch_display_invert(bool inv);

/** @brief Display arbitrary segment data at a particular position.
* cf watch_display_character.
* @param segdata Raw bits which are mapped to the position. See below.
* @param position The position where you wish to display the character. The day of week digits
* are positions 0 and 1; the day of month digits are positions 2 and 3, and the main
* clock line occupies positions 4-9.
* @note The bits are mapped as follows:
*
* --0--
* | |
* 5 1
* | |
* --6--
* | |
* 4 2
* | |
* --3--
*
* 7 is the middle divider. i.e. only position 0.
*
* If you want to display/clear an individual segment, use watch_display_segment.
*/
void watch_display_segdata(uint8_t segdata, uint8_t position);

/** @brief Generate segdata for a position that could be used as input to watch_display_segdata.
* @param character A single character to convert.
* @param position The position to generate the character for.
* @note You might wonder why the position is necessary for this function even though
* we don't actually display the character here. This is because we generate different segment
* data depending on the characte to handle the limitiations of the position.
*/
uint8_t watch_convert_char_to_segdata(uint8_t character, uint8_t position);

/** @brief Generate segdata for a position that could be used as input to watch_display_segdata (lp).
* This is analagous to watch_display_character_lp.
* @param character A single character to convert.
*/
uint8_t watch_convert_char_to_segdata_lp(uint8_t character);

/** @brief Map an individual segment to a particular position. See watch_display_segdata for segment info.
* This is similar to watch_set_pixel, but maps the segments for an arbitrary spot on the display.
* @param bit_pos Bit position (0 to 7).
* @param position Character position.
* @param on Whether to set or clear at that position.
*/
void watch_display_segment(uint8_t bit_pos, uint8_t position, bool on);

/** @brief Turns the colon segment on.
*/
void watch_set_colon(void);
Expand Down Expand Up @@ -147,5 +220,6 @@ bool watch_tick_animation_is_running(void);
* @details This will stop the animation and clear all segments in position 8.
*/
void watch_stop_tick_animation(void);

/// @}
#endif

0 comments on commit e9d9800

Please sign in to comment.