1 蜂鸣器发声(harib22a)

大家早上好,今天我们还要继续努力哦。

之前我们为系统添加了很多API,但那些只不过是将以前操作系统就有的功能开放给应用程序来调用而已,没什么让人眼前一亮的东西。现在看来,我们之前给操作系统编写的功能已经用光了,以后如果要为API添加新的功能,就需要同时为操作系统本身编写新的代码了。

因此我们这次来做一个“蜂鸣器发声”的功能吧。蜂鸣器发出的声音,英语叫“BEEP”,是个象声词,也就是那种哔哔哔的声音。一提到电脑上的声音,可能大家首先会想到“声卡”,不过声卡的调用实在过于复杂,新手上路难度太大,因此我们还是先来实现蜂鸣器发声吧,这个功能是所有型号的电脑都有的(如果是自己组装的电脑,只要你没忘记接上蜂鸣器的线就好)。

■■■■■

关于发声的方法,笔者从这里查阅了一下资料。

http://community.osdev.info/?(PIT)82548254)

没错,其实蜂鸣器发声和定时器一样,都是由PIT来控制的,而PIT位于芯片组中,因此所有型号的电脑都能使用它。

跟以前一样,我们还是直接来看原文中“懒人专用指南”一节的内容吧。

  • 蜂鸣器发声的控制

    • 音高操作

      • AL = 0xb6; OUT(0x43, AL);

      • AL = 设定值的低位8bit; OUT(0x42, AL);

      • AL = 设定值的高位8bit; OUT(0x42, AL);

      • 设定值为0时当作65536来处理。

      • 发声的音高为时钟除以设定值,也就是说设定值为1000时相当于发出1.19318KHz的声音;设定值为10000时相当于119.318Hz。因此设定2712即可发出约440Hz的声音1。

1 440Hz为中央C之上的A音,即国际标准音。

  • 蜂鸣器ON/OFF

    • 使用I/O端口0x61控制。

    • ON:IN(AL, 0x61); AL |= 0x03; AL &= 0x0f; OUT(0x61, AL);

    • OFF:IN(AL, 0x61); AL &= 0xd; OUT(0x61, AL);

嗯,差不多看懂了,我们来编写API吧(这里所提到的时钟不是CPU时钟,而是PIT时钟。在电脑中PIT时钟与CPU无关,频率恒定为1.19318MHz)。

蜂鸣器发声

EDX=20

DAX=声音频率(单位是mHz,即毫赫兹)

例如当EAX=4400000时,则发出440Hz的声音

频率设为0则表示停止发声

 

本次的console.c节选

  1. int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
  2. {
  3. (中略)
  4. } else if (edx == 20) {
  5. if (eax == 0) {
  6. i = io_in8(0x61);
  7. io_out8(0x61, i & 0x0d);
  8. } else {
  9. i = 1193180000 / eax;
  10. io_out8(0x43, 0xb6);
  11. io_out8(0x42, i & 0xff);
  12. io_out8(0x42, i >> 8);
  13. i = io_in8(0x61);
  14. io_out8(0x61, (i | 0x03) & 0x0f);
  15. }
  16. }
  17. return 0;
  18. }

■■■■■

接着我们编写用来测试的应用程序。

本次的a_nask.nas节选

  1. _api_beep: ; void api_beep(int tone);
  2. MOV EDX,20
  3. MOV EAX,[ESP+4] ; tone
  4. INT 0x40
  5. RET

本次的beepdown.c

  1. void api_end(void);
  2. int api_getkey(int mode);
  3. int api_alloctimer(void);
  4. void api_inittimer(int timer, int data);
  5. void api_settimer(int timer, int time);
  6. void api_beep(int tone);
  7. void HariMain(void)
  8. {
  9. int i, timer;
  10. timer = api_alloctimer();
  11. api_inittimer(timer, 128);
  12. for (i = 20000000; i >= 20000; i -= i / 100) {
  13. /* 20KHz~20Hz,即人类可以听到的声音范围*/
  14. /* i以1%的速度递减*/
  15. api_beep(i);
  16. api_settimer(timer, 1); /* 0.01秒*/
  17. if (api_getkey(1) != 128) {
  18. break;
  19. }
  20. }
  21. api_beep(0);
  22. api_end();
  23. }

这个应用程序每0.01秒便降低一次发出的声音频率,当声音频率降至20Hz或者用户按下任意键时结束。

■■■■■

笔者刚要“make run”的时候突然想到, QEMU并没有模拟蜂鸣器发声的功能,因此即便运行这个程序也发不出声音。没办法,我们只好用“make install”在真机环境下测试了,只要主板和蜂鸣器正确连接就会发出声音哦。

1 蜂鸣器发声(harib22a) - 图1 咻——

如果你听了这个声音觉得“哇!掉下去啦”,心情也跟着一起跌落到低谷的话,不妨编写下面这个beepup.c听听看。只是将i由递减改为递增,就能发出让人“充满能量”的声音啦(笑)。

beepup.c

  1. void HariMain(void)
  2. {
  3. int i, timer;
  4. timer = api_alloctimer();
  5. api_inittimer(timer, 128);
  6. for (i = 20000; i <= 20000000; i += i / 100) {
  7. api_beep(i);
  8. api_settimer(timer, 1); /* 0.01秒*/
  9. if (api_getkey(1) != 128) {
  10. break;
  11. }
  12. }
  13. api_beep(0);
  14. api_end();
  15. }