发 帖  
原厂入驻New
[经验]

【"小梅哥 AC620V2 FPGA 开发板"免费试用】+PWM控制蜂鸣器

2020-11-17 10:53:59  651 FPGA
分享
0
PWM控制对于现在的工控也好,模拟DAC输出也好均有广泛的应用,甚至在某些方面成了选择芯片的一个标准依据。普通的MCU一般定时器都带PWM发送模块,也可以用延时来模拟。那对于FPGA是如何输出PWM,跟MCU的延时翻转引脚电平类似,其实就是对时钟的分频,延时再切换引脚电平输出。但是FPGA由于工作频率非常快,它的效果远远超过普通MCU的模拟PWM输出。
MCU通过定时器产生PWM,实际上就是计时,当低于该设定的计时数量时,引脚电平状态1,当高于时,状态翻转。当到达一个周期时,计数值清0,又重新循环计时判断。如下图所示:
K2.png
而用软件模拟,直接就是引脚输出一个电平状态,延时后输出另一个状态一段延时后,又循环之前的。
FPGA无硬件的PWM发生器,但是我们可以参考MCU的思路来完成。我们先确定频率也就是一个周期需要多长时间的计时值,然后设定一个阀值,当计数从周期值开始递减,到阀值前一个电平状态,之后反转状态。计数为0,即一个周期结束后,再次循环前面的,这样就实现的了PWM的产生。

这里我们来测试PWM的一个应用,就是通过更改PWM的频率来改变蜂鸣器发出的声音,这样一段时间间隔切换一次,蜂鸣器就能发出不停的音调,也就能输出简单的音乐了。
查看开发板原理图:
M1.png
一个经典的蜂鸣器驱动电路
其中电容 C36 用于提高电路抗干扰性能。 D1 起保护三极管的作用(当三极管突然截止时,无源蜂鸣器两端产生的瞬时感应电动势可以通过 D3 迅速释放掉,避免叠加到三极管集电极上从而击穿三极管。)beep 端口接FPGA 输出管脚,使用时,只需要在beep 信号上输出pwm 波(当然这个PWM波的频率要在蜂鸣器的发生范围内),就能驱动蜂鸣器发声。

我们先看看,如何PWM的模型是如何定义的:
  1. <font color="#ff0000" size="4">//模型的定义,完全类比于MCU的定时器PWM发送模块:</font>
  2. module pwm_generator(
  3.                         Clk50M,   
  4.                         Rst_n,      
  5.                         cnt_en,
  6.                         counter_arr,
  7.                         counter_ccr,
  8.                         o_pwm
  9.                 );
  10.         input Clk50M;        //50MHz时钟输入
  11.         input Rst_n;        //复位输入,低电平复位
  12. <font color="#ff0000" size="4">        input cnt_en;        //计数使能信号
  13.         input [31:0]counter_arr;//输入32位预重装值
  14.         input [31:0]counter_ccr;//输入32位输出比较值
  15.         output reg o_pwm;        //pwm输出信号</font>

  16. <font color="#ff0000" size="4">//周期产生</font>
  17.         reg [31:0]counter;//定义32位计数器
  18.         always@(posedge Clk50M or negedge Rst_n)
  19.         if(!Rst_n)
  20.                 counter <= 32'd0;
  21.         else if(cnt_en)begin
  22.                 if(counter == 0)
  23.                         counter <= counter_arr;//计数到0,加载自动预重装寄存器值
  24.                 else
  25.                         counter <= counter - 1'b1;//计数器自减1
  26.         end
  27.         else
  28.                 counter <= counter_arr;        //没有使能时,计数器值等于预重装寄存器值

  29. <font color="#ff0000" size="4">//与阀值比较翻转电平产生PWM波形</font>
  30.         always@(posedge Clk50M or negedge Rst_n)
  31.         if(!Rst_n)        //让PWM输出信号复位时输出低电平
  32.                 o_pwm <= 1'b0;
  33.         else if(counter >= counter_ccr)//计数值大于比较值
  34.                 o_pwm <= 1'b0;        //输出为0
  35.         else        //计数值小于比较值
  36.                 o_pwm <= 1'b1; //输出为1
  37.                
  38. endmodule
复制代码
我们看到,完全类比于MCU的定时器PWM发送模块,思路几乎一样。这也看出了FPGA的有点,可以模仿已知时序或者逻辑的其他外设。
我们看看模型的实例化:
module pwm_generator_test(
                        Clk50M,    //系统时钟50M
                        Rst_n,     //系统复位
                        beep       //蜂鸣器控制输出
                );

        input Clk50M;
        input Rst_n;
        output beep;
       
        reg [31:0]counter_arr;        //预重装值寄存器
        wire [31:0]counter_ccr;        //输出比较值
       
        reg [24:0]delay_cnt;        //500ms延时计数器
        reg [4:0]Pitch_num;        //音调编号
       
        localparam
                L1 = 191130, //低音1
                L2 = 170241, //低音2
                L3 = 151698, //低音3
                L4 = 143183, //低音4
                L5 = 127550, //低音5
                L6 = 113635, //低音6
                L7 = 101234, //低音7

                M1 = 95546, //中音1
                M2 = 85134, //中音2
                M3 = 75837, //中音3
                M4 = 71581, //中音4
                M5 = 63775, //中音5
                M6 = 56817, //中音6
                M7 = 50617, //中音7

                H1 = 47823, //高音1
                H2 = 42563, //高音2
                H3 = 37921, //高音3
                H4 = 35793, //高音4
                H5 = 31887, //高音5
                H6 = 28408, //高音6
                H7 = 25309; //高音7
               
        //输出比较值为预重装值一半
        assign counter_ccr = counter_arr >> 1;

        pwm_generator pwm_generator(
                .Clk50M(Clk50M),
                .Rst_n(Rst_n),
                .cnt_en(1'b1),
                .counter_arr(counter_arr),
                .counter_ccr(counter_ccr),
                .o_pwm(beep)
        );
       
        //500ms延时计数器计数
        always@(posedge Clk50M or negedge Rst_n)
        if(!Rst_n)
                delay_cnt <= 25'd0;
        else if(delay_cnt == 0)
                delay_cnt <= 25'd24999999;
        else
                delay_cnt <= delay_cnt - 1'b1;
       
        //每500ms切换一次音调
        always@(posedge Clk50M or negedge Rst_n)
        if(!Rst_n)
                Pitch_num <= 5'd0;
        else if(delay_cnt == 0)begin
                if(Pitch_num == 5'd20)
                        Pitch_num <= 5'd0;
                else
                        Pitch_num <= Pitch_num + 5'd1;
        end
        else
                Pitch_num <= Pitch_num;
               
        //根据音调编号给预重装值给相应的值
        always@(*)
                case(Pitch_num)
                        0 :counter_arr  = L1;
                        1 :counter_arr  = L2;
                        2 :counter_arr  = L3;
                        3 :counter_arr  = L4;
                        4 :counter_arr  = L5;
                        5 :counter_arr  = L6;
                        6 :counter_arr  = L7;
                        7 :counter_arr  = M1;
                        8 :counter_arr  = M2;
                        9 :counter_arr  = M3;
                        10:counter_arr  = M4;
                        11:counter_arr  = M5;
                        12:counter_arr  = M6;
                        13:counter_arr  = M7;
                        14:counter_arr  = H1;
                        15:counter_arr  = H2;
                        16:counter_arr  = H3;
                        17:counter_arr  = H4;
                        18:counter_arr  = H5;
                        19:counter_arr  = H6;
                        20:counter_arr  = H7;
                        default:counter_arr  = L1;
                endcase
       
endmodule

我们看到蜂鸣器发出不同声调,实际上就是每个固定时间,切换PWM的不同频率而已。
如果对音调对应的周期计时数,按照一个频谱来对应的话,就能奏出美妙的音乐了。
如果要,及时更改对应频率的占空比怎么做,那就在对应的周期数边上,再加上比较值的数值就行了:
19:counter_arr  = H6;counter_ccr=counter_arr  *0.4;
我们让他输出1KPWM波形,并查看:
屏蔽其他的赋值代码:
always@(*)
counter_arr=50000;

下载查看:
Z1.jpg
PWM就到这里了。


评论

高级模式
您需要登录后才可以回帖 登录 | 注册

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
发经验
关闭

站长推荐 上一条 /7 下一条

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