FPGA之手把手教你做多路信号发生器(STM32与FPGA数据互传控制波形生成)

这篇具有很好参考价值的文章主要介绍了FPGA之手把手教你做多路信号发生器(STM32与FPGA数据互传控制波形生成)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


博主的念叨

最近趁热打铁做了一个关于STM32与FPGA通信并且控制高速DA模块产生不同频率信号的正弦波、方波、三角波和锯齿波的项目,从中收获到了很多东西,也踩了一些雷和坑,将分为几篇文章将整个过程分享出来。

这一次准备分享的是将串口解析的出来的波形频率数据以及波形类型数据送入顶层文件中,通过调用不同的ROM核驱动高速DA模块产生对应的信号,通过调用IP核生成特定频率的时钟,使得正弦波能够贴近整数倍的输出频率。

本文参考正点原子EP4CE系列开发板的源码,做了部分修改

一、任务介绍

1、本文目标

实现STM32和FPGA的串口通信,并将STM32传输过来的频率信息和波形信息解析存入定义的reg变量中。通过调用ROM核内部的数据给高速DA模块发送数据,使得高速DA模块能够产生特定频率的波形

2、设计思路

根据设计需求,需要对串口数据进行接收解析,同时需要把解析的数据传送给内存单元。内存单元接收到数据以后传递到顶层模块,在顶层模块中作为其它子模块的输入条件,根据频率的不同改变计数次数,从而实现500KHz波形信号的生成以及其分频信号的产生。根据波形类型值的不同选择不同的波形单元

3、设计注意事项

1、串口通信属于异步通信方式,因此出现信号是不确定的,为了防止亚稳态的产生,需要对信号打拍子避免亚稳态的产生。

2、本文串口通信协议是 EA 频率高位 频率中位 频率低位 波形 AE,其中EA代表起始标志,AE代表停止标志。频率高位代表频率/65536,中位代表%65536/256,低位代表%65526%256。

3、串口波特率要对的上,否则无法进行正常通信

4、生成的波形模拟点数为128点,基频是锁相环产生的500KHz*128=64MHz信号

5、生成了多个ROM核,注意ROM核的调用

6、此设计与博主前面博文串口协议解析相关,请提前了解一下串口解析的过程

二、设计代码

1.顶层文件代码

module hs_da(
    input           sys_clk,       //时钟信号
	 input           sys_rst_n,     //复位信号
	 input           uart_rxd,      //串口接收
	 output          da_clk,        //da时钟
	 output [7:0]    da_data,       //da数据
	 output          uart_txd,       //串口发送
	 output          test_uart_txd       //串口发送

);
wire [6:0]   rd_addr;//ROM读地址
wire [7:0]   rd_data;//ROM读出的数据
wire [6:0]   rd_addr_ju;//ROM读地址
wire [7:0]   rd_data_ju;//ROM读出的数据
wire [6:0]   rd_addr_juchi;//ROM读地址
wire [7:0]   rd_data_juchi;//ROM读出的数据
wire [6:0]   rd_addr_sanjiao;//ROM读地址
wire [7:0]   rd_data_sanjiao;//ROM读出的数据
wire [23:0]  freq;
wire [7:0]   wave;
wire locked;
wire rst_n;
wire clk;
assign rst_n=sys_rst_n&locked;
assign test_uart_txd=uart_txd;
//例化串口接收数据和发送数据模块
uart_loopback_top u_uart_loopback_top(
    .sys_clk     (sys_clk),
	 .sys_rst_n   (sys_rst_n),
	 .uart_rxd    (uart_rxd),
	 .uart_txd    (uart_txd),
	 .freq        (freq),
    .wave        (wave)	 
);

da_wave_send u_da_wave_send(
    .clk               (clk),
	 .rst_n            (sys_rst_n),
	 .freq             (freq),
	 .wave             (wave),
	 .rd_data          (rd_data),
	 .rd_addr          (rd_addr),
	 .rd_data_ju       (rd_data_ju),
	 .rd_addr_ju       (rd_addr_ju),
	 .rd_data_juchi    (rd_data_juchi),
	 .rd_addr_juchi    (rd_addr_juchi),
	 .rd_data_sanjiao  (rd_data_sanjiao),
	 .rd_addr_sanjiao  (rd_addr_sanjiao), 
	 .da_clk           (da_clk),
	 .da_data          (da_data)
);
//正弦波例化
rom u_rom(
	.address ( rd_addr ),
	.clock   ( clk ),
	.q       ( rd_data )
);
//矩形波例化
rom_ju u_rom_ju(
	.address ( rd_addr_ju ),
	.clock   ( clk ),
	.q       ( rd_data_ju )
);

//锯齿波例化
rom_juchi u_rom_juchi(
	.address ( rd_addr_juchi ),
	.clock   ( clk ),
	.q       ( rd_data_juchi )
);

//三角波例化
rom_sanjiao u_rom_sanjiao(
	.address ( rd_addr_sanjiao ),
	.clock   ( clk ),
	.q       ( rd_data_sanjiao )
);
pll_clk u_pll_clk(
    .areset   (~sys_rst_n),
	 .inclk0   (sys_clk),
	 .c0       (clk),
	 .locked   (locked)
	 );
endmodule

输入总共定义三个变量,其中uart_rxd代表串口输入接入。sys_clk代表输入50MHz基准时钟,sys_rst_n代表复位信号。输出da_clk是提供给高速AD/DA模块的时钟,确保高速AD/DA模块能够与FPGA发送数据进行同步。da_data代表发送给DA模块的八位数据信号,uart_txd代表串口发送输出引脚,在其中还设置了test_uart_txd引脚,这个引脚可留下也可去除,主要目的是为了连接到FPGACH340的TX引脚,用于数据发送至电脑端进行数据的查看。

定义了许多wire变量。以正弦波为例,定义正弦波ROM地址为rd_addr,正弦波读出数据为rd_data。其中地址设置为七位,数据设置为八位。原因在于高速AD/DA模块可以接收0-255的数据,而我们整个周期只需要模拟出128个点即可,因此地址位为7位。以下带后缀的ju,juchi,sanjiao也都同理。另外freq和wave是由串口回环模块输出的变量,在顶层文件中被调用。locked,rst_n以及clk信号都是pll锁相环生成的。需要注意的是,我在设计中遇到了一个问题是在底层文件中进行PLL的时钟调用无效,但是到顶层中调用时钟就可行了,网上说PLL时钟的调用必须得放到顶层,然后赋给底层,如果有知道的小伙伴可以在评论区下方留言。

第一个assign语句的作用是生成锁相环输出时钟信号的复位信号,第二个assign语句的作用是连接串口线,也即前文提到的连接到ch340模块TX引脚的接口。

实例化da_wave_send模块,输入包括前面12路信号,输出为da_clk和da_data,其中da_clk直接与锁相环输出的64MHz信号连接起来。rom的例化都是按照指定格式进行例化的,大家只需要注意例化格式即可。

pll锁相环的例化也是如此,在这里博主只选择了一路锁相环的输出,因此输出信号也只有一个c0信号,若需要更改锁相环信号的输出可在ip核界面直接进行修正即可。

2.波形生成模块

module da_wave_send(
    input                 clk    ,  //时钟
    input                 rst_n  ,  //复位信号,低电平有效
    input        [23:0]   freq   ,  //频率信号
	 input        [7:0]    wave   ,  //波形信号
	 
    input        [7:0]    rd_data,  //ROM读出的数据
    output  reg  [6:0]    rd_addr,  //读ROM地址
    input        [7:0]    rd_data_ju,  //ROM读出的数据
    output  reg  [6:0]    rd_addr_ju,  //读ROM地址
    input        [7:0]    rd_data_juchi,  //ROM读出的数据
    output  reg  [6:0]    rd_addr_juchi,  //读ROM地址
    input        [7:0]    rd_data_sanjiao,  //ROM读出的数据
    output  reg  [6:0]    rd_addr_sanjiao,  //读ROM地址	 
    //DA芯片接口
    output                da_clk ,  //DA(AD9708)驱动时钟,最大支持125Mhz时钟
    output  reg  [7:0]    da_data   //输出给DA的数据  
    );

//频率调节控制
wire [23:0] FREQ_ADJ;               
assign FREQ_ADJ= 24'd500_000/freq-1'b1;//分频频率
//assign FREQ_ADJ = 8'd1;//分频频率
//reg define
reg    [19:0]    freq_cnt  ;  //频率调节计数器

//*****************************************************
//**                    main code
//*****************************************************

//数据rd_data是在clk的上升沿更新的,所以DA芯片在clk的下降沿锁存数据是稳定的时刻
//而DA实际上在da_clk的上升沿锁存数据,所以时钟取反,这样clk的下降沿相当于da_clk的上升沿
assign  da_clk = ~clk;       

//频率调节计数器
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        freq_cnt <= 20'd0;
    else if(freq_cnt == FREQ_ADJ)    
        freq_cnt <= 20'd0;
    else         
        freq_cnt <= freq_cnt + 20'd1;
end

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        da_data <= 8'd0;
    else begin   
        case (wave)
		      8'd0:da_data = rd_data;
				8'd1:da_data = rd_data_ju;
				8'd2:da_data = rd_data_juchi;
				8'd3:da_data = rd_data_sanjiao;
				default da_data = 8'd0;
		  endcase
	 end 
end
//读ROM地址
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        rd_addr <= 7'd0;
    else begin
        if(freq_cnt == FREQ_ADJ) begin
          case (wave)
		        8'd0:rd_addr <= rd_addr + 7'd1;
				  8'd1:rd_addr_ju <= rd_addr_ju + 7'd1;
				  8'd2:rd_addr_juchi <= rd_addr_juchi + 7'd1;
				  8'd3:rd_addr_sanjiao <= rd_addr_sanjiao + 7'd1;
				  default rd_addr <= rd_addr + 7'd1;
		    endcase		  
        end    
    end            
end

endmodule

从代码中可见,定义了wire变量FREQ_ADJ,即我们当前的频率计数值。通过assign语句赋值,由于我们最高的频率为500KHz,因此分频计数值为500000/freq-1,假设我们接收到的频率数据为500000,那么计数值就为0。计数值为0的时候在程序内部每隔500KHz重新读一遍地址,从而也就实现了500KHz频率的输出,freq为250000的时候计数值为1就是250KHz频率输出。

第一个always语句是对频率进行计数,由于此时输入的时钟为64MHz,因此此always语句是每隔64MHz执行一次的。第二个always语句是对当前的da_data进行一个赋值,根据wave变量的不同将对应ROM里面的值赋值给da_data。第三个always语句是执行ROM的读取,当计数值等于设定的分频计数值时,就会对ROM进行一次数据读取。根据波形的不同对应的波形ROM地址就会加1,此时读取出来的ROM值就会对应不同的波形ROM。要注意的是,ROM只能够被读取而不能被写入,因此只需要改变读取的地址就能够读到不同地址对应的不同数据。

3.ROM例化

ROM的例化主要包括几个步骤:

1、生成mif文件。这个mif文件由对应的波形生成软件生成,我们可以设置波形的宽度和深度。其中深度就是波形一个完整周期的点数,宽度指的是波形的位宽。在这里我们需要128个点,和最大255的数据,就需要设置宽度为8位,深度为7位。

fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
2、生成mif文件放入到doc文件夹里面,然后打开Quartus的ip核,搜索ROM
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
我这里由于是已经生成了,所以选择编辑已存在的
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
即可

4.PLL例化

fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件
fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件

5.引脚分配

fpga stm32通信,ZYNQ/FPGA实战合集,stm32,fpga开发,嵌入式硬件


总结

有什么不懂的可以在下方留言,只要学会了方法,实现信号发生器会比较简单的。文章来源地址https://www.toymoban.com/news/detail-609834.html

到了这里,关于FPGA之手把手教你做多路信号发生器(STM32与FPGA数据互传控制波形生成)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 手把手教你做一个物联网垃圾桶(ARDUINO ESP8266)

    最近自创的项目结题了,就顺手将相关的技术细节记录一下,也给需要做自创的同学提供一个简单的可复制的小项目 智能垃圾桶的选型 通过充分考虑实际生活场景,加入了 IP65 级别防水设计,可以满足大多数需要场景,此外采用升压和可充电电池, 践行环保理念 ,实现长续

    2024年02月03日
    浏览(78)
  • 手把手教你做一个 ChatGPT !丝滑小白版,只需一张单卡 GPU,轻松开启个性化训练!...

    大家好,我是贺同学。 一直以来密切关注 ChatGPT 的趋势,最近相关的话题可谓是热度不减,虽然从事互联网行业,但一直对 LLM 相关领域关注较少。 最近的 ChatGPT 的火热,让我对 LLM 相关开源社区也关注了起来,相关的开源社区,也涌现了很多优秀的工作,吸引了很多人的关

    2024年02月08日
    浏览(51)
  • 手把手教你移植 tinyriscv 到FPGA上

    我是 雪天鱼 ,一名FPGA爱好者,研究方向是FPGA架构探索和数字IC设计。 关注公众号【集成电路设计教程】,获取更多学习资料,并拉你进“ IC设计交流群 ”。 QQ IC设计交流群 群号: 866169462 。 所用开发板:正点原子达芬奇FPGA开发板 芯片型号:Xilinx Artix-7 35T tinyriscv 官方库链

    2023年04月17日
    浏览(42)
  • FPGA之手把手教你写串口协议解析(STM32与FPGA数据互传)

    最近趁热打铁做了一个关于STM32与FPGA通信并且控制高速DA模块产生不同频率信号的正弦波、方波、三角波和锯齿波的项目,从中收获到了很多东西,也踩了一些雷和坑,将分为几篇文章将整个过程分享出来。 这一次准备分享的是对串口数据的解析和赋值。解析的数据由STM32发

    2024年02月06日
    浏览(37)
  • 手把手带你做一套毕业设计-征程开启

     本文是《Vue + SpringBoot前后端分离项目实战》专栏的开篇,文本将会包含我们创作这个专栏的初衷,专栏的主体内容,以及我们专栏的后续规划。关于这套毕业设计的作者呢前端部分由狗哥负责,服务端部分则由天哥操刀。我们力求毕业生或者新手通过学完本专栏,可以开心

    2023年04月10日
    浏览(154)
  • 10年测试工程师 —— 手把手教会你做前端性能测试(超详细)

    普通用户如何评价一个网站的体验好不好呢? 除了满足他的功能需求以外,用得爽不爽可能是最大的评估因素。这个爽不爽可以简单理解为快不快,好不好看,是不是符合他的操作习惯等等。而这里的快不快就是我们说的性能。 有数据表明,性能在一定程度上跟公司的收益

    2024年02月13日
    浏览(53)
  • (一)手把手教你如何通过ARM DesignStart计划在FPGA上搭建一个Cortex-M3软核

    1.1 如何下载ARM DesignStart Cortex-M3相关文件 ​ 关于ARM DesignStart计划的介绍:ARM DesignStart计划——私人定制一颗ARM处理器 - 知乎 (zhihu.com)。 ​ 在arm Developer官网[Arm Developer](https://developer.arm.com/downloads)右上方的Downloads中搜索ARM DesignStart Cortex-M3第一个即是FPGA上定制的Cortex-M3软核IP。

    2024年02月04日
    浏览(39)
  • 手把手教你SHA-256

    SHA-256是SHA-2协议簇的一部分,也是当前最流行的协议算法之一。在本篇文章中,我们会了解这个密码学算法的每一个步骤,并且通过实例演示。SHA-2因它的安全性(比SHA-1强很多)和速度为人所知。在没有键(keys)生成的情况下,例如挖掘比特币,像SHA-2这样的快速哈希算法很

    2024年02月13日
    浏览(73)
  • 手把手教你暴力破解

    暴力破解是一种攻击手段,使用大量的认证信息在认证接口尝试登录,直到得到正确的结果。 2.1标题基于表单的暴力破解 2.1.1 第一步:打开burpsuite拦截 2.1.2 第二步:将拦截到的包右击发送到intruder模块 (其中简单介绍一下intruder模块) Target主要是设置暴力破解访问的host地址

    2024年02月07日
    浏览(55)
  • 手把手教你做主成分分析

    主成分分析是一种降维处理的统计方法,实践中有三个应用场景: 信息浓缩:将多个分析项浓缩成几个关键概括性指标; 权重计算:利用方差解释率值计算各概括性指标的权重; 综合评价:基于主成分得分构造综合得分数据,用于综合评价。 接下来,以一个具体案例来学习

    2024年02月01日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包