最终大赛作品——VGA简单显示、简单视力检测
最终的功能怎么说呢。。一个字——怂!(PS:此时此刻只想默默地发个笑不出的表情。。。)
算个半成品吧,不过还是记录一下,中间对Verilog有了很多新的理解
其实其中的一部分代码是借鉴网上的,比如PLL.v,比如VGA驱动时序,比如按键消抖。。
1. PLL.v
这个据说是可以自动生成的,还有待学习——
// synopsys translate_off `timescale 1 ps / 1 ps // synopsys translate_on module PLL ( inclk0, c0, c1, c2); input inclk0; output c0; output c1; output c2; wire [5:0] sub_wire0; wire [0:0] sub_wire6 = 1'h0; wire [2:2] sub_wire3 = sub_wire0[2:2]; wire [1:1] sub_wire2 = sub_wire0[1:1]; wire [0:0] sub_wire1 = sub_wire0[0:0]; wire c0 = sub_wire1; wire c1 = sub_wire2; wire c2 = sub_wire3; wire sub_wire4 = inclk0; wire [1:0] sub_wire5 = {sub_wire6, sub_wire4}; altpll altpll_component ( .inclk (sub_wire5), .clk (sub_wire0), .activeclock (), .areset (1'b0), .clkbad (), .clkena ({6{1'b1}}), .clkloss (), .clkswitch (1'b0), .enable0 (), .enable1 (), .extclk (), .extclkena ({4{1'b1}}), .fbin (1'b1), .locked (), .pfdena (1'b1), .pllena (1'b1), .scanaclr (1'b0), .scanclk (1'b0), .scandata (1'b0), .scandataout (), .scandone (), .scanread (1'b0), .scanwrite (1'b0), .sclkout0 (), .sclkout1 ()); defparam altpll_component.clk0_divide_by = 2, altpll_component.clk0_duty_cycle = 50, altpll_component.clk0_multiply_by = 1, altpll_component.clk0_phase_shift = "0", altpll_component.clk1_divide_by = 1250, altpll_component.clk1_duty_cycle = 50, altpll_component.clk1_multiply_by = 1597, altpll_component.clk1_phase_shift = "0", altpll_component.clk2_divide_by = 32, altpll_component.clk2_duty_cycle = 50, altpll_component.clk2_multiply_by = 23, altpll_component.clk2_phase_shift = "0", altpll_component.compensate_clock = "CLK0", altpll_component.inclk0_input_frequency = 20000, altpll_component.intended_device_family = "Cyclone II", altpll_component.lpm_type = "altpll", altpll_component.operation_mode = "NORMAL", altpll_component.pll_type = "FAST", altpll_component.port_activeclock = "PORT_UNUSED", altpll_component.port_areset = "PORT_UNUSED", altpll_component.port_clkbad0 = "PORT_UNUSED", altpll_component.port_clkbad1 = "PORT_UNUSED", altpll_component.port_clkloss = "PORT_UNUSED", altpll_component.port_clkswitch = "PORT_UNUSED", altpll_component.port_fbin = "PORT_UNUSED", altpll_component.port_inclk0 = "PORT_USED", altpll_component.port_inclk1 = "PORT_UNUSED", altpll_component.port_locked = "PORT_UNUSED", altpll_component.port_pfdena = "PORT_UNUSED", altpll_component.port_pllena = "PORT_UNUSED", altpll_component.port_scanaclr = "PORT_UNUSED", altpll_component.port_scanclk = "PORT_UNUSED", altpll_component.port_scandata = "PORT_UNUSED", altpll_component.port_scandataout = "PORT_UNUSED", altpll_component.port_scandone = "PORT_UNUSED", altpll_component.port_scanread = "PORT_UNUSED", altpll_component.port_scanwrite = "PORT_UNUSED", altpll_component.port_clk0 = "PORT_USED", altpll_component.port_clk1 = "PORT_USED", altpll_component.port_clk2 = "PORT_USED", altpll_component.port_clk3 = "PORT_UNUSED", altpll_component.port_clk4 = "PORT_UNUSED", altpll_component.port_clk5 = "PORT_UNUSED", altpll_component.port_clkena0 = "PORT_UNUSED", altpll_component.port_clkena1 = "PORT_UNUSED", altpll_component.port_clkena2 = "PORT_UNUSED", altpll_component.port_clkena3 = "PORT_UNUSED", altpll_component.port_clkena4 = "PORT_UNUSED", altpll_component.port_clkena5 = "PORT_UNUSED", altpll_component.port_enable0 = "PORT_UNUSED", altpll_component.port_enable1 = "PORT_UNUSED", altpll_component.port_extclk0 = "PORT_UNUSED", altpll_component.port_extclk1 = "PORT_UNUSED", altpll_component.port_extclk2 = "PORT_UNUSED", altpll_component.port_extclk3 = "PORT_UNUSED", altpll_component.port_extclkena0 = "PORT_UNUSED", altpll_component.port_extclkena1 = "PORT_UNUSED", altpll_component.port_extclkena2 = "PORT_UNUSED", altpll_component.port_extclkena3 = "PORT_UNUSED", altpll_component.port_sclkout0 = "PORT_UNUSED", altpll_component.port_sclkout1 = "PORT_UNUSED"; endmodule
2. VGA_Control.v
前半部分为借鉴网上代码,后半部分为本人原创,但是依照最后的结果来看,后半部分并不能稳定工作->-
module VGA_Control( input CLOCK_50, input RSTn, input [3:0] KEY, output VGA_BLANK_N, output VGA_CLK, output VGA_SYNC_N, output VGA_VS, output VGA_HS, output [7:0] VGA_R, output [7:0] VGA_G, output [7:0] VGA_B ); //==============================VGA底层驱动-时序控制==================================== reg[9:0] H_Cont; //行扫描计数器 reg[9:0] V_Cont; //列扫描计数器 reg vga_hs; reg vga_vs; reg[10:0] X; //横坐标 reg[10:0] Y; //纵坐标 assign VGA_HS=vga_hs; assign VGA_VS=vga_vs; //水平参数 parameter H_FRONT=16; parameter H_SYNC=96; parameter H_BACK=48; parameter H_ACT=640; parameter H_BLANK=H_FRONT+H_SYNC+H_BACK; parameter H_TOTAL=H_FRONT+H_SYNC+H_BACK+H_ACT; //垂直参数 parameter V_FRONT=11; parameter V_SYNC=2; parameter V_BACK=32; parameter V_ACT=480; parameter V_BLANK=V_FRONT+V_SYNC+V_BACK; parameter V_TOTAL=V_FRONT+V_SYNC+V_BACK+V_ACT; //锁相环产生25MHZ的时钟 wire CLK_25; PLL pll0( .inclk0 ( CLOCK_50 ), .c0 ( CLK_25 ) ); //Select DAC CLOCK assign VGA_CLK = CLK_25; assign VGA_SYNC_N = 1'b0; //If not SOG, Sync input should be tied to 0; assign VGA_BLANK_N = ~((H_Cont<H_BLANK)||(V_Cont<V_BLANK)); //Horizontal Generator:Refer to the pixel clock always@(posedge CLK_25, negedge RSTn)begin if(!RSTn)begin H_Cont<=0; vga_hs<=1; X<=0; end else begin if(H_Cont<H_TOTAL) H_Cont<=H_Cont+1'b1; else H_Cont<=0; //垂直扫描 if(H_Cont==H_FRONT-1) //Front porch end vga_hs<=1'b0; if(H_Cont==H_FRONT+H_SYNC-1) vga_hs<=1'b1; //Current X if(H_Cont>=H_BLANK) X<=H_Cont-H_BLANK; else X<=0; end end //水平扫描 always@(posedge VGA_HS, negedge RSTn)begin if(!RSTn)begin V_Cont<=0; vga_vs<=1; Y<=0; end else begin if(V_Cont<V_TOTAL) V_Cont<=V_Cont+1'b1; else V_Cont<=0; //Vertical Sync if(V_Cont==V_FRONT-1) vga_vs<=1'b0; if(V_Cont==V_FRONT+V_SYNC-1) vga_vs<=1'b1; //Current Y if(V_Cont>=V_BLANK) Y<=V_Cont-V_BLANK; else Y<=0; end end //================================================VGA显示================================================= reg[7:0] vga_r; reg[7:0] vga_g; reg[7:0] vga_b; assign VGA_R=vga_r; assign VGA_G=vga_g; assign VGA_B=vga_b; //==========显示控制算法模块=========== //==========键盘检测=========== wire isDown1; wire isDown2; wire isDown3; wire isDown4; Button_Module b0( .CLOCK_50(CLOCK_50), .RSTn(RSTn), .key(KEY[3]), .isDown(isDown1) ); Button_Module b1( .CLOCK_50(CLOCK_50), .RSTn(RSTn), .key(KEY[2]), .isDown(isDown2) ); Button_Module b2( .CLOCK_50(CLOCK_50), .RSTn(RSTn), .key(KEY[1]), .isDown(isDown3) ); Button_Module b3( .CLOCK_50(CLOCK_50), .RSTn(RSTn), .key(KEY[0]), .isDown(isDown4) ); //=========一旦检测到按键,产生一个高脉冲 reg Start_Sig; always@(posedge CLOCK_50, negedge RSTn) if(!RSTn) Start_Sig <= 1'b0; else begin if(isDown1 || isDown2 || isDown3 || isDown4) Start_Sig <= 1'b1; else Start_Sig <= 1'b0; end //============产生随机数============== wire [1:0] direction; wire Done_Sig; suijishu s0( .clk(CLK_25), .RSTn(RSTn), .Start_Sig(Start_Sig), .Done_Sig(Done_Sig), .direction(direction) ); //==================================================================== //======判断为E显示还是结果显示 reg [1:0] i; reg [1:0] qi; always@(posedge CLOCK_50, negedge RSTn)begin if(!RSTn) begin i <= 0; qi <= 0; end else if(Start_Sig) begin case({isDown1,isDown2,isDown3,isDown4}) 4'b1000 : if(direction == 2'b00) begin i <= 1; qi <= i; end else begin i <= 2; qi <= i; end 4'b0100 : if(direction == 2'b01) begin i <= 1; qi <= i; end else begin i <= 2; qi <= i; end 4'b0010 : if(direction == 2'b10) begin i <= 1; qi <= i; end else begin i <= 2; qi <= i; end 4'b0001 : if(direction == 2'b11) begin i <= 1; qi <= i; end else begin i <= 2; qi <= i; end endcase end else begin i <= 0; qi <= 0; end end //计时 reg [15:0] cnt0; reg [15:0] cnt1; reg e; reg [1:0] ee; always@(posedge CLOCK_50, negedge RSTn)begin if(!RSTn) cnt0 <= 0; else if(cnt0 == 16'd49_999) cnt0 <= 0; else cnt0 <= cnt0 + 1; end always@(posedge CLOCK_50, negedge RSTn)begin if(!RSTn) cnt1 <= 0; else if(e) begin if(cnt0 == 16'd49_999) cnt1 <= cnt1 + 1; end else cnt1 <= 0; end always@(posedge CLK_25, negedge RSTn)begin if(!RSTn) e <= 0; else case(e) 0: if((qi == 0) && (i == 1)) begin //正确提示 e <= 1; ee <= 1; end else if((qi == 0) && (i == 2)) begin //错误提示 e <= 1; ee <= 2; end else e <= 0; 1: e <= cnt1 == 10'd5000 ? 0 : 1; endcase end /* wire [1:0] direction; wire [1:0] ee; wire e; judge j0( .CLOCK_50(CLOCK_50), .CLOCK_25(CLOCK_25), .RSTn(RSTn), .KEY(KEY), .e(e), .ee(ee) ); */ always@(posedge CLK_25, negedge RSTn)begin if(!RSTn)begin vga_r<=0; vga_g<=0; vga_b<=0; end else if(e == 0) begin case(direction) 2'b00: //右 begin if((X>=215 && X<=240 && Y>=110 && Y<=370) || ((X>=240 && X<=400) && ((Y>=110 && Y<=130) || (Y>=230 && Y<=250) || (Y>=350 && Y<=370)))) begin vga_r <= 0; vga_g <= 0; vga_b <= 0; end else begin vga_r <= 128; vga_g <= 128; vga_b <= 128; end end 2'b01: //上 begin if((X>=190 && X<=450 && Y>=307 && Y<=332) || ((Y>=147 && Y<=307) && ((X>=190 && X<=210) || (X>=310 && X<=330) || (X>=430 && X<=450)))) begin vga_r <= 0; vga_g <= 0; vga_b <= 0; end else begin vga_r <= 128; vga_g <= 128; vga_b <= 128; end end 2'b10: //左 begin if((X>=375 && X<=400 && Y>=110 && Y<=370) || ((X>=215 && X<=375) && ((Y>=110 && Y<=130) || (Y>=230 && Y<=250) || (Y>=350 && Y<=370)))) begin vga_r <= 0; vga_g <= 0; vga_b <= 0; end else begin vga_r <= 128; vga_g <= 128; vga_b <= 128; end end 2'b11: //下 begin if((X>=190 && X<=450 && Y>=147 && Y<=172) || ((Y>=172 && Y<=332) && ((X>=190 && X<=210) || (X>=310 && X<=330) || (X>=430 && X<=450)))) begin vga_r <= 0; vga_g <= 0; vga_b <= 0; end else begin vga_r <= 128; vga_g <= 128; vga_b <= 128; end end endcase end else if(e == 1) begin case(ee) 1: begin vga_r <= 0; vga_g <= 128; vga_b <= 0; end 2: begin vga_r <= 128; vga_g <= 0; vga_b <= 0; end endcase end end endmodule
3. suijishu.v
这个算法思想借鉴一篇论文,代码是自己实现的
http://max.book118.com/html/2014/0528/8498156.shtm
module suijishu( input clk, input RSTn, input Start_Sig, output Done_Sig, output [1:0] direction ); reg [3:0] e; reg [1:0] rd; reg [1:0] qd; reg rDone; always @ (posedge clk or negedge RSTn) if(!RSTn) begin e <= 4'd0; rd <= 2'd0; qd <= 2'd0; rDone <= 0; end else begin if(Start_Sig == 1) begin rd <= e[1:0]; qd <= rd; rDone <= 1; end else e <= e + 1'b1; end //防止与上一次重复 assign direction = (qd == rd) ? rd+1 : rd; assign Done_Sig = rDone; endmodule
4. Button_Module.v
这个部分靠自己之前写的代码不能稳定工作,因此后来还是借鉴了《VerilogHDL那些事儿——建模篇》这本书上的代码
module Button_Module( input CLOCK_50, input RSTn, input key, output isDown ); /* //按键按下后有个延迟等待时间,知道键被松开 reg i; //当前i reg qi; //当前i的前一个状态 always@(posedge clk or negedge RSTn) begin if(!RSTn) i <= 1'b1; else case(i) 1'b0: begin i <= key == 1'b0 ? 1'b0 : 1'b1; qi <= i; end 1'b1: begin i <= key == 1'b1 ? 1'b1 : 1'b0; qi <= i; end endcase end */ //===========电平检测模块============ parameter T100US = 11'd4999; reg [10:0] Count1; reg isEn; //电平检测不稳定需延时100US always @ (posedge CLOCK_50 or negedge RSTn) if(!RSTn) begin Count1 <= 11'd0; isEn <=1'b0; end else if(Count1==T100US) isEn <= 1'b1; else Count1 <= Count1 + 1'b1; //检测按键状态 reg H2L_F1; reg H2L_F2; reg L2H_F1; reg L2H_F2; always @ (posedge CLOCK_50 or negedge RSTn) if(!RSTn) begin H2L_F1 <= 1'b1; H2L_F2 <= 1'b1; L2H_F1 <= 1'b0; L2H_F2 <= 1'b0; end else begin H2L_F1 <= key; H2L_F2 <= H2L_F1; L2H_F1 <= key; L2H_F2 <= L2H_F1; end wire H2L_Sig; wire L2H_Sig; assign H2L_Sig = isEn ? (H2L_F2 & !H2L_F1) : 1'b0; assign L2H_Sig = isEn ? (!L2H_F2 & L2H_F1) : 1'b0; //=============消抖模块=============== parameter T1MS = 16'd49_999; reg [15:0] Count2; reg [3:0] Count_MS; //延时1MS always @ (posedge CLOCK_50 or negedge RSTn) if(!RSTn) Count2 <= 16'd0; else if(isCount && Count2==T1MS) Count2 <= 16'd0; else if(isCount) Count2 <= Count2 + 1'b1; else if(!isCount) Count2 <= 16'd0; always @ (posedge CLOCK_50 or negedge RSTn) if(!RSTn) Count_MS <= 4'd0; else if(isCount && Count2==T1MS) Count_MS <= Count_MS + 1'b1; else if(!isCount) Count_MS <= 4'd0; reg isCount; reg Pin_Out; reg [1:0] i; always @ (posedge CLOCK_50 or negedge RSTn) if(!RSTn) begin isCount <= 1'b0; Pin_Out <= 1'b0; i <= 2'd0; end else case(i) 2'd0: if(H2L_Sig) i <= 2'd1; else if(L2H_Sig) i<= 2'd2; 2'd1: if(Count_MS == 4'd10) begin isCount <= 1'b0; Pin_Out <= 1'b1; i <= 2'd0; end else isCount <= 1'b1; 2'd2: if(Count_MS == 4'd10) begin isCount <= 1'b0; Pin_Out <= 1'b0; i <= 2'd0; end else isCount <= 1'b1; endcase assign isDown = Pin_Out; //Pin_Out为1表示按键被按下,为表示松开 endmodule
5. sighting.v
顶层
//======================================================= // This code is generated by Terasic System Builder //======================================================= module sighting( //////////// CLOCK ////////// CLOCK_50, RSTn, //////////// LED ////////// LEDG, LEDR, //////////// KEY ////////// KEY, //////////// VGA ////////// VGA_B, VGA_BLANK_N, VGA_CLK, VGA_G, VGA_HS, VGA_R, VGA_SYNC_N, VGA_VS ); //======================================================= // PORT declarations //======================================================= //////////// CLOCK ////////// input CLOCK_50; input RSTn; //////////// LED ////////// output [8:0] LEDG; output [17:0] LEDR; //////////// KEY ////////// input [3:0] KEY; //////////// VGA ////////// output [7:0] VGA_B; output VGA_BLANK_N; output VGA_CLK; output [7:0] VGA_G; output VGA_HS; output [7:0] VGA_R; output VGA_SYNC_N; output VGA_VS; //======================================================= // Structural coding //======================================================= //====================VGA显示======================== VGA_Control v0( .CLOCK_50(CLOCK_50), .RSTn(RSTn), .KEY(KEY), .VGA_BLANK_N(VGA_BLANK_N), .VGA_CLK(VGA_CLK), .VGA_SYNC_N(VGA_SYNC_N), .VGA_VS(VGA_VS), .VGA_HS(VGA_HS), .VGA_R(VGA_R), .VGA_B(VGA_B), .VGA_G(VGA_G)); endmodule
遇到的问题——尚未解决的有:1、VGA的分模块写,这个尝试了好多遍,一把底层驱动部分和VGA显示部分分开写就不能正常工作了,后来就只能把这两部分写在一个模块里了
2、VGA数码管,LCD仿佛不能同时用的样子,一把Seg或者LCD用DE2_115_SystemBuilder添进去VGA就不能正常工作了,原因不明
已解决部分的问题:判断按键与图标的一致性之后,需控制显示屏红或绿5秒,一个大概可以说搞了一整天,最后勉强可以实现了,但是不稳定,这个时候我才意识到仿真的重要性。——的确,一个工具只有当你不得不用到它的时候才不得不去学,嗯,就是这样。
人家书上之前讲案件模块中电平检测需要延时的时候自己总觉得没啥必要,后来自己实现了一个不用电平检测延时的程序依旧能用按键控制LED亮灭的时候就更确定自己的想法了——知道在这个小项目中遇到显示屏显示闪动问题,才知道自己错了,完全没有考虑全面的思想啊。。。
收获:看了那么多代码,明白了什么时候用reg,什么时候用wire,reg 和 wire 后面的[]里的数怎么自己根据情况定。
需要在always里用到的变量需要用reg先定义,如果要output需用assign赋给对应的output
如果要执行assign,而output里有没有该变量的话,就需要用wire定义一个
嗯,其实也没有很难,当你自己敲代码调试代码,遇到报错的次数多了之后,其中的规则就都明白了,不错,为自己点个赞*^_^*
- 还有就是想循环间歇性执行某个操作的时候,因为没有for语句,所以实现起来还是有点技巧的,具体的技巧我体会得也不是很透彻,还有待阅读更多书籍丫。。。