/********************* (C) COPYRIGHT 2007 RAISONANCE S.A.S. *******************/ | |
/** | |
* | |
* @file buzzer.c | |
* @brief Buzzer dedicated functions with RTTTL format support. | |
* @author IB | |
* @date 07/2007 | |
* | |
**/ | |
/******************************************************************************/ | |
/* Includes ------------------------------------------------------------------*/ | |
#include "circle.h" | |
/// @cond Internal | |
/* Private typedef -----------------------------------------------------------*/ | |
/*! Octaves */ | |
enum eOctave { | |
OCT_440 = 0, /*!< o = 5 */ | |
OCT_880 = 1, /*!< o = 6 */ | |
OCT_1760 = 2, /*!< o = 7 */ | |
OCT_3520 = 3, /*!< o = 8 */ | |
OCT_7040 = 4 /*!< o = 9 */ | |
} octave; | |
/*! Notes */ | |
enum eNotes { | |
NOTE_PAUSE = 0, /*!< P */ | |
NOTE_LA = 1, /*!< A */ | |
NOTE_LA_H = 8+1, /*!< A# */ | |
NOTE_SI = 2, /*!< B */ | |
NOTE_DO = 3, /*!< C */ | |
NOTE_DO_H = 8+3, /*!< C# */ | |
NOTE_RE = 4, /*!< D */ | |
NOTE_RE_H = 8+4, /*!< D# */ | |
NOTE_MI = 5, /*!< E */ | |
NOTE_FA = 6, /*!< F */ | |
NOTE_FA_H = 8+6, /*!< F# */ | |
NOTE_SOL = 7, /*!< G */ | |
NOTE_SOL_H = 8+7 /*!< G# */ | |
} note; | |
/* Private define ------------------------------------------------------------*/ | |
#define BUZZER_SHORTBEEP_DURATION 100 | |
#define BUZZER_LONGBEEP_DURATION 1000 | |
#define RTTTL_SEP ':' | |
/* Private macro -------------------------------------------------------------*/ | |
/* Private variables ---------------------------------------------------------*/ | |
int buzz_counter = 0; | |
int buzz_in_progress = 0; | |
static TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; | |
static TIM_OCInitTypeDef TIM_OCInitStructure; | |
u16 CCR_Val = 0x2EE0; | |
enum BUZZER_mode Buzzer_Mode = BUZZER_UNDEF; | |
u32 Buzzer_Counter = 0; | |
// For the melody. | |
const u8* CurrentMelody = 0; | |
const u8* CurrentMelodySTART = 0; | |
u8 DefaultOctave = OCT_880; | |
u8 DefaultDuration = 4; | |
u16 DefaultBeats = 63; | |
u16 Note_Freq [16] = { | |
0, //pause | |
440, //A=LA | |
494, //B=SI | |
524, //C=DO | |
588, //D=RE | |
660, //E=MI | |
698, //F=FA | |
784, //G=SOL | |
0, // "8+n" for "NOTE#" | |
466, //A#=LA# | |
0, | |
544, //C#=DO# | |
622, //D#=RE# | |
0, | |
740, //F#=FA# | |
830 //G#=SOL# | |
}; | |
/* Private function prototypes -----------------------------------------------*/ | |
static void PlayMusic( void ); | |
static void BUZZER_SetFrequency( u16 freq ); | |
/* Private functions ---------------------------------------------------------*/ | |
/******************************************************************************* | |
* | |
* PlayMusic | |
* | |
*******************************************************************************/ | |
/** | |
* | |
* Play the next note of the current melody. | |
* | |
**/ | |
/******************************************************************************/ | |
static void PlayMusic( void ) | |
{ | |
u8 duration = DefaultDuration; | |
u8 c; | |
// Discard blank characters | |
while ( *CurrentMelody == ' ') | |
{ | |
CurrentMelody++; | |
} | |
// Check whether a duration is present. | |
if ( (*CurrentMelody > '0') && (*CurrentMelody < '9') ) | |
{ | |
duration = *CurrentMelody++ - '0'; | |
if ( (*CurrentMelody > '0') && (*CurrentMelody < '9') ) | |
{ | |
duration *= 10; | |
duration += (*CurrentMelody++ - '0'); | |
} | |
} | |
Buzzer_Counter = ( (32/duration) * 256L * 32L) / DefaultBeats; | |
Buzzer_Counter*= (RCC_ClockFreq.SYSCLK_Frequency / 12000000L); //Adapt to HCLK1 | |
//read the note | |
c = *CurrentMelody++; | |
if ( (c >= 'a') && (c <= 'z') ) | |
{ | |
c+=('A'-'a'); | |
} | |
if ( c == 'P' ) | |
{ | |
note = NOTE_PAUSE; | |
} | |
else if ( (c >= 'A') && (c <= 'G') ) | |
{ | |
note = (c - 'A') + NOTE_LA; | |
if ( *CurrentMelody == '#' ) | |
{ | |
note|=0x8; | |
CurrentMelody++; | |
} | |
} | |
octave = DefaultOctave; | |
c = *CurrentMelody; | |
if ( (c>= '5') && (c<= '8') ) | |
{ | |
octave = OCT_440 + (c-'5'); | |
CurrentMelody++; | |
} | |
BUZZER_SetFrequency ( (Note_Freq [ note ] * (1<<octave))); | |
//discard delimiter and ignore special duration | |
while ( (c = *CurrentMelody++) != 0 ) | |
{ | |
if ( c==',') | |
break; | |
} | |
if ( *(CurrentMelody-1)==0 ) | |
{ | |
CurrentMelody = 0; | |
} | |
if ( c == 0 ) | |
{ | |
BUZZER_SetMode ( BUZZER_OFF ); | |
} | |
} | |
/*********************************************************************************** | |
* | |
* BUZZER_SetFrequency | |
* | |
************************************************************************************/ | |
/** | |
* | |
* Set the buzzer frequency | |
* | |
* @param[in] freq New frequency. | |
* | |
**/ | |
/********************************************************************************/ | |
void BUZZER_SetFrequency ( u16 freq ) | |
{ | |
/* Calculate the frequency (depend on the PCLK1 clock value) */ | |
CCR_Val = (RCC_ClockFreq.PCLK1_Frequency / freq); | |
TIM_TimeBaseStructure.TIM_Period = CCR_Val * 2; | |
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; | |
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; | |
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; | |
TIM_TimeBaseInit( TIM3, &TIM_TimeBaseStructure ); | |
/* Output Compare Toggle Mode configuration: Channel3 */ | |
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; | |
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; | |
TIM_OCInitStructure.TIM_Pulse = CCR_Val; | |
TIM_OC3Init( TIM3, &TIM_OCInitStructure ); | |
TIM_OC3PreloadConfig( TIM3, TIM_OCPreload_Enable ); | |
} | |
/* Public functions for CircleOS ---------------------------------------------*/ | |
/******************************************************************************* | |
* | |
* BUZZER_Init | |
* | |
*******************************************************************************/ | |
/** | |
* | |
* Buzzer Initialization | |
* | |
* @attention This function must <b>NOT</b> be called by the user. | |
* | |
**/ | |
/******************************************************************************/ | |
void BUZZER_Init( void ) | |
{ | |
GPIO_InitTypeDef GPIO_InitStructure; | |
/* Enable GPIOB clock */ | |
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); | |
/* GPIOB Configuration: TIM3 3in Output */ | |
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; | |
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; | |
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; | |
GPIO_Init( GPIOB, &GPIO_InitStructure ); | |
/* TIM3 Configuration ------------------------------------------------------*/ | |
/* TIM3CLK = 18 MHz, Prescaler = 0x0, TIM3 counter clock = 18 MHz */ | |
/* CC update rate = TIM3 counter clock / (2* CCR_Val) ~= 750 Hz */ | |
/* Enable TIM3 clock */ | |
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3, ENABLE ); | |
TIM_DeInit( TIM3 ); | |
TIM_TimeBaseStructInit( &TIM_TimeBaseStructure ); | |
TIM_OCStructInit( &TIM_OCInitStructure ); | |
/* Time base configuration */ | |
TIM_TimeBaseStructure.TIM_Period = 0xFFFF; | |
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; | |
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; | |
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; | |
TIM_TimeBaseInit( TIM3, &TIM_TimeBaseStructure ); | |
/* Output Compare Toggle Mode configuration: Channel3 */ | |
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; | |
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; | |
TIM_OCInitStructure.TIM_Pulse = CCR_Val; | |
TIM_OC3Init( TIM3, &TIM_OCInitStructure ); | |
TIM_OC3PreloadConfig( TIM3, TIM_OCPreload_Disable ); | |
BUZZER_SetFrequency( 440 ); | |
/* Enable TIM3 IT */ | |
TIM_ITConfig( TIM3, TIM_IT_CC3, ENABLE ); | |
Buzzer_Mode = BUZZER_OFF; | |
} | |
/******************************************************************************* | |
* | |
* BUZZER_Handler | |
* | |
*******************************************************************************/ | |
/** | |
* | |
* Called by the CircleOS scheduler to manage Buzzer tasks. | |
* | |
* @attention This function must <b>NOT</b> be called by the user. | |
* | |
**/ | |
/******************************************************************************/ | |
void BUZZER_Handler( void ) | |
{ | |
int fSetOFF = 0; | |
if ( Buzzer_Mode == BUZZER_PLAYMUSIC ) | |
{ | |
if ( Buzzer_Counter == 0 ) | |
{ | |
PlayMusic(); | |
} | |
else | |
{ | |
Buzzer_Counter--; | |
} | |
return; | |
} | |
else if ( Buzzer_Mode == BUZZER_SHORTBEEP ) | |
{ | |
if ( Buzzer_Counter++ == (BUZZER_SHORTBEEP_DURATION) ) | |
{ | |
Buzzer_Mode = BUZZER_OFF; | |
return; | |
} | |
if ( Buzzer_Counter == (BUZZER_SHORTBEEP_DURATION/2) ) | |
{ | |
fSetOFF = 1; | |
} | |
} | |
else if ( Buzzer_Mode == BUZZER_LONGBEEP ) | |
{ | |
if ( Buzzer_Counter++ == (BUZZER_LONGBEEP_DURATION) ) | |
{ | |
Buzzer_Mode = BUZZER_OFF; | |
return; | |
} | |
if ( Buzzer_Counter > (BUZZER_LONGBEEP_DURATION/2) ) | |
{ | |
fSetOFF = 1; | |
} | |
} | |
if ( fSetOFF == 1 ) | |
{ | |
TIM_Cmd(TIM3, DISABLE); | |
} | |
} | |
/// @endcond | |
/* Public functions ----------------------------------------------------------*/ | |
/******************************************************************************* | |
* | |
* BUZZER_GetMode | |
* | |
*******************************************************************************/ | |
/** | |
* | |
* Get the current buzzer mode. | |
* | |
* @return Current buzzer mode. | |
* | |
**/ | |
/******************************************************************************/ | |
enum BUZZER_mode BUZZER_GetMode( void ) | |
{ | |
return Buzzer_Mode; | |
} | |
/******************************************************************************* | |
* | |
* BUZZER_SetMode | |
* | |
*******************************************************************************/ | |
/** | |
* | |
* Set new buzzer mode | |
* | |
* @param[in] mode New buzzer mode. | |
* | |
**/ | |
/******************************************************************************/ | |
void BUZZER_SetMode( enum BUZZER_mode mode ) | |
{ | |
Buzzer_Mode = mode; | |
Buzzer_Counter = 0; | |
switch ( mode ) | |
{ | |
case BUZZER_PLAYMUSIC : | |
PlayMusic(); //start melody | |
/* no break */ | |
case BUZZER_LONGBEEP : | |
case BUZZER_SHORTBEEP : | |
case BUZZER_ON : | |
TIM_Cmd( TIM3, ENABLE ); | |
break; | |
case BUZZER_OFF : | |
TIM_Cmd( TIM3, DISABLE ); | |
break; | |
} | |
} | |
/******************************************************************************* | |
* | |
* BUZZER_PlayMusic | |
* | |
*******************************************************************************/ | |
/** | |
* | |
* Plays the provided melody that follows the RTTTL Format. | |
* | |
* Official Specification | |
* @verbatim | |
<ringing-tones-text-transfer-language> := | |
<name> <sep> [<defaults>] <sep> <note-command>+ | |
<name> := <char>+ ; maximum name length 10 characters | |
<sep> := ":" | |
<defaults> := | |
<def-note-duration> | | |
<def-note-scale> | | |
<def-beats> | |
<def-note-duration> := "d=" <duration> | |
<def-note-scale> := "o=" <scale> | |
<def-beats> := "b=" <beats-per-minute> | |
<beats-per-minute> := 25,28,...,900 ; decimal value | |
; If not specified, defaults are | |
; | |
; 4 = duration | |
; 6 = scale | |
; 63 = beats-per-minute | |
<note-command> := | |
[<duration>] <note> [<scale>] [<special-duration>] <delimiter> | |
<duration> := | |
"1" | ; Full 1/1 note | |
"2" | ; 1/2 note | |
"4" | ; 1/4 note | |
"8" | ; 1/8 note | |
"16" | ; 1/16 note | |
"32" | ; 1/32 note | |
<note> := | |
"P" | ; pause | |
"C" | | |
"C#" | | |
"D" | | |
"D#" | | |
"E" | | |
"F" | | |
"F#" | | |
"G" | | |
"G#" | | |
"A" | | |
"A#" | | |
"B" | |
<scale> := | |
"5" | ; Note A is 440Hz | |
"6" | ; Note A is 880Hz | |
"7" | ; Note A is 1.76 kHz | |
"8" ; Note A is 3.52 kHz | |
<special-duration> := | |
"." ; Dotted note | |
<delimiter> := "," | |
@endverbatim | |
* | |
* @param[in] melody New melody to play on buzzer. | |
* | |
**/ | |
/******************************************************************************/ | |
void BUZZER_PlayMusic (const u8 *melody ) | |
{ | |
u8 c; | |
u8 default_id = 0; | |
u16 default_val = 0; | |
DefaultOctave = OCT_880; // Default for the default Octave. | |
DefaultDuration = 4; // Default for the default Duration. | |
DefaultBeats = 63; | |
CurrentMelody = melody; | |
CurrentMelodySTART = melody; | |
while( *CurrentMelody != RTTTL_SEP ) | |
{ | |
if( *CurrentMelody == 0 ) | |
{ | |
return; | |
} | |
// Discard the melody name. | |
CurrentMelody++; | |
} | |
// Now read the defaults if any. | |
for( ++CurrentMelody; *CurrentMelody != RTTTL_SEP; CurrentMelody++ ) | |
{ | |
if( *CurrentMelody == 0 ) | |
{ | |
return; | |
} | |
// Discard any blank. | |
while ( *CurrentMelody == ' ' ) | |
{ | |
CurrentMelody++; | |
} | |
c = *CurrentMelody; | |
if ( c == RTTTL_SEP ) | |
{ | |
break; | |
} | |
if ( (c >= 'a') && (c <= 'z') ) | |
{ | |
c+=('A'-'a'); | |
} | |
if ( (c >= 'A') && (c <= 'Z') ) | |
{ | |
default_id = c; | |
continue; | |
} | |
if ( (c >= '0') && (c <= '9') ) | |
{ | |
default_val *= 10; | |
default_val += (c-'0'); | |
c = * (CurrentMelody + 1 ); | |
if ( (c >= '0') && (c <= '9') ) | |
{ | |
continue; | |
} | |
if ( default_id == 'D' ) | |
{ | |
DefaultDuration = default_val; | |
} | |
else if ( default_id == 'O' ) | |
{ | |
DefaultOctave = default_val - 5; | |
if ( DefaultOctave > OCT_7040 ) | |
DefaultOctave = OCT_440; | |
} | |
else if ( default_id == 'B' ) | |
{ | |
DefaultBeats = default_val; | |
if ( ( DefaultBeats == 0 ) || ( DefaultBeats > 500 ) ) | |
DefaultBeats = 63; | |
} | |
default_val = 0; | |
default_id = 0; | |
} | |
} | |
BUZZER_SetMode( BUZZER_PLAYMUSIC ); | |
} |