多路 PWM 原理分析以及实现试验

基本原理(参考 http͗//www͘fpga4fun͘com/PWM_D_C͘html)

PWM_(Pulse_Width_Modulation)

A PWM takes an input value of any width and creates an output that is just
one-bit wide.
PWM using a free-running counter

That’s the simplest PWM we can make.

module PWM(
input clk,
input [3:0] PWM_in,
output PWM_out
); reg [3:0] cnt;
always @(posedge clk) cnt <= cnt + 1'b1;  // free-running
counter assign PWM_out = (PWM_in > cnt);  // comparator
endmodule 

We choose a 4bit PWM here so the PWM period is 16. The input
can go from 0 to 15 so the PWM output ratio goes from 0% to
15/16=93%. If you need to be able to go up to 100%, the input needs
to have an extra bit.

The code works fine, although it is a bit naive in its current form
because the input must be fixed (or change only when the counter
overflows = goes back to 0). Otherwise the output will glitch. So most
likely a bit of extra logic is required (usually in the form of a latch
capturing the input at the right time).

PWM using a loadable up-down counter

That’s a slightly more sophisticated design.

module PWM(
input clk,
input [3:0] PWM_in,
output PWM_out
);
reg [3:0] cnt;
reg cnt_dir;  // 0 to count up, 1 to count down
wire [3:0] cnt_next = cnt_dir ? cnt-1'b1 : cnt+1'b1;
wire cnt_end = cnt_dir ? cnt==4'b0000 : cnt==4'b1111;
always @(posedge clk) cnt <= cnt_end ? PWM_in : cnt_next;
always @(posedge clk) cnt_dir <= cnt_dir ^ cnt_end;
assign PWM_out = cnt_dir;
endmodule

First-order sigma-delta modulator

A first-order sigma-delta modulator resembles a PWM, but with a
better frequency response if you need to filter it because of its higher
frequency output content.

The simplest way to create a first-order sigma-delta modulator is
to use an hardware accumulator… every time the accumulator
overflows, output a ‘1’. Otherwise output a ‘0’. That’s very easily done
in an FPGA.

Verilog module PWM(clk, PWM_in, PWM_out);
input clk;
input [7:0] PWM_in;
output PWM_out; reg [8:0] PWM_accumulator;
always @(posedge clk) PWM_accumulator <=
PWM_accumulator[7:0] + PWM_in; assign PWM_out = PWM_accumulator[8];
endmodule 

One-bit DAC

Take one pin of an FPGA, connect a speaker and listen to an MP3?
Easy. Here, we’ll use a PC to decode an MP3, and then send the
decoded data to an FPGA that is configured as a one-bit DAC.

Audio output

We require a DAC (digital-to-analog converter) to connect the FPGA
(digital) to a speaker (analog).
The conventional approach would be to use a resistor ladder
(see here), or use a dedicated DAC IC, like the venerable DAC-08.

Since the frequency at which FPGAs run is so fast compared to the
frequencies required in the audio domain (MHZ’s compared to KHz’s),
a one-bit DAC is a better choice.

Basically, to create the analog output, we smooth out the PWM or
sigma-delta modulator output pulses using a low-pass filter. A
sigma-delta modulator is better because of its higher-frequency
output content, with which a single-order low-pass RC filter is usually
enough.

Playing an MP3

The first step is to decode the MP3. The decoded MP3 data is called
“PCM” data. To keep things simple, we send the PCM data through the
serial port of the PC to the FPGA. The maximum rate possible through
the serial port is 115200 baud (i.e. about 11.5 KBytes per second), so
the music has to be down-sampled to 11KHz 8 bits. All that is easily
done by a PC. Here’s the software used (with source code).
And for the HDL code, we simply modify the sigma-delta modulator so
that the PWM data input comes from the serial port.

module PWM(input clk, input RxD, output PWM_out);
wire RxD_data_ready;
wire [7:0] RxD_data;
async_receiver
deserializer(.clk(clk), .RxD(RxD), .RxD_data_ready(RxD_data_ready), .RxD_d
ata(RxD_data)); reg [7:0] RxD_data_reg;
always @(posedge clk) if(RxD_data_ready) RxD_data_reg <= RxD_data;
reg [8:0] PWM_accumulator;
always @(posedge clk)   PWM_accumulator   <=   PWM_accumulator[7:0]   +
RxD_data_reg; assign PWM_out = PWM_accumulator[8];
endmodule 

Now is time to connect a speaker to the FPGA. There are 3 basic ways
to do it.

使用创建和封装 IP 向导创建自定义 IP

1.使用提供的 axi_lite 从属外设模板和自定义 IP 源代码来创建自定义 IP。

打开 Vivado 软件,单击 Manage IP,然后选择 New IP Location,然后在新
建 IP 位置窗口中单击 Next。
选择 Verilog 作为 Target Language,Mixed 作为 Simulator language,对于
IP 位置,请键入 D:/IP Core,然后单击 Finish(将其他设置保留为默认值,如果
提示创建目录,请单击确定)。

2.运行创建和封装 IP向导 选择 Tools > Create and Package New IP…


在下一个窗口中,
单击 Next

由于我们需要挂在到总线上,因此创建一个带
AXI 总线的用户 IP,故选择 Create a new AXI4 peripheral。 点击 Next.

设置
IP 的名字为 ADAU1761,版本号默认,并且记住 IP的位置 ,单击 Next.

设置总线形式为
Lite 总线, Lite 总线是简化的 AXI 总线消耗的资源少,当然性
能也是比完全版的 AXI 总线差一点,但是由于音频的速度并不高,因此采用 Lite 总线就够了,设置寄存器数量为 8,因为后面我们需要用到 8个寄存器。 单击
Next.

选择
edit IP 单击 Finish 完成

完成后的界面如下图所示

3.用户 IP 的修改

IP创建完成后,并不能立马使用,还需要做一些修改。

  1. 打开PWM_IP_v1_0.v 文件在以下位置修改

  2. 打开PWM_IP_v1_0_S00_AXI.v 修改PWM_IP_v1_0_S00_AXI.v 的端口部分
  3. slv_reg0、slv_reg1、slv_reg2、slv_reg3、slv_reg4、slv_reg5、slv_reg6、slv_reg7 为PS 部分写入PL 的寄存器。通过这个8 个寄存器的值,我们可以控制PWM 的占空比。
  4. 下面这段代码就是PS 写PL 部分的寄存器,一共有8 个寄存器

always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
slv_reg0 <= 0;
slv_reg1 <= 0;
slv_reg2 <= 0;
slv_reg3 <= 0;
slv_reg4 <= 0;
slv_reg5 <= 0;
slv_reg6 <= 0;
slv_reg7 <= 0;
end
else begin
if (slv_reg_wren)
begin
case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
3'h0:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 0
slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h1:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 1
slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h2:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 2
slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h3:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 3
slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h4:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 4
slv_reg4[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h5:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 5
slv_reg5[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h6:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 6
slv_reg6[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
3'h7:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 7
slv_reg7[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
default : begin
slv_reg0 <= slv_reg0;
slv_reg1 <= slv_reg1;
slv_reg2 <= slv_reg2;
slv_reg3 <= slv_reg3;
slv_reg4 <= slv_reg4;
slv_reg5 <= slv_reg5;
slv_reg6 <= slv_reg6;
slv_reg7 <= slv_reg7;
end
endcase
end
end
end

5新建一个PWM_driver.v 文件实现8 路PWM 并行输出然后保存到PWM_IP_1.0/hdl 文件夹,并添加进来。

PWM_driver.v具体内容为:
module PWM_driver(
input clk_i,
input rst_n_i,
input [31:0]pwm_reg0_i,
input [31:0]pwm_reg1_i,
input [31:0]pwm_reg2_i,
input [31:0]pwm_reg3_i,
input [31:0]pwm_reg4_i,
input [31:0]pwm_reg5_i,
input [31:0]pwm_reg6_i,
input [31:0]pwm_reg7_i,
output reg [7:0] pwm_o
);
reg[31:0]pwm_cnt0;
reg [31:0]pwm_cnt1;
reg [31:0]pwm_cnt2;
reg [31:0]pwm_cnt3;
reg [31:0]pwm_cnt4;
reg [31:0]pwm_cnt5;
reg [31:0]pwm_cnt6;
reg [31:0]pwm_cnt7;
//pwm0
always @(posedge clk_i)begin
if(!rst_n_i)begin
pwm_cnt0 <= 32'd0;
pwm_o[0] <= 1'b0;
end
else begin
if(pwm_cnt0<pwm_reg0_i)begin
pwm_cnt0 <= pwm_cnt0 +1'b1;
end
else begin
pwm_cnt0<=32'D0;
pwm_o[0]<=~pwm_o[0];
end
end
end
//pwm1
always @(posedge clk_i)begin
if(!rst_n_i)begin
pwm_cnt1 <= 32'd0;
pwm_o[1] <= 1'b0;
end
else begin
if(pwm_cnt1<pwm_reg1_i)begin
pwm_cnt1 <= pwm_cnt1 +1'b1;
end
else begin
pwm_cnt1<=32'D0;
pwm_o[1]<=~pwm_o[1];
end
end
end
//pwm2
always @(posedge clk_i)begin
if(!rst_n_i)begin
pwm_cnt2 <= 32'd0;
pwm_o[2] <= 1'b0;
end
else begin
if(pwm_cnt2<pwm_reg2_i)begin
pwm_cnt2 <= pwm_cnt2 +1'b1;
end
else begin
pwm_cnt2<=32'D0;
pwm_o[2]<=~pwm_o[2];
end
end
end
//pwm3
always @(posedge clk_i)begin
if(!rst_n_i)begin
pwm_cnt3 <= 32'd0;
pwm_o[3] <= 1'b0;
end
else begin
if(pwm_cnt3<pwm_reg3_i)begin
pwm_cnt3 <= pwm_cnt3 +1'b1;
end
else begin
pwm_cnt3<=32'D0;
pwm_o[3]<=~pwm_o[3];
end
end
end
//pwm4
always @(posedge clk_i)begin
if(!rst_n_i)begin
pwm_cnt4 <= 32'd0;
pwm_o[4] <= 1'b0;
end
else begin
if(pwm_cnt4<pwm_reg4_i)begin
pwm_cnt4 <= pwm_cnt4 +1'b1;
end
else begin
pwm_cnt4<=32'D0;
pwm_o[4]<=~pwm_o[4];
end
end
end
//pwm5
always @(posedge clk_i)begin
if(!rst_n_i)begin
pwm_cnt5 <= 32'd0;
pwm_o[5] <= 1'b0;
end
else begin
if(pwm_cnt5<pwm_reg5_i)begin
pwm_cnt5 <= pwm_cnt5 +1'b1;
end
else begin
pwm_cnt5<=32'D0;
pwm_o[5]<=~pwm_o[5];
end
end
end
//pwm5
always @(posedge clk_i)begin
if(!rst_n_i)begin
pwm_cnt6 <= 32'd0;
pwm_o[6] <= 1'b0;
end
else begin
if(pwm_cnt6<pwm_reg6_i)begin
pwm_cnt6 <= pwm_cnt6 +1'b1;
end
else begin
pwm_cnt6<=32'D0;
pwm_o[6]<=~pwm_o[6];
end
end
end
//pwm7
always @(posedge clk_i)begin
if(!rst_n_i)begin
pwm_cnt7 <= 32'd0;
pwm_o[7] <= 1'b0;
end
else begin
if(pwm_cnt7<pwm_reg7_i)begin
pwm_cnt7 <= pwm_cnt7 +1'b1;
end
else begin
pwm_cnt7<=32'D0;
pwm_o[7]<=~pwm_o[7];
end
end
end
endmodule

6.点击File–>点击 Save all files,最终如下

4.修改完成后还要重新打包
1 选择 tool–>Create and Package New Ip…
2. 选择 package your current project 选择 next

3.保持默认设置,不做任何修改,点击 Next

4.点击 Next 选择 Overwrite

5.点击 Finish,完成。

6.执行以下操作检查 IP是否封装完成,展开 IP XACT(1)>双击
component.xml,展开 Ports and Interface,可以看到封装 IP完成。

至此,创建用户
IP完成。
5添加 PWM_IP_v1_0 IP
重新建立一个新的空的工程。
Create Block Design 直接添加 zynq7 processing system。这个前面的教程内容部分已经重复很多次了,这里不重复。
1.在进程导航窗格中,单击 Project Setting选项,选择 IP,然后单击 Add Repository 按钮。 浏览窗口打开,浏览到 IP核的位置

D:/IP Core/ip_repo/PWM_IP_1.0 然后单击“ Select”,单击 Ok。

2.注意工具如何在目录中检测到新的 IP,点击 Apply,然后 OK。

3.这样做后,就可以将 PWM_IP_v1.0添加到当前项目的 IP库中,下一步是
将其添加到块设计中,并将其从一侧连接到 Zynq处理系统,并从另一侧通过使
用外部端口连接到 LED板块 。
4.由于前面的过程非常详细,很多步骤省略,搭建完成后的系统如下图所示

5.添加 管脚约束 文件,之后编译工程,导出到 SDK,这个步骤前面的教程中
也是重复过,不再过多重复,这里只给出 管脚约束 文件 PWM_pin.xdc

set_property PACKAGE_PIN T22 [get_ports {pwm_o[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {pwm_o[0]}]
set_property PACKAGE_PIN T21 [get_ports {pwm_o[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {pwm_o[1]}]
set_property PACKAGE_PIN U22 [get_ports {pwm_o[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {pwm_o[2]}]
set_property PACKAGE_PIN U21 [get_ports {pwm_o[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {pwm_o[3]}]
set_property PACKAGE_PIN V22 [get_ports {pwm_o[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {pwm_o[4]}]
set_property PACKAGE_PIN W22 [get_ports {pwm_o[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {pwm_o[5]}]
set_property PACKAGE_PIN U19 [get_ports {pwm_o[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {pwm_o[6]}]
set_property PACKAGE_PIN U14 [get_ports {pwm_o[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {pwm_o[7]}]

6.SDK 工程源码
SDK 工程部分的 C 工程新建工程也不详细讲解,前面已经重复很多次了,
这里只给出 C 代码略作分析。

#include "xparameters.h"
#include "stdio.h"
#include "xil_io.h"
#define PWM_REG0 XPAR_PWM_IP_V1_0_0_BASEADDR + 0
#define PWM_REG1 XPAR_PWM_IP_V1_0_0_BASEADDR + 4
#define PWM_REG2 XPAR_PWM_IP_V1_0_0_BASEADDR + 8
#define PWM_REG3 XPAR_PWM_IP_V1_0_0_BASEADDR + 12
#define PWM_REG4 XPAR_PWM_IP_V1_0_0_BASEADDR + 16
#define PWM_REG5 XPAR_PWM_IP_V1_0_0_BASEADDR + 20
#define PWM_REG6 XPAR_PWM_IP_V1_0_0_BASEADDR + 24
#define PWM_REG7 XPAR_PWM_IP_V1_0_0_BASEADDR + 28
int main()
{Xil_Out32(PWM_REG0,100000000);
Xil_Out32(PWM_REG1,100000000/2);
Xil_Out32(PWM_REG2,100000000/4);
Xil_Out32(PWM_REG3,100000000/8);
Xil_Out32(PWM_REG4,100000000/10);
Xil_Out32(PWM_REG5,100000000/16);
Xil_Out32(PWM_REG6,100000000/20);
Xil_Out32(PWM_REG7,100000000/32);
return 0;
}

以上函数中
我们自定一点 PWM IP 寄存器中写 数据,实现了 100MHZ AXI4 总线的 2 分
频、 4分频、 8 分频、 10 分频、 16 分频、 20 分频、 32 分频。
测试完成,现象:
8个 灯 流水 亮起。

ZYNQ7000学习(十八)多路 PWM 原理分析以及实现试验相关推荐

  1. PyTorch框架学习十八——Layer Normalization、Instance Normalization、Group Normalization

    PyTorch框架学习十八--Layer Normalization.Instance Normalization.Group Normalization 一.为什么要标准化? 二.BN.LN.IN. ...

  2. C1认证学习十八、十九(表单元素、转义字符)

    C1认证学习十八.十九(表单元素.语义化标签) 十八 任务背景 HTML的表单用于收集用户的输入,表单元素是指的不同类型的input元素,复选框,单选按钮,提交按钮等等. 任务目标 掌握表单标签以及其 ...

  3. Android网络编程(八) 之 HttpURLConnection原理分析

    1 使用回顾 我们在前面博文<Android网络编程(四) 之 HttpClient与HttpURLConnection>中已经对HttpURLConnection的使用进行过介绍.今天我 ...

  4. slam 学习之 AMCL 概念与原理分析

    AMCL(adaptive Monte Carlo Localization)自适应蒙特卡洛定位,A也可以理解为augmented,是机器人在二维移动过程中概率定位系统,采用粒子滤波器来跟踪已经知道的 ...

  5. java aqs实现原理_JAVA基础学习之-AQS的实现原理分析

    AbstractQueuedSynchronizer是JUC的核心框架,其设计非常精妙. 使用了 Java 的模板方法模式. 首先试图还原一下其使用场景: 对于排他锁,在同一时刻,N个线程只有1个线程 ...

  6. 第二十八课.AlphaGo实例分析

    目录 AlphaGo概述 AlphaGo的Policy Network 网络结构 训练方法 Behavior Cloning预训练 使用Policy Gradient继续训练策略网络 使用策略网络下棋 ...

  7. Jmeter(四十八)_动态线程分析HTML测试报告

    概述 jmeterGUI模式下,性能测试的结果往往误差很大,因为GUI本身就会消耗一部分资源.所以我们常常用命令行去跑性能脚本,得出结果 同时,jmeter在命令行下还可以生成多维度的测试报告,里面包 ...

  8. 强化学习(十八) 基于模拟的搜索与蒙特卡罗树搜索(MCTS)

    在强化学习(十七) 基于模型的强化学习与Dyna算法框架中,我们讨论基于模型的强化学习方法的基本思路,以及集合基于模型与不基于模型的强化学习框架Dyna.本文我们讨论另一种非常流行的集合基于模型与不基 ...

  9. [系统安全] 二十八.WannaCry勒索病毒分析 (4)全网“最“详细的蠕虫传播机制解读

    您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...

最新文章

  1. R 语言 download.file 的几点知识
  2. 转:Java NIO系列教程(九) Pipe
  3. 电力系统通信站和设备管理系统分析设计
  4. DynamicArray
  5. 腾讯35k招.NET Core开发,深扒这些技术要求 真的很难吗?
  6. 探讨.NET Core的未来
  7. Java多线程(3)--线程的生命周期
  8. NHK SHV 的 22.2 声道音频系统
  9. 《java程序员全攻略:从小工到专家》连载一:外行人眼中的IT人
  10. Windows Mobile 6 中为开发人员提供的新功能
  11. 2018蓝桥杯模拟赛(一)--青出于蓝而胜于蓝(线段树)
  12. vue实现非同源图片的下载功能--跨域问题(解决浏览器打开图片,而不是下载)
  13. 1880-2010年间全美婴儿姓名
  14. 计算机是什么信号转换为什么信号,模拟信号转化为数字信号的原理是什么
  15. 整合腾讯云地图的绘制和编辑几何图形
  16. 机器学习在金融大数据风险建模中的应用
  17. Unity DOTS 介绍
  18. 运动控制卡的函数库与Linux,运动控制卡应用开发教程之ROS(下)
  19. 垃圾分类查询小程序(可回收物、有害垃圾、干垃圾、湿垃圾)
  20. STM32驱动74HC165原理图加程序

热门文章

  1. 魔术的逻辑(三)——明明是假的,但为何奇迹依旧美妙?
  2. mysql list database_Navicat Premium 操作MySQL数据库
  3. css常见效果——棱形图片
  4. 大数据----Hive学习(6)----Hive 基本操作4
  5. 小谈Oben_Pai——AI+区块链的超级APP落地应用
  6. c语言程序流程控制(下)
  7. python面向过程学习笔记
  8. docker-jenkins将打包的镜像推送到镜像仓库
  9. python matplotlib绘制伯德图
  10. 格林纳达常驻WTO大使孙宇晨视频会见马来西亚驻WTO大使