You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
389 lines
11 KiB
389 lines
11 KiB
/**
|
|
* \file
|
|
* \brief Buzzer driver interface.
|
|
* \author Erich Styger, erich.styger@hslu.ch
|
|
* \license SPDX-License-Identifier: BSD-3-Clause
|
|
* This module implements the driver for the buzzer.
|
|
*/
|
|
|
|
#include "platform.h"
|
|
#if PL_CONFIG_USE_BUZZER
|
|
#include "Buzzer.h"
|
|
#include "McuGPIO.h"
|
|
#include "Trigger.h"
|
|
#include "McuUtility.h"
|
|
#include "McuRTOS.h"
|
|
#if PL_CONFIG_USE_SHELL
|
|
#include "McuShell.h"
|
|
#endif
|
|
|
|
#define BUZZER_USE_FREERTOS_TIMER (1) /* if using FreeRTOS or software timer */
|
|
|
|
#if BUZZER_USE_FREERTOS_TIMER
|
|
static TimerHandle_t timerToggle, timerDuration;
|
|
#else
|
|
typedef struct {
|
|
uint16_t buzPeriodTicks; /*!< number of trigger ticks for a PWM period */
|
|
uint16_t buzIterationCntr; /*!< number of iterations */
|
|
} BUZ_TrgInfo;
|
|
|
|
static volatile BUZ_TrgInfo trgInfo;
|
|
#endif
|
|
|
|
static McuGPIO_Handle_t BUZ_Pin;
|
|
|
|
typedef struct {
|
|
int freq; /* frequency */
|
|
int ms; /* milliseconds */
|
|
} BUZ_Tune;
|
|
|
|
#define NOTE_C5 261
|
|
#define NOTE_D5 293
|
|
#define NOTE_E5 329
|
|
#define NOTE_F5 349
|
|
#define NOTE_G5 392
|
|
|
|
static const BUZ_Tune MelodyJingleBells[] =
|
|
{ /* freq, ms */
|
|
{NOTE_E5, 4*100},
|
|
{NOTE_E5, 4*100},
|
|
{NOTE_E5, 4*200},
|
|
{NOTE_E5, 4*100},
|
|
{NOTE_E5, 4*100},
|
|
{NOTE_E5, 4*200},
|
|
{NOTE_E5, 4*100},
|
|
{NOTE_G5, 4*100},
|
|
{NOTE_C5, 4*100},
|
|
{NOTE_D5, 4*100},
|
|
{NOTE_E5, 4*200},
|
|
{0, 4*100},
|
|
{NOTE_F5, 4*100},
|
|
{NOTE_F5, 4*100},
|
|
{NOTE_F5, 4*100},
|
|
{NOTE_F5, 4*100},
|
|
{NOTE_F5, 4*100},
|
|
{NOTE_E5, 4*100},
|
|
{NOTE_E5, 4*100},
|
|
{NOTE_E5, 4*100},
|
|
{NOTE_E5, 4*100},
|
|
{NOTE_D5, 4*100},
|
|
{NOTE_D5, 4*100},
|
|
{NOTE_E5, 4*100},
|
|
{NOTE_D5, 4*200},
|
|
{NOTE_G5, 4*200},
|
|
};
|
|
|
|
static const BUZ_Tune MelodyWelcome[] =
|
|
{ /* freq, ms */
|
|
{300,500},
|
|
{500,200},
|
|
{300,100},
|
|
{200,300},
|
|
{500,400},
|
|
{300,100},
|
|
{200,300},
|
|
{300,100},
|
|
{200,300},
|
|
{500,400},
|
|
};
|
|
|
|
static const BUZ_Tune MelodyButton[] =
|
|
{ /* freq, ms */
|
|
{200,100},
|
|
{600,100},
|
|
};
|
|
|
|
static const BUZ_Tune MelodyButtonLong[] =
|
|
{ /* freq, ms */
|
|
{500,50},
|
|
{100,100},
|
|
{300,50},
|
|
{150,50},
|
|
{450,50},
|
|
{500,50},
|
|
{250,200},
|
|
};
|
|
|
|
static const BUZ_Tune MelodyMazeDestination[] =
|
|
{ /* freq, ms */
|
|
{500,50},
|
|
{100,100},
|
|
{300,50},
|
|
{150,50},
|
|
{450,50},
|
|
{500,50},
|
|
{250,200},
|
|
{500,50},
|
|
{100,100},
|
|
{300,50},
|
|
{150,50},
|
|
{450,50},
|
|
{500,50},
|
|
{250,200},
|
|
{500,50},
|
|
{100,100},
|
|
{300,50},
|
|
{150,50},
|
|
{450,50},
|
|
{500,50},
|
|
{250,200},
|
|
};
|
|
|
|
typedef struct {
|
|
int idx; /* current index */
|
|
int maxIdx; /* maximum index */
|
|
#if !BUZZER_USE_FREERTOS_TIMER
|
|
BUZ_TrgInfo trgInfo;
|
|
#endif
|
|
const BUZ_Tune *melody;
|
|
} MelodyDesc;
|
|
|
|
#if BUZZER_USE_FREERTOS_TIMER
|
|
static MelodyDesc *playingMelody = NULL;
|
|
#endif
|
|
|
|
static MelodyDesc BUZ_Melodies[] = {
|
|
#if BUZZER_USE_FREERTOS_TIMER
|
|
{0, sizeof(MelodyWelcome)/sizeof(MelodyWelcome[0]), MelodyWelcome}, /* BUZ_TUNE_WELCOME */
|
|
{0, sizeof(MelodyButton)/sizeof(MelodyButton[0]), MelodyButton}, /* BUZ_TUNE_BUTTON */
|
|
{0, sizeof(MelodyButtonLong)/sizeof(MelodyButtonLong[0]), MelodyButtonLong}, /* BUZ_TUNE_BUTTON_LONG */
|
|
{0, sizeof(MelodyMazeDestination)/sizeof(MelodyMazeDestination[0]), MelodyMazeDestination}, /* BUZ_TUNE_MAZE_DESTINATION */
|
|
{0, sizeof(MelodyJingleBells)/sizeof(MelodyJingleBells[0]), MelodyJingleBells}, /* BUZ_TUNE_JINGLE_BELLS */
|
|
#else
|
|
{0, sizeof(MelodyWelcome)/sizeof(MelodyWelcome[0]), {0, 0}, MelodyWelcome}, /* BUZ_TUNE_WELCOME */
|
|
{0, sizeof(MelodyButton)/sizeof(MelodyButton[0]), {0, 0}, MelodyButton}, /* BUZ_TUNE_BUTTON */
|
|
{0, sizeof(MelodyButtonLong)/sizeof(MelodyButtonLong[0]), {0, 0}, MelodyButtonLong}, /* BUZ_TUNE_BUTTON_LONG */
|
|
{0, sizeof(MelodyMazeDestination)/sizeof(MelodyMazeDestination[0]), {0, 0}, MelodyMazeDestination}, /* BUZ_TUNE_MAZE_DESTINATION */
|
|
{0, sizeof(MelodyJingleBells)/sizeof(MelodyJingleBells[0]), [0, 0}, MelodyJingleBells}, /* BUZ_TUNE_JINGLE_BELLS */
|
|
#endif
|
|
};
|
|
|
|
#if !BUZZER_USE_FREERTOS_TIMER
|
|
static void BUZ_Toggle(void *dataPtr) {
|
|
BUZ_TrgInfo *trgInfo = (BUZ_TrgInfo *)dataPtr;
|
|
|
|
if (trgInfo->buzIterationCntr==0) {
|
|
McuGPIO_SetLow(BUZ_Pin); /* turn buzzer off */
|
|
} else {
|
|
trgInfo->buzIterationCntr--;
|
|
McuGPIO_Toggle(BUZ_Pin);
|
|
(void)TRG_SetTrigger(TRG_BUZ_BEEP, trgInfo->buzPeriodTicks, BUZ_Toggle, trgInfo);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
uint8_t BUZ_Beep(uint16_t freqHz, uint16_t durationMs) {
|
|
#if BUZZER_USE_FREERTOS_TIMER
|
|
BaseType_t res;
|
|
|
|
if (freqHz>1000) { /* timer frequency is max 1 kHz */
|
|
return ERR_FAILED;
|
|
}
|
|
if (freqHz==0) { /* pause */
|
|
res = xTimerChangePeriod(timerToggle, pdMS_TO_TICKS(durationMs), pdMS_TO_TICKS(100));
|
|
} else {
|
|
res = xTimerChangePeriod(timerToggle, pdMS_TO_TICKS(1000/freqHz), pdMS_TO_TICKS(100));
|
|
}
|
|
if (res!=pdPASS) {
|
|
return ERR_FAILED;
|
|
}
|
|
res = xTimerChangePeriod(timerDuration, pdMS_TO_TICKS(durationMs), pdMS_TO_TICKS(100));
|
|
if (res!=pdPASS) {
|
|
return ERR_FAILED;
|
|
}
|
|
res = xTimerStart(timerToggle, pdMS_TO_TICKS(100));
|
|
if (res!=pdPASS) {
|
|
return ERR_FAILED;
|
|
}
|
|
res = xTimerStart(timerDuration, pdMS_TO_TICKS(100));
|
|
if (res!=pdPASS) {
|
|
return ERR_FAILED;
|
|
}
|
|
return ERR_OK;
|
|
#else
|
|
if (trgInfo.buzIterationCntr==0) { /* only if buzzer is not running right now */
|
|
McuGPIO_SetHigh(BUZ_Pin); /* turn buzzer on */
|
|
trgInfo.buzPeriodTicks = (1000*TRG_TICKS_MS)/freq;
|
|
trgInfo.buzIterationCntr = durationMs/TRG_TICKS_MS/trgInfo.buzPeriodTicks;
|
|
return TRG_SetTrigger(TRG_BUZ_BEEP, trgInfo.buzPeriodTicks, BUZ_Toggle, (void*)&trgInfo);
|
|
} else {
|
|
return ERR_BUSY;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void BUZ_Play(void *dataPtr) {
|
|
MelodyDesc *melody = (MelodyDesc*)dataPtr;
|
|
|
|
/* play tune */
|
|
(void)BUZ_Beep(melody->melody[melody->idx].freq, melody->melody[melody->idx].ms);
|
|
melody->idx++;
|
|
if (melody->idx<melody->maxIdx) { /* not reached end of tune? */
|
|
#if BUZZER_USE_FREERTOS_TIMER
|
|
/* nothing needed */
|
|
#else
|
|
TRG_SetTrigger(TRG_BUZ_TUNE, melody->melody[melody->idx-1].ms/TRG_TICKS_MS, BUZ_Play, (void*)melody);
|
|
#endif
|
|
} else { /* end of tune */
|
|
#if BUZZER_USE_FREERTOS_TIMER
|
|
playingMelody = NULL;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void BUZ_StopTune(void) {
|
|
#if BUZZER_USE_FREERTOS_TIMER
|
|
(void)xTimerStop(timerToggle, pdMS_TO_TICKS(100));
|
|
(void)xTimerStop(timerDuration, pdMS_TO_TICKS(100));
|
|
#else
|
|
TRG_StopTrigger(TRG_BUZ_TUNE);
|
|
#endif
|
|
}
|
|
|
|
void BUZ_StopBeep(void) {
|
|
#if BUZZER_USE_FREERTOS_TIMER
|
|
(void)xTimerStop(timerToggle, pdMS_TO_TICKS(100));
|
|
(void)xTimerStop(timerDuration, pdMS_TO_TICKS(100));
|
|
#else
|
|
trgInfo.buzIterationCntr = 0;
|
|
TRG_StopTrigger(TRG_BUZ_BEEP);
|
|
#endif
|
|
}
|
|
|
|
uint8_t BUZ_PlayTune(BUZ_Tunes tune) {
|
|
if (tune>=BUZ_TUNE_NOF_TUNES) {
|
|
return ERR_OVERFLOW;
|
|
}
|
|
BUZ_Melodies[tune].idx = 0; /* reset index */
|
|
#if BUZZER_USE_FREERTOS_TIMER
|
|
playingMelody = &BUZ_Melodies[tune];
|
|
BUZ_Play((void*)playingMelody);
|
|
return ERR_OK;
|
|
#else
|
|
return TRG_SetTrigger(TRG_BUZ_TUNE, 0, BUZ_Play, (void*)&BUZ_Melodies[tune]);
|
|
#endif
|
|
}
|
|
|
|
#if BUZZER_USE_FREERTOS_TIMER
|
|
static void vTimerCallbackToggle(TimerHandle_t pxTimer) {
|
|
(void)pxTimer; /* not used */
|
|
/* called with TIMER_PERIOD_MS while playing */
|
|
McuGPIO_Toggle(BUZ_Pin);
|
|
}
|
|
|
|
static void vTimerCallbackDuration(TimerHandle_t pxTimer) {
|
|
/* called with TIMER_PERIOD_MS while playing */
|
|
(void)pxTimer; /* not used */
|
|
(void)xTimerStop(timerToggle, pdMS_TO_TICKS(100));
|
|
if (playingMelody!=NULL) {
|
|
BUZ_Play((void*)playingMelody); /* next tune */
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if PL_CONFIG_USE_SHELL
|
|
static uint8_t BUZ_PrintHelp(const McuShell_StdIOType *io) {
|
|
McuShell_SendHelpStr((unsigned char*)"buzzer", (unsigned char*)"Group of buzzer commands\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows buzzer help or status\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" buz <freq> <time>", (unsigned char*)"Beep for time (ms) and frequency (Hz)\r\n", io->stdOut);
|
|
McuShell_SendHelpStr((unsigned char*)" play tune <tune>", (unsigned char*)"Play tune, default tune 0\r\n", io->stdOut);
|
|
return ERR_OK;
|
|
}
|
|
|
|
static uint8_t BUZ_PrintStatus(const McuShell_StdIOType *io) {
|
|
unsigned char buf[16];
|
|
McuShell_SendStatusStr((unsigned char*)"buzzer", (unsigned char*)"Buzzer status\r\n", io->stdOut);
|
|
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0 - ");
|
|
McuUtility_strcatNum32u(buf, sizeof(buf), BUZ_TUNE_NOF_TUNES-1);
|
|
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
|
|
McuShell_SendStatusStr((unsigned char*)" tunes", buf, io->stdOut);
|
|
return ERR_OK;
|
|
}
|
|
|
|
uint8_t BUZ_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io) {
|
|
const unsigned char *p;
|
|
uint16_t freq, duration;
|
|
|
|
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"buzzer help")==0) {
|
|
*handled = TRUE;
|
|
return BUZ_PrintHelp(io);
|
|
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"buzzer status")==0) {
|
|
*handled = TRUE;
|
|
return BUZ_PrintStatus(io);
|
|
} else if (McuUtility_strncmp((char*)cmd, (char*)"buzzer buz ", sizeof("buzzer buz ")-1)==0) {
|
|
*handled = TRUE;
|
|
p = cmd+sizeof("buzzer buz ")-1;
|
|
if (McuUtility_ScanDecimal16uNumber(&p, &freq)==ERR_OK && McuUtility_ScanDecimal16uNumber(&p, &duration)==ERR_OK) {
|
|
if (BUZ_Beep(freq, duration)!=ERR_OK) {
|
|
McuShell_SendStr((unsigned char*)"Starting buzzer failed\r\n", io->stdErr);
|
|
return ERR_FAILED;
|
|
}
|
|
return ERR_OK;
|
|
}
|
|
} else if (McuUtility_strncmp((char*)cmd, (char*)"buzzer play tune ", sizeof("buzzer play tune ")-1)==0) {
|
|
uint8_t tune;
|
|
|
|
*handled = TRUE;
|
|
p = cmd+sizeof("buzzer play tune ")-1;
|
|
McuUtility_ScanDecimal8uNumber(&p, &tune);
|
|
if (tune<BUZ_TUNE_NOF_TUNES) {
|
|
return BUZ_PlayTune(tune);
|
|
} else {
|
|
return ERR_FAILED;
|
|
}
|
|
} else if (McuUtility_strcmp((char*)cmd, (char*)"buzzer play tune")==0) {
|
|
*handled = TRUE;
|
|
return BUZ_PlayTune(BUZ_TUNE_WELCOME);
|
|
}
|
|
return ERR_OK;
|
|
}
|
|
#endif /* PL_CONFIG_USE_SHELL */
|
|
|
|
void BUZ_Deinit(void) {
|
|
#if BUZZER_USE_FREERTOS_TIMER
|
|
(void)xTimerDelete(timerToggle, pdMS_TO_TICKS(100));
|
|
timerToggle = NULL;
|
|
(void)xTimerDelete(timerDuration, pdMS_TO_TICKS(100));
|
|
timerDuration = NULL;
|
|
#else
|
|
/* nothing special to do */
|
|
#endif
|
|
BUZ_Pin = McuGPIO_DeinitGPIO(BUZ_Pin);
|
|
}
|
|
|
|
void BUZ_Init(void) {
|
|
McuGPIO_Config_t gpioConfig;
|
|
|
|
McuGPIO_GetDefaultConfig(&gpioConfig);
|
|
/* IR on/off: PTC3, Low active */
|
|
gpioConfig.hw.gpio = GPIOC;
|
|
gpioConfig.hw.port = PORTC;
|
|
gpioConfig.hw.pin = 3;
|
|
gpioConfig.isInput = false;
|
|
gpioConfig.isHighOnInit = true;
|
|
BUZ_Pin = McuGPIO_InitGPIO(&gpioConfig);
|
|
#if BUZZER_USE_FREERTOS_TIMER
|
|
timerToggle = xTimerCreate(
|
|
"buzToggle", /* name */
|
|
1, /* period/time */
|
|
pdTRUE, /* auto reload */
|
|
(void*)0, /* timer ID */
|
|
vTimerCallbackToggle); /* callback */
|
|
if (timerToggle==NULL) {
|
|
for(;;); /* failure! */
|
|
}
|
|
timerDuration = xTimerCreate(
|
|
"buzDuration", /* name */
|
|
1, /* period/time */
|
|
pdFALSE, /* auto reload */
|
|
(void*)0, /* timer ID */
|
|
vTimerCallbackDuration); /* callback */
|
|
if (timerDuration==NULL) {
|
|
for(;;); /* failure! */
|
|
}
|
|
#else
|
|
trgInfo.buzPeriodTicks = 0;
|
|
trgInfo.buzIterationCntr = 0;
|
|
#endif
|
|
}
|
|
#endif /* PL_CONFIG_HAS_BUZZER */
|
|
|