FPGA——八位七段数码管驱动

一、设计思路



数码管共阳
SEL:位选
SEG:段选
数码管1显示0:sel = 3'b001,seg = 8'b0000_0011;
数码管2显示0:sel = 3'b010,seg = 8'b0000_0011;
数码管3显示0:sel = 3'b100,seg = 8'b0000_0011;
想要数码管1,2,3看着全亮,就要让位选的变化非常快(<=20ms),以至于肉眼观察不到
所以需要设计一个计算20ms的计数器,一个位选8个数码管的计数器,等到计数器计满一次就循环移位以达到位选的目的
至于段选,8个数码管,要输入指定数据,每一个指定数据占用4位,4*8=32位
输入的数据如何如何变成数码管上显示的数据,首先需要一个8选1的多路选择器,选择器的控制端由位选计数器来控制,输出的即为指定数码管的指定数据,但是输入的数据是十进制不是数码管所表示的数据,因此要建立一个查找表,以输出指定的值

二、代码

module hex8(
   clk      ,
   rst_n    ,
   disp_data,
   sel      ,  //片选
   seg      ,  //段选
   en          //现在用不到,是为了接芯片驱动的时候,在数据加载完成后,使能芯片驱动输入数据   
);
parameter   DATA_W   =  32;
parameter   SEL_W    =  8;
parameter   SEG_W    =  8;
parameter   CNTIME_N =  50000;   //1ms
parameter   HEX_N    =  8;       //8个数码管
parameter   CNTIME_W =  16;
parameter   HEX_W    =  4;

input                   clk;
input                   rst_n;
input    [DATA_W-1:0]   disp_data;
output   [SEL_W-1:0]    sel;
output   [SEG_W-1:0]    seg;
output                  en;

reg      [SEL_W-1:0]    sel;
reg      [SEG_W-1:0]    seg;

reg      [CNTIME_W-1:0] cnt_time;
wire                    add_cnt_time;
wire                    end_cnt_time;

reg      [HEX_W-1:0]    cnt_hex;
wire                    add_cnt_hex;
wire                    end_cnt_hex;

reg      [HEX_W-1:0]    hex_code;

reg                     en;

//1ms计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt_time <= 0;
   else if(add_cnt_time)begin
      if(end_cnt_time)
         cnt_time <= 0;
      else
         cnt_time <= cnt_time + 1'b1;
   end
end
assign add_cnt_time = 1;
assign end_cnt_time = add_cnt_time && cnt_time == CNTIME_N - 1;

//片选数码管计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt_hex <= 0;
   else if(add_cnt_hex)begin
      if(end_cnt_hex)
         cnt_hex <= 0;
      else
         cnt_hex <= cnt_hex + 1'b1;
   end
end
assign add_cnt_hex = end_cnt_time;
assign end_cnt_hex = add_cnt_hex && cnt_hex == HEX_N - 1;

//片选
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      sel <= 8'h01;
   else if(end_cnt_time)
      sel <= {sel[6:0],sel[7]};
end

always @(*)begin
   case(cnt_hex)
      4'd0:hex_code = disp_data[3:0]  ;
      4'd1:hex_code = disp_data[7:4]  ;
      4'd2:hex_code = disp_data[11:8] ;
      4'd3:hex_code = disp_data[15:12];
      4'd4:hex_code = disp_data[19:16];
      4'd5:hex_code = disp_data[23:20];
      4'd6:hex_code = disp_data[27:24];
      4'd7:hex_code = disp_data[31:28];
      default:hex_code = 0;
   endcase
end

//共阳编码
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      seg <= 8'hFF;
   else begin
      case(hex_code)
         4'd0 :seg <= 8'hC0;
         4'd1 :seg <= 8'hF9;
         4'd2 :seg <= 8'hA4;
         4'd3 :seg <= 8'hB0;
         4'd4 :seg <= 8'h99;
         4'd5 :seg <= 8'h92;
         4'd6 :seg <= 8'h82;
         4'd7 :seg <= 8'hF8;
         4'd8 :seg <= 8'h80;
         4'd9 :seg <= 8'h90;
         4'd10:seg <= 8'h88;
         4'd11:seg <= 8'h83;
         4'd12:seg <= 8'hC6;
         4'd13:seg <= 8'hA1;
         4'd14:seg <= 8'h86;
         4'd15:seg <= 8'h8E;
         default:seg <= 8'hFF;
      endcase
   end
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      en <= 0;
   else if(cnt_time ==  CNTIME_N - 2 && add_cnt_time)
      en <= 1;
   else
      en <=0;
end

endmodule

三、仿真代码

`timescale 1ns / 1ns

module hex8_tb();

parameter   CYCLE    =  20;
parameter   DATA_W   =  32;
parameter   SEL_W    =  8;
parameter   SEG_W    =  8;

reg                     clk;
reg                     rst_n;
reg    [DATA_W-1:0]     disp_data;
wire   [SEL_W-1:0]      sel;
wire   [SEG_W-1:0]      seg;
wire                    en;

hex8 hex8(
   clk      ,
   rst_n    ,
   disp_data,
   sel      ,  //片选
   seg      ,  //段选
   en
);

initial clk = 1;
always #(CYCLE/2) clk = ~clk;

initial begin
   rst_n = 1;
   #3;
   rst_n = 0;
   #(10*CYCLE);
   rst_n = 1;
end

initial begin
   disp_data = 0;
   #(15*CYCLE)
   disp_data = {4'd1,4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8};
   #(10_000_000);
   disp_data = {4'd9,4'd10,4'd11,4'd12,4'd13,4'd14,4'd15,4'd16};
   #(16_000_000);
   $stop;
end


endmodule

四、改进

增加一个HC595芯片,串转并输出,减少输入端口,使用HC595仅需clk,seg,sel三根线即可实现以上数码管的功能

  • HC595(移位寄存器)原理


移位寄存器时钟打四拍,即可把四位串行数据存储在寄存器里,存储寄存器打一拍就可以把锁存的数据并行输出

  • HC595时序图


    这里取125℃,3.3V,12.5MHz

五、595驱动代码

module hc595_driver(
   clk   ,
   rst_n ,
   en    ,  //数据使能信号
   data  ,  //要输入的数据
   sh_cp ,  //移位寄存器时钟
   st_cp ,  //存储寄存器时钟
   ds       //串行数据输入    
);
parameter   DATA_W   =  16;

parameter   CNTSH_N  =  4;
parameter   CNTSH_W  =  3;
parameter   CNTB_N   =  16;
parameter   CNTB_W   =  4;

input                   clk;
input                   rst_n;
input    [DATA_W-1:0]   data;
input                   en;

output                  sh_cp;
output                  st_cp;
output                  ds;

reg                     sh_cp;
reg                     st_cp;
reg                     ds;

reg      [CNTSH_W-1:0]  cnt_sh;
wire                    add_cnt_sh;
wire                    end_cnt_sh;
reg      [CNTB_W-1:0]   cnt_bit;
wire                    add_cnt_bit;
wire                    end_cnt_bit;

reg                     add_sh_flag;
reg      [DATA_W-1:0]   data_tmp;

//12.5M时钟计数器
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt_sh <= 0;
   else if(add_cnt_sh)begin
      if(end_cnt_sh)
         cnt_sh <= 0;
      else
         cnt_sh <= cnt_sh + 1'b1;
   end
end
assign add_cnt_sh = add_sh_flag;
assign end_cnt_sh = add_cnt_sh && cnt_sh == CNTSH_N - 1;

//计算移位寄存器移动了多少位
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt_bit <= 0;
   else if(add_cnt_bit)begin
      if(end_cnt_bit)
         cnt_bit <= 0;
      else
         cnt_bit <= cnt_bit + 1'b1;
   end
end
assign add_cnt_bit = end_cnt_sh;
assign end_cnt_bit = add_cnt_bit && (cnt_bit == CNTB_N - 1);


//实现移位寄存器时钟
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      sh_cp <= 0;
   else if(cnt_sh == (CNTSH_N/2 - 1) && add_cnt_sh)
      sh_cp <= 1;
   else if(end_cnt_sh)
      sh_cp <= 0;
end

//实现存储寄存器时钟,当移位寄存器移位了16位数码管信号,就输出数据
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      st_cp <= 0;
   else if(end_cnt_bit)
      st_cp <= 1;
   else if(cnt_bit == (CNTB_N/2 - 1) && add_cnt_bit)
      st_cp <= 0;
end

//并转串输出数据
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      ds <= 0;
   else if(cnt_sh==(CNTSH_N/4 - 1) && add_cnt_sh)
      ds <= data_tmp[(CNTB_N - 1)- cnt_bit];
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      add_sh_flag <= 0;
   else if(en)
      add_sh_flag <= 1;
   else if(end_cnt_bit)
      add_sh_flag <= 0;
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      data_tmp <= 0;
   else if(en && !add_sh_flag)
      data_tmp <= data;
end

endmodule

六、仿真文件代码

`timescale 1ns / 1ns

module hc595_driver_tb();

parameter CYCLE   =  20;

parameter   DATA_W   =  16;

reg                   clk;
reg                   rst_n;
reg                   en;
reg    [DATA_W-1:0]   data;

wire                  sh_cp;
wire                  st_cp;
wire                  ds;

hc595_driver hc595_driver(
   clk   ,
   rst_n ,
   en    ,
   data  ,  //要输入的数据
   sh_cp ,  //移位寄存器时钟
   st_cp ,  //存储寄存器时钟
   ds       //串行数据输入    
);

initial clk = 1;
always #(CYCLE/2) clk = ~clk;

initial begin
   rst_n = 1;
   #3;
   rst_n = 0;
   #(10*CYCLE);
   rst_n = 1;
end


initial begin
   data = 0;
   en = 0;
   #(15*CYCLE);
   data = 16'b1010_1010_1001_1001;
   en = 1;
   #(CYCLE);
   en = 0;
   #1280;
   data = 0;
   #5000;
   $stop;
end

endmodule

七、板级验证代码

module hex8_test(
   clk   ,
   rst_n ,
   sh_cp ,
   st_cp ,
   ds    
);

input          clk;
input          rst_n;
output         sh_cp;
output         st_cp;
output         ds;

wire           sh_cp;
wire           st_cp;
wire           ds;

wire           en;
wire  [15:0]   data;
wire  [7:0]    sel;
wire  [7:0]    seg;
wire  [31:0]   disp_data;

hc595_driver hc595_driver(
   .clk     (clk),
   .rst_n   (rst_n),
   .en      (en),       //数据使能信号
   .data    (data),     //要输入的数据
   .sh_cp   (sh_cp),    //移位寄存器时钟
   .st_cp   (st_cp),    //存储寄存器时钟
   .ds      (ds)        //串行数据输入    
);

hex8 hex8(
   .clk        (clk),
   .rst_n      (rst_n),
   .disp_data  (disp_data),
   .sel        (sel),      //片选
   .seg        (seg),      //段选
   .en         (en)
);


assign data = {seg,sel};
assign disp_data = 32'h12345678;
endmodule

八、仿真代码

`timescale 1ns / 1ns

module hex8_test_tb();

reg          clk;
reg          rst_n;
wire         sh_cp;
wire         st_cp;
wire         ds;

hex8_test hex8_test(
   clk   ,
   rst_n ,
   sh_cp ,
   st_cp ,
   ds    
);

initial clk = 1;
always #10 clk = ~clk;

initial begin
   rst_n = 1;
   #3;
   rst_n = 0;
   #(10 * 20)
   rst_n = 1;
end

endmodule
posted @ 2021-01-27 11:22  AdriftCore  阅读(1839)  评论(0编辑  收藏  举报