日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時間:2024/3/7 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ZYNQ7000学习(十八)多路 PWM 原理分析以及实现试验 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

多路 PWM 原理分析以及實現(xiàn)試驗

基本原理(參考 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.

使用創(chuàng)建和封裝 IP 向導創(chuàng)建自定義 IP

1.使用提供的 axi_lite 從屬外設模板和自定義 IP 源代碼來創(chuàng)建自定義 IP。

打開 Vivado 軟件,單擊 Manage IP,然后選擇 New IP Location,然后在新
建 IP 位置窗口中單擊 Next。
選擇 Verilog 作為 Target Language,Mixed 作為 Simulator language,對于
IP 位置,請鍵入 D:/IP Core,然后單擊 Finish(將其他設置保留為默認值,如果
提示創(chuàng)建目錄,請單擊確定)。

2.運行創(chuàng)建和封裝 IP向導 選擇 Tools > Create and Package New IP…


在下一個窗口中,
單擊 Next

由于我們需要掛在到總線上,因此創(chuàng)建一個帶
AXI 總線的用戶 IP,故選擇 Create a new AXI4 peripheral。 點擊 Next.

設置
IP 的名字為 ADAU1761,版本號默認,并且記住 IP的位置 ,單擊 Next.

設置總線形式為
Lite 總線, Lite 總線是簡化的 AXI 總線消耗的資源少,當然性
能也是比完全版的 AXI 總線差一點,但是由于音頻的速度并不高,因此采用 Lite 總線就夠了,設置寄存器數(shù)量為 8,因為后面我們需要用到 8個寄存器。 單擊
Next.

選擇
edit IP 單擊 Finish 完成

完成后的界面如下圖所示

3.用戶 IP 的修改

IP創(chuàng)建完成后,并不能立馬使用,還需要做一些修改。

  • 打開PWM_IP_v1_0.v 文件在以下位置修改

  • 打開PWM_IP_v1_0_S00_AXI.v 修改PWM_IP_v1_0_S00_AXI.v 的端口部分
  • slv_reg0、slv_reg1、slv_reg2、slv_reg3、slv_reg4、slv_reg5、slv_reg6、slv_reg7 為PS 部分寫入PL 的寄存器。通過這個8 個寄存器的值,我們可以控制PWM 的占空比。
  • 下面這段代碼就是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 文件實現(xiàn)8 路PWM 并行輸出然后保存到PWM_IP_1.0/hdl 文件夾,并添加進來。

    PWM_driver.v具體內(nèi)容為: 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.執(zhí)行以下操作檢查 IP是否封裝完成,展開 IP XACT(1)>雙擊
    component.xml,展開 Ports and Interface,可以看到封裝 IP完成。

    至此,創(chuàng)建用戶
    IP完成。
    5添加 PWM_IP_v1_0 IP
    重新建立一個新的空的工程。
    Create Block Design 直接添加 zynq7 processing system。這個前面的教程內(nèi)容部分已經(jīng)重復很多次了,這里不重復。
    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處理系統(tǒng),并從另一側通過使
    用外部端口連接到 LED板塊 。
    4.由于前面的過程非常詳細,很多步驟省略,搭建完成后的系統(tǒng)如下圖所示

    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 工程新建工程也不詳細講解,前面已經(jīng)重復很多次了,
    這里只給出 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; }

    以上函數(shù)中
    我們自定一點 PWM IP 寄存器中寫 數(shù)據(jù),實現(xiàn)了 100MHZ AXI4 總線的 2 分
    頻、 4分頻、 8 分頻、 10 分頻、 16 分頻、 20 分頻、 32 分頻。
    測試完成,現(xiàn)象:
    8個 燈 流水 亮起。

    總結

    以上是生活随笔為你收集整理的ZYNQ7000学习(十八)多路 PWM 原理分析以及实现试验的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。