差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
t2_project02 [2019/08/19 19:45] haozhuo [七、可改进的不足之处] |
t2_project02 [2021/01/25 17:00] (当前版本) gongyusu [双通道示波器项目] |
||
---|---|---|---|
行 1: | 行 1: | ||
- | 双通道示波器项目 | + | ## 双通道示波器项目 |
- | --- | + | |
- | # 一、项目简介 | + | |
- | ## 1、项目参与人员: | + | ### 一、项目简介 |
+ | #### 1、项目参与人员: | ||
* 杜晓雷 | * 杜晓雷 | ||
* 李雨轩 | * 李雨轩 | ||
行 9: | 行 9: | ||
* 邬超 | * 邬超 | ||
- | ## 2、项目需求 | + | #### 2、项目需求 |
* 双通道数据采样 | * 双通道数据采样 | ||
* 模拟信号0-10MHz | * 模拟信号0-10MHz | ||
行 17: | 行 17: | ||
* 留出CSI MIPI接口 | * 留出CSI MIPI接口 | ||
- | # 二、PCB模块 | + | ### 二、硬件设计 |
PCB项目主要为硬件,需要收取信号,将其进行衰减,处理,放大后经由模数转换器输出到树莓派进行显示,完成示波器的作用 | PCB项目主要为硬件,需要收取信号,将其进行衰减,处理,放大后经由模数转换器输出到树莓派进行显示,完成示波器的作用 | ||
- | ## 1、项目设计流程 | + | #### 1、项目设计流程 |
- | ### (1) 项目整体方案 | + | ##### (1) 项目整体方案 |
- | 通过接口接入信号,通过耦合选择交/直流,衰减网络进行衰减,选择器进行衰减倍数选择,随后低电流偏置放大,经由差分放大器变成双路差分信号,传至模数转换器处理后将信号经由FPGA送至树莓派做显示 | + | 通过接口接入信号,通过耦合选择交/直流,衰减网络进行衰减,选择器进行衰减倍数选择,随后低电流偏置放大,经由差分放大器变成双路差分信号,传至模数转换器处理后将信号经由FPGA送至树莓派做显示 |
- | + | {{ :双通道示波器系统框图.png |}} | |
- | ### (2) 关键器件 | + | |
+ | ##### (2) 关键器件 | ||
- ADM8660 {{:adm660_8660.pdf|}} | - ADM8660 {{:adm660_8660.pdf|}} | ||
- LT3032 {{:lt3032.pdf|}} | - LT3032 {{:lt3032.pdf|}} | ||
行 35: | 行 35: | ||
- ADG658 {{:adg658_659.pdf|}} | - ADG658 {{:adg658_659.pdf|}} | ||
- | ### (3) 电路设计和PCB | + | ##### (3) 电路设计和PCB |
-原理图: {{:raspberry_os终极版.pdf|}} | -原理图: {{:raspberry_os终极版.pdf|}} | ||
行 41: | 行 41: | ||
- | ### (4) 心得 | + | ##### (4) 心得 |
在器件选择方面,需要参考电路各部分以及器件的特性(如额定电流,封装等),才能找到最适合的元件。参阅数据手册在了解器件特性这一方面是最为有效的方法。因此在选定元件的时候,应当大量参阅不同元件的数据手册,从而从中择取适合的元件。同时设计时一定要预留测试点,方便日后焊接电路时进行测量调试 | 在器件选择方面,需要参考电路各部分以及器件的特性(如额定电流,封装等),才能找到最适合的元件。参阅数据手册在了解器件特性这一方面是最为有效的方法。因此在选定元件的时候,应当大量参阅不同元件的数据手册,从而从中择取适合的元件。同时设计时一定要预留测试点,方便日后焊接电路时进行测量调试 | ||
- | ## 2、项目调试 | + | #### 2、项目调试 |
- | ### (1) 功能调试 | + | ##### (1) 功能调试 |
首先构筑由AD8660和LT3032组成的电源模块,通过测试点验证5v输入接入后经由AD8660可以反向出-5v电压,经由LT3032后可输出正负3.3v电压。 | 首先构筑由AD8660和LT3032组成的电源模块,通过测试点验证5v输入接入后经由AD8660可以反向出-5v电压,经由LT3032后可输出正负3.3v电压。 | ||
行 55: | 行 55: | ||
最后焊接剩下的一路输入,以同上方法进行测试确保功能正常。至此PCB电路组装完毕 | 最后焊接剩下的一路输入,以同上方法进行测试确保功能正常。至此PCB电路组装完毕 | ||
- | ### (2) 问题和解决方法 | + | ##### (2) 问题和解决方法 |
- | + | ||
问题1: 由于没有仔细核对数据手册与数据库的封装,Maxim 19516 模数转换器封装尺寸发生错误。 | 问题1: 由于没有仔细核对数据手册与数据库的封装,Maxim 19516 模数转换器封装尺寸发生错误。 | ||
- | |||
解决方法:将PCB封装图重新进行了设计,重新打板 | 解决方法:将PCB封装图重新进行了设计,重新打板 | ||
- | |||
{{ :对比.jpg |}} | {{ :对比.jpg |}} | ||
行 69: | 行 65: | ||
解决方法:首先重新焊接电路,未成功。增加了负载之后正电压输出恢复正常值 | 解决方法:首先重新焊接电路,未成功。增加了负载之后正电压输出恢复正常值 | ||
- | + | ##### (3) 遗留问题 | |
- | ### (3) 遗留问题 | + | ##### (4) 调试总结 |
- | + | ||
- | + | ||
- | ### (4) 调试总结 | + | |
调试过程中遭遇了一些问题。其中最为严重的是导致了需要重新打板的芯片封装问题。由此可见数据手册的重要性相当高,需要给予相当的重视。由电源问题也可知有时需要多思考才能发现问题的根源,也可以避免调试过程中绕弯路。 | 调试过程中遭遇了一些问题。其中最为严重的是导致了需要重新打板的芯片封装问题。由此可见数据手册的重要性相当高,需要给予相当的重视。由电源问题也可知有时需要多思考才能发现问题的根源,也可以避免调试过程中绕弯路。 | ||
- | ## 3、PCB项目总结 | + | #### 3、PCB项目总结 |
本次在PCB制作过程中,组员齐心协力,共同积极参与了从PCB的规划,设计,到最终成果的所有过程。这大幅提高了设计的效率,并降低了错误率。即使如此,在设计中仍然出现了相当大的挑战以及几处错误,其中尤为显著的问题是一处芯片封装发生错误。错误来源于没有将数据库中的封装具体尺寸与数据手册进行认真对比,由此产生了这项问题。从中可以得出必须认真检查从规划,设计到最终成果的每一个步骤,才能有效避免错误的发生。 | 本次在PCB制作过程中,组员齐心协力,共同积极参与了从PCB的规划,设计,到最终成果的所有过程。这大幅提高了设计的效率,并降低了错误率。即使如此,在设计中仍然出现了相当大的挑战以及几处错误,其中尤为显著的问题是一处芯片封装发生错误。错误来源于没有将数据库中的封装具体尺寸与数据手册进行认真对比,由此产生了这项问题。从中可以得出必须认真检查从规划,设计到最终成果的每一个步骤,才能有效避免错误的发生。 | ||
{{ :p_20190816_164337.jpg?300 |}} | {{ :p_20190816_164337.jpg?300 |}} | ||
- | # 三、FPGA模块 | + | ### 三、FPGA逻辑 |
FPGA 主要的目的在于从软件层面上抓取由硬件的模数转换器送来的数据,进行处理,从而送到树莓派进行显示 | FPGA 主要的目的在于从软件层面上抓取由硬件的模数转换器送来的数据,进行处理,从而送到树莓派进行显示 | ||
- | ## 1、硬件连接 | + | #### 1、硬件连接 |
{{ :fpga.jpg |}} | {{ :fpga.jpg |}} | ||
行 102: | 行 95: | ||
- | ## 2、工作原理 | + | #### 2、工作原理 |
数据传输到FPGA处理后再送入树莓派显示 | 数据传输到FPGA处理后再送入树莓派显示 | ||
行 110: | 行 103: | ||
不同幅值的信号需要不同的衰减倍数确保能够送入后续电路进行处理。衰减倍数的控制是通过选择器进行的。FPGA会输出相应的数值进行选择。 | 不同幅值的信号需要不同的衰减倍数确保能够送入后续电路进行处理。衰减倍数的控制是通过选择器进行的。FPGA会输出相应的数值进行选择。 | ||
- | 小于1Vpp(±0.5V)输出'00',衰减1倍(即不衰减); | + | - 小于1Vpp(±0.5V)输出'00',衰减1倍(即不衰减); |
- | 在1Vpp(±0.5V)和10Vpp(±5V)之间的输出'01',衰减10倍; | + | - 在1Vpp(±0.5V)和10Vpp(±5V)之间的输出'01',衰减10倍; |
- | 超过10Vpp(±5V)输出'10',衰减100倍 | + | - 超过10Vpp(±5V)输出'10',衰减100倍 |
行 122: | 行 115: | ||
- | ## 3、代码设计 | + | #### 3、代码设计 |
- | ### (1) 模块划分 | + | ##### (1) 模块划分 |
{{ :fpga程序1.png?500 |}} | {{ :fpga程序1.png?500 |}} | ||
{{ :代码树.png?300 |}} | {{ :代码树.png?300 |}} | ||
- | ### (2) 关键模块实现 | + | ##### (2) 关键模块实现 |
+ | 顶层模块的代码: | ||
+ | <code verilog> | ||
+ | module osc_top( | ||
+ | input ss,sck,sdin,clkin, //4线SPI、FPGA输入时钟、以及复位键 | ||
+ | input DCLKA, DCLKB, //ADC采样时钟输入 | ||
+ | //input DORA,DORB, //溢出标志 | ||
+ | output clk, //输出的时钟 | ||
+ | input [7:0] CH1, //CH1和CH2数据输入 | ||
+ | input [7:0] CH2, | ||
- | #### SPI模块 | + | output A0_1, //衰减倍数控制位 |
+ | output A1_1, | ||
+ | output A0_2, | ||
+ | output A1_2, | ||
+ | |||
+ | output reg DC_CH1, //AC/DC控制 | ||
+ | output reg DC_CH2, | ||
+ | |||
+ | output sdout, //SPI数据输出 | ||
+ | output Full_CH1, //fifo1写满标志,传给树莓派 | ||
+ | output Full_CH2 //fifo2写满标志,传给树莓派 | ||
+ | ); | ||
+ | |||
+ | wire clk_100MHz; | ||
+ | |||
+ | wire rstb = 1'b1; //系统复位 | ||
+ | |||
+ | wire [7:0] rdata; | ||
+ | |||
+ | |||
+ | /********************衰减倍数选择***********************/ | ||
+ | /*****************注意:shuaijian_level=01不衰减;00衰减十倍;11衰减100倍*******************/ | ||
+ | reg [1:0] shuaijian_level; | ||
+ | always @ (posedge clk_100MHz or negedge rstb) begin | ||
+ | if (!rstb) | ||
+ | shuaijian_level <= 2'b00; | ||
+ | else if (rdata >= 8'd61 && rdata <= 8'd65) | ||
+ | shuaijian_level <= 2'b01; | ||
+ | else if (rdata >= 8'd66 && rdata <= 8'd68) | ||
+ | shuaijian_level <= 2'b00; | ||
+ | else if (rdata >= 8'd69 && rdata <= 8'd72) | ||
+ | shuaijian_level <= 2'b11; | ||
+ | else | ||
+ | shuaijian_level <= shuaijian_level; | ||
+ | end | ||
+ | |||
+ | assign A0_1 = ~shuaijian_level[0]; | ||
+ | assign A1_1 = shuaijian_level[1]; | ||
+ | assign A0_2 = ~shuaijian_level[0]; | ||
+ | assign A1_2 = shuaijian_level[1]; | ||
+ | |||
+ | /********************耦合方式控制***********************/ | ||
+ | always@(posedge done or negedge rstb) | ||
+ | begin | ||
+ | if(!rstb) | ||
+ | begin | ||
+ | DC_CH1 <= 1'b0; | ||
+ | DC_CH2 <= 1'b0; | ||
+ | end | ||
+ | else if(rdata == 8'd24) | ||
+ | begin | ||
+ | DC_CH1 <= ~DC_CH1; | ||
+ | end | ||
+ | else if(rdata == 8'd25) | ||
+ | begin | ||
+ | DC_CH2 <= ~DC_CH2; | ||
+ | end | ||
+ | else | ||
+ | begin | ||
+ | DC_CH1 <= DC_CH1; | ||
+ | DC_CH2 <= DC_CH2; | ||
+ | end | ||
+ | end | ||
+ | |||
+ | /********************SPI通信,读数据和写数据***********************/ | ||
+ | wire done; | ||
+ | |||
+ | wire [7:0] tdata; | ||
+ | wire [7:0] tdata_ch1; | ||
+ | wire [7:0] tdata_ch2; | ||
+ | |||
+ | reg channel_select; | ||
+ | always @ (posedge clk_100MHz) begin | ||
+ | if (rdata == 8'd31) | ||
+ | channel_select = 1'b0; | ||
+ | else if (rdata == 8'd32) | ||
+ | channel_select = 1'b1; | ||
+ | else | ||
+ | channel_select = channel_select; | ||
+ | end | ||
+ | |||
+ | assign tdata = (channel_select)?tdata_ch2:tdata_ch1; | ||
+ | |||
+ | spi_slave U_spi_slave | ||
+ | ( | ||
+ | .rstb(rstb), | ||
+ | .ss(ss), | ||
+ | .sck(sck), | ||
+ | .sdin(sdin), | ||
+ | .sdout(sdout), | ||
+ | .done(done), | ||
+ | .rdata(rdata), | ||
+ | .tdata(tdata) | ||
+ | ); | ||
+ | |||
+ | /********************波形数据写入FIFO***********************/ | ||
+ | wire Empty_CH1; | ||
+ | reg WrEn_CH1; | ||
+ | reg RdEn_CH1; | ||
+ | wire Empty_CH2; | ||
+ | reg WrEn_CH2; | ||
+ | reg RdEn_CH2; | ||
+ | |||
+ | reg [7:0] display_a; | ||
+ | always@ (posedge DCLKA) //将过来的数丢进寄存器中 | ||
+ | begin | ||
+ | display_a <= CH1; | ||
+ | end | ||
+ | |||
+ | reg [7:0] data_in_last_ch1; | ||
+ | always @ (posedge DCLKA) begin | ||
+ | data_in_last_ch1 <= display_a; | ||
+ | end | ||
+ | |||
+ | //连续多次上升 | ||
+ | reg [4:0] up_or_down_flag_ch1; | ||
+ | always @ (posedge DCLKA) begin | ||
+ | if (display_a < data_in_last_ch1) | ||
+ | up_or_down_flag_ch1 <= {up_or_down_flag_ch1[3:0],1'b0}; | ||
+ | else | ||
+ | up_or_down_flag_ch1 <= {up_or_down_flag_ch1[3:0],1'b1}; | ||
+ | end | ||
+ | |||
+ | reg [7:0] trigger_REF = 8'h00; | ||
+ | always @ (posedge done or negedge rstb) begin | ||
+ | if (!rstb) | ||
+ | trigger_REF <= 8'h00; | ||
+ | else if (rdata == 8'd81) | ||
+ | trigger_REF <= trigger_REF + 3'd5; | ||
+ | else if (rdata == 8'd82) | ||
+ | trigger_REF <= trigger_REF - 3'd5; | ||
+ | else | ||
+ | trigger_REF <= trigger_REF; | ||
+ | end | ||
+ | /* reg [7:0] trigger_REF; | ||
+ | always @ (*) begin | ||
+ | if (!rstb) | ||
+ | trigger_REF = 8'h00; | ||
+ | else if (rdata == 8'd81) | ||
+ | trigger_REF = trigger_REF + 3'd5; | ||
+ | else if (rdata == 8'd82) | ||
+ | trigger_REF = trigger_REF - 3'd5; | ||
+ | else | ||
+ | trigger_REF = trigger_REF; | ||
+ | end */ | ||
+ | |||
+ | always @ (*) begin //fifo1写使能 | ||
+ | if(!rstb) | ||
+ | WrEn_CH1 = 1'b0; | ||
+ | else if (Full_CH1) //如果满了,就不再写 | ||
+ | WrEn_CH1 = 1'b0; | ||
+ | else if (Empty_CH1 && (up_or_down_flag_ch1==5'b11111) && (data_in_last_ch1==trigger_REF)) | ||
+ | WrEn_CH1 = 1'b1; | ||
+ | else | ||
+ | WrEn_CH1 = WrEn_CH1; | ||
+ | end | ||
+ | |||
+ | always @ (*) begin //fifo1读使能 | ||
+ | if(!rstb) | ||
+ | RdEn_CH1 = 1'b0; | ||
+ | else if (Full_CH1 && (rdata==8'd31)) //数据满了,并且收到spi发来信号,就开始读 | ||
+ | RdEn_CH1 = 1'b1; | ||
+ | else if (Empty_CH1) //开启后一直读知道读空 | ||
+ | RdEn_CH1 = 1'b0; | ||
+ | else | ||
+ | RdEn_CH1 = RdEn_CH1; | ||
+ | end | ||
+ | |||
+ | reg [7:0] display_b; | ||
+ | always@ (posedge DCLKB) | ||
+ | begin | ||
+ | display_b <= CH2; | ||
+ | end | ||
+ | |||
+ | reg [7:0] data_in_last_ch2; | ||
+ | always @ (posedge DCLKB) begin | ||
+ | data_in_last_ch2 <= display_b; | ||
+ | end | ||
+ | |||
+ | reg [2:0] up_or_down_flag_ch2; | ||
+ | always @ (posedge DCLKB) begin | ||
+ | if (display_b < data_in_last_ch2) | ||
+ | up_or_down_flag_ch2 <= {up_or_down_flag_ch2[1:0],1'b0}; | ||
+ | else | ||
+ | up_or_down_flag_ch2 <= {up_or_down_flag_ch2[1:0],1'b1}; | ||
+ | end | ||
+ | |||
+ | always @ (*) begin | ||
+ | if(!rstb) | ||
+ | WrEn_CH2 = 1'b0; | ||
+ | else if (Full_CH2) | ||
+ | WrEn_CH2 = 1'b0; | ||
+ | else if (Empty_CH2 && (up_or_down_flag_ch2==3'b111) && (data_in_last_ch2 == trigger_REF)) | ||
+ | WrEn_CH2 = 1'b1; | ||
+ | else | ||
+ | WrEn_CH2 = WrEn_CH2; | ||
+ | end | ||
+ | |||
+ | always @ (*) begin | ||
+ | if(!rstb) | ||
+ | RdEn_CH2 = 1'b0; | ||
+ | else if (Full_CH2 && (rdata == 8'd32)) | ||
+ | RdEn_CH2 = 1'b1; | ||
+ | else if (Empty_CH2) | ||
+ | RdEn_CH2 = 1'b0; | ||
+ | else | ||
+ | RdEn_CH2 = RdEn_CH2; | ||
+ | end | ||
+ | |||
+ | myFIFO U_fifo_ch1 | ||
+ | ( | ||
+ | .Data(display_a), | ||
+ | .WrClock(DCLKA), | ||
+ | .RdClock(done), | ||
+ | .WrEn(WrEn_CH1), | ||
+ | .RdEn(RdEn_CH1), | ||
+ | .Reset(~rstb), | ||
+ | .RPReset(~rstb), | ||
+ | .Q(tdata_ch1), | ||
+ | .Empty(Empty_CH1), | ||
+ | .Full(Full_CH1) | ||
+ | ); | ||
+ | |||
+ | myFIFO U_fifo_ch2 | ||
+ | ( | ||
+ | .Data(display_b), | ||
+ | .WrClock(DCLKB), | ||
+ | .RdClock(done), | ||
+ | .WrEn(WrEn_CH2), | ||
+ | .RdEn(RdEn_CH2), | ||
+ | .Reset(~rstb), | ||
+ | .RPReset(~rstb), | ||
+ | .Q(tdata_ch2), | ||
+ | .Empty(Empty_CH2), | ||
+ | .Full(Full_CH2) | ||
+ | ); | ||
+ | |||
+ | /********************100MHz,由PLL倍频产生***********************/ | ||
+ | PLL U_PLL | ||
+ | ( | ||
+ | .CLKI(clkin), | ||
+ | .CLKOP(clk_100MHz) | ||
+ | ); | ||
+ | /**************ADC采样时钟,cnt_set是指每隔多少个点采一次*************/ | ||
+ | reg [17:0] cnt_set = 18'b1; | ||
+ | divide U_divide | ||
+ | (.rst_n(rstb), | ||
+ | .clk(clk_100MHz), | ||
+ | .clkout(clk), | ||
+ | .N(cnt_set) | ||
+ | ); | ||
+ | |||
+ | always@(*) //通过spi发来的命令控制cnt | ||
+ | begin | ||
+ | if(!rstb) | ||
+ | begin | ||
+ | cnt_set = 18'b1; | ||
+ | end | ||
+ | else | ||
+ | begin | ||
+ | case(rdata) | ||
+ | 8'd1: begin cnt_set = 18'd1; end | ||
+ | 8'd2: begin cnt_set = 18'd1; end | ||
+ | 8'd3: begin cnt_set = 18'd1; end | ||
+ | 8'd4: begin cnt_set = 18'd1; end | ||
+ | 8'd5: begin cnt_set = 18'd1; end | ||
+ | 8'd6: begin cnt_set = 18'd1; end | ||
+ | 8'd7: begin cnt_set = 18'd3; end | ||
+ | 8'd8: begin cnt_set = 18'd5; end | ||
+ | 8'd9: begin cnt_set = 18'd10; end | ||
+ | 8'd10:begin cnt_set = 18'd24; end | ||
+ | 8'd11:begin cnt_set = 18'd47; end | ||
+ | 8'd12:begin cnt_set = 18'd94; end | ||
+ | 8'd13:begin cnt_set = 18'd235; end | ||
+ | 8'd14:begin cnt_set = 18'd469; end | ||
+ | 8'd15:begin cnt_set = 18'd938; end | ||
+ | 8'd16:begin cnt_set = 18'd2344; end | ||
+ | 8'd17:begin cnt_set = 18'd4688; end | ||
+ | 8'd18:begin cnt_set = 18'd9375; end | ||
+ | 8'd19:begin cnt_set = 18'd23438; end | ||
+ | 8'd20:begin cnt_set = 18'd46875; end | ||
+ | 8'd21:begin cnt_set = 18'd93750; end | ||
+ | 8'd22:begin cnt_set = 18'd234375; end | ||
+ | default:begin cnt_set = cnt_set; end | ||
+ | endcase | ||
+ | end | ||
+ | end | ||
+ | |||
+ | |||
+ | endmodule | ||
+ | |||
+ | </code> | ||
+ | |||
+ | 分频器的代码 | ||
+ | <code verilog> | ||
+ | module divide(rst_n,clk,clkout,N); | ||
+ | input rst_n; | ||
+ | input clk; | ||
+ | input [17:0] N; | ||
+ | output clkout; //定义输入输出 | ||
+ | |||
+ | |||
+ | parameter width = 18; | ||
+ | |||
+ | reg [width-1:0] cnt_p,cnt_n; | ||
+ | reg clk_p,clk_n; | ||
+ | |||
+ | always @(posedge clk or negedge rst_n ) | ||
+ | begin | ||
+ | if(!rst_n) | ||
+ | cnt_p<=0; | ||
+ | else if(cnt_p==(N-1)) | ||
+ | cnt_p<=0; | ||
+ | else | ||
+ | cnt_p<=cnt_p+1'b1; | ||
+ | end | ||
+ | |||
+ | |||
+ | always @(posedge clk or negedge rst_n ) | ||
+ | begin | ||
+ | if(!rst_n) | ||
+ | clk_p<=0; | ||
+ | else if(cnt_p<(N>>1)) | ||
+ | clk_p<=0; | ||
+ | else | ||
+ | clk_p<=1; | ||
+ | end | ||
+ | |||
+ | always @(negedge clk or negedge rst_n ) | ||
+ | begin | ||
+ | if(!rst_n) | ||
+ | cnt_n<=0; | ||
+ | else if(cnt_n==(N-1)) | ||
+ | cnt_n<=0; | ||
+ | else | ||
+ | cnt_n<=cnt_n+1'b1; | ||
+ | end | ||
+ | |||
+ | |||
+ | always @(negedge clk ) | ||
+ | begin | ||
+ | if(!rst_n) | ||
+ | clk_n<=0; | ||
+ | else if(cnt_n<(N>>1)) | ||
+ | clk_n<=0; | ||
+ | else | ||
+ | clk_n<=1; | ||
+ | end | ||
+ | |||
+ | assign clkout = (N==1)?clk:(N[0])?(clk_n&clk_p):clk_p; | ||
+ | endmodule | ||
+ | </code> | ||
+ | |||
+ | |||
+ | |||
+ | ###### SPI模块 | ||
此款PFGA里的EFB中有SPI的IP核,但用了WISHBONE,接口较繁琐,故舍弃这种方案。 | 此款PFGA里的EFB中有SPI的IP核,但用了WISHBONE,接口较繁琐,故舍弃这种方案。 | ||
- | #### FIFO模块 | + | ###### FIFO模块 |
{{ :fifo图.png?800 |}} | {{ :fifo图.png?800 |}} | ||
- | ### (3) 关键知识点/难点 | + | <code verilog> |
+ | module spi_slave (rstb,ss,sck,sdin, sdout,done,rdata,tdata); | ||
+ | input rstb,ss,sck,sdin; | ||
+ | input [7:0] tdata; | ||
+ | output sdout; //slave out master in | ||
+ | output reg done; | ||
+ | |||
+ | output reg [7:0] rdata; | ||
+ | |||
+ | |||
+ | reg [7:0] treg,rreg; | ||
+ | reg [3:0] nb; | ||
+ | wire sout; | ||
+ | |||
+ | assign sout=treg[7]; | ||
+ | assign sdout=(!ss)?sout:1'bz; //if 1=> send data else TRI-STATE sdout | ||
+ | |||
+ | //read from sdin | ||
+ | always @(posedge sck or negedge rstb) | ||
+ | begin | ||
+ | if (rstb==0) | ||
+ | begin rreg = 8'h00; rdata = 8'h00; done = 1'b0; nb = 4'd0; end | ||
+ | else if (!ss) begin | ||
+ | //MSB first, in@lsb -> left shift | ||
+ | rreg ={rreg[6:0],sdin}; | ||
+ | //increment bit count | ||
+ | nb=nb+1'b1; | ||
+ | if(nb!=4'd8) done=1'b0; | ||
+ | else begin rdata=rreg; done=1'b1; nb=4'd0; end | ||
+ | end //if(!ss)_END if(nb==8) | ||
+ | end | ||
+ | |||
+ | //send to sdout | ||
+ | always @(negedge sck or negedge rstb) | ||
+ | begin | ||
+ | if (rstb==1'b0) | ||
+ | begin treg = 8'hFF; end | ||
+ | else begin | ||
+ | if(!ss) begin | ||
+ | if(nb==4'd0) treg=tdata; | ||
+ | else begin | ||
+ | //MSB first, out=msb -> left shift | ||
+ | treg = {treg[6:0],1'b1}; | ||
+ | end | ||
+ | end //!ss | ||
+ | end //rstb | ||
+ | end //always | ||
+ | |||
+ | </code> | ||
+ | |||
+ | |||
+ | ##### (3) 关键知识点/难点 | ||
此次示波器项目,需要对FPGA进行大量编程,FPGA本身涉及例如FIFO存储等未有接触过的内容,同时程序逻辑亦十分严密,需要厘清思路方可写出正确的逻辑。下图为其中一项内容FIFO之逻辑 | 此次示波器项目,需要对FPGA进行大量编程,FPGA本身涉及例如FIFO存储等未有接触过的内容,同时程序逻辑亦十分严密,需要厘清思路方可写出正确的逻辑。下图为其中一项内容FIFO之逻辑 | ||
- | {{ :fifo_queue.png |}} | + | |
同时,为了能够将最终波形显示于树莓派,需编写图形界面,此亦为以往未有之挑战。图形界面编写上,用到了QT软件(C++语言),组内成员均无使用经验,因此图形界面的编写也为一大挑战 | 同时,为了能够将最终波形显示于树莓派,需编写图形界面,此亦为以往未有之挑战。图形界面编写上,用到了QT软件(C++语言),组内成员均无使用经验,因此图形界面的编写也为一大挑战 | ||
- | ## 4、资源报告 | + | #### 4、资源报告 |
Design Summary | Design Summary | ||
行 182: | 行 590: | ||
Number of ECLKBRIDGECS: 0 out of 2 (0%) | Number of ECLKBRIDGECS: 0 out of 2 (0%) | ||
- | # 四、树莓派端程序 | + | ### 四、树莓派端程序 |
- | ## 1、Qt图形界面 | + | #### 1、Qt图形界面 |
下图为树莓派上的Qt图形界面,有运行/暂停,通道选择,调节时基轴,调节幅值轴,调节交直流耦合功能 | 下图为树莓派上的Qt图形界面,有运行/暂停,通道选择,调节时基轴,调节幅值轴,调节交直流耦合功能 | ||
行 190: | 行 598: | ||
{{:示波器界面1.png?1000|}} | {{:示波器界面1.png?1000|}} | ||
- | ## 2、Qt程序模块(用C++编写) | + | #### 2、Qt程序模块(用C++编写) |
{{ :树莓派程序_with_qt_c_1.png?800 |}} | {{ :树莓派程序_with_qt_c_1.png?800 |}} | ||
- | # 五、成果演示 | + | ### 五、成果演示 |
{{ :shiboqiyanshishipin.mp4 |}} | {{ :shiboqiyanshishipin.mp4 |}} | ||
行 200: | 行 608: | ||
- | # 六、心得体会 | + | ### 六、心得体会 |
- | 本次项目是相当大的挑战,不论是硬件还是程序方面都有相当大的困难。 | + | - 本次项目是相当大的挑战,不论是硬件还是程序方面都有相当大的困难。 |
- | 首先是硬件上的选定,我们参考了大量的资料与数据才得以选择出合适的元件。例如选择器由于参数原因前后更换了数个 | + | - 首先是硬件上的选定,我们参考了大量的资料与数据才得以选择出合适的元件。例如选择器由于参数原因前后更换了数个 |
- | 选定元件之后绘制电路图,电路图较为复杂,且由于电路图庞大的体量中途亦有不少小错与不规范之处花费了一些时间修正。 | + | - 选定元件之后绘制电路图,电路图较为复杂,且由于电路图庞大的体量中途亦有不少小错与不规范之处花费了一些时间修正。 |
- | 电路焊接中出现了数个小问题以及一个大问题(封装),导致重新打板,更使人认识到了仔细参阅数据手册的重要性 | + | - 电路焊接中出现了数个小问题以及一个大问题(封装),导致重新打板,更使人认识到了仔细参阅数据手册的重要性 |
- | 软件部分为这次项目最大的挑战。由于显示的原因不仅需要撰写FPGA程序,亦需要在树莓派创建图形界面,这一点上毫无经验的原因也进行了大量的调研和学习才得以实现。FPGA方面逻辑错综复杂,需要考虑的因素很多,也是一大难处。累积工程量巨大,但最终通过分工也基本完成 | + | - 软件部分为这次项目最大的挑战。由于显示的原因不仅需要撰写FPGA程序,亦需要在树莓派创建图形界面,这一点上毫无经验的原因也进行了大量的调研和学习才得以实现。FPGA方面逻辑错综复杂,需要考虑的因素很多,也是一大难处。累积工程量巨大,但最终通过分工也基本完成 |
- | 最终大部分原本预定的功能均已实现,示波器可正常运转,实现双通道测量,改变时间轴/伏值轴量程,调整交直流耦合。 | + | - 最终大部分原本预定的功能均已实现,示波器可正常运转,实现双通道测量,改变时间轴/伏值轴量程,调整交直流耦合。 |
- | 本次项目中是我们大量学习到了编程逻辑,电路规划,器件选择等相当多的重要的知识,相信这些知识应当让人受益匪浅,将在未来学习工作中起到重大的帮助作用 | + | - 本次项目中是我们大量学习到了编程逻辑,电路规划,器件选择等相当多的重要的知识,相信这些知识应当让人受益匪浅,将在未来学习工作中起到重大的帮助作用 |
- | # 七、可改进的不足之处 | + | ### 七、可改进的不足之处 |
- | 1. 板子上可以加一个RESET按键。 | + | - 板子上可以加一个RESET按键。 |
- | 2. 选择耦合方式的光耦继电器AQY282S的控制位处应该串一个电阻,而不是直接接到FPGA引脚上。 | + | - 选择耦合方式的光耦继电器AQY282S的控制位处应该串一个电阻,而不是直接接到FPGA引脚上。 |
- | 3. 模拟开关ADG658的模拟输入不能超过±3.6V,导致我们的方案只能测量7.2Vpp以内的波形,未达到20Vpp的要求。改进思路:(1)1倍衰减通道上加一个光耦继电器,控制这一路的通断;(2)进入模拟开关之前先把信号衰减三倍(这样就在7V以内了)。 | + | - 模拟开关ADG658的模拟输入不能超过±3.6V(详见数据手册),导致我们的方案只能测量7.2Vpp以内的波形,未达到20Vpp的要求。改进思路:(1)1倍衰减通道上加一个光耦继电器,控制这一路的通断;(2)进入模拟开关之前先把信号衰减三倍(这样就在7V以内了)。 |
- | 4. |