0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何通过FPGA实现显示年、月、日、周、时、分、秒且能精准校时的数字时钟

电子工程师 来源:lp 2019-03-07 16:12 次阅读

数字时钟FPGA设计中较为常见,其主要包含时钟、分钟、秒钟,以及必备的校时功能,当然还可以根据需要加入年、月,日,周,星期或者闹钟设置等元素。

在这里,我们给小伙伴分享如何通过FPGA实现显示年、月、日、周、时、分、秒且能精准校时的数字时钟。

一、设计任务

通过FPGA编程驱动实时时钟芯片DS1340Z,实现时间写入和读出的功能,驱动旋转编码器获取操作信息,根据编码器操作信息控制数字时钟的逻辑(包括时间调节,显示控制),最后驱动数码管显示数字时钟信息。

二、设计准备

硬件:小脚丫FPGA核心板、时钟芯片DS1340Z、旋转编码器、数码

软件:Quartus Prime/Lattice Diamond

三、设计结构

四、设计原理

1.实时时钟芯片DS1340Z模块

实时时钟芯片DS1340Z模块电路图如下(上拉电阻未显示),模块电路中有电池座,电池电压范围为1.3V~5.5V,当安装电池后底板掉电不影响实时时钟芯片的运行,重新上电后读取实时时钟数据。另外,芯片内部集成了起振电阻电容,只需外置时钟晶振32.768KHz直接连接即可。

2.旋转编码器模块

机械增量式旋转编码器,旋转过程中提供周期性输出,不能定位绝对位置,只能用于感知运动方向和增量,其电路图如下,本设计所使用的旋转编码器为EC11系列的,支持按动开关,共有5个管脚:

1、2管脚支持按动开关,如同独立按键连接方式。

3、4、5管脚支持旋转编码,4脚为公共端,3、5管脚分别为旋转编码器的A、B相输出,如上图所示,4脚接地,3、5管脚则需接上拉电阻,同时为了降低输出脉冲信号的抖动干扰,增加了电容到地做硬件去抖。

五、设计驱动过程

1.DS1340Z驱动设计

DS1340Z支持I2C通信400KHz快速模式同时兼容100KHz的标准模式,还有两种模式下时序中的各种时间参数,本设计中,通过分频得到400KHz的时钟。

I2C时序基本单元(启动、停止、发送、接收、发应答、读应答)协议里统一的,所以所以基本单元状态的设计也是不需要调整的。

启动时序状态设计程序实现同智能接近系统设计实验。

发送单元和读应答单元合并,时序状态设计程序实现同智能接近系统设计实验。

接收单元和写应答单元合并,时序状态设计程序实现同智能接近系统设计实验。

停止时序状态设计程序实现同智能接近系统设计实验。

DS1340Z芯片有很多寄存器,用于存储实时时钟的时间信息,同时芯片支持连续读、写寄存器操作(寄存器地址自加1),其设计程序实现如下:

连续写寄存器:

MAIN:begin
if(cnt_main>=6'd11)//对MAIN中的子状态执行控制cnt_main
cnt_main<=6'd0;   //
elsecnt_main<=cnt_main+1'b1;  
case(cnt_main)
6'd0:beginstate<=START;end   //I2C通信时序中的START
6'd1:begindata_wr<=8'hd0;state<=WRITE;end     //写地址为8'hd0
6'd2:begindata_wr<=8'h00;state<=WRITE;end     //8'h00,起始寄存器
6'd3:begindata_wr<=adj_sec;state<=WRITE;end   //00寄存器地址,写秒
6'd4:begindata_wr<=adj_min;state<=WRITE;end   //01寄存器地址,写分
6'd5:begindata_wr<=adj_hour;state<=WRITE;end  //02寄存器地址,写时
6'd6:begindata_wr<=adj_week;state<=WRITE;end  //03寄存器地址,写周
6'd7:begindata_wr<=adj_day;state<=WRITE;end   //04寄存器地址,写日
6'd8:begindata_wr<=adj_mon;state<=WRITE;end   //05寄存器地址,写月
6'd9:begindata_wr<=adj_year;state<=WRITE;end  //06寄存器地址,写年
6'd10:begindata_wr<=8'h40;state<=WRITE;end   //07寄存器地址,8'h40
6'd11:beginstate<=STOP;end    //I2C通信时序中的STOP
default:state<=IDLE;//如果程序失控,进入IDLE自复位状态
endcase
end

连续读寄存器:

MAIN:begin
if(cnt_main>=6'd32)//对MAIN中的子状态执行控制cnt_main
cnt_main<=6'd12;         //否则只执行时间读取操作
elsecnt_main<=cnt_main+1'b1;  
case(cnt_main)
6'd12:beginstate<=START;end   //I2C通信时序中的START
6'd13:begindata_wr<=8'hd0;state<=WRITE;end//写地址为8'hd0
6'd14:begindata_wr<=8'h00;state<=WRITE;end//8'h00,寄存器初始地址
6'd15:beginstate<=START;end   //I2C通信时序中的START
6'd16:begindata_wr<=8'hd1;state<=WRITE;end//读地址为8'hd1
6'd17:beginack<=ACK;state<=READ;end    //读秒
6'd18:beginrtc_sec<=rtc_data_r;end
6'd19:beginack<=ACK;state<=READ;end    //读分
6'd20:beginrtc_min<=rtc_data_r;end
6'd21:beginack<=ACK;state<=READ;end    //读时
6'd22:beginrtc_hour<=rtc_data_r;end
6'd23:beginack<=ACK;state<=READ;end    //读周
6'd24:beginrtc_week<=rtc_data_r;end
6'd25:beginack<=ACK;state<=READ;end    //读日
6'd26:beginrtc_day<=rtc_data_r;end
6'd27:beginack<=ACK;state<=READ;end    //读月
6'd28:beginrtc_mon<=rtc_data_r;end
6'd29:beginack<=ACK;state<=READ;end    //读年
6'd30:beginrtc_year<=rtc_data_r;end
6'd31:beginack<=NACK;state<=READ;end   //控制
6'd32:beginstate<=STOP;end    //I2C通信时序中的STOP,读取完成标志
default:state<=IDLE;//如果程序失控,进入IDLE自复位状态
endcase
end

上面两段程序就是对于DS1340Z芯片的两种操作,调时间和读时间,对于时钟来说因为有电池供电,实时时钟一直都处于工作状态,当给FPGA上电时只需要读时间即可,只有遇到时间不对的时候才需要调时间,所以DS1340Z驱动模块平时都在循环读取时间,所以如果将调时间和读时间的时序操作融合到同一个状态下时,对于cnt_main要加以控制,cnt_main初值为12,且运行轨迹在12~32之间,控制程序调整如下:

if(cnt_main>=6'd32)//对MAIN中的子状态执行控制cnt_main
if(set_flag)cnt_main<=6'd0;   //当set_flag被置位时才会执行时间写入操作
elsecnt_main<=6'd12;         //否则只执行时间读取操作
elsecnt_main<=cnt_main+1'b1;  

上面set_flag为时间调整标志位,只有按动编码器在调时间模式时需要用到写时间数据的操作流程,可以根据按键脉冲置位set_flag并自锁,每次完成写入操作后再将set_flag复位。程序实现如下:

reg set_flag;
always@(posedgeclkornegedgerst_n)begin
if(!rst_n)set_flag<=1'b0;
elseif(cnt_main==5'd11)set_flag<=1'b0;  //完成写入时间操作复位set_flag
elseif(key_set)set_flag<=1'b1;          //按键脉冲控制set_flag置位
elseset_flag<=set_flag;
end

模块端口如下:

moduleDS1340Z_driver
(
inputclk,rst_n,//系统时钟和复位
inputkey_set, //按动脉冲输入
input[7:0]adj_hour,adj_min,adj_sec,//时分秒调整输入
input[7:0]adj_year,adj_mon,adj_day,adj_week,//年份调整输入
outputi2c_scl,//I2C总线SCL
inout i2c_sda,//I2C总线SDA
output[7:0]rtc_hour,rtc_min,rtc_sec,//实时时钟输出
output[7:0]rtc_year,rtc_mon,rtc_day,rtc_week //实时年份输出
);

到这里就完成了万年历中DS1340Z模块的驱动设计,宏观上讲,该模块的功能可以这样描述:

正常模式下循环读取时间信息,并把时间数据输出

由旋转编码器按动脉冲信号key_set触发进行一次写操作,用于调节时间

每次写操作调节时间的时间数据由其他模块提供

2.旋转编码器驱动设计

机械增量式旋转编码器的原理示意图,中间圆形齿轮连接到旋转编码器的公共端4管脚,A、B两个触点连接到旋转编码器的A、B相输出端3、5管脚,当进行旋转操作时,A、B触点会先后接触和错开圆形齿轮,从而导致A、B相输出信号产生相位不同的脉冲信号:

顺时针旋转时,A触点超前于B触点接触和错开圆形齿轮,A信号脉冲相位超前

逆时针旋转时,B触点超前于A触点接触和错开圆形齿轮,B信号脉冲相位超前

对A信号采样程序实现如下(对B信号一样):

regkey_a_r,key_a_r1,key_a_r2;
//消除亚稳态
always@(posedgeclk_500us)begin
key_a_r<=   key_a;
key_a_r1<=   key_a_r;
key_a_r2<=   key_a_r1;
end

然后简单去抖处理程序实现如下(对B信号一样):

regA_state;
//简单去抖动处理
always@(key_a_r1orkey_a_r2)begin
case({key_a_r1,key_a_r2})
2'b11: A_state<=1'b1;
2'b00: A_state<=1'b0;
default:A_state<=A_state;
endcase
end

检测A信号的边沿程序实现如下:

regA_state_r,A_state_r1;
//对A_state信号进行边沿检测
always@(posedgeclk)begin
A_state_r<=A_state;
A_state_r1<=A_state_r;
end
wire A_pos=(!A_state_r1)&&A_state_r;
wire A_neg=A_state_r1&&(!A_state_r);

最后根据A信号边沿与B信号的状态组合判定旋转的信息。

旋转脉冲输出程序实现如下:

//当A的上升沿伴随B的高电平或当A的下降沿伴随B的低电平为向左旋转
always@(posedgeclkornegedgerst_n)begin
if(!rst_n)L_pulse<=1'b0;
elseif((A_pos&&B_state)||(A_neg&&(!B_state)))L_pulse<=1'b1;
elseL_pulse<=1'b0;
end
//当A的上升沿伴随B的低电平或当A的下降沿伴随B的高电平为向右旋转
always@(posedgeclkornegedgerst_n)begin
if(!rst_n)R_pulse<=1'b0;
elseif((A_pos&&(!B_state))||(A_neg&&B_state))R_pulse<=1'b1;
elseR_pulse<=1'b0;
end

通过上面程序最终实现了左旋右旋的脉冲输出,脉冲的脉宽等于系统时钟的周期。

3.模式控制:

本设计要实现8个模式(常态、调年、调月、调日、调周、调时、调分、调秒),对8个状态编码,常态—0、调秒—1、调分—2、调时—3、调周—4、调日—5、调月—6、调年—7,通过按动旋转编码器切换,按照常识调时间从大到小调节,先调节年份最后调秒钟,所以我们这8个状态的状态机跳转顺序是固定的(0→7→6→5→4→3→2→1→0),依次循环跳转,程序实现如下:

//时钟运行状态控制
always@(posedgeclkornegedgerst_n)
if(!rst_n)state<=3'd0;
elseif(O_pulse)//按键脉冲控制时钟运行状态的跳变,
if(state)state<=state-3'd1;
elsestate<=3'd7;
elsestate<=state;

4.调时控制:

调时控制在不同的调节模式对不同时间进行调整,我们分别以常态模式和调秒模式为例进行分析。时间调节要以当时的时间为基础,常态模式下不需要调整任何时间,但是可以将实时时钟读出的时间数据赋给调节变量,这样等跳转到调节模式时对调节变量的控制就是以当时的时间为基础了,程序实现如下:

3'd0: //正常模式
begin
if(O_pulse)begin //在常态下按动编码器将当前实时时间赋值给调节寄存器
adj_sec<=rtc_sec;
adj_min<=rtc_min;
adj_hour<=rtc_hour;
adj_week<=rtc_week;
adj_day<=rtc_day;
adj_mon<=rtc_mon;
adj_year<=rtc_year;
end
end

调秒模式与其他调节模式操作一样,不同的是调节的规则不同,例如秒和分的调节范围为0~59,小时调节范围0~11或0~23,日期调节范围需要考虑年和月的值(1、3、5、7、8、10、12月范围1~31,4、6、9、11月范围1~30,2月平年范围1~28,2月闰年范围1~29),周调节范围1~7,月调节范围1~12,年调节范围0~99。对秒钟数据进行调节,程序实现如下:

3'd1: //调秒模式
begin
if(L_pulse)begin//逆时针转
if(adj_sec[3:0])adj_sec<=adj_sec-1'h1;
elseif(adj_sec[7:4])adj_sec<={adj_sec[7:4]-1'h1,4'h9};
elseadj_sec<=8'h59;
endelseif(R_pulse)begin//顺时针转
if(adj_sec[3:0]!=4'h9)adj_sec<=adj_sec+1'h1;
elseif(adj_sec[7:4]!=4'h5)adj_sec<={adj_sec[7:4]+1'h1,4'h0};
elseadj_sec<=8'h00;
endelseadj_sec<=adj_sec;
end

5.显示控制:

首先使用8位数码管分两页显示时钟数据,第一页显示年月日周,第二页显示时分秒。任何一项时间选项都由两位数码管显示,每页最多显示4个时间选项,我们可以使用4位的变量disp_en[3:0]控制4个时间选项的点亮或熄灭,disp_en[3]控制最左侧两个数码管,disp_en[0]控制最右侧两个数码管,我们分别以常态模式和调秒模式为例进行显示使能控制的分析。常态模式下,转动编码器控制显示页码,两个页码对应的显示控制,程序实现如下:

3'd0: //正常模式
if(L_pulse)disp_en<=4'b1111;    //逆时针转显示第一页,数码管全亮
elseif(R_pulse)disp_en<=4'b0111;//顺时针转显示第二页,时分秒亮
elsedisp_en<=disp_en;

调秒模式下,小时和分钟数码管点亮,秒钟闪烁显示,转动编码器时秒钟强制显示,最后按动旋转编码器切到常态模式时,时分秒数码管都回复显示,程序实现如下:

3'd1:begin //调秒模式
disp_en[3:1]<=3'b011;//时和分显示
if(L_pulse|R_pulse)disp_en[0]<=1'b1;//转动时强制显示
elseif(sec_pulse)disp_en[0]<=~disp_en[0];//秒钟闪烁显示
elseif(O_pulse)disp_en<=4'b0111;//返回常态时显示时分秒
elsedisp_en[0]<=disp_en[0];
end

数码管与时间选项是对应关系,每个选项对应两位数码管,程序实现如下:

wire[7:0]data_en=//数码管位选控制
{{2{disp_en[3]}},{2{disp_en[2]}},{2{disp_en[1]}},{2{disp_en[0]}}};
wire[7:0]dot_en=//数码管小数点显示控制
{1'b0,disp_en[3],1'b0,disp_en[2],1'b0,disp_en[1],1'b0,disp_en[0]};

时钟显示分两页实现,我们以最右侧两个数码管显示内容为例,这两位数码管在第一页中显示周数据,在第二页中显示秒数据,那么我们怎么控制显示内容呢?分析,万年历8中模式,

常态模式下,显示读取的实时时钟数据,具体显示周还是秒再次细化

Ø disp_en等于4'b1111的时候,对应第一页,显示周数据

Ø disp_en等于4'b0111的时候,对应第二页,显示秒数据

常态模式下,根据disp_en选择显示周数据还是秒数据,程序实现如下:

//常态下数码管显示数据
wire[7:0]data_rtc0=disp_en[3]?rtc_week:rtc_sec;

调节模式下,显示写入的调节时钟数据,具体显示周还是秒再次细化

Ø 调年、调月、调日、调周 状态下(state>=3),对应第一页,显示周数据

Ø 调时、调分、调秒 状态下(state<3),对应第二页,显示秒数据

调节模式下,根据state选择显示周数据还是秒数据,程序实现如下:

//调节状态下数码管显示数据
wire[7:0]data_adj0=state[2]?adj_week:adj_sec;

最后根据常态模式还是调节模式控制数码管显示实时时钟数据还是调节时钟数据

根据state选择显示实时时钟数据还是调节时钟数据,程序实现如下:

//根据状态选择显示常态数据还是调节状态数据
assign{data_7,data_8}=state?data_adj0:data_rtc3;

到这里为止,就完成了数字时钟的设计,当然在本设计中,可以通过简化只留下时分秒及调时功能,实现24小时计时,省去对于页的判断。同时对于旋转编码器有难度的,可以改为按键调时。有兴趣的小伙伴可以动手试试哦~

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1592

    文章

    21207

    浏览量

    592147
  • 编码器
    +关注

    关注

    41

    文章

    3338

    浏览量

    131252
  • 数字时钟
    +关注

    关注

    2

    文章

    144

    浏览量

    20104

原文标题:FPGA毕设系列 | 1.数字时钟

文章出处:【微信号:xiaojiaoyafpga,微信公众号:电子森林】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    基于vhdl的数字时钟

    时器,此时时计数器数值在原有基础上加1,其显示器将显示00、01、02、...、23、00。即当数字钟运行到23点5959时,当
    发表于 01-11 11:16

    12时制数字显示电子钟

    (1)时钟的“时”要求用两位显示(1~12);(2)时钟的“”、“”要求各用两位显示;(3)
    发表于 07-06 17:31

    [原创]基于FPGA历12864显示-生肖---时---星.....

    ` 本帖最后由 qq274822790 于 2013-1-17 17:01 编辑 [原创]基于FPGA历12864显示显示-生
    发表于 01-17 17:01

    时钟问题

    本人编写有一个 时钟时钟没有问题了,就是在年,
    发表于 05-20 23:17

    12时制数字显示电子钟,MCU为LPC2103/2106

    12时制数字显示电子钟 设计一个数字显示电子时钟,要求:(1)时钟的“时”要求用两位
    发表于 07-10 19:20

    如何将时间分开显示 毫秒

    如何将时间分开显示 毫秒
    发表于 08-06 09:17

    跪求 基于单片机的万历设计

    基于单片机的万历设计拟实现的基本功能:(1)可显示公历年、和时、
    发表于 01-07 10:17

    FPGA实现显示历电子时钟

    使用FPGA实现显示历电子时钟,分钟,小时,月份,年份,,有
    发表于 04-19 14:33

    EAD万历实训报告及Verilog HDL源码

    设计一个数字日历。 ⑵数字日历显示、时、
    发表于 07-03 05:38

    EAD万历实训报告及Verilog HDL源码

    设计一个数字日历。 ⑵数字日历显示、时、
    发表于 07-09 04:35

    89c51时钟

    keil与protues的联合仿真,使用的89c51和DS1302芯片,通过编程实现日历/时钟的计时显示功能。具体要求是,开机时DS1302初始化为:19
    发表于 06-14 17:54

    如何去实现一种基于51单片机的电子时钟历设计

    、时、进行计时,还具有闰年补偿等多种功能。温度采集选用DS18B20芯片,万历采用直观的数字显示,数据
    发表于 11-10 08:12

    基于51单片机的DS1302实时时钟程序分享

    DS1302概述DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对、时、
    发表于 11-19 09:10

    基于FPGA设计实现一个多功能数字钟相关资料分享

    1、基于FPGA设计实现一个多功能数字钟在FPGA中设计实现一个多功能数字钟,具备以下功能:准确
    发表于 07-08 17:26

    基于FPGA数字时钟设计

    基于FPGA数字时钟设计,可实现闹钟的功能,可校时
    发表于 06-23 17:15 64次下载