FPGA——基于串口校时的数字钟设计
一、设计思路
- 器件使用:小梅哥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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!