[文章]【HarmonyOS HiSpark Wi-Fi IoT 套件试用连连载】第六篇、利用蜂鸣器播放音乐

阅读量0
0
0


        很早之前便看到有大佬做了蜂鸣器播放音乐的项目,当时也想尝试一番,奈何对鸿蒙系统不太熟悉,经过一段时间摸索,对鸿蒙系统有了一些了解,终于完成了蜂鸣器播放音乐的项目。        
        下面先介绍蜂鸣器播放音乐的原理,然后介绍Hi3861的PWM外设配置,最后介绍如何实现PWM播放音乐。
一、蜂鸣器播放音乐的原理
        单片机通过I/O口输出不同频率的PWM可以使无源蜂鸣器产生不同频率的声音,为了增强驱动能力,通常单片机通过控制三极管来控制蜂鸣器的通断。控制蜂鸣器发声的频率、时长、间隔,便可以演奏出一首音乐。
        在最常见的定律法——十二平均律中,中央C(钢琴最中间的C音)右边的第一个A音被定义为440 Hz,然后其它音的频率用等比数列推出,相隔半音的两个音的频率之比为。由此可以推断出中央C的频率为262Hz,它右边的#C的频率为277 Hz,D的频率为293Hz等等。还有低音、高音,在此频率基础上减半或加倍。
音名(中音)
  频率(Hz)
C
262
#C
277
D
293
#D
311
E
329
F
349
#F
369
G
392
#G
415
A
440
#A
466
B
494
二、配置PWM外设
        找到vendor/hisi/hi3861/hi3861/app/wifiiot_app/init/app_io_init.c文件夹,修改PWM引脚,如下所示。板卡上连接蜂鸣器的引脚为GPIO09,复用后的PWM为PWM0。
配置PWM引脚.png

        找到vendor/hisi/hi3861/hi3861/build/config/usr_config.mk文件,添加"
CONFIG_PWM_SUPPORT=y",使能PWM功能。

使能PWM引脚.png
        在applications/sample/wifi-iot/app下建立buzzer文件夹,然后在applications/sample/wifi-iot/app/oled_demo下建立buzzer.c、buzzer.h(未用,可以不建)、BUILD.gn(编译脚本)三个文件。
蜂鸣器文件夹.png
        为了蜂鸣器声音更悦耳,PWM占空比一般设为50%,buzzer.c代码如下:
  1. #include <unistd.h>
  2. #include "stdio.h"
  3. #include "ohos_init.h"
  4. #include "cmsis_os2.h"

  5. #include "wifiiot_gpio.h"
  6. #include "wifiiot_gpio_ex.h"

  7. #include <hi_pwm.h>
  8. #include "buzzer.h"

  9. #define BUZZER_TASK_STACK_SIZE 512
  10. #define BUZZER_TASK_PRIO 25

  11. static void *BuzzerTask(const char *arg)
  12. {
  13.   (void)arg;

  14.   hi_pwm_start(HI_PWM_PORT_PWM0,32767,65535);  //端口号;占空比(需计算);分频系数

  15.   return NULL;
  16. }

  17. static void BuzzerExampleEntry(void)
  18. {
  19.     osThreadAttr_t attr;

  20.     IoSetFunc(WIFI_IOT_IO_NAME_GPIO_9,

  21.     hi_pwm_init(HI_PWM_PORT_PWM0);   //初始化PWM端口
  22.     hi_pwm_set_clock(PWM_CLK_XTAL);   //设置时钟源 40MHz

  23.     attr.name = "BuzzerTask";
  24.     attr.attr_bits = 0U;
  25.     attr.cb_mem = NULL;
  26.     attr.cb_size = 0U;
  27.     attr.stack_mem = NULL;
  28.     attr.stack_size = BUZZER_TASK_STACK_SIZE;
  29.     attr.priority = BUZZER_TASK_PRIO;

  30.     if (osThreadNew((osThreadFunc_t)BuzzerTask, NULL, &attr) == NULL) {
  31.         printf("[BuzzerExample] Falied to create BuzzerTask!n");
  32.     }
  33. }

  34. SYS_RUN(BuzzerExampleEntry);
复制代码
        BUILD.gn脚本文件如下:
  1. static_library("buzzer") {
  2.     sources = [
  3.         "buzzer.c"
  4.     ]

  5.     include_dirs = [
  6.         "//utils/native/lite/include",
  7.         "//kernel/liteos_m/components/cmsis/2.0",
  8.         "//base/iot_hardware/interfaces/kits/wifiiot_lite",
  9.     ]
  10. }
复制代码
        最后修改applications/sample/wifi-iot/app/BUILD.gn 文件,如下所示:
  1. import("//build/lite/config/component/lite_component.gni")

  2. lite_component("app") {
  3.     features = [
  4.         "buzzer:buzzer",
  5.     ]
  6. }
复制代码
        完成上述步骤后,编译下载即可驱动蜂鸣器,但是Hi3861的PWM外设的最低频率为610Hz(40MHz/65535),为了能正常播放音乐,只能将音乐频率升高两个8度,即最低频率为262Hz*2=524Hz,能满足一些简单音乐的频率要求。
三、播放音乐
        前面说过,蜂鸣器播放音乐的三要素为频率、时长、停顿间隔,但为了简化流程,一般取消频率间隔,只保留频率和时长。
        (1)选好一首乐谱,统计好乐谱所需的所有频率,为了后续方便调用,可写成数组,并按照调式顺序排列,以《两只老虎为例》。

  1. static const uint16_t g_tuneFreqs[] = {
  2.     0, // 40M Hz 对应的分频系数:
  3.     38223, // 1046.5
  4.     34052, // 1174.7
  5.     30338, // 1318.5
  6.     28635, // 1396.9
  7.     25511, // 1568
  8.     22728, // 1760
  9.     20249, // 1975.5
  10.     51021 // 5_ 783.99 // 第一个八度的 5
  11. };
复制代码
       (2)根据简谱找出每拍的频率,按照其在频率表中的顺序,分别列出,并写成数组形式。
  1. tatic const uint8_t g_scoreNotes[] = {
  2.     1, 2, 3, 1,  1, 2, 3, 1,   3, 4, 5,  3,   4, 5, 5, 6,
复制代码
       (3)将每个节拍的时长列出,为了方便调用,同样写成数组形式
  1. static const uint8_t g_scoreDurations[] = {
  2.     4, 4, 4, 4,   4, 4, 4, 4,    4, 4, 8,  4,   4, 8, 3, 1,
复制代码
       (4)  最后按照以上描述,顺序调用PWM即可播放音乐。这一步主要是修改'二'中的 BuzzerTask 函数。
  1. static void *BuzzerTask(const char *arg)
  2. {
  3.   (void)arg;

  4.   for (size_t i = 0; i < sizeof(g_scoreNotes)/sizeof(g_scoreNotes[0]); i++) {
  5.     uint32_t tune = g_scoreNotes[i]; // 音符
  6.     uint16_t freqDivisor = g_tuneFreqs[tune];
  7.     uint32_t tuneInterval = g_scoreDurations[i] * (125*1000); // 音符时间
  8.     printf("%d %d %d %drn", tune, (40*1000*1000) / freqDivisor, freqDivisor, tuneInterval);
  9.     hi_pwm_start(HI_PWM_PORT_PWM0, freqDivisor/2, freqDivisor);
  10.     usleep(tuneInterval);
  11.     hi_pwm_stop(HI_PWM_PORT_PWM0);
  12.   }

  13.   return NULL;
  14. }
复制代码

回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友