用鸿蒙OS在蜂鸣器上播放一曲《两只老虎》
本文介绍如何在HiSpark Wi-Fi IoT套件上,使用Harmony OS IoT硬件子系统的PWM接口 驱动蜂鸣器 播放音乐。
用PWM输出方波的API
鸿蒙系统IoT硬件子系统提供了PWM相关接口,接口头文件为wifiiot_pwm.h,其中开始输出方波的接口为:
/** * @brief Outputs PWM signals based on the input parameters. * * This function outputs PWM signals from a specified port based on * the configured frequency division multiple and duty cycle. * * @param port Indicates the PWM port number. * @param duty Indicates the PWM duty cycle. * @param freq Indicates the frequency-division multiple. * @return Returns {@link WIFI_IOT_SUCCESS} if the operation is successful; * returns an error code defined in {@link wifiiot_errno.h} otherwise. * @since 1.0 * @version 1.0 */ unsigned int PwmStart(WifiIotPwmPort port, unsigned short duty, unsigned short freq);
PWM输出的方波频率
通过PwmStart接口的注释,可以知道freq参数是分频倍数,PWM实际输出的方波频率等于 PWM时钟源频率 除以 分频倍数,即
f = Fcs / freq
其中,Fcs是PWM时钟源频率;
PWM输出方波的占空比
通过PwmStart接口的duty参数可以控制输出方波的占空比,占空比是指PWM输出的方波波形的高电平时间占整个方波周期的比例,具体占空比值是 duty 和 freq的比值,例如想要输出占空比 50%的方波信号,那么duty填的值就要是 freq/2;
音符-频率对应关系
这个表中有一个规律——音高升高一个八度,频率升高一倍。
表格来自:https://liam.page/2018/04/09/pitch-interval-and-harmonic/
开发板可以输出的最低频率 通过前面的公式,我们知道:
-
PWM输出的方波频率和freq成反比,freq越大,输出的方波频率越小;
-
freq是unsinged short类型,最大值为65535; 因此,输出频率的最小值取决于时钟源,而PWM的默认时钟源为160M:
unsigned int HalPwmInit(HalWifiIotPwmPort port) { if (hi_pwm_set_clock(PWM_CLK_160M) != HI_ERR_SUCCESS) { return (unsigned int)HAL_WIFI_IOT_FAILURE; } return hi_pwm_init((hi_pwm_port)port); }
160M时钟源条件下,输出方波的最低频率是:160M/65535=2441.44...,这个频率还是略高,在上面的表格中没有找到音名。但是我可以用上面表格值继续往后推算两个八度,就能够覆盖这个频率(不过通常只使用7个八度,所以还是有点高)。
如果时钟源频率可以更低,那么输出频率也可以更低! 幸运的是,通过调用hi_pwm_set_clock接口,可以修改时钟源:
/** * @ingroup iot_pwm * * Enumerates the PWM clock sources.CNcomment:PWM时钟源枚举。CNend */ typedef enum { PWM_CLK_160M, /**< 160M APB clock.CNcomment:160M 工作时钟 CNend */ PWM_CLK_XTAL, /**< 24M/40M crystal clock.CNcomment:24M或40M 晶体时钟 CNend */ PWM_CLK_MAX /**< Maximum value, which cannot be used.CNcomment:最大值,不可使用CNend */ } hi_pwm_clk_source; hi_u32 hi_pwm_set_clock(hi_pwm_clk_source clk_type);
通过注释我们知道hi_pwm_set_clock(PWM_CLK_XTAL);可以将时钟源设置为晶体时钟,晶体时钟可能为24M或40M; 那么问题来了——晶体时钟频率到底是多少?
晶体时钟频率是多少? 可以通过实验测算出晶体时钟频率,具体步骤如下:
-
使用 hi_pwm_set_clock(PWM_CLK_XTAL); 设置时钟源为晶体时钟;
-
使用PwmStart(WIFI_IOT_PWM_PORT_PWM0, 201000, 401000);输出方波信号;
-
使用示波器测量方波频率,根据测量的频率计算时钟源频率;
经实际测量,方波频率为1000Hz,
因此,时钟频率为 1000 * 40 * 1000,即 40 MHz;
可以输出的方波最低频率 因此,方波最低频率就是 40M / 65535 ,也就是:
40 * 1000 * 1000 / 65535 610.3608758678569 对照上面的频率表,可以知道,能够输出E5及以上的所有音符;
准备曲谱 为了代码实现起来简单,我选择了《两只老虎》的曲谱作为素材,在简谱网找到了简谱:
简谱说明 简谱上的一些记号,有的同学可能不太清楚是什么意思,这里简单说明一下:
-
左上角的1=C是表示调式(可以不用关心),1是唱名,C是音名,1=C是正调(就是常规的对应关系: 1-C,2-D, 3-E, 4-F, 5-G, 6-A, 7-B);
-
左上角的 4/4 是四四拍,是指 四分音符为一拍, 每小节有四拍;
-
下面谱子上的竖线就是每个小节分隔符,和4/4对应;
-
“跑得快”上面5后面的横线表示延时一拍;
-
“一只没有眼睛”一句,5后面的点表示顺延半拍,一条下划线表示二分之一时间,两条下划线表示四分之一时间;
编写代码 有了以上知识,我们就可以编写代码了,关键代码如下:
static const uint16_t g_tuneFreqs[] = { // 音符对应的分频系数 0, // 40M Hz 时钟源,C6 ~ B6: 38223, // 1 1046.5 34052, // 2 1174.7 30338, // 3 1318.5 28635, // 4 1396.9 25511, // 5 1568 22728, // 6 1760 20249, // 7 1975.5 51021 // 5_ 783.99 // 低一个八度的 5 }; // 曲谱音符 static const uint8_t g_scoreNotes[] = { // 《两只老虎》简谱:http://www.jianpu.cn/pu/33/33945.htm 1, 2, 3, 1, 1, 2, 3, 1, 3, 4, 5, 3, 4, 5, 5, 6, 5, 4, 3, 1, 5, 6, 5, 4, 3, 1, 1, 8, 1, 1, 8, 1, // 最后两个 5 应该是低八度的,链接图片中的曲谱不对,声音到最后听起来不太对劲 }; // 曲谱时值,根据简谱记谱方法转写 static const uint8_t g_scoreDurations[] = { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 8, 3, 1, 3, 1, 4, 4, 3, 1, 3, 1, 4, 4, 4, 4, 8, 4, 4, 8, }; static void *BeeperMusicTask(const char *arg) { (void)arg; printf("BeeperMusicTask start!\r\n"); hi_pwm_set_clock(PWM_CLK_XTAL); // 设置时钟源为晶体时钟(40MHz,默认时钟源160MHz) for (size_t i = 0; i < sizeof(g_scoreNotes)/sizeof(g_scoreNotes[0]); i++) { uint32_t tune = g_scoreNotes[i]; // 音符 uint16_t freqDivisor = g_tuneFreqs[tune]; uint32_t tuneInterval = g_scoreDurations[i] * (125*1000); // 音符时间 printf("%d %d %d %d\r\n", tune, (40*1000*1000) / freqDivisor, freqDivisor, tuneInterval); PwmStart(WIFI_IOT_PWM_PORT_PWM0, freqDivisor/2, freqDivisor); usleep(tuneInterval); PwmStop(WIFI_IOT_PWM_PORT_PWM0); } return NULL; }
谱子中最后两个5是错误的,应该是低八度的5,也就是5下面应该打一个点;我修改了代码,让整个曲子听起来更自然;
原文链接: https://developer.huawei.com/consumer/cn/forum/topic/0204398682948650101?fid=0101303901040230869 作者:思维

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
白话科普系列——最好用的浏览器 Chrome,你用了么?
市面上的浏览器多种多样,而浏览器的王者——IE 浏览器,它在 2002 年市场份额高达 95.4%。直到后续 Firefox,Safari,Chrome 相继问世才动摇了 IE 浏览器的地位,其中 Chrome 在 2008 年一经问世便表现出了非凡的天赋,一路披荆斩棘所向披靡。 最近,国外统计公司 Statcounter 公布了全球浏览器市场份额调查数据。 从上面两张图中可以看出,无论是移动端还是桌面端,谷歌 Chrome 浏览器都名列第一,大幅领先其他品牌。 为何 Chrome 浏览器会有如此高的市场占比,我们一起来看下 Chrome 浏览器都有哪些优点。 Chrome 浏览器的优点 界面简洁 浏览器的本质是一个展示网络资源的工具,一个简洁的界面会让用户更加专注于内容,而不被浏览器的外观所干扰。毫无疑问,Chome 在这一点上就打败了不少对手。 整合了 Google 服务 Chrome 作为 Google 公司开发的浏览器,肯定会给自己的产品打打广告,但是 Google 的产品确实质量过硬,我们可以在 Chrome 上体验到很多优质的 Google 产品,比如在线文档表格编辑器、网...
- 下一篇
GraphX 在图数据库 Nebula Graph 的图计算实践
不同来源的异构数据间存在着千丝万缕的关联,这种数据之间隐藏的关联关系和网络结构特性对于数据分析至关重要,图计算就是以图作为数据模型来表达问题并予以解决的过程。 一、背景 随着网络信息技术的飞速发展,数据逐渐向多源异构化方向发展,且不同来源的异构数据之间也存在的千丝万缕的关联,这种数据之间隐藏的关联关系和网络结构特性对于数据分析至关重要。但传统关系型数据库在分析大规模数据关联特性时存在性能缺陷、表达有限等问题,因此有着更强大表达能力的图数据受到业界极大重视,图计算就是以图作为数据模型来表达问题并予以解决的过程。图可以融合多源多类型的数据,除了可以展示数据静态基础特性之外,还可通过图计算展示隐藏在数据之间的图结构特性和点对关联关系,成为社交网络、推荐系统、知识图谱、金融风控、网络安全、文本检索等领域重要的分析手段。 二、算法应用 为了支撑大规模图计算的业务需求,Nebula Graph 基于 GraphX 提供了 PageRank 和 Louvain 社区发现的图计算算法,允许用户通过提交 Spark 任务的形式执行算法应用。此外,用户也可以通过 Spark Connector 编写 Sp...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Hadoop3单机部署,实现最简伪集群
- CentOS7设置SWAP分区,小内存服务器的救世主