summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoey Castillo <jose.castillo@gmail.com>2021-08-23 08:11:49 -0600
committerJoey Castillo <jose.castillo@gmail.com>2021-08-23 08:11:49 -0600
commit67b749f3afe08bc8a14fa89e92019d142dafa74e (patch)
treedc46ba08c45a7aab96177a385dadc5a616a4e502
parent42f0eac6d12bb354ce34f1cee42ad43f8a023282 (diff)
downloadSensor-Watch-67b749f3afe08bc8a14fa89e92019d142dafa74e.tar.gz
Sensor-Watch-67b749f3afe08bc8a14fa89e92019d142dafa74e.tar.bz2
Sensor-Watch-67b749f3afe08bc8a14fa89e92019d142dafa74e.zip
refactor: break out different areas of functionality
-rw-r--r--watch-library/watch/watch.c639
-rw-r--r--watch-library/watch/watch.h534
-rw-r--r--watch-library/watch/watch_adc.c45
-rw-r--r--watch-library/watch/watch_adc.h35
-rw-r--r--watch-library/watch/watch_app.c25
-rw-r--r--watch-library/watch/watch_app.h105
-rw-r--r--watch-library/watch/watch_buzzer.c52
-rw-r--r--watch-library/watch/watch_buzzer.h (renamed from watch-library/watch/notes.h)39
-rw-r--r--watch-library/watch/watch_deepsleep.c85
-rw-r--r--watch-library/watch/watch_deepsleep.h74
-rw-r--r--watch-library/watch/watch_extint.c39
-rw-r--r--watch-library/watch/watch_extint.h52
-rw-r--r--watch-library/watch/watch_gpio.c53
-rw-r--r--watch-library/watch/watch_gpio.h66
-rw-r--r--watch-library/watch/watch_i2c.c86
-rw-r--r--watch-library/watch/watch_i2c.h97
-rw-r--r--watch-library/watch/watch_led.c95
-rw-r--r--watch-library/watch/watch_led.h74
-rw-r--r--watch-library/watch/watch_private.c44
-rw-r--r--watch-library/watch/watch_private.h26
-rw-r--r--watch-library/watch/watch_rtc.c40
-rw-r--r--watch-library/watch/watch_rtc.h58
-rw-r--r--watch-library/watch/watch_slcd.c217
-rw-r--r--watch-library/watch/watch_slcd.h102
-rw-r--r--watch-library/watch/watch_uart.c85
-rw-r--r--watch-library/watch/watch_uart.h46
26 files changed, 1669 insertions, 1144 deletions
diff --git a/watch-library/watch/watch.c b/watch-library/watch/watch.c
index 659be1c5..d55b61fe 100644
--- a/watch-library/watch/watch.c
+++ b/watch-library/watch/watch.c
@@ -23,631 +23,20 @@
*/
#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"
diff --git a/watch-library/watch/watch.h b/watch-library/watch/watch.h
index 2b8cd4e0..8c207ddb 100644
--- a/watch-library/watch/watch.h
+++ b/watch-library/watch/watch.h
@@ -26,23 +26,21 @@
#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.
@@ -53,518 +51,18 @@
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
index 00000000..ebb8bb60
--- /dev/null
+++ b/watch-library/watch/watch_adc.c
@@ -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
index 00000000..d4620365
--- /dev/null
+++ b/watch-library/watch/watch_adc.h
@@ -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
index 00000000..0fb87b9a
--- /dev/null
+++ b/watch-library/watch/watch_app.c
@@ -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
index 00000000..fd7ea706
--- /dev/null
+++ b/watch-library/watch/watch_app.h
@@ -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
index 00000000..04746416
--- /dev/null
+++ b/watch-library/watch/watch_buzzer.c
@@ -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/notes.h b/watch-library/watch/watch_buzzer.h
index bc4a3fa2..75b30a79 100644
--- a/watch-library/watch/notes.h
+++ b/watch-library/watch/watch_buzzer.h
@@ -21,7 +21,31 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-////< @file notes.h
+////< @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 {
@@ -135,3 +159,16 @@ typedef enum BuzzerNote {
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
index 00000000..53a41b5c
--- /dev/null
+++ b/watch-library/watch/watch_deepsleep.c
@@ -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
index 00000000..cea6f1aa
--- /dev/null
+++ b/watch-library/watch/watch_deepsleep.h
@@ -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
index 00000000..19f6041a
--- /dev/null
+++ b/watch-library/watch/watch_extint.c
@@ -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
index 00000000..71295902
--- /dev/null
+++ b/watch-library/watch/watch_extint.h
@@ -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
index 00000000..460a38df
--- /dev/null
+++ b/watch-library/watch/watch_gpio.c
@@ -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
index 00000000..f7678f6f
--- /dev/null
+++ b/watch-library/watch/watch_gpio.h
@@ -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
index 00000000..bded405b
--- /dev/null
+++ b/watch-library/watch/watch_i2c.c
@@ -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
index 00000000..e944bbab
--- /dev/null
+++ b/watch-library/watch/watch_i2c.h
@@ -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
index 00000000..01f59fc4
--- /dev/null
+++ b/watch-library/watch/watch_led.c
@@ -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
index 00000000..04b6e5fa
--- /dev/null
+++ b/watch-library/watch/watch_led.h
@@ -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
index 00000000..bfe171f1
--- /dev/null
+++ b/watch-library/watch/watch_private.c
@@ -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
index 00000000..99171126
--- /dev/null
+++ b/watch-library/watch/watch_private.h
@@ -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
index 00000000..2d6d598f
--- /dev/null
+++ b/watch-library/watch/watch_rtc.c
@@ -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
index 00000000..c685ac26
--- /dev/null
+++ b/watch-library/watch/watch_rtc.h
@@ -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
index 00000000..cdade07f
--- /dev/null
+++ b/watch-library/watch/watch_slcd.c
@@ -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
index 00000000..efef99ac
--- /dev/null
+++ b/watch-library/watch/watch_slcd.h
@@ -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
index 00000000..4cbac89b
--- /dev/null
+++ b/watch-library/watch/watch_uart.c
@@ -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
index 00000000..a26d60d2
--- /dev/null
+++ b/watch-library/watch/watch_uart.h
@@ -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);
+/// @}