Sensor Watch Simulator (#35)
* Put something on screen * Use the 32bit watch_date_time repr to pass from JS * Implement periodic callbacks * Clear display on enabling * Hook up watch_set_led_color() to SVG (green-only) * Make debug output full-width * Remove default Emscripten canvas * Implement sleep and button clicks * Fix time zone conversion bug in beats-time app * Clean up warnings * Fix pin levels * Set time zone to browser value (if available) * Add basic backup data saving * Silence format specifier warnings in both targets * Remove unnecessary, copied files * Use RTC pointer to clear callbacks (if available) * Use preprocessor define to avoid hardcoding MOVEMENT_NUM_FACES * Change each face to const preprocessor definition * Remove Intl.DateTimeFormat usage * Update shell.html title, header * Add touch start/end event handlers on SVG buttons * Update shell.html * Update folder structure (shared, simulator, hardware under watch-library) * Tease out shared components from watch_slcd * Clean up simulator watch_slcd.c inline JS calls * Fix missing newlines at end of file * Add simulator warnings (except format, unused-paremter) * Implement remaining watch_rtc functions * Fix button bug on mouse down then drag out * Implement remaining watch_slcd functions * Link keyboard events to buttons (for keys A, L, M) * Rewrite event handling (mouse, touch, keyboard) in C * Set explicit text UTF-8 charset in shell.html * Address PR comments * Remove unused directories from include paths
This commit is contained in:
91
watch-library/hardware/watch/tusb_config.h
Normal file
91
watch-library/hardware/watch/tusb_config.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// defined by board.mk
|
||||
#define CFG_TUSB_MCU OPT_MCU_SAML22
|
||||
|
||||
#define BOARD_DEVICE_RHPORT_NUM 0
|
||||
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
|
||||
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED)
|
||||
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
|
||||
// disable TinyUSB debug. our printf method prints stuff to the USB console, so you just get infinite noise.
|
||||
// if you need to debug tinyUSB issues, use the alternate _write function in watch_private.c to echo to the UART.
|
||||
#define CFG_TUSB_DEBUG 0
|
||||
|
||||
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
|
||||
* Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||
* into those specific section.
|
||||
* e.g
|
||||
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||
*/
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_CDC 1
|
||||
#define CFG_TUD_MSC 0
|
||||
#define CFG_TUD_HID 0
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 0
|
||||
|
||||
// CDC FIFO size of TX and RX
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE (64)
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE (64)
|
||||
|
||||
// CDC Endpoint transfer buffer size, more is faster
|
||||
#define CFG_TUD_CDC_EP_BUFSIZE (64)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
||||
44
watch-library/hardware/watch/watch.c
Normal file
44
watch-library/hardware/watch/watch.c
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
bool battery_is_low = false;
|
||||
|
||||
// receives interrupts from MCLK, OSC32KCTRL, OSCCTRL, PAC, PM, SUPC and TAL, whatever that is.
|
||||
void SYSTEM_Handler(void) {
|
||||
if (SUPC->INTFLAG.bit.BOD33DET) {
|
||||
battery_is_low = true;
|
||||
SUPC->INTENCLR.bit.BOD33DET = 1;
|
||||
SUPC->INTFLAG.reg &= ~SUPC_INTFLAG_BOD33DET;
|
||||
}
|
||||
}
|
||||
|
||||
bool watch_is_battery_low(void) {
|
||||
return battery_is_low;
|
||||
}
|
||||
|
||||
bool watch_is_buzzer_or_led_enabled(void){
|
||||
return hri_mclk_get_APBCMASK_TCC0_bit(MCLK);
|
||||
}
|
||||
179
watch-library/hardware/watch/watch_adc.c
Normal file
179
watch-library/hardware/watch/watch_adc.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "watch_adc.h"
|
||||
|
||||
static void _watch_sync_adc(void) {
|
||||
while (ADC->SYNCBUSY.reg);
|
||||
}
|
||||
|
||||
static uint16_t _watch_get_analog_value(uint16_t channel) {
|
||||
if (ADC->INPUTCTRL.bit.MUXPOS != channel) {
|
||||
ADC->INPUTCTRL.bit.MUXPOS = channel;
|
||||
_watch_sync_adc();
|
||||
}
|
||||
|
||||
ADC->SWTRIG.bit.START = 1;
|
||||
while (!ADC->INTFLAG.bit.RESRDY);
|
||||
|
||||
return ADC->RESULT.reg;
|
||||
}
|
||||
|
||||
void watch_enable_adc(void) {
|
||||
MCLK->APBCMASK.reg |= MCLK_APBCMASK_ADC;
|
||||
GCLK->PCHCTRL[ADC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0 | GCLK_PCHCTRL_CHEN;
|
||||
|
||||
uint16_t calib_reg = 0;
|
||||
calib_reg = ADC_CALIB_BIASREFBUF((*(uint32_t *)ADC_FUSES_BIASREFBUF_ADDR >> ADC_FUSES_BIASREFBUF_Pos)) |
|
||||
ADC_CALIB_BIASCOMP((*(uint32_t *)ADC_FUSES_BIASCOMP_ADDR >> ADC_FUSES_BIASCOMP_Pos));
|
||||
|
||||
if (!ADC->SYNCBUSY.bit.SWRST) {
|
||||
if (ADC->CTRLA.bit.ENABLE) {
|
||||
ADC->CTRLA.bit.ENABLE = 0;
|
||||
_watch_sync_adc();
|
||||
}
|
||||
ADC->CTRLA.bit.SWRST = 1;
|
||||
}
|
||||
_watch_sync_adc();
|
||||
|
||||
if (USB->DEVICE.CTRLA.bit.ENABLE) {
|
||||
// if USB is enabled, we are running an 8 MHz clock.
|
||||
// divide by 16 for a 500kHz ADC clock.
|
||||
ADC->CTRLB.bit.PRESCALER = ADC_CTRLB_PRESCALER_DIV16_Val;
|
||||
} else {
|
||||
// otherwise it's 4 Mhz. divide by 8 for a 500kHz ADC clock.
|
||||
ADC->CTRLB.bit.PRESCALER = ADC_CTRLB_PRESCALER_DIV8_Val;
|
||||
}
|
||||
ADC->CALIB.reg = calib_reg;
|
||||
ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC2_Val;
|
||||
ADC->INPUTCTRL.bit.MUXNEG = ADC_INPUTCTRL_MUXNEG_GND_Val;
|
||||
ADC->CTRLC.bit.RESSEL = ADC_CTRLC_RESSEL_16BIT_Val;
|
||||
ADC->AVGCTRL.bit.SAMPLENUM = ADC_AVGCTRL_SAMPLENUM_16_Val;
|
||||
ADC->SAMPCTRL.bit.SAMPLEN = 0;
|
||||
ADC->INTENSET.reg = ADC_INTENSET_RESRDY;
|
||||
ADC->CTRLA.bit.ENABLE = 1;
|
||||
_watch_sync_adc();
|
||||
// throw away one measurement after reference change (the channel doesn't matter).
|
||||
_watch_get_analog_value(ADC_INPUTCTRL_MUXPOS_SCALEDCOREVCC);
|
||||
}
|
||||
|
||||
void watch_enable_analog_input(const uint8_t pin) {
|
||||
gpio_set_pin_direction(pin, GPIO_DIRECTION_OFF);
|
||||
switch (pin) {
|
||||
case A0:
|
||||
gpio_set_pin_function(pin, PINMUX_PB04B_ADC_AIN12);
|
||||
break;
|
||||
case A1:
|
||||
gpio_set_pin_function(pin, PINMUX_PB01B_ADC_AIN9);
|
||||
break;
|
||||
case A2:
|
||||
gpio_set_pin_function(pin, PINMUX_PB02B_ADC_AIN10);
|
||||
break;
|
||||
case A3:
|
||||
gpio_set_pin_function(pin, PINMUX_PB03B_ADC_AIN11);
|
||||
break;
|
||||
case A4:
|
||||
gpio_set_pin_function(pin, PINMUX_PB00B_ADC_AIN8);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t watch_get_analog_pin_level(const uint8_t pin) {
|
||||
switch (pin) {
|
||||
case A0:
|
||||
return _watch_get_analog_value(ADC_INPUTCTRL_MUXPOS_AIN12_Val);
|
||||
case A1:
|
||||
return _watch_get_analog_value(ADC_INPUTCTRL_MUXPOS_AIN9_Val);
|
||||
case A2:
|
||||
return _watch_get_analog_value(ADC_INPUTCTRL_MUXPOS_AIN10_Val);
|
||||
case A3:
|
||||
return _watch_get_analog_value(ADC_INPUTCTRL_MUXPOS_AIN11_Val);
|
||||
case A4:
|
||||
return _watch_get_analog_value(ADC_INPUTCTRL_MUXPOS_AIN8_Val);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void watch_set_analog_num_samples(uint16_t samples) {
|
||||
// ignore any input that's not a power of 2 (i.e. only one bit set)
|
||||
if (__builtin_popcount(samples) != 1) return;
|
||||
// if only one bit is set, counting the trailing zeroes is equivalent to log2(samples)
|
||||
uint8_t sample_val = __builtin_ctz(samples);
|
||||
// make sure the desired value is within range and set it, if so.
|
||||
if (sample_val <= ADC_AVGCTRL_SAMPLENUM_1024_Val) {
|
||||
ADC->AVGCTRL.bit.SAMPLENUM = sample_val;
|
||||
_watch_sync_adc();
|
||||
}
|
||||
}
|
||||
|
||||
void watch_set_analog_sampling_length(uint8_t cycles) {
|
||||
// for clarity the API asks the user how many cycles they want the measurement to take.
|
||||
// but the ADC always needs at least one cycle; it just wants to know how many *extra* cycles we want.
|
||||
// so we subtract one from the user-provided value, and clamp to the maximum.
|
||||
ADC->SAMPCTRL.bit.SAMPLEN = (cycles - 1) & 0x3F;
|
||||
_watch_sync_adc();
|
||||
}
|
||||
|
||||
void watch_set_analog_reference_voltage(watch_adc_reference_voltage reference) {
|
||||
ADC->CTRLA.bit.ENABLE = 0;
|
||||
|
||||
if (reference == ADC_REFERENCE_INTREF) SUPC->VREF.bit.VREFOE = 1;
|
||||
else SUPC->VREF.bit.VREFOE = 0;
|
||||
|
||||
ADC->REFCTRL.bit.REFSEL = reference;
|
||||
ADC->CTRLA.bit.ENABLE = 1;
|
||||
_watch_sync_adc();
|
||||
// throw away one measurement after reference change (the channel doesn't matter).
|
||||
_watch_get_analog_value(ADC_INPUTCTRL_MUXPOS_SCALEDCOREVCC);
|
||||
}
|
||||
|
||||
uint16_t watch_get_vcc_voltage(void) {
|
||||
// stash the previous reference so we can restore it when we're done.
|
||||
uint8_t oldref = ADC->REFCTRL.bit.REFSEL;
|
||||
|
||||
// if we weren't already using the internal reference voltage, select it now.
|
||||
if (oldref != ADC_REFERENCE_INTREF) watch_set_analog_reference_voltage(ADC_REFERENCE_INTREF);
|
||||
|
||||
// get the data
|
||||
uint32_t raw_val = _watch_get_analog_value(ADC_INPUTCTRL_MUXPOS_SCALEDIOVCC_Val);
|
||||
|
||||
// restore the old reference, if needed.
|
||||
if (oldref != ADC_REFERENCE_INTREF) watch_set_analog_reference_voltage(oldref);
|
||||
|
||||
return (uint16_t)((raw_val * 1000) / (1024 * 1 << ADC->AVGCTRL.bit.SAMPLENUM));
|
||||
}
|
||||
|
||||
inline void watch_disable_analog_input(const uint8_t pin) {
|
||||
gpio_set_pin_function(pin, GPIO_PIN_FUNCTION_OFF);
|
||||
}
|
||||
|
||||
inline void watch_disable_adc(void) {
|
||||
ADC->CTRLA.bit.ENABLE = 0;
|
||||
_watch_sync_adc();
|
||||
|
||||
MCLK->APBCMASK.reg &= ~MCLK_APBCMASK_ADC;
|
||||
}
|
||||
64
watch-library/hardware/watch/watch_buzzer.c
Normal file
64
watch-library/hardware/watch/watch_buzzer.c
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "watch_buzzer.h"
|
||||
|
||||
inline void watch_enable_buzzer(void) {
|
||||
if (!hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
|
||||
_watch_enable_tcc();
|
||||
}
|
||||
}
|
||||
inline void watch_set_buzzer_period(uint32_t period) {
|
||||
hri_tcc_write_PERBUF_reg(TCC0, period);
|
||||
}
|
||||
|
||||
void watch_disable_buzzer(void) {
|
||||
_watch_disable_tcc();
|
||||
}
|
||||
|
||||
inline void watch_set_buzzer_on(void) {
|
||||
gpio_set_pin_direction(BUZZER, GPIO_DIRECTION_OUT);
|
||||
gpio_set_pin_function(BUZZER, WATCH_BUZZER_TCC_PINMUX);
|
||||
}
|
||||
|
||||
inline void watch_set_buzzer_off(void) {
|
||||
gpio_set_pin_direction(BUZZER, GPIO_DIRECTION_OFF);
|
||||
gpio_set_pin_function(BUZZER, GPIO_PIN_FUNCTION_OFF);
|
||||
}
|
||||
|
||||
// note: the buzzer uses a 1 MHz clock. these values were determined by dividing 1,000,000 by the target frequency.
|
||||
// i.e. for a 440 Hz tone (A4 on the piano), 1MHz/440Hz = 2273
|
||||
const uint16_t NotePeriods[108] = {18182,17161,16197,15288,14430,13620,12857,12134,11453,10811,10204,9631,9091,8581,8099,7645,7216,6811,6428,6068,5727,5405,5102,4816,4545,4290,4050,3822,3608,3405,3214,3034,2863,2703,2551,2408,2273,2145,2025,1911,1804,1703,1607,1517,1432,1351,1276,1204,1136,1073,1012,956,902,851,804,758,716,676,638,602,568,536,506,478,451,426,402,379,358,338,319,301,284,268,253,239,225,213,201,190,179,169,159,150,142,134,127};
|
||||
|
||||
void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms) {
|
||||
if (note == BUZZER_NOTE_REST) {
|
||||
watch_set_buzzer_off();
|
||||
} else {
|
||||
hri_tcc_write_PERBUF_reg(TCC0, NotePeriods[note]);
|
||||
hri_tcc_write_CCBUF_reg(TCC0, WATCH_BUZZER_TCC_CHANNEL, NotePeriods[note] / 2);
|
||||
watch_set_buzzer_on();
|
||||
}
|
||||
delay_ms(duration_ms);
|
||||
watch_set_buzzer_off();
|
||||
}
|
||||
209
watch-library/hardware/watch/watch_deepsleep.c
Normal file
209
watch-library/hardware/watch/watch_deepsleep.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "watch_extint.h"
|
||||
|
||||
// this warning only appears when you `make BOARD=OSO-SWAT-A1-02`. it's annoying,
|
||||
// but i'd rather have it warn us at build-time than fail silently at run-time.
|
||||
// besides, no one but me really has any of these boards anyway.
|
||||
#if BTN_ALARM != GPIO(GPIO_PORTA, 2)
|
||||
#warning This board revision does not support external wake on BTN_ALARM, so watch_register_extwake_callback will not work with it. Use watch_register_interrupt_callback instead.
|
||||
#endif
|
||||
|
||||
void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback, bool level) {
|
||||
uint32_t pinmux;
|
||||
hri_rtc_tampctrl_reg_t config = RTC->MODE2.TAMPCTRL.reg;
|
||||
|
||||
switch (pin) {
|
||||
case A4:
|
||||
a4_callback = callback;
|
||||
pinmux = PINMUX_PB00G_RTC_IN0;
|
||||
config &= ~(3 << RTC_TAMPCTRL_IN0ACT_Pos);
|
||||
config &= ~(1 << RTC_TAMPCTRL_TAMLVL0_Pos);
|
||||
config |= 1 << RTC_TAMPCTRL_IN0ACT_Pos;
|
||||
config |= 1 << RTC_TAMPCTRL_DEBNC0_Pos;
|
||||
if (level) config |= 1 << RTC_TAMPCTRL_TAMLVL0_Pos;
|
||||
break;
|
||||
case A2:
|
||||
a2_callback = callback;
|
||||
pinmux = PINMUX_PB02G_RTC_IN1;
|
||||
config &= ~(3 << RTC_TAMPCTRL_IN1ACT_Pos);
|
||||
config &= ~(1 << RTC_TAMPCTRL_TAMLVL1_Pos);
|
||||
config |= 1 << RTC_TAMPCTRL_IN1ACT_Pos;
|
||||
config |= 1 << RTC_TAMPCTRL_DEBNC1_Pos;
|
||||
if (level) config |= 1 << RTC_TAMPCTRL_TAMLVL1_Pos;
|
||||
break;
|
||||
case BTN_ALARM:
|
||||
gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN);
|
||||
btn_alarm_callback = callback;
|
||||
pinmux = PINMUX_PA02G_RTC_IN2;
|
||||
config &= ~(3 << RTC_TAMPCTRL_IN2ACT_Pos);
|
||||
config &= ~(1 << RTC_TAMPCTRL_TAMLVL2_Pos);
|
||||
config |= 1 << RTC_TAMPCTRL_IN2ACT_Pos;
|
||||
config |= 1 << RTC_TAMPCTRL_DEBNC2_Pos;
|
||||
if (level) config |= 1 << RTC_TAMPCTRL_TAMLVL2_Pos;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
gpio_set_pin_direction(pin, GPIO_DIRECTION_IN);
|
||||
gpio_set_pin_function(pin, pinmux);
|
||||
|
||||
// disable the RTC
|
||||
RTC->MODE2.CTRLA.bit.ENABLE = 0;
|
||||
while (RTC->MODE2.SYNCBUSY.bit.ENABLE);
|
||||
|
||||
// update the configuration
|
||||
RTC->MODE2.TAMPCTRL.reg = config;
|
||||
// re-enable the RTC
|
||||
RTC->MODE2.CTRLA.bit.ENABLE = 1;
|
||||
|
||||
NVIC_ClearPendingIRQ(RTC_IRQn);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_TAMPER;
|
||||
}
|
||||
|
||||
void watch_disable_extwake_interrupt(uint8_t pin) {
|
||||
hri_rtc_tampctrl_reg_t config = hri_rtc_get_TAMPCTRL_reg(RTC, 0xFFFFFFFF);
|
||||
|
||||
switch (pin) {
|
||||
case A4:
|
||||
a4_callback = NULL;
|
||||
config &= ~(3 << RTC_TAMPCTRL_IN0ACT_Pos);
|
||||
break;
|
||||
case A2:
|
||||
a2_callback = NULL;
|
||||
config &= ~(3 << RTC_TAMPCTRL_IN1ACT_Pos);
|
||||
break;
|
||||
case BTN_ALARM:
|
||||
btn_alarm_callback = NULL;
|
||||
config &= ~(3 << RTC_TAMPCTRL_IN2ACT_Pos);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (hri_rtcmode0_get_CTRLA_ENABLE_bit(RTC)) {
|
||||
hri_rtcmode0_clear_CTRLA_ENABLE_bit(RTC);
|
||||
hri_rtcmode0_wait_for_sync(RTC, RTC_MODE0_SYNCBUSY_ENABLE);
|
||||
}
|
||||
hri_rtc_write_TAMPCTRL_reg(RTC, config);
|
||||
hri_rtcmode0_set_CTRLA_ENABLE_bit(RTC);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void _watch_disable_all_pins_except_rtc(void) {
|
||||
uint32_t config = RTC->MODE0.TAMPCTRL.reg;
|
||||
uint32_t portb_pins_to_disable = 0xFFFFFFFF;
|
||||
|
||||
// if there's an action set on RTC/IN[0], leave PB00 configured
|
||||
if (config & RTC_TAMPCTRL_IN0ACT_Msk) portb_pins_to_disable &= 0xFFFFFFFE;
|
||||
// same with RTC/IN[1] and PB02
|
||||
if (config & RTC_TAMPCTRL_IN1ACT_Msk) portb_pins_to_disable &= 0xFFFFFFFB;
|
||||
|
||||
// port A: always keep PA02 configured as-is; that's our ALARM button.
|
||||
gpio_set_port_direction(0, 0xFFFFFFFB, GPIO_DIRECTION_OFF);
|
||||
// port B: disable all pins we didn't save above.
|
||||
gpio_set_port_direction(1, portb_pins_to_disable, GPIO_DIRECTION_OFF);
|
||||
}
|
||||
|
||||
static void _watch_disable_all_peripherals_except_slcd(void) {
|
||||
_watch_disable_tcc();
|
||||
watch_disable_adc();
|
||||
watch_disable_external_interrupts();
|
||||
watch_disable_i2c();
|
||||
// TODO: replace this with a proper function when we remove the debug UART
|
||||
SERCOM3->USART.CTRLA.reg &= ~SERCOM_USART_CTRLA_ENABLE;
|
||||
MCLK->APBCMASK.reg &= ~MCLK_APBCMASK_SERCOM3;
|
||||
}
|
||||
|
||||
void watch_enter_sleep_mode(void) {
|
||||
// disable all other peripherals
|
||||
_watch_disable_all_peripherals_except_slcd();
|
||||
|
||||
// disable tick interrupt
|
||||
watch_rtc_disable_all_periodic_callbacks();
|
||||
|
||||
// disable brownout detector interrupt, which could inadvertently wake us up.
|
||||
SUPC->INTENCLR.bit.BOD33DET = 1;
|
||||
|
||||
// disable all pins
|
||||
_watch_disable_all_pins_except_rtc();
|
||||
|
||||
// enter standby (4); we basically hang out here until an interrupt wakes us.
|
||||
sleep(4);
|
||||
|
||||
// and we awake! re-enable the brownout detector
|
||||
SUPC->INTENSET.bit.BOD33DET = 1;
|
||||
|
||||
// call app_setup so the app can re-enable everything we disabled.
|
||||
app_setup();
|
||||
|
||||
// and call app_wake_from_standby (since main won't have a chance to do it)
|
||||
app_wake_from_standby();
|
||||
}
|
||||
|
||||
void watch_enter_deep_sleep_mode(void) {
|
||||
// identical to sleep mode except we disable the LCD first.
|
||||
slcd_sync_deinit(&SEGMENT_LCD_0);
|
||||
hri_mclk_clear_APBCMASK_SLCD_bit(SLCD);
|
||||
|
||||
watch_enter_sleep_mode();
|
||||
}
|
||||
|
||||
void watch_enter_backup_mode(void) {
|
||||
watch_rtc_disable_all_periodic_callbacks();
|
||||
_watch_disable_all_peripherals_except_slcd();
|
||||
slcd_sync_deinit(&SEGMENT_LCD_0);
|
||||
hri_mclk_clear_APBCMASK_SLCD_bit(SLCD);
|
||||
_watch_disable_all_pins_except_rtc();
|
||||
|
||||
// go into backup sleep mode (5). when we exit, the reset controller will take over.
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
// deprecated
|
||||
void watch_enter_shallow_sleep(bool display_on) {
|
||||
if (display_on) watch_enter_sleep_mode();
|
||||
else watch_enter_deep_sleep_mode();
|
||||
}
|
||||
|
||||
// deprecated
|
||||
void watch_enter_deep_sleep(void) {
|
||||
watch_register_extwake_callback(BTN_ALARM, NULL, true);
|
||||
watch_enter_backup_mode();
|
||||
}
|
||||
111
watch-library/hardware/watch/watch_extint.c
Normal file
111
watch-library/hardware/watch/watch_extint.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "watch_extint.h"
|
||||
|
||||
void watch_enable_external_interrupts(void) {
|
||||
// Configure EIC to use GCLK3 (the 32.768 kHz crystal)
|
||||
hri_gclk_write_PCHCTRL_reg(GCLK, EIC_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK3_Val | (1 << GCLK_PCHCTRL_CHEN_Pos));
|
||||
// Enable AHB clock for the EIC
|
||||
hri_mclk_set_APBAMASK_EIC_bit(MCLK);
|
||||
// call HAL's external interrupt init function
|
||||
ext_irq_init();
|
||||
}
|
||||
|
||||
void watch_disable_external_interrupts(void) {
|
||||
ext_irq_deinit();
|
||||
hri_mclk_clear_APBAMASK_EIC_bit(MCLK);
|
||||
}
|
||||
|
||||
void watch_register_interrupt_callback(const uint8_t pin, ext_irq_cb_t callback, watch_interrupt_trigger trigger) {
|
||||
uint8_t config_index;
|
||||
uint8_t sense_pos;
|
||||
switch (pin) {
|
||||
case A0:
|
||||
// for EIC channels 8-15, we need to set the SENSE value in CONFIG[1]
|
||||
config_index = (WATCH_A0_EIC_CHANNEL > 7) ? 1 : 0;
|
||||
// either way the index in CONFIG[n] must be 0-7
|
||||
sense_pos = 4 * (WATCH_A0_EIC_CHANNEL % 8);
|
||||
break;
|
||||
case A1:
|
||||
config_index = (WATCH_A1_EIC_CHANNEL > 7) ? 1 : 0;
|
||||
sense_pos = 4 * (WATCH_A1_EIC_CHANNEL % 8);
|
||||
break;
|
||||
case A2:
|
||||
config_index = (WATCH_A2_EIC_CHANNEL > 7) ? 1 : 0;
|
||||
sense_pos = 4 * (WATCH_A2_EIC_CHANNEL % 8);
|
||||
break;
|
||||
case A3:
|
||||
config_index = (WATCH_A3_EIC_CHANNEL > 7) ? 1 : 0;
|
||||
sense_pos = 4 * (WATCH_A3_EIC_CHANNEL % 8);
|
||||
break;
|
||||
case A4:
|
||||
config_index = (WATCH_A4_EIC_CHANNEL > 7) ? 1 : 0;
|
||||
sense_pos = 4 * (WATCH_A4_EIC_CHANNEL % 8);
|
||||
break;
|
||||
case BTN_ALARM:
|
||||
config_index = (WATCH_BTN_ALARM_EIC_CHANNEL > 7) ? 1 : 0;
|
||||
sense_pos = 4 * (WATCH_BTN_ALARM_EIC_CHANNEL % 8);
|
||||
break;
|
||||
case BTN_LIGHT:
|
||||
config_index = (WATCH_BTN_LIGHT_EIC_CHANNEL > 7) ? 1 : 0;
|
||||
sense_pos = 4 * (WATCH_BTN_LIGHT_EIC_CHANNEL % 8);
|
||||
break;
|
||||
case BTN_MODE:
|
||||
config_index = (WATCH_BTN_MODE_EIC_CHANNEL > 7) ? 1 : 0;
|
||||
sense_pos = 4 * (WATCH_BTN_MODE_EIC_CHANNEL % 8);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_set_pin_direction(pin, GPIO_DIRECTION_IN);
|
||||
|
||||
// EIC configuration register is enable-protected, so we have to disable it first...
|
||||
if (hri_eic_get_CTRLA_reg(EIC, EIC_CTRLA_ENABLE)) {
|
||||
hri_eic_clear_CTRLA_ENABLE_bit(EIC);
|
||||
// ...and wait for it to synchronize.
|
||||
hri_eic_wait_for_sync(EIC, EIC_SYNCBUSY_ENABLE);
|
||||
}
|
||||
// now update the configuration...
|
||||
hri_eic_config_reg_t config = EIC->CONFIG[config_index].reg;
|
||||
config &= ~(7 << sense_pos);
|
||||
config |= trigger << (sense_pos);
|
||||
hri_eic_write_CONFIG_reg(EIC, config_index, config);
|
||||
// ...set the pin mode...
|
||||
gpio_set_pin_function(pin, GPIO_PIN_FUNCTION_A);
|
||||
if (pin == BTN_ALARM || pin == BTN_LIGHT || pin == BTN_MODE) gpio_set_pin_pull_mode(pin, GPIO_PULL_DOWN);
|
||||
// ...and re-enable the EIC
|
||||
hri_eic_set_CTRLA_ENABLE_bit(EIC);
|
||||
|
||||
ext_irq_register(pin, callback);
|
||||
}
|
||||
|
||||
inline void watch_register_button_callback(const uint8_t pin, ext_irq_cb_t callback) {
|
||||
watch_register_interrupt_callback(pin, callback, INTERRUPT_TRIGGER_RISING);
|
||||
}
|
||||
|
||||
inline void watch_enable_buttons(void) {
|
||||
watch_enable_external_interrupts();
|
||||
}
|
||||
61
watch-library/hardware/watch/watch_gpio.c
Normal file
61
watch-library/hardware/watch/watch_gpio.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "watch_gpio.h"
|
||||
|
||||
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_disable_digital_input(const uint8_t pin) {
|
||||
gpio_set_pin_direction(pin, GPIO_DIRECTION_OFF);
|
||||
gpio_set_pin_pull_mode(pin, GPIO_PULL_OFF);
|
||||
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);
|
||||
}
|
||||
93
watch-library/hardware/watch/watch_i2c.c
Normal file
93
watch-library/hardware/watch/watch_i2c.c
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "watch_i2c.h"
|
||||
|
||||
struct io_descriptor *I2C_0_io;
|
||||
|
||||
void watch_enable_i2c(void) {
|
||||
I2C_0_init();
|
||||
i2c_m_sync_get_io_descriptor(&I2C_0, &I2C_0_io);
|
||||
i2c_m_sync_enable(&I2C_0);
|
||||
}
|
||||
|
||||
void watch_disable_i2c(void) {
|
||||
i2c_m_sync_disable(&I2C_0);
|
||||
hri_mclk_clear_APBCMASK_SERCOM1_bit(MCLK);
|
||||
}
|
||||
|
||||
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 *)®, 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 *)®, 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 *)®, 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 *)®, 1);
|
||||
watch_i2c_receive(addr, (uint8_t *)&data, 4);
|
||||
|
||||
return data;
|
||||
}
|
||||
69
watch-library/hardware/watch/watch_led.c
Normal file
69
watch-library/hardware/watch/watch_led.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "watch_led.h"
|
||||
|
||||
void watch_enable_leds(void) {
|
||||
if (!hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
|
||||
_watch_enable_tcc();
|
||||
}
|
||||
}
|
||||
|
||||
void watch_disable_leds(void) {
|
||||
_watch_disable_tcc();
|
||||
}
|
||||
|
||||
void watch_enable_led(bool unused) {
|
||||
(void)unused;
|
||||
watch_enable_leds();
|
||||
}
|
||||
|
||||
void watch_disable_led(bool unused) {
|
||||
(void)unused;
|
||||
watch_disable_leds();
|
||||
}
|
||||
|
||||
void watch_set_led_color(uint8_t red, uint8_t green) {
|
||||
if (hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
|
||||
uint32_t period = hri_tcc_get_PER_reg(TCC0, TCC_PER_MASK);
|
||||
hri_tcc_write_CCBUF_reg(TCC0, WATCH_RED_TCC_CHANNEL, ((period * red * 1000ull) / 255000ull));
|
||||
hri_tcc_write_CCBUF_reg(TCC0, WATCH_GREEN_TCC_CHANNEL, ((period * green * 1000ull) / 255000ull));
|
||||
}
|
||||
}
|
||||
|
||||
void watch_set_led_red(void) {
|
||||
watch_set_led_color(255, 0);
|
||||
}
|
||||
|
||||
void watch_set_led_green(void) {
|
||||
watch_set_led_color(0, 255);
|
||||
}
|
||||
|
||||
void watch_set_led_yellow(void) {
|
||||
watch_set_led_color(255, 255);
|
||||
}
|
||||
|
||||
void watch_set_led_off(void) {
|
||||
watch_set_led_color(0, 0);
|
||||
}
|
||||
423
watch-library/hardware/watch/watch_private.c
Normal file
423
watch-library/hardware/watch/watch_private.c
Normal file
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "watch_private.h"
|
||||
#include "watch_utility.h"
|
||||
#include "tusb.h"
|
||||
|
||||
void _watch_init(void) {
|
||||
// disable the LED pin (it may have been enabled by the bootloader)
|
||||
watch_disable_digital_output(RED);
|
||||
|
||||
// RAM should be back-biased in STANDBY
|
||||
PM->STDBYCFG.bit.BBIASHS = 1;
|
||||
|
||||
// Use switching regulator for lower power consumption.
|
||||
SUPC->VREG.bit.SEL = 1;
|
||||
while(!SUPC->STATUS.bit.VREGRDY);
|
||||
|
||||
// set up the brownout detector (low battery warning)
|
||||
NVIC_DisableIRQ(SYSTEM_IRQn);
|
||||
NVIC_ClearPendingIRQ(SYSTEM_IRQn);
|
||||
NVIC_EnableIRQ(SYSTEM_IRQn);
|
||||
SUPC->BOD33.bit.ENABLE = 0; // BOD33 must be disabled to change its configuration
|
||||
SUPC->BOD33.bit.VMON = 0; // Monitor VDD in active and standby mode
|
||||
SUPC->BOD33.bit.ACTCFG = 1; // Enable sampling mode when active
|
||||
SUPC->BOD33.bit.RUNSTDBY = 1; // Enable sampling mode in standby
|
||||
SUPC->BOD33.bit.STDBYCFG = 1; // Run in standby
|
||||
SUPC->BOD33.bit.RUNBKUP = 0; // Don't run in backup mode
|
||||
SUPC->BOD33.bit.PSEL = 0xB; // Check battery level every 4 seconds
|
||||
SUPC->BOD33.bit.LEVEL = 31; // Detect brownout at 2.5V (1.445V + level * 34mV)
|
||||
SUPC->BOD33.bit.ACTION = 0x2; // Generate an interrupt when BOD33 is triggered
|
||||
SUPC->BOD33.bit.HYST = 0; // Disable hysteresis
|
||||
while(!SUPC->STATUS.bit.B33SRDY);
|
||||
|
||||
// Enable interrupt on BOD33 detect
|
||||
SUPC->INTENSET.bit.BOD33DET = 1;
|
||||
SUPC->BOD33.bit.ENABLE = 1;
|
||||
|
||||
// External wake depends on RTC; calendar is a required module.
|
||||
_watch_rtc_init();
|
||||
|
||||
// set up state
|
||||
btn_alarm_callback = NULL;
|
||||
a2_callback = NULL;
|
||||
a4_callback = NULL;
|
||||
}
|
||||
|
||||
static inline void _watch_wait_for_entropy() {
|
||||
while (!hri_trng_get_INTFLAG_reg(TRNG, TRNG_INTFLAG_DATARDY));
|
||||
}
|
||||
|
||||
// this function is called by arc4random to get entropy for random number generation.
|
||||
// let's use the SAM L22's true random number generator to seed the PRNG!
|
||||
int getentropy(void *buf, size_t buflen) {
|
||||
hri_mclk_set_APBCMASK_TRNG_bit(MCLK);
|
||||
hri_trng_set_CTRLA_ENABLE_bit(TRNG);
|
||||
|
||||
size_t i = 0;
|
||||
while(i < buflen / 4) {
|
||||
_watch_wait_for_entropy();
|
||||
((uint32_t *)buf)[i++] = hri_trng_read_DATA_reg(TRNG);
|
||||
}
|
||||
|
||||
// but what if they asked for an awkward number of bytes?
|
||||
if (buflen % 4) {
|
||||
// all good: let's fill in one, two or three bytes at the end of the buffer.
|
||||
_watch_wait_for_entropy();
|
||||
uint32_t last_little_bit = hri_trng_read_DATA_reg(TRNG);
|
||||
for(size_t j = 0; j <= (buflen % 4); j++) {
|
||||
((uint8_t *)buf)[i * 4 + j] = (last_little_bit >> (j * 8)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
hri_trng_clear_CTRLA_ENABLE_bit(TRNG);
|
||||
hri_mclk_clear_APBCMASK_TRNG_bit(MCLK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _gettimeofday(struct timeval *tv, void *tzvp) {
|
||||
(void)tzvp;
|
||||
watch_date_time date_time = watch_rtc_get_date_time();
|
||||
|
||||
// FIXME: this assumes the system time is UTC! Will break for any other time zone.
|
||||
tv->tv_sec = watch_utility_date_time_to_unix_time(date_time, 0);
|
||||
tv->tv_usec = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _watch_enable_tcc(void) {
|
||||
// clock TCC0 with the main clock (8 MHz) and enable the peripheral clock.
|
||||
hri_gclk_write_PCHCTRL_reg(GCLK, TCC0_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK0_Val | GCLK_PCHCTRL_CHEN);
|
||||
hri_mclk_set_APBCMASK_TCC0_bit(MCLK);
|
||||
// disable and reset TCC0.
|
||||
hri_tcc_clear_CTRLA_ENABLE_bit(TCC0);
|
||||
hri_tcc_wait_for_sync(TCC0, TCC_SYNCBUSY_ENABLE);
|
||||
hri_tcc_write_CTRLA_reg(TCC0, TCC_CTRLA_SWRST);
|
||||
hri_tcc_wait_for_sync(TCC0, TCC_SYNCBUSY_SWRST);
|
||||
// divide the clock down to 1 MHz
|
||||
if (hri_usbdevice_get_CTRLA_ENABLE_bit(USB)) {
|
||||
// if USB is enabled, we are running an 8 MHz clock.
|
||||
hri_tcc_write_CTRLA_reg(TCC0, TCC_CTRLA_PRESCALER_DIV8);
|
||||
} else {
|
||||
// otherwise it's 4 Mhz.
|
||||
hri_tcc_write_CTRLA_reg(TCC0, TCC_CTRLA_PRESCALER_DIV4);
|
||||
}
|
||||
// We're going to use normal PWM mode, which means period is controlled by PER, and duty cycle is controlled by
|
||||
// each compare channel's value:
|
||||
// * Buzzer tones are set by setting PER to the desired period for a given frequency, and CC[1] to half of that
|
||||
// period (i.e. a square wave with a 50% duty cycle).
|
||||
// * LEDs on CC[2] and CC[3] can be set to any value from 0 (off) to PER (fully on).
|
||||
hri_tcc_write_WAVE_reg(TCC0, TCC_WAVE_WAVEGEN_NPWM);
|
||||
#ifdef WATCH_INVERT_LED_POLARITY
|
||||
// This is here for the dev board, which uses a common anode LED (instead of common cathode like the actual watch).
|
||||
hri_tcc_set_WAVE_reg(TCC0, (1 << (TCC_WAVE_POL0_Pos + WATCH_RED_TCC_CHANNEL)) |
|
||||
(1 << (TCC_WAVE_POL0_Pos + WATCH_GREEN_TCC_CHANNEL)));
|
||||
#endif
|
||||
// The buzzer will set the period depending on the tone it wants to play, but we have to set some period here to
|
||||
// get the LED working. Almost any period will do, tho it should be below 20000 (i.e. 50 Hz) to avoid flickering.
|
||||
hri_tcc_write_PER_reg(TCC0, 4096);
|
||||
// Set the duty cycle of all pins to 0: LED's off, buzzer not buzzing.
|
||||
hri_tcc_write_CC_reg(TCC0, WATCH_BUZZER_TCC_CHANNEL, 0);
|
||||
hri_tcc_write_CC_reg(TCC0, WATCH_RED_TCC_CHANNEL, 0);
|
||||
hri_tcc_write_CC_reg(TCC0, WATCH_GREEN_TCC_CHANNEL, 0);
|
||||
// Enable the TCC
|
||||
hri_tcc_set_CTRLA_ENABLE_bit(TCC0);
|
||||
hri_tcc_wait_for_sync(TCC0, TCC_SYNCBUSY_ENABLE);
|
||||
|
||||
// enable LED PWM pins (the LED driver assumes if the TCC is on, the pins are enabled)
|
||||
gpio_set_pin_direction(RED, GPIO_DIRECTION_OUT);
|
||||
gpio_set_pin_function(RED, WATCH_RED_TCC_PINMUX);
|
||||
gpio_set_pin_direction(GREEN, GPIO_DIRECTION_OUT);
|
||||
gpio_set_pin_function(GREEN, WATCH_GREEN_TCC_PINMUX);
|
||||
}
|
||||
|
||||
void _watch_disable_tcc(void) {
|
||||
// disable all PWM pins
|
||||
gpio_set_pin_direction(BUZZER, GPIO_DIRECTION_OFF);
|
||||
gpio_set_pin_function(BUZZER, GPIO_PIN_FUNCTION_OFF);
|
||||
gpio_set_pin_direction(RED, GPIO_DIRECTION_OFF);
|
||||
gpio_set_pin_function(RED, GPIO_PIN_FUNCTION_OFF);
|
||||
gpio_set_pin_direction(GREEN, GPIO_DIRECTION_OFF);
|
||||
gpio_set_pin_function(GREEN, GPIO_PIN_FUNCTION_OFF);
|
||||
|
||||
// disable the TCC
|
||||
hri_tcc_clear_CTRLA_ENABLE_bit(TCC0);
|
||||
hri_mclk_clear_APBCMASK_TCC0_bit(MCLK);
|
||||
}
|
||||
|
||||
void _watch_enable_usb(void) {
|
||||
// disable USB, just in case.
|
||||
hri_usb_clear_CTRLA_ENABLE_bit(USB);
|
||||
|
||||
// bump clock up to 8 MHz
|
||||
hri_oscctrl_write_OSC16MCTRL_FSEL_bf(OSCCTRL, OSCCTRL_OSC16MCTRL_FSEL_8_Val);
|
||||
|
||||
// reset flags and disable DFLL
|
||||
OSCCTRL->INTFLAG.reg = OSCCTRL_INTFLAG_DFLLRDY;
|
||||
OSCCTRL->DFLLCTRL.reg = 0;
|
||||
while (!(OSCCTRL->STATUS.reg & OSCCTRL_STATUS_DFLLRDY));
|
||||
|
||||
// set the coarse and fine values to speed up frequency lock.
|
||||
uint32_t coarse =(*((uint32_t *)NVMCTRL_OTP5)) >> 26;
|
||||
OSCCTRL->DFLLVAL.reg = OSCCTRL_DFLLVAL_COARSE(coarse) |
|
||||
OSCCTRL_DFLLVAL_FINE(0x200);
|
||||
// set coarse and fine steps, and multiplier (48 MHz = 32768 Hz * 1465)
|
||||
OSCCTRL->DFLLMUL.reg = OSCCTRL_DFLLMUL_CSTEP( 1 ) |
|
||||
OSCCTRL_DFLLMUL_FSTEP( 1 ) |
|
||||
OSCCTRL_DFLLMUL_MUL( 1465 );
|
||||
// set closed loop mode, chill cycle disable and USB clock recovery mode, and enable the DFLL.
|
||||
OSCCTRL->DFLLCTRL.reg = OSCCTRL_DFLLCTRL_MODE | OSCCTRL_DFLLCTRL_CCDIS | OSCCTRL_DFLLCTRL_ONDEMAND | OSCCTRL_DFLLCTRL_RUNSTDBY | OSCCTRL_DFLLCTRL_USBCRM | OSCCTRL_DFLLCTRL_ENABLE;
|
||||
while (!(OSCCTRL->STATUS.reg & OSCCTRL_STATUS_DFLLRDY));
|
||||
|
||||
// assign DFLL to GCLK1
|
||||
GCLK->GENCTRL[1].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DFLL48M) | GCLK_GENCTRL_DIV(1) | GCLK_GENCTRL_GENEN;// | GCLK_GENCTRL_OE;
|
||||
while (GCLK->SYNCBUSY.bit.GENCTRL1);
|
||||
|
||||
// assign GCLK1 to USB
|
||||
hri_gclk_write_PCHCTRL_reg(GCLK, USB_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK1_Val | GCLK_PCHCTRL_CHEN);
|
||||
hri_mclk_set_AHBMASK_USB_bit(MCLK);
|
||||
hri_mclk_set_APBBMASK_USB_bit(MCLK);
|
||||
|
||||
// USB Pin Init
|
||||
gpio_set_pin_direction(PIN_PA24, GPIO_DIRECTION_OUT);
|
||||
gpio_set_pin_level(PIN_PA24, false);
|
||||
gpio_set_pin_pull_mode(PIN_PA24, GPIO_PULL_OFF);
|
||||
gpio_set_pin_direction(PIN_PA25, GPIO_DIRECTION_OUT);
|
||||
gpio_set_pin_level(PIN_PA25, false);
|
||||
gpio_set_pin_pull_mode(PIN_PA25, GPIO_PULL_OFF);
|
||||
|
||||
gpio_set_pin_function(PIN_PA24, PINMUX_PA24G_USB_DM);
|
||||
gpio_set_pin_function(PIN_PA25, PINMUX_PA25G_USB_DP);
|
||||
|
||||
// before we init TinyUSB, we are going to need a periodic callback to handle TinyUSB tasks.
|
||||
// TC2 and TC3 are reserved for devices on the 9-pin connector, so let's use TC0.
|
||||
// clock TC0 with the 8 MHz clock on GCLK0.
|
||||
hri_gclk_write_PCHCTRL_reg(GCLK, TC0_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK0_Val | GCLK_PCHCTRL_CHEN);
|
||||
// and enable the peripheral clock.
|
||||
hri_mclk_set_APBCMASK_TC0_bit(MCLK);
|
||||
// disable and reset TC0.
|
||||
hri_tc_clear_CTRLA_ENABLE_bit(TC0);
|
||||
hri_tc_wait_for_sync(TC0, TC_SYNCBUSY_ENABLE);
|
||||
hri_tc_write_CTRLA_reg(TC0, TC_CTRLA_SWRST);
|
||||
hri_tc_wait_for_sync(TC0, TC_SYNCBUSY_SWRST);
|
||||
// configure the TC to overflow 1,000 times per second
|
||||
hri_tc_write_CTRLA_reg(TC0, TC_CTRLA_PRESCALER_DIV64 | // divide the 8 MHz clock by 64 to count at 125 KHz
|
||||
TC_CTRLA_MODE_COUNT8 | // count in 8-bit mode
|
||||
TC_CTRLA_RUNSTDBY); // run in standby, just in case we figure that out
|
||||
hri_tccount8_write_PER_reg(TC0, 125); // 125000 Hz / 125 = 1,000 Hz
|
||||
// set an interrupt on overflow; this will call TC0_Handler below.
|
||||
hri_tc_set_INTEN_OVF_bit(TC0);
|
||||
NVIC_ClearPendingIRQ(TC0_IRQn);
|
||||
NVIC_EnableIRQ (TC0_IRQn);
|
||||
|
||||
// now we can init TinyUSB
|
||||
tusb_init();
|
||||
// and start the timer that handles USB device tasks.
|
||||
hri_tc_set_CTRLA_ENABLE_bit(TC0);
|
||||
}
|
||||
|
||||
// this function ends up getting called by printf to log stuff to the USB console.
|
||||
int _write(int file, char *ptr, int len) {
|
||||
(void)file;
|
||||
if (hri_usbdevice_get_CTRLA_ENABLE_bit(USB)) {
|
||||
tud_cdc_n_write(0, (void const*)ptr, len);
|
||||
tud_cdc_n_write_flush(0);
|
||||
return len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// this method could be overridden to read stuff from the USB console? but no need rn.
|
||||
int _read(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Alternate function that outputs to the debug UART. useful for debugging USB issues.
|
||||
// int _write(int file, char *ptr, int len) {
|
||||
// (void)file;
|
||||
// int pos = 0;
|
||||
// while(pos < len) watch_debug_putc(ptr[pos++]);
|
||||
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
void USB_Handler(void) {
|
||||
tud_int_handler(0);
|
||||
}
|
||||
|
||||
void TC0_Handler(void) {
|
||||
tud_task();
|
||||
TC0->COUNT8.INTFLAG.reg |= TC_INTFLAG_OVF;
|
||||
}
|
||||
|
||||
|
||||
// USB Descriptors and tinyUSB callbacks follow.
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
tusb_desc_device_t const desc_device =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
|
||||
// Use Interface Association Descriptor (IAD) for CDC
|
||||
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = 0x1209,
|
||||
.idProduct = 0x2151,
|
||||
.bcdDevice = 0x0100,
|
||||
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const * tud_descriptor_device_cb(void) {
|
||||
return (uint8_t const *) &desc_device;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
enum {
|
||||
ITF_NUM_CDC = 0,
|
||||
ITF_NUM_CDC_DATA,
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
|
||||
|
||||
#define EPNUM_CDC_NOTIF 0x81
|
||||
#define EPNUM_CDC_OUT 0x02
|
||||
#define EPNUM_CDC_IN 0x82
|
||||
|
||||
|
||||
uint8_t const desc_fs_configuration[] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
|
||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
|
||||
};
|
||||
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const * tud_descriptor_configuration_cb(uint8_t index) {
|
||||
(void) index; // for multiple configurations
|
||||
return desc_fs_configuration;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// String Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// array of pointer to string descriptors
|
||||
char const* string_desc_arr [] =
|
||||
{
|
||||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||
"TinyUSB", // 1: Manufacturer
|
||||
"TinyUSB Device", // 2: Product
|
||||
"123456", // 3: Serials, should use chip ID
|
||||
"TinyUSB CDC", // 4: CDC Interface
|
||||
};
|
||||
|
||||
static uint16_t _desc_str[32];
|
||||
|
||||
// Invoked when received GET STRING DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
(void) langid;
|
||||
|
||||
uint8_t chr_count;
|
||||
|
||||
if ( index == 0) {
|
||||
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||
chr_count = 1;
|
||||
} else {
|
||||
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
|
||||
|
||||
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
|
||||
|
||||
const char* str = string_desc_arr[index];
|
||||
|
||||
// Cap at max char
|
||||
chr_count = strlen(str);
|
||||
if ( chr_count > 31 ) chr_count = 31;
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for(uint8_t i=0; i<chr_count; i++)
|
||||
{
|
||||
_desc_str[1+i] = str[i];
|
||||
}
|
||||
}
|
||||
|
||||
// first byte is length (including header), second byte is string type
|
||||
_desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
|
||||
|
||||
return _desc_str;
|
||||
}
|
||||
195
watch-library/hardware/watch/watch_rtc.c
Normal file
195
watch-library/hardware/watch/watch_rtc.c
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "watch_rtc.h"
|
||||
|
||||
ext_irq_cb_t tick_callbacks[8];
|
||||
ext_irq_cb_t alarm_callback;
|
||||
ext_irq_cb_t btn_alarm_callback;
|
||||
ext_irq_cb_t a2_callback;
|
||||
ext_irq_cb_t a4_callback;
|
||||
|
||||
bool _watch_rtc_is_enabled(void) {
|
||||
return RTC->MODE2.CTRLA.bit.ENABLE;
|
||||
}
|
||||
|
||||
static void _sync_rtc(void) {
|
||||
while (RTC->MODE2.SYNCBUSY.reg);
|
||||
}
|
||||
|
||||
void _watch_rtc_init(void) {
|
||||
MCLK->APBAMASK.reg |= MCLK_APBAMASK_RTC;
|
||||
|
||||
if (_watch_rtc_is_enabled()) return; // don't reset the RTC if it's already set up.
|
||||
|
||||
RTC->MODE2.CTRLA.bit.ENABLE = 0;
|
||||
_sync_rtc();
|
||||
|
||||
RTC->MODE2.CTRLA.bit.SWRST = 1;
|
||||
_sync_rtc();
|
||||
|
||||
RTC->MODE2.CTRLA.bit.MODE = RTC_MODE2_CTRLA_MODE_CLOCK_Val;
|
||||
RTC->MODE2.CTRLA.bit.PRESCALER = RTC_MODE2_CTRLA_PRESCALER_DIV1024_Val;
|
||||
RTC->MODE2.CTRLA.bit.CLOCKSYNC = 1;
|
||||
RTC->MODE2.CTRLA.bit.ENABLE = 1;
|
||||
_sync_rtc();
|
||||
}
|
||||
|
||||
void watch_rtc_set_date_time(watch_date_time date_time) {
|
||||
RTC->MODE2.CLOCK.reg = date_time.reg;
|
||||
_sync_rtc();
|
||||
}
|
||||
|
||||
watch_date_time watch_rtc_get_date_time(void) {
|
||||
watch_date_time retval;
|
||||
|
||||
_sync_rtc();
|
||||
retval.reg = RTC->MODE2.CLOCK.reg;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void watch_rtc_register_tick_callback(ext_irq_cb_t callback) {
|
||||
watch_rtc_register_periodic_callback(callback, 1);
|
||||
}
|
||||
|
||||
void watch_rtc_disable_tick_callback(void) {
|
||||
watch_rtc_disable_periodic_callback(1);
|
||||
}
|
||||
|
||||
void watch_rtc_register_periodic_callback(ext_irq_cb_t callback, uint8_t frequency) {
|
||||
// we told them, it has to be a power of 2.
|
||||
if (__builtin_popcount(frequency) != 1) return;
|
||||
|
||||
// this left-justifies the period in a 32-bit integer.
|
||||
uint32_t tmp = frequency << 24;
|
||||
// now we can count the leading zeroes to get the value we need.
|
||||
// 0x01 (1 Hz) will have 7 leading zeros for PER7. 0xF0 (128 Hz) will have no leading zeroes for PER0.
|
||||
uint8_t per_n = __builtin_clz(tmp);
|
||||
|
||||
// this also maps nicely to an index for our list of tick callbacks.
|
||||
tick_callbacks[per_n] = callback;
|
||||
|
||||
NVIC_ClearPendingIRQ(RTC_IRQn);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
RTC->MODE2.INTENSET.reg = 1 << per_n;
|
||||
}
|
||||
|
||||
void watch_rtc_disable_periodic_callback(uint8_t frequency) {
|
||||
if (__builtin_popcount(frequency) != 1) return;
|
||||
uint8_t per_n = __builtin_clz(frequency << 24);
|
||||
RTC->MODE2.INTENCLR.reg = 1 << per_n;
|
||||
}
|
||||
|
||||
void watch_rtc_disable_all_periodic_callbacks(void) {
|
||||
RTC->MODE2.INTENCLR.reg = 0xFF;
|
||||
}
|
||||
|
||||
void watch_rtc_register_alarm_callback(ext_irq_cb_t callback, watch_date_time alarm_time, watch_rtc_alarm_match mask) {
|
||||
RTC->MODE2.Mode2Alarm[0].ALARM.reg = alarm_time.reg;
|
||||
RTC->MODE2.Mode2Alarm[0].MASK.reg = mask;
|
||||
RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_ALARM0;
|
||||
alarm_callback = callback;
|
||||
NVIC_ClearPendingIRQ(RTC_IRQn);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_ALARM0;
|
||||
}
|
||||
|
||||
void watch_rtc_disable_alarm_callback(void) {
|
||||
RTC->MODE2.INTENCLR.reg = RTC_MODE2_INTENCLR_ALARM0;
|
||||
}
|
||||
|
||||
void RTC_Handler(void) {
|
||||
uint16_t interrupt_status = RTC->MODE2.INTFLAG.reg;
|
||||
uint16_t interrupt_enabled = RTC->MODE2.INTENSET.reg;
|
||||
|
||||
if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_PER_Msk) {
|
||||
// handle the tick callback first, it's what we do the most.
|
||||
// start from PER7, the 1 Hz tick.
|
||||
for(int8_t i = 7; i >= 0; i--) {
|
||||
if ((interrupt_status & interrupt_enabled) & (1 << i)) {
|
||||
if (tick_callbacks[i] != NULL) {
|
||||
tick_callbacks[i]();
|
||||
}
|
||||
RTC->MODE2.INTFLAG.reg = 1 << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_TAMPER) {
|
||||
// handle the extwake interrupts next.
|
||||
uint8_t reason = RTC->MODE2.TAMPID.reg;
|
||||
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 (a4_callback != NULL) a4_callback();
|
||||
}
|
||||
RTC->MODE2.TAMPID.reg = reason;
|
||||
RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_TAMPER;
|
||||
} else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_ALARM0) {
|
||||
// finally handle the alarm.
|
||||
if (alarm_callback != NULL) {
|
||||
alarm_callback();
|
||||
}
|
||||
RTC->MODE2.INTFLAG.reg = RTC_MODE2_INTFLAG_ALARM0;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// Deprecated functions
|
||||
|
||||
void watch_set_date_time(struct calendar_date_time date_time) {
|
||||
RTC_MODE2_CLOCK_Type val;
|
||||
|
||||
val.bit.SECOND = date_time.time.sec;
|
||||
val.bit.MINUTE = date_time.time.min;
|
||||
val.bit.HOUR = date_time.time.hour;
|
||||
val.bit.DAY = date_time.date.day;
|
||||
val.bit.MONTH = date_time.date.month;
|
||||
val.bit.YEAR = (uint8_t)(date_time.date.year - WATCH_RTC_REFERENCE_YEAR);
|
||||
|
||||
RTC->MODE2.CLOCK.reg = val.reg;
|
||||
|
||||
_sync_rtc();
|
||||
}
|
||||
|
||||
void watch_get_date_time(struct calendar_date_time *date_time) {
|
||||
_sync_rtc();
|
||||
RTC_MODE2_CLOCK_Type val = RTC->MODE2.CLOCK;
|
||||
|
||||
date_time->time.sec = val.bit.SECOND;
|
||||
date_time->time.min = val.bit.MINUTE;
|
||||
date_time->time.hour = val.bit.HOUR;
|
||||
date_time->date.day = val.bit.DAY;
|
||||
date_time->date.month = val.bit.MONTH;
|
||||
date_time->date.year = val.bit.YEAR + WATCH_RTC_REFERENCE_YEAR;
|
||||
}
|
||||
|
||||
void watch_register_tick_callback(ext_irq_cb_t callback) {
|
||||
tick_callbacks[7] = callback;
|
||||
NVIC_ClearPendingIRQ(RTC_IRQn);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
RTC->MODE2.INTENSET.reg = RTC_MODE2_INTENSET_PER7;
|
||||
}
|
||||
101
watch-library/hardware/watch/watch_slcd.c
Normal file
101
watch-library/hardware/watch/watch_slcd.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "watch_slcd.h"
|
||||
#include "watch_private_display.h"
|
||||
#include "hpl_slcd_config.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Segmented Display
|
||||
|
||||
static void _sync_slcd(void) {
|
||||
while (SLCD->SYNCBUSY.reg);
|
||||
}
|
||||
|
||||
void watch_enable_display(void) {
|
||||
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_clear_display(void) {
|
||||
SLCD->SDATAL0.reg = 0;
|
||||
SLCD->SDATAL1.reg = 0;
|
||||
SLCD->SDATAL2.reg = 0;
|
||||
}
|
||||
|
||||
void watch_start_character_blink(char character, uint32_t duration) {
|
||||
SLCD->CTRLD.bit.FC0EN = 0;
|
||||
_sync_slcd();
|
||||
|
||||
if (duration <= SLCD_FC_BYPASS_MAX_MS) {
|
||||
SLCD->FC0.reg = SLCD_FC0_PB | ((duration / (1000 / SLCD_FRAME_FREQUENCY)) - 1);
|
||||
} else {
|
||||
SLCD->FC0.reg = (((duration / (1000 / SLCD_FRAME_FREQUENCY)) / 8 - 1));
|
||||
}
|
||||
SLCD->CTRLD.bit.FC0EN = 1;
|
||||
|
||||
watch_display_character(character, 7);
|
||||
watch_clear_pixel(2, 10); // clear segment B of position 7 since it can't blink
|
||||
|
||||
SLCD->CTRLD.bit.BLINK = 0;
|
||||
SLCD->CTRLA.bit.ENABLE = 0;
|
||||
_sync_slcd();
|
||||
|
||||
SLCD->BCFG.bit.BSS0 = 0x07;
|
||||
SLCD->BCFG.bit.BSS1 = 0x07;
|
||||
|
||||
SLCD->CTRLD.bit.BLINK = 1;
|
||||
_sync_slcd();
|
||||
SLCD->CTRLA.bit.ENABLE = 1;
|
||||
_sync_slcd();
|
||||
}
|
||||
|
||||
void watch_stop_blink(void) {
|
||||
SLCD->CTRLD.bit.FC0EN = 0;
|
||||
SLCD->CTRLD.bit.BLINK = 0;
|
||||
}
|
||||
|
||||
void watch_start_tick_animation(uint32_t duration) {
|
||||
watch_display_character(' ', 8);
|
||||
const uint32_t segs[] = { SLCD_SEGID(0, 2)};
|
||||
slcd_sync_start_animation(&SEGMENT_LCD_0, segs, 1, duration);
|
||||
}
|
||||
|
||||
bool watch_tick_animation_is_running(void) {
|
||||
return hri_slcd_get_CTRLD_CSREN_bit(SLCD);
|
||||
}
|
||||
|
||||
void watch_stop_tick_animation(void) {
|
||||
const uint32_t segs[] = { SLCD_SEGID(0, 2)};
|
||||
slcd_sync_stop_animation(&SEGMENT_LCD_0, segs, 1);
|
||||
watch_display_character(' ', 8);
|
||||
}
|
||||
89
watch-library/hardware/watch/watch_uart.c
Normal file
89
watch-library/hardware/watch/watch_uart.c
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 "watch_uart.h"
|
||||
#include "peripheral_clk_config.h"
|
||||
|
||||
void watch_enable_debug_uart(uint32_t baud) {
|
||||
uint64_t br = (uint64_t)65536 * ((CONF_CPU_FREQUENCY * 4) - 16 * baud) / (CONF_CPU_FREQUENCY * 4);
|
||||
|
||||
gpio_set_pin_direction(A2, GPIO_DIRECTION_OUT);
|
||||
gpio_set_pin_function(A2, PINMUX_PB02C_SERCOM3_PAD0);
|
||||
|
||||
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(1/*PAD1*/) | SERCOM_USART_CTRLA_TXPO(0/*PAD0*/);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
void watch_debug_puts(char *s) {
|
||||
while (*s) watch_debug_putc(*s++);
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
Reference in New Issue
Block a user