Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c65c4bfb4e | ||
|
|
6431b8157b | ||
|
|
7d1e83b416 | ||
|
|
fd685aac82 |
+20
-18
@@ -31,36 +31,38 @@
|
||||
extern void serialEvent1() __attribute__((weak));
|
||||
extern void serialEvent2() __attribute__((weak));
|
||||
|
||||
bool SerialUART::setRX(pin_size_t rx) {
|
||||
bool SerialUART::setRX(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({1, 13, 17, 29}) /* UART0 */,
|
||||
__bitset({5, 9, 21, 25}) /* UART1 */
|
||||
};
|
||||
if (_running) {
|
||||
DEBUGCORE("ERROR: SerialUART setRX while running\n");
|
||||
return false;
|
||||
} else if ((1 << rx) & valid[uart_get_index(_uart)]) {
|
||||
_rx = rx;
|
||||
if ((!_running) && ((1 << pin) & valid[uart_get_index(_uart)])) {
|
||||
_rx = pin;
|
||||
return true;
|
||||
} else {
|
||||
DEBUGCORE("ERROR: SerialUART setRX illegal pin (%d)\n", rx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.RX while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
panic("FATAL: Attempting to set Serial%d.RX to illegal pin %d", uart_get_index(_uart) + 1, pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SerialUART::setTX(pin_size_t tx) {
|
||||
bool SerialUART::setTX(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({0, 12, 16, 28}) /* UART0 */,
|
||||
__bitset({4, 8, 20, 24}) /* UART1 */
|
||||
};
|
||||
if (_running) {
|
||||
DEBUGCORE("ERROR: SerialUART setTX while running\n");
|
||||
return false;
|
||||
} else if ((1 << tx) & valid[uart_get_index(_uart)]) {
|
||||
_tx = tx;
|
||||
if ((!_running) && ((1 << pin) & valid[uart_get_index(_uart)])) {
|
||||
_tx = pin;
|
||||
return true;
|
||||
} else {
|
||||
DEBUGCORE("ERROR: SerialUART setTX illegal pin (%d)\n", tx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Serial%d.TX while running", uart_get_index(_uart) + 1);
|
||||
} else {
|
||||
panic("FATAL: Attempting to set Serial%d.TX to illegal pin %d", uart_get_index(_uart) + 1, pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SerialUART::SerialUART(uart_inst_t *uart, pin_size_t tx, pin_size_t rx) {
|
||||
|
||||
+45
-23
@@ -28,16 +28,24 @@ typedef struct {
|
||||
pin_size_t pin;
|
||||
PIO pio;
|
||||
int sm;
|
||||
alarm_id_t alarm;
|
||||
} Tone;
|
||||
|
||||
// Keep std::map safe for multicore use
|
||||
auto_init_mutex(_toneMutex);
|
||||
|
||||
|
||||
#include "tone.pio.h"
|
||||
static PIOProgram _tonePgm(&tone_program);
|
||||
#include "tone2.pio.h"
|
||||
static PIOProgram _tone2Pgm(&tone2_program);
|
||||
static std::map<pin_size_t, Tone *> _toneMap;
|
||||
|
||||
int64_t _stopTonePIO(alarm_id_t id, void *user_data) {
|
||||
(void) id;
|
||||
Tone *tone = (Tone *)user_data;
|
||||
tone->alarm = 0;
|
||||
pio_sm_set_enabled(tone->pio, tone->sm, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
if (pin > 29) {
|
||||
DEBUGCORE("ERROR: Illegal pin in tone (%d)\n", pin);
|
||||
@@ -58,32 +66,42 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) {
|
||||
if (us < 5) {
|
||||
us = 5;
|
||||
}
|
||||
// Even phases run forever, odd phases end after count...so ensure its odd
|
||||
int phases = duration ? (duration * 1000 / us) | 1 : 2;
|
||||
auto entry = _toneMap.find(pin);
|
||||
if (entry != _toneMap.end()) {
|
||||
noTone(pin);
|
||||
Tone *newTone;
|
||||
if (entry == _toneMap.end()) {
|
||||
newTone = new Tone();
|
||||
newTone->pin = pin;
|
||||
pinMode(pin, OUTPUT);
|
||||
int off;
|
||||
if (!_tone2Pgm.prepare(&newTone->pio, &newTone->sm, &off)) {
|
||||
DEBUGCORE("ERROR: tone unable to start, out of PIO resources\n");
|
||||
// ERROR, no free slots
|
||||
delete newTone;
|
||||
return;
|
||||
}
|
||||
tone2_program_init(newTone->pio, newTone->sm, off, pin);
|
||||
newTone->alarm = 0;
|
||||
} else {
|
||||
newTone = entry->second;
|
||||
if (newTone->alarm) {
|
||||
cancel_alarm(newTone->alarm);
|
||||
newTone->alarm = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto newTone = new Tone();
|
||||
newTone->pin = pin;
|
||||
pinMode(pin, OUTPUT);
|
||||
int off;
|
||||
if (!_tonePgm.prepare(&newTone->pio, &newTone->sm, &off)) {
|
||||
DEBUGCORE("ERROR: tone unable to start, out of PIO resources\n");
|
||||
// ERROR, no free slots
|
||||
delete newTone;
|
||||
return;
|
||||
}
|
||||
tone_program_init(newTone->pio, newTone->sm, off, pin);
|
||||
pio_sm_set_enabled(newTone->pio, newTone->sm, false);
|
||||
pio_sm_put_blocking(newTone->pio, newTone->sm, RP2040::usToPIOCycles(us));
|
||||
pio_sm_exec(newTone->pio, newTone->sm, pio_encode_pull(false, false));
|
||||
pio_sm_exec(newTone->pio, newTone->sm, pio_encode_out(pio_isr, 32));
|
||||
pio_sm_set_enabled(newTone->pio, newTone->sm, true);
|
||||
pio_sm_put_blocking(newTone->pio, newTone->sm, phases);
|
||||
|
||||
_toneMap.insert({pin, newTone});
|
||||
|
||||
if (duration) {
|
||||
auto ret = add_alarm_in_ms(duration, _stopTonePIO, (void *)newTone, true);
|
||||
if (ret > 0) {
|
||||
newTone->alarm = ret;
|
||||
} else {
|
||||
DEBUGCORE("ERROR: Unable to allocate timer for tone(%d, %d, %d)\n",
|
||||
pin, frequency, duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void noTone(uint8_t pin) {
|
||||
@@ -95,6 +113,10 @@ void noTone(uint8_t pin) {
|
||||
}
|
||||
auto entry = _toneMap.find(pin);
|
||||
if (entry != _toneMap.end()) {
|
||||
if (entry->second->alarm) {
|
||||
cancel_alarm(entry->second->alarm);
|
||||
entry->second->alarm = 0;
|
||||
}
|
||||
pio_sm_set_enabled(entry->second->pio, entry->second->sm, false);
|
||||
pio_sm_unclaim(entry->second->pio, entry->second->sm);
|
||||
delete entry->second;
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// -------------------------------------------------- //
|
||||
// This file is autogenerated by pioasm; do not edit! //
|
||||
// -------------------------------------------------- //
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
#include "hardware/pio.h"
|
||||
#endif
|
||||
|
||||
// ---- //
|
||||
// tone //
|
||||
// ---- //
|
||||
|
||||
#define tone_wrap_target 0
|
||||
#define tone_wrap 7
|
||||
|
||||
static const uint16_t tone_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0x80a0, // 0: pull block
|
||||
0xa027, // 1: mov x, osr
|
||||
0xb846, // 2: mov y, isr side 1
|
||||
0x0083, // 3: jmp y--, 3
|
||||
0x0045, // 4: jmp x--, 5
|
||||
0xb046, // 5: mov y, isr side 0
|
||||
0x0086, // 6: jmp y--, 6
|
||||
0x0042, // 7: jmp x--, 2
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program tone_program = {
|
||||
.instructions = tone_program_instructions,
|
||||
.length = 8,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config tone_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + tone_wrap_target, offset + tone_wrap);
|
||||
sm_config_set_sideset(&c, 2, true, false);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void tone_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_gpio_init(pio, pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
|
||||
pio_sm_config c = tone_program_get_default_config(offset);
|
||||
sm_config_set_sideset_pins(&c, pin);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; Tone for the Raspberry Pi Pico RP2040
|
||||
; Tone2 for the Raspberry Pi Pico RP2040
|
||||
;
|
||||
; Copyright (c) 2021 Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
;
|
||||
@@ -18,32 +18,35 @@
|
||||
|
||||
; Side-set pin 0 is used for Tone output
|
||||
|
||||
.program tone
|
||||
; OSR == Halfcycle count
|
||||
|
||||
.program tone2
|
||||
.side_set 1 opt
|
||||
|
||||
pull
|
||||
mov x, osr
|
||||
pull ; TXFIFO -> OSR, or X -> OSR if no new period
|
||||
mov x, osr ; OSR -> X
|
||||
|
||||
high:
|
||||
mov y, isr side 1
|
||||
pull noblock ; Potentially grab new HALFCYCLECOUNT, OTW copy from backup in X
|
||||
mov x, osr ; OSR -> X
|
||||
mov y, osr side 1 ; HALFCYCLECOUNT -> Y
|
||||
highloop:
|
||||
jmp y-- highloop
|
||||
|
||||
jmp x-- low
|
||||
jmp y-- highloop ; while (y--) { /* noop delay */ }
|
||||
|
||||
low:
|
||||
mov y, isr side 0
|
||||
mov y, osr side 0 ; HALFCYCLECOUNT -> Y
|
||||
lowloop:
|
||||
jmp y-- lowloop
|
||||
jmp y-- lowloop ; while (y--) { /* noop delay */ }
|
||||
|
||||
jmp x-- high
|
||||
jmp high ; GOTO high
|
||||
|
||||
% c-sdk {
|
||||
static inline void tone_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
static inline void tone2_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_gpio_init(pio, pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
|
||||
pio_sm_config c = tone_program_get_default_config(offset);
|
||||
pio_sm_config c = tone2_program_get_default_config(offset);
|
||||
sm_config_set_sideset_pins(&c, pin);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
%}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// -------------------------------------------------- //
|
||||
// This file is autogenerated by pioasm; do not edit! //
|
||||
// -------------------------------------------------- //
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
#include "hardware/pio.h"
|
||||
#endif
|
||||
|
||||
// ----- //
|
||||
// tone2 //
|
||||
// ----- //
|
||||
|
||||
#define tone2_wrap_target 0
|
||||
#define tone2_wrap 8
|
||||
|
||||
static const uint16_t tone2_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0x80a0, // 0: pull block
|
||||
0xa027, // 1: mov x, osr
|
||||
0x8080, // 2: pull noblock
|
||||
0xa027, // 3: mov x, osr
|
||||
0xb847, // 4: mov y, osr side 1
|
||||
0x0085, // 5: jmp y--, 5
|
||||
0xb047, // 6: mov y, osr side 0
|
||||
0x0087, // 7: jmp y--, 7
|
||||
0x0002, // 8: jmp 2
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program tone2_program = {
|
||||
.instructions = tone2_program_instructions,
|
||||
.length = 9,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config tone2_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + tone2_wrap_target, offset + tone2_wrap);
|
||||
sm_config_set_sideset(&c, 2, true, false);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void tone2_program_init(PIO pio, uint sm, uint offset, uint pin) {
|
||||
pio_gpio_init(pio, pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
|
||||
pio_sm_config c = tone2_program_get_default_config(offset);
|
||||
sm_config_set_sideset_pins(&c, pin);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name=SD(rp2040)
|
||||
name=SD
|
||||
version=2.0.0
|
||||
author=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
|
||||
+32
-20
@@ -202,56 +202,68 @@ bool SPIClassRP2040::setRX(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({0, 4, 16, 20}) /* SPI0 */,
|
||||
__bitset({8, 12, 24, 28}) /* SPI1 */
|
||||
};
|
||||
if (_running) {
|
||||
return false;
|
||||
} else if ((1 << pin) & valid[spi_get_index(_spi)]) {
|
||||
if ((!_running) && ((1 << pin) & valid[spi_get_index(_spi)])) {
|
||||
_RX = pin;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.RX while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set SPI%s.RX to illegal pin %d", spi_get_index(_spi) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SPIClassRP2040::setCS(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({1, 5, 17, 21}) /* SPI0 */,
|
||||
__bitset({9, 13, 25, 29}) /* SPI1 */
|
||||
};
|
||||
if (_running) {
|
||||
return false;
|
||||
} else if ((1 << pin) & valid[spi_get_index(_spi)]) {
|
||||
if ((!_running) && ((1 << pin) & valid[spi_get_index(_spi)])) {
|
||||
_CS = pin;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.CS while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set SPI%s.CS to illegal pin %d", spi_get_index(_spi) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SPIClassRP2040::setSCK(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({2, 6, 18, 22}) /* SPI0 */,
|
||||
__bitset({10, 14, 26}) /* SPI1 */
|
||||
};
|
||||
if (_running) {
|
||||
return false;
|
||||
} else if ((1 << pin) & valid[spi_get_index(_spi)]) {
|
||||
if ((!_running) && ((1 << pin) & valid[spi_get_index(_spi)])) {
|
||||
_SCK = pin;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.SCK while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set SPI%s.SCK to illegal pin %d", spi_get_index(_spi) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SPIClassRP2040::setTX(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({3, 7, 19, 23}) /* SPI0 */,
|
||||
__bitset({11, 15, 27}) /* SPI1 */
|
||||
};
|
||||
if (_running) {
|
||||
return false;
|
||||
} else if ((1 << pin) & valid[spi_get_index(_spi)]) {
|
||||
if ((!_running) && ((1 << pin) & valid[spi_get_index(_spi)])) {
|
||||
_TX = pin;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set SPI%s.TX while running", spi_get_index(_spi) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set SPI%s.TX to illegal pin %d", spi_get_index(_spi) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SPIClassRP2040::begin(bool hwCS) {
|
||||
|
||||
+16
-10
@@ -51,28 +51,34 @@ bool TwoWire::setSDA(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({0, 4, 8, 12, 16, 20, 24, 28}) /* I2C0 */,
|
||||
__bitset({2, 6, 10, 14, 18, 22, 26}) /* I2C1 */
|
||||
};
|
||||
if (_running) {
|
||||
return false;
|
||||
} else if ((1 << pin) & valid[i2c_hw_index(_i2c)]) {
|
||||
if ((!_running) && ((1 << pin) & valid[i2c_hw_index(_i2c)])) {
|
||||
_sda = pin;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Wire%s.SDA while running", i2c_hw_index(_i2c) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set Wire%s.SDA to illegal pin %d", i2c_hw_index(_i2c) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TwoWire::setSCL(pin_size_t pin) {
|
||||
constexpr uint32_t valid[2] = { __bitset({1, 5, 9, 13, 17, 21, 25, 29}) /* I2C0 */,
|
||||
__bitset({3, 7, 11, 15, 19, 23, 27}) /* I2C1 */
|
||||
};
|
||||
if (_running) {
|
||||
return false;
|
||||
} else if ((1 << pin) & valid[i2c_hw_index(_i2c)]) {
|
||||
if ((!_running) && ((1 << pin) & valid[i2c_hw_index(_i2c)])) {
|
||||
_scl = pin;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_running) {
|
||||
panic("FATAL: Attempting to set Wire%s.SCL while running", i2c_hw_index(_i2c) ? "1" : "");
|
||||
} else {
|
||||
panic("FATAL: Attempting to set Wire%s.SCL to illegal pin %d", i2c_hw_index(_i2c) ? "1" : "", pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TwoWire::setClock(uint32_t hz) {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/* Simple annoying siren example using tone() */
|
||||
/* Released to the public domain by Earle F. Philhower, III */
|
||||
|
||||
#define TONEPIN 7
|
||||
|
||||
void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
for (int i = 100; i < 10000; i += 5) {
|
||||
tone(TONEPIN, i);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// The speaker will play the tune to Happy Birthday continuously
|
||||
// Author: Tony DiCola
|
||||
// License: MIT (https://opensource.org/licenses/MIT)
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef USE_TINYUSB
|
||||
// For Serial when selecting TinyUSB. Can't include in the core because Arduino IDE
|
||||
// will not link in libraries called from the core. Instead, add the header to all
|
||||
// the standard libraries in the hope it will still catch some user cases where they
|
||||
// use these libraries.
|
||||
// See https://github.com/earlephilhower/arduino-pico/issues/167#issuecomment-848622174
|
||||
#include <Adafruit_TinyUSB.h>
|
||||
#endif
|
||||
|
||||
// pin_buzzer should be defined by the supported variant e.g CPlay Bluefruit or CLUE.
|
||||
// Otherwise please define the pin you would like to use for tone output
|
||||
#ifndef PIN_BUZZER
|
||||
#define PIN_BUZZER A0
|
||||
#endif
|
||||
|
||||
uint8_t const pin_buzzer = PIN_BUZZER;
|
||||
|
||||
// A few music note frequencies as defined in this tone example:
|
||||
// https://www.arduino.cc/en/Tutorial/toneMelody
|
||||
#define NOTE_C4 262
|
||||
#define NOTE_CS4 277
|
||||
#define NOTE_D4 294
|
||||
#define NOTE_DS4 311
|
||||
#define NOTE_E4 330
|
||||
#define NOTE_F4 349
|
||||
#define NOTE_FS4 370
|
||||
#define NOTE_G4 392
|
||||
#define NOTE_GS4 415
|
||||
#define NOTE_A4 440
|
||||
#define NOTE_AS4 466
|
||||
#define NOTE_B4 494
|
||||
#define NOTE_C5 523
|
||||
#define NOTE_CS5 554
|
||||
#define NOTE_D5 587
|
||||
#define NOTE_DS5 622
|
||||
#define NOTE_E5 659
|
||||
#define NOTE_F5 698
|
||||
#define NOTE_FS5 740
|
||||
#define NOTE_G5 784
|
||||
#define NOTE_GS5 831
|
||||
#define NOTE_A5 880
|
||||
#define NOTE_AS5 932
|
||||
#define NOTE_B5 988
|
||||
|
||||
// Define note durations. You only need to adjust the whole note
|
||||
// time and other notes will be subdivided from it directly.
|
||||
#define WHOLE 2200 // Length of time in milliseconds of a whole note (i.e. a full bar).
|
||||
#define HALF WHOLE/2
|
||||
#define QUARTER HALF/2
|
||||
#define EIGHTH QUARTER/2
|
||||
#define EIGHTH_TRIPLE QUARTER/3
|
||||
#define SIXTEENTH EIGHTH/2
|
||||
|
||||
// Play a note of the specified frequency and for the specified duration.
|
||||
// Hold is an optional bool that specifies if this note should be held a
|
||||
// little longer, i.e. for eighth notes that are tied together.
|
||||
// While waiting for a note to play the waitBreath delay function is used
|
||||
// so breath detection and pixel animation continues to run. No tones
|
||||
// will play if the slide switch is in the -/off position or all the
|
||||
// candles have been blown out.
|
||||
void playNote(int frequency, int duration, bool hold = false, bool measure = true) {
|
||||
(void) measure;
|
||||
|
||||
if (hold) {
|
||||
// For a note that's held play it a little longer than the specified duration
|
||||
// so it blends into the next tone (but there's still a small delay to
|
||||
// hear the next note).
|
||||
tone(pin_buzzer, frequency, duration + duration / 32);
|
||||
} else {
|
||||
// For a note that isn't held just play it for the specified duration.
|
||||
tone(pin_buzzer, frequency, duration);
|
||||
}
|
||||
|
||||
delay(duration + duration / 16);
|
||||
}
|
||||
|
||||
// Song to play when the candles are blown out.
|
||||
void celebrateSong() {
|
||||
// Play a little charge melody, from:
|
||||
// https://en.wikipedia.org/wiki/Charge_(fanfare)
|
||||
// Note the explicit boolean parameters in particular the measure=false
|
||||
// at the end. This means the notes will play without any breath measurement
|
||||
// logic. Without this false value playNote will try to keep waiting for candles
|
||||
// to blow out during the celebration song!
|
||||
playNote(NOTE_G4, EIGHTH_TRIPLE, true, false);
|
||||
playNote(NOTE_C5, EIGHTH_TRIPLE, true, false);
|
||||
playNote(NOTE_E5, EIGHTH_TRIPLE, false, false);
|
||||
playNote(NOTE_G5, EIGHTH, true, false);
|
||||
playNote(NOTE_E5, SIXTEENTH, false);
|
||||
playNote(NOTE_G5, HALF, false);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// Initialize serial output and Circuit Playground library.
|
||||
Serial.begin(115200);
|
||||
|
||||
pinMode(pin_buzzer, OUTPUT);
|
||||
digitalWrite(pin_buzzer, LOW);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Play happy birthday tune, from:
|
||||
// http://www.irish-folk-songs.com/happy-birthday-tin-whistle-sheet-music.html#.WXFJMtPytBw
|
||||
// Inside each playNote call it will play a note and drive the NeoPixel animation
|
||||
// and check for a breath against the sound sensor. Once all the candles are blown out
|
||||
// the playNote calls will stop playing music.
|
||||
playNote(NOTE_D4, EIGHTH, true);
|
||||
playNote(NOTE_D4, EIGHTH);
|
||||
playNote(NOTE_E4, QUARTER); // Bar 1
|
||||
playNote(NOTE_D4, QUARTER);
|
||||
playNote(NOTE_G4, QUARTER);
|
||||
playNote(NOTE_FS4, HALF); // Bar 2
|
||||
playNote(NOTE_D4, EIGHTH, true);
|
||||
playNote(NOTE_D4, EIGHTH);
|
||||
playNote(NOTE_E4, QUARTER); // Bar 3
|
||||
playNote(NOTE_D4, QUARTER);
|
||||
playNote(NOTE_A4, QUARTER);
|
||||
playNote(NOTE_G4, HALF); // Bar 4
|
||||
playNote(NOTE_D4, EIGHTH, true);
|
||||
playNote(NOTE_D4, EIGHTH);
|
||||
playNote(NOTE_D5, QUARTER); // Bar 5
|
||||
playNote(NOTE_B4, QUARTER);
|
||||
playNote(NOTE_G4, QUARTER);
|
||||
playNote(NOTE_FS4, QUARTER); // Bar 6
|
||||
playNote(NOTE_E4, QUARTER);
|
||||
playNote(NOTE_C5, EIGHTH, true);
|
||||
playNote(NOTE_C5, EIGHTH);
|
||||
playNote(NOTE_B4, QUARTER); // Bar 7
|
||||
playNote(NOTE_G4, QUARTER);
|
||||
playNote(NOTE_A4, QUARTER);
|
||||
playNote(NOTE_G4, HALF); // Bar 8
|
||||
|
||||
celebrateSong();
|
||||
|
||||
// One second pause before repeating the loop and playing
|
||||
delay(1000);
|
||||
}
|
||||
Reference in New Issue
Block a user