Advanced Distributed Systems module at HSLU
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

/**
* \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 */