refactor: break out different areas of functionality

This commit is contained in:
Joey Castillo 2021-08-23 08:11:49 -06:00
parent 42f0eac6d1
commit 67b749f3af
26 changed files with 1670 additions and 1145 deletions

View file

@ -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);
//////////////////////////////////////////////////////////////////////////////////////////
// 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);
}
ext_irq_cb_t btn_alarm_callback;
#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"

View file

@ -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_ */

View file

@ -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;
}
}

View file

@ -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);
/// @}

View file

@ -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.
*/

View file

@ -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();
/// @}

View file

@ -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();
}

View file

@ -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];
/// @}

View file

@ -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);
}

View file

@ -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();
/// @}

View file

@ -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);
}
}

View file

@ -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);
/// @}

View file

@ -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);
}

View file

@ -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);
/// @}

View file

@ -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;
}

View file

@ -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);
/// @}

View file

@ -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);
}
}

View file

@ -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();
/// @}

View file

@ -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;
}

View file

@ -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();

View file

@ -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);
}

View file

@ -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);
/// @}

View file

@ -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));
}

View file

@ -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();
/// @}

View file

@ -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++);
}

View file

@ -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);
/// @}