OpenHarmony设备开发入门学习笔记——PWM输出歌曲声音

举报
yd_266832696 发表于 2025/05/31 23:09:13 2025/05/31
【摘要】 本文代码使用的是传智鸿蒙元气派再封装过的OpenHarmony master版本代码。 一、前言学习OpenHarmony南向设备开发除了只会普通IO口的输入输出还是远远不够的。普通IO口的输出能控制LED灯的亮灭,PWM输出就可以控制灯亮的程度,深入学习发现舵机的运动也是需要PWM输出控制的。 二、PWM简介 1.什么是PWM脉冲宽度调制 (PWM) ,英文“Pulse Width Mod...

本文代码使用的是传智鸿蒙元气派再封装过的OpenHarmony master版本代码。

一、前言

学习OpenHarmony南向设备开发除了只会普通IO口的输入输出还是远远不够的。普通IO口的输出能控制LED灯的亮灭,PWM输出就可以控制灯亮的程度,深入学习发现舵机的运动也是需要PWM输出控制的。

二、PWM简介

1.什么是PWM

脉冲宽度调制 (PWM) ,英文“Pulse Width Modulation”,简称脉宽调制。它是利用微处理器的数字输出,来对模拟电路进行控制的一种非常有效的技术 。简单一点,就是对脉冲宽度的控制。
这些脉冲将以==方波==的形式出现。在任何给定的时间点,波型要么是高电平或者是低电平。
需要关注两个与之相关的重要参数,一个是PWM占空比,另一个是PWM信号的频率。
image.png

2.PWM占空比

由于PWM信号在特定的时间内保持接通状态(高电平),然后在剩余的时间内保持断开状态(低电平),所以引用占空比来表示一个脉冲循环内==接通时间相对于总时间所占的比例==。也就是说在一段连续工作时间内脉冲==高电平时间与总时间的比值==。
举个栗子:如果信号总是处于高电平,它的占空比是100%;如果信号总是处于低电平,那么它的占空比是0%。
通过改变PWM的占空比就可以任意输出0-3.3V/5V的任意电压了。

3.PWM频率

PWM信号的频率决定了PWM完成一个信号周期的速度(==一秒内完成了多少次PWM变换==)。周期是指PWM信号完成一个接通和关闭的时间。计算频率的公式如下所示:
频率 = 1 / 周期

三、demo设计

1.呼吸灯

只需要改变PWM的占空比即可。
首先查看led灯的引脚号。
注意:==不同开发板的led灯对应的引脚号不一样,一定要事先查看原理图!==
image.png

上主程序代码👇

#include <stdio.h>  //标准输入输出头文件
#include <string.h> //字符数组头文件
#include <unistd.h> //操作系统API
#include "ohos_init.h" //鸿蒙操作系统初始化和启动服务和功能的条目
#include "cmsis_os2.h" //微控制器软件接口标准
#include "iot_gpio.h" // GPIO 初始化、输入/输出设置和级别设置
#include "iot_pwm.h"  //操作 PWM 设备头文件
#include "iot_io.h"   //io口的功能设置
#include "genki_pin.h"//传智鸿蒙元气派再封装过的引脚功能
static void start(void) {
    //初始化GPIO口
    IoTGpioInit(IOT_IO_NAME_2);
    //设置IO口功能为GPIO
    IoTIoSetFunc(IOT_IO_NAME_2, IOT_IO_FUNC_2_PWM2_OUT);
    //设置IO口输出方向:输出
    IoTGpioSetDir(IOT_IO_NAME_2, IOT_GPIO_DIR_OUT);
    //初始化PWM功能
    IoTPwmInit(IOT_IO_NAME_2);
    //不断输出pwm方波
    while (1) {
        for (int i = 0; i < 20; i++) {//由暗到亮
            IoTPwmStart(IOT_IO_NAME_2, i, 10000);
            usleep(0.05 * 1000 * 1000);
        }
        for (int i = 20; i > 0; i--) {//由亮到暗
            IoTPwmStart(IOT_IO_NAME_2, i, 10000);
            usleep(0.05 * 1000 * 1000);
        }
    }
}

APP_FEATURE_INIT(start);

2.演奏歌曲

演奏歌曲则需要不断改变PWM的频率了。

2.1材料准备

无源蜂鸣器:无源内部不带震荡源,所以直流信号无法令其鸣叫,必须用方波去驱动它才能鸣叫。

2.2乐理知识准备

每个音符都有对应的震动频率,下面以常见的C大调为例。
image.png

歌曲曲谱:
image.png

C调,4/4拍(以四分音符为一拍,每小节四拍)

2.3程序编写

#include <stdio.h>     //标准输入输出头文件
#include <string.h>    //字符数组头文件
#include <unistd.h>    //操作系统API
#include "ohos_init.h" //鸿蒙操作系统初始化和启动服务和功能的条目
#include "cmsis_os2.h" //微控制器软件接口标准
#include "iot_gpio.h"  // GPIO 初始化、输入/输出设置和级别设置
#include "iot_pwm.h"   //操作 PWM 设备头文件
#include "iot_io.h"    //io口的功能设置
#include "genki_pin.h" //传智鸿蒙元气派再封装过的引脚功能
#include <math.h>      //常用数学运算方法

//C大调各个音符对应的频率数组
static const uint16_t g_tuneFreqs[] = {
    0,
    1046.5, // 高音1-do
    1174.7, // 高音2-re
    1318.5, // 高音3-mi
    1396.9, // 高音4-fa
    1568,   // 高音5-so
    1760,   // 高音6-la
    1975.5, // 高音7-si
    783.99  // 中音5-so
};

// 曲谱音符
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 music(void *arg)
{
    //初始化GPIO口
    IoTGpioInit(IOT_IO_NAME_14);
    //设置IO口功能为PWM输出,14号引脚支持pwm输出
    IoTIoSetFunc(IOT_IO_NAME_14, IOT_IO_FUNC_14_PWM5_OUT);
    //设置IO口输出方向:输出
    IoTGpioSetDir(IOT_IO_NAME_14, IOT_GPIO_DIR_OUT);
    //初始化PWM功能
    IoTPwmInit(IOT_PWM_NAME_5);

    while (1)
    {
        for (size_t i = 0; i < sizeof(g_scoreNotes) / sizeof(g_scoreNotes[0]); i++)
        {
            uint32_t tune = g_scoreNotes[i];                            // 曲谱音符、12345678
            uint16_t freqDivisor = g_tuneFreqs[tune];                   //每个曲谱音符相对应的频率
            uint32_t tuneInterval = g_scoreDurations[i] * (125 * 1000); // 音符持续时间
            printf("音符:%d 音符对应频率:%d 持续时间:%d\r\n", tune, freqDivisor, tuneInterval);
            IoTPwmStart(IOT_PWM_NAME_5, 50, freqDivisor * 4); //占空比50%
            usleep(tuneInterval);                             //每个音符持续时间
            IoTPwmStop(IOT_PWM_NAME_5);
        }
    }
}

static void start(void)
{
    osThreadAttr_t attr;

    attr.name = "thread_1";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 1024 * 4;
    attr.priority = 25;

    if (osThreadNew(music, NULL, &attr) == NULL)
    {
        printf("Falied to create music!\r\n");
    }
}

APP_FEATURE_INIT(start);

四、总结

Hi3861的PWM使用流程:
 1.初始化io、2. io复用为pwm输出、3. 使能pwm通道4. 开始输出pwm(通道、占空比、频率)5.停止输出pwm
这次学习pwm为下次学习怎么使用舵机打下基础。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。