首页 > 技术文章 > FPGA——基于串口校时的数字钟设计

cnlntr 2021-01-27 12:09 原文

一、设计思路

  • 器件使用:小梅哥AC703开发板
  • 计数器设计:一个计数器用来计算1秒的时间,第二个计数器用来计算60秒,第三个计数器用来计算60分钟,第四个计数器用来计算24小时
  • 编码问题1:计数器显示时间为10进制,在使用过程中是需要使用BCD码的,因此做一个十进制转BCD的模块
  • 编码问题2:计数器显示时间为10进制,在使用串口通信的时候,串口的输入以及输出都是ASCII码,接收串口信号的时候把ASCII码转成十进制,发送串口信号的时候把十进制转成ASCII码,要设计十进制转ASCII码,ASCII码转十进制的模块
  • 多字节串口接收通信协议问题:这里将由上位机串口发送的ASCII码进行识别,ASCII码为":"的才为正确的数据,输入时间格式限定为"23:59:59"

二、计数器代码

module my_clock(
   clk         ,
   rst_n       , 
   uart_rx_done,
   uart_rx_data,
   disp_data   ,
   uart_tx
);
parameter UARTRX_W   =  48;         //接收为ASCII码,245959,6*8=48;
parameter UARTTX_W   =  72;         //发出为ASCII码,24:59:59,8*8=64;还有一个换行符64+8=72
parameter DISPDA_W   =  32;

parameter CNTD_W     =  26;
parameter CNTD_N     =  50_000_000;
parameter CNTS_W     =  6;
parameter CNTS_N     =  60;
parameter CNTM_W     =  6;
parameter CNTM_N     =  60;
parameter CNTH_W     =  5;
parameter CNTH_N     =  24;
parameter BCD_W      =  8;
parameter ASCII_W    =  16;
parameter DEC_W      =  6;


input                      clk;
input                      rst_n;
input    [UARTRX_W-1:0]    uart_rx_data;
input                      uart_rx_done;
output   [DISPDA_W-1:0]    disp_data;
output                     uart_tx;

reg      [DISPDA_W-1:0]    disp_data;
wire                       uart_tx;

reg      [UARTTX_W-1:0]    uart_tx_data;
reg      [CNTD_W-1:0]      cnt_div;
wire                       add_cnt_div;
wire                       end_cnt_div;

reg      [CNTS_W-1:0]      cnt_s;
wire                       add_cnt_s;
wire                       end_cnt_s;

reg      [CNTM_W-1:0]      cnt_m;
wire                       add_cnt_m;
wire                       end_cnt_m;

reg      [CNTH_W-1:0]      cnt_h;
wire                       add_cnt_h;
wire                       end_cnt_h;

wire     [BCD_W-1:0]       second_bcd;
wire     [BCD_W-1:0]       minute_bcd;
wire     [BCD_W-1:0]       hour_bcd;
wire     [ASCII_W-1:0]     second_ascii;
wire     [ASCII_W-1:0]     minute_ascii;
wire     [ASCII_W-1:0]     hour_ascii;
wire     [DEC_W-1:0]       second_dec;
wire     [DEC_W-1:0]       minute_dec;
wire     [DEC_W-1:0]       hour_dec;

reg                        set_flag;

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      cnt_div <= 0;
   else if(add_cnt_div)begin
      if(end_cnt_div)
         cnt_div <= 0;
      else
         cnt_div <= cnt_div + 1'b1;
   end
end
assign add_cnt_div = 1;
assign end_cnt_div = add_cnt_div && (cnt_div == CNTD_N - 1 || set_flag);

//秒
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      cnt_s <= 0;
   end
   else if(add_cnt_s)begin
      if(end_cnt_s)
         cnt_s <= 0;
      else
         cnt_s <= cnt_s + 1;
   end
   else if(set_flag)
      cnt_s <= second_dec;
end
assign add_cnt_s = end_cnt_div && !set_flag;
assign end_cnt_s = add_cnt_s && cnt_s == CNTS_N - 1;

//分
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      cnt_m <= 0;
   end
   else if(add_cnt_m)begin
      if(end_cnt_m)
         cnt_m <= 0;
      else
         cnt_m <= cnt_m + 1;
   end
   else if(set_flag)
      cnt_m <= minute_dec;
end
assign add_cnt_m = end_cnt_s && !set_flag;
assign end_cnt_m = add_cnt_m && cnt_m == CNTM_N - 1 ;

//时
always @(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
      cnt_h <= 0;
   end
   else if(add_cnt_h)begin
      if(end_cnt_h)
         cnt_h <= 0;
      else
         cnt_h <= cnt_h + 1;
   end
   else if(set_flag)
      cnt_h <= hour_dec;
end
assign add_cnt_h = end_cnt_m && !set_flag;
assign end_cnt_h = add_cnt_h && cnt_h == CNTH_N - 1 ;

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      disp_data <= 0;
   else
      disp_data <= {hour_bcd,4'hF,minute_bcd,4'hF,second_bcd};
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      uart_tx_data <= 0;
   else
      uart_tx_data <= {hour_ascii,8'h3A,minute_ascii,8'h3A,second_ascii,8'h0A};
end

always @(posedge clk or negedge rst_n)begin
   if(!rst_n)
      set_flag <= 0;
   else if(uart_rx_done)
      set_flag <= 1;
   else
      set_flag <= 0;
end

uart_tx_multibyte uart_tx_multibyte(
   .clk      (clk),                             //时钟
   .rst_n    (rst_n),                           //复位
   .data_n   (uart_tx_data),                    //要发送的多字节数据
   .trans_go (cnt_div == 2 && add_cnt_div),   //发送使能
   .uart_tx  (uart_tx)                          //串口发送数据
);

DecimaltoBCD DecimaltoBCD_S(
   .clk(clk),
   .rst_n(rst_n),
   .dec(cnt_s)   ,
   .bcd(second_bcd)
);

DecimaltoBCD DecimaltoBCD_M(
   .clk(clk),
   .rst_n(rst_n),
   .dec(cnt_m)   ,
   .bcd(minute_bcd)
);

DecimaltoBCD DecimaltoBCD_H(
   .clk(clk),
   .rst_n(rst_n),
   .dec(cnt_h)   ,
   .bcd(hour_bcd)
);

DecimaltoASCII DecimaltoASCII_S(
   .clk(clk),
   .rst_n(rst_n),
   .dec  (cnt_s) ,
   .ascii(second_ascii) 
);

DecimaltoASCII DecimaltoASCII_M(
   .clk(clk),
   .rst_n(rst_n),
   .dec  (cnt_m) ,
   .ascii(minute_ascii) 
);

DecimaltoASCII DecimaltoASCII_H(
   .clk(clk),
   .rst_n(rst_n),
   .dec  (cnt_h) ,
   .ascii(hour_ascii) 
);

ASCIItoDecimal ASCIItoDecimal_S(
   .clk(clk),
   .rst_n(rst_n),
   .ascii (uart_rx_data[15:0]),
   .dec   (second_dec)
);

ASCIItoDecimal ASCIItoDecimal_M(
   .clk(clk),
   .rst_n(rst_n),
   .ascii (uart_rx_data[31:16]),
   .dec   (minute_dec)
);

ASCIItoDecimal ASCIItoDecimal_H(
   .clk(clk),
   .rst_n(rst_n),
   .ascii (uart_rx_data[47:32]),
   .dec   (hour_dec)
);


endmodule

三、顶层文件

module my_clock_top(
   clk      ,
   rst_n    ,
   uart_rx  ,
   ch_cp    ,
   st_cp    ,
   uart_tx  ,
   ds    
);

input       clk;
input       rst_n;
input       uart_rx;

output      ch_cp;
output      st_cp;
output      ds;
output      uart_tx;

wire        ch_cp;
wire        st_cp;
wire        ds;

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

wire  [47:0]   clock_data;
wire           uart_rx_done;

my_clock my_clock(
   .clk         (clk),
   .rst_n       (rst_n), 
   .uart_rx_done(uart_rx_done),
   .uart_rx_data(clock_data),
   .disp_data   (disp_data),
   .uart_tx     (uart_tx)
);

uart_rx_test uart_rx_test(
   .rst_n       (rst_n),
   .clk         (clk),
   .uart_rx     (uart_rx),
   .uart_rx_done(uart_rx_done),
   .clock_data  (clock_data)
);

hc595_driver hc595_driver(
   .clk   (clk),
   .rst_n (rst_n),
   .en    (en),      //数据使能信号
   .data  (data),    //要输入的数据
   .sh_cp (ch_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};

endmodule

原工程
提取码:d4mp

推荐阅读