]> git.earman.xyz Git - sensor-watch.git/commitdiff
refactor: break out different areas of functionality
authorJoey Castillo <jose.castillo@gmail.com>
Mon, 23 Aug 2021 14:11:49 +0000 (08:11 -0600)
committerJoey Castillo <jose.castillo@gmail.com>
Mon, 23 Aug 2021 14:11:49 +0000 (08:11 -0600)
27 files changed:
watch-library/watch/notes.h [deleted file]
watch-library/watch/watch.c
watch-library/watch/watch.h
watch-library/watch/watch_adc.c [new file with mode: 0644]
watch-library/watch/watch_adc.h [new file with mode: 0644]
watch-library/watch/watch_app.c [new file with mode: 0644]
watch-library/watch/watch_app.h [new file with mode: 0644]
watch-library/watch/watch_buzzer.c [new file with mode: 0644]
watch-library/watch/watch_buzzer.h [new file with mode: 0644]
watch-library/watch/watch_deepsleep.c [new file with mode: 0644]
watch-library/watch/watch_deepsleep.h [new file with mode: 0644]
watch-library/watch/watch_extint.c [new file with mode: 0644]
watch-library/watch/watch_extint.h [new file with mode: 0644]
watch-library/watch/watch_gpio.c [new file with mode: 0644]
watch-library/watch/watch_gpio.h [new file with mode: 0644]
watch-library/watch/watch_i2c.c [new file with mode: 0644]
watch-library/watch/watch_i2c.h [new file with mode: 0644]
watch-library/watch/watch_led.c [new file with mode: 0644]
watch-library/watch/watch_led.h [new file with mode: 0644]
watch-library/watch/watch_private.c [new file with mode: 0644]
watch-library/watch/watch_private.h [new file with mode: 0644]
watch-library/watch/watch_rtc.c [new file with mode: 0644]
watch-library/watch/watch_rtc.h [new file with mode: 0644]
watch-library/watch/watch_slcd.c [new file with mode: 0644]
watch-library/watch/watch_slcd.h [new file with mode: 0644]
watch-library/watch/watch_uart.c [new file with mode: 0644]
watch-library/watch/watch_uart.h [new file with mode: 0644]

diff --git a/watch-library/watch/notes.h b/watch-library/watch/notes.h
deleted file mode 100644 (file)
index bc4a3fa..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2020 Joey Castillo
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-////< @file notes.h
-
-/// @brief 108 notes for use with watch_buzzer_play_note
-typedef enum BuzzerNote {
-    BUZZER_NOTE_C0 = 0,          ///< 16.35 Hz
-    BUZZER_NOTE_C0SHARP_D0FLAT,  ///< 17.32 Hz
-    BUZZER_NOTE_D0,              ///< 18.35 Hz
-    BUZZER_NOTE_D0SHARP_E0FLAT,  ///< 19.45 Hz
-    BUZZER_NOTE_E0,              ///< 20.60 Hz
-    BUZZER_NOTE_F0,              ///< 21.83 Hz
-    BUZZER_NOTE_F0SHARP_G0FLAT,  ///< 23.12 Hz
-    BUZZER_NOTE_G0,              ///< 24.50 Hz
-    BUZZER_NOTE_G0SHARP_A0FLAT,  ///< 25.96 Hz
-    BUZZER_NOTE_A0,              ///< 27.50 Hz
-    BUZZER_NOTE_A0SHARP_B0FLAT,  ///< 29.14 Hz
-    BUZZER_NOTE_B0,              ///< 30.87 Hz
-    BUZZER_NOTE_C1,              ///< 32.70 Hz
-    BUZZER_NOTE_C1SHARP_D1FLAT,  ///< 34.65 Hz
-    BUZZER_NOTE_D1,              ///< 36.71 Hz
-    BUZZER_NOTE_D1SHARP_E1FLAT,  ///< 38.89 Hz
-    BUZZER_NOTE_E1,              ///< 41.20 Hz
-    BUZZER_NOTE_F1,              ///< 43.65 Hz
-    BUZZER_NOTE_F1SHARP_G1FLAT,  ///< 46.25 Hz
-    BUZZER_NOTE_G1,              ///< 49.00 Hz
-    BUZZER_NOTE_G1SHARP_A1FLAT,  ///< 51.91 Hz
-    BUZZER_NOTE_A1,              ///< 55.00 Hz
-    BUZZER_NOTE_A1SHARP_B1FLAT,  ///< 58.27 Hz
-    BUZZER_NOTE_B1,              ///< 61.74 Hz
-    BUZZER_NOTE_C2,              ///< 65.41 Hz
-    BUZZER_NOTE_C2SHARP_D2FLAT,  ///< 69.30 Hz
-    BUZZER_NOTE_D2,              ///< 73.42 Hz
-    BUZZER_NOTE_D2SHARP_E2FLAT,  ///< 77.78 Hz
-    BUZZER_NOTE_E2,              ///< 82.41 Hz
-    BUZZER_NOTE_F2,              ///< 87.31 Hz
-    BUZZER_NOTE_F2SHARP_G2FLAT,  ///< 92.50 Hz
-    BUZZER_NOTE_G2,              ///< 98.00 Hz
-    BUZZER_NOTE_G2SHARP_A2FLAT,  ///< 103.83 Hz
-    BUZZER_NOTE_A2,              ///< 110.00 Hz
-    BUZZER_NOTE_A2SHARP_B2FLAT,  ///< 116.54 Hz
-    BUZZER_NOTE_B2,              ///< 123.47 Hz
-    BUZZER_NOTE_C3,              ///< 130.81 Hz
-    BUZZER_NOTE_C3SHARP_D3FLAT,  ///< 138.59 Hz
-    BUZZER_NOTE_D3,              ///< 146.83 Hz
-    BUZZER_NOTE_D3SHARP_E3FLAT,  ///< 155.56 Hz
-    BUZZER_NOTE_E3,              ///< 164.81 Hz
-    BUZZER_NOTE_F3,              ///< 174.61 Hz
-    BUZZER_NOTE_F3SHARP_G3FLAT,  ///< 185.00 Hz
-    BUZZER_NOTE_G3,              ///< 196.00 Hz
-    BUZZER_NOTE_G3SHARP_A3FLAT,  ///< 207.65 Hz
-    BUZZER_NOTE_A3,              ///< 220.00 Hz
-    BUZZER_NOTE_A3SHARP_B3FLAT,  ///< 233.08 Hz
-    BUZZER_NOTE_B3,              ///< 246.94 Hz
-    BUZZER_NOTE_C4,              ///< 261.63 Hz
-    BUZZER_NOTE_C4SHARP_D4FLAT,  ///< 277.18 Hz
-    BUZZER_NOTE_D4,              ///< 293.66 Hz
-    BUZZER_NOTE_D4SHARP_E4FLAT,  ///< 311.13 Hz
-    BUZZER_NOTE_E4,              ///< 329.63 Hz
-    BUZZER_NOTE_F4,              ///< 349.23 Hz
-    BUZZER_NOTE_F4SHARP_G4FLAT,  ///< 369.99 Hz
-    BUZZER_NOTE_G4,              ///< 392.00 Hz
-    BUZZER_NOTE_G4SHARP_A4FLAT,  ///< 415.30 Hz
-    BUZZER_NOTE_A4,              ///< 440.00 Hz
-    BUZZER_NOTE_A4SHARP_B4FLAT,  ///< 466.16 Hz
-    BUZZER_NOTE_B4,              ///< 493.88 Hz
-    BUZZER_NOTE_C5,              ///< 523.25 Hz
-    BUZZER_NOTE_C5SHARP_D5FLAT,  ///< 554.37 Hz
-    BUZZER_NOTE_D5,              ///< 587.33 Hz
-    BUZZER_NOTE_D5SHARP_E5FLAT,  ///< 622.25 Hz
-    BUZZER_NOTE_E5,              ///< 659.25 Hz
-    BUZZER_NOTE_F5,              ///< 698.46 Hz
-    BUZZER_NOTE_F5SHARP_G5FLAT,  ///< 739.99 Hz
-    BUZZER_NOTE_G5,              ///< 783.99 Hz
-    BUZZER_NOTE_G5SHARP_A5FLAT,  ///< 830.61 Hz
-    BUZZER_NOTE_A5,              ///< 880.00 Hz
-    BUZZER_NOTE_A5SHARP_B5FLAT,  ///< 932.33 Hz
-    BUZZER_NOTE_B5,              ///< 987.77 Hz
-    BUZZER_NOTE_C6,              ///< 1046.50 Hz
-    BUZZER_NOTE_C6SHARP_D6FLAT,  ///< 1108.73 Hz
-    BUZZER_NOTE_D6,              ///< 1174.66 Hz
-    BUZZER_NOTE_D6SHARP_E6FLAT,  ///< 1244.51 Hz
-    BUZZER_NOTE_E6,              ///< 1318.51 Hz
-    BUZZER_NOTE_F6,              ///< 1396.91 Hz
-    BUZZER_NOTE_F6SHARP_G6FLAT,  ///< 1479.98 Hz
-    BUZZER_NOTE_G6,              ///< 1567.98 Hz
-    BUZZER_NOTE_G6SHARP_A6FLAT,  ///< 1661.22 Hz
-    BUZZER_NOTE_A6,              ///< 1760.00 Hz
-    BUZZER_NOTE_A6SHARP_B6FLAT,  ///< 1864.66 Hz
-    BUZZER_NOTE_B6,              ///< 1975.53 Hz
-    BUZZER_NOTE_C7,              ///< 2093.00 Hz
-    BUZZER_NOTE_C7SHARP_D7FLAT,  ///< 2217.46 Hz
-    BUZZER_NOTE_D7,              ///< 2349.32 Hz
-    BUZZER_NOTE_D7SHARP_E7FLAT,  ///< 2489.02 Hz
-    BUZZER_NOTE_E7,              ///< 2637.02 Hz
-    BUZZER_NOTE_F7,              ///< 2793.83 Hz
-    BUZZER_NOTE_F7SHARP_G7FLAT,  ///< 2959.96 Hz
-    BUZZER_NOTE_G7,              ///< 3135.96 Hz
-    BUZZER_NOTE_G7SHARP_A7FLAT,  ///< 3322.44 Hz
-    BUZZER_NOTE_A7,              ///< 3520.00 Hz
-    BUZZER_NOTE_A7SHARP_B7FLAT,  ///< 3729.31 Hz
-    BUZZER_NOTE_B7,              ///< 3951.07 Hz
-    BUZZER_NOTE_C8,              ///< 4186.01 Hz
-    BUZZER_NOTE_C8SHARP_D8FLAT,  ///< 4434.92 Hz
-    BUZZER_NOTE_D8,              ///< 4698.63 Hz
-    BUZZER_NOTE_D8SHARP_E8FLAT,  ///< 4978.03 Hz
-    BUZZER_NOTE_E8,              ///< 5274.04 Hz
-    BUZZER_NOTE_F8,              ///< 5587.65 Hz
-    BUZZER_NOTE_F8SHARP_G8FLAT,  ///< 5919.91 Hz
-    BUZZER_NOTE_G8,              ///< 6271.93 Hz
-    BUZZER_NOTE_G8SHARP_A8FLAT,  ///< 6644.88 Hz
-    BUZZER_NOTE_A8,              ///< 7040.00 Hz
-    BUZZER_NOTE_A8SHARP_B8FLAT,  ///< 7458.62 Hz
-    BUZZER_NOTE_B8,              ///< 7902.13 Hz
-    BUZZER_NOTE_REST             ///< no sound
-} BuzzerNote;
index 659be1c5f113836487426172a7ab82100439519c..d55b61fe689f112fb705b018e405f10dff19ae2b 100644 (file)
  */
 
 #include "watch.h"
-#include "peripheral_clk_config.h"
-#include <stdlib.h>
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// User callbacks and other definitions
-
-ext_irq_cb_t btn_alarm_callback;
-ext_irq_cb_t a2_callback;
-ext_irq_cb_t d1_callback;
 
+// TODO: this should all live in watch_deepsleep.c, but right now watch_extint.c needs it
+// because we're being too clever about the alarm button.
 static void extwake_callback(uint8_t reason);
+ext_irq_cb_t btn_alarm_callback;
 
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// Initialization
-
-void _watch_init() {
-    // disable the LED pin (it may have been enabled by the bootloader)
-    watch_disable_digital_output(RED);
-
-    // Use switching regulator for lower power consumption.
-    SUPC->VREG.bit.SEL = 1;
-    while(!SUPC->STATUS.bit.VREGRDY);
-
-    // External wake depends on RTC; calendar is a required module.
-    CALENDAR_0_init();
-    calendar_enable(&CALENDAR_0);
-
-    // Not sure if this belongs in every app -- is there a power impact?
-    delay_driver_init();
-
-    // set up state
-    btn_alarm_callback = NULL;
-    a2_callback = NULL;
-    d1_callback = NULL;
-}
-
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// Segmented Display
-
-static const uint8_t Character_Set[] =
-{
-    0b00000000, //  
-    0b00000000, // ! (unused)
-    0b00100010, // "
-    0b01100011, // # (degree symbol, hash mark doesn't fit)
-    0b00000000, // $ (unused)
-    0b00000000, // % (unused)
-    0b01000100, // & ("lowercase 7" for positions 4 and 6)
-    0b00100000, // '
-    0b00111001, // (
-    0b00001111, // )
-    0b00000000, // * (unused)
-    0b11000000, // + (only works in position 0)
-    0b00000100, // ,
-    0b01000000, // -
-    0b01000000, // . (same as -, semantically most useful)
-    0b00010010, // /
-    0b00111111, // 0
-    0b00000110, // 1
-    0b01011011, // 2
-    0b01001111, // 3
-    0b01100110, // 4
-    0b01101101, // 5
-    0b01111101, // 6
-    0b00000111, // 7
-    0b01111111, // 8
-    0b01101111, // 9
-    0b00000000, // : (unused)
-    0b00000000, // ; (unused)
-    0b01011000, // <
-    0b01001000, // =
-    0b01001100, // >
-    0b01010011, // ?
-    0b11111111, // @ (all segments on)
-    0b01110111, // A
-    0b01111111, // B
-    0b00111001, // C
-    0b00111111, // D
-    0b01111001, // E
-    0b01110001, // F
-    0b00111101, // G
-    0b01110110, // H
-    0b10001001, // I (only works in position 0)
-    0b00001110, // J
-    0b01110101, // K
-    0b00111000, // L
-    0b10110111, // M (only works in position 0)
-    0b00110111, // N
-    0b00111111, // O
-    0b01110011, // P
-    0b01100111, // Q
-    0b11110111, // R (only works in position 1)
-    0b01101101, // S
-    0b10000001, // T (only works in position 0; set (1, 12) to make it work in position 1)
-    0b00111110, // U
-    0b00111110, // V
-    0b10111110, // W (only works in position 0)
-    0b01111110, // X
-    0b01101110, // Y
-    0b00011011, // Z
-    0b00111001, // [
-    0b00100100, // backslash
-    0b00001111, // ]
-    0b00100011, // ^
-    0b00001000, // _
-    0b00000010, // `
-    0b01011111, // a
-    0b01111100, // b
-    0b01011000, // c
-    0b01011110, // d
-    0b01111011, // e
-    0b01110001, // f
-    0b01101111, // g
-    0b01110100, // h
-    0b00010000, // i
-    0b01000010, // j (appears as superscript to work in more positions)
-    0b01110101, // k
-    0b00110000, // l
-    0b10110111, // m (only works in position 0)
-    0b01010100, // n
-    0b01011100, // o
-    0b01110011, // p
-    0b01100111, // q
-    0b01010000, // r
-    0b01101101, // s
-    0b01111000, // t
-    0b01100010, // u (appears as superscript to work in more positions)
-    0b01100010, // v (appears as superscript to work in more positions)
-    0b10111110, // w (only works in position 0)
-    0b01111110, // x
-    0b01101110, // y
-    0b00011011, // z
-    0b00111001, // {
-    0b00110000, // |
-    0b00001111, // }
-    0b00000001, // ~
-};
-
-static const uint64_t Segment_Map[] = {
-    0x4e4f0e8e8f8d4d0d, // Position 0, mode
-    0xc8c4c4c8b4b4b0b,  // Position 1, mode (Segments B and C shared, as are segments E and F)
-    0xc049c00a49890949, // Position 2, day of month (Segments A, D, G shared; missing segment F)
-    0xc048088886874707, // Position 3, day of month
-    0xc053921252139352, // Position 4, clock hours (Segments A and D shared)
-    0xc054511415559594, // Position 5, clock hours
-    0xc057965616179716, // Position 6, clock minutes (Segments A and D shared)
-    0xc041804000018a81, // Position 7, clock minutes
-    0xc043420203048382, // Position 8, clock seconds
-    0xc045440506468584, // Position 9, clock seconds
-};
-
-static const uint8_t Num_Chars = 10;
-
-static const uint32_t IndicatorSegments[6] = {
-    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
-};
-
-void watch_enable_display() {
-    SEGMENT_LCD_0_init();
-    slcd_sync_enable(&SEGMENT_LCD_0);
-}
-
-inline void watch_set_pixel(uint8_t com, uint8_t seg) {
-    slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(com, seg));
-}
-
-inline void watch_clear_pixel(uint8_t com, uint8_t seg) {
-    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(com, seg));
-}
-
-void watch_display_character(uint8_t character, uint8_t position) {
-    // handle lowercase 7 if needed
-    if (character == '7' && (position == 4 || position == 6)) character = '&';
-
-    uint64_t segmap = Segment_Map[position];
-    uint64_t segdata = 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;
-        slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(com, seg));
-        if (segdata & 1) slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(com, seg));
-        segmap = segmap >> 8;
-        segdata = segdata >> 1;
-    }
-}
-
-void watch_display_string(char *string, uint8_t position) {
-    size_t i = 0;
-    while(string[i] != 0) {
-        watch_display_character(string[i], position + i);
-        i++;
-        if (i >= Num_Chars) break;
-    }
-}
-
-inline void watch_set_colon() {
-    slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(1, 16));
-}
-
-inline void watch_clear_colon() {
-    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(1, 16));
-}
-
-inline void watch_set_indicator(WatchIndicatorSegment indicator) {
-    slcd_sync_seg_on(&SEGMENT_LCD_0, IndicatorSegments[indicator]);
-}
-
-inline void watch_clear_indicator(WatchIndicatorSegment indicator) {
-    slcd_sync_seg_off(&SEGMENT_LCD_0, IndicatorSegments[indicator]);
-}
-
-void watch_clear_all_indicators() {
-    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(2, 17));
-    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(2, 16));
-    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(0, 17));
-    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(0, 16));
-    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(1, 10));
-}
-
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// Buttons
-
-void watch_enable_buttons() {
-    EXTERNAL_IRQ_0_init();
-}
-
-void watch_register_button_callback(const uint8_t pin, ext_irq_cb_t callback) {
-    if (pin == BTN_ALARM) {
-        gpio_set_pin_direction(BTN_ALARM, GPIO_DIRECTION_IN);
-        gpio_set_pin_pull_mode(BTN_ALARM, GPIO_PULL_DOWN);
-        gpio_set_pin_function(BTN_ALARM, PINMUX_PA02G_RTC_IN2);
-        btn_alarm_callback = callback;
-        _extwake_register_callback(&CALENDAR_0.device, extwake_callback);
-    } else {
-        ext_irq_register(pin, callback);
-    }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// LED
-
-bool PWM_0_enabled = false;
-
-void watch_enable_led(bool pwm) {
-    if (pwm) {
-        if (PWM_0_enabled) return;
-
-        PWM_0_init();
-        pwm_set_parameters(&PWM_0, 10000, 0);
-        pwm_enable(&PWM_0);
-
-        PWM_0_enabled = true;
-    } else {
-        watch_enable_digital_output(RED);
-        watch_enable_digital_output(GREEN);
-    }
-    watch_set_led_off();
-}
-
-void watch_disable_led(bool pwm) {
-    if (pwm) {
-        if (!PWM_0_enabled) return;
-        pwm_disable(&PWM_0);
-        PWM_0_enabled = false;
-    }
-
-    watch_disable_digital_output(RED);
-    watch_disable_digital_output(GREEN);
-}
-
-void watch_set_led_color(uint16_t red, uint16_t green) {
-    if (PWM_0_enabled) {
-        TC3->COUNT16.CC[0].reg = red;
-        TC3->COUNT16.CC[1].reg = green;
-    }
-}
-
-void watch_set_led_red() {
-    if (PWM_0_enabled) {
-        watch_set_led_color(65535, 0);
-    } else {
-        watch_set_pin_level(RED, true);
-        watch_set_pin_level(GREEN, false);
-    }
-}
-
-void watch_set_led_green() {
-    if (PWM_0_enabled) {
-        watch_set_led_color(65535, 0);
-    } else {
-        watch_set_pin_level(RED, false);
-        watch_set_pin_level(GREEN, true);
-    }
-}
-
-void watch_set_led_yellow() {
-    if (PWM_0_enabled) {
-        watch_set_led_color(65535, 65535);
-    } else {
-        watch_set_pin_level(RED, true);
-        watch_set_pin_level(GREEN, true);
-    }
-}
-
-void watch_set_led_off() {
-    if (PWM_0_enabled) {
-        watch_set_led_color(0, 0);
-    } else {
-        watch_set_pin_level(RED, false);
-        watch_set_pin_level(GREEN, false);
-    }
-}
-
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// Buzzer
-
-inline void watch_enable_buzzer() {
-    PWM_1_init();
-}
-
-inline void watch_set_buzzer_period(uint32_t period) {
-    pwm_set_parameters(&PWM_1, period, period / 2);
-}
-
-inline void watch_set_buzzer_on() {
-       pwm_enable(&PWM_1);
-}
-
-inline void watch_set_buzzer_off() {
-       pwm_disable(&PWM_1);
-}
-
-const uint16_t NotePeriods[108] = {31047, 29301, 27649, 26079, 24617, 23224, 21923, 20683, 19515, 18418, 17377, 16399, 15477, 14603, 13780, 13004, 12272, 11580, 10926, 10311, 9730, 9181, 8664, 8175, 7714, 7280, 6869, 6483, 6117, 5772, 5447, 5140, 4850, 4577, 4319, 4076, 3846, 3629, 3425, 3232, 3050, 2878, 2715, 2562, 2418, 2282, 2153, 2032, 1917, 1809, 1707, 1611, 1520, 1435, 1354, 1277, 1205, 1137, 1073, 1013, 956, 902, 851, 803, 758, 715, 675, 637, 601, 567, 535, 505, 476, 450, 424, 400, 378, 357, 336, 317, 300, 283, 267, 252, 238, 224, 212, 200, 188, 178, 168, 158, 149, 141, 133, 125, 118, 112, 105, 99, 94, 89, 84, 79, 74, 70, 66, 63};
-
-void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms) {
-    if (note == BUZZER_NOTE_REST) {
-        watch_set_buzzer_off();
-    } else {
-        pwm_set_parameters(&PWM_1, NotePeriods[note], NotePeriods[note] / 2);
-        watch_set_buzzer_on();
-    }
-    delay_ms(duration_ms);
-    watch_set_buzzer_off();
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// Real-time Clock
-
-bool _watch_rtc_is_enabled() {
-    return RTC->MODE0.CTRLA.bit.ENABLE;
-}
-
-void watch_set_date_time(struct calendar_date_time date_time) {
-    calendar_set_date(&CALENDAR_0, &date_time.date);
-    calendar_set_time(&CALENDAR_0, &date_time.time);
-}
-
-void watch_get_date_time(struct calendar_date_time *date_time) {
-    calendar_get_date_time(&CALENDAR_0, date_time);
-}
-
-void watch_register_tick_callback(ext_irq_cb_t callback) {
-    _prescaler_register_callback(&CALENDAR_0.device, callback);
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// Analog Input
-
-static bool ADC_0_ENABLED = false;
-
-void watch_enable_analog(const uint8_t pin) {
-    if (!ADC_0_ENABLED) ADC_0_init();
-    ADC_0_ENABLED = true;
-
-    gpio_set_pin_direction(pin, GPIO_DIRECTION_OFF);
-    switch (pin) {
-        case A0:
-            gpio_set_pin_function(A0, PINMUX_PB04B_ADC_AIN12);
-            break;
-        case A1:
-            gpio_set_pin_function(A1, PINMUX_PB01B_ADC_AIN9);
-            break;
-        case A2:
-            gpio_set_pin_function(A2, PINMUX_PB02B_ADC_AIN10);
-            break;
-        default:
-            return;
-    }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// Digital IO
-
-void watch_enable_digital_input(const uint8_t pin) {
-    gpio_set_pin_direction(pin, GPIO_DIRECTION_IN);
-    gpio_set_pin_function(pin, GPIO_PIN_FUNCTION_OFF);
-}
-
-void watch_enable_pull_up(const uint8_t pin) {
-    gpio_set_pin_pull_mode(pin, GPIO_PULL_UP);
-}
-
-void watch_enable_pull_down(const uint8_t pin) {
-    gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN);
-}
-
-bool watch_get_pin_level(const uint8_t pin) {
-    return gpio_get_pin_level(pin);
-}
-
-void watch_enable_digital_output(const uint8_t pin) {
-    gpio_set_pin_direction(pin, GPIO_DIRECTION_OUT);
-    gpio_set_pin_function(pin, GPIO_PIN_FUNCTION_OFF);
-}
-
-void watch_disable_digital_output(const uint8_t pin) {
-    gpio_set_pin_direction(pin, GPIO_DIRECTION_OFF);
-}
-
-void watch_set_pin_level(const uint8_t pin, const bool level) {
-    gpio_set_pin_level(pin, level);
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// I2C
-
-struct io_descriptor *I2C_0_io;
-
-void watch_enable_i2c() {
-    I2C_0_init();
-    i2c_m_sync_get_io_descriptor(&I2C_0, &I2C_0_io);
-    i2c_m_sync_enable(&I2C_0);
-}
-
-void watch_i2c_send(int16_t addr, uint8_t *buf, uint16_t length) {
-    i2c_m_sync_set_periphaddr(&I2C_0, addr, I2C_M_SEVEN);
-    io_write(I2C_0_io, buf, length);
-}
-
-void watch_i2c_receive(int16_t addr, uint8_t *buf, uint16_t length) {
-    i2c_m_sync_set_periphaddr(&I2C_0, addr, I2C_M_SEVEN);
-    io_read(I2C_0_io, buf, length);
-}
-
-void watch_i2c_write8(int16_t addr, uint8_t reg, uint8_t data) {
-    uint8_t buf[2];
-    buf[0] = reg;
-    buf[1] = data;
-
-    watch_i2c_send(addr, (uint8_t *)&buf, 2);
-}
-
-uint8_t watch_i2c_read8(int16_t addr, uint8_t reg) {
-    uint8_t data;
-
-    watch_i2c_send(addr, (uint8_t *)&reg, 1);
-    watch_i2c_receive(addr, (uint8_t *)&data, 1);
-
-    return data;
-}
-
-uint16_t watch_i2c_read16(int16_t addr, uint8_t reg) {
-    uint16_t data;
-
-    watch_i2c_send(addr, (uint8_t *)&reg, 1);
-    watch_i2c_receive(addr, (uint8_t *)&data, 2);
-
-    return data;
-}
-
-uint32_t watch_i2c_read24(int16_t addr, uint8_t reg) {
-    uint32_t data;
-    data = 0;
-
-    watch_i2c_send(addr, (uint8_t *)&reg, 1);
-    watch_i2c_receive(addr, (uint8_t *)&data, 3);
-
-    return data << 8;
-}
-
-uint32_t watch_i2c_read32(int16_t addr, uint8_t reg) {
-    uint32_t data;
-
-    watch_i2c_send(addr, (uint8_t *)&reg, 1);
-    watch_i2c_receive(addr, (uint8_t *)&data, 4);
-
-    return data;
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// Debug UART
-
-/*
- * UART methods are Copyright (c) 2014-2017, Alex Taradov <alex@taradov.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-void watch_enable_debug_uart(uint32_t baud) {
-    uint64_t br = (uint64_t)65536 * (CONF_CPU_FREQUENCY - 16 * baud) / CONF_CPU_FREQUENCY;
-
-    gpio_set_pin_direction(D1, GPIO_DIRECTION_IN);
-    gpio_set_pin_function(D1, PINMUX_PB00C_SERCOM3_PAD2);
-
-    MCLK->APBCMASK.reg |= MCLK_APBCMASK_SERCOM3;
-
-    GCLK->PCHCTRL[SERCOM3_GCLK_ID_CORE].reg = GCLK_PCHCTRL_GEN(0) | GCLK_PCHCTRL_CHEN;
-    while (0 == (GCLK->PCHCTRL[SERCOM3_GCLK_ID_CORE].reg & GCLK_PCHCTRL_CHEN));
-
-    SERCOM3->USART.CTRLA.reg =
-        SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_MODE(1/*USART_INT_CLK*/) |
-        SERCOM_USART_CTRLA_RXPO(0/*PAD0*/) | SERCOM_USART_CTRLA_TXPO(1/*PAD2*/);
-
-    SERCOM3->USART.CTRLB.reg = SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN |
-        SERCOM_USART_CTRLB_CHSIZE(0/*8 bits*/);
-
-    SERCOM3->USART.BAUD.reg = (uint16_t)br;
-
-    SERCOM3->USART.CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;
-}
-
-void watch_debug_putc(char c) {
-    while (!(SERCOM3->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_DRE));
-    SERCOM3->USART.DATA.reg = c;
-}
-
-void watch_debug_puts(char *s) {
-    while (*s) watch_debug_putc(*s++);
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// Deep Sleep
-
-static void extwake_callback(uint8_t reason) {
-    if (reason & RTC_TAMPID_TAMPID2) {
-        if (btn_alarm_callback != NULL) btn_alarm_callback();
-    } else if (reason & RTC_TAMPID_TAMPID1) {
-        if (a2_callback != NULL) a2_callback();
-    } else if (reason & RTC_TAMPID_TAMPID0) {
-        if (d1_callback != NULL) d1_callback();
-    }
-}
-
-void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback) {
-    uint32_t pinmux;
-    if (pin == D1) {
-        d1_callback = callback;
-        pinmux = PINMUX_PB00G_RTC_IN0;
-    } else if (pin == A2) {
-        a2_callback = callback;
-        pinmux = PINMUX_PB02G_RTC_IN1;
-    } else {
-        return;
-    }
-    gpio_set_pin_direction(pin, GPIO_DIRECTION_IN);
-    gpio_set_pin_function(pin, pinmux);
-    _extwake_register_callback(&CALENDAR_0.device, extwake_callback);
-}
-
-void watch_store_backup_data(uint32_t data, uint8_t reg) {
-    if (reg < 8) {
-        RTC->MODE0.BKUP[reg].reg = data;
-    }
-}
-
-uint32_t watch_get_backup_data(uint8_t reg) {
-    if (reg < 8) {
-        return RTC->MODE0.BKUP[reg].reg;
-    }
-
-    return 0;
-}
-
-void watch_enter_deep_sleep() {
-    // enable and configure the external wake interrupt, if not already set up.
-    if (btn_alarm_callback == NULL && a2_callback == NULL && d1_callback == NULL) {
-        gpio_set_pin_direction(BTN_ALARM, GPIO_DIRECTION_IN);
-        gpio_set_pin_pull_mode(BTN_ALARM, GPIO_PULL_DOWN);
-        gpio_set_pin_function(BTN_ALARM, PINMUX_PA02G_RTC_IN2);
-        _extwake_register_callback(&CALENDAR_0.device, extwake_callback);
-    }
-
-    // disable SLCD
-    slcd_sync_deinit(&SEGMENT_LCD_0);
-    hri_mclk_clear_APBCMASK_SLCD_bit(SLCD);
-
-    // TODO: disable other peripherals
-
-    // go into backup sleep mode
-    sleep(5);
-}
+#include "watch_rtc.c"
+#include "watch_slcd.c"
+#include "watch_extint.c"
+#include "watch_led.c"
+#include "watch_buzzer.c"
+#include "watch_adc.c"
+#include "watch_gpio.c"
+#include "watch_i2c.c"
+#include "watch_uart.c"
+#include "watch_deepsleep.c"
+#include "watch_private.c"
index 2b8cd4e092b39f08077fd01dea44f76abf90fa3b..8c207ddb645ba6d06830b15d8b97c7e0585c07dc 100644 (file)
 #ifndef WATCH_H_
 #define WATCH_H_
 #include <stdint.h>
+#include <stdbool.h>
 #include "driver_init.h"
-#include "hpl_calendar.h"
-#include "hal_ext_irq.h"
-#include "notes.h"
 
 /** @mainpage Sensor Watch Documentation
  *  @brief This documentation covers most of the functions you will use to interact with the Sensor Watch
            hardware. It is divided into the following sections:
             - @ref app - This section covers the functions that you will implement in your app.c file when designing a
                          Sensor Watch app.
+            - @ref rtc - This section covers functions related to the SAM L22's real-time clock peripheral, including
+                         date, time and alarm functions.
             - @ref slcd - This section covers functions related to the Segment LCD display driver, which is responsible
                           for displaying strings of characters and indicators on the main watch display.
             - @ref buttons - This section covers functions related to the three buttons: Light, Mode and Alarm.
             - @ref led - This section covers functions related to the bi-color red/green LED mounted behind the LCD.
             - @ref buzzer - This section covers functions related to the piezo buzzer.
-            - @ref rtc - This section covers functions related to the SAM L22's real-time clock peripheral, including
-                         date, time and alarm functions.
             - @ref adc - This section covers functions related to the SAM L22's analog-to-digital converter, as well as
                          configuring and reading values from the three analog-capable pins on the 9-pin connector.
             - @ref gpio - This section covers functions related to general-purpose input and output signals.
                                deepest sleep mode available on the SAM L22.
  */
 
-/** @addtogroup app Application Framework
-  * @brief This section covers the functions that you will implement in your app.c file when designing a Sensor Watch app.
-  * @details You should be able to write a watch app by simply implementing these functions and declaring callbacks for
-  *          various GPIO and peripheral interrupts. The main.c file takes care of calling these functions for you. The
-  *          general flow:
-  *
-  *            1. Your app_init() function is called.
-  *               - This method should only be used to set your initial application state.
-  *            2. If your app is waking from BACKUP, app_wake_from_deep_sleep()  is called.
-  *               - If you saved state in the RTC's backup registers, you can restore it here.
-  *            3. Your app_setup() method is called.
-  *               - You may wish to enable some functionality and peripherals here.
-  *               - You should definitely set up some interrupts here.
-  *            4. The main run loop begins: your app_loop() function is called.
-  *               - Run code and update your UI here.
-  *               - Return true if your app is prepared to enter STANDBY mode.
-  *            5. This step differs depending on the value returned by app_loop:
-  *               - If you returned false, execution resumes at (4).
-  *               - If you returned true, app_prepare_for_sleep() is called; execution moves on to (6).
-  *            6. The microcontroller enters the STANDBY sleep mode.
-  *               - No user code will run, and the watch will enter a low power mode.
-  *               - The watch will remain in this state until an interrupt wakes it.
-  *            7. Once woken from STANDBY, your app_wake_from_sleep() function is called.
-  *               - After this, execution resumes at (4).
-  */
-/// @{
-/** @brief A function you will implement to initialize your application state. The app_init function is called before
-  *        anything else. Use it to set up any internal data structures or application state required by your app,
-  *        but don't configure any peripherals just yet.
-  */
-void app_init();
-
-/** @brief A function you will implement to wake from deep sleep mode. The app_wake_from_deep_sleep function is only
-  *        called if your app is waking from the ultra-low power BACKUP sleep mode. You may have chosen to store some
-  *        state in the RTC's backup registers prior to entering this mode. You may restore that state here.
-  */
-void app_wake_from_deep_sleep();
-
-/** @brief A function you will implement to set up your application. The app_setup function is like setup() in Arduino.
-  *        It is called once when the program begins. You should set pin modes and enable any peripherals you want to
-  *        set up (real-time clock, I2C, etc.) Depending on your application, you may or may not want to configure
-  *        sensors on your sensor board here. For example, a low-power accelerometer that will run at all times should
-  *        be configured here, whereas you may want to enable a more power-hungry sensor only when you need it.
-  * @note If your app enters the ultra-low power BACKUP sleep mode, this function will be called again when it wakes
-  *       from that deep sleep state. In this state, the RTC will still be configured with the correct date and time.
-  */
-void app_setup();
-
-/** @brief A function you will implement to serve as the app's main run loop. This method will be called repeatedly,
-           or if you enter STANDBY sleep mode, as soon as the device wakes from sleep.
-  * @return You should return true if your app is prepared to enter STANDBY sleep mode. If you return false, your
-  *         app's app_loop method will be called again immediately. Note that in STANDBY mode, the watch will consume
-  *         only about 95 microamperes of power, whereas if you return false and keep the app awake, it will consume
-  *         about 355 microamperes. This is the difference between months of battery life and days. As much as
-  *         possible, you should limit the amount of time your app spends awake.
-  * @note Only the RTC, the segment LCD controller and the external interrupt controller run in STANDBY mode. If you
-  *       are using, e.g. the PWM function to set a custom LED color, you should return false here until you are
-  *       finished with that operation. Note however that the peripherals will continue running after waking up,
-  *       so e.g. the I2C controller, if configured, will sleep in STANDBY. But you can use it again as soon as your
-  *       app wakes up.
-  */
-bool app_loop();
-
-/** @brief A function you will implement to prepare to enter STANDBY sleep mode. The app_prepare_for_sleep function is
- *         called before the watch goes into the STANDBY sleep mode. In STANDBY mode, most peripherals are shut down,
- *         and no code will run until the watch receives an interrupt (generally either the 1Hz tick or a press on one
- *         of the buttons).
- * @note If you are PWM'ing the LED or playing a sound on the buzzer, the TC/TCC peripherals that drive those operations
- *       will not run in STANDBY. BUT! the output pins will retain the state they had when entering standby. This means
- *       you could end up entering standby with an LED on and draining power, or with a DC potential across the piezo
- *       buzzer that could damage it if left in this state. If your app_loop does not prevent sleep during these
- *       activities, you should make sure to disable these outputs in app_prepare_for_sleep.
- */
-void app_prepare_for_sleep();
-
-/** @brief A method you will implement to configure the app after waking from STANDBY sleep mode.
-  */
-void app_wake_from_sleep();
-
-/// Called by main.c while setting up the app. You should not call this from your app.
-void _watch_init();
-/// @}
-
-
-/** @addtogroup slcd Segment LCD Display
-  * @brief This section covers functions related to the Segment LCD display driver, which is responsible
-  *        for displaying strings of characters and indicators on the main watch display.
-  * @details The segment LCD controller consumes about 3 microamperes of power with no segments on, and
-  *          about 4 microamperes with all segments on. There is also a slight power impact associated
-  *          with updating the screen (about 1 microampere to update at 1 Hz). For the absolute lowest
-  *          power operation, update the display only when its contents have changed, and disable the
-  *          SLCD peripheral when the screen is not in use.
-  *          For a map of all common and segment pins, see <a href="segmap.html">segmap.html</a>. You can
-  *          hover over any segment in that diagram to view the common and segment pins associated with
-  *          each segment of the display.
-  */
-/// @{
-
-/// An enum listing the icons and indicators available on the watch.
-typedef enum WatchIndicatorSegment {
-    WATCH_INDICATOR_SIGNAL = 0, ///< The hourly signal indicator; also useful for indicating that sensors are on.
-    WATCH_INDICATOR_BELL,       ///< The small bell indicating that an alarm is set.
-    WATCH_INDICATOR_PM,         ///< The PM indicator, indicating that a time is in the afternoon.
-    WATCH_INDICATOR_24H,        ///< The 24H indicator, indicating that the watch is in a 24-hour mode.
-    WATCH_INDICATOR_LAP         ///< The LAP indicator; the F-91W uses this in its stopwatch UI.
-} WatchIndicatorSegment;
-
-/** @brief Enables the Segment LCD display.
-  * Call this before attempting to set pixels or display strings.
-  */
-void watch_enable_display();
-
-/** @brief Sets a pixel. Use this to manually set a pixel with a given common and segment number.
-  *        See <a href="segmap.html">segmap.html</a>.
-  * @param com the common pin, numbered from 0-2.
-  * @param seg the segment pin, numbered from 0-23.
-  */
-void watch_set_pixel(uint8_t com, uint8_t seg);
-
-/** @brief Clears a pixel. Use this to manually clear a pixel with a given common and segment number.
-  *        See <a href="segmap.html">segmap.html</a>.
-  * @param com the common pin, numbered from 0-2.
-  * @param seg the segment pin, numbered from 0-23.
-  */
-void watch_clear_pixel(uint8_t com, uint8_t seg);
-
-/** @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.
-  * @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
-  *                 clock line occupies positions 4-9.
-  * @note This method does not clear the display; if for example you display a two-character string at
-          position 0, positions 2-9 will retain whatever state they were previously displaying.
-  */
-void watch_display_string(char *string, uint8_t position);
-
-/** @brief Turns the colon segment on.
-  */
-void watch_set_colon();
-
-/** @brief Turns the colon segment off.
-  */
-void watch_clear_colon();
-
-/** @brief Sets an indicator on the LCD. Use this to turn on one of the indicator segments.
-  * @param indicator One of the indicator segments from the enum. @see WatchIndicatorSegment
-  */
-void watch_set_indicator(WatchIndicatorSegment indicator);
-
-/** @brief Clears an indicator on the LCD. Use this to turn off one of the indicator segments.
-  * @param indicator One of the indicator segments from the enum. @see WatchIndicatorSegment
-  */
-void watch_clear_indicator(WatchIndicatorSegment indicator);
-
-/** @brief Clears all indicator segments.
-  * @see WatchIndicatorSegment
-  */
-void watch_clear_all_indicators();
-
-/// @}
-
-
-/** @addtogroup led LED Control
-  * @brief This section covers functions related to the bi-color red/green LED mounted behind the LCD.
-  * @details The SAM L22 is an exceedingly power efficient chip, whereas the LED's are relatively power-
-  *          hungry. The green LED, at full power, consumes more power than the whole chip in active mode,
-  *          and the red LED consumes about twelve times as much power! The LED's should thus be used only
-  *          sparingly in order to preserve battery life.
-  * @todo Explore running the TC3 PWM driver in standby mode; this would require that the user disable it
-  *       in app_prepare_for_sleep, but could allow for low power, low duty indicator LED usage.
-  */
-/// @{
-/** @brief Enables the LED.
-  * @param pwm if true, enables PWM output for brightness control (required to use @ref watch_set_led_color).
-               If false, configures the LED pins as digital outputs.
-  * @note The TC driver required for PWM mode does not run in STANDBY mode. You should keep your app awake
-          while PWM'ing the LED's, and disable them before going to sleep.
-  */
-void watch_enable_led(bool pwm);
-
-/** @brief Disables the LEDs.
-  * @param pwm if true, disables the PWM output. If false, disables the digital outputs.
-  */
-void watch_disable_led(bool pwm);
-
-/** @brief Sets the LED to a custom color by modulating each output's duty cycle.
-  * @param red The red value.
-  * @param green The green value.
-  * @note still working on this, 0-65535 works now but these values may change.
-  */
-void watch_set_led_color(uint16_t red, uint16_t green);
-
-/** @brief Sets the red LED to full brightness, and turns the green LED off.
-  * @note Of the two LED's in the RG bi-color LED, the red LED is the less power-efficient one (~4.5 mA).
-  */
-void watch_set_led_red();
-
-/** @brief Sets the green LED to full brightness, and turns the red LED off.
-  * @note Of the two LED's in the RG bi-color LED, the green LED is the more power-efficient one (~0.44 mA).
-  */
-void watch_set_led_green();
-
-/** @brief Sets both red and green LEDs to full brightness.
-  * @note The total current draw between the two LED's in this mode will be ~5 mA, which is more than the
-  *       watch draws in any other mode. Take care not to drain the battery.
-  */
-void watch_set_led_yellow();
-
-/** @brief Turns both the red and the green LEDs off. */
-void watch_set_led_off();
-/// @}
-
-
-/** @addtogroup buzzer Buzzer
-  * @brief This section covers functions related to the piezo buzzer embedded in the F-91W's back plate.
-  */
-/// @{
-/** @brief Enables the TCC peripheral, which drives the buzzer.
-  */
-void watch_enable_buzzer();
-
-/** @brief Sets the period of the buzzer.
-  * @param period The period of a single cycle for the PWM peripheral. You can use the following formula to
-  *               convert a desired frequency to a period for this function: period = 513751 * (freq^−1.0043)
-  */
-void watch_set_buzzer_period(uint32_t period);
-
-/** @brief Turns the buzzer output on. It will emit a continuous sound at the given frequency.
-  * @note The TCC peripheral that drives the buzzer does not run in standby mode; if you wish for buzzer
-  *       output to continue, you should prevent your app from going to sleep.
-  */
-void watch_set_buzzer_on();
-
-/** @brief Turns the buzzer output off.
-  */
-void watch_set_buzzer_off();
-
-/** @brief Plays the given note for a set duration.
-  * @param note The note you wish to play, or BUZZER_NOTE_REST to disable output for the given duration.
-  * @param duration_ms The duration of the note.
-  * @note Note that this will block your UI for the duration of the note's play time, and it will
-  *       after this call, the buzzer period will be set to the period of this note.
-  */
-void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms);
-
-/** @brief An array of periods for all the notes on a piano, corresponding to the names in BuzzerNote.
-  */
-extern const uint16_t NotePeriods[108];
-
-/// @}
-
-
-/** @addtogroup rtc Real-Time Clock
-  * @brief This section covers functions related to the SAM L22's real-time clock peripheral, including
-  *        date, time and alarm functions.
-  * @details The real-time clock is the only peripheral that main.c enables for you. It is the cornerstone
-  *          of low power operation on the watch, and it is required for several key functions that we
-  *          assume will be available, namely the wake from BACKUP mode and the callback on the ALARM button.
-  *          It is also required for the operation of the 1 Hz tick interrupt, which you will most likely use
-  *          to wake from STANDBY mode.
-  */
-/// @{
-/** @brief Called by main.c to check if the RTC is enabled.
-  * You may call this function, but outside of app_init, it sbould always return true.
-  */
-bool _watch_rtc_is_enabled();
-
-/** @brief Sets the system date and time.
-  * @param date_time A struct representing the date and time you wish to set.
-  */
-void watch_set_date_time(struct calendar_date_time date_time);
-
-/** @brief Returns the system date and time in the provided struct.
-  * @param date_time A pointer to a calendar_date_time struct.
-                     It will be populated with the correct date and time on return.
-  */
-void watch_get_date_time(struct calendar_date_time *date_time);
-
-/** @brief Registers a "tick" callback that will be called once per second.
-  * @param callback The function you wish to have called when the clock ticks.
-  */
-void watch_register_tick_callback(ext_irq_cb_t callback);
-/// @}
-
-
-/** @addtogroup adc Analog Input
-  * @brief This section covers functions related to the SAM L22's analog-to-digital converter, as well as
-  *        configuring and reading values from the three analog-capable pins on the 9-pin connector.
-  */
-/// @{
-/** @brief Enables the ADC peripheral, and configures the selected pin for analog input.
-  * @param pin One of pins A0, A1 or A2.
-  */
-void watch_enable_analog(const uint8_t pin);
-/// @}
-
-
-/** @addtogroup buttons Buttons
-  * @brief This section covers functions related to the three buttons: Light, Mode and Alarm.
-  * @details The buttons are the core input UI of the watch, and the way the user will interact with
-  *          your application. They are active high, pulled down by the microcontroller, and triggered
-  *          when one of the "pushers" brings a tab from the metal frame into contact with the edge
-  *          of the board. Note that the buttons can only wake the watch from STANDBY mode (except maybe for the
-  *          ALARM button; still working on that one). The external interrupt controller runs in
-             STANDBY mode, but it does not runin BACKUP mode; to wake from BACKUP, buttons will not cut it,
-  */
-/// @{
-/** @brief Enables the external interrupt controller for use with the buttons.
-  * @note The BTN_ALARM button runs off of an interrupt in the the RTC controller, not the EIC. If your
-  *       application ONLY makes use of the alarm button, you do not need to call this method; you can
-  *       save ~5µA by leaving the EIC disabled and only registering a callback for BTN_ALARM.
-  */
-void watch_enable_buttons();
-
-/** @brief Configures an external interrupt on one of the button pins.
-  * @param pin One of pins BTN_LIGHT, BTN_MODE or BTN_ALARM.
-  * @param callback The function you wish to have called when the button is pressed.
-  * @note The BTN_ALARM button runs off of an interrupt in the the RTC controller, not the EIC. This
-  *       implementation detail should not make any difference to your app,
-  */
-void watch_register_button_callback(const uint8_t pin, ext_irq_cb_t callback);
-/// @}
-
-
-/** @addtogroup gpio Digital Input and Output
-  * @brief This section covers functions related to general-purpose input and output signals.
-  */
-/// @{
-/** @brief Configures the selected pin for digital input.
-  * @param pin The pin that you wish to act as an input.
-  */
-void watch_enable_digital_input(const uint8_t pin);
-
-/** @brief Enables a pull-up resistor on the selected pin.
-  * @param pin The pin that you wish to configure.
-  */
-void watch_enable_pull_up(const uint8_t pin);
-
-/** @brief Enables a pull-down resistor on the selected pin.
-  * @param pin The pin that you wish to configure.
-  */
-void watch_enable_pull_down(const uint8_t pin);
-
-/** @brief Gets the level of the selected pin.
-  * @param pin The pin whose value you wish to read.
-  * @return true if the pin was logic high; otherwise, false.
-  */
-bool watch_get_pin_level(const uint8_t pin);
-
-/** @brief Configures the selected pin for digital output.
-  * @param pin The pin that you wish to act as an output.
-  */
-void watch_enable_digital_output(const uint8_t pin);
-
-/** @brief Disables digital output on the selected pin.
-  * @param pin The pin that you wish disable.
-  */
-void watch_disable_digital_output(const uint8_t pin);
-
-/** @brief Sets the level of the selected pin.
-  * @param pin The pin whose value you wish to set.
-  * @param level The level you wish to set: true for high, false for low.
-  */
-void watch_set_pin_level(const uint8_t pin, const bool level);
-/// @}
-
-
-/** @addtogroup i2c I2C Controller Driver
-  * @brief This section covers functions related to the SAM L22's built-I2C driver, including
-  *        configuring the I2C bus, putting values directly on the bus and reading data from
-  *        registers on I2C devices.
-  */
-/// @{
-/** @brief Enables the I2C peripheral. Call this before attempting to interface with I2C devices.
-  */
-void watch_enable_i2c();
-
-/** @brief Sends a series of values to a device on the I2C bus.
-  * @param addr The address of the device you wish to talk to.
-  * @param buf A series of unsigned bytes; the data you wish to transmit.
-  * @param length The number of bytes in buf that you wish to send.
-  */
-void watch_i2c_send(int16_t addr, uint8_t *buf, uint16_t length);
-
-/** @brief Receives a series of values from a device on the I2C bus.
-  * @param addr The address of the device you wish to hear from.
-  * @param buf Storage for the incoming bytes; on return, it will contain the received data.
-  * @param length The number of bytes that you wish to receive.
-  */
-void watch_i2c_receive(int16_t addr, uint8_t *buf, uint16_t length);
-
-/** @brief Writes a byte to a register in an I2C device.
-  * @param addr The address of the device you wish to address.
-  * @param reg The register on the device that you wish to set.
-  * @param data The value that you wish to set the register to.
-  */
-void watch_i2c_write8(int16_t addr, uint8_t reg, uint8_t data);
-
-/** @brief Reads a byte from a register in an I2C device.
-  * @param addr The address of the device you wish to address.
-  * @param reg The register on the device that you wish to read.
-  * @return An unsigned byte representing the value of the register that was read.
-  */
-uint8_t watch_i2c_read8(int16_t addr, uint8_t reg);
-
-/** @brief Reads an unsigned little-endian word from a register in an I2C device.
-  * @param addr The address of the device you wish to address.
-  * @param reg The register on the device that you wish to read.
-  * @return An unsigned word representing the value of the register that was read.
-  * @note This reads two bytes into the word in bus order. If the device returns
-          the LSB first and then the MSB, you can use this value as returned.
-          If the device returns the data in big-endian order or uses some other
-          kind of fancy bit packing, you may need to shuffle some bits around.
-  */
-uint16_t watch_i2c_read16(int16_t addr, uint8_t reg);
-
-/** @brief Reads three bytes as an unsigned little-endian int from a register in an I2C device.
-  * @param addr The address of the device you wish to address.
-  * @param reg The register on the device that you wish to read.
-  * @return An unsigned word representing the value of the register that was read.
-  * @note This reads three bytes into the word in bus order. If the device returns
-          these bytes LSB first, you can use this value as returned. If there is a
-          sign bit, the device returns the data in big-endian order, or it uses some
-          other kind of fancy bit packing, you may need to shuffle some bits around.
-  */
-uint32_t watch_i2c_read24(int16_t addr, uint8_t reg);
-
-
-/** @brief Reads an unsigned little-endian int from a register in an I2C device.
-  * @param addr The address of the device you wish to address.
-  * @param reg The register on the device that you wish to read.
-  * @return An unsigned word representing the value of the register that was read.
-  * @note This reads three bytes into the word in bus order. If the device returns
-          these bytes LSB first, you can use this value as returned. If the device
-          returns the data in big-endian order, or it uses some other kind of fancy
-          bit packing, you may need to shuffle some bits around.
-  */
-uint32_t watch_i2c_read32(int16_t addr, uint8_t reg);
-/// @}
-
-/** @addtogroup debug Debug UART
-  * @brief This section covers functions related to the debug UART, available on
-  *        pin D1 of the 9-pin connector.
-  * @todo Refactor this as a USB CDC so that folks can debug over USB.
-  */
-/// @{
-/** @brief Initializes the debug UART.
-  * @param baud The baud rate
-  */
-void watch_enable_debug_uart(uint32_t baud);
-
-/** @brief Outputs a single character on the debug UART.
-  * @param c The character you wish to output.
-  */
-void watch_debug_putc(char c);
-
-/** @brief Outputs a string on the debug UART.
-  * @param s A null-terminated string.
-  */
-void watch_debug_puts(char *s);
-/// @}
-
-
-/** @addtogroup deepsleep Deep Sleep Control
-  * @brief This section covers functions related to preparing for and entering BACKUP mode, the
-  *        deepest sleep mode available on the SAM L22
-  */
-/// @{
-/** @brief Registers a callback on one of the RTC's external wake pins, which can wake the device
-  * from deep sleep mode.
-  * @param pin Either pin A2 or pin D1, the two external wake pins on the nine-pin connector.
-  * @param callback The callback to be called if this pin triggers outside of deep sleep mode.
-  * @note When in normal or STANDBY mode, this will function much like a standard external interrupt
-  *       situation: these pins will wake from standby, and your callback will be called. However,
-  *       if the device enters deep sleep and one of these pins wakes the device, your callback
-  *       WILL NOT be called.
-  */
-void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback);
-
-/** @brief Stores data in one of the RTC's backup registers, which retain their data in deep sleep.
-  * @param data An unsigned 32 bit integer with the data you wish to store.
-  * @param reg A register from 0-7.
-  */
-void watch_store_backup_data(uint32_t data, uint8_t reg);
-
-/** @brief Gets 32 bits of data from the RTC's backup register, which retains its data in deep sleep.
-  * @param reg A register from 0-7.
-  * @return An unsigned 32 bit integer with the from the backup register.
-  */
-uint32_t watch_get_backup_data(uint8_t reg);
-
-/** @brief Enters the SAM L22's lowest-power mode, BACKUP.
-  * @details This function does some housekeeping before entering BACKUP mode. It first disables all
-  *          peripherals except for the RTC, and disables the tick interrupt (since that would wake)
-  *          us up from deep sleep. It also sets an external wake source on the ALARM button, if one
-  *          was not already set. If you wish to wake from another source, such as one of the external
-  *          wake interrupt pins on the 9-pin connector, set that up prior to calling this function.
-  * @note If you have a callback set for an external wake interrupt, it will be called if triggered while
-  *       in ACTIVE, IDLE or STANDBY modes, but it *will not be called* when waking from BACKUP.
-  *       Waking from backup is effectively like waking from reset, except that your @ref
-  *       app_wake_from_deep_sleep function will be called.
-  * @warning In initial testing, it seems like the ALARM_BTN pin (PA02 RTC/IN2) cannot wake the device
-  *          from deep sleep mode. There is an errata note (Reference: 15010, linked) that says that
-  *          due to a silicon bug, PB01 cannot be used as RTC/IN2. It seems though that this bug may
-  *          also affect PA02. As a result â€” and I'm very bummed about this â€” you cannot use deep sleep
-  *          mode unless you set up an external wake interrupt using a device on the nine-pin connector
-  *          (i.e. an accelerometer with an interrupt pin). Otherwise your only option for waking will
-  *          be to unscrew the watch case and press the reset button on the back of the board.
-  *          http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_L22_Family_Errata_DS80000782B.pdf
-  */
-void watch_enter_deep_sleep();
-/// @}
+#include "watch_app.h"
+#include "watch_rtc.h"
+#include "watch_slcd.h"
+#include "watch_extint.h"
+#include "watch_led.h"
+#include "watch_buzzer.h"
+#include "watch_adc.h"
+#include "watch_gpio.h"
+#include "watch_i2c.h"
+#include "watch_uart.h"
+#include "watch_deepsleep.h"
+
+#include "watch_private.h"
 
 #endif /* WATCH_H_ */
\ No newline at end of file
diff --git a/watch-library/watch/watch_adc.c b/watch-library/watch/watch_adc.c
new file mode 100644 (file)
index 0000000..ebb8bb6
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+ static bool ADC_0_ENABLED = false;
+
+void watch_enable_analog(const uint8_t pin) {
+    if (!ADC_0_ENABLED) ADC_0_init();
+    ADC_0_ENABLED = true;
+
+    gpio_set_pin_direction(pin, GPIO_DIRECTION_OFF);
+    switch (pin) {
+        case A0:
+            gpio_set_pin_function(A0, PINMUX_PB04B_ADC_AIN12);
+            break;
+        case A1:
+            gpio_set_pin_function(A1, PINMUX_PB01B_ADC_AIN9);
+            break;
+        case A2:
+            gpio_set_pin_function(A2, PINMUX_PB02B_ADC_AIN10);
+            break;
+        default:
+            return;
+    }
+}
diff --git a/watch-library/watch/watch_adc.h b/watch-library/watch/watch_adc.h
new file mode 100644 (file)
index 0000000..d462036
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+////< @file watch_adc.h
+
+/** @addtogroup adc Analog Input
+  * @brief This section covers functions related to the SAM L22's analog-to-digital converter, as well as
+  *        configuring and reading values from the three analog-capable pins on the 9-pin connector.
+  */
+/// @{
+/** @brief Enables the ADC peripheral, and configures the selected pin for analog input.
+  * @param pin One of pins A0, A1 or A2.
+  */
+void watch_enable_analog(const uint8_t pin);
+/// @}
diff --git a/watch-library/watch/watch_app.c b/watch-library/watch/watch_app.c
new file mode 100644 (file)
index 0000000..0fb87b9
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
\ No newline at end of file
diff --git a/watch-library/watch/watch_app.h b/watch-library/watch/watch_app.h
new file mode 100644 (file)
index 0000000..fd7ea70
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+////< @file watch_app.h
+
+/** @addtogroup app Application Framework
+  * @brief This section covers the functions that you will implement in your app.c file when designing a Sensor Watch app.
+  * @details You should be able to write a watch app by simply implementing these functions and declaring callbacks for
+  *          various GPIO and peripheral interrupts. The main.c file takes care of calling these functions for you. The
+  *          general flow:
+  *
+  *            1. Your app_init() function is called.
+  *               - This method should only be used to set your initial application state.
+  *            2. If your app is waking from BACKUP, app_wake_from_deep_sleep()  is called.
+  *               - If you saved state in the RTC's backup registers, you can restore it here.
+  *            3. Your app_setup() method is called.
+  *               - You may wish to enable some functionality and peripherals here.
+  *               - You should definitely set up some interrupts here.
+  *            4. The main run loop begins: your app_loop() function is called.
+  *               - Run code and update your UI here.
+  *               - Return true if your app is prepared to enter STANDBY mode.
+  *            5. This step differs depending on the value returned by app_loop:
+  *               - If you returned false, execution resumes at (4).
+  *               - If you returned true, app_prepare_for_sleep() is called; execution moves on to (6).
+  *            6. The microcontroller enters the STANDBY sleep mode.
+  *               - No user code will run, and the watch will enter a low power mode.
+  *               - The watch will remain in this state until an interrupt wakes it.
+  *            7. Once woken from STANDBY, your app_wake_from_sleep() function is called.
+  *               - After this, execution resumes at (4).
+  */
+/// @{
+/** @brief A function you will implement to initialize your application state. The app_init function is called before
+  *        anything else. Use it to set up any internal data structures or application state required by your app,
+  *        but don't configure any peripherals just yet.
+  */
+void app_init();
+
+/** @brief A function you will implement to wake from deep sleep mode. The app_wake_from_deep_sleep function is only
+  *        called if your app is waking from the ultra-low power BACKUP sleep mode. You may have chosen to store some
+  *        state in the RTC's backup registers prior to entering this mode. You may restore that state here.
+  */
+void app_wake_from_deep_sleep();
+
+/** @brief A function you will implement to set up your application. The app_setup function is like setup() in Arduino.
+  *        It is called once when the program begins. You should set pin modes and enable any peripherals you want to
+  *        set up (real-time clock, I2C, etc.) Depending on your application, you may or may not want to configure
+  *        sensors on your sensor board here. For example, a low-power accelerometer that will run at all times should
+  *        be configured here, whereas you may want to enable a more power-hungry sensor only when you need it.
+  * @note If your app enters the ultra-low power BACKUP sleep mode, this function will be called again when it wakes
+  *       from that deep sleep state. In this state, the RTC will still be configured with the correct date and time.
+  */
+void app_setup();
+
+/** @brief A function you will implement to serve as the app's main run loop. This method will be called repeatedly,
+           or if you enter STANDBY sleep mode, as soon as the device wakes from sleep.
+  * @return You should return true if your app is prepared to enter STANDBY sleep mode. If you return false, your
+  *         app's app_loop method will be called again immediately. Note that in STANDBY mode, the watch will consume
+  *         only about 95 microamperes of power, whereas if you return false and keep the app awake, it will consume
+  *         about 355 microamperes. This is the difference between months of battery life and days. As much as
+  *         possible, you should limit the amount of time your app spends awake.
+  * @note Only the RTC, the segment LCD controller and the external interrupt controller run in STANDBY mode. If you
+  *       are using, e.g. the PWM function to set a custom LED color, you should return false here until you are
+  *       finished with that operation. Note however that the peripherals will continue running after waking up,
+  *       so e.g. the I2C controller, if configured, will sleep in STANDBY. But you can use it again as soon as your
+  *       app wakes up.
+  */
+bool app_loop();
+
+/** @brief A function you will implement to prepare to enter STANDBY sleep mode. The app_prepare_for_sleep function is
+ *         called before the watch goes into the STANDBY sleep mode. In STANDBY mode, most peripherals are shut down,
+ *         and no code will run until the watch receives an interrupt (generally either the 1Hz tick or a press on one
+ *         of the buttons).
+ * @note If you are PWM'ing the LED or playing a sound on the buzzer, the TC/TCC peripherals that drive those operations
+ *       will not run in STANDBY. BUT! the output pins will retain the state they had when entering standby. This means
+ *       you could end up entering standby with an LED on and draining power, or with a DC potential across the piezo
+ *       buzzer that could damage it if left in this state. If your app_loop does not prevent sleep during these
+ *       activities, you should make sure to disable these outputs in app_prepare_for_sleep.
+ */
+void app_prepare_for_sleep();
+
+/** @brief A method you will implement to configure the app after waking from STANDBY sleep mode.
+  */
+void app_wake_from_sleep();
+
+/// @}
diff --git a/watch-library/watch/watch_buzzer.c b/watch-library/watch/watch_buzzer.c
new file mode 100644 (file)
index 0000000..0474641
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+ inline void watch_enable_buzzer() {
+    PWM_1_init();
+}
+
+inline void watch_set_buzzer_period(uint32_t period) {
+    pwm_set_parameters(&PWM_1, period, period / 2);
+}
+
+inline void watch_set_buzzer_on() {
+       pwm_enable(&PWM_1);
+}
+
+inline void watch_set_buzzer_off() {
+       pwm_disable(&PWM_1);
+}
+
+const uint16_t NotePeriods[108] = {31047, 29301, 27649, 26079, 24617, 23224, 21923, 20683, 19515, 18418, 17377, 16399, 15477, 14603, 13780, 13004, 12272, 11580, 10926, 10311, 9730, 9181, 8664, 8175, 7714, 7280, 6869, 6483, 6117, 5772, 5447, 5140, 4850, 4577, 4319, 4076, 3846, 3629, 3425, 3232, 3050, 2878, 2715, 2562, 2418, 2282, 2153, 2032, 1917, 1809, 1707, 1611, 1520, 1435, 1354, 1277, 1205, 1137, 1073, 1013, 956, 902, 851, 803, 758, 715, 675, 637, 601, 567, 535, 505, 476, 450, 424, 400, 378, 357, 336, 317, 300, 283, 267, 252, 238, 224, 212, 200, 188, 178, 168, 158, 149, 141, 133, 125, 118, 112, 105, 99, 94, 89, 84, 79, 74, 70, 66, 63};
+
+void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms) {
+    if (note == BUZZER_NOTE_REST) {
+        watch_set_buzzer_off();
+    } else {
+        pwm_set_parameters(&PWM_1, NotePeriods[note], NotePeriods[note] / 2);
+        watch_set_buzzer_on();
+    }
+    delay_ms(duration_ms);
+    watch_set_buzzer_off();
+}
diff --git a/watch-library/watch/watch_buzzer.h b/watch-library/watch/watch_buzzer.h
new file mode 100644 (file)
index 0000000..75b30a7
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+////< @file watch_buzzer.h
+
+/** @addtogroup buzzer Buzzer
+  * @brief This section covers functions related to the piezo buzzer embedded in the F-91W's back plate.
+  */
+/// @{
+/** @brief Enables the TCC peripheral, which drives the buzzer.
+  */
+void watch_enable_buzzer();
+
+/** @brief Sets the period of the buzzer.
+  * @param period The period of a single cycle for the PWM peripheral. You can use the following formula to
+  *               convert a desired frequency to a period for this function: period = 513751 * (freq^−1.0043)
+  */
+void watch_set_buzzer_period(uint32_t period);
+
+/** @brief Turns the buzzer output on. It will emit a continuous sound at the given frequency.
+  * @note The TCC peripheral that drives the buzzer does not run in standby mode; if you wish for buzzer
+  *       output to continue, you should prevent your app from going to sleep.
+  */
+void watch_set_buzzer_on();
+
+/** @brief Turns the buzzer output off.
+  */
+void watch_set_buzzer_off();
+
+/// @brief 108 notes for use with watch_buzzer_play_note
+typedef enum BuzzerNote {
+    BUZZER_NOTE_C0 = 0,          ///< 16.35 Hz
+    BUZZER_NOTE_C0SHARP_D0FLAT,  ///< 17.32 Hz
+    BUZZER_NOTE_D0,              ///< 18.35 Hz
+    BUZZER_NOTE_D0SHARP_E0FLAT,  ///< 19.45 Hz
+    BUZZER_NOTE_E0,              ///< 20.60 Hz
+    BUZZER_NOTE_F0,              ///< 21.83 Hz
+    BUZZER_NOTE_F0SHARP_G0FLAT,  ///< 23.12 Hz
+    BUZZER_NOTE_G0,              ///< 24.50 Hz
+    BUZZER_NOTE_G0SHARP_A0FLAT,  ///< 25.96 Hz
+    BUZZER_NOTE_A0,              ///< 27.50 Hz
+    BUZZER_NOTE_A0SHARP_B0FLAT,  ///< 29.14 Hz
+    BUZZER_NOTE_B0,              ///< 30.87 Hz
+    BUZZER_NOTE_C1,              ///< 32.70 Hz
+    BUZZER_NOTE_C1SHARP_D1FLAT,  ///< 34.65 Hz
+    BUZZER_NOTE_D1,              ///< 36.71 Hz
+    BUZZER_NOTE_D1SHARP_E1FLAT,  ///< 38.89 Hz
+    BUZZER_NOTE_E1,              ///< 41.20 Hz
+    BUZZER_NOTE_F1,              ///< 43.65 Hz
+    BUZZER_NOTE_F1SHARP_G1FLAT,  ///< 46.25 Hz
+    BUZZER_NOTE_G1,              ///< 49.00 Hz
+    BUZZER_NOTE_G1SHARP_A1FLAT,  ///< 51.91 Hz
+    BUZZER_NOTE_A1,              ///< 55.00 Hz
+    BUZZER_NOTE_A1SHARP_B1FLAT,  ///< 58.27 Hz
+    BUZZER_NOTE_B1,              ///< 61.74 Hz
+    BUZZER_NOTE_C2,              ///< 65.41 Hz
+    BUZZER_NOTE_C2SHARP_D2FLAT,  ///< 69.30 Hz
+    BUZZER_NOTE_D2,              ///< 73.42 Hz
+    BUZZER_NOTE_D2SHARP_E2FLAT,  ///< 77.78 Hz
+    BUZZER_NOTE_E2,              ///< 82.41 Hz
+    BUZZER_NOTE_F2,              ///< 87.31 Hz
+    BUZZER_NOTE_F2SHARP_G2FLAT,  ///< 92.50 Hz
+    BUZZER_NOTE_G2,              ///< 98.00 Hz
+    BUZZER_NOTE_G2SHARP_A2FLAT,  ///< 103.83 Hz
+    BUZZER_NOTE_A2,              ///< 110.00 Hz
+    BUZZER_NOTE_A2SHARP_B2FLAT,  ///< 116.54 Hz
+    BUZZER_NOTE_B2,              ///< 123.47 Hz
+    BUZZER_NOTE_C3,              ///< 130.81 Hz
+    BUZZER_NOTE_C3SHARP_D3FLAT,  ///< 138.59 Hz
+    BUZZER_NOTE_D3,              ///< 146.83 Hz
+    BUZZER_NOTE_D3SHARP_E3FLAT,  ///< 155.56 Hz
+    BUZZER_NOTE_E3,              ///< 164.81 Hz
+    BUZZER_NOTE_F3,              ///< 174.61 Hz
+    BUZZER_NOTE_F3SHARP_G3FLAT,  ///< 185.00 Hz
+    BUZZER_NOTE_G3,              ///< 196.00 Hz
+    BUZZER_NOTE_G3SHARP_A3FLAT,  ///< 207.65 Hz
+    BUZZER_NOTE_A3,              ///< 220.00 Hz
+    BUZZER_NOTE_A3SHARP_B3FLAT,  ///< 233.08 Hz
+    BUZZER_NOTE_B3,              ///< 246.94 Hz
+    BUZZER_NOTE_C4,              ///< 261.63 Hz
+    BUZZER_NOTE_C4SHARP_D4FLAT,  ///< 277.18 Hz
+    BUZZER_NOTE_D4,              ///< 293.66 Hz
+    BUZZER_NOTE_D4SHARP_E4FLAT,  ///< 311.13 Hz
+    BUZZER_NOTE_E4,              ///< 329.63 Hz
+    BUZZER_NOTE_F4,              ///< 349.23 Hz
+    BUZZER_NOTE_F4SHARP_G4FLAT,  ///< 369.99 Hz
+    BUZZER_NOTE_G4,              ///< 392.00 Hz
+    BUZZER_NOTE_G4SHARP_A4FLAT,  ///< 415.30 Hz
+    BUZZER_NOTE_A4,              ///< 440.00 Hz
+    BUZZER_NOTE_A4SHARP_B4FLAT,  ///< 466.16 Hz
+    BUZZER_NOTE_B4,              ///< 493.88 Hz
+    BUZZER_NOTE_C5,              ///< 523.25 Hz
+    BUZZER_NOTE_C5SHARP_D5FLAT,  ///< 554.37 Hz
+    BUZZER_NOTE_D5,              ///< 587.33 Hz
+    BUZZER_NOTE_D5SHARP_E5FLAT,  ///< 622.25 Hz
+    BUZZER_NOTE_E5,              ///< 659.25 Hz
+    BUZZER_NOTE_F5,              ///< 698.46 Hz
+    BUZZER_NOTE_F5SHARP_G5FLAT,  ///< 739.99 Hz
+    BUZZER_NOTE_G5,              ///< 783.99 Hz
+    BUZZER_NOTE_G5SHARP_A5FLAT,  ///< 830.61 Hz
+    BUZZER_NOTE_A5,              ///< 880.00 Hz
+    BUZZER_NOTE_A5SHARP_B5FLAT,  ///< 932.33 Hz
+    BUZZER_NOTE_B5,              ///< 987.77 Hz
+    BUZZER_NOTE_C6,              ///< 1046.50 Hz
+    BUZZER_NOTE_C6SHARP_D6FLAT,  ///< 1108.73 Hz
+    BUZZER_NOTE_D6,              ///< 1174.66 Hz
+    BUZZER_NOTE_D6SHARP_E6FLAT,  ///< 1244.51 Hz
+    BUZZER_NOTE_E6,              ///< 1318.51 Hz
+    BUZZER_NOTE_F6,              ///< 1396.91 Hz
+    BUZZER_NOTE_F6SHARP_G6FLAT,  ///< 1479.98 Hz
+    BUZZER_NOTE_G6,              ///< 1567.98 Hz
+    BUZZER_NOTE_G6SHARP_A6FLAT,  ///< 1661.22 Hz
+    BUZZER_NOTE_A6,              ///< 1760.00 Hz
+    BUZZER_NOTE_A6SHARP_B6FLAT,  ///< 1864.66 Hz
+    BUZZER_NOTE_B6,              ///< 1975.53 Hz
+    BUZZER_NOTE_C7,              ///< 2093.00 Hz
+    BUZZER_NOTE_C7SHARP_D7FLAT,  ///< 2217.46 Hz
+    BUZZER_NOTE_D7,              ///< 2349.32 Hz
+    BUZZER_NOTE_D7SHARP_E7FLAT,  ///< 2489.02 Hz
+    BUZZER_NOTE_E7,              ///< 2637.02 Hz
+    BUZZER_NOTE_F7,              ///< 2793.83 Hz
+    BUZZER_NOTE_F7SHARP_G7FLAT,  ///< 2959.96 Hz
+    BUZZER_NOTE_G7,              ///< 3135.96 Hz
+    BUZZER_NOTE_G7SHARP_A7FLAT,  ///< 3322.44 Hz
+    BUZZER_NOTE_A7,              ///< 3520.00 Hz
+    BUZZER_NOTE_A7SHARP_B7FLAT,  ///< 3729.31 Hz
+    BUZZER_NOTE_B7,              ///< 3951.07 Hz
+    BUZZER_NOTE_C8,              ///< 4186.01 Hz
+    BUZZER_NOTE_C8SHARP_D8FLAT,  ///< 4434.92 Hz
+    BUZZER_NOTE_D8,              ///< 4698.63 Hz
+    BUZZER_NOTE_D8SHARP_E8FLAT,  ///< 4978.03 Hz
+    BUZZER_NOTE_E8,              ///< 5274.04 Hz
+    BUZZER_NOTE_F8,              ///< 5587.65 Hz
+    BUZZER_NOTE_F8SHARP_G8FLAT,  ///< 5919.91 Hz
+    BUZZER_NOTE_G8,              ///< 6271.93 Hz
+    BUZZER_NOTE_G8SHARP_A8FLAT,  ///< 6644.88 Hz
+    BUZZER_NOTE_A8,              ///< 7040.00 Hz
+    BUZZER_NOTE_A8SHARP_B8FLAT,  ///< 7458.62 Hz
+    BUZZER_NOTE_B8,              ///< 7902.13 Hz
+    BUZZER_NOTE_REST             ///< no sound
+} BuzzerNote;
+
+/** @brief Plays the given note for a set duration.
+  * @param note The note you wish to play, or BUZZER_NOTE_REST to disable output for the given duration.
+  * @param duration_ms The duration of the note.
+  * @note Note that this will block your UI for the duration of the note's play time, and it will
+  *       after this call, the buzzer period will be set to the period of this note.
+  */
+void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms);
+
+/// @brief An array of periods for all the notes on a piano, corresponding to the names in BuzzerNote.
+extern const uint16_t NotePeriods[108];
+
+/// @}
diff --git a/watch-library/watch/watch_deepsleep.c b/watch-library/watch/watch_deepsleep.c
new file mode 100644 (file)
index 0000000..53a41b5
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+ext_irq_cb_t a2_callback;
+ext_irq_cb_t d1_callback;
+
+ static void extwake_callback(uint8_t reason) {
+    if (reason & RTC_TAMPID_TAMPID2) {
+        if (btn_alarm_callback != NULL) btn_alarm_callback();
+    } else if (reason & RTC_TAMPID_TAMPID1) {
+        if (a2_callback != NULL) a2_callback();
+    } else if (reason & RTC_TAMPID_TAMPID0) {
+        if (d1_callback != NULL) d1_callback();
+    }
+}
+
+void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback) {
+    uint32_t pinmux;
+    if (pin == D1) {
+        d1_callback = callback;
+        pinmux = PINMUX_PB00G_RTC_IN0;
+    } else if (pin == A2) {
+        a2_callback = callback;
+        pinmux = PINMUX_PB02G_RTC_IN1;
+    } else {
+        return;
+    }
+    gpio_set_pin_direction(pin, GPIO_DIRECTION_IN);
+    gpio_set_pin_function(pin, pinmux);
+    _extwake_register_callback(&CALENDAR_0.device, extwake_callback);
+}
+
+void watch_store_backup_data(uint32_t data, uint8_t reg) {
+    if (reg < 8) {
+        RTC->MODE0.BKUP[reg].reg = data;
+    }
+}
+
+uint32_t watch_get_backup_data(uint8_t reg) {
+    if (reg < 8) {
+        return RTC->MODE0.BKUP[reg].reg;
+    }
+
+    return 0;
+}
+
+void watch_enter_deep_sleep() {
+    // enable and configure the external wake interrupt, if not already set up.
+    if (btn_alarm_callback == NULL && a2_callback == NULL && d1_callback == NULL) {
+        gpio_set_pin_direction(BTN_ALARM, GPIO_DIRECTION_IN);
+        gpio_set_pin_pull_mode(BTN_ALARM, GPIO_PULL_DOWN);
+        gpio_set_pin_function(BTN_ALARM, PINMUX_PA02G_RTC_IN2);
+        _extwake_register_callback(&CALENDAR_0.device, extwake_callback);
+    }
+
+    // disable SLCD
+    slcd_sync_deinit(&SEGMENT_LCD_0);
+    hri_mclk_clear_APBCMASK_SLCD_bit(SLCD);
+
+    // TODO: disable other peripherals
+
+    // go into backup sleep mode
+    sleep(5);
+}
diff --git a/watch-library/watch/watch_deepsleep.h b/watch-library/watch/watch_deepsleep.h
new file mode 100644 (file)
index 0000000..cea6f1a
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+////< @file watch_deepsleep.h
+
+/** @addtogroup deepsleep Deep Sleep Control
+  * @brief This section covers functions related to preparing for and entering BACKUP mode, the
+  *        deepest sleep mode available on the SAM L22
+  */
+/// @{
+/** @brief Registers a callback on one of the RTC's external wake pins, which can wake the device
+  * from deep sleep mode.
+  * @param pin Either pin A2 or pin D1, the two external wake pins on the nine-pin connector.
+  * @param callback The callback to be called if this pin triggers outside of deep sleep mode.
+  * @note When in normal or STANDBY mode, this will function much like a standard external interrupt
+  *       situation: these pins will wake from standby, and your callback will be called. However,
+  *       if the device enters deep sleep and one of these pins wakes the device, your callback
+  *       WILL NOT be called.
+  */
+void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback);
+
+/** @brief Stores data in one of the RTC's backup registers, which retain their data in deep sleep.
+  * @param data An unsigned 32 bit integer with the data you wish to store.
+  * @param reg A register from 0-7.
+  */
+void watch_store_backup_data(uint32_t data, uint8_t reg);
+
+/** @brief Gets 32 bits of data from the RTC's backup register, which retains its data in deep sleep.
+  * @param reg A register from 0-7.
+  * @return An unsigned 32 bit integer with the from the backup register.
+  */
+uint32_t watch_get_backup_data(uint8_t reg);
+
+/** @brief Enters the SAM L22's lowest-power mode, BACKUP.
+  * @details This function does some housekeeping before entering BACKUP mode. It first disables all
+  *          peripherals except for the RTC, and disables the tick interrupt (since that would wake)
+  *          us up from deep sleep. It also sets an external wake source on the ALARM button, if one
+  *          was not already set. If you wish to wake from another source, such as one of the external
+  *          wake interrupt pins on the 9-pin connector, set that up prior to calling this function.
+  * @note If you have a callback set for an external wake interrupt, it will be called if triggered while
+  *       in ACTIVE, IDLE or STANDBY modes, but it *will not be called* when waking from BACKUP.
+  *       Waking from backup is effectively like waking from reset, except that your @ref
+  *       app_wake_from_deep_sleep function will be called.
+  * @warning In initial testing, it seems like the ALARM_BTN pin (PA02 RTC/IN2) cannot wake the device
+  *          from deep sleep mode. There is an errata note (Reference: 15010, linked) that says that
+  *          due to a silicon bug, PB01 cannot be used as RTC/IN2. It seems though that this bug may
+  *          also affect PA02. As a result â€” and I'm very bummed about this â€” you cannot use deep sleep
+  *          mode unless you set up an external wake interrupt using a device on the nine-pin connector
+  *          (i.e. an accelerometer with an interrupt pin). Otherwise your only option for waking will
+  *          be to unscrew the watch case and press the reset button on the back of the board.
+  *          http://ww1.microchip.com/downloads/en/DeviceDoc/SAM_L22_Family_Errata_DS80000782B.pdf
+  */
+void watch_enter_deep_sleep();
+/// @}
diff --git a/watch-library/watch/watch_extint.c b/watch-library/watch/watch_extint.c
new file mode 100644 (file)
index 0000000..19f6041
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+ void watch_enable_buttons() {
+    EXTERNAL_IRQ_0_init();
+}
+
+void watch_register_button_callback(const uint8_t pin, ext_irq_cb_t callback) {
+    if (pin == BTN_ALARM) {
+        gpio_set_pin_direction(BTN_ALARM, GPIO_DIRECTION_IN);
+        gpio_set_pin_pull_mode(BTN_ALARM, GPIO_PULL_DOWN);
+        gpio_set_pin_function(BTN_ALARM, PINMUX_PA02G_RTC_IN2);
+        btn_alarm_callback = callback;
+        _extwake_register_callback(&CALENDAR_0.device, extwake_callback);
+    } else {
+        ext_irq_register(pin, callback);
+    }
+}
diff --git a/watch-library/watch/watch_extint.h b/watch-library/watch/watch_extint.h
new file mode 100644 (file)
index 0000000..7129590
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+////< @file watch_extint.h
+
+#include "hal_ext_irq.h"
+
+/** @addtogroup buttons Buttons
+  * @brief This section covers functions related to the three buttons: Light, Mode and Alarm.
+  * @details The buttons are the core input UI of the watch, and the way the user will interact with
+  *          your application. They are active high, pulled down by the microcontroller, and triggered
+  *          when one of the "pushers" brings a tab from the metal frame into contact with the edge
+  *          of the board. Note that the buttons can only wake the watch from STANDBY mode (except maybe for the
+  *          ALARM button; still working on that one). The external interrupt controller runs in
+             STANDBY mode, but it does not runin BACKUP mode; to wake from BACKUP, buttons will not cut it,
+  */
+/// @{
+/** @brief Enables the external interrupt controller for use with the buttons.
+  * @note The BTN_ALARM button runs off of an interrupt in the the RTC controller, not the EIC. If your
+  *       application ONLY makes use of the alarm button, you do not need to call this method; you can
+  *       save ~5µA by leaving the EIC disabled and only registering a callback for BTN_ALARM.
+  */
+void watch_enable_buttons();
+
+/** @brief Configures an external interrupt on one of the button pins.
+  * @param pin One of pins BTN_LIGHT, BTN_MODE or BTN_ALARM.
+  * @param callback The function you wish to have called when the button is pressed.
+  * @note The BTN_ALARM button runs off of an interrupt in the the RTC controller, not the EIC. This
+  *       implementation detail should not make any difference to your app,
+  */
+void watch_register_button_callback(const uint8_t pin, ext_irq_cb_t callback);
+/// @}
diff --git a/watch-library/watch/watch_gpio.c b/watch-library/watch/watch_gpio.c
new file mode 100644 (file)
index 0000000..460a38d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+ void watch_enable_digital_input(const uint8_t pin) {
+    gpio_set_pin_direction(pin, GPIO_DIRECTION_IN);
+    gpio_set_pin_function(pin, GPIO_PIN_FUNCTION_OFF);
+}
+
+void watch_enable_pull_up(const uint8_t pin) {
+    gpio_set_pin_pull_mode(pin, GPIO_PULL_UP);
+}
+
+void watch_enable_pull_down(const uint8_t pin) {
+    gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN);
+}
+
+bool watch_get_pin_level(const uint8_t pin) {
+    return gpio_get_pin_level(pin);
+}
+
+void watch_enable_digital_output(const uint8_t pin) {
+    gpio_set_pin_direction(pin, GPIO_DIRECTION_OUT);
+    gpio_set_pin_function(pin, GPIO_PIN_FUNCTION_OFF);
+}
+
+void watch_disable_digital_output(const uint8_t pin) {
+    gpio_set_pin_direction(pin, GPIO_DIRECTION_OFF);
+}
+
+void watch_set_pin_level(const uint8_t pin, const bool level) {
+    gpio_set_pin_level(pin, level);
+}
diff --git a/watch-library/watch/watch_gpio.h b/watch-library/watch/watch_gpio.h
new file mode 100644 (file)
index 0000000..f7678f6
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+////< @file watch_gpio.h
+
+/** @addtogroup gpio Digital Input and Output
+  * @brief This section covers functions related to general-purpose input and output signals.
+  */
+/// @{
+/** @brief Configures the selected pin for digital input.
+  * @param pin The pin that you wish to act as an input.
+  */
+void watch_enable_digital_input(const uint8_t pin);
+
+/** @brief Enables a pull-up resistor on the selected pin.
+  * @param pin The pin that you wish to configure.
+  */
+void watch_enable_pull_up(const uint8_t pin);
+
+/** @brief Enables a pull-down resistor on the selected pin.
+  * @param pin The pin that you wish to configure.
+  */
+void watch_enable_pull_down(const uint8_t pin);
+
+/** @brief Gets the level of the selected pin.
+  * @param pin The pin whose value you wish to read.
+  * @return true if the pin was logic high; otherwise, false.
+  */
+bool watch_get_pin_level(const uint8_t pin);
+
+/** @brief Configures the selected pin for digital output.
+  * @param pin The pin that you wish to act as an output.
+  */
+void watch_enable_digital_output(const uint8_t pin);
+
+/** @brief Disables digital output on the selected pin.
+  * @param pin The pin that you wish disable.
+  */
+void watch_disable_digital_output(const uint8_t pin);
+
+/** @brief Sets the level of the selected pin.
+  * @param pin The pin whose value you wish to set.
+  * @param level The level you wish to set: true for high, false for low.
+  */
+void watch_set_pin_level(const uint8_t pin, const bool level);
+/// @}
diff --git a/watch-library/watch/watch_i2c.c b/watch-library/watch/watch_i2c.c
new file mode 100644 (file)
index 0000000..bded405
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+ struct io_descriptor *I2C_0_io;
+
+void watch_enable_i2c() {
+    I2C_0_init();
+    i2c_m_sync_get_io_descriptor(&I2C_0, &I2C_0_io);
+    i2c_m_sync_enable(&I2C_0);
+}
+
+void watch_i2c_send(int16_t addr, uint8_t *buf, uint16_t length) {
+    i2c_m_sync_set_periphaddr(&I2C_0, addr, I2C_M_SEVEN);
+    io_write(I2C_0_io, buf, length);
+}
+
+void watch_i2c_receive(int16_t addr, uint8_t *buf, uint16_t length) {
+    i2c_m_sync_set_periphaddr(&I2C_0, addr, I2C_M_SEVEN);
+    io_read(I2C_0_io, buf, length);
+}
+
+void watch_i2c_write8(int16_t addr, uint8_t reg, uint8_t data) {
+    uint8_t buf[2];
+    buf[0] = reg;
+    buf[1] = data;
+
+    watch_i2c_send(addr, (uint8_t *)&buf, 2);
+}
+
+uint8_t watch_i2c_read8(int16_t addr, uint8_t reg) {
+    uint8_t data;
+
+    watch_i2c_send(addr, (uint8_t *)&reg, 1);
+    watch_i2c_receive(addr, (uint8_t *)&data, 1);
+
+    return data;
+}
+
+uint16_t watch_i2c_read16(int16_t addr, uint8_t reg) {
+    uint16_t data;
+
+    watch_i2c_send(addr, (uint8_t *)&reg, 1);
+    watch_i2c_receive(addr, (uint8_t *)&data, 2);
+
+    return data;
+}
+
+uint32_t watch_i2c_read24(int16_t addr, uint8_t reg) {
+    uint32_t data;
+    data = 0;
+
+    watch_i2c_send(addr, (uint8_t *)&reg, 1);
+    watch_i2c_receive(addr, (uint8_t *)&data, 3);
+
+    return data << 8;
+}
+
+uint32_t watch_i2c_read32(int16_t addr, uint8_t reg) {
+    uint32_t data;
+
+    watch_i2c_send(addr, (uint8_t *)&reg, 1);
+    watch_i2c_receive(addr, (uint8_t *)&data, 4);
+
+    return data;
+}
diff --git a/watch-library/watch/watch_i2c.h b/watch-library/watch/watch_i2c.h
new file mode 100644 (file)
index 0000000..e944bba
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+////< @file watch_i2c.h
+
+/** @addtogroup i2c I2C Controller Driver
+  * @brief This section covers functions related to the SAM L22's built-I2C driver, including
+  *        configuring the I2C bus, putting values directly on the bus and reading data from
+  *        registers on I2C devices.
+  */
+/// @{
+/** @brief Enables the I2C peripheral. Call this before attempting to interface with I2C devices.
+  */
+void watch_enable_i2c();
+
+/** @brief Sends a series of values to a device on the I2C bus.
+  * @param addr The address of the device you wish to talk to.
+  * @param buf A series of unsigned bytes; the data you wish to transmit.
+  * @param length The number of bytes in buf that you wish to send.
+  */
+void watch_i2c_send(int16_t addr, uint8_t *buf, uint16_t length);
+
+/** @brief Receives a series of values from a device on the I2C bus.
+  * @param addr The address of the device you wish to hear from.
+  * @param buf Storage for the incoming bytes; on return, it will contain the received data.
+  * @param length The number of bytes that you wish to receive.
+  */
+void watch_i2c_receive(int16_t addr, uint8_t *buf, uint16_t length);
+
+/** @brief Writes a byte to a register in an I2C device.
+  * @param addr The address of the device you wish to address.
+  * @param reg The register on the device that you wish to set.
+  * @param data The value that you wish to set the register to.
+  */
+void watch_i2c_write8(int16_t addr, uint8_t reg, uint8_t data);
+
+/** @brief Reads a byte from a register in an I2C device.
+  * @param addr The address of the device you wish to address.
+  * @param reg The register on the device that you wish to read.
+  * @return An unsigned byte representing the value of the register that was read.
+  */
+uint8_t watch_i2c_read8(int16_t addr, uint8_t reg);
+
+/** @brief Reads an unsigned little-endian word from a register in an I2C device.
+  * @param addr The address of the device you wish to address.
+  * @param reg The register on the device that you wish to read.
+  * @return An unsigned word representing the value of the register that was read.
+  * @note This reads two bytes into the word in bus order. If the device returns
+          the LSB first and then the MSB, you can use this value as returned.
+          If the device returns the data in big-endian order or uses some other
+          kind of fancy bit packing, you may need to shuffle some bits around.
+  */
+uint16_t watch_i2c_read16(int16_t addr, uint8_t reg);
+
+/** @brief Reads three bytes as an unsigned little-endian int from a register in an I2C device.
+  * @param addr The address of the device you wish to address.
+  * @param reg The register on the device that you wish to read.
+  * @return An unsigned word representing the value of the register that was read.
+  * @note This reads three bytes into the word in bus order. If the device returns
+          these bytes LSB first, you can use this value as returned. If there is a
+          sign bit, the device returns the data in big-endian order, or it uses some
+          other kind of fancy bit packing, you may need to shuffle some bits around.
+  */
+uint32_t watch_i2c_read24(int16_t addr, uint8_t reg);
+
+
+/** @brief Reads an unsigned little-endian int from a register in an I2C device.
+  * @param addr The address of the device you wish to address.
+  * @param reg The register on the device that you wish to read.
+  * @return An unsigned word representing the value of the register that was read.
+  * @note This reads three bytes into the word in bus order. If the device returns
+          these bytes LSB first, you can use this value as returned. If the device
+          returns the data in big-endian order, or it uses some other kind of fancy
+          bit packing, you may need to shuffle some bits around.
+  */
+uint32_t watch_i2c_read32(int16_t addr, uint8_t reg);
+/// @}
diff --git a/watch-library/watch/watch_led.c b/watch-library/watch/watch_led.c
new file mode 100644 (file)
index 0000000..01f59fc
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+ bool PWM_0_enabled = false;
+
+void watch_enable_led(bool pwm) {
+    if (pwm) {
+        if (PWM_0_enabled) return;
+
+        PWM_0_init();
+        pwm_set_parameters(&PWM_0, 10000, 0);
+        pwm_enable(&PWM_0);
+
+        PWM_0_enabled = true;
+    } else {
+        watch_enable_digital_output(RED);
+        watch_enable_digital_output(GREEN);
+    }
+    watch_set_led_off();
+}
+
+void watch_disable_led(bool pwm) {
+    if (pwm) {
+        if (!PWM_0_enabled) return;
+        pwm_disable(&PWM_0);
+        PWM_0_enabled = false;
+    }
+
+    watch_disable_digital_output(RED);
+    watch_disable_digital_output(GREEN);
+}
+
+void watch_set_led_color(uint16_t red, uint16_t green) {
+    if (PWM_0_enabled) {
+        TC3->COUNT16.CC[0].reg = red;
+        TC3->COUNT16.CC[1].reg = green;
+    }
+}
+
+void watch_set_led_red() {
+    if (PWM_0_enabled) {
+        watch_set_led_color(65535, 0);
+    } else {
+        watch_set_pin_level(RED, true);
+        watch_set_pin_level(GREEN, false);
+    }
+}
+
+void watch_set_led_green() {
+    if (PWM_0_enabled) {
+        watch_set_led_color(65535, 0);
+    } else {
+        watch_set_pin_level(RED, false);
+        watch_set_pin_level(GREEN, true);
+    }
+}
+
+void watch_set_led_yellow() {
+    if (PWM_0_enabled) {
+        watch_set_led_color(65535, 65535);
+    } else {
+        watch_set_pin_level(RED, true);
+        watch_set_pin_level(GREEN, true);
+    }
+}
+
+void watch_set_led_off() {
+    if (PWM_0_enabled) {
+        watch_set_led_color(0, 0);
+    } else {
+        watch_set_pin_level(RED, false);
+        watch_set_pin_level(GREEN, false);
+    }
+}
diff --git a/watch-library/watch/watch_led.h b/watch-library/watch/watch_led.h
new file mode 100644 (file)
index 0000000..04b6e5f
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+////< @file watch_led.h
+
+/** @addtogroup led LED Control
+  * @brief This section covers functions related to the bi-color red/green LED mounted behind the LCD.
+  * @details The SAM L22 is an exceedingly power efficient chip, whereas the LED's are relatively power-
+  *          hungry. The green LED, at full power, consumes more power than the whole chip in active mode,
+  *          and the red LED consumes about twelve times as much power! The LED's should thus be used only
+  *          sparingly in order to preserve battery life.
+  * @todo Explore running the TC3 PWM driver in standby mode; this would require that the user disable it
+  *       in app_prepare_for_sleep, but could allow for low power, low duty indicator LED usage.
+  */
+/// @{
+/** @brief Enables the LED.
+  * @param pwm if true, enables PWM output for brightness control (required to use @ref watch_set_led_color).
+               If false, configures the LED pins as digital outputs.
+  * @note The TC driver required for PWM mode does not run in STANDBY mode. You should keep your app awake
+          while PWM'ing the LED's, and disable them before going to sleep.
+  */
+void watch_enable_led(bool pwm);
+
+/** @brief Disables the LEDs.
+  * @param pwm if true, disables the PWM output. If false, disables the digital outputs.
+  */
+void watch_disable_led(bool pwm);
+
+/** @brief Sets the LED to a custom color by modulating each output's duty cycle.
+  * @param red The red value.
+  * @param green The green value.
+  * @note still working on this, 0-65535 works now but these values may change.
+  */
+void watch_set_led_color(uint16_t red, uint16_t green);
+
+/** @brief Sets the red LED to full brightness, and turns the green LED off.
+  * @note Of the two LED's in the RG bi-color LED, the red LED is the less power-efficient one (~4.5 mA).
+  */
+void watch_set_led_red();
+
+/** @brief Sets the green LED to full brightness, and turns the red LED off.
+  * @note Of the two LED's in the RG bi-color LED, the green LED is the more power-efficient one (~0.44 mA).
+  */
+void watch_set_led_green();
+
+/** @brief Sets both red and green LEDs to full brightness.
+  * @note The total current draw between the two LED's in this mode will be ~5 mA, which is more than the
+  *       watch draws in any other mode. Take care not to drain the battery.
+  */
+void watch_set_led_yellow();
+
+/** @brief Turns both the red and the green LEDs off. */
+void watch_set_led_off();
+/// @}
diff --git a/watch-library/watch/watch_private.c b/watch-library/watch/watch_private.c
new file mode 100644 (file)
index 0000000..bfe171f
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+void _watch_init() {
+    // disable the LED pin (it may have been enabled by the bootloader)
+    watch_disable_digital_output(RED);
+
+    // Use switching regulator for lower power consumption.
+    SUPC->VREG.bit.SEL = 1;
+    while(!SUPC->STATUS.bit.VREGRDY);
+
+    // External wake depends on RTC; calendar is a required module.
+    CALENDAR_0_init();
+    calendar_enable(&CALENDAR_0);
+
+    // Not sure if this belongs in every app -- is there a power impact?
+    delay_driver_init();
+
+    // set up state
+    btn_alarm_callback = NULL;
+    a2_callback = NULL;
+    d1_callback = NULL;
+}
diff --git a/watch-library/watch/watch_private.h b/watch-library/watch/watch_private.h
new file mode 100644 (file)
index 0000000..9917112
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2021 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/// Called by main.c while setting up the app. You should not call this from your app.
+void _watch_init();
diff --git a/watch-library/watch/watch_rtc.c b/watch-library/watch/watch_rtc.c
new file mode 100644 (file)
index 0000000..2d6d598
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+ bool _watch_rtc_is_enabled() {
+    return RTC->MODE0.CTRLA.bit.ENABLE;
+}
+
+void watch_set_date_time(struct calendar_date_time date_time) {
+    calendar_set_date(&CALENDAR_0, &date_time.date);
+    calendar_set_time(&CALENDAR_0, &date_time.time);
+}
+
+void watch_get_date_time(struct calendar_date_time *date_time) {
+    calendar_get_date_time(&CALENDAR_0, date_time);
+}
+
+void watch_register_tick_callback(ext_irq_cb_t callback) {
+    _prescaler_register_callback(&CALENDAR_0.device, callback);
+}
diff --git a/watch-library/watch/watch_rtc.h b/watch-library/watch/watch_rtc.h
new file mode 100644 (file)
index 0000000..c685ac2
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+////< @file watch_rtc.h
+
+#include "hpl_calendar.h"
+
+/** @addtogroup rtc Real-Time Clock
+  * @brief This section covers functions related to the SAM L22's real-time clock peripheral, including
+  *        date, time and alarm functions.
+  * @details The real-time clock is the only peripheral that main.c enables for you. It is the cornerstone
+  *          of low power operation on the watch, and it is required for several key functions that we
+  *          assume will be available, namely the wake from BACKUP mode and the callback on the ALARM button.
+  *          It is also required for the operation of the 1 Hz tick interrupt, which you will most likely use
+  *          to wake from STANDBY mode.
+  */
+/// @{
+/** @brief Called by main.c to check if the RTC is enabled.
+  * You may call this function, but outside of app_init, it sbould always return true.
+  */
+bool _watch_rtc_is_enabled();
+
+/** @brief Sets the system date and time.
+  * @param date_time A struct representing the date and time you wish to set.
+  */
+void watch_set_date_time(struct calendar_date_time date_time);
+
+/** @brief Returns the system date and time in the provided struct.
+  * @param date_time A pointer to a calendar_date_time struct.
+                     It will be populated with the correct date and time on return.
+  */
+void watch_get_date_time(struct calendar_date_time *date_time);
+
+/** @brief Registers a "tick" callback that will be called once per second.
+  * @param callback The function you wish to have called when the clock ticks.
+  */
+void watch_register_tick_callback(ext_irq_cb_t callback);
+/// @}
diff --git a/watch-library/watch/watch_slcd.c b/watch-library/watch/watch_slcd.c
new file mode 100644 (file)
index 0000000..cdade07
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+ //////////////////////////////////////////////////////////////////////////////////////////
+// Segmented Display
+
+static const uint8_t Character_Set[] =
+{
+    0b00000000, //  
+    0b00000000, // ! (unused)
+    0b00100010, // "
+    0b01100011, // # (degree symbol, hash mark doesn't fit)
+    0b00000000, // $ (unused)
+    0b00000000, // % (unused)
+    0b01000100, // & ("lowercase 7" for positions 4 and 6)
+    0b00100000, // '
+    0b00111001, // (
+    0b00001111, // )
+    0b00000000, // * (unused)
+    0b11000000, // + (only works in position 0)
+    0b00000100, // ,
+    0b01000000, // -
+    0b01000000, // . (same as -, semantically most useful)
+    0b00010010, // /
+    0b00111111, // 0
+    0b00000110, // 1
+    0b01011011, // 2
+    0b01001111, // 3
+    0b01100110, // 4
+    0b01101101, // 5
+    0b01111101, // 6
+    0b00000111, // 7
+    0b01111111, // 8
+    0b01101111, // 9
+    0b00000000, // : (unused)
+    0b00000000, // ; (unused)
+    0b01011000, // <
+    0b01001000, // =
+    0b01001100, // >
+    0b01010011, // ?
+    0b11111111, // @ (all segments on)
+    0b01110111, // A
+    0b01111111, // B
+    0b00111001, // C
+    0b00111111, // D
+    0b01111001, // E
+    0b01110001, // F
+    0b00111101, // G
+    0b01110110, // H
+    0b10001001, // I (only works in position 0)
+    0b00001110, // J
+    0b01110101, // K
+    0b00111000, // L
+    0b10110111, // M (only works in position 0)
+    0b00110111, // N
+    0b00111111, // O
+    0b01110011, // P
+    0b01100111, // Q
+    0b11110111, // R (only works in position 1)
+    0b01101101, // S
+    0b10000001, // T (only works in position 0; set (1, 12) to make it work in position 1)
+    0b00111110, // U
+    0b00111110, // V
+    0b10111110, // W (only works in position 0)
+    0b01111110, // X
+    0b01101110, // Y
+    0b00011011, // Z
+    0b00111001, // [
+    0b00100100, // backslash
+    0b00001111, // ]
+    0b00100011, // ^
+    0b00001000, // _
+    0b00000010, // `
+    0b01011111, // a
+    0b01111100, // b
+    0b01011000, // c
+    0b01011110, // d
+    0b01111011, // e
+    0b01110001, // f
+    0b01101111, // g
+    0b01110100, // h
+    0b00010000, // i
+    0b01000010, // j (appears as superscript to work in more positions)
+    0b01110101, // k
+    0b00110000, // l
+    0b10110111, // m (only works in position 0)
+    0b01010100, // n
+    0b01011100, // o
+    0b01110011, // p
+    0b01100111, // q
+    0b01010000, // r
+    0b01101101, // s
+    0b01111000, // t
+    0b01100010, // u (appears as superscript to work in more positions)
+    0b01100010, // v (appears as superscript to work in more positions)
+    0b10111110, // w (only works in position 0)
+    0b01111110, // x
+    0b01101110, // y
+    0b00011011, // z
+    0b00111001, // {
+    0b00110000, // |
+    0b00001111, // }
+    0b00000001, // ~
+};
+
+static const uint64_t Segment_Map[] = {
+    0x4e4f0e8e8f8d4d0d, // Position 0, mode
+    0xc8c4c4c8b4b4b0b,  // Position 1, mode (Segments B and C shared, as are segments E and F)
+    0xc049c00a49890949, // Position 2, day of month (Segments A, D, G shared; missing segment F)
+    0xc048088886874707, // Position 3, day of month
+    0xc053921252139352, // Position 4, clock hours (Segments A and D shared)
+    0xc054511415559594, // Position 5, clock hours
+    0xc057965616179716, // Position 6, clock minutes (Segments A and D shared)
+    0xc041804000018a81, // Position 7, clock minutes
+    0xc043420203048382, // Position 8, clock seconds
+    0xc045440506468584, // Position 9, clock seconds
+};
+
+static const uint8_t Num_Chars = 10;
+
+static const uint32_t IndicatorSegments[6] = {
+    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
+};
+
+void watch_enable_display() {
+    SEGMENT_LCD_0_init();
+    slcd_sync_enable(&SEGMENT_LCD_0);
+}
+
+inline void watch_set_pixel(uint8_t com, uint8_t seg) {
+    slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(com, seg));
+}
+
+inline void watch_clear_pixel(uint8_t com, uint8_t seg) {
+    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(com, seg));
+}
+
+void watch_display_character(uint8_t character, uint8_t position) {
+    // handle lowercase 7 if needed
+    if (character == '7' && (position == 4 || position == 6)) character = '&';
+
+    uint64_t segmap = Segment_Map[position];
+    uint64_t segdata = 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;
+        slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(com, seg));
+        if (segdata & 1) slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(com, seg));
+        segmap = segmap >> 8;
+        segdata = segdata >> 1;
+    }
+}
+
+void watch_display_string(char *string, uint8_t position) {
+    size_t i = 0;
+    while(string[i] != 0) {
+        watch_display_character(string[i], position + i);
+        i++;
+        if (i >= Num_Chars) break;
+    }
+}
+
+inline void watch_set_colon() {
+    slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(1, 16));
+}
+
+inline void watch_clear_colon() {
+    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(1, 16));
+}
+
+inline void watch_set_indicator(WatchIndicatorSegment indicator) {
+    slcd_sync_seg_on(&SEGMENT_LCD_0, IndicatorSegments[indicator]);
+}
+
+inline void watch_clear_indicator(WatchIndicatorSegment indicator) {
+    slcd_sync_seg_off(&SEGMENT_LCD_0, IndicatorSegments[indicator]);
+}
+
+void watch_clear_all_indicators() {
+    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(2, 17));
+    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(2, 16));
+    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(0, 17));
+    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(0, 16));
+    slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(1, 10));
+}
diff --git a/watch-library/watch/watch_slcd.h b/watch-library/watch/watch_slcd.h
new file mode 100644 (file)
index 0000000..efef99a
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+////< @file watch_slcd.h
+
+/** @addtogroup slcd Segment LCD Display
+  * @brief This section covers functions related to the Segment LCD display driver, which is responsible
+  *        for displaying strings of characters and indicators on the main watch display.
+  * @details The segment LCD controller consumes about 3 microamperes of power with no segments on, and
+  *          about 4 microamperes with all segments on. There is also a slight power impact associated
+  *          with updating the screen (about 1 microampere to update at 1 Hz). For the absolute lowest
+  *          power operation, update the display only when its contents have changed, and disable the
+  *          SLCD peripheral when the screen is not in use.
+  *          For a map of all common and segment pins, see <a href="segmap.html">segmap.html</a>. You can
+  *          hover over any segment in that diagram to view the common and segment pins associated with
+  *          each segment of the display.
+  */
+/// @{
+
+/// An enum listing the icons and indicators available on the watch.
+typedef enum WatchIndicatorSegment {
+    WATCH_INDICATOR_SIGNAL = 0, ///< The hourly signal indicator; also useful for indicating that sensors are on.
+    WATCH_INDICATOR_BELL,       ///< The small bell indicating that an alarm is set.
+    WATCH_INDICATOR_PM,         ///< The PM indicator, indicating that a time is in the afternoon.
+    WATCH_INDICATOR_24H,        ///< The 24H indicator, indicating that the watch is in a 24-hour mode.
+    WATCH_INDICATOR_LAP         ///< The LAP indicator; the F-91W uses this in its stopwatch UI.
+} WatchIndicatorSegment;
+
+/** @brief Enables the Segment LCD display.
+  * Call this before attempting to set pixels or display strings.
+  */
+void watch_enable_display();
+
+/** @brief Sets a pixel. Use this to manually set a pixel with a given common and segment number.
+  *        See <a href="segmap.html">segmap.html</a>.
+  * @param com the common pin, numbered from 0-2.
+  * @param seg the segment pin, numbered from 0-23.
+  */
+void watch_set_pixel(uint8_t com, uint8_t seg);
+
+/** @brief Clears a pixel. Use this to manually clear a pixel with a given common and segment number.
+  *        See <a href="segmap.html">segmap.html</a>.
+  * @param com the common pin, numbered from 0-2.
+  * @param seg the segment pin, numbered from 0-23.
+  */
+void watch_clear_pixel(uint8_t com, uint8_t seg);
+
+/** @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.
+  * @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
+  *                 clock line occupies positions 4-9.
+  * @note This method does not clear the display; if for example you display a two-character string at
+          position 0, positions 2-9 will retain whatever state they were previously displaying.
+  */
+void watch_display_string(char *string, uint8_t position);
+
+/** @brief Turns the colon segment on.
+  */
+void watch_set_colon();
+
+/** @brief Turns the colon segment off.
+  */
+void watch_clear_colon();
+
+/** @brief Sets an indicator on the LCD. Use this to turn on one of the indicator segments.
+  * @param indicator One of the indicator segments from the enum. @see WatchIndicatorSegment
+  */
+void watch_set_indicator(WatchIndicatorSegment indicator);
+
+/** @brief Clears an indicator on the LCD. Use this to turn off one of the indicator segments.
+  * @param indicator One of the indicator segments from the enum. @see WatchIndicatorSegment
+  */
+void watch_clear_indicator(WatchIndicatorSegment indicator);
+
+/** @brief Clears all indicator segments.
+  * @see WatchIndicatorSegment
+  */
+void watch_clear_all_indicators();
+
+/// @}
diff --git a/watch-library/watch/watch_uart.c b/watch-library/watch/watch_uart.c
new file mode 100644 (file)
index 0000000..4cbac89
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+ /*
+ * UART methods are Copyright (c) 2014-2017, Alex Taradov <alex@taradov.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "peripheral_clk_config.h"
+
+void watch_enable_debug_uart(uint32_t baud) {
+    uint64_t br = (uint64_t)65536 * (CONF_CPU_FREQUENCY - 16 * baud) / CONF_CPU_FREQUENCY;
+
+    gpio_set_pin_direction(D1, GPIO_DIRECTION_IN);
+    gpio_set_pin_function(D1, PINMUX_PB00C_SERCOM3_PAD2);
+
+    MCLK->APBCMASK.reg |= MCLK_APBCMASK_SERCOM3;
+
+    GCLK->PCHCTRL[SERCOM3_GCLK_ID_CORE].reg = GCLK_PCHCTRL_GEN(0) | GCLK_PCHCTRL_CHEN;
+    while (0 == (GCLK->PCHCTRL[SERCOM3_GCLK_ID_CORE].reg & GCLK_PCHCTRL_CHEN));
+
+    SERCOM3->USART.CTRLA.reg =
+        SERCOM_USART_CTRLA_DORD | SERCOM_USART_CTRLA_MODE(1/*USART_INT_CLK*/) |
+        SERCOM_USART_CTRLA_RXPO(0/*PAD0*/) | SERCOM_USART_CTRLA_TXPO(1/*PAD2*/);
+
+    SERCOM3->USART.CTRLB.reg = SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN |
+        SERCOM_USART_CTRLB_CHSIZE(0/*8 bits*/);
+
+    SERCOM3->USART.BAUD.reg = (uint16_t)br;
+
+    SERCOM3->USART.CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;
+}
+
+void watch_debug_putc(char c) {
+    while (!(SERCOM3->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_DRE));
+    SERCOM3->USART.DATA.reg = c;
+}
+
+void watch_debug_puts(char *s) {
+    while (*s) watch_debug_putc(*s++);
+}
diff --git a/watch-library/watch/watch_uart.h b/watch-library/watch/watch_uart.h
new file mode 100644 (file)
index 0000000..a26d60d
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+////< @file watch_uart.h
+
+/** @addtogroup debug Debug UART
+  * @brief This section covers functions related to the debug UART, available on
+  *        pin D1 of the 9-pin connector.
+  * @todo Refactor this as a USB CDC so that folks can debug over USB.
+  */
+/// @{
+/** @brief Initializes the debug UART.
+  * @param baud The baud rate
+  */
+void watch_enable_debug_uart(uint32_t baud);
+
+/** @brief Outputs a single character on the debug UART.
+  * @param c The character you wish to output.
+  */
+void watch_debug_putc(char c);
+
+/** @brief Outputs a string on the debug UART.
+  * @param s A null-terminated string.
+  */
+void watch_debug_puts(char *s);
+/// @}