基于STM32+iCE40的电赛训练平台项目4 - DDS任意波形发生器/PC远程控制
本项目使用STM32+iCE40核心板来实现DDS任意信号发生器,并借助‘基于STM32+iCE40的电赛训练平台'板卡上的DAC来实现波形的产生,并使用PC来进行远程控制波形、幅度、频率等信息
标签
FPGA
DDS
2023寒假在家练
wyk
更新2023-03-28
北京交通大学
501

一、项目需求

  • 通过板上的高速DAC(10bits/125Msps)配合FPGA内部DDS的逻辑,生成波形可调(正弦波、三角波、方波)、频率可调、幅度可调的波形
  • 生成模拟信号的频率范围为DC-5MHz,调节精度为1Hz
  • 生成模拟信号的幅度为最大1Vpp,调节范围为0.1V-1V
  • 通过UART同PC连接,在PC上可以使用Matlab、Labview或其它调试工具来控制波形的切换、参数的改变

二、整体设计思路

  • 在PC使用Matlab软件中设定幅度、频率、波形等参数。
  • PC通过USB转TTL串口使用UART协议向FPGA传输数据。
  • 在FPGA中使用DDS模块产生10位的波形数据。
  • 将10位的波形数据发给DAC,产生电压。

三、系统框图

 

FrOkzhzYMsclx6Bk2ig3LzXQt-u7

 

四、模块实现

4.1时钟模块

在该项目中,由于最大的输出频率为5MHz,又因为要保证一个正弦波观测时不发生明显失真,最少要一个周期20个点,因此最小需要100MHz的时钟信号。所以,在本项目中,使用IP核设置的输出时钟信号为120MHz,如果需要更加理想的波形,可以继续增大时钟信号的频率。

4.2UART_RX模块

在该项目中,由于需要PC上位机和FPGA通信,因此决定采用UART协议通信,同时由于只需要PC向FPGA发送信息,因此在该项目中只做了UART_RX模块。

在该项目中,设置波特率为115200,因此可以计算出在FPGA内部的一位的计数周期为

120M/115200=1041

module UART_RX(
    input clk, //120MHz
    input rst, 
    input uart_data_s, //串行数据
    
    output reg [7:0] uart_data_p, //并行数据
    output reg rx_done //接收完成信号
    );
    

    parameter B_CNT = 1042; //120M/115200 波特率计数器最大值
    
	
	
    reg [1:0] uart_data_s_r1; 
    reg [1:0] uart_data_s_r2; //边沿检测
	reg  uart_data_s_r3;
    reg uart_en; //使能信号
    reg [23:0] time_cnt = 24'd0; //115200波特率计数器
    reg [3:0] bit_cnt = 4'd0; //位计数器
	reg [2:0] judge_cnt;//判决计数器
    reg [7:0] rx_data; //接收数据寄存器
 
    
	
	
//消抖
    always @ (posedge clk)
    begin
        uart_data_s_r1 <= {uart_data_s_r1[0] , uart_data_s};
    end
 
//边沿检测
    always @ (posedge clk)
    begin
        uart_data_s_r2 <= {uart_data_s_r2[0] , uart_data_s_r1[1]};
    end
 
//使能信号控制
    always @ (posedge clk or negedge rst)
    begin
        if (!rst)
            uart_en <= 1'd1;
        else
        begin 
            if (uart_data_s_r2 == 2'b10)
                uart_en <= 1'd1;
            else if ((bit_cnt == 4'd9) && (time_cnt == B_CNT / 2))
                uart_en <= 1'd0;
            else 
                uart_en <= uart_en;
        end
    end
 
//波特率计数器
    always @ (posedge clk or negedge rst)
    begin
        if (!rst)
            time_cnt <= 24'd0;
        else if (uart_en)
        begin
            if (time_cnt == B_CNT - 1)
                time_cnt <= 24'd0;
            else
                time_cnt <= time_cnt + 1;
        end
        else
            time_cnt <= 24'd0;
    end
//位计数器
  always @ (posedge clk or negedge rst)
    begin
        if (!rst)
            bit_cnt <= 0;
        else if (uart_en)
        begin
            if (time_cnt == B_CNT - 1)
                bit_cnt <= bit_cnt + 1;
            else
                bit_cnt <= bit_cnt;
        end
		else 
			bit_cnt<=0;
    end
 
//三次判决计数
   always @ (posedge clk or negedge rst)
    begin
        if (!rst)
            judge_cnt <= 4'd0;
        else if (uart_en)
        begin
            if (time_cnt == B_CNT /4||time_cnt==B_CNT /2||time_cnt==3*B_CNT /4)
            begin
				if(uart_data_s==1)
					judge_cnt<=judge_cnt+1;
				else
					judge_cnt<=judge_cnt;
			end
			else if(time_cnt==1)
				judge_cnt<=0;
        end
		else 
			judge_cnt<=0;
    end
//根据判决结果确定输入电压值
  always @ (posedge clk or negedge rst)
    begin
        if (!rst)
            uart_data_s_r3 <= 0;
        else if (uart_en)
        begin
            if (time_cnt ==B_CNT-10)
			begin
				if(judge_cnt>=2)
					uart_data_s_r3<=1;
				else
					uart_data_s_r3<=0;
			end
        end
		else 
			uart_data_s_r3<=0;
    end
//接收缓存
    always @ (posedge clk or negedge rst)
    begin
        if (!rst)
            rx_data <= 8'd0;
        else if (uart_en)
        begin
            if (time_cnt == B_CNT-5)
            begin
                case (bit_cnt)
                    1:rx_data[0] <=uart_data_s_r3;
                    2:rx_data[1] <=uart_data_s_r3;
                    3:rx_data[2] <=uart_data_s_r3;
                    4:rx_data[3] <=uart_data_s_r3;
                    5:rx_data[4] <=uart_data_s_r3;
                    6:rx_data[5] <=uart_data_s_r3;
                    7:rx_data[6] <=uart_data_s_r3;
                    8:rx_data[7] <=uart_data_s_r3;
                endcase
            end
            else
                rx_data <= rx_data;
        end
    end
 
//接收
    always @ (posedge clk or negedge rst)
    begin
        if (!rst)
            uart_data_p <= 8'd0;
        else if (bit_cnt == 4'd9)
            uart_data_p[7:0] <= rx_data[7:0];
        else
            uart_data_p <= uart_data_p;
    end
//接收完成标志
    always @ (posedge clk or negedge rst)
    begin
        if (!rst)
            rx_done <= 0;
        else if (bit_cnt == 4'd9&& (time_cnt == B_CNT / 4))
            rx_done <= 1'd1;
        else
            rx_done <= 0;
    end
    
endmodule

4.2 DDS模块

该模块中,需要根据需要的频率输入频率控制字,频率控制字在相位累加器中不断累加直到计满低若干位后溢出,将高若干位传入相位调制器,相位调制器在此基础上加上相位控制字,然后,根据相位调制器输出的值从波形存储器中查找地址,获取输出的波形数据,然后进入幅度控制模块,在该模块中将波形数据与幅度控制字相乘,然后取高10位,得到输入DAC的波形数据。

在该模块中,设相位累加器的高位有M位,总位数为N位,fc为时钟频率,f_word为频率控制字,可以得到输出频率为

f=fc/(2^N)*f_word

由于频率调节范围为1Hz-5MHz,因此最小调节频率应该为1Hz,FPGA时钟信号为120MHz,因此可以知道N至少应该为27位,考虑到FPGA内部的存储以及传输,因此在该项目中,令N为28位。由于正弦信号地址长度为4096,因为5MHz信号一个周期应该要经过4096个地址长度,因此可以计算得到5M/4096=4882,因此低位至少应该要小于等于12位,因此M的值为16位。

FtE47hLBxwmAKedzVkOvGkra6WhE

 

DDS模块流程图

由于方波和三角波的波形较为简单,同时为了减少FPGA内部存储的使用,因此方波和三角波的波形是通过地址直接计算得到的。

其中方波可以用如下代码实现

	else if(mode==1'd1)//方波
		begin
			
			if(phase_ctrl<2048)
				wave_data<=amp+10'b1000000000;
			else if(phase_ctrl<4096)
				wave_data<=-amp+10'b1000000000;
		end

三角波可以用如下代码实现

	if(mode==2)//三角波
		begin
			if(phase_ctrl<1024)
				address<=phase_ctrl;
			else if(phase_ctrl<2048)
				address<=2048-phase_ctrl;
			else if(phase_ctrl<3072)
				address<=phase_ctrl-2048;
			else if(phase_ctrl<4096)
				address<=4096-phase_ctrl;
		end

4.3 sin_list模块

考虑到ICE40UP5K FPGA芯片中RAM的大小为80kB,同时方便程序中计算,因此选用了1024位地址长度,12位的数据的正弦波数据表,占用了1536B。实际上,如果需要更精细的波形,可以再适当加大地址长度,但是由于在该项目中差别非常小,几乎无法看出,因此选择了1024位地址长度。同时,由于正弦波信号波形每1/4周期都存在完全一致或者完全对称的图形,因此我们在该模块中只存储了1/4的波形。

4.4 MATLAB模块

在该模块中,使用了MATLAB中的APP设计工具,通过该工具设计相应的操作界面。在该界面中,主要包括了幅度、频率以及波形信息选择。由于幅度、频率控制字的计算涉及小数,在MATLAB中计算比较方便,因此在MATLAB中直接计算出幅度、幅度控制字。由于FPGA供电为3.3V,因此可以将幅度转换成相应的数字量。

幅度/1000=3.3/1024*幅度控制字

其中幅度的单位为mV。

因此有

幅度控制字=幅度*0.31

由于频率

f=fc/(2^N)*f_word

所以可以知道

频率控制字=2.24*频率

 %模式选择
            if(app.DropDown.Value == "正弦波")
                mode = 0;
            elseif(app.DropDown.Value == "方波")
                mode = 1;
            elseif(app.DropDown.Value == "三角波")
                mode = 2;
            else
                mode = 0;
            end
            %幅值
            amp=ceil(0.31.*app.mVEditField.Value);
            %频率
            freq=ceil(1.12.*app.HzEditField.Value);
            %端口
            com=app.DropDown_2.Value;
            %连接端口
            baud = 115200;
            port = serialport(com,baud);
            %发送数据
            data = zeros(1,8);
            data(1) = mode;
            data(2) =amp;
            data(3) = mod(freq,256);
            data(4) = mod((freq-data(3))/256,256);
            data(5) = mod(((freq-data(3))/256-data(4))/256,256);
            data(6) = mod((((freq-data(3))/256-data(4))/256-data(5))/256,256);
            write(port,data,"uint8");

 

五、实现功能截图

5.1 1V 1000Hz 正弦波

FgPkHIudEtOAb9pxp02fgbNTiMRi

5.2 1V 1000Hz 方波

FuZViOgkOROJvS42WgnsC0yG8vq5

5.3 1V 1000Hz 三角波

Fhbn_pH3gqJ4JFm5LU6NISOoM2Ti

5.4 0.1V 1000Hz 正弦波

FjKGR0YOAhE5zeszmFi6lw9J6tX-

5.5 0.1V 1000Hz 方波

Fnhl-eNGyCdGzgfb3u_jdA6hLmao

5.6 0.1V 1000Hz 三角波

FnIOiYqKHXU4GZ-FlMJw-92tSLjI

5.7 1V 10Hz 正弦波

FkfdwsR8fO4rDdnMiB_EwOIwUkxF

5.8 1V 1MHz 正弦波

Fn0I2XKR4bD0yqk69TTTozAJzQja

六、资源占用截图

FtR8s7yRqvJNO2WAi2HkMq8Cuv2O

七、主要难题

在该项目中,主要的难点在于DDS的实现,由于频率控制,以及如何使用地址来进行查表,这都花费了较长时间去理解并且实现。

此外对于FPGA的使用不够熟练,在计算频率控制字以及幅度控制字,以及实现UART的部分都遇到了一定的困难。

八、未来的计划建议

由于该模块输出最大频率为5MHz,相对较大,因此在实际输出时会存在关于时钟信号对称的高频部分,由于DAC的输出最大频率为120MHz,因此这些谐波成分会对最后输出的结果产生一些比较明显的失真,这些部分的信号可通过在输出口后面加入一个低通滤波器,考虑到滤波器的衰减造成的失真,因此该滤波器的截止频率应当至少为25MHz,具体截止频率及阶数可以通过ADIsimDDS仿真确定。

此外可以增加调节信号相位以及占空比的部分,相位可以通过DDS模块中的相位调制器中相位控制字来确定,占空比可以在通过在DDS模块中加入占空比控制字,与方波和正弦波的地址控制字相乘,从而就可以调节方波以及正弦波的占空比。

在该项目中,还可以添加相应的存储功能,然后通过与正弦波相似的方法,通过地址从中读取波形信息,从而即可实现能够产生幅度可调、频率可调的存储波形的信号发生器。

九、硬件接线

实际使用的时候需要将小脚丫板上的19号口,与TTL模块的TX相连,GND与TTL 的GND相连,18号口为复位信号口,在不使用时需要连接3.3V的引脚,例如1号口,需要复位时拔下该杜邦线即可

 

附件下载
DDS.zip
代码文件
团队介绍
北京交通大学
团队成员
wyk
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号