-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.c
127 lines (119 loc) · 3.15 KB
/
main.c
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
/**
* 无源蜂鸣器发声原理:
* 无源蜂鸣器内部不带内部震荡源,因此需要对51单片机发出的方波信号进行放大
*才能将无源蜂鸣器进行驱动。
* 发出固定频率声音的方法:
* 上述提到需要使用方波进行驱动,因此只需要51单片机能产生出对应的方波信号即可
* 已知标准音A = 440HZ,通过12平均律的计算方法F(y) = x * (y-x的音数)*1.059463。
* 可以计算出一组有效的,满足12平均律的频率。
* --------------------------------------------------
*| C | D | E | F | G | A | B | C |
*|--------------------------------------------------|
*|2093 |2349 | 2636|2793 | 3135|3519 | 3951 | 4185|
* --------------------------------------------------
* 频率是指在1s内震动的次数,公式为f = 1/T,T为震动一次的时间。使用51单片机控制频率
* 的关键是在T时间内保持为高电平或者是低电平,然后T结束后立刻反转电平,达到震荡的目的。
* 根据频率计算公式得出f和T的关系
*-------------------------------------------------------
*|音名| C | D | E | F | G | A | B | C |
*|----|--------------------------------------------------|
*| f |2093 |2349 | 2636|2793 | 3135|3519 | 3951 | 4185|
*|----|--------------------------------------------------|
*| T | 477 | 425 | 379 | 357 | 318 |284 | 253 | 238 |
* -------------------------------------------------------
* -------- -------
* | T | | T |
* | | | |
* | | T | | T
*------ -------- -------
*/
#include "reg52.h"
// 对数据类型重定义
typedef unsigned char u8;
typedef unsigned int u16;
// 已知51开发板的蜂鸣器输出端口连接的是p15
sbit beep= P1^5;
/**
* 震动一次需要花费的时间,单位us
* 这个地方与表中的数据少10倍的原因是一次i--的指令执行需要花费10us,不能再更精细了,所以
* 直接转换除以10避免无意义的计算。
* 0 C D E F G A B C
*/
int T[] = {0,48,43,38,36,32,28,25};
/**
* 延迟函数,执行一次t--的时间花费约为10us
* 例如 t=10,则delay(10)将会延迟大约100us
*/
void delay(u16 t) {
while(t--);
}
/**
* 发声函数
* v 音调,对应T[],如果v = 1,则表示需要发C音
* t 音的持续时间
* 如果一个音要发500ms,得计算出该音频率下要进行多少次高低电平翻转
* 反转次数 = 500000us / T, 函数中的计算做了单位转换防止u16溢出
* 然后对电平进行控制,使得在一段时间内(通过延时函数保证)输出一样的电平
* 之后立刻进行电平反转
*/
void play(u8 v,u16 time) {
u16 t= time / T[v] * 100;
while(t--) { // 控制反转次数
beep=~beep; // 电平反转
delay(T[v]); // 维护电平输出时长
}
}
/**
* 小星星的简谱:
* 4/4
* C C G G | A A G - | F F E E | D D C -
* G G F F | E E D - | G G F F | E E D -
* C C G G | A A G - | F F E E | D D C -
*/
/**
* 播放A段 C C G G | A A G - | F F E E | D D C -
*/
void playA() {
play(1,500);
play(1,500);
play(5,500);
play(5,500);
play(6,500);
play(6,500);
play(5,1000);
// FFEEDDC
play(4,500);
play(4,500);
play(3,500);
play(3,500);
play(2,500);
play(2,500);
play(1,1000);
}
/**
* 播放B段 G G F F | E E D - | G G F F | E E D -
*/
void playB() {
int i=0;
//GGFFEED
for(i=0;i<2;i++) {
play(5,500);
play(5,500);
play(4,500);
play(4,500);
play(3,500);
play(3,500);
play(2,1000);
}
}
/**
* 主函数,进行小星星播放
*/
void main() {
while(1) {
playA();
playB();
playA();
break;
}
}