注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
xusiwei1236的个人空间 https://home.eeworld.com.cn/space-uid-1305707.html [收藏] [复制] [分享] [RSS]
日志

【先楫HPM6750测评】PWM控制蜂鸣器发声

热度 1已有 566 次阅读2022-5-28 22:13 |个人分类:国产MCU

    

本篇将介绍如何使用HPM6750输出PWM信号,并使用PWM信号驱动蜂鸣器发声。

PWM简介

PWM 的全称是 脉冲宽度调制 ( Pulse-width modulation ),常用于模拟设备的控制,例如驱动舵机、电机等等,以及控制LED的亮度,控制无源蜂鸣器发声等等。

HPM6750EVKMINI蜂鸣器相关电路分析

通过查阅开发板原理图,可以找到蜂鸣器相关电路原理图如下:

可以看到:

  • 蜂鸣器BZ1是无源蜂鸣器
  • PWM3.P4引脚输出高电平时,有电流通过蜂鸣器
  • PWM3.P4引脚,也是HPM6750芯片的PE05引脚

HPM6750芯片的PWM模块及编程接口

HPM6750芯片PWM模块简介

在《HPM6750 系列超高性能微控制器用户手册》(HPM6750_UM__V1_0.pdf文件)中,我们可以找到HPM6750芯片PWM模块的介绍。

HPM6750手册中PWM模块相关的介绍前后一共有二十几页,如果需要使用HPM6750芯片的PWM功能的话,建议耐心读完这个章节,以及hpm_pwm_drv.h和hpm_pwm_drv.c的代码。因为HPM SDK的PWM编程接口和STM32的HAL编程接口差别较大,如果不阅读这部分文档,可能会对PWM接口有无从下手的感觉。

PWM模块框图:

可以看到,PWM整体上由时间基准、比较器、输出通道、输出控制几个部分组成。

对于每一路PWM波形,主要由计数寄存器(XCNT)、起始寄存器(XSTA)、重载寄存器(XRLD)、比较寄存器(XCMP)控制。具体细节参见HPM6750用户手册相关章节,这里不作详细介绍。

手册中的一个PWM生成例子:

HPM SDK中PWM API接口简介

HPM SDK文档中可以找到PWM相关的API介绍,具体文件为:sdk_env_v0.9.0/hpm_sdk/doc/output/api_doc/html/group__pwm__interface.html

这个文档是从源码生成的,内容和hpm_pwm_drv.h中的注释是一致的,如下图所示:

使用HPM6750输出PWM信号

有了上面的知识之后,我们就可以开始用HPM6750输出PWM信号,驱动蜂鸣器发声了。

设置蜂鸣器控制引脚为PWM功能

要驱动蜂鸣器发声,首先我们需要将蜂鸣器的控制引脚PE05设置为PWM功能。

通过翻阅SDK代码,可以发现SDK中已经实现了将蜂鸣器引脚设置为PWM的函数:

  • board.h和board.c中的board_init_beep_pwm_pins()函数;
  • pinmux.h和pinmux.c中的init_beep_pwm_pins()函数;

这两个函数的实现都比较简单,我们后续的程序中直接调用即可。

void board_init_beep_pwm_pins(void)
{
    init_beep_pwm_pins();
}

void init_beep_pwm_pins(void)
{
    HPM_IOC->PAD[IOC_PAD_PE05].FUNC_CTL = IOC_PE05_FUNC_CTL_PWM3_P_4;
}

输出PWM波函数

接下来我们实现如下函数:

void gen_pwm_to_beep(uint32_t pwm_freq, uint32_t duration_ms);

两个参数分别为:

  • pwm_freq是PWM输出波形的频率
  • duration_ms是输出的持续时间

实现这个函数需要使用到HPM SDk中的一些函数,这些函数及其功能如下:

  • pwm_get_default_pwm_config 填充 pwm_config_t 默认值
  • pwm_get_default_cmp_config 填充 pwm_cmp_config_t 默认值
  • pwm_set_reload 设置RLD寄存器
  • pwm_set_start_count 设置STA寄存器
  • pwm_setup_waveform 使用给定参数对PWM通道进行设置
  • pwm_start_counter 开始计数
  • pwm_issue_shadow_register_lock_event 锁定影子寄存器
  • pwm_disable_output 停止输出PWM信号

完整蜂鸣器发声代码

好了,了解了上面这些背景知识之后,就可以实现使用PWM驱动蜂鸣器发声了,完整代码如下:

#include <stdio.h>
#include "board.h"
#include "hpm_pwm_drv.h"

#define BEEP_PWM BOARD_BEEP_PWM
#define BEEP_OUT BOARD_BEEP_PWM_OUT
#define BEEP_CLK BOARD_BEEP_PWM_CLOCK_NAME

void gen_pwm_to_beep(uint32_t pwm_freq, uint32_t duration_ms)
{
    uint8_t cmp_index = 0;
    bool increase_duty_cycle = true;
    pwm_cmp_config_t cmp_config = {0};
    pwm_config_t pwm_config = {0};
    uint32_t clk_freq = 0;
    uint32_t pwm_reload = 0;

    // 计算reload值
    // 因为, pwm_freq = clk_freq / (reload + 1)
    // 所以, reload = clk_freq / pwm_freq - 1
    clk_freq = clock_get_frequency(BEEP_CLK);
    pwm_reload = clk_freq / pwm_freq - 1;
    printf("clk_freq = %d, pwm_reload = %d\\n", clk_freq, pwm_reload);

    // 准备pwm_config
    pwm_get_default_pwm_config(BEEP_PWM, &pwm_config); // 填充 pwm_config_t 默认值
    pwm_config.enable_output = true;
    pwm_config.dead_zone_in_half_cycle = 0;
    pwm_config.invert_output = false;

    // 准备cmp_config
    pwm_get_default_cmp_config(BEEP_PWM, &cmp_config); // 填充 pwm_cmp_config_t 默认值
    cmp_config.mode = pwm_cmp_mode_output_compare; // 设置PWM工作模式为输出
    cmp_config.cmp = pwm_reload / 2;  // 设置初始CMP值,这样直接设置为 1/2 则后续不需要更新即可生成 50% 占空比
    // cmp_config.cmp = pwm_reload + 1; // CMP > RLD, 由于计数器值 CNT 始终达不到 CMPx,比较器输出 OCx 会保持逻辑 0
    cmp_config.update_trigger = pwm_shadow_register_update_on_modify; // 设置CMP影子寄存器值生效时刻为 更新后的下一个周期
    // pwm_shadow_register_update_on_modify 这种方式下一个指令周期就会重装CMP,可能会导致PWM波形不完整,手册不推荐这种方式

    //pwm_stop_counter(BEEP_PWM); // 停止计数(没有也可以)
    pwm_set_reload(BEEP_PWM, 0, pwm_reload); // 设置RLD寄存器
    pwm_set_start_count(BEEP_PWM, 0, 0); // 设置STA寄存器

    // 使用给定参数对PWM通道进行设置
    if (status_success != pwm_setup_waveform(BEEP_PWM, BEEP_OUT, &pwm_config, cmp_index, &cmp_config, 1)) {
        printf("failed to setup waveform\\n");
        while(1);
    }
   
    pwm_start_counter(BEEP_PWM); // 开始计数(PWM输出开始)
    pwm_issue_shadow_register_lock_event(BEEP_PWM); // 锁定影子寄存器

    // 和 cmp = pwm_reload + 1 一起使用,也可以得到 50% 占空比的 PWM波形
    // 在这里更新CMP影子寄存器,下一个周期CMP寄存器会得到更新,这种方式便于动态更新PWM波形
    // pwm_update_raw_cmp_edge_aligned(BEEP_PWM, cmp_index, pwm_reload / 2); // 50 % HIGH

    board_delay_ms(duration_ms); // 按参数延时一段时间,这段时间一直会由PWM波形输出
    pwm_disable_output(BEEP_PWM, BEEP_OUT); // 停止输出PWM信号
}

int main(void)
{
    board_init();
    board_init_beep_pwm_pins();
    printf("pwm_beep start!\\n");

    printf("Generate PWM waveform to BEEP...\\n");
    gen_pwm_to_beep(440, 1000); // 440Hz, 1秒

    printf("pwm_beep done!\\n");
    while(1);

    return 0;
}

代码里以及加了很多注释,这里就不作详细解释了。当然,更多细节需要参考HPM6750用户手册里面的寄存器说明,以及hpm_pwm_drv.h里面的注释。

效果演示:

hpm_pwm_beep

 

本文来自论坛,点击查看完整帖子内容。

发表评论 评论 (1 个评论)
回复 Neeooc 2022-5-30 09:58
受教了!

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章