用鸿蒙OS在蜂鸣器上播放一曲《两只老虎》 原创 精华
本文介绍如何在HiSpark Wi-Fi IoT套件上,使用Harmony OS IoT硬件子系统的PWM接口 驱动蜂鸣器 播放音乐。
用PWM输出方波的API
鸿蒙系统IoT硬件子系统提供了PWM相关接口,接口头文件为wifiiot_pwm.h,其中开始输出方波的接口为:
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:
160M时钟源条件下,输出方波的最低频率是:160M/65535=2441.44...,这个频率还是略高,在上面的表格中没有找到音名。但是我可以用上面表格值继续往后推算两个八度,就能够覆盖这个频率(不过通常只使用7个八度,所以还是有点高)。
如果时钟源频率可以更低,那么输出频率也可以更低!
幸运的是,通过调用hi_pwm_set_clock接口,可以修改时钟源:
通过注释我们知道hi_pwm_set_clock(PWM_CLK_XTAL);可以将时钟源设置为晶体时钟,晶体时钟可能为24M或40M;
那么问题来了——晶体时钟频率到底是多少?
晶体时钟频率是多少?
可以通过实验测算出晶体时钟频率,具体步骤如下:
- 使用 hi_pwm_set_clock(PWM_CLK_XTAL); 设置时钟源为晶体时钟;
- 使用PwmStart(WIFI_IOT_PWM_PORT_PWM0, 20*1000, 40*1000);输出方波信号;
- 使用示波器测量方波频率,根据测量的频率计算时钟源频率;
经实际测量,方波频率为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后面的点表示顺延半拍,一条下划线表示二分之一时间,两条下划线表示四分之一时间;
编写代码
有了以上知识,我们就可以编写代码了,关键代码如下:
谱子中最后两个5是错误的,应该是低八度的5,也就是5下面应该打一个点;我修改了代码,让整个曲子听起来更自然;
厉害,谢谢分享!!!
前排留名。。。。。。。。。。。。。许老师你继续加油。
有童鞋贴编译过程中报错问题的解决方案哦,有同遇到的,可以看过来https://harmonyos.51cto.com/answer/209
插眼,静静的观察
老师 请教一下 1上有个点 是哪个音? 是1108.7吗?
简谱上加一个点就是高一个八度,
比如代码注释里面的1的频率是 1046.5,高一个八度就是频率乘以2
// 1 1046.5 => 2093.0
你好,请问hi_u32 hi_pwm_set_clock这个函数在哪里?我从hpm拉取的源码中的wifiiot_pwm.h文件里没有找到。
而且现在下载的鸿蒙源码头文件都改变了,内容也有些不同,好多之前课程的代码都用不了了,请问应该怎么继续学习(哪里更全面系统)?
请问一下,我需要周期为20ms的PWM输出,应该怎么做?因为想驱动SG90 Servo,一直驱动不起来。
两个思路:
1. 关闭内核调度器,然后循环延时20ms切换一次gpio输出高低电平,操作完了之后在开启调度器;
这种方法需要用到CMSIS v2几个接口:
osKernelLock和osKernelUnlock关闭、开启调度器;
osKernelGetSysTimerCount 获取CPU硬件cycle数,
osKernelGetSysTimerFreq 获取CPU工作频率,这两个接口可以用来实现短时间的忙等延时;
这种方式的好处是时间控制精度比较高,缺点是忙等过程中CPU会一直被占用,不能用来处理其他任务;
2. 使用定时器,定时20ms切换一次gpio输出高低电平;
这种方式的好处是不占用CPU,缺点是由调度器切换实现,控制精度为调度单位(当前为10ms);
这个接口是海思 hi3861 SDK里面的接口,可以在vendor下面搜索到:
grep -nr hi_pwm_set_clock vendor/
> 下载的鸿蒙源码头文件都改变了,内容也有些不同,好多之前课程的代码都用不了了,请问应该怎么继续学习(哪里更全面系统)?
这个确实是很大的问题,OpenHarmony IoT硬件的API接口一直在变化,这一块来目前说只有直接用海思SDK接口是比较稳定的;
IoT方向的话,可以继续学习一下:
1. 内核接口CMSIS-RTOS:https://arm-software.github.io/CMSIS_5/RTOS2/html/index.html
2. 海思的SDK文档;
3. 物联网相关协议应用和原理;
以及更深入的,可以学习:
4. liteos_m的相关实现原理、移植;
5. 分布式软总线的应用和原理(目前需要HarmonyOS手机作为主控端);
6. 其他RTOS,例如RT-Thread、Zephyr等;