From: joeycastillo Date: Wed, 18 Sep 2024 18:53:46 +0000 (-0400) Subject: consolidate buzzer and LED into TCC file X-Git-Url: https://git.earman.xyz/?a=commitdiff_plain;h=0585a1d0d850cbe8def8830565225f910c3e27a4;p=sensor-watch.git consolidate buzzer and LED into TCC file --- diff --git a/Makefile b/Makefile index d5b47da..1532bd2 100644 --- a/Makefile +++ b/Makefile @@ -29,9 +29,8 @@ INCLUDES += \ # Add your source files here. SRCS += \ - ./watch-library/hardware/watch/watch_buzzer.c \ ./watch-library/hardware/watch/watch_gpio.c \ - ./watch-library/hardware/watch/watch_led.c \ + ./watch-library/hardware/watch/watch_tcc.c \ ./app.c \ # Finally, leave this line at the bottom of the file. diff --git a/watch-library/hardware/watch/watch_buzzer.c b/watch-library/hardware/watch/watch_buzzer.c deleted file mode 100644 index f762dd2..0000000 --- a/watch-library/hardware/watch/watch_buzzer.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020 Joey Castillo - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "watch_buzzer.h" -#include "watch_private.h" -#include "delay.h" -#include "tcc.h" - -void _watch_enable_tcc(void); - -bool watch_is_buzzer_or_led_enabled(void){ - return tcc_is_enabled(0); -} - -inline void watch_enable_buzzer(void) { - if (!tcc_is_enabled(0)) { - _watch_enable_tcc(); - } -} - -inline void watch_set_buzzer_period(uint32_t period) { - tcc_set_period(0, period, true); - tcc_set_cc(0, (WATCH_BUZZER_TCC_CHANNEL) % 4, period / 2, true); -} - -void watch_disable_buzzer(void) { - _watch_disable_tcc(); -} - -inline void watch_set_buzzer_on(void) { - HAL_GPIO_BUZZER_out(); - HAL_GPIO_BUZZER_pmuxen(HAL_GPIO_PMUX_TCC_ALT); -} - -inline void watch_set_buzzer_off(void) { - HAL_GPIO_BUZZER_pmuxdis(); - HAL_GPIO_BUZZER_off(); -} - -void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms) { - if (note == BUZZER_NOTE_REST) { - watch_set_buzzer_off(); - } else { - watch_set_buzzer_period(NotePeriods[note]); - watch_set_buzzer_on(); - } - delay_ms(duration_ms); - watch_set_buzzer_off(); -} - -void _watch_enable_tcc(void) { - // set up the TCC with a 1 MHz clock, but there's a trick: - if (USB->DEVICE.CTRLA.bit.ENABLE) { - // if USB is enabled, we are running an 8 MHz clock, so we divide by 8. - tcc_init(0, GENERIC_CLOCK_0, TCC_PRESCALER_DIV8); - } else { - // otherwise it's 4 Mhz and we divide by 4. - tcc_init(0, GENERIC_CLOCK_0, TCC_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[0] CC[2] and CC[3] can be set to any value from 0 (off) to PER (fully on). - tcc_set_wavegen(0, TCC_WAVEGEN_NORMAL_PWM); -#ifdef WATCH_INVERT_LED_POLARITY - // invert all channels, we'll flip the buzzer back in just a moment. - // this is easier than writing a maze of #ifdefs. - tcc_set_channel_polarity(0, 4, TCC_CHANNEL_POLARITY_INVERTED); - tcc_set_channel_polarity(0, 5, TCC_CHANNEL_POLARITY_INVERTED); - tcc_set_channel_polarity(0, 6, TCC_CHANNEL_POLARITY_INVERTED); - tcc_set_channel_polarity(0, 7, TCC_CHANNEL_POLARITY_INVERTED); -#endif // WATCH_INVERT_LED_POLARITY - tcc_set_channel_polarity(0, WATCH_BUZZER_TCC_CHANNEL, TCC_CHANNEL_POLARITY_NORMAL); - - // Set the period to 1 kHz to start. - tcc_set_period(0, 1000, false); - - // Set the duty cycle of all pins to 0: LED's off, buzzer not buzzing. - tcc_set_cc(0, (WATCH_BUZZER_TCC_CHANNEL) % 4, 0, false); - tcc_set_cc(0, (WATCH_RED_TCC_CHANNEL) % 4, 0, false); -#ifdef WATCH_GREEN_TCC_CHANNEL - tcc_set_cc(0, (WATCH_GREEN_TCC_CHANNEL) % 4, 0, false); -#endif -#ifdef WATCH_BLUE_TCC_CHANNEL - tcc_set_cc(0, (WATCH_BLUE_TCC_CHANNEL) % 4, 0, false); -#endif - - // enable LED PWM pins (the LED driver assumes if the TCC is on, the pins are enabled) - HAL_GPIO_RED_pmuxen(HAL_GPIO_PMUX_TCC_ALT); - HAL_GPIO_RED_out(); -#ifdef WATCH_GREEN_TCC_CHANNEL - HAL_GPIO_GREEN_pmuxen(HAL_GPIO_PMUX_TCC_ALT); - HAL_GPIO_GREEN_out(); -#endif -#ifdef WATCH_BLUE_TCC_CHANNEL - HAL_GPIO_BLUE_pmuxen(HAL_GPIO_PMUX_TCC_ALT); - HAL_GPIO_BLUE_out(); -#endif - - // Enable the TCC - tcc_enable(0); -} - -void _watch_disable_tcc(void) { - // disable all PWM pins - HAL_GPIO_BUZZER_pmuxdis(); - HAL_GPIO_BUZZER_off(); - HAL_GPIO_RED_pmuxdis(); - HAL_GPIO_RED_off(); -#ifdef WATCH_GREEN_TCC_CHANNEL - HAL_GPIO_GREEN_pmuxdis(); - HAL_GPIO_GREEN_off(); -#endif -#ifdef WATCH_BLUE_TCC_CHANNEL - HAL_GPIO_BLUE_pmuxdis(); - HAL_GPIO_BLUE_off(); -#endif - tcc_disable(0); -} diff --git a/watch-library/hardware/watch/watch_led.c b/watch-library/hardware/watch/watch_led.c deleted file mode 100644 index 70ce305..0000000 --- a/watch-library/hardware/watch/watch_led.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020 Joey Castillo - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "watch_led.h" -#include "watch_private.h" -#include "tcc.h" - -void _watch_enable_tcc(void); - -void watch_enable_leds(void) { - if (!tcc_is_enabled(0)) { - _watch_enable_tcc(); - } -} - -void watch_disable_leds(void) { - _watch_disable_tcc(); -} - -void watch_set_led_color(uint8_t red, uint8_t green) { -#ifdef WATCH_BLUE_TCC_CHANNEL - watch_set_led_color_rgb(red, green, 0); -#else - watch_set_led_color_rgb(red, green, green); -#endif -} - -void watch_set_led_color_rgb(uint8_t red, uint8_t green, uint8_t blue) { - if (tcc_is_enabled(0)) { - uint32_t period = tcc_get_period(0); - tcc_set_cc(0, (WATCH_RED_TCC_CHANNEL) % 4, ((period * (uint32_t)red * 1000ull) / 255000ull), true); -#ifdef WATCH_GREEN_TCC_CHANNEL - tcc_set_cc(0, (WATCH_GREEN_TCC_CHANNEL) % 4, ((period * (uint32_t)green * 1000ull) / 255000ull), true); -#else - (void) green; // silence warning -#endif -#ifdef WATCH_BLUE_TCC_CHANNEL - tcc_set_cc(0, (WATCH_BLUE_TCC_CHANNEL) % 4, ((period * (uint32_t)blue * 1000ull) / 255000ull), true); -#else - (void) blue; // silence warning -#endif - } -} - -void watch_set_led_red(void) { - watch_set_led_color_rgb(255, 0, 0); -} - -void watch_set_led_green(void) { - watch_set_led_color_rgb(0, 255, 0); -} - -void watch_set_led_yellow(void) { - watch_set_led_color_rgb(255, 255, 0); -} - -void watch_set_led_off(void) { - watch_set_led_color_rgb(0, 0, 0); -} diff --git a/watch-library/hardware/watch/watch_tcc.c b/watch-library/hardware/watch/watch_tcc.c new file mode 100644 index 0000000..630f95d --- /dev/null +++ b/watch-library/hardware/watch/watch_tcc.c @@ -0,0 +1,192 @@ +/* + * 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_tcc.h" +#include "delay.h" +#include "tcc.h" + +void _watch_enable_tcc(void); +void _watch_disable_tcc(void); + +bool watch_is_buzzer_or_led_enabled(void){ + return tcc_is_enabled(0); +} + +inline void watch_enable_buzzer(void) { + if (!tcc_is_enabled(0)) { + _watch_enable_tcc(); + } +} + +inline void watch_set_buzzer_period(uint32_t period) { + tcc_set_period(0, period, true); + tcc_set_cc(0, (WATCH_BUZZER_TCC_CHANNEL) % 4, period / 2, true); +} + +void watch_disable_buzzer(void) { + _watch_disable_tcc(); +} + +inline void watch_set_buzzer_on(void) { + HAL_GPIO_BUZZER_out(); + HAL_GPIO_BUZZER_pmuxen(HAL_GPIO_PMUX_TCC_ALT); +} + +inline void watch_set_buzzer_off(void) { + HAL_GPIO_BUZZER_pmuxdis(); + HAL_GPIO_BUZZER_off(); +} + +void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms) { + if (note == BUZZER_NOTE_REST) { + watch_set_buzzer_off(); + } else { + watch_set_buzzer_period(NotePeriods[note]); + watch_set_buzzer_on(); + } + delay_ms(duration_ms); + watch_set_buzzer_off(); +} + +void _watch_enable_tcc(void) { + // set up the TCC with a 1 MHz clock, but there's a trick: + if (USB->DEVICE.CTRLA.bit.ENABLE) { + // if USB is enabled, we are running an 8 MHz clock, so we divide by 8. + tcc_init(0, GENERIC_CLOCK_0, TCC_PRESCALER_DIV8); + } else { + // otherwise it's 4 Mhz and we divide by 4. + tcc_init(0, GENERIC_CLOCK_0, TCC_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[0] CC[2] and CC[3] can be set to any value from 0 (off) to PER (fully on). + tcc_set_wavegen(0, TCC_WAVEGEN_NORMAL_PWM); +#ifdef WATCH_INVERT_LED_POLARITY + // invert all channels, we'll flip the buzzer back in just a moment. + // this is easier than writing a maze of #ifdefs. + tcc_set_channel_polarity(0, 4, TCC_CHANNEL_POLARITY_INVERTED); + tcc_set_channel_polarity(0, 5, TCC_CHANNEL_POLARITY_INVERTED); + tcc_set_channel_polarity(0, 6, TCC_CHANNEL_POLARITY_INVERTED); + tcc_set_channel_polarity(0, 7, TCC_CHANNEL_POLARITY_INVERTED); +#endif // WATCH_INVERT_LED_POLARITY + tcc_set_channel_polarity(0, WATCH_BUZZER_TCC_CHANNEL, TCC_CHANNEL_POLARITY_NORMAL); + + // Set the period to 1 kHz to start. + tcc_set_period(0, 1000, false); + + // Set the duty cycle of all pins to 0: LED's off, buzzer not buzzing. + tcc_set_cc(0, (WATCH_BUZZER_TCC_CHANNEL) % 4, 0, false); + tcc_set_cc(0, (WATCH_RED_TCC_CHANNEL) % 4, 0, false); +#ifdef WATCH_GREEN_TCC_CHANNEL + tcc_set_cc(0, (WATCH_GREEN_TCC_CHANNEL) % 4, 0, false); +#endif +#ifdef WATCH_BLUE_TCC_CHANNEL + tcc_set_cc(0, (WATCH_BLUE_TCC_CHANNEL) % 4, 0, false); +#endif + + // enable LED PWM pins (the LED driver assumes if the TCC is on, the pins are enabled) + HAL_GPIO_RED_pmuxen(HAL_GPIO_PMUX_TCC_ALT); + HAL_GPIO_RED_out(); +#ifdef WATCH_GREEN_TCC_CHANNEL + HAL_GPIO_GREEN_pmuxen(HAL_GPIO_PMUX_TCC_ALT); + HAL_GPIO_GREEN_out(); +#endif +#ifdef WATCH_BLUE_TCC_CHANNEL + HAL_GPIO_BLUE_pmuxen(HAL_GPIO_PMUX_TCC_ALT); + HAL_GPIO_BLUE_out(); +#endif + + // Enable the TCC + tcc_enable(0); +} + +void _watch_disable_tcc(void) { + // disable all PWM pins + HAL_GPIO_BUZZER_pmuxdis(); + HAL_GPIO_BUZZER_off(); + HAL_GPIO_RED_pmuxdis(); + HAL_GPIO_RED_off(); +#ifdef WATCH_GREEN_TCC_CHANNEL + HAL_GPIO_GREEN_pmuxdis(); + HAL_GPIO_GREEN_off(); +#endif +#ifdef WATCH_BLUE_TCC_CHANNEL + HAL_GPIO_BLUE_pmuxdis(); + HAL_GPIO_BLUE_off(); +#endif + tcc_disable(0); +} + +void watch_enable_leds(void) { + if (!tcc_is_enabled(0)) { + _watch_enable_tcc(); + } +} + +void watch_disable_leds(void) { + _watch_disable_tcc(); +} + +void watch_set_led_color(uint8_t red, uint8_t green) { +#ifdef WATCH_BLUE_TCC_CHANNEL + watch_set_led_color_rgb(red, green, 0); +#else + watch_set_led_color_rgb(red, green, green); +#endif +} + +void watch_set_led_color_rgb(uint8_t red, uint8_t green, uint8_t blue) { + if (tcc_is_enabled(0)) { + uint32_t period = tcc_get_period(0); + tcc_set_cc(0, (WATCH_RED_TCC_CHANNEL) % 4, ((period * (uint32_t)red * 1000ull) / 255000ull), true); +#ifdef WATCH_GREEN_TCC_CHANNEL + tcc_set_cc(0, (WATCH_GREEN_TCC_CHANNEL) % 4, ((period * (uint32_t)green * 1000ull) / 255000ull), true); +#else + (void) green; // silence warning +#endif +#ifdef WATCH_BLUE_TCC_CHANNEL + tcc_set_cc(0, (WATCH_BLUE_TCC_CHANNEL) % 4, ((period * (uint32_t)blue * 1000ull) / 255000ull), true); +#else + (void) blue; // silence warning +#endif + } +} + +void watch_set_led_red(void) { + watch_set_led_color_rgb(255, 0, 0); +} + +void watch_set_led_green(void) { + watch_set_led_color_rgb(0, 255, 0); +} + +void watch_set_led_yellow(void) { + watch_set_led_color_rgb(255, 255, 0); +} + +void watch_set_led_off(void) { + watch_set_led_color_rgb(0, 0, 0); +} diff --git a/watch-library/shared/watch/watch.h b/watch-library/shared/watch/watch.h index d2bdeba..10d9c47 100644 --- a/watch-library/shared/watch/watch.h +++ b/watch-library/shared/watch/watch.h @@ -44,8 +44,8 @@ - @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 tcc - This section covers functions related to the LED and piezo buzzer, which are controlled by the + SAM L22's Timer Counter for Control, or TCC peripheral. - @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 five analog-capable pins on the 9-pin connector. - @ref gpio - This section covers functions related to general-purpose input and output signals. @@ -60,8 +60,7 @@ // #include "watch_rtc.h" // #include "watch_slcd.h" // #include "watch_extint.h" -#include "watch_led.h" -#include "watch_buzzer.h" +#include "watch_tcc.h" // #include "watch_adc.h" #include "watch_gpio.h" // #include "watch_i2c.h" diff --git a/watch-library/shared/watch/watch_buzzer.h b/watch-library/shared/watch/watch_buzzer.h deleted file mode 100644 index 89dc395..0000000 --- a/watch-library/shared/watch/watch_buzzer.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020 Joey Castillo - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef _WATCH_BUZZER_H_INCLUDED -#define _WATCH_BUZZER_H_INCLUDED -////< @file watch_buzzer.h - -#include "watch.h" - -/** @brief Returns true if either the buzzer or the LED driver is enabled. - * @details Both the buzzer and the LED use the TCC peripheral to drive their behavior. This function returns true if that - * peripheral is enabled. You can use this function to determine whether you need to call the watch_disable_leds or - * or watch_enable_buzzer functions before using these peripherals. - */ -bool watch_is_buzzer_or_led_enabled(void); - -/** @addtogroup tcc Buzzer and LED Control (via the TCC peripheral) - * @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(void); - -/** @brief Sets the period of the buzzer. - * @param period The period of a single cycle for the TCC peripheral. You can determine the period for - * a desired frequency with the following formula: period = 1000000 / freq - */ -void watch_set_buzzer_period(uint32_t period); - -/** @brief Disables the TCC peripheral that drives the buzzer. - * @note If you are using PWM to set custom LED colors, this method will also disable the LED PWM driver, - * since the buzzer and LED both make use of the same peripheral to drive their PWM behavior. - */ -void watch_disable_buzzer(void); - -/** @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(void); - -/** @brief Turns the buzzer output off. - */ -void watch_set_buzzer_off(void); - -/// @brief 87 notes for use with watch_buzzer_play_note -typedef enum BuzzerNote { - BUZZER_NOTE_A1, ///< 55.00 Hz - BUZZER_NOTE_A1SHARP_B1FLAT, ///< 58.27 Hz - BUZZER_NOTE_B1, ///< 61.74 Hz - BUZZER_NOTE_C2, ///< 65.41 Hz - BUZZER_NOTE_C2SHARP_D2FLAT, ///< 69.30 Hz - BUZZER_NOTE_D2, ///< 73.42 Hz - BUZZER_NOTE_D2SHARP_E2FLAT, ///< 77.78 Hz - BUZZER_NOTE_E2, ///< 82.41 Hz - BUZZER_NOTE_F2, ///< 87.31 Hz - BUZZER_NOTE_F2SHARP_G2FLAT, ///< 92.50 Hz - BUZZER_NOTE_G2, ///< 98.00 Hz - BUZZER_NOTE_G2SHARP_A2FLAT, ///< 103.83 Hz - BUZZER_NOTE_A2, ///< 110.00 Hz - BUZZER_NOTE_A2SHARP_B2FLAT, ///< 116.54 Hz - BUZZER_NOTE_B2, ///< 123.47 Hz - BUZZER_NOTE_C3, ///< 130.81 Hz - BUZZER_NOTE_C3SHARP_D3FLAT, ///< 138.59 Hz - BUZZER_NOTE_D3, ///< 146.83 Hz - BUZZER_NOTE_D3SHARP_E3FLAT, ///< 155.56 Hz - BUZZER_NOTE_E3, ///< 164.81 Hz - BUZZER_NOTE_F3, ///< 174.61 Hz - BUZZER_NOTE_F3SHARP_G3FLAT, ///< 185.00 Hz - BUZZER_NOTE_G3, ///< 196.00 Hz - BUZZER_NOTE_G3SHARP_A3FLAT, ///< 207.65 Hz - BUZZER_NOTE_A3, ///< 220.00 Hz - BUZZER_NOTE_A3SHARP_B3FLAT, ///< 233.08 Hz - BUZZER_NOTE_B3, ///< 246.94 Hz - BUZZER_NOTE_C4, ///< 261.63 Hz - BUZZER_NOTE_C4SHARP_D4FLAT, ///< 277.18 Hz - BUZZER_NOTE_D4, ///< 293.66 Hz - BUZZER_NOTE_D4SHARP_E4FLAT, ///< 311.13 Hz - BUZZER_NOTE_E4, ///< 329.63 Hz - BUZZER_NOTE_F4, ///< 349.23 Hz - BUZZER_NOTE_F4SHARP_G4FLAT, ///< 369.99 Hz - BUZZER_NOTE_G4, ///< 392.00 Hz - BUZZER_NOTE_G4SHARP_A4FLAT, ///< 415.30 Hz - BUZZER_NOTE_A4, ///< 440.00 Hz - BUZZER_NOTE_A4SHARP_B4FLAT, ///< 466.16 Hz - BUZZER_NOTE_B4, ///< 493.88 Hz - BUZZER_NOTE_C5, ///< 523.25 Hz - BUZZER_NOTE_C5SHARP_D5FLAT, ///< 554.37 Hz - BUZZER_NOTE_D5, ///< 587.33 Hz - BUZZER_NOTE_D5SHARP_E5FLAT, ///< 622.25 Hz - BUZZER_NOTE_E5, ///< 659.25 Hz - BUZZER_NOTE_F5, ///< 698.46 Hz - BUZZER_NOTE_F5SHARP_G5FLAT, ///< 739.99 Hz - BUZZER_NOTE_G5, ///< 783.99 Hz - BUZZER_NOTE_G5SHARP_A5FLAT, ///< 830.61 Hz - BUZZER_NOTE_A5, ///< 880.00 Hz - BUZZER_NOTE_A5SHARP_B5FLAT, ///< 932.33 Hz - BUZZER_NOTE_B5, ///< 987.77 Hz - BUZZER_NOTE_C6, ///< 1046.50 Hz - BUZZER_NOTE_C6SHARP_D6FLAT, ///< 1108.73 Hz - BUZZER_NOTE_D6, ///< 1174.66 Hz - BUZZER_NOTE_D6SHARP_E6FLAT, ///< 1244.51 Hz - BUZZER_NOTE_E6, ///< 1318.51 Hz - BUZZER_NOTE_F6, ///< 1396.91 Hz - BUZZER_NOTE_F6SHARP_G6FLAT, ///< 1479.98 Hz - BUZZER_NOTE_G6, ///< 1567.98 Hz - BUZZER_NOTE_G6SHARP_A6FLAT, ///< 1661.22 Hz - BUZZER_NOTE_A6, ///< 1760.00 Hz - BUZZER_NOTE_A6SHARP_B6FLAT, ///< 1864.66 Hz - BUZZER_NOTE_B6, ///< 1975.53 Hz - BUZZER_NOTE_C7, ///< 2093.00 Hz - BUZZER_NOTE_C7SHARP_D7FLAT, ///< 2217.46 Hz - BUZZER_NOTE_D7, ///< 2349.32 Hz - BUZZER_NOTE_D7SHARP_E7FLAT, ///< 2489.02 Hz - BUZZER_NOTE_E7, ///< 2637.02 Hz - BUZZER_NOTE_F7, ///< 2793.83 Hz - BUZZER_NOTE_F7SHARP_G7FLAT, ///< 2959.96 Hz - BUZZER_NOTE_G7, ///< 3135.96 Hz - BUZZER_NOTE_G7SHARP_A7FLAT, ///< 3322.44 Hz - BUZZER_NOTE_A7, ///< 3520.00 Hz - BUZZER_NOTE_A7SHARP_B7FLAT, ///< 3729.31 Hz - BUZZER_NOTE_B7, ///< 3951.07 Hz - BUZZER_NOTE_C8, ///< 4186.01 Hz - BUZZER_NOTE_C8SHARP_D8FLAT, ///< 4434.92 Hz - BUZZER_NOTE_D8, ///< 4698.63 Hz - BUZZER_NOTE_D8SHARP_E8FLAT, ///< 4978.03 Hz - BUZZER_NOTE_E8, ///< 5274.04 Hz - BUZZER_NOTE_F8, ///< 5587.65 Hz - BUZZER_NOTE_F8SHARP_G8FLAT, ///< 5919.91 Hz - BUZZER_NOTE_G8, ///< 6271.93 Hz - BUZZER_NOTE_G8SHARP_A8FLAT, ///< 6644.88 Hz - BUZZER_NOTE_A8, ///< 7040.00 Hz - BUZZER_NOTE_A8SHARP_B8FLAT, ///< 7458.62 Hz - BUZZER_NOTE_B8, ///< 7902.13 Hz - BUZZER_NOTE_REST ///< no sound -} BuzzerNote; - -/** @brief Plays the given note for a set duration. - * @param note The note you wish to play, or BUZZER_NOTE_REST to disable output for the given duration. - * @param duration_ms The duration of the note. - * @note Note that this will block your UI for the duration of the note's play time, and it will - * after this call, the buzzer period will be set to the period of this note. - */ -void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms); - -/// @brief An array of periods for all the notes on a piano, corresponding to the names in BuzzerNote. -extern const uint16_t NotePeriods[108]; - -/** @brief Plays the given sequence of notes in a non-blocking way. - * @param note_sequence A pointer to the sequence of buzzer note & duration tuples, ending with a zero. A simple - * RLE logic is implemented: a negative number instead of a buzzer note means that the sequence - * is rewound by the given number of notes. The byte following a negative number determines the number - * of loops. I.e. if you want to repeat the last three notes of the sequence one time, you should provide - * the tuple -3, 1. The repeated notes must not contain any other repeat markers, or you will end up with - * an eternal loop. - * @param callback_on_end A pointer to a callback function to be invoked when the sequence has finished playing. - * @note This function plays the sequence asynchronously, so the UI will not be blocked. - * Hint: It is not possible to play the lowest note BUZZER_NOTE_A1 (55.00 Hz). The note is represented by a - * zero byte, which is used here as the end-of-sequence marker. But hey, a frequency that low cannot be - * played properly by the watch's buzzer, anyway. - */ -void watch_buzzer_play_sequence(int8_t *note_sequence, void (*callback_on_end)(void)); - -/** @brief Aborts a playing sequence. - */ -void watch_buzzer_abort_sequence(void); - -#ifndef __EMSCRIPTEN__ -void TC3_Handler(void); -#endif - -/// @} -#endif diff --git a/watch-library/shared/watch/watch_led.h b/watch-library/shared/watch/watch_led.h deleted file mode 100644 index 134803a..0000000 --- a/watch-library/shared/watch/watch_led.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020 Joey Castillo - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef _WATCH_LED_H_INCLUDED -#define _WATCH_LED_H_INCLUDED -////< @file watch_led.h - -#include "watch.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. - * @note Some watches use a red/blue LED instead of a red/green LED. You will be able to determine this - * easily when you double tap the reset button: if the pulsing bootloader LED is red, you have a - * red/green edition; if it is blue, you have a red/blue edition. For red/blue watches, build your - * project with the command `make COLOR=BLUE`, and the watch library will automatically swap the pins - * so that watch_set_led_red sets the red LED, and watch_set_led_green sets the blue one. - */ -/// @{ -/** @brief Enables the bi-color LED. - * @note The TCC peripheral that drives the LEDs does not run in STANDBY mode — but the outputs do! This - * means that if you set either red, green or both LEDs to full power, they will shine even when - * your app is asleep. If, however, you set a custom color using watch_set_led_color, the color will - * not display correctly in STANDBY mode. You will need to keep your app running while the LED is on. - */ -void watch_enable_leds(void); - -/** @brief Disables the LEDs. - * @note This method will also disable the buzzer, since the buzzer and LED both make use of the same - * peripheral to drive their PWM behavior. - */ -void watch_disable_leds(void); - -/** @brief Sets the LED to a custom color by modulating each output's duty cycle. - * @param red The red value from 0-255. - * @param green The green value from 0-255. If your watch has a red/blue LED, this will be the blue value. - * @note If you are displaying a custom color, you will need to prevent your app from going to sleep - * while the LED is on; otherwise, the color will not display correctly. You can do this by - * returning false in your app_loop method. - */ -void watch_set_led_color(uint8_t red, uint8_t green); - -/** @brief On boards with an RGB LED, sets the LED to a custom color by modulating each output's duty cycle. - * @param red The red value from 0-255. - * @param green The green value from 0-255. - * @param blue The blue value from 0-255. - * @note If you are displaying a custom color, you will need to prevent your app from going to sleep - * while the LED is on; otherwise, the color will not display correctly. You can do this by - * returning false in your app_loop method. - */ -void watch_set_led_color_rgb(uint8_t red, uint8_t green, uint8_t blue); - -/** @brief Sets the red LED to full brightness, and turns the green LED off. - * @details 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(void); - -/** @brief Sets the green LED to full brightness, and turns the red LED off. - * @details Of the two LED's in the RG bi-color LED, the green LED is the more power-efficient one (~0.44 mA). - * @note If your watch has a red/blue LED, this method will set the LED to blue. - */ -void watch_set_led_green(void); - -/** @brief Sets both red and green LEDs to full brightness. - * @details 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. - * @note If your watch has a red/blue LED, this method will set the LED to pink. - */ -void watch_set_led_yellow(void); - -/** @brief Turns both the red and the green LEDs off. */ -void watch_set_led_off(void); - -/// @} -#endif diff --git a/watch-library/shared/watch/watch_tcc.h b/watch-library/shared/watch/watch_tcc.h new file mode 100644 index 0000000..0443b42 --- /dev/null +++ b/watch-library/shared/watch/watch_tcc.h @@ -0,0 +1,261 @@ +/* + * 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. + */ +#ifndef _WATCH_BUZZER_H_INCLUDED +#define _WATCH_BUZZER_H_INCLUDED +////< @file watch_buzzer.h + +#include "watch.h" + +/** @brief Returns true if either the buzzer or the LED driver is enabled. + * @details Both the buzzer and the LED use the TCC peripheral to drive their behavior. This function returns true if that + * peripheral is enabled. You can use this function to determine whether you need to call the watch_disable_leds or + * or watch_enable_buzzer functions before using these peripherals. + */ +bool watch_is_buzzer_or_led_enabled(void); + +/** @addtogroup tcc Buzzer and LED Control (via the TCC peripheral) + * @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(void); + +/** @brief Sets the period of the buzzer. + * @param period The period of a single cycle for the TCC peripheral. You can determine the period for + * a desired frequency with the following formula: period = 1000000 / freq + */ +void watch_set_buzzer_period(uint32_t period); + +/** @brief Disables the TCC peripheral that drives the buzzer. + * @note If you are using PWM to set custom LED colors, this method will also disable the LED PWM driver, + * since the buzzer and LED both make use of the same peripheral to drive their PWM behavior. + */ +void watch_disable_buzzer(void); + +/** @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(void); + +/** @brief Turns the buzzer output off. + */ +void watch_set_buzzer_off(void); + +/// @brief 87 notes for use with watch_buzzer_play_note +typedef enum BuzzerNote { + BUZZER_NOTE_A1, ///< 55.00 Hz + BUZZER_NOTE_A1SHARP_B1FLAT, ///< 58.27 Hz + BUZZER_NOTE_B1, ///< 61.74 Hz + BUZZER_NOTE_C2, ///< 65.41 Hz + BUZZER_NOTE_C2SHARP_D2FLAT, ///< 69.30 Hz + BUZZER_NOTE_D2, ///< 73.42 Hz + BUZZER_NOTE_D2SHARP_E2FLAT, ///< 77.78 Hz + BUZZER_NOTE_E2, ///< 82.41 Hz + BUZZER_NOTE_F2, ///< 87.31 Hz + BUZZER_NOTE_F2SHARP_G2FLAT, ///< 92.50 Hz + BUZZER_NOTE_G2, ///< 98.00 Hz + BUZZER_NOTE_G2SHARP_A2FLAT, ///< 103.83 Hz + BUZZER_NOTE_A2, ///< 110.00 Hz + BUZZER_NOTE_A2SHARP_B2FLAT, ///< 116.54 Hz + BUZZER_NOTE_B2, ///< 123.47 Hz + BUZZER_NOTE_C3, ///< 130.81 Hz + BUZZER_NOTE_C3SHARP_D3FLAT, ///< 138.59 Hz + BUZZER_NOTE_D3, ///< 146.83 Hz + BUZZER_NOTE_D3SHARP_E3FLAT, ///< 155.56 Hz + BUZZER_NOTE_E3, ///< 164.81 Hz + BUZZER_NOTE_F3, ///< 174.61 Hz + BUZZER_NOTE_F3SHARP_G3FLAT, ///< 185.00 Hz + BUZZER_NOTE_G3, ///< 196.00 Hz + BUZZER_NOTE_G3SHARP_A3FLAT, ///< 207.65 Hz + BUZZER_NOTE_A3, ///< 220.00 Hz + BUZZER_NOTE_A3SHARP_B3FLAT, ///< 233.08 Hz + BUZZER_NOTE_B3, ///< 246.94 Hz + BUZZER_NOTE_C4, ///< 261.63 Hz + BUZZER_NOTE_C4SHARP_D4FLAT, ///< 277.18 Hz + BUZZER_NOTE_D4, ///< 293.66 Hz + BUZZER_NOTE_D4SHARP_E4FLAT, ///< 311.13 Hz + BUZZER_NOTE_E4, ///< 329.63 Hz + BUZZER_NOTE_F4, ///< 349.23 Hz + BUZZER_NOTE_F4SHARP_G4FLAT, ///< 369.99 Hz + BUZZER_NOTE_G4, ///< 392.00 Hz + BUZZER_NOTE_G4SHARP_A4FLAT, ///< 415.30 Hz + BUZZER_NOTE_A4, ///< 440.00 Hz + BUZZER_NOTE_A4SHARP_B4FLAT, ///< 466.16 Hz + BUZZER_NOTE_B4, ///< 493.88 Hz + BUZZER_NOTE_C5, ///< 523.25 Hz + BUZZER_NOTE_C5SHARP_D5FLAT, ///< 554.37 Hz + BUZZER_NOTE_D5, ///< 587.33 Hz + BUZZER_NOTE_D5SHARP_E5FLAT, ///< 622.25 Hz + BUZZER_NOTE_E5, ///< 659.25 Hz + BUZZER_NOTE_F5, ///< 698.46 Hz + BUZZER_NOTE_F5SHARP_G5FLAT, ///< 739.99 Hz + BUZZER_NOTE_G5, ///< 783.99 Hz + BUZZER_NOTE_G5SHARP_A5FLAT, ///< 830.61 Hz + BUZZER_NOTE_A5, ///< 880.00 Hz + BUZZER_NOTE_A5SHARP_B5FLAT, ///< 932.33 Hz + BUZZER_NOTE_B5, ///< 987.77 Hz + BUZZER_NOTE_C6, ///< 1046.50 Hz + BUZZER_NOTE_C6SHARP_D6FLAT, ///< 1108.73 Hz + BUZZER_NOTE_D6, ///< 1174.66 Hz + BUZZER_NOTE_D6SHARP_E6FLAT, ///< 1244.51 Hz + BUZZER_NOTE_E6, ///< 1318.51 Hz + BUZZER_NOTE_F6, ///< 1396.91 Hz + BUZZER_NOTE_F6SHARP_G6FLAT, ///< 1479.98 Hz + BUZZER_NOTE_G6, ///< 1567.98 Hz + BUZZER_NOTE_G6SHARP_A6FLAT, ///< 1661.22 Hz + BUZZER_NOTE_A6, ///< 1760.00 Hz + BUZZER_NOTE_A6SHARP_B6FLAT, ///< 1864.66 Hz + BUZZER_NOTE_B6, ///< 1975.53 Hz + BUZZER_NOTE_C7, ///< 2093.00 Hz + BUZZER_NOTE_C7SHARP_D7FLAT, ///< 2217.46 Hz + BUZZER_NOTE_D7, ///< 2349.32 Hz + BUZZER_NOTE_D7SHARP_E7FLAT, ///< 2489.02 Hz + BUZZER_NOTE_E7, ///< 2637.02 Hz + BUZZER_NOTE_F7, ///< 2793.83 Hz + BUZZER_NOTE_F7SHARP_G7FLAT, ///< 2959.96 Hz + BUZZER_NOTE_G7, ///< 3135.96 Hz + BUZZER_NOTE_G7SHARP_A7FLAT, ///< 3322.44 Hz + BUZZER_NOTE_A7, ///< 3520.00 Hz + BUZZER_NOTE_A7SHARP_B7FLAT, ///< 3729.31 Hz + BUZZER_NOTE_B7, ///< 3951.07 Hz + BUZZER_NOTE_C8, ///< 4186.01 Hz + BUZZER_NOTE_C8SHARP_D8FLAT, ///< 4434.92 Hz + BUZZER_NOTE_D8, ///< 4698.63 Hz + BUZZER_NOTE_D8SHARP_E8FLAT, ///< 4978.03 Hz + BUZZER_NOTE_E8, ///< 5274.04 Hz + BUZZER_NOTE_F8, ///< 5587.65 Hz + BUZZER_NOTE_F8SHARP_G8FLAT, ///< 5919.91 Hz + BUZZER_NOTE_G8, ///< 6271.93 Hz + BUZZER_NOTE_G8SHARP_A8FLAT, ///< 6644.88 Hz + BUZZER_NOTE_A8, ///< 7040.00 Hz + BUZZER_NOTE_A8SHARP_B8FLAT, ///< 7458.62 Hz + BUZZER_NOTE_B8, ///< 7902.13 Hz + BUZZER_NOTE_REST ///< no sound +} BuzzerNote; + +/** @brief Plays the given note for a set duration. + * @param note The note you wish to play, or BUZZER_NOTE_REST to disable output for the given duration. + * @param duration_ms The duration of the note. + * @note Note that this will block your UI for the duration of the note's play time, and it will + * after this call, the buzzer period will be set to the period of this note. + */ +void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms); + +/// @brief An array of periods for all the notes on a piano, corresponding to the names in BuzzerNote. +extern const uint16_t NotePeriods[108]; + +/** @brief Plays the given sequence of notes in a non-blocking way. + * @param note_sequence A pointer to the sequence of buzzer note & duration tuples, ending with a zero. A simple + * RLE logic is implemented: a negative number instead of a buzzer note means that the sequence + * is rewound by the given number of notes. The byte following a negative number determines the number + * of loops. I.e. if you want to repeat the last three notes of the sequence one time, you should provide + * the tuple -3, 1. The repeated notes must not contain any other repeat markers, or you will end up with + * an eternal loop. + * @param callback_on_end A pointer to a callback function to be invoked when the sequence has finished playing. + * @note This function plays the sequence asynchronously, so the UI will not be blocked. + * Hint: It is not possible to play the lowest note BUZZER_NOTE_A1 (55.00 Hz). The note is represented by a + * zero byte, which is used here as the end-of-sequence marker. But hey, a frequency that low cannot be + * played properly by the watch's buzzer, anyway. + */ +void watch_buzzer_play_sequence(int8_t *note_sequence, void (*callback_on_end)(void)); + +/** @brief Aborts a playing sequence. + */ +void watch_buzzer_abort_sequence(void); + +#ifndef __EMSCRIPTEN__ +void TC3_Handler(void); +#endif + +/** @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. + * @note Some watches use a red/blue LED instead of a red/green LED. You will be able to determine this + * easily when you double tap the reset button: if the pulsing bootloader LED is red, you have a + * red/green edition; if it is blue, you have a red/blue edition. For red/blue watches, build your + * project with the command `make COLOR=BLUE`, and the watch library will automatically swap the pins + * so that watch_set_led_red sets the red LED, and watch_set_led_green sets the blue one. + */ +/// @{ +/** @brief Enables the bi-color LED. + * @note The TCC peripheral that drives the LEDs does not run in STANDBY mode — but the outputs do! This + * means that if you set either red, green or both LEDs to full power, they will shine even when + * your app is asleep. If, however, you set a custom color using watch_set_led_color, the color will + * not display correctly in STANDBY mode. You will need to keep your app running while the LED is on. + */ +void watch_enable_leds(void); + +/** @brief Disables the LEDs. + * @note This method will also disable the buzzer, since the buzzer and LED both make use of the same + * peripheral to drive their PWM behavior. + */ +void watch_disable_leds(void); + +/** @brief Sets the LED to a custom color by modulating each output's duty cycle. + * @param red The red value from 0-255. + * @param green The green value from 0-255. If your watch has a red/blue LED, this will be the blue value. + * @note If you are displaying a custom color, you will need to prevent your app from going to sleep + * while the LED is on; otherwise, the color will not display correctly. You can do this by + * returning false in your app_loop method. + */ +void watch_set_led_color(uint8_t red, uint8_t green); + +/** @brief On boards with an RGB LED, sets the LED to a custom color by modulating each output's duty cycle. + * @param red The red value from 0-255. + * @param green The green value from 0-255. + * @param blue The blue value from 0-255. + * @note If you are displaying a custom color, you will need to prevent your app from going to sleep + * while the LED is on; otherwise, the color will not display correctly. You can do this by + * returning false in your app_loop method. + */ +void watch_set_led_color_rgb(uint8_t red, uint8_t green, uint8_t blue); + +/** @brief Sets the red LED to full brightness, and turns the green LED off. + * @details 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(void); + +/** @brief Sets the green LED to full brightness, and turns the red LED off. + * @details Of the two LED's in the RG bi-color LED, the green LED is the more power-efficient one (~0.44 mA). + * @note If your watch has a red/blue LED, this method will set the LED to blue. + */ +void watch_set_led_green(void); + +/** @brief Sets both red and green LEDs to full brightness. + * @details 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. + * @note If your watch has a red/blue LED, this method will set the LED to pink. + */ +void watch_set_led_yellow(void); + +/** @brief Turns both the red and the green LEDs off. */ +void watch_set_led_off(void); + +/// @} +#endif diff --git a/watch-library/simulator/watch/watch_buzzer.c b/watch-library/simulator/watch/watch_buzzer.c deleted file mode 100644 index c5d9d7e..0000000 --- a/watch-library/simulator/watch/watch_buzzer.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020 Joey Castillo - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "watch_buzzer.h" -#include "watch_private_buzzer.h" -#include "watch_main_loop.h" - -#include -#include - -static bool buzzer_enabled = false; -static uint32_t buzzer_period; - -void cb_watch_buzzer_seq(void *userData); - -static uint16_t _seq_position; -static int8_t _tone_ticks, _repeat_counter; -static long _em_interval_id = 0; -static int8_t *_sequence; -static void (*_cb_finished)(void); - -void _watch_enable_tcc(void) {} - -static inline void _em_interval_stop() { - emscripten_clear_interval(_em_interval_id); - _em_interval_id = 0; -} - -void watch_buzzer_play_sequence(int8_t *note_sequence, void (*callback_on_end)(void)) { - if (_em_interval_id) _em_interval_stop(); - watch_set_buzzer_off(); - _sequence = note_sequence; - _cb_finished = callback_on_end; - _seq_position = 0; - _tone_ticks = 0; - _repeat_counter = -1; - // prepare buzzer - watch_enable_buzzer(); - // initiate 64 hz callback - _em_interval_id = emscripten_set_interval(cb_watch_buzzer_seq, (double)(1000/64), (void *)NULL); -} - -void cb_watch_buzzer_seq(void *userData) { - // callback for reading the note sequence - (void) userData; - if (_tone_ticks == 0) { - if (_sequence[_seq_position] < 0 && _sequence[_seq_position + 1]) { - // repeat indicator found - if (_repeat_counter == -1) { - // first encounter: load repeat counter - _repeat_counter = _sequence[_seq_position + 1]; - } else _repeat_counter--; - if (_repeat_counter > 0) - // rewind - if (_seq_position > _sequence[_seq_position] * -2) - _seq_position += _sequence[_seq_position] * 2; - else - _seq_position = 0; - else { - // continue - _seq_position += 2; - _repeat_counter = -1; - } - } - if (_sequence[_seq_position] && _sequence[_seq_position + 1]) { - // read note - BuzzerNote note = _sequence[_seq_position]; - if (note == BUZZER_NOTE_REST) { - watch_set_buzzer_off(); - } else { - watch_set_buzzer_period(NotePeriods[note]); - watch_set_buzzer_on(); - } - // set duration ticks and move to next tone - _tone_ticks = _sequence[_seq_position + 1]; - _seq_position += 2; - } else { - // end the sequence - watch_buzzer_abort_sequence(); - if (_cb_finished) _cb_finished(); - } - } else _tone_ticks--; -} - -void watch_buzzer_abort_sequence(void) { - // ends/aborts the sequence - if (_em_interval_id) _em_interval_stop(); - watch_set_buzzer_off(); -} - -void watch_enable_buzzer(void) { - buzzer_enabled = true; - buzzer_period = NotePeriods[BUZZER_NOTE_A4]; - - EM_ASM({ - Module['audioContext'] = new (window.AudioContext || window.webkitAudioContext)(); - }); -} - -void watch_set_buzzer_period(uint32_t period) { - if (!buzzer_enabled) return; - buzzer_period = period; -} - -void watch_disable_buzzer(void) { - buzzer_enabled = false; - buzzer_period = NotePeriods[BUZZER_NOTE_A4]; - - EM_ASM({ - if (Module['audioContext']) { - Module['audioContext'].close(); - Module['audioContext'] = null; - } - }); -} - -void watch_set_buzzer_on(void) { - if (!buzzer_enabled) return; - - EM_ASM({ - const audioContext = Module['audioContext']; - if (!audioContext) return; - - if (!(audioContext._oscillator && audioContext._gain)) { - const oscillator = audioContext.createOscillator(); - const gain = audioContext.createGain(); - oscillator.type = 'triangle'; - oscillator.connect(gain); - gain.connect(audioContext.destination); - oscillator.start(0); - - audioContext._oscillator = oscillator; - audioContext._gain = gain; - } - - audioContext._oscillator.frequency.value = 1e6/$0; - audioContext._gain.gain.value = volumeGain; - }, buzzer_period); -} - -void watch_set_buzzer_off(void) { - if (!buzzer_enabled) return; - - EM_ASM({ - const audioContext = Module['audioContext']; - if (audioContext && audioContext._gain) { - audioContext._gain.gain.value = 0; - } - }); -} - -void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms) { - if (note == BUZZER_NOTE_REST) { - watch_set_buzzer_off(); - } else { - watch_set_buzzer_period(NotePeriods[note]); - watch_set_buzzer_on(); - } - - main_loop_sleep(duration_ms); - watch_set_buzzer_off(); -} diff --git a/watch-library/simulator/watch/watch_led.c b/watch-library/simulator/watch/watch_led.c deleted file mode 100644 index 584611f..0000000 --- a/watch-library/simulator/watch/watch_led.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020 Joey Castillo - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "watch_led.h" - -#include - -void watch_enable_leds(void) {} - -void watch_disable_leds(void) {} - -void watch_set_led_color(uint8_t red, uint8_t green) { - EM_ASM({ - // the watch svg contains an feColorMatrix filter with id ledcolor - // and a green svg gradient that mimics the led being on - // https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feColorMatrix - // this changes the color of the gradient to match the red+green combination - let filter = document.getElementById("ledcolor"); - let color_matrix = filter.children[0].values.baseVal; - color_matrix[1].value = $0 / 255; // red value - color_matrix[6].value = $1 / 255; // green value - document.getElementById('light').style.opacity = Math.min(255, $0 + $1) / 255; - }, red, green); -} - -void watch_set_led_color_rgb(uint8_t red, uint8_t green, uint8_t blue) { - (void) blue; - watch_set_led_color(red, green); -} - -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); -} diff --git a/watch-library/simulator/watch/watch_tcc.c b/watch-library/simulator/watch/watch_tcc.c new file mode 100644 index 0000000..77c0640 --- /dev/null +++ b/watch-library/simulator/watch/watch_tcc.c @@ -0,0 +1,222 @@ +/* + * 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" +#include "watch_private_buzzer.h" +#include "watch_main_loop.h" + +#include +#include + +static bool buzzer_enabled = false; +static uint32_t buzzer_period; + +void cb_watch_buzzer_seq(void *userData); + +static uint16_t _seq_position; +static int8_t _tone_ticks, _repeat_counter; +static long _em_interval_id = 0; +static int8_t *_sequence; +static void (*_cb_finished)(void); + +void _watch_enable_tcc(void) {} + +static inline void _em_interval_stop() { + emscripten_clear_interval(_em_interval_id); + _em_interval_id = 0; +} + +void watch_buzzer_play_sequence(int8_t *note_sequence, void (*callback_on_end)(void)) { + if (_em_interval_id) _em_interval_stop(); + watch_set_buzzer_off(); + _sequence = note_sequence; + _cb_finished = callback_on_end; + _seq_position = 0; + _tone_ticks = 0; + _repeat_counter = -1; + // prepare buzzer + watch_enable_buzzer(); + // initiate 64 hz callback + _em_interval_id = emscripten_set_interval(cb_watch_buzzer_seq, (double)(1000/64), (void *)NULL); +} + +void cb_watch_buzzer_seq(void *userData) { + // callback for reading the note sequence + (void) userData; + if (_tone_ticks == 0) { + if (_sequence[_seq_position] < 0 && _sequence[_seq_position + 1]) { + // repeat indicator found + if (_repeat_counter == -1) { + // first encounter: load repeat counter + _repeat_counter = _sequence[_seq_position + 1]; + } else _repeat_counter--; + if (_repeat_counter > 0) + // rewind + if (_seq_position > _sequence[_seq_position] * -2) + _seq_position += _sequence[_seq_position] * 2; + else + _seq_position = 0; + else { + // continue + _seq_position += 2; + _repeat_counter = -1; + } + } + if (_sequence[_seq_position] && _sequence[_seq_position + 1]) { + // read note + BuzzerNote note = _sequence[_seq_position]; + if (note == BUZZER_NOTE_REST) { + watch_set_buzzer_off(); + } else { + watch_set_buzzer_period(NotePeriods[note]); + watch_set_buzzer_on(); + } + // set duration ticks and move to next tone + _tone_ticks = _sequence[_seq_position + 1]; + _seq_position += 2; + } else { + // end the sequence + watch_buzzer_abort_sequence(); + if (_cb_finished) _cb_finished(); + } + } else _tone_ticks--; +} + +void watch_buzzer_abort_sequence(void) { + // ends/aborts the sequence + if (_em_interval_id) _em_interval_stop(); + watch_set_buzzer_off(); +} + +void watch_enable_buzzer(void) { + buzzer_enabled = true; + buzzer_period = NotePeriods[BUZZER_NOTE_A4]; + + EM_ASM({ + Module['audioContext'] = new (window.AudioContext || window.webkitAudioContext)(); + }); +} + +void watch_set_buzzer_period(uint32_t period) { + if (!buzzer_enabled) return; + buzzer_period = period; +} + +void watch_disable_buzzer(void) { + buzzer_enabled = false; + buzzer_period = NotePeriods[BUZZER_NOTE_A4]; + + EM_ASM({ + if (Module['audioContext']) { + Module['audioContext'].close(); + Module['audioContext'] = null; + } + }); +} + +void watch_set_buzzer_on(void) { + if (!buzzer_enabled) return; + + EM_ASM({ + const audioContext = Module['audioContext']; + if (!audioContext) return; + + if (!(audioContext._oscillator && audioContext._gain)) { + const oscillator = audioContext.createOscillator(); + const gain = audioContext.createGain(); + oscillator.type = 'triangle'; + oscillator.connect(gain); + gain.connect(audioContext.destination); + oscillator.start(0); + + audioContext._oscillator = oscillator; + audioContext._gain = gain; + } + + audioContext._oscillator.frequency.value = 1e6/$0; + audioContext._gain.gain.value = volumeGain; + }, buzzer_period); +} + +void watch_set_buzzer_off(void) { + if (!buzzer_enabled) return; + + EM_ASM({ + const audioContext = Module['audioContext']; + if (audioContext && audioContext._gain) { + audioContext._gain.gain.value = 0; + } + }); +} + +void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms) { + if (note == BUZZER_NOTE_REST) { + watch_set_buzzer_off(); + } else { + watch_set_buzzer_period(NotePeriods[note]); + watch_set_buzzer_on(); + } + + main_loop_sleep(duration_ms); + watch_set_buzzer_off(); +} + +void watch_enable_leds(void) {} + +void watch_disable_leds(void) {} + +void watch_set_led_color(uint8_t red, uint8_t green) { + EM_ASM({ + // the watch svg contains an feColorMatrix filter with id ledcolor + // and a green svg gradient that mimics the led being on + // https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feColorMatrix + // this changes the color of the gradient to match the red+green combination + let filter = document.getElementById("ledcolor"); + let color_matrix = filter.children[0].values.baseVal; + color_matrix[1].value = $0 / 255; // red value + color_matrix[6].value = $1 / 255; // green value + document.getElementById('light').style.opacity = Math.min(255, $0 + $1) / 255; + }, red, green); +} + +void watch_set_led_color_rgb(uint8_t red, uint8_t green, uint8_t blue) { + (void) blue; + watch_set_led_color(red, green); +} + +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); +}