国产紫光FPGA实现DDS信号发生器

本文详细介绍了如何在紫光FPGA上使用PDS软件实现DDS,包括新建工程、添加源文件、编写DDS程序、配置IP核、编译综合、布局布线、生成bit流并下载到开发板的过程。内容涵盖按键切换波形、频率,以及不同波形(正弦、方波、三角、锯齿)的生成。最后展示了100Hz和3.5kHz四种波形的输出结果。
摘要由CSDN通过智能技术生成

本文主要参考https://blog.csdn.net/syyzuiqiang/article/details/118103211?spm=1001.2014.3001.5501一文,在国产的紫光FPGA上实现DDS,使用的EDA软件是紫光PDS,开发板是国产紫光同创PGL22G-6MBG324(软件下载地址PDS Lite软件-紫光同创 (pangomicro.com),该版本为试用版)

一、下载PDS、新建工程添加源文件

(1)首先在紫光的官网上下载PDS,安装完成后打开软件

选择New Project新建工程

设置工程名及保存目录,创建子目录然后点击Next

选择RTL project 点击Next

进入添加设计文件界面,这里我们先不添加,按照上图勾选,然后点击Next

进入添加IP界面,按照上图勾选,然后点击Next

进入添加约束文件界面,按照上图勾选,然后点击Next

根据自己使用的板子选择对应的芯片,这里我使用的是Logos系列的PGL22G-6MBG324,然后点击next,finish工程建立完成。

(2)添加设计文件

右键Design,选择Add Source

选择添加或创建设计文件 Next

选择创建文件 Next

起好名字 OK,到这里就完成了新建工程与设计文件的添加。

二、DDS程序的编写

接下来就是程序的编写,由于我用的这块板子上没有拨码开关,所以使用的是按键切换波形和频率,如果板子上有拨码开关的话建议参考本文开头引用的文章的程序

(1)首先是顶层设计文件

module dds_1
   (clk,
    rst,
    key_1,        //按键1切换频率
    key_2,        //按键2切换波形
    da_clk,       //输出DAC时钟
    data);        //11位数据输出
    input clk;
    input rst;
    input key_1;
    input key_2;
    output da_clk;
    output reg [11:0] data;
    reg [2:0] mode = 2'b0;
    reg key_2_0 = 1;
    reg key_2_1 = 1, key_2_enen = 1;
    wire [11:0] sin_wave_douta;
    wire [11:0] saw_wave_douta;
    wire [11:0] sawtooth_wave_douta;
    wire [11:0] square_wave_douta;
    wire [11:0] addra;
    wire add_mode;
    wire end_mode;
    wire non;
//按键切换波形模式,低电平有效
assign da_clk = clk;
always @(posedge clk or negedge rst)  
begin
    if(!rst) begin
        key_2_0 <= 0;
        key_2_1 <= 0;
    end
    else begin
        key_2_1 <= key_2_enen;
        key_2_0 <= key_2_1;
    end
end
assign key_2_en = key_2_0&&(~key_2_1);//检测到按键按下(下降沿)
//按键消抖,按下延时20ms后检测
    reg [0:21] del_cnt = 0;
    wire add_del;
    wire end_del;
always @(posedge clk or negedge rst)  
begin
    if(!rst) begin      //低电平有效
        del_cnt<=0;
    end    
    else if(add_del) begin
        if(end_del)
            del_cnt<=0;
        else 
            del_cnt<=del_cnt+1;
    end
    else 
        del_cnt<=0;
    
end
assign add_del = ~key_2;
assign end_del = add_del && del_cnt==1000000-1;
always @(posedge clk )  
begin
    if (end_del)
       key_2_enen<=0;
    else if (key_2) 
       key_2_enen<=1;
end
//波形控制
always @(posedge clk or negedge rst)  
begin
    if(!rst) begin      //低电平有效
        mode<=2'b0;
    end    
    else if(add_mode) begin
        if(end_mode)
            mode<=2'b0;
        else
            mode<=mode+1;
    end
end
assign add_mode=key_2_en;     //高电平有效
assign end_mode=add_mode&&mode==3;
//例化IP
assign non = 0;
    sin u_1
    (.addr(addra),
     .clk(clk),
     .rd_data(sin_wave_douta),
     .rst(non)
    );
    saw u_2
    (.addr(addra),
     .clk(clk),
     .rd_data(saw_wave_douta),
     .rst(non)
    );
     sawtooth u_3
    (.addr(addra),
     .clk(clk),
     .rd_data(sawtooth_wave_douta),
     .rst(non)
    );
    square u_4
    (.addr(addra),
     .clk(clk),
     .rd_data(square_wave_douta),
     .rst(non)
    );  
    count u_0
    (.clk_c(clk),
     .rst_c(rst),
     .key_1(key_1),
     .q(addra)
    );
//按键切换波形
always @(posedge clk)begin
    case(mode)
            1 : data = sin_wave_douta;
            3 : data = saw_wave_douta;
            2 : data = sawtooth_wave_douta;
            0 : data = square_wave_douta;
            default : data = square_wave_douta;
    endcase
end

endmodule

(2)然后是相位累加器模块

module count
    (clk_c,
     rst_c,
     key_1,
     q);
     input clk_c;
     input rst_c;
     input key_1;
     output [9:0] q;
     reg [31:0] fw = 8950;
     reg [31:0] cnt = 32'b0;
     reg key_1_0 = 1;
     reg key_1_1 = 1,key_1_enen = 1;
     reg [0:1] fwcnt = 0;
     wire add_fwcnt;
     wire end_fwcnt;
     wire key_1;
//切换频率
always @(posedge clk_c or negedge rst_c)  
begin
    if(!rst_c) begin
        key_1_0 <= 0;
        key_1_1 <= 0;
    end
    else begin
        key_1_1 <= key_1_enen;
        key_1_0 <= key_1_1;
    end
end
assign key_1_en = key_1_0&&(~key_1_1);//低电平有效

    reg [0:21] del_cnt = 0;
    wire add_del;
    wire end_del;
always @(posedge clk_c or negedge rst_c)  
begin
    if(!rst_c) begin      //低电平有效
        del_cnt<=0;
    end    
    else if(add_del) begin
        if(end_del)
            del_cnt<=0;
        else 
            del_cnt<=del_cnt+1;
    end
    else 
        del_cnt<=0;
    
end
assign add_del = ~key_1;
assign end_del = add_del && del_cnt==1000000-1;
always @(posedge clk_c )  
begin
    if (end_del)
       key_1_enen<=0;
    else if (key_1) 
       key_1_enen<=1;
end

always @(posedge clk_c or negedge rst_c) //高电平有效
begin
    if(!rst_c) begin      //低电平有效
        fwcnt<=0;
    end    
    else if(add_fwcnt) begin
         
         if(end_fwcnt)
            fwcnt<=0;
         else
            fwcnt<=fwcnt+1;
    end
end
assign add_fwcnt=key_1_en;     //高电平有效
assign end_fwcnt=add_fwcnt&&fwcnt==3;

always @(posedge clk_c)
begin
    case(fwcnt)
            0 : fw = 8590; //100HZ
            1 : fw = 42949;//500HZ
            2 : fw = 128849;//1500HZ
            3 : fw = 300647;//3500HZ
            default : fw = 8590;
    endcase
end
     
always @(posedge clk_c or negedge rst_c)
begin
    if(!rst_c) begin      //低电平有效
        cnt<=0;
    end    
    else 
        cnt<=cnt+fw;
end

assign q = cnt[31:22];

endmodule

(3)接下来是导入ROM

在工具栏找到IP或者可以通过右键设计文件添加

选中ROM,起好名字,点击Customize

在这里进行IP核的配置,在这里需要添加IP核的初始化文件sin.dat。PDS的初始化文件支持十六进制和二进制,格式相对比较简单,只要把十六进制或二进制数据写入到.dat文件中即可,这里我用的是matlab生成的初始化文件,具体方法如下:

首先是代码,用matlab生成正弦波(这里的地址深度为10即共有1024个数据,数据宽度是12即正弦波峰处为4095)

x = linspace(0,6.28,1024); %在0和2pi间取1024个点
y2 = sin (x)+1;
y3 = sawtooth (x,0.5)+1;
y4 = sawtooth (x,1)+1;
y5 = square (x)+1;
y2 =round (y2 * 2047);
y3 = round(y3 * 2047);
y4 = round(y4 * 2047);
y5 = round(y5 * 2047);
fid = fopen('c:/sin.txt','wt'); % 生成TXT文件
fprintf(fid,'%x\n',y2);
fclose(fid);
fid = fopen('c:/sawtooth.txt','wt'); % 生成TXT文件
fprintf(fid,'%x\n',y3);
fclose(fid);
fid = fopen('c:/saw.txt','wt'); % 生成TXT文件
fprintf(fid,'%x\n',y4);
fclose(fid);
fid = fopen('c:/square.txt','wt'); % 生成TXT文件
fprintf(fid,'%x\n',y5);
fclose(fid);

运行过后就会在c盘中生成四种波形数据的.txt文件,这时只需要将后缀改成.dat即可,然后导入到初始化文件

点击Generate,生成IP核

三、编译,综合,布局布线及添加约束文件

(1)编译,综合

(2)添加约束文件

PDS的约束文件可以通过手写代码或者图形界面来配置,这里介绍图形界面配置引脚的方法

①首先在工具栏上找到User Constraint Editor

②选中Device,点击I/O

③具体引脚分配情况如下

也可添加代码

define_attribute {p:data[11]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[11]} {PAP_IO_LOC} {P11}
define_attribute {p:data[11]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[11]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[11]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[11]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[10]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[10]} {PAP_IO_LOC} {T13}
define_attribute {p:data[10]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[10]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[10]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[10]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[9]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[9]} {PAP_IO_LOC} {R13}
define_attribute {p:data[9]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[9]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[9]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[9]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[8]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[8]} {PAP_IO_LOC} {P13}
define_attribute {p:data[8]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[8]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[8]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[8]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[7]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[7]} {PAP_IO_LOC} {P14}
define_attribute {p:data[7]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[7]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[7]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[7]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[6]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[6]} {PAP_IO_LOC} {R15}
define_attribute {p:data[6]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[6]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[6]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[6]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[5]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[5]} {PAP_IO_LOC} {R14}
define_attribute {p:data[5]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[5]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[5]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[5]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[4]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[4]} {PAP_IO_LOC} {T16}
define_attribute {p:data[4]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[4]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[4]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[4]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[3]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[3]} {PAP_IO_LOC} {R16}
define_attribute {p:data[3]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[3]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[3]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[3]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[2]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[2]} {PAP_IO_LOC} {U16}
define_attribute {p:data[2]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[2]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[2]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[2]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[1]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[1]} {PAP_IO_LOC} {V16}
define_attribute {p:data[1]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[1]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[1]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[1]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[0]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[0]} {PAP_IO_LOC} {V18}
define_attribute {p:data[0]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[0]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[0]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[0]} {PAP_IO_SLEW} {FAST}
define_attribute {p:da_clk} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:da_clk} {PAP_IO_LOC} {P12}
define_attribute {p:da_clk} {PAP_IO_VCCIO} {3.3}
define_attribute {p:da_clk} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:da_clk} {PAP_IO_DRIVE} {4}
define_attribute {p:da_clk} {PAP_IO_SLEW} {FAST}
define_attribute {p:clk} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:clk} {PAP_IO_LOC} {B5}
define_attribute {p:clk} {PAP_IO_VCCIO} {3.3}
define_attribute {p:clk} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:key_1} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:key_1} {PAP_IO_LOC} {V12}
define_attribute {p:key_1} {PAP_IO_VCCIO} {3.3}
define_attribute {p:key_1} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:key_2} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:key_2} {PAP_IO_LOC} {P17}
define_attribute {p:key_2} {PAP_IO_VCCIO} {3.3}
define_attribute {p:key_2} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:rst} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:rst} {PAP_IO_LOC} {U12}
define_attribute {p:rst} {PAP_IO_VCCIO} {3.3}
define_attribute {p:rst} {PAP_IO_STANDARD} {LVCMOS33}
#define_attribute {p:data[11]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[10]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[9]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[8]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[7]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[6]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[5]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[4]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[3]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[2]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[1]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[0]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:da_clk} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:clk} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:key_1} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:key_2} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:rst} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[11]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[10]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[9]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[8]} {PAP_IO_REGISTER} {TRUE}
define_attribute {p:key_1} {PAP_IO_PULLUP} {TRUE}
define_attribute {p:key_1} {PAP_IO_HYS_DRIVE_MODE} {NOHYS}
define_attribute {p:key_2} {PAP_IO_PULLUP} {TRUE}
define_attribute {p:key_2} {PAP_IO_HYS_DRIVE_MODE} {NOHYS}

(3)布局布线

四、生成bit流并下载到板子中

点击Generate Bitstream,生成bit流文件

接下来将bit流文件下载到板子中

(1)首先将板子接好,点击Configuration

(2)点击scan device,连接开发板

(3)选择bit流文件,点击open

(4)右键选择program,进行程序的下载

以上下载bit流文件的方式掉电之后会消失,如果要将程序固化到flash中还需进行以下操作

1. 首先,需要 sbit 文件转换成能下载的 flash 的 sfc 文件。在完成下载和调试后,选择菜 单"Operations"下"Convert File"进行文件转换。

2.然后弹出如下界面,这里要根据硬件的 flash 型号来选择 flash 的厂家和设备型号,开发板用到的是 WINBOND 的 W25Q128Q。Flash Read Mode 选择 SPI X4 然后选择要转换的 sbit 文件, 点击 OK 即可转换

3.右键,选择Scan Outer Flash,选中刚刚生成的.sfc文件open

4.右键Outer Flash,program下载完成

五、结果展示

(1)100Hz正弦波

(2)100Hz方波

(3)100Hz三角波

(4)100Hz锯齿波

(5)3.5kHz正弦波

(6)3.5kHz方波

(7)3.5kHz三角波

(8)3.5kHz锯齿波

基于紫光同创FPGA(Field-Programmable Gate Array)进行多模块数据采集的代码演示通常涉及到硬件描述语言如VHDL或Verilog以及专用IP(Intellectual Property),这里我会提供一个简化的示例,假设我们使用Zynq SoC架构: 首先,你需要定义FPGA中的硬件模块(Hardware Description Language, HDL): ```vhdl entity DataCollector is Port ( clk : in std_logic; -- 主时钟 data_in : in std_logic_vector; -- 输入数据 module_select : in integer; -- 选择哪个模块 data_out : out std_logic_vector -- 输出数据 ); end DataCollector; ``` 然后,编写模块的结构体和功能部分: ```vhdl architecture Behavioral of DataCollector is component ModuleA or ModuleB is ...; -- 根据模块类型的不同 Port (...); end Component; signal local_data : std_logic_vector; begin case module_select is when 0 -> use ModuleA; when 1 -> use ModuleB; ... -- 其他模块情况 default -> null; -- 没有选择的默认处理 end case; process(clk) begin if rising_edge(clk) then if reset = '0' then local_data <= (others => 'X'); -- 初始化 else if valid_input then local_data <= data_in; -- 接收输入并传递到对应的模块 end if; data_out <= ModuleA_or_B_output; -- 取模块输出 end if; end if; end process; end Behavioral; ``` 最后,在软件层面上,通过SDK连接到FPGA并配置,你可以读取各个模块的数据: ```c #include <zynqpsdk.h> void setup() { // FPGA初始化,配置通道选择等 configureModuleSelect(0); -- 设置为ModuleA while (true) { uint8_t data = readDataFromFPGA(); -- 从FPGA读取数据 handleData(data); -- 处理接收到的数据 } } // 省略实际数据读取和处理函数... ``` 这只是一个基本的示例,实际应用可能需要考虑更多细节,比如错误处理、同步信号处理和更复杂的模块选择机制。具体的代码实现会因紫光同创FPGA的具体平台和工具链而有所不同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值