big PWM refactor: drive both LEDs and buzzer from TCC
This commit is contained in:
parent
7dc4b0fdc6
commit
6353d25d46
@ -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)
|
static struct tcc_cfg *_get_tcc_cfg(void *hw)
|
||||||
{
|
{
|
||||||
|
@ -83,37 +83,6 @@ void delay_driver_init(void) {
|
|||||||
delay_init(SysTick);
|
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) {
|
void SEGMENT_LCD_0_PORT_init(void) {
|
||||||
gpio_set_pin_function(COM0, PINMUX_PB06B_SLCD_LP0);
|
gpio_set_pin_function(COM0, PINMUX_PB06B_SLCD_LP0);
|
||||||
gpio_set_pin_function(COM1, PINMUX_PB07B_SLCD_LP1);
|
gpio_set_pin_function(COM1, PINMUX_PB07B_SLCD_LP1);
|
||||||
|
@ -23,28 +23,38 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
inline void watch_enable_buzzer() {
|
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) {
|
||||||
|
hri_tcc_write_PERBUF_reg(TCC0, period);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void watch_set_buzzer_period(uint32_t period) {
|
void watch_disable_buzzer() {
|
||||||
pwm_set_parameters(&PWM_1, period, period / 2);
|
_watch_disable_tcc();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void watch_set_buzzer_on() {
|
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() {
|
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};
|
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) {
|
void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms) {
|
||||||
if (note == BUZZER_NOTE_REST) {
|
if (note == BUZZER_NOTE_REST) {
|
||||||
watch_set_buzzer_off();
|
watch_set_buzzer_off();
|
||||||
} else {
|
} 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();
|
watch_set_buzzer_on();
|
||||||
}
|
}
|
||||||
delay_ms(duration_ms);
|
delay_ms(duration_ms);
|
||||||
|
@ -32,11 +32,17 @@
|
|||||||
void watch_enable_buzzer();
|
void watch_enable_buzzer();
|
||||||
|
|
||||||
/** @brief Sets the period of the 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
|
* @param period The period of a single cycle for the TCC peripheral. You can determine the period for
|
||||||
* convert a desired frequency to a period for this function: period = 513751 * (freq^−1.0043)
|
* a desired frequency with the following formula: period = 1000000 / freq
|
||||||
*/
|
*/
|
||||||
void watch_set_buzzer_period(uint32_t period);
|
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.
|
/** @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
|
* @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.
|
* output to continue, you should prevent your app from going to sleep.
|
||||||
|
@ -22,45 +22,39 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool PWM_0_enabled = false;
|
|
||||||
|
|
||||||
void watch_enable_led(bool pwm) {
|
void watch_enable_led(bool pwm) {
|
||||||
if (pwm) {
|
if (pwm) {
|
||||||
if (PWM_0_enabled) return;
|
if (!hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
|
||||||
|
_watch_enable_tcc();
|
||||||
PWM_0_init();
|
}
|
||||||
pwm_set_parameters(&PWM_0, 10000, 0);
|
|
||||||
pwm_enable(&PWM_0);
|
|
||||||
|
|
||||||
PWM_0_enabled = true;
|
|
||||||
} else {
|
} else {
|
||||||
watch_enable_digital_output(RED);
|
watch_enable_digital_output(RED);
|
||||||
watch_enable_digital_output(GREEN);
|
watch_enable_digital_output(GREEN);
|
||||||
}
|
|
||||||
watch_set_led_off();
|
watch_set_led_off();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void watch_disable_led(bool pwm) {
|
void watch_disable_led(bool pwm) {
|
||||||
if (pwm) {
|
if (pwm) {
|
||||||
if (!PWM_0_enabled) return;
|
_watch_disable_tcc();
|
||||||
pwm_disable(&PWM_0);
|
} else {
|
||||||
PWM_0_enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
watch_disable_digital_output(RED);
|
watch_disable_digital_output(RED);
|
||||||
watch_disable_digital_output(GREEN);
|
watch_disable_digital_output(GREEN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void watch_set_led_color(uint16_t red, uint16_t green) {
|
void watch_set_led_color(uint8_t red, uint8_t green) {
|
||||||
if (PWM_0_enabled) {
|
if (hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
|
||||||
TC3->COUNT16.CC[0].reg = red;
|
uint32_t period = hri_tcc_get_PER_reg(TCC0, TCC_PER_MASK);
|
||||||
TC3->COUNT16.CC[1].reg = green;
|
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() {
|
void watch_set_led_red() {
|
||||||
if (PWM_0_enabled) {
|
if (hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
|
||||||
watch_set_led_color(65535, 0);
|
watch_set_led_color(255, 0);
|
||||||
} else {
|
} else {
|
||||||
watch_set_pin_level(RED, true);
|
watch_set_pin_level(RED, true);
|
||||||
watch_set_pin_level(GREEN, false);
|
watch_set_pin_level(GREEN, false);
|
||||||
@ -68,8 +62,8 @@ void watch_set_led_red() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void watch_set_led_green() {
|
void watch_set_led_green() {
|
||||||
if (PWM_0_enabled) {
|
if (hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
|
||||||
watch_set_led_color(65535, 0);
|
watch_set_led_color(0, 255);
|
||||||
} else {
|
} else {
|
||||||
watch_set_pin_level(RED, false);
|
watch_set_pin_level(RED, false);
|
||||||
watch_set_pin_level(GREEN, true);
|
watch_set_pin_level(GREEN, true);
|
||||||
@ -77,8 +71,8 @@ void watch_set_led_green() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void watch_set_led_yellow() {
|
void watch_set_led_yellow() {
|
||||||
if (PWM_0_enabled) {
|
if (hri_tcc_get_CTRLA_reg(TCC0, TCC_CTRLA_ENABLE)) {
|
||||||
watch_set_led_color(65535, 65535);
|
watch_set_led_color(255, 255);
|
||||||
} else {
|
} else {
|
||||||
watch_set_pin_level(RED, true);
|
watch_set_pin_level(RED, true);
|
||||||
watch_set_pin_level(GREEN, true);
|
watch_set_pin_level(GREEN, true);
|
||||||
@ -86,7 +80,7 @@ void watch_set_led_yellow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void watch_set_led_off() {
|
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);
|
watch_set_led_color(0, 0);
|
||||||
} else {
|
} else {
|
||||||
watch_set_pin_level(RED, false);
|
watch_set_pin_level(RED, false);
|
||||||
|
@ -43,15 +43,16 @@ void watch_enable_led(bool pwm);
|
|||||||
|
|
||||||
/** @brief Disables the LEDs.
|
/** @brief Disables the LEDs.
|
||||||
* @param pwm if true, disables the PWM output. If false, disables the digital outputs.
|
* @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);
|
void watch_disable_led(bool pwm);
|
||||||
|
|
||||||
/** @brief Sets the LED to a custom color by modulating each output's duty cycle.
|
/** @brief Sets the LED to a custom color by modulating each output's duty cycle.
|
||||||
* @param red The red value.
|
* @param red The red value from 0-255.
|
||||||
* @param green The green value.
|
* @param green The green value from 0-255.
|
||||||
* @note still working on this, 0-65535 works now but these values may change.
|
|
||||||
*/
|
*/
|
||||||
void watch_set_led_color(uint16_t red, uint16_t green);
|
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.
|
/** @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).
|
* @note Of the two LED's in the RG bi-color LED, the red LED is the less power-efficient one (~4.5 mA).
|
||||||
|
@ -45,6 +45,71 @@ void _watch_init() {
|
|||||||
a4_callback = NULL;
|
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() {
|
void _watch_enable_usb() {
|
||||||
// disable USB, just in case.
|
// disable USB, just in case.
|
||||||
hri_usb_clear_CTRLA_ENABLE_bit(USB);
|
hri_usb_clear_CTRLA_ENABLE_bit(USB);
|
||||||
|
@ -25,5 +25,11 @@
|
|||||||
/// Called by main.c while setting up the app. You should not call this from your app.
|
/// Called by main.c while setting up the app. You should not call this from your app.
|
||||||
void _watch_init();
|
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.
|
/// Called by main.c if plugged in to USB. You should not call this from your app.
|
||||||
void _watch_enable_usb();
|
void _watch_enable_usb();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user