关于操作系统:用鸿蒙OS在蜂鸣器上播放一曲两只老虎

5次阅读

共计 3806 个字符,预计需要花费 10 分钟才能阅读完成。

本文介绍如何在 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/…

开发板能够输入的最低频率
通过后面的公式,咱们晓得:

  1. PWM 输入的方波频率和 freq 成反比,freq 越大,输入的方波频率越小;
  2. 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;
那么问题来了——晶体时钟频率到底是多少?

晶体时钟频率是多少?
能够通过试验测算出晶体时钟频率,具体步骤如下:

  1. 应用 hi_pwm_set_clock(PWM_CLK_XTAL); 设置时钟源为晶体时钟;
  2. 应用 PwmStart(WIFI_IOT_PWM_PORT_PWM0, 201000, 401000); 输入方波信号;
  3. 应用示波器测量方波频率,依据测量的频率计算时钟源频率;

经理论测量,方波频率为 1000Hz,

因而,时钟频率为 1000 40 1000,即 40 MHz;

能够输入的方波最低频率
因而,方波最低频率就是 40M / 65535,也就是:

40 1000 1000 / 65535
610.3608758678569
对照下面的频率表,能够晓得,可能输入 E5 及以上的所有音符;

筹备曲谱
为了代码实现起来简略,我抉择了《两只老虎》的曲谱作为素材,在简谱网找到了简谱:

简谱阐明
简谱上的一些记号,有的同学可能不太分明是什么意思,这里简略阐明一下:

  1. 左上角的 1 = C 是示意调式(能够不必关怀),1 是唱名,C 是音名,1= C 是正调(就是惯例的对应关系: 1-C,2-D, 3-E, 4-F, 5-G, 6-A, 7-B);
  2. 左上角的 4/4 是四四拍,是指 四分音符为一拍,每大节有四拍;
  3. 上面谱子上的竖线就是每个大节分隔符,和 4 / 4 对应;
  4. “跑得快”下面 5 前面的横线示意延时一拍;
  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://gitee.com/hihopeorg/HarmonyOS-IoT-Application-Development/blob/master/02_device_control/beeper_music_demo.c


原文链接:
https://developer.huawei.com/consumer/cn/forum/topic/0204398682948650101?fid=0101303901040230869
作者:思维

正文完
 0