/********************************************************************** * Copyright: (C)2024 LingYun IoT System Studio * Author: GuoWenxue * Description: ISKBoard buzzer/beeper driver and music play * * ChangeLog: * Version Date Author Description * V1.0.0 2024.08.29 GuoWenxue Release initial version ***********************************************************************/ #include "tim.h" #include "buzzer.h" #define ARRAY_SIZE(song) (sizeof(song)/sizeof(song[0])) /* 音符频率定义(Hz) */ #define NOTE_C4 524 #define NOTE_D4 588 #define NOTE_E4 660 #define NOTE_F4 698 #define NOTE_G4 784 #define NOTE_A4 880 #define NOTE_B4 988 #define NOTE_C5 1046 #define NOTE_D5 1174 #define NOTE_E5 1318 #define NOTE_F5 1396 #define NOTE_G5 1568 #define NOTE_A5 1760 #define NOTE_B5 1976 #define NOTE_C6 2093 #define REST 0 #define BEAT 400 /* 一拍时长(ms),可自行调整节奏快慢 */ /* 《小星星》简谱: * 1 1 5 5 6 6 5 - 4 4 3 3 2 2 1 * 5 5 4 4 3 3 2 - 5 5 4 4 3 3 2 * 1 1 5 5 6 6 5 - 4 4 3 3 2 2 1 * (C=1, D=2, E=3, F=4, G=5, A=6, B=7) */ const Note_t twinkle[] = { /* Twinkle twinkle little star */ {NOTE_C4, BEAT}, {NOTE_C4, BEAT}, {NOTE_G4, BEAT}, {NOTE_G4, BEAT}, {NOTE_A4, BEAT}, {NOTE_A4, BEAT}, {NOTE_G4, BEAT * 2}, /* How I wonder what you are */ {NOTE_F4, BEAT}, {NOTE_F4, BEAT}, {NOTE_E4, BEAT}, {NOTE_E4, BEAT}, {NOTE_D4, BEAT}, {NOTE_D4, BEAT}, {NOTE_C4, BEAT * 2}, /* Up above the world so high */ {NOTE_G4, BEAT}, {NOTE_G4, BEAT}, {NOTE_F4, BEAT}, {NOTE_F4, BEAT}, {NOTE_E4, BEAT}, {NOTE_E4, BEAT}, {NOTE_D4, BEAT * 2}, /* Like a diamond in the sky */ {NOTE_G4, BEAT}, {NOTE_G4, BEAT}, {NOTE_F4, BEAT}, {NOTE_F4, BEAT}, {NOTE_E4, BEAT}, {NOTE_E4, BEAT}, {NOTE_D4, BEAT * 2}, /* Twinkle twinkle little star (repeat) */ {NOTE_C4, BEAT}, {NOTE_C4, BEAT}, {NOTE_G4, BEAT}, {NOTE_G4, BEAT}, {NOTE_A4, BEAT}, {NOTE_A4, BEAT}, {NOTE_G4, BEAT * 2}, /* How I wonder what you are (repeat) */ {NOTE_F4, BEAT}, {NOTE_F4, BEAT}, {NOTE_E4, BEAT}, {NOTE_E4, BEAT}, {NOTE_D4, BEAT}, {NOTE_D4, BEAT}, {NOTE_C4, BEAT * 2}, }; const uint32_t twinkle_count = ARRAY_SIZE(twinkle); /* 《祝你生日快乐》(Happy Birthday to You) * 该曲旋律/歌词经美国法院2015-2016年裁定确认为公有领域作品,可放心使用。 * 简谱: 5 5 6 5 1' 7 | 5 5 6 5 2' 1' | 5 5 5' 3' 1' 7 6 | 4' 4' 3' 1' 2' 1' */ const Note_t happy_birthday[] = { /* Happy Birthday to you */ {NOTE_G4, BEAT / 2}, {NOTE_G4, BEAT / 2}, {NOTE_A4, BEAT}, {NOTE_G4, BEAT}, {NOTE_C5, BEAT}, {NOTE_B4, BEAT * 2}, /* Happy Birthday to you */ {NOTE_G4, BEAT / 2}, {NOTE_G4, BEAT / 2}, {NOTE_A4, BEAT}, {NOTE_G4, BEAT}, {NOTE_D5, BEAT}, {NOTE_C5, BEAT * 2}, /* Happy Birthday dear friend */ {NOTE_G4, BEAT / 2}, {NOTE_G4, BEAT / 2}, {NOTE_G5, BEAT}, {NOTE_E5, BEAT}, {NOTE_C5, BEAT}, {NOTE_B4, BEAT}, {NOTE_A4, BEAT * 2}, /* Happy Birthday to you */ {NOTE_F5, BEAT / 2}, {NOTE_F5, BEAT / 2}, {NOTE_E5, BEAT}, {NOTE_C5, BEAT}, {NOTE_D5, BEAT}, {NOTE_C5, BEAT * 2}, /* 结尾留白 */ {REST, BEAT * 2}, }; const uint32_t happy_birthday_count = ARRAY_SIZE(happy_birthday); /* 《Jingle Bells》(铃儿响叮当) * 1857年James Lord Pierpont创作,早已进入公有领域。 * (下面是常见的简化版本,节奏为大致还原,供播放测试使用) */ const Note_t jingle_bells[] = { /* Jingle bells, jingle bells, jingle all the way */ {NOTE_E4, BEAT}, {NOTE_E4, BEAT}, {NOTE_E4, BEAT * 2}, {NOTE_E4, BEAT}, {NOTE_E4, BEAT}, {NOTE_E4, BEAT * 2}, {NOTE_E4, BEAT}, {NOTE_G4, BEAT}, {NOTE_C4, BEAT}, {NOTE_D4, BEAT}, {NOTE_E4, BEAT * 4}, /* Oh what fun it is to ride in a one-horse open sleigh */ {NOTE_F4, BEAT}, {NOTE_F4, BEAT}, {NOTE_F4, BEAT}, {NOTE_F4, BEAT}, {NOTE_F4, BEAT}, {NOTE_E4, BEAT}, {NOTE_E4, BEAT}, {NOTE_E4, BEAT / 2}, {NOTE_E4, BEAT / 2}, {NOTE_E4, BEAT}, {NOTE_D4, BEAT}, {NOTE_D4, BEAT}, {NOTE_E4, BEAT}, {NOTE_D4, BEAT * 2}, {NOTE_G4, BEAT * 2}, /* 结尾留白 */ {REST, BEAT * 2}, }; const uint32_t jingle_bells_count = ARRAY_SIZE(jingle_bells); /* *+----------------------------+ *| PWM buzzer API | *+----------------------------+ */ /** * @brief 设置蜂鸣器发声频率(占空比固定50%) */ static inline void Buzzer_SetFreq(uint32_t freq) { /* TIM1 初始化 PSC = 79,计数时钟 = 80MHz/80 = 1MHz */ uint32_t arr = 1000000U / freq - 1U; __HAL_TIM_SET_AUTORELOAD(&htim1, arr); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, arr / 2U); __HAL_TIM_SET_COUNTER(&htim1, 0); } /** * @brief 让蜂鸣器响 $times 次,每次间隔 $interval 毫秒 */ void beep_start(uint16_t times, uint16_t interval) { Buzzer_SetFreq(2700); while( times-- ) { /* Start buzzer */ HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4); HAL_Delay(interval); /* Stop buzzer */ HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_4); HAL_Delay(interval); } } /** * @brief 播放单个音符 */ static void Buzzer_PlayNote(uint32_t freq, uint32_t duration_ms) { if (freq == 0) { HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_4); HAL_Delay(duration_ms); return; } Buzzer_SetFreq(freq); HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4); HAL_Delay(duration_ms - 30); HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_4); HAL_Delay(30); } /** * @brief 播放歌曲 */ void Play_Song(const Note_t *song, uint32_t note_count) { for (uint32_t i = 0; i < note_count; i++) { Buzzer_PlayNote(song[i].freq, song[i].duration); } }