当前位置:   article > 正文

VHDL课程设计:基于FPGA开发板的时钟-日历-秒表-闹钟(按键控制+红外遥控),获课程最高分,附带源码。_vhdl 1s时钟模块

vhdl 1s时钟模块

源码:

【免费】基于VHDL的课程设计-源码资源-CSDN文库

一.实验目的:

电子系统设计课程标志性内容的设计理解和综合运用,鼓励独立性设计和功能扩展的创新实践。

二.功能要求:

2.1基本性要求

  1. 设计一个万年历,可以显示年、月、日、星期、小时、分钟和秒。
  2. 支持采用按键调整时间。
  3. 具备闰年计算和现实。
  4. 具备闹钟提醒功能,以蜂鸣器作为闹钟的提醒。

2.2发挥性要求

  1. 具备运动秒表计数功能。
  2. 年、月、日、星期相互切换,即设置年、月、日可以自动切换为星期。
  3. 具备12和24小时的切换。

三.方案考虑:

3.1硬件方案

(1)采用6位共阳极数码管(SEGLED)来显示日期或者时间。

(2)显示器的驱动可采用“动态扫描驱动”,利用视觉残留效果实现连贯显示。

(3)键盘的按键数目有限,应采用“一键多用”的构思和设计,减少按键的数目。

(4)整体设计上考虑系统的结构简单﹑操作简便﹑布局美观﹑成本低廉。

(5)可以考虑使用小数点或其他提示标志表示闹钟设置状态。

3.2软件方案

  1. “时钟”基准时间由单片机内部的晶振提供,通常来说基准时间越短,越有利于提高时钟的运行精确度。
  2. 用一个计数器对时钟上升沿的次数进行计数,可实现“秒”定时,同理可以进行“分”﹑“时”定时,以及“日”﹑“月”﹑“年”定时。采用不同计数值的计数器,可以得到不同的周期方波,应用于时钟、按键消抖、动态显示等不同的功能中。
  3. LED 数码管显示器采用“动态扫描驱动”时要注意的是:驱动信号的维持时间必须大于“起辉时间”,而驱动信号的间歇时间必须小于“余辉时间”,但驱动电流大小受硬件电路能力和LED数码管极限功耗的制约。
  4. 动态扫描显示方式在更新显示内容时,因LED数码管余辉的存在可能会造成显示字符的模糊,新内容写入显示器之前须将所有的LED数码管熄灭。
  5. 关于自动识别“月大﹑月小”和“平年﹑润年”问题的考虑
  1. 月大和月小

2月另外计算;

4月﹑6月﹑9月﹑11 月为月小30天,其余为月大31天。

  1. 平年和润年(普通年能整除4且不能整除100的为闰年,能整除400的是闰年)

平年的2月为28天;

润年的2月为29天。

3.3增加外设

为了丰富产品功能、进一步锻炼自身的工程实践能力,本项目在课程要求的基础上,合理增加了两项外设:扬声器模块红外遥控模块

(1)扬声器模块本身无源,受变频方波驱动,用来播放闹钟音乐。

(2)红外遥控模块用于闹钟的远程控制,与按键的功能一致。本项目所使用遥控器含17个按键,方便进一步扩展功能。

四.实际方案:

4.1.实现的功能(使用操作指引)

图4.1实验使用的按键电路

REST为复位按键,按下时低电平复位。KEY0~KEY3为普通按键输入,外接上拉电阻,未按下时按下端口输出高电平,按下时输出低电平。按键作为最简单的输入设备,适合在需要给系统输入控制信号的场合使用。

4.1.1模块切换

按下key3,可实现功能模块的顺序切换,切换顺序如下:

……—>24小时制时钟—>12小时制时钟—>日历显示—>

闹钟设置—>电子秒表—>星期数显示—>24小时制时钟—>……

4.1.2时钟模式

a)时钟显示:如图4.2所示:

图4.2 时钟显示

系统复位后,能够按照设定的初始时间进行走时,并用8段数码管显示。时、分、秒之间以小数点分隔

b)时钟校准:

按key0,秒钟增加1。

按key1,分钟增加1。

按key2,时钟增加1。

按key3,切换成下一模式。

4.1.3日历模式

a)日历显示

日历以“XX.XX.XX”的形式显示,如“23.07.14”,表示2023年7月14日。年、月、日以小数点相隔。

能够实现秒-分-时-日-月-年的进位。能自动识别大、小月份和闰年、平年,采取不同的“天->月”进位模式。

考虑到数码管只有六位,且实际用途是记录当下的日期,因此将年分的可设定范围设置为2000年-2099年。

b)日历校准

  按key0,日增加1。

  按key1,月增加1。

  按key2,年增加1。

  按key3,切换成下一功能。

4.1.4闹钟模式

a)LED显示

闹钟模块的LED以“XX.XX.XX”的形式显示,如“00.12.OF”,表示闹钟关闭,响铃时间设定为00时12分;“20.32.ON”,表示闹钟开启,响铃时间设定为20时32分。

b)闹钟设置

  按key0,ON和OF切换。

  按key1,分增加1。

  按key2,时增加1。

  按key3,切换成下一功能。

c)响铃

  当时钟(24小时制)的时、分与闹钟设定响铃时间均一致时,蜂鸣器发出警报,持续约10s。同时,开发板的D6口输出变频方波,驱动扬声器(外设)播放一段音乐。

4.1.5秒表模式

a)LED显示

秒表模块的LED以“XX.XX.XX”的形式显示,如“00.12.13”,表示0分钟12秒钟13*10毫秒。

b)按键设置

  按key0,秒表归零。

  按key1,秒表暂停/启动。

  按key3,切换成下一模式。

4.1.6星期模式

a)LED显示

秒表模块的LED以“XX.XX.-X”的形式显示,如“12.11.-4”,表示特定年(依据日历模块)的12月11号对应星期四。

b)按键设置

  按key0,月加1。

  按key1,日加1。

  按key3,切换成下一功能。

注:该模式的月、日和日历模式是同步调节的,始终保持一致。

4.1.7红外遥控

图4.3 红外遥控

启用了1、2、3、4四个按钮。分别对应按键的key0,key1,key2,key3。

4.2.硬件方案

4.2.1数码管

采用6位共阳极数码管来显示日期或者时间。显示器的驱动采用“动态扫描驱动”,各位数码管以极高的频率分时点亮,依靠人体视觉的暂留效果,实现了多位同时显示。

图4.4 数码管

数字编码表: 

    数码管:8段

led1: out std_logic_vector(7 downto 0);低电平有效

    选通位:6位

s   : out std_logic_vector(5 downto 0);低电平有效

4.2.2蜂鸣器

采用有源蜂鸣器,本身自带振荡电路,采用直流供电即可发出固定频率的蜂鸣。

图4.5 独立按键模块

4.2.3时钟晶振电路

图4.6 时钟晶振电路50MHz

如图4.6所示为系统的时钟晶振电路,振荡频率为50MHz,为系统提供时间基准。对上升沿计数,50000000次为一秒。

4.2.4红外接头

图4.7 红外接头

4.3.软件方案

4.9 实体列表

4.3.1  分频器模块

对应实体:  clkdiv

图4.10 分频器RTL图

原理:

已知系统时钟的固有频率为50Mhz,取以下四种计数值进行分频,得到周期为1s、1ms、140ms、0.01s的数字方波。其中,周期为1s的波用于时钟模块;周期为1s的波用于led动态显示、按键消抖等模块上;周期为140ms的波用于蜂鸣器模块和音乐播放模块;周期为0.01s的波用于秒表模块。

  1. signal timecnt:integer range  0 to 50000000;--1s
  2.  signal timecnt2:integer range  0 to 50000;--1ms
  3.  signal timecnt3:integer range  0 to 7000000;--140ms
  4.  signal timecnt4:integer range  0 to 500000;--0.01s

4.3.2   LED 数码管动态显示

对应实体:ledout

原理:

将所有的数码管的段选线并接在一起,用IO接口控制,6个数码管轮流显示相应的信息,一遍显示完毕,隔一段时间,又这样循环显示。从算法的角度,每个数码管隔一段时间才显示一次,但是由于人的视觉暂留效应,只要隔离时间足够短,循环的周期足够长,每秒达到24次以上,看起来数码管就一直稳定显示了,这就是动态显示原理。

动态显示时候需要注意闪烁的频率。如果每秒显示的次数少,频率低,则显示的信息是闪烁的,这时候应该增加显示频率。如果每个数码管在每秒钟显示的总时间太短,则显示的亮度低,显示的信息不清楚,这时候应该增加显示的时间。

代码思路:

1.设计一个6进制计数器,对应数码管的6个选通位。以1ms周期信号的上升沿进行控制:每次检测到上升沿,则计数值加一(计数值满则清零),同时更改数码管的选通位,如此循环。

2.应用case语句,在每个上升沿对输入cnt进行一次检测,依据输入设制八段数码管的选通,以6位数码管的第1位为例,具体代码如下。

  1. case cnt_one is
  2.           
  3.            when"0000"=>led1<="11000000";
  4.            when"0001"=>led1<="11111001";
  5.            when"0010"=>led1<="10100100";
  6.            when"0011"=>led1<="10110000";
  7.            when"0100"=>led1<="10011001";
  8.            when"0101"=>led1<="10010010";
  9.            when"0110"=>led1<="10000010";
  10.           
  11.            when"0111"=>led1<="11111000";
  12.           
  13.            when"1000"=>led1<="10000000";
  14.           
  15.            when"1001"=>led1<="10010000";
  16.           
  17.            when"1010"=>led1<="10001110";
  18.           
  19.            when"1011"=>led1<="11001000";
  20.           
  21.            when"1100"=>led1<="10111111";
  22.           
  23.            when others=>null;
  24.            end case;

4.3.3  按键的软件消抖

对应实体:key_xiaodou(板载按键)、red_key(红外遥控)

利用了计数器对按键进行消抖。定义一个变量count。

以周期1ms的上升沿检测,检测出键闭合,若count<10,则count执行一次累加。当count=9时,输出由0置1,即输出一次上升沿,表示“键按下一次”;当count=10时,若检测到键闭合,count不变,输出保持0。当检测到按键释放后,count由10开始递减,到0后保持不变。

对于红外信号的“消抖”,也采用了同样的原理。但由于后续逻辑控制的需要,在count由2减少至1时,也输出一个上升沿,表示“键释放一次”。即用户按下按键和松开按键,均输出一次上升沿。

图4.11 按键RTL图

按键检测程序:

   

  1. if clk'event and clk = '1' then
  2.         if key_in = '0' then
  3.           if count < 10   
  4.           then count := count + 1;
  5.           else count := count; 
  6.            end if;
  7.             if count = 9   
  8.             then key_out <= '1';   
  9.             else key_out <= '0';   
  10.             end if;
  11.         else
  12.           if count > 0   
  13.           then count := count - 1;
  14.           else count := count; 
  15.             end if;
  16.         end if;
  17.     end if;

4.3.4  各模式的计数模块

相关实体:

miao_biao     --秒表模块

counter_60    --通用计数模块

counter_12h   --时计数模块(12小时制)

counter_day   --日计数模块

counter_month --月计数模块

counter_week  --星期计算模块

a)通用计数模块 counter_60

counter_60是本次设计中采用最多的计数模块,通用于时钟模式、日历模式、闹钟模式,其RTL图如图4.12所示:

图4.12 计数器RTL图

端口介绍:

clk:上一级进位信号/基础时钟信号的输入,上升沿触发。

例如:分钟计时模块的clk是秒钟计时模块的进位输出。

key_add:直接进位指令,上升沿触发。作用是让c_out直接输出一个进位信号,相当于是下一级计数模块的“加一”指令。

例如:秒钟计时模块的key_add每触发一次,分钟计时就加一。

reset:复位指令。

top_ten/top_one:计数模块的计数上限,决定了计数模块的计数逻辑和进位逻辑。

c_out:进位信号,传入下一级计数器。

cnt_ten/cnt_one:三位二进制信号,输出当前计数值的十位/个位。

模块的具体代码如下(结构体部分):

  1. ARCHITECTURE behave OF counter_60 IS
  2.    SIGNAL cnt_ten_buf : STD_LOGIC_VECTOR(3 DOWNTO 0) := "0000";--计数值十位
  3.    SIGNAL cnt_one_buf : STD_LOGIC_VECTOR(3 DOWNTO 0) := "0000";--计数值个位
  4.    signal count_en : std_logic;
  5. BEGIN
  6.    c_out <= key_add OR count_en;
  7.    PROCESS (clk,reset)
  8.    BEGIN
  9. IF(reset='0')THEN--异步复位(zhiwei)
  10.         cnt_ten_buf <= "0000";
  11.         cnt_one_buf <= "0000";
  12. ELSIF (clk'EVENT AND clk = '1') THEN--正常计时
  13.    
  14.        IF (cnt_ten_buf = top_ten AND cnt_one_buf = top_one) THEN--59后变为00
  15.        cnt_ten_buf <= "0000";
  16.        cnt_one_buf <= "0000";
  17.        count_en <= '1'; 
  18.        ELSIF (cnt_ten_buf < top_ten and cnt_one_buf = "1001") THEN--个位为9,十位加1
  19.        cnt_ten_buf <= cnt_ten_buf + "0001";
  20.        cnt_one_buf <= "0000";
  21.        count_en <= '0';
  22.       
  23.        ELSE--个位累加
  24.        cnt_ten_buf <= cnt_ten_buf;
  25.        cnt_one_buf <= cnt_one_buf + "0001";
  26.        count_en <= '0';
  27.        END IF;
  28. END IF;
  29. END PROCESS;
  30.    cnt_ten <= cnt_ten_buf;--to计数值十位
  31.    cnt_one <= cnt_one_buf;--to计数值个位
  32. END behave;

b)其它计数模块:
由于12小时制、日、月、星期的计数逻辑和进位逻辑比较特殊,所以无法用通用计数器实现,需要使用单独的计数器模块。

12小时制:特点在于小时位的计数逻辑存在“am”和“pm”的差别,am的计数范围是00-12,而pm则是01-11。因此,要定义一个标志信号:SIGNAL am_pm :std_logic。am_pm的逻辑值在0和1间切换,am_pm=0时对应am,反之对应pm。

日:特点在于下限为01且上限按月和年变化,需要增加输入端口,根据输入的月份和年份决定计数逻辑。

月:特点在于下限为01而非00。

星期:特点在于星期的复杂计算。“counter_week”严格来说不是计数模块,而是进行了一套复杂的数学运算和逻辑运算。

运算代码如下:

  1. if (m_buf=13 or m_buf=14) and y_buf=0 then
  2.  week_buf<=((y_buf+99)+(y_buf+99)/4-2*y_hun_buf+2*m_buf+3*(m_buf+1)/5+d_buf+7)mod 7;        
  3.           
  4. elsif m_buf=13 or m_buf=14 then
  5.  week_buf<=((y_buf-1)+(y_buf-1)/4-2*y_hun_buf+2*m_buf+3*(m_buf+1)/5+d_buf+6)mod 7;
  6.           
  7. else
  8.  week_buf<=( y_buf+y_buf/4-2*y_hun_buf+2*m_buf+3*(m_buf+1)/5+d_buf+6) mod 7;
  9.  end if;

该运算逻辑的数学基础是蔡勒公式:

w=(y+y/4-2*h+2*m+3*(m+1)/5+d+6) mod 7;

其中,w表示星期,y表示年份的十位和个位,h表示年份的千位和百位,m表示月份,d表示日。w=0时,对应星期日。其余1-6对应星期一到星期六。当月份为1月或者2月时,应更改为前一年的13月和14月。

y的计算公式为:

y=a*10+b ,其中a为年份十位,b为年份个位。例如17年表示17=1*10+7。

对于一般的情况,前一年的年份为y-1。而对于2000年,则前一年的年份为y+99,且h改为19。

c)秒表模块

秒表模式与其它计数器相关模式不同。其它计数器相关模式由多个计数器实例级联而成,而秒表采用了单一的实例实现,集成度较高。RTL图如图4.13所示:

图4.13 秒表RTL图

clear:计时器清空信号。

clk  :时钟信号,信号周期0.01s,上升沿触发。

reset:复位信号。

stop :计数开启/停止信号,电平触发。由于是电平触发,因此消抖模块的输出信号需要先经过一个T触发器,才能控制秒表模块。RTL图如图4.13所示:

图4.13 T触发器与秒表RTL图

运用if-elsif-else语句,确定了reset,clear,stop,clk这四种信号指令的优先级。

其中reset和clear的优先级最高,其次是stop,最后是clk。

   百分之一秒级计数范围为00-99;秒级计数范围为00-59;分级计数范围为00-59。

4.3.5  按键总控模块

RTL图:

图4.14按键总控模块RTL图

端口解释:

输入:

o_key0:按键0信号检测,上升沿触发。

o_key1: 按键1信号检测,电平触发。

o_key2: 按键2信号检测,电平触发。

o_key3: 按键3信号检测,电平触发。

输出:

key0_sec, key1_min, key2_hour:时钟的秒、分、时“加一”指令。

key0_day, key1_month, key2_year :日历的日、月、年“加一”指令。

model(2 downto 0)

模式选择信号,从“000”到“100”分别对应系统“24/12小时制时钟、日历、闹钟、秒表、星期”这五种模式,对于模式切换和按键复用起到关键的作用。

am_add_m, am_add_h, key_swith :闹钟的分加一、时加一和开/关指令。

key_clear,key_stop :运动秒表的清零和启动/停止指令。

程序设计思路:

a)由o_key3 信号控制模式的切换:定义一个范围为000-100的中间变量rest_count,对于的o_key3每个上升沿,rest_count计数一次。

b)由o_key0、o_key1、o_key2进行具体模式的控制:运用if-else语句,同时考虑按键输入o_key0、o_key1、o_key2和模式控制变量res_count,输出相应的控制信号。

示例:

  1. if (o_key1 = '1' and (reset_count = "000" or reset_count = "001" )) then  key1_min<= '1';
  2. else  key1_min<= '0';
  3. end if;

4.3.6  闹钟控制模块

相关实体:

alarm_clock:内含闹钟的开关逻辑。输出端的flag的表示闹钟的开关情况,接入alarm_ring的使能端口。model_l&model_r有两种取值,分别对应了数码管的“0F”和“0N”。当swith信号触发时,flag翻转一次,model&model_r相应变化。其RTL图如图4.15所示:

图4.15 alarm_clockRTL图

alarm_ring:若使能端口显示闹钟处于“开”状态,则比对时钟的时、分与闹钟设置的时、分,若相同且时钟的秒处于0-10之间,则输出高电平。

MusicBox:通用型音乐播放模块。

如下图所示,模块内部是一个计数器,clk决定计数频率,selection决定了计数器的上限。计数器每达上限,则final翻转,形成了方波。因此改变selection可以更改输出方波final的频率。对于V2,其输入selection为一定值“1011”,控制final输出一个直流电平,该直流电平可驱动蜂鸣器工作。而对于U11,其输入selection范围为“0000”到“1010”,对应着11种不同频率的输出,可以驱动扬声器发出不同频率的声音。其RTL图如图4.16所示:

图4.16 音乐播放模块RTL图

voice_dlay和music_1:

voice_dlay的RTL图如图4.17所示,music_1的端口结构与之相同。

图4.16 voice_dlayRTL图

此两模块受周期为140ms的时钟信号控制,输出端连接MusicBox的selection输入。使能信号en来自alarm_ring,当输入为1时,时钟上升沿触发输出信号按程序设定的规律进行变化,从而控制MusicBox输出既定规律的方波,进而驱动扬声器发出特点的音乐。

4.3.7  红外信号的解码

参考文章:

红外遥控解码设计与验证VHDL_用vhdl红外发射接收电路代码-CSDN博客

相关实体:

4.17 红外解码实体列表

红外遥控的通信原理[1]:

红外发射部分电路包括矩阵键盘,红外发光二极管,编码以及调制电路,这些都已经集成在红外遥控器上(遥控器示意图如下图所示)。接收部分包括光敏二极管,解调以及解码电路。在编写程序时,我们仅需要对其输出的信号进行解码操作。

NEC协议

HT6221为Holtek公司生产的一款基于NEC红外通信协议的遥控编码芯片,采用Pulse Position Modulation(PPM)进行编码。HT6221芯片的红外遥控发送一次数据的数据帧定义如下图所示:一帧数据:帧头,16位地址码,8位数据码,8位数据反码以及1bit结束位(可忽略)组成。

https://img-blog.csdnimg.cn/20201222203452771.PNG#pic_center

图4.18 数据帧定义

数据发送:

引导码由9ms高电平和4.5ms的低电平构成,代码一个数据帧的帧头;

地址码由16位地址构成,低8位在前,高8位在后(对于一个红外遥控器,地址为固定值)。所以NEC协议理论上能最多支持65536个不同的用户。

数据码由数据原码和数据反码构成(可以通过比较这两个8位数据检测数据接收是否正确)。所以理论上支持256种指令。

数据发送0/1:

数据发送1: 560μs高电平+1.69ms低电平

数据发送0: 560μs高电平+560μs低电平

https://img-blog.csdnimg.cn/20201222204559911.PNG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NvbW51c196,size_16,color_FFFFFF,t_70#pic_center

图4.19数据编码发送波形

数据接收:

对于数据接收端,接收到信号之后再输出的波形和输入端发送的波形反相。因此对于FPGA中接收端输出的波形为: 9ms低+4.5ms高 + 0.56ms 低+ 1.69ms高(1)/ 0.56ms高(0)。本模块就是检测此波形然后输出数据(由于红外传感器的品质不同,因此本模块的检测时间为一个区间范围)

图4.20 数据接收波形

本模块只进行短按时数据波形设计。

状态机:

idle: 空闲态,当检测下降沿开始计数,转到wait9ms_low状态

wait9ms_low: 等待9ms低电平计数,当上升沿没到来的时候,计数器一直计数。当检测到上升沿时,如果9ms计时完成,将计数器使能无效(计数器清零),跳到wait4_5ms_high状态,否则说明接受错误,回到idle状态。

wait4_5ms_high: 等待4.5ms高电平计数。如果下降沿未到来,那么计数使能,一直计数等待下降沿的到来。当检测到下降沿,如果T4_5ms_ok=1,说明4.5ms计数完成,将计数器使能无效,跳转到data_get_56ms_low状态;如果检测到下降沿但是4.5ms计数未完成,说明接受错误,回到idle态。

data_get_56ms_low: 等待0.56ms低电平计数完成。 当未检测到上升沿,计数使能,一直计数,等待上升沿。 当检测到上升沿,并且0.56ms计时完成,将计数器使能无效,跳到data_get_01状态。;如果0.56ms计时没有完成,跳到idle态。

data_get_01: 等到1.69 ms或者0.56 ms高电平计数完成。 当没检测到下降沿,一直计时。检测到下降沿之后,如果还没有接收到32位数据(地址+数据+反码),并且0.56ms/1.69ms 高电平接收完成,回到data_get_56ms_low状态继续检测下一个数据。 当检测到下降沿,但是0.56/1.69ms 计数都未完成,或者当32位数据接收完成,回到idle态。

https://img-blog.csdnimg.cn/20201222221607635.PNG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NvbW51c196,size_16,color_FFFFFF,t_70#pic_center

图4.21H6221红外遥控器接收状态图

当timeout=0的时候才能进入状态机,当timeout=1意味着计时器超出10ms了,此时将state置为idle并且计数器使能无效。

一帧数据传输完成标志data_get_done:

由于每个传输的数据0/1都需要在下降沿才能判断是0/1,所以对于最后一位数据,在下降沿拉低后0.56ms之后信号又会重新拉高,然后等待下一次9ms的拉低(红外接收器输出波形iIR默认高电平)。

32个数据有32个下降沿,33个上升沿(最后的拉高),因此当检测到第33个上升沿的时候,表明数据接收完成。在最后data_get_01状态检测到下降沿之后将数据赋给寄存器,最后一个上升沿的时候将寄存器的数据输出。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/659958
推荐阅读
相关标签
  

闽ICP备14008679号