OpenVINOTM,给你看得见的未来!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » FPGA » 常用Verilog HDL程序

共5条 1/1 1 跳转至

常用Verilog HDL程序

菜鸟
2017-06-28 16:46:00    评分

1.按键软件去抖动

input[KEY_WIDTH-1:0] key_in; //外部按键输入

output[KEY_WIDTH-1:0]key_out; //按键消抖输出

reg[KEY_WIDTH-1:0]dout1,dout2,dout3; //寄存器

parameter KEY_WIDTH = 8; //参数

assign key_out = (dout1 | dout2 | dout3); //按键消抖输出

always @(posedge clk)  //  always @(posedge count[17])  当系统时钟是48MHZ时,count一直计算,计到18位为1时也就满足了按键时钟的要求

begin

dout1 <= key_in;

dout2 <= dout1;

dout3 <= dout2;

end


//下面是利用按键 消抖后的输出  设置一个标志位,将琴键开关转换为乒乓开关   就需要点击2下才可以回复到原来的状态

always @(negedge key_out)
begin
keyen = ~keyen;//将琴键开关转换为乒乓开关
end

或者利用边沿检测的知识

always @(posedge clock)//按键6
begin
if(key_out )//2中的key_out 
keyen = ~keyen;
end

按键消抖关键在于提取稳定的低电平状态,滤除前沿、后沿抖动毛刺。对于一个按键信号,可以用一个脉冲对它进行采样。如果连续三次采样为低电平,可以认为信号已经处于键稳定状态,这时输出一个低电平按键信号。继续采样的过程中如果不能满足连续三次采样为低,则认为键稳定状态结束,这时输出变为高电平。

按键消抖:按键消抖处理一般有硬件和软件两种方法。软件消抖是检测到有触发后,延时一段时间(一般为10ms)后再检测触发状态,如果与之前检测到的状态相同,则认为有按键按下;如果没有。则判断为误触发。硬件就是加去抖动电路,这样可以从根本上解决按键抖动的问题。

消抖电路的采样时钟要实际应用可以灵活改变,因为按键触发的时间一般为几百毫秒,干扰毛刺脉宽一般为几百毫秒到几毫秒,所以采样时钟的周期一般为几毫秒。

这里我们可以选择5ms 即上式的clk的频率为200HZ

2.按键的边沿(下降沿)检测


reg [7:0] dout1,dout2,dout3,buff;

always @(posedge clk)

begin

dout1 <= key_in;

dout2 <= dout1;

dout3 <= dout2;

end

always @(posedge clk)

begin

buff<=dout1|dout2|dout3;

end

assign key_out = ~(dout1 | dout2 | dout3)&buff;//按键的边缘检测  if(key_out[0])..然后就可以用key_out来作为控制信号了


//时钟分频部分     系统时钟 48MHZ
always @(posedge clock)
begin
if (count < 17'd120000)
begin
     count <= count + 1'b1;
     div_clk <= 1'b0;
  end
  else
  begin
     count <= 17'd0;
     div_clk <= 1'b1;
  end
end


//按键消抖部分
always @(posedge clock)
begin
if(div_clk) //分频时钟,用于消抖和扫描
begin
dout1 <= key;
dout2 <= dout1;
dout3 <= dout2;
end 
end

//assign key_out = (dout1 | dout2 | dout3); //按键消抖输出


//按键边沿检测部分
always @(posedge clock)
begin
buff <= dout1 | dout2 | dout3;  //buff为按键消抖输出
end


assign key_edge = ~(dout1 | dout2 | dout3) & buff;  //按键消抖输出


always @(posedge clock) //有按键按下
begin
if(key_edge[0])
hex_r[15:12] <= hex_r[15:12] + 1'b1;
end

3.程序中用到的小分频程序


always @(posedge clk)

begin

if(dividi_clk)

counter<=26'd0;//使counter为0

else

counter<=counter+1'b1;

end

assign dividi=(counter>=26'd48000);//26‘d48000就是分频数(-1)。

 如图是8分频的时序图

reg[16:0]count;//时钟分频计数器    120000分频

always @(posedge clock)
begin
if (count < 17'd120000)
begin
     count <= count + 1'b1;
     div_clk <= 1'b0;
  end
  else
  begin
     count <= 17'd0;
     div_clk <= 1'b1;
  end
end

以上2个程序的div都只是在一个系统时钟内才会出现一个高电平。。这样做有利于使整个的always块的时钟都是系统时钟  像

always @(posedge clock)           都用系统时钟                                        
        if(div_clk)

always @(posedge clk)

begin

     if(cnt==设定值) //设定值-起始值=分频数的一半,即计数到分频数的一半时反转输出

begin

             cnt<=起始值;

     out<=~out;

end

     else

cnt<=cnt+1'b1;

end 


 

如图8分频


always@(posedge clk)
begin
count <= count + 1'b1;//计数器加1
if(count == count_end)          //count_end为分频数的一半
begin
count <= 16'h0;//计数器清零
beep_r <= !beep_r;//取反输出信号
end
end


4.循环左移,循环右移

现假设

led[4:0] =11101;

循环左移:      led<={led[3:0],led[4]};

循环右移:      led<={led[0],led[4:1]};


5.16位的乘法器(吞吐量不大)

if(i == 5'd0) begin//锁存乘数、被乘数
areg <= ain;//ain,bin分别为乘数和被乘数
breg <= bin;
end
else if(i > 5'd0 && i < 5'd16) begin
if(areg[i-1]) yout_r = {1'b0,yout[30:15]+breg,yout_r[14:1]};//累加并移位
else yout_r <= yout_r>>1;//移位不累加
end
else if(i == 5'd16 && areg[15]) yout_r[31:16] <= yout_r[31:16]+breg;//累加不移位   yout_r 是积

assign yout = yout_r;


5.波特率的设置和利用

系统时钟为50HZ

parameter bps9600= 5207,//波特率为9600bps
          bps19200= 2603,//波特率为19200bps
          bps38400 = 1301,//波特率为38400bps
          bps57600 = 867,//波特率为57600bps
          bps115200  = 433; //波特率为115200bps


parameter bps9600_2 = 2603,
          bps19200_2  = 1301,
          bps38400_2  = 650,
          bps57600_2  = 433,
          bps115200_2 = 216;  
`define BPS_PARA5207//波特率为9600时的分频计数值
`define BPS_PARA_22603//波特率为9600时的分频计数值的一半,用于数据采样
reg[12:0] cnt;  //分频计数
reg clk_bps_r; //波特率时钟寄存器

always @ (posedge clk or negedge rst_n)
      if(!rst_n)    cnt <= 13'd0;
      else if(cnt == `BPS_PARA )     cnt <= 13'd0;//波特率计数清零
       else     cnt <= cnt+1'b1;//波特率时钟计数启动


always @ (posedge clk or negedge rst_n)
     if(!rst_n)   clk_bps_r <= 1'b0;
     else if(cnt == `BPS_PARA_2)    clk_bps_r <= 1'b1;// clk_bps_r高电平为接收数据位的中间采样点,同时也作为发送数据的数据改变点
     else   clk_bps_r <= 1'b0;

assign clk_bps = clk_bps_r;


6.对于inout信号的处理

当遇到inout双向数据的时候,要把它分成2部分,

assign data = oe ? data_in : 2'bzz;

assign data_out= data;


7.数码管的显示

always @(posedge clock)                                   //定义上升沿触发进程
begin
        if(div_clk)    // 数码管的刷新时间  大概可以是1MS
                cnt3 <= cnt3 + 1'b1;
end

always @(posedge clock)                                                   
begin
        if(div_clk)
        begin
                case(cnt3)                                                        //选择扫描显示数据
                        3'd0:disp_dat = hex_r[15:12];        //第一个数码管    第一个数码管要显示的值
                        3'd1:disp_dat = hex_r[11:8];        //第二个数码管
                        3'd2:disp_dat = hex_r[7:4];                //第三个数码管
                        3'd3:disp_dat = hex_r[3:0];                //第四个数码管
                        3'd4:disp_dat = data[15:12];        //第五个数码管
                        3'd5:disp_dat = data[11:8];                //第六个数码管
                        3'd6:disp_dat = data[7:4];                //第七个数码管
                        3'd7:disp_dat = data[3:0];                //第八个数码管
                endcase
                case(cnt3)                                                        //选择数码管显示位
                        3'd0:dig_r = 8'b01111111;                //选择第一个数码管显示
                        3'd1:dig_r = 8'b10111111;                //选择第二个数码管显示
                        3'd2:dig_r = 8'b11011111;                //选择第三个数码管显示
                        3'd3:dig_r = 8'b11101111;                //选择第四个数码管显示
                        3'd4:dig_r = 8'b11110111;                //选择第五个数码管显示
                        3'd5:dig_r = 8'b11111011;                //选择第六个数码管显示
                        3'd6:dig_r = 8'b11111101;                //选择第七个数码管显示
                        3'd7:dig_r = 8'b11111110;                //选择第八个数码管显示
                endcase        
        end
end

always @(disp_dat)
begin
        case(disp_dat)                                                //七段译码
                4'h0:seg_r = 8'hc0;                                //显示0
                4'h1:seg_r = 8'hf9;                                //显示1
                4'h2:seg_r = 8'ha4;                                //显示2
                4'h3:seg_r = 8'hb0;                                //显示3
                4'h4:seg_r = 8'h99;                                //显示4
                4'h5:seg_r = 8'h92;                                //显示5
                4'h6:seg_r = 8'h82;                                //显示6
                4'h7:seg_r = 8'hf8;                                //显示7
                4'h8:seg_r = 8'h80;                                //显示8
                4'h9:seg_r = 8'h90;                                //显示9
                4'ha:seg_r = 8'h88;                                //显示a
                4'hb:seg_r = 8'h83;                                //显示b
                4'hc:seg_r = 8'hc6;                                //显示c
                4'hd:seg_r = 8'ha1;                                //显示d
                4'he:seg_r = 8'h86;                                //显示e
                4'hf:seg_r = 8'h8e;                                //显示f
        endcase
end

//数码管动态扫描显示部分
always @(posedge clk)                                           //count[17:15]大约1ms改变一次
begin
        case(count[17:15])                                                //选择扫描显示数据
                3'd0:disp_dat = hour[3:0];                        //秒个位   第一个数码管要显示的值
                3'd1:disp_dat = hour[7:4];                        //秒十位
                3'd2:disp_dat = 4'ha;                                //显示"-"
                3'd3:disp_dat = hour[11:8];                        //分个位
                3'd4:disp_dat = hour[15:12];                //分十位
                3'd5:disp_dat = 4'ha;                                //显示"-"
                3'd6:disp_dat = hour[19:16];                //时个位
                3'd7:disp_dat = hour[23:20];                //时十位
        endcase
        case(count[17:15])                                                //选择数码管显示位
                3'd0:dig_r = 8'b11111110;                        //选择第一个数码管显示
                3'd1:dig_r = 8'b11111101;                        //选择第二个数码管显示
                3'd2:dig_r = 8'b11111011;                        //选择第三个数码管显示
                3'd3:dig_r = 8'b11110111;                        //选择第四个数码管显示
                3'd4:dig_r = 8'b11101111;                        //选择第五个数码管显示
                3'd5:dig_r = 8'b11011111;                        //选择第六个数码管显示
                3'd6:dig_r = 8'b10111111;                        //选择第七个数码管显示
                3'd7:dig_r = 8'b01111111;                        //选择第八个数码管显示
        endcase        
end

always @(posedge clk)
begin
        case(disp_dat)
                4'h0:seg_r = 8'hc0;                                        //显示0
                4'h1:seg_r = 8'hf9;                                        //显示1
                4'h2:seg_r = 8'ha4;                                        //显示2
                4'h3:seg_r = 8'hb0;                                        //显示3
                4'h4:seg_r = 8'h99;                                        //显示4
                4'h5:seg_r = 8'h92;                                        //显示5
                4'h6:seg_r = 8'h82;                                        //显示6
                4'h7:seg_r = 8'hf8;                                        //显示7
                4'h8:seg_r = 8'h80;                                        //显示8
                4'h9:seg_r = 8'h90;                                        //显示9
                4'ha:seg_r = 8'hbf;                                        //显示-
                default:seg_r = 8'hff;                                //不显示
        endcase
        if((count[17:15]== 3'd2)&sec)
                seg_r = 8'hff;
end
数码管的动态扫描显示时刷新率最好大于50HZ,即每显示一轮的时间不超过20ms。每个数码管显示的时间不能太长也不能太短,时间太长会影响刷新率,导致总体显示呈现闪烁的现象;时间太短发光二极管的电流导通时间也就短,会影响总体的显示亮度。一般控制在1ms左右最佳。即数码管的时钟设置为1000Hz


8.复位----异步复位 同步释放
  1. reg R1;
  2. reg rst_out;  //rst_out 为需要使用的复位信号
  3. always @ (posedge clk,negedge rst_n)// rst_n为输入的复位信号
  4.      if(!rst_n)
  5.          begin
  6.              R1 <= 1'b0;
  7.              rst_out <= 1'b0;
  8.          end
  9.      else  
  10.          begin
  11.              R1 <= 1'b1;
  12.              rst_out <= R1;
  13.         end
  14. endmodule
复制代码
9.移位发送的问题
  1. if(bincnt<4'd10)
  2.   begin
  3.         txd_reg =  Datainbuf2[0]; //从最低位开始发送
  4.         Datainbuf2 = Datainbuf>>bincnt;        //移位输出
  5.         bincnt = bincnt+4'd1;                //发送数据位计数
  6.   end
  7. else
  8.         bincnt = 4'd0;
复制代码
10.检测信号的上升沿
  1. always @(posedge clock)
  2. begin
  3.         buf1 <= adc_clk_r;
  4.         buf2 <= buf1;
  5. end
  6. assign en=buf1 & ~buf2;//如果adc_clk_r出现了上升沿 那么en =1一个系统时钟

  7. always @(posedge clock)
  8. begin
  9.         if(en)       
复制代码


11.信号的边沿检测
  1. always @(posedge clock)
  2. begin 
  3.      data_r1<=data_in;
  4.      data_r2<=data_r1;
  5. end 
  6. assign s_posedge = data_r1 & ~data_r2;  //上升沿检测
  7. assign s_negedge = ~data_r1 & data_r2;  //下降沿检测
  8. assign s_edge = data_r1 ^ data_r2;    //双边沿检测
复制代码


菜鸟
2018-01-30 22:02:48    评分
2楼

太好啦



菜鸟
2018-02-01 10:53:03    评分
3楼

感谢楼主分享


工程师
2020-04-29 14:10:34    评分
4楼

多谢楼主


助工
2020-05-02 23:37:33    评分
5楼

总结的很全面


共5条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]