프로젝트 목적
- VHDL을 이용하여 생활에서 많이 사용하는 디지털 시계를 구현해 봄으로써 Clock의 활용과 7 Segment의 제어를 익히고 디지털 시계의 구현을 통해 VHDL의 응용 능력을 기른다.
- 입력 장치인 Push Button Switch, 출력 장치인 FND(7 Segment)의 특성을 이해한다.
프로젝트 개요
- 본 프로젝트에서는 7 Segment LED를 이용한 디지털 시계를 VHDL을 이용하여 구현하고자 한다.
- 프로젝트에서 사용하는 7 Segment LED는 6개의 Segment LED가 Dynamic 구동방식으로 동작한다.
- 디지털 시계는 6개의 7 Segment LED에 시, 분, 초 각각 2자리씩 표현한다. 그리고 시계를 리셋 시키는 기능과 시간을 맞추는 작업은 FPGA 보드에 있는 스위치를 이용한다.
- 본 프로젝트에서 사용하는 FPGA 보드의 기본 주파수는 33Mhz를 사용한다. 디지털 시계의 1초를 구하기 위해서는 33Mhz의 Main Clock 주파수를 변환 작업을 통하여 1초로 만들고 1초를 60분주하여 1분을 만든다.
PXA255-FPGA 보드
FPGA
|
Altera Cyclon EP1C6
|
Logic Elements
|
5980
|
Logic Gate
|
120000 Logic Gate
|
RAM Bit
|
92160 Bit
|
외부 CPU
|
PXA255
|
외부 인터페이스
|
32 Bit Address/Data Bus
|
입력 I/O
|
Push SW, Dip SW, ADC, Image Sensor
|
출력 I/O
|
Text LCD, LED, 7 Segment, Buzzer, DotMatrix,
VGA
|
기타 I/O
|
EEPROM
|
- 디지털 시계에서는 7 Segment LED 제어를 위하여 EP1C6 칩 11~18의 데이터 라인 8핀과 19~25까지의 COM 라인 6핀을 출력 PORT로 사용한다.
- PXA255-FPGA 보드에서 사용하는 FND는 공통으로 사용하는 데이터 라인 8개와 7 Segment의 출력을 결정하는 COM라인 6개로 구성된다.
- FND 동작은 6개라인(FCOM[5..0])의 6비트 중 비트 값이 0인 Segment에 데이터(FDATA[7..0])를 출력한다.
- 각각 다른 데이터를 7 Segment LED에 출력 하려면 6개의 FCOM의 비트들을 쉬프트 시켜가면서 FCOM의 값이 바뀌는 시점에 데이터 라인을 세팅하면 선택된 7 Segment LED에 데이터를 출력한다. 이와 같은 방법을 Dynamic Display 방식이라고 한다.
- Dynamic Display 방식은 빠른 속도로 쉬프트 시킴으로서 사람의 눈의 잔상효과를 이용하여 마치 6개의 7 Segment가 동시에 출력되는 것처럼 보여준다.
디지털 시계 프로그램의 블록도
- 위의 블럭도에서는 입력으로 Main Clock과 리셋 버튼, 그리고 시, 분, 초를 제어할 수 있는 스위치 3개를 가진다.
- 디지털 시계의 출력으로는 6개의 7 Segment를 제어하기 위한 8개의 데이터 라인 FDATA_SM[7..0]과 출력 7 Segment 선택 라인인 FCOM[5..0]의 6개 출력을 가진다.
구현
- clk_div.vhd -
library
ieee;
use ieee.std_logic_1164.all;
entity clk_div is
generic(count : integer range 0
to 30000 := 2);
port(CLK_IN : in std_logic;
SM_CLK : out std_logic );
end clk_div;
architecture sample of clk_div is
begin
process(CLK_IN)
variable tmp :
integer range 0 to 30000 := 0;
begin
if(CLK_IN'event and
CLK_IN = '1') then
if(tmp =
count-1) then
tmp
:= 0;
SM_CLK
<= '1';
else
tmp
:= tmp + 1;
SM_CLK
<= '0';
end if;
end if;
end process;
end sample;
- DigitalTimer.vhd -
library
ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
entity DigitalTimer is
port( CLK :
in std_logic;
rset :
in std_logic;
ih_key :
in std_logic;
im_key :
in std_logic;
is_key :
in std_logic;
FDATA_SM :
out std_logic_vector(7 downto 0) := "00111111";
FCOM :
out std_logic_vector(5 downto 0) := "001111" );
end DigitalTimer;
architecture rt1 of DigitalTimer is
function fnd_disnum(cnt :
integer range 0 to 9) return std_logic_vector is
variable seg_decode :
std_logic_vector(7 downto 0);
begin
case cnt is
when 0
=> seg_decode := "00111111";
when 1
=> seg_decode := "00000110";
when 2
=> seg_decode := "01011011";
when 3
=> seg_decode := "01001111";
when 4
=> seg_decode := "01100110";
when 5
=> seg_decode := "01101101";
when 6
=> seg_decode := "01111101";
when 7
=> seg_decode := "00100111";
when 8 => seg_decode :=
"01111111";
when 9
=> seg_decode := "01100111";
when
others => seg_decode := "00000000";
end case;
return (seg_decode);
end fnd_disnum;
-- clock division
component clk_div
generic(count :
integer range 0 to 30000);
port( CLK_IN : in
std_logic;
SM_CLK : out std_logic );
end component;
-- used signal
signal temp : integer range 0
to 5;
signal cnt_sec1 : integer range
0 to 10 := 0;
signal cnt_min : integer range
0 to 10 := 0;
signal cnt_min1 : integer range
0 to 10 := 0;
signal seg0 :
std_logic_vector(7 downto 0) := "00111111";
signal seg1 :
std_logic_vector(7 downto 0) := "00111111";
signal seg2 :
std_logic_vector(7 downto 0) := "00111111";
signal seg3 :
std_logic_vector(7 downto 0) := "00111111";
signal seg4 :
std_logic_vector(7 downto 0) := "00111111";
signal seg5 :
std_logic_vector(7 downto 0) := "00111111";
signal sclk0, sclk1, sclk2,
sclk3 : std_logic := '0';
signal temp_clk : std_logic;
signal fnd_clk : std_logic;
signal sec_clk : std_logic;
begin
LED_CLK_DIV : clk_div generic
map(330) port map(CLK, fnd_clk); -- fnd_clk : 100Khz
TEMP_CLK_DIV : clk_div generic
map(3300) port map(CLK, temp_clk); -- temp_clk : 10Khz
SEC_CLK_DIV : clk_div generic
map(10000) port map(temp_clk, sec_clk); --
sec_clk : 1hz
-- FND Output Process
process(fnd_clk)
begin
if(fnd_clk'event and
fnd_clk = '1') then
case temp
is
when
0 => temp <=1; FCOM <= "111110"; FDATA_SM <= seg5;
when
1 => temp <=2; FCOM <= "111101"; FDATA_SM <= seg4;
when
2 => temp <=3; FCOM <= "111011"; FDATA_SM <= seg3;
when
3 => temp <=4; FCOM <= "110111"; FDATA_SM <= seg2;
when
4 => temp <=5; FCOM <= "101111"; FDATA_SM <= seg1;
when
5 => temp <=0; FCOM <= "011111"; FDATA_SM <= seg0;
when
others => FCOM <= "111111"; FDATA_SM <=
"00111111";
end case;
end if;
end process;
-- 1 second Process
process(sec_clk, rset, is_key)
variable cnt :
integer range 0 to 10 := 0;
begin
if(rset = '1' or
is_key = '1') then
cnt := 0;
sclk0
<= '0';
seg0 <=
fnd_disnum(0);
elsif(sec_clk'event
and sec_clk = '1') then
if(cnt =
9) then
cnt
:= 0;
sclk0
<= '0';
elsif(cnt
= 8) then
sclk0
<= '1';
cnt
:= cnt + 1;
else
cnt
:= cnt + 1;
sclk0
<= '0';
end if;
seg0 <=
fnd_disnum(cnt);
end if;
end process;
-- 10 second process
process(sec_clk, rset, is_key)
begin
if(rset = '1' or
is_key = '1') then
cnt_sec1
<= 0;
elsif(sec_clk'event
and sec_clk = '1') then
if(sclk0 =
'1') then
if(cnt_sec1
= 5) then
cnt_sec1
<= 0;
else
cnt_sec1
<= cnt_sec1 + 1;
end
if;
end if;
end if;
seg1 <=
fnd_disnum(cnt_sec1);
end process;
sclk1 <= '1' when(cnt_sec1 =
5 and sclk0 = '1') else '0';
-- 1 miniute process
process(sec_clk, rset, im_key)
begin
if(rset = '1') then
cnt_min
<= 0;
elsif(sec_clk'event
and sec_clk = '1') then
if(im_key
= '1') then
if(cnt_min
= 9) then
cnt_min
<= 0;
else
cnt_min
<= cnt_min + 1;
end
if;
elsif(sclk1
= '1') then
if(cnt_min
= 9) then
cnt_min
<= 0;
else
cnt_min
<= cnt_min + 1;
end
if;
end if;
end if;
seg2 <=
fnd_disnum(cnt_min);
end process;
sclk2 <= '1' when(cnt_min =
9 and sclk1 = '1') else
'1' when(cnt_min = 9 and im_key = '1') else
'0';
-- 10 miniute process
process(sec_clk, rset)
begin
if(rset = '1') then
cnt_min1
<= 0;
elsif(sec_clk'event
and sec_clk = '1') then
if(sclk2 =
'1') then
if(cnt_min1
= 5) then
cnt_min1
<= 0;
else
cnt_min1
<= cnt_min1 + 1;
end
if;
end if;
end if;
seg3 <=
fnd_disnum(cnt_min1);
end process;
sclk3 <= '1' when(cnt_min1 =
5 and sclk2 = '1') else '0';
-- hour process
process(sec_clk, rset, ih_key)
variable cnt :
integer range 0 to 24 := 0;
variable a, b :
integer range 0 to 50 := 0;
begin
if(rset = '1') then
cnt := 0;
elsif(sec_clk'event
and sec_clk = '1') then
if(ih_key
= '1') then
if(cnt
= 23) then
cnt
:= 0;
else
cnt
:= cnt + 1;
end
if;
elsif(sclk3
= '1') then
if(cnt
= 23) then
cnt
:= 0;
else
cnt
:= cnt + 1;
end
if;
end if;
end if;
a := cnt / 10;
b := cnt mod 10;
seg4 <= fnd_disnum(b);
seg5 <=
fnd_disnum(a);
end process;
end rt1;
결론
이번 프로젝트에서는 디지털시계를 HDL언어인 VHDL를 사용하여 설계하여 실습 보드인 PXA255 FPGA에 직접 올려보았다.
디지털시계를 구현하기 위해서 33Mhz의 메인 클럭에서 1hz의 클럭을 얻기 위한 묘듈이 필요했다. 이 묘듈은 카운터를 이용하여 원하는 수만큼 카운트 한 후 신호를 내보내는 원리로 동작한다.
디지털시계는 1초, 10초, 1분, 10분, 시간을 각각 처리하는 프로세스 문으로 작성되며, 모든 동작은 1hz인 second clock에 동기 되어 동작하게 된다. 그 외 sclk0, sclk1, sclk2, sclk3 의 클럭들이 내부에서 발생 된다. sclk0은 9초에서 10초로 넘어 갈 때, sclk1은 59초에서 1분으로 넘어 갈 때, sclk2는 9분에서 10분으로 넘어 갈 때, sclk3는 59분에서 1시간으로 넘어 갈 때 사용 되는 신호이다.
이번 프로젝트를 통해서 타이밍에 대한 이해와 VHDL의 활용능력 및 쿼터스 같은 툴 사용에 대해 익숙해 진 것 같다. 또한 자신이 작성한 디지털시계를 직접 FPGA에 올려 보고 제대로 동작을 하는지 확인해봄으로써, 평소 시뮬레이션만 해보는 것과 다르게 회로설계에 대한 자신감을 가질 수 있었다.