guowenxue
2026-07-06 b9ddb1040cf68600bfc33a60edf36d91d82fcf1b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/**********************************************************************
*   Copyright: (C)2024 LingYun IoT System Studio
*      Author: GuoWenxue<guowenxue@gmail.com>
* 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);
    }
}