summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoey Castillo <jose.castillo@gmail.com>2021-08-29 17:40:44 -0400
committerJoey Castillo <jose.castillo@gmail.com>2021-08-29 17:40:44 -0400
commit6353d25d46979efed36c50ec186c8006f266d78a (patch)
tree37725afdd72ffb87f6e2965aaff122457b1f0738
parent7dc4b0fdc60b6240515d6c4143a5310b343dc8a5 (diff)
downloadSensor-Watch-6353d25d46979efed36c50ec186c8006f266d78a.tar.gz
Sensor-Watch-6353d25d46979efed36c50ec186c8006f266d78a.tar.bz2
Sensor-Watch-6353d25d46979efed36c50ec186c8006f266d78a.zip
big PWM refactor: drive both LEDs and buzzer from TCC
-rw-r--r--watch-library/hpl/tcc/hpl_tcc.c7
-rw-r--r--watch-library/hw/driver_init.c31
-rw-r--r--watch-library/watch/watch_buzzer.c22
-rw-r--r--watch-library/watch/watch_buzzer.h10
-rw-r--r--watch-library/watch/watch_led.c46
-rw-r--r--watch-library/watch/watch_led.h9
-rw-r--r--watch-library/watch/watch_private.c65
-rw-r--r--watch-library/watch/watch_private.h6
8 files changed, 120 insertions, 76 deletions
diff --git a/watch-library/hpl/tcc/hpl_tcc.c b/watch-library/hpl/tcc/hpl_tcc.c
index 52fc6f3c..af44aec1 100644
--- a/watch-library/hpl/tcc/hpl_tcc.c
+++ b/watch-library/hpl/tcc/hpl_tcc.c
@@ -331,13 +331,6 @@ static void tcc_pwm_interrupt_handler(struct _pwm_device *device)
}
}
-/**
- * \brief TCC interrupt handler
- */
-void TCC0_Handler(void)
-{
- tcc_pwm_interrupt_handler(_tcc0_dev);
-}
static struct tcc_cfg *_get_tcc_cfg(void *hw)
{
diff --git a/watch-library/hw/driver_init.c b/watch-library/hw/driver_init.c
index 6d910d22..daf3901d 100644
--- a/watch-library/hw/driver_init.c
+++ b/watch-library/hw/driver_init.c
@@ -83,37 +83,6 @@ void delay_driver_init(void) {
delay_init(SysTick);
}
-void PWM_0_PORT_init(void) {
- gpio_set_pin_function(RED, PINMUX_PA20E_TC3_WO0);
- gpio_set_pin_function(GREEN, PINMUX_PA21E_TC3_WO1);
-}
-
-void PWM_0_CLOCK_init(void) {
- hri_mclk_set_APBCMASK_TC3_bit(MCLK);
- hri_gclk_write_PCHCTRL_reg(GCLK, TC3_GCLK_ID, CONF_GCLK_TC3_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos));
-}
-
-void PWM_0_init(void) {
- PWM_0_CLOCK_init();
- PWM_0_PORT_init();
- pwm_init(&PWM_0, TC3, _tc_get_pwm());
-}
-
-void PWM_1_PORT_init(void) {
- gpio_set_pin_function(BUZZER, PINMUX_PA27F_TCC0_WO5);
-}
-
-void PWM_1_CLOCK_init(void) {
- hri_mclk_set_APBCMASK_TCC0_bit(MCLK);
- hri_gclk_write_PCHCTRL_reg(GCLK, TCC0_GCLK_ID, CONF_GCLK_TCC0_SRC | (1 << GCLK_PCHCTRL_CHEN_Pos));
-}
-
-void PWM_1_init(void) {
- PWM_1_CLOCK_init();
- PWM_1_PORT_init();
- pwm_init(&PWM_1, TCC0, _tcc_get_pwm());
-}
-
void SEGMENT_LCD_0_PORT_init(void) {
gpio_set_pin_function(COM0, PINMUX_PB06B_SLCD_LP0);
gpio_set_pin_function(COM1, PINMUX_PB07B_SLCD_LP1);
diff --git a/watch-library/watch/watch_buzzer.c b/watch-library/watch/watch_buzzer.c
index 72771508..c6c89a0e 100644
--- a/watch-library/watch/watch_buzzer.c
+++ b/watch-library/watch/watch_buzzer.c
@@ -23,28 +23,38 @@
*/
inline void watch_enable_buzzer() {
- PWM_1_init();
+ if (!hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
+ _watch_enable_tcc();
+ }
}
-
inline void watch_set_buzzer_period(uint32_t period) {
- pwm_set_parameters(&PWM_1, period, period / 2);
+ hri_tcc_write_PERBUF_reg(TCC0, period);
+}
+
+void watch_disable_buzzer() {
+ _watch_disable_tcc();
}
inline void watch_set_buzzer_on() {
- pwm_enable(&PWM_1);
+ gpio_set_pin_direction(BUZZER, GPIO_DIRECTION_OUT);
+ gpio_set_pin_function(BUZZER, PINMUX_PA27F_TCC0_WO5);
}
inline void watch_set_buzzer_off() {
- pwm_disable(&PWM_1);
+ 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 {
- pwm_set_parameters(&PWM_1, NotePeriods[note], NotePeriods[note] / 2);
+ hri_tcc_write_PERBUF_reg(TCC0, NotePeriods[note]);
+ hri_tcc_write_CCBUF_reg(TCC0, 1, NotePeriods[note] / 2);
watch_set_buzzer_on();
}
delay_ms(duration_ms);
diff --git a/watch-library/watch/watch_buzzer.h b/watch-library/watch/watch_buzzer.h
index 38b40f6e..995e059a 100644
--- a/watch-library/watch/watch_buzzer.h
+++ b/watch-library/watch/watch_buzzer.h
@@ -32,11 +32,17 @@
void watch_enable_buzzer();
/** @brief Sets the period of the buzzer.
- * @param period The period of a single cycle for the PWM peripheral. You can use the following formula to
- * convert a desired frequency to a period for this function: period = 513751 * (freq^−1.0043)
+ * @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();
+
/** @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.
diff --git a/watch-library/watch/watch_led.c b/watch-library/watch/watch_led.c
index 01f59fc4..4f9898d8 100644
--- a/watch-library/watch/watch_led.c
+++ b/watch-library/watch/watch_led.c
@@ -22,45 +22,39 @@
* SOFTWARE.
*/
- bool PWM_0_enabled = false;
void watch_enable_led(bool pwm) {
if (pwm) {
- if (PWM_0_enabled) return;
-
- PWM_0_init();
- pwm_set_parameters(&PWM_0, 10000, 0);
- pwm_enable(&PWM_0);
-
- PWM_0_enabled = true;
+ if (!hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
+ _watch_enable_tcc();
+ }
} else {
watch_enable_digital_output(RED);
watch_enable_digital_output(GREEN);
+ watch_set_led_off();
}
- watch_set_led_off();
}
void watch_disable_led(bool pwm) {
if (pwm) {
- if (!PWM_0_enabled) return;
- pwm_disable(&PWM_0);
- PWM_0_enabled = false;
+ _watch_disable_tcc();
+ } else {
+ watch_disable_digital_output(RED);
+ watch_disable_digital_output(GREEN);
}
-
- watch_disable_digital_output(RED);
- watch_disable_digital_output(GREEN);
}
-void watch_set_led_color(uint16_t red, uint16_t green) {
- if (PWM_0_enabled) {
- TC3->COUNT16.CC[0].reg = red;
- TC3->COUNT16.CC[1].reg = green;
+void watch_set_led_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, 2, ((period * red * 1000ull) / 255000ull));
+ hri_tcc_write_CCBUF_reg(TCC0, 3, ((period * green * 1000ull) / 255000ull));
}
}
void watch_set_led_red() {
- if (PWM_0_enabled) {
- watch_set_led_color(65535, 0);
+ if (hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
+ watch_set_led_color(255, 0);
} else {
watch_set_pin_level(RED, true);
watch_set_pin_level(GREEN, false);
@@ -68,8 +62,8 @@ void watch_set_led_red() {
}
void watch_set_led_green() {
- if (PWM_0_enabled) {
- watch_set_led_color(65535, 0);
+ if (hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
+ watch_set_led_color(0, 255);
} else {
watch_set_pin_level(RED, false);
watch_set_pin_level(GREEN, true);
@@ -77,8 +71,8 @@ void watch_set_led_green() {
}
void watch_set_led_yellow() {
- if (PWM_0_enabled) {
- watch_set_led_color(65535, 65535);
+ if (hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
+ watch_set_led_color(255, 255);
} else {
watch_set_pin_level(RED, true);
watch_set_pin_level(GREEN, true);
@@ -86,7 +80,7 @@ void watch_set_led_yellow() {
}
void watch_set_led_off() {
- if (PWM_0_enabled) {
+ if (hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
watch_set_led_color(0, 0);
} else {
watch_set_pin_level(RED, false);
diff --git a/watch-library/watch/watch_led.h b/watch-library/watch/watch_led.h
index 04b6e5fa..5dedadf3 100644
--- a/watch-library/watch/watch_led.h
+++ b/watch-library/watch/watch_led.h
@@ -43,15 +43,16 @@ void watch_enable_led(bool pwm);
/** @brief Disables the LEDs.
* @param pwm if true, disables the PWM output. If false, disables the digital outputs.
+ * @note If pwm is true, 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_led(bool pwm);
/** @brief Sets the LED to a custom color by modulating each output's duty cycle.
- * @param red The red value.
- * @param green The green value.
- * @note still working on this, 0-65535 works now but these values may change.
+ * @param red The red value from 0-255.
+ * @param green The green value from 0-255.
*/
-void watch_set_led_color(uint16_t red, uint16_t green);
+void watch_set_led_color(uint8_t red, uint8_t green);
/** @brief Sets the red LED to full brightness, and turns the green LED off.
* @note Of the two LED's in the RG bi-color LED, the red LED is the less power-efficient one (~4.5 mA).
diff --git a/watch-library/watch/watch_private.c b/watch-library/watch/watch_private.c
index ab988d10..e820bf44 100644
--- a/watch-library/watch/watch_private.c
+++ b/watch-library/watch/watch_private.c
@@ -45,6 +45,71 @@ void _watch_init() {
a4_callback = NULL;
}
+void _watch_enable_tcc() {
+ // clock TCC0 with the main clock (4 or 16 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);
+ // have prescaler divide it down to 1 MHz. we need to know the actual CPU speed to do this.
+ uint32_t freq = watch_get_cpu_speed();
+ switch (freq) {
+ case 4000000:
+ hri_tcc_write_CTRLA_reg(TCC0, TCC_CTRLA_PRESCALER_DIV4);
+ break;
+ case 8000000:
+ hri_tcc_write_CTRLA_reg(TCC0, TCC_CTRLA_PRESCALER_DIV8);
+ break;
+ case 12000000:
+ // NOTE: this case is here for completeness but the watch library never runs the hardware at 12 MHz.
+ // If you do, buzzer tones will be out of tune, as we can't evenly divide a 12 MHz clock into 1 MHz.
+ hri_tcc_write_CTRLA_reg(TCC0, TCC_CTRLA_PRESCALER_DIV16);
+ break;
+ case 16000000:
+ hri_tcc_write_CTRLA_reg(TCC0, TCC_CTRLA_PRESCALER_DIV16);
+ break;
+ }
+ // 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);
+ // 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, 1, 0);
+ hri_tcc_write_CC_reg(TCC0, 2, 0);
+ hri_tcc_write_CC_reg(TCC0, 3, 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, PINMUX_PA20F_TCC0_WO6);
+ gpio_set_pin_direction(GREEN, GPIO_DIRECTION_OUT);
+ gpio_set_pin_function(GREEN, PINMUX_PA21F_TCC0_WO7);
+}
+
+void _watch_disable_tcc() {
+ // 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() {
// disable USB, just in case.
hri_usb_clear_CTRLA_ENABLE_bit(USB);
diff --git a/watch-library/watch/watch_private.h b/watch-library/watch/watch_private.h
index 51c78ae1..abee085a 100644
--- a/watch-library/watch/watch_private.h
+++ b/watch-library/watch/watch_private.h
@@ -25,5 +25,11 @@
/// Called by main.c while setting up the app. You should not call this from your app.
void _watch_init();
+/// Called by buzzer and LED setup functions. You should not call this from your app.
+void _watch_enable_tcc();
+
+/// Called by buzzer and LED teardown functions. You should not call this from your app.
+void _watch_disable_tcc();
+
/// Called by main.c if plugged in to USB. You should not call this from your app.
void _watch_enable_usb();