Skip to content

Commit

Permalink
lib: rewrite buzzer to play melodies in FreeRTOS task
Browse files Browse the repository at this point in the history
  • Loading branch information
and3rson committed Mar 10, 2024
1 parent b1b9b5c commit ec12561
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 25 deletions.
80 changes: 62 additions & 18 deletions sdk/lib/lilka/src/lilka/buzzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,58 +6,102 @@

namespace lilka {

Buzzer::Buzzer() :
buzzerMutex(xSemaphoreCreateMutex()),
melodyTaskHandle(NULL),
currentMelody(NULL),
currentMelodyLength(0),
currentMelodyTempo(0) {
}

void Buzzer::begin() {
// TODO: Use ledc?
#if LILKA_VERSION < 2
serial_err("Buzzer is not supported on this board");
return;
#endif
#else
pinMode(LILKA_BUZZER, OUTPUT);
#endif
}

void Buzzer::play(uint16_t frequency) {
#if LILKA_VERSION < 2
serial_err("Buzzer is not supported on this board");
return;
#endif
#else
xSemaphoreTake(buzzerMutex, portMAX_DELAY);
_stop();
tone(LILKA_BUZZER, frequency);
xSemaphoreGive(buzzerMutex);
#endif
}

void Buzzer::play(uint16_t frequency, uint32_t duration) {
#if LILKA_VERSION < 2
serial_err("Buzzer is not supported on this board");
return;
#endif
#else
xSemaphoreTake(buzzerMutex, portMAX_DELAY);
_stop();
tone(LILKA_BUZZER, frequency, duration);
xSemaphoreGive(buzzerMutex);
#endif
}

void Buzzer::stop() {
void Buzzer::playMelody(const Tone* melody, uint32_t length, uint32_t tempo) {
#if LILKA_VERSION < 2
serial_err("Buzzer is not supported on this board");
return;
#else
xSemaphoreTake(buzzerMutex, portMAX_DELAY);
_stop();
currentMelody = static_cast<Tone*>(realloc(currentMelody, length * sizeof(Tone)));
memcpy(currentMelody, melody, length * sizeof(Tone));
currentMelodyLength = length;
currentMelodyTempo = tempo;
xTaskCreate(melodyTask, "melodyTask", 2048, this, 1, &melodyTaskHandle);
xSemaphoreGive(buzzerMutex);
#endif
noTone(LILKA_BUZZER);
}

void Buzzer::playMelody(const Tone* melody, uint32_t length, uint32_t tempo) {
// TODO: Make this a FreeRTOS task
for (uint32_t i = 0; i < length; i++) {
Tone tone = melody[i];
if (tone.size == 0) {
void Buzzer::melodyTask(void* arg) {
Buzzer* buzzer = static_cast<Buzzer*>(arg);
for (uint32_t i = 0; i < buzzer->currentMelodyLength; i++) {
xSemaphoreTake(buzzer->buzzerMutex, portMAX_DELAY);
Tone currentTone = buzzer->currentMelody[i];
if (currentTone.size == 0) {
taskYIELD();
continue;
}
uint32_t duration = (60000 / tempo) / abs(tone.size);
if (tone.size < 0) {
uint32_t duration = (60000 / buzzer->currentMelodyTempo) / abs(currentTone.size);
if (currentTone.size < 0) {
duration += duration / 2;
}

if (melody[i].frequency == 0) {
delay(duration);
} else {
play(melody[i].frequency, duration);
delay(duration);
if (buzzer->currentMelody[i].frequency != 0) {
tone(LILKA_BUZZER, buzzer->currentMelody[i].frequency);
}
xSemaphoreGive(buzzer->buzzerMutex);
vTaskDelay(duration / portTICK_PERIOD_MS);
}
buzzer->stop();
}

void Buzzer::stop() {
#if LILKA_VERSION < 2
serial_err("Buzzer is not supported on this board");
#else
xSemaphoreTake(buzzerMutex, portMAX_DELAY);
_stop();
xSemaphoreGive(buzzerMutex);
#endif
}

void Buzzer::_stop() {
if (melodyTaskHandle != NULL) {
vTaskDelete(melodyTaskHandle);
melodyTaskHandle = NULL;
}
noTone(LILKA_BUZZER);
}

void Buzzer::playDoom() {
Expand Down
30 changes: 23 additions & 7 deletions sdk/lib/lilka/src/lilka/buzzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#define LILKA_BUZZER_H

#include <stdint.h>
#include <FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>

namespace lilka {

Expand Down Expand Up @@ -46,6 +49,10 @@ typedef struct {
/// Клас для роботи з п'єзо-динаміком.
/// Використовується для відтворення монотонних звуків.
///
/// Всі методи цього класу є неблокуючими, тобто вони не чекають завершення відтворення звуку і не блокують виконання коду, що йде після них.
///
/// Щоб зупинити відтворення звуку, використовуйте метод `stop()`.
///
/// Приклад використання:
///
/// @code
Expand All @@ -64,24 +71,33 @@ typedef struct {
/// @endcode
class Buzzer {
public:
Buzzer();
/// Почати роботу з п'єзо-динаміком.
/// \warning Цей метод викликається автоматично при виклику `lilka::begin()`.
void begin();
/// Відтворити ноту з певною частотою.
void play(uint16_t frequency);
/// Відтворити ноту з певною частотою протягом певного часу.
/// Відтворити ноту з певною частотою впродовж певного часу.
void play(uint16_t frequency, uint32_t duration);
/// Зупинити відтворення.
void stop();
/// Відтворити мелодію.
void playMelody(const Tone* melody, uint32_t length, uint32_t tempo = 120);
/// Зупинити відтворення всіх звуків.
void stop();
/// Відтворити мелодію з DOOM - E1M1, At Doom's Gate (Bobby Prince).
void playDoom();
};

#define DOOM_MELODY
#define DOOM_LENGTH
#define DOOM_TEMPO
static void melodyTask(void* arg);

private:
// cppcheck-suppress unusedPrivateFunction
void _stop();

SemaphoreHandle_t buzzerMutex;
TaskHandle_t melodyTaskHandle;
Tone* currentMelody;
uint32_t currentMelodyLength;
uint32_t currentMelodyTempo;
};

/// Екземпляр класу `Buzzer`, який можна використовувати для відтворення монотонних звуків.
/// Вам не потрібно інстанціювати `Buzzer` вручну.
Expand Down

0 comments on commit ec12561

Please sign in to comment.