题目要求:
利用有限状态机实现实现一个具有启动、停止、清零功能的秒表,显示格式:分:秒:十分秒。启动、停止、清零由一个按键控制,按键按下时,功能按启动、停止、清零顺序循环。
思路分析:
参考知乎上的这篇文章FPGA | Finite State Machine 有限状态机,对比两种状态机:
1.Mealy型状态机
2.Moore型状态机:
从这两张图上看,这两种状态机的唯一区别在于决定输出的是什么,在本实验中,最终的输出是数码管上的显示结果,题目中说“启动、停止、清零由一个按键控制,按键按下时,功能按启动、停止、清零顺序循环”也就是说按键的次数会影响到数码管的显示,因此本实验采用Mealy型状态机。
画出秒表的状态图:
S1: 0状态,所有数码管显示为0
S2:计时并显示
S3:停止计时,显示不变动
状态之间的“0”代表按键输入,本项目所用的开发板在不按动的情况下产生高电位,按动了就产生低电位,由于开发板的按键使用了施密特触发电路,因此在代码中就不做消抖了。
我在硬件设计代码里使用枚举类型来表示这3个状态,综合器在综合时,会自动对它们四个编码,将状态表示为二进制码的形式。
本项目用3个process,1s = 1000000000 ns
刚刚编译的时候出现了一条警告信息:
Warning (10631): VHDL Process Statement warning at Second.vhd(46): inferring latch(es) for signal or variable "current_state", which holds its previous value in one or more paths through the process
参考了http://www.itdaan.com/blog/2012/05/28/171e7e47cea3348ae9b125dc8154ecb3.html
看到的说法是:
解释:信号被综合成了latch,锁存器的EN和数据输入端口存在一个竞争的问题
好了,我的代码如下:
library ieee ;
use ieee.std_logic_1164.all ;
use ieee.std_logic_unsigned.all ;
use ieee.std_logic_arith.all ;
entity Second is
port (
clk : in std_logic ; -- the signal from 50MHZ clock
key : in std_logic ; -- the signal from key3 on development board
hex7 : out std_logic_vector(0 to 6) ;
hex6 : out std_logic_vector(0 to 6) ;
hex5 : out std_logic_vector(0 to 6) ;
hex4 : out std_logic_vector(0 to 6) ;
hex3 : out std_logic_vector(0 to 6) ;
hex2 : out std_logic_vector(0 to 6) ;
hex1 : out std_logic_vector(0 to 6) ;
hex0 : out std_logic_vector(0 to 6) ; -- the result to output
separate1 : out std_logic ;
separate2 : out std_logic ;
separate3 : out std_logic ;
separate4 : out std_logic ;
separate5 : out std_logic -- to separate hour, minute, second.
) ;
end Second ;
architecture Timer of Second is
constant matrix_num : integer := 9 ;
constant MAX_INT : integer := 2147483647 ;
TYPE Number is array (0 to matrix_num) of std_logic_vector(0 to 6);
signal initial : Number := (('0', '0', '0', '0', '0', '0', '1'), -- 0
('1', '0', '0', '1', '1', '1', '1'), -- 1
('0', '0', '1', '0', '0', '1', '0'), -- 2
('0', '0', '0', '0', '1', '1', '0'), -- 3
('1', '0', '0', '1', '1', '0', '0'), -- 4
('0', '1', '0', '0', '1', '0', '0'), -- 5
('0', '1', '0', '0', '0', '0', '0'), -- 6
('0', '0', '0', '1', '1', '1', '1'), -- 7
('0', '0', '0', '0', '0', '0', '0'), -- 8
('0', '0', '0', '0', '1', '0', '0') -- 9
) ;
TYPE state_type is (s1, s2, s3) ; -- how many states does the circuit have?
signal current_state : state_type ;
begin
process(key) -- to decide to change
variable num : integer := 0 ; -- how many times does user press the key?
begin
if falling_edge(key) then
num := (num + 1) MOD 3 ;
end if ;
if (num = 0) then
current_state <= s1 ;
elsif (num = 1) then
current_state <= s2 ;
elsif (num = 2) then
current_state <= s3 ;
end if ;
end process ;
process(clk, current_state, initial)
variable jump : integer ; -- store the times the clock rising
variable tenth : integer ;
variable i : integer ;
begin
if (current_state = s1) then
jump := 0 ;
tenth := 0 ;
elsif (current_state = s2) then
if rising_edge(clk) then
jump := jump + 1 ;
if (jump = 5000000) then
tenth := (tenth + 1) MOD MAX_INT ;
jump := 0 ;
end if ;
end if ;
end if ;
hex7 <= initial((tenth/36000)/10) ;
hex6 <= initial((tenth/36000) MOD 10) ;
hex5 <= initial(((tenth MOD 36000) / 600) / 10) ;
hex4 <= initial(((tenth MOD 36000) / 600) MOD 10) ;
hex3 <= initial((((tenth MOD 36000) MOD 600) / 10) / 10) ;
hex2 <= initial((((tenth MOD 36000) MOD 600) / 10) MOD 10) ;
hex1 <= initial(((tenth MOD 36000) MOD 600) mod 10);
hex0 <= ('1', '1', '1', '1', '1', '1', '1') ;
separate1 <= '1' ;
separate2 <= '1' ;
separate3 <= '1' ;
separate4 <= '1' ;
separate5 <= '1' ;
end process ;
end Timer ;