/** * \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->idxmaxIdx) { /* 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