查看: 1150|回复: 0
收起左侧

教你玩转[16]_RVSTAR—PWM使用篇

[复制链接]

  离线 

  • TA的每日心情
    慵懒
    2021-7-23 17:16
  • 签到天数: 17 天

    [LV.4]

    发表于 2021-6-9 17:54:57 | 显示全部楼层 |阅读模式

    有人预言,RISC-V或将是继Intel和Arm之后的第三大主流处理器体系。欢迎访问全球首家只专注于RISC-V单片机行业应用的中文网站

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    本帖最后由 草帽王子 于 2021-6-9 19:31 编辑

    PWM(脉冲宽度调制)可用于电机的调速、LED的亮度调节、无源蜂鸣器输出音调等,是嵌入式系统开发中经常采用的方法。本期内容以一个用无源蜂鸣器播放音乐的例子,带领大家了解使用定时器PWM输出功能的方法。

    • 系统环境: Windows 10-64bit
    • 软件平台: NucleiStudio IDE 202102版 或 PlatformIO IDE
    • 硬件需求: RV-STAR开发板 和 无源蜂鸣器


    一、脉冲宽度调制
    GD32VF 单片机芯片及应用-教你玩转[16]_RVSTAR—PWM使用篇risc-v单片机中文社区(1)

    脉冲宽度调制(Pulse Width Modulation,PWM)是利用微控制器的数字输出来对模拟电路进行控制的一种非常有效的技术。

    PWM的控制方式就是对逆变电路开关器件的通断进行控制,使输出端得到一系列幅值相等的脉冲,用这些脉冲来代替正弦波或所需要的波形。也就是在输出波形的半个周期中产生多个脉冲,使各脉冲的等值电压为正弦波形,所获得的输出平滑且低次谐波少。按一定的规则对各脉冲的宽度进行调制,既可改变逆变电路输出电压的大小,也可改变输出频率。其中,一个周期内脉冲时间占总时间的比值称为占空比

    简言之,如果你想让电机的速度变慢或者控制的LED变暗,只需降低PWM的占空比,这使得原本需要控制模拟电压或电流才能达到的效果,通过数字方式也能够实现,这样可以大幅度降低系统的成本和功耗。


    二、无源蜂鸣器

    无源蜂鸣器是很常见的一个电子元件,相信读者朋友们都见过可以播放生日歌的电子蜡烛,那个就是靠无源蜂鸣器来播放的。无源蜂鸣器的内部不带振荡源,通过使用50%占空比、频率500Hz~4.5kHz的PWM驱动,就可以播放出不同音调的声音。
    GD32VF 单片机芯片及应用-教你玩转[16]_RVSTAR—PWM使用篇risc-v单片机中文社区(2)

    三、GD32VF103的定时器

    GD32VF103的定时器分为三种类型,分别是基本定时器、通用定时器和高级定时器,其中普通定时器和高级定时器都支持PWM输出功能,在本次的实验中使用TIMER2(普通定时器)来进行PWM输出实验。


    四、实验部分

    首先通过NucleiStudio或者PlatformIO等开发工具创建工程,然后在工程目录中添加“tone.h”和“main.c”文件,然后开始进行代码的编写。

    首先,我们要为蜂鸣器发出的不同音调所需要的频率进行预先的定义,以下的经调音测试后的D调的不同音符所对应的频率,直接复制到“tone.h”中即可:
    1. #define NTD0 -1
    2. #define NTD1 293
    3. #define NTD2 329
    4. #define NTD3 368
    5. #define NTD4 390
    6. #define NTD5 438
    7. #define NTD6 492
    8. #define NTD7 554

    9. #define NTDL1 147
    10. #define NTDL2 166
    11. #define NTDL3 185
    12. #define NTDL4 196
    13. #define NTDL5 221
    14. #define NTDL6 248
    15. #define NTDL7 278

    16. #define NTDH1 585
    17. #define NTDH2 657
    18. #define NTDH3 700
    19. #define NTDH4 781
    20. #define NTDH5 882
    21. #define NTDH6 990
    22. #define NTDH7 1112
    复制代码

    然后,在“main.c”中,我们要实现三个函数
    • buzzer_init() -- 初始化蜂鸣器
    • buzzer_on(int freq) -- 蜂鸣器根据频率发声
    • buzzer_off() -- 关闭蜂鸣器

    下面依次进行讲解,首先是buzzer_init(),我们使用的是PB1引脚,它对应着TIMER2的CH3通道,要依次使能端口时钟、复用时钟、定时器时钟,然后把引脚初始化为推挽输出模式。
    1. void buzzer_init()
    2. {
    3.     rcu_periph_clock_enable(RCU_GPIOB);
    4.     rcu_periph_clock_enable(RCU_AF);
    5.     rcu_periph_clock_enable(RCU_TIMER2);

    6.     gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
    7. }
    复制代码

    然后是buzzer_on(int freq),它接收不同的频率参数,对定时器进行配置,输出50%占空比的不同频率的PWM波,进而控制蜂鸣器发声。实现方法参考下方代码,其中要注意的是预分频系数、周期以及PWM输出的占空比等几个参数的配置。
    1. void buzzer_on(int freq)
    2. {
    3.     timer_oc_parameter_struct timer_ocinitpara;
    4.     timer_parameter_struct timer_initpara;
    5.     timer_deinit(TIMER2);
    6.     /* initialize TIMER init parameter struct */
    7.     timer_struct_para_init(&timer_initpara);
    8.     /* TIMER2 configuration */
    9.     timer_initpara.prescaler = 107;
    10.     timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
    11.     timer_initpara.counterdirection = TIMER_COUNTER_UP;
    12.     timer_initpara.period = 1000000 / freq;
    13.     timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
    14.     timer_initpara.repetitioncounter = 0;
    15.     timer_init(TIMER2, &timer_initpara);

    16.     /* initialize TIMER channel output parameter struct */
    17.     timer_channel_output_struct_para_init(&timer_ocinitpara);
    18.     /* CH3 configuration in PWM mode */
    19.     timer_ocinitpara.outputstate = TIMER_CCX_ENABLE;
    20.     timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;
    21.     timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
    22.     timer_ocinitpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH;
    23.     timer_ocinitpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW;
    24.     timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
    25.     timer_channel_output_config(TIMER2, TIMER_CH_3, &timer_ocinitpara);

    26.     /* auto-reload preload enable */
    27.     timer_auto_reload_shadow_enable(TIMER2);
    28.     timer_enable(TIMER2);

    29.     /* CH3 configuration in PWM mode1,duty cycle 50% */
    30.     timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_3, 500000 / freq);
    31.     timer_channel_output_mode_config(TIMER2, TIMER_CH_3, TIMER_OC_MODE_PWM0);
    32.     timer_channel_output_shadow_config(TIMER2, TIMER_CH_3, TIMER_OC_SHADOW_DISABLE);
    33. }
    复制代码

    第三个函数buzzer_off(),这个比较简单,只需复位定时器即可。
    1. void buzzer_off()
    2. {
    3.     timer_deinit(TIMER2);
    4. }
    复制代码

    最后需要用两个数组分别存储一段音乐的音符、节拍,这里以“小星星”的前16拍为例,然后在主循环中对这两个数组进行遍历,就可以实现播放音乐的效果。
    1. int notes[] = {
    2.     NTD1, NTD1, NTD5, NTD5,
    3.     NTD6, NTD6, NTD5,
    4.     NTD4, NTD4, NTD3, NTD3,
    5.     NTD2, NTD2, NTD1
    6. };

    7. int beats[] = {
    8.     1, 1, 1, 1,
    9.     1, 1, 2,
    10.     1, 1, 1, 1,
    11.     1, 1, 2
    12. };

    13. int main()
    14. {
    15.     buzzer_init();
    16.     int length = sizeof(notes) / sizeof(notes[0]);

    17.     while (1) {
    18.         for (int i = 0; i < length; i++) {
    19.             buzzer_on(notes[i]);
    20.             delay_1ms(500 * beats[i]);
    21.             buzzer_off();
    22.         }
    23.         delay_1ms(1000);
    24.     }
    25. }
    复制代码

    完成代码编写后,将无源蜂鸣器的(+)引脚接到RV-STAR开发板的PB5引脚上,另一个引脚接地,然后编译、上传,就可以听到蜂鸣器播放“小星星”啦~
    GD32VF 单片机芯片及应用-教你玩转[16]_RVSTAR—PWM使用篇risc-v单片机中文社区(3)

    实现源码:
    https://github.com/Nuclei-Softwa ... timer_pwmout_buzzer






    上一篇:芯来科技设计资料
    下一篇:教你玩转[17]_RVSTAR—正交编码器接口篇
    RISCV作者优文
    全球首家只专注于RISC-V单片机行业应用的中文网站
    回复

    使用道具 举报

    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    关闭

    RISC-V单片机中文网上一条 /2 下一条



    版权及免责声明|RISC-V单片机中文网 |网站地图

    GMT+8, 2024-3-29 22:03 , Processed in 0.671810 second(s), 48 queries .

    快速回复 返回顶部 返回列表