差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
数字万年历设计 [2018/10/31 16:39] anran [实验原理] |
数字万年历设计 [2021/08/18 09:10] (当前版本) gongyusu |
||
---|---|---|---|
行 1: | 行 1: | ||
- | =====数字万年历设计===== | + | ##数字万年历设计 |
- | ----- | + | |
- | ====实验任务==== | + | ###实验任务 |
* 普通列表项目任务:基于 STEP-MAX10M08核心板 和 STEP BaseBoard V3.0底板 完成数字万年历设计并观察调试结果 | * 普通列表项目任务:基于 STEP-MAX10M08核心板 和 STEP BaseBoard V3.0底板 完成数字万年历设计并观察调试结果 | ||
行 12: | 行 12: | ||
* 普通列表项目解析:通过FPGA编程驱动实时时钟芯片DS1340Z,实现时间写入和读出的功能,驱动旋转编码器获取操作信息,设计一个控制模块根据编码器操作信息控制数字万年历的逻辑(包括时间调节,显示控制),最后驱动数码管显示数字万年历信息。 | * 普通列表项目解析:通过FPGA编程驱动实时时钟芯片DS1340Z,实现时间写入和读出的功能,驱动旋转编码器获取操作信息,设计一个控制模块根据编码器操作信息控制数字万年历的逻辑(包括时间调节,显示控制),最后驱动数码管显示数字万年历信息。 | ||
- | ====实验目的==== | + | ### 实验目的 |
前面的章节中我们学习了扫描式数码管模块和旋转编码器模块的工作原理及驱动方法,也对I2C总线协议及相关知识,本实验主要对I2C总线驱动方法加以练习,同时完成数字万年历时间调节和显示控制的逻辑,最终完成数字万年历总体设计。 | 前面的章节中我们学习了扫描式数码管模块和旋转编码器模块的工作原理及驱动方法,也对I2C总线协议及相关知识,本实验主要对I2C总线驱动方法加以练习,同时完成数字万年历时间调节和显示控制的逻辑,最终完成数字万年历总体设计。 | ||
行 20: | 行 20: | ||
* 完成数字万年历设计实现 | * 完成数字万年历设计实现 | ||
- | ====设计框图==== | + | ### 设计框图 |
根据前面的实验解析我们可以得知,该设计可以拆分成几个功能模块实现, | 根据前面的实验解析我们可以得知,该设计可以拆分成几个功能模块实现, | ||
行 30: | 行 30: | ||
{{:11-Top-Down层次设计.png?500|Top-Down层次设计}} {{:11-模块结构设计.png?500|模块结构设计}} | {{:11-Top-Down层次设计.png?500|Top-Down层次设计}} {{:11-模块结构设计.png?500|模块结构设计}} | ||
- | ====实验原理==== | ||
- | ===DS1340Z模块介绍=== | + | #### 实验原理 |
+ | |||
+ | ##### DS1340Z模块介绍 | ||
从DS1340Z芯片手册可以得到如下信息,DS1340Z芯片典型电路连接如下: | 从DS1340Z芯片手册可以得到如下信息,DS1340Z芯片典型电路连接如下: | ||
行 46: | 行 47: | ||
{{:11-芯片内部结构图.png?600|芯片内部结构图}} | {{:11-芯片内部结构图.png?600|芯片内部结构图}} | ||
- | ===DS1340Z模块连接=== | + | #### DS1340Z模块连接 |
STEP BaseBoard V3.0底板上的实时时钟芯片DS1340Z模块电路图如下(上拉电阻未显示): | STEP BaseBoard V3.0底板上的实时时钟芯片DS1340Z模块电路图如下(上拉电阻未显示): | ||
行 60: | 行 61: | ||
{{:11-DS1340Z晶振.png?500|DS1340Z晶振连接}} | {{:11-DS1340Z晶振.png?500|DS1340Z晶振连接}} | ||
- | ===DS1340Z驱动设计=== | + | #### DS1340Z驱动设计 |
前面实验中我们已经讲述学习过I2C总线驱动的设计,本实验可以上原来的基础上调整,首先来了解DS1340Z时序中的参数要点。 | 前面实验中我们已经讲述学习过I2C总线驱动的设计,本实验可以上原来的基础上调整,首先来了解DS1340Z时序中的参数要点。 | ||
行 110: | 行 111: | ||
endcase | endcase | ||
end | end | ||
- | <\code> | + | </code> |
芯片支持连续读寄存器操作(寄存器地址自加1),时序流程如下: | 芯片支持连续读寄存器操作(寄存器地址自加1),时序流程如下: | ||
行 148: | 行 149: | ||
endcase | endcase | ||
end | end | ||
- | <\code> | + | </code> |
上面两段程序就是对于DS1340Z芯片的两种操作,调时间和读时间,对于万年历来说因为有电池供电,实时时钟一直都处于工作状态,当给FPGA上电时只需要读时间即可,只有遇到时间不对的时候才需要调时间,所以DS1340Z驱动模块平时都在循环读取时间,所以如果将调时间和读时间的时序操作融合到同一个状态下时,对于cnt_main要加以控制,cnt_main初值为12,且运行轨迹在12~32之间,控制程序调整如下: | 上面两段程序就是对于DS1340Z芯片的两种操作,调时间和读时间,对于万年历来说因为有电池供电,实时时钟一直都处于工作状态,当给FPGA上电时只需要读时间即可,只有遇到时间不对的时候才需要调时间,所以DS1340Z驱动模块平时都在循环读取时间,所以如果将调时间和读时间的时序操作融合到同一个状态下时,对于cnt_main要加以控制,cnt_main初值为12,且运行轨迹在12~32之间,控制程序调整如下: | ||
行 157: | 行 158: | ||
else cnt_main <= 6'd12; //否则只执行时间读取操作 | else cnt_main <= 6'd12; //否则只执行时间读取操作 | ||
else cnt_main <= cnt_main + 1'b1; | else cnt_main <= cnt_main + 1'b1; | ||
- | <\code> | + | </code> |
上面set_flag为时间调整标志位,只有按动编码器在调时间模式时需要用到写时间数据的操作流程,可以根据按键脉冲置位set_flag并自锁,每次完成写入操作后再将set_flag复位。程序实现如下: | 上面set_flag为时间调整标志位,只有按动编码器在调时间模式时需要用到写时间数据的操作流程,可以根据按键脉冲置位set_flag并自锁,每次完成写入操作后再将set_flag复位。程序实现如下: | ||
行 169: | 行 170: | ||
else set_flag <= set_flag; | else set_flag <= set_flag; | ||
end | end | ||
- | <\code> | + | </code> |
模块端口如下: | 模块端口如下: | ||
行 188: | 行 189: | ||
output [7:0] rtc_year, rtc_mon, rtc_day, rtc_week //实时年份输出 | output [7:0] rtc_year, rtc_mon, rtc_day, rtc_week //实时年份输出 | ||
); | ); | ||
- | <\code> | + | </code> |
到这里就完成了万年历中DS1340Z模块的驱动设计,宏观上讲,该模块的功能可以这样描述: | 到这里就完成了万年历中DS1340Z模块的驱动设计,宏观上讲,该模块的功能可以这样描述: | ||
行 195: | 行 196: | ||
* 每次写操作调节时间的时间数据由其他模块提供 | * 每次写操作调节时间的时间数据由其他模块提供 | ||
- | ===万年历控制模块实现=== | + | #### 万年历控制模块实现 |
控制模块包含多个功能的设计:模式控制、调时控制、显示控制,可以细化成多个模块实现,本实验例程中就写在了一个模块下,我们会针对这三个功能分别讲解其实现方法及原理。 | 控制模块包含多个功能的设计:模式控制、调时控制、显示控制,可以细化成多个模块实现,本实验例程中就写在了一个模块下,我们会针对这三个功能分别讲解其实现方法及原理。 | ||
行 211: | 行 212: | ||
else state <= 3'd7; | else state <= 3'd7; | ||
else state <= state; | else state <= state; | ||
- | <\code> | + | </code> |
**<wrap hi>调时控制</wrap>** | **<wrap hi>调时控制</wrap>** | ||
行 232: | 行 233: | ||
end | end | ||
end | end | ||
- | <\code> | + | </code> |
调秒模式与其他调节模式操作一样,不同的是调节的规则不同,例如秒和分的调节范围为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。对秒钟数据进行调节,程序实现如下: | 调秒模式与其他调节模式操作一样,不同的是调节的规则不同,例如秒和分的调节范围为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。对秒钟数据进行调节,程序实现如下: | ||
行 249: | 行 250: | ||
end else adj_sec <= adj_sec; | end else adj_sec <= adj_sec; | ||
end | end | ||
- | <\code> | + | </code> |
**<wrap hi>显示控制</wrap>** | **<wrap hi>显示控制</wrap>** | ||
行 266: | 行 267: | ||
else if(R_pulse) disp_en <= 4'b0111; //顺时针转显示第二页,时分秒亮 | else if(R_pulse) disp_en <= 4'b0111; //顺时针转显示第二页,时分秒亮 | ||
else disp_en <= disp_en; | else disp_en <= disp_en; | ||
- | <\code> | + | </code> |
调秒模式下,小时和分钟数码管点亮,秒钟闪烁显示,转动编码器时秒钟强制显示,最后按动旋转编码器切到常态模式时,时分秒数码管都回复显示,程序实现如下: | 调秒模式下,小时和分钟数码管点亮,秒钟闪烁显示,转动编码器时秒钟强制显示,最后按动旋转编码器切到常态模式时,时分秒数码管都回复显示,程序实现如下: | ||
行 278: | 行 279: | ||
else disp_en[0] <= disp_en[0]; | else disp_en[0] <= disp_en[0]; | ||
end | end | ||
- | <\code> | + | </code> |
- | ===系统总体实现=== | + | #### 系统总体实现 |
前面分析了显示控制,主要对时间选项的点亮还是熄灭做控制,对应到数码管上就转化成数码管位的点亮和熄灭控制。另外还包含显示数据的控制,而这部分设计我们放到顶层模块中实现了,我们来分析一下。 | 前面分析了显示控制,主要对时间选项的点亮还是熄灭做控制,对应到数码管上就转化成数码管位的点亮和熄灭控制。另外还包含显示数据的控制,而这部分设计我们放到顶层模块中实现了,我们来分析一下。 | ||
行 291: | 行 292: | ||
wire [7:0] data_en = {{2{disp_en[3]}},{2{disp_en[2]}},{2{disp_en[1]}},{2{disp_en[0]}}}; //数码管位选控制 | 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]}; //数码管小数点显示控制 | 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]}; //数码管小数点显示控制 | ||
- | <\code> | + | </code> |
**<wrap hi>数码管内容控制</wrap>** | **<wrap hi>数码管内容控制</wrap>** | ||
行 297: | 行 298: | ||
万年历的显示分两页实现,我们以最右侧两个数码管显示内容为例,这两位数码管在第一页中显示周数据,在第二页中显示秒数据,那么我们怎么控制显示内容呢?分析,万年历8中模式, | 万年历的显示分两页实现,我们以最右侧两个数码管显示内容为例,这两位数码管在第一页中显示周数据,在第二页中显示秒数据,那么我们怎么控制显示内容呢?分析,万年历8中模式, | ||
- | 1.常态模式下,显示读取的实时时钟数据,具体显示周还是秒再次细化 | + | <wrap em>1</wrap>.常态模式下,显示读取的实时时钟数据,具体显示周还是秒再次细化 |
* disp_en等于4'b1111的时候,对应第一页,显示周数据 | * disp_en等于4'b1111的时候,对应第一页,显示周数据 | ||
* disp_en等于4'b0111的时候,对应第二页,显示秒数据 | * disp_en等于4'b0111的时候,对应第二页,显示秒数据 | ||
行 305: | 行 306: | ||
<code verilog> | <code verilog> | ||
wire [7:0] data_rtc0 = disp_en[3]? rtc_week:rtc_sec; //常态下数码管显示数据 | wire [7:0] data_rtc0 = disp_en[3]? rtc_week:rtc_sec; //常态下数码管显示数据 | ||
- | <\code> | + | </code> |
- | 2.调节模式下,显示写入的调节时钟数据,具体显示周还是秒再次细化 | + | <wrap em>2</wrap>.调节模式下,显示写入的调节时钟数据,具体显示周还是秒再次细化 |
* 调年、调月、调日、调周 状态下(state>=3),对应第一页,显示周数据 | * 调年、调月、调日、调周 状态下(state>=3),对应第一页,显示周数据 | ||
* 调时、调分、调秒 状态下(state<3),对应第二页,显示秒数据 | * 调时、调分、调秒 状态下(state<3),对应第二页,显示秒数据 | ||
行 315: | 行 316: | ||
<code verilog> | <code verilog> | ||
wire [7:0] data_adj0 = state[2]? adj_week:adj_sec; //调节状态下数码管显示数据 | wire [7:0] data_adj0 = state[2]? adj_week:adj_sec; //调节状态下数码管显示数据 | ||
- | <\code> | + | </code> |
- | 3.最后根据常态模式还是调节模式控制数码管显示实时时钟数据还是调节时钟数据 | + | <wrap em>3</wrap>.最后根据常态模式还是调节模式控制数码管显示实时时钟数据还是调节时钟数据 |
根据state选择显示实时时钟数据还是调节时钟数据,程序实现如下: | 根据state选择显示实时时钟数据还是调节时钟数据,程序实现如下: | ||
行 323: | 行 324: | ||
<code verilog> | <code verilog> | ||
assign {data_7,data_8} = state? data_adj0:data_rtc3; //根据状态选择显示常态数据还是调节状态数据 | assign {data_7,data_8} = state? data_adj0:data_rtc3; //根据状态选择显示常态数据还是调节状态数据 | ||
- | <\code> | + | </code> |
综合后的设计框图如下: | 综合后的设计框图如下: | ||
行 329: | 行 330: | ||
{{:11-RTL设计框图.png?800|RTL设计框图}} | {{:11-RTL设计框图.png?800|RTL设计框图}} | ||
- | ====实验步骤==== | + | #### 实验步骤 |
- 双击打开Quartus Prime工具软件; | - 双击打开Quartus Prime工具软件; | ||
- 新建工程:File → New Project Wizard(工程命名,工程目录选择,设备型号选择,EDA工具选择); | - 新建工程:File → New Project Wizard(工程命名,工程目录选择,设备型号选择,EDA工具选择); | ||
行 340: | 行 341: | ||
- | ====实验现象==== | + | ### 实验现象 |
+ | |||
+ | 将程序下载到FPGA中,按照设计要求的功能操作调节万年历的时间,观察数码管万年历显示,如图时间为18年6月27日,周三,19点15分14秒。 | ||
+ | |||
+ | {{:11-实验现象1.png?500|实验现象}} {{:11-实验现象2.png?500|实验现象}} |