# 原理

## 蜂鸣器

int i;
while (1) {
for (i = 0; i < 10; ++i); // 改变循环次数可以改变方波频率
P1_0 = 1;
for (i = 0; i < 10; ++i);
P1_0 = 0;
}


## 乐谱转成循环次数

import json

# 晶振频率(Hz)
CRYSTAL_FREQUENCY = 11059200
# 计数周期（机器周期）(s)
COUNT_PERIOD = 1 / (CRYSTAL_FREQUENCY / 12)
# 一次循环几个机器周期，通过定时器实验得到
COUNT_PER_LOOP = 38
# 一次循环时间(ms)
MS_PER_LOOP = COUNT_PERIOD * 1000 * COUNT_PER_LOOP

def tone_to_loop_count(notes, output_path):
res = []
for frequency, duration in notes:
if frequency == 0:
# 延时
loop_count = 65535
period_count = round(duration / MS_PER_LOOP)
else:
period = 1000 / frequency
loop_count = round(period / 2 / MS_PER_LOOP)
period_count = round(duration / period)
# 最低频率0.185，loop_count = 65534
assert 0 <= loop_count <= 65535, f'frequency = {frequency}, loop_count = {loop_count}，不在unsigned int范围内'
# 把一个时间过长的音符拆成多个音符
while period_count > 65535:
res.append((loop_count, 65535))
period_count -= 65535
res.append((loop_count, period_count))

with open(output_path + '.h', 'w') as f:
f.write(f"""#define DELAY_COUNT 65535
#define NOTES_LEN {len(res)}
extern const unsigned int code notes[][2];
""")

with open(output_path + '.c', 'w') as f:
f.write('const unsigned int code notes[][2] = {\n')
for i in range(0, len(res), 6):
f.write('\t')
for loop_count, period_count in res[i: i + 6]:
f.write(f'{{{loop_count}, {period_count}}}, ')
f.write('\n')
f.write('};\n')

def main():
with open('beep.json') as f:
tone_to_loop_count(notes, '../beep/music_data')

if __name__ == '__main__':
main()


# 单片机代码

#include <AT89X51.H>

#include "music_data.h"

// 引脚定义
#define beepOut P1_0

int main() {
beepOut = 0;
while (1) {
unsigned int i, j, k;
for (i = 0; i < NOTES_LEN; ++i) {
if (notes[i][0] == DELAY_COUNT) // 延时
for (j = 0; j < notes[i][1]; ++j);
else {
for (j = 0; j < notes[i][1]; ++j) {
for (k = 0; k < notes[i][0]; ++k);
beepOut = 1;
for (k = 0; k < notes[i][0]; ++k);
beepOut = 0;
}
}
}
}

return 0;
}