1 |
3 |
howe.r.j.8 |
--------------------------------------------------------------------------------
|
2 |
|
|
--| @file led.vhd
|
3 |
|
|
--| @brief controls a number of led displays, 8 segment LEDs, there
|
4 |
|
|
--| is no enable, just write 0 to the displays to turn them off.
|
5 |
|
|
--|
|
6 |
|
|
--| @author Richard James Howe.
|
7 |
|
|
--| @copyright Copyright 2013 Richard James Howe.
|
8 |
|
|
--| @license MIT
|
9 |
|
|
--| @email howe.r.j.89@gmail.com
|
10 |
|
|
--|
|
11 |
|
|
--------------------------------------------------------------------------------
|
12 |
|
|
library ieee,work;
|
13 |
|
|
use ieee.std_logic_1164.all;
|
14 |
|
|
use ieee.numeric_std.all;
|
15 |
|
|
|
16 |
|
|
package led_pkg is
|
17 |
|
|
constant character_length: positive := 4;
|
18 |
|
|
|
19 |
|
|
subtype led_character is std_ulogic_vector(character_length - 1 downto 0);
|
20 |
|
|
subtype led_8_segment is std_ulogic_vector(7 downto 0);
|
21 |
|
|
|
22 |
|
|
component led_8_segment_display is
|
23 |
|
|
generic(
|
24 |
|
|
clock_frequency: positive;
|
25 |
|
|
use_bcd_not_hex: boolean := true;
|
26 |
|
|
refresh_rate_us: natural := 1500;
|
27 |
|
|
number_of_led_displays: positive := 4);
|
28 |
|
|
port(
|
29 |
|
|
clk: in std_ulogic;
|
30 |
|
|
rst: in std_ulogic;
|
31 |
|
|
|
32 |
|
|
leds_we: in std_ulogic;
|
33 |
|
|
leds: in std_ulogic_vector((number_of_led_displays * character_length) - 1 downto 0);
|
34 |
|
|
|
35 |
|
|
-- Physical outputs
|
36 |
|
|
an: out std_ulogic_vector(number_of_led_displays - 1 downto 0); -- anodes, controls on/off
|
37 |
|
|
ka: out std_ulogic_vector(7 downto 0)); -- cathodes, data on display
|
38 |
|
|
end component;
|
39 |
|
|
|
40 |
|
|
end package;
|
41 |
|
|
|
42 |
|
|
|
43 |
|
|
--| This module implements a 8 segment display driver, with 4 displays in total:
|
44 |
|
|
--|
|
45 |
|
|
--| _____________________ an (selects segment)
|
46 |
|
|
--| | | | |
|
47 |
|
|
--| __ __ __ __
|
48 |
|
|
--| | | | | | | | |
|
49 |
|
|
--| |__| |__| |__| |__|
|
50 |
|
|
--| | | | | | | | |
|
51 |
|
|
--| |__|. |__|. |__|. |__|.
|
52 |
|
|
--| |____|_____|_____|____ ka (value to display on segment)
|
53 |
|
|
--|
|
54 |
|
|
--| Each of the display shares a common anode for all of its LEDs, this can be
|
55 |
|
|
--| used to select an individual display
|
56 |
|
|
|
57 |
|
|
library ieee,work;
|
58 |
|
|
use ieee.std_logic_1164.all;
|
59 |
|
|
use ieee.numeric_std.all;
|
60 |
|
|
use work.util.reg;
|
61 |
|
|
use work.util.timer_us;
|
62 |
|
|
use work.util.invert;
|
63 |
|
|
use work.led_pkg.all;
|
64 |
|
|
|
65 |
|
|
entity led_8_segment_display is
|
66 |
|
|
generic(
|
67 |
|
|
clock_frequency: positive;
|
68 |
|
|
use_bcd_not_hex: boolean := true;
|
69 |
|
|
refresh_rate_us: natural := 1500;
|
70 |
|
|
number_of_led_displays: positive := 4);
|
71 |
|
|
port(
|
72 |
|
|
clk: in std_ulogic;
|
73 |
|
|
rst: in std_ulogic;
|
74 |
|
|
|
75 |
|
|
leds_we: in std_ulogic;
|
76 |
|
|
leds: in std_ulogic_vector((number_of_led_displays * character_length) - 1 downto 0);
|
77 |
|
|
|
78 |
|
|
-- Physical outputs
|
79 |
|
|
an: out std_ulogic_vector(number_of_led_displays - 1 downto 0); -- anodes, controls on/off
|
80 |
|
|
ka: out std_ulogic_vector(7 downto 0)); -- cathodes, data on display
|
81 |
|
|
end;
|
82 |
|
|
|
83 |
|
|
architecture rtl of led_8_segment_display is
|
84 |
|
|
|
85 |
|
|
-- 8 Segment LED lookup table converts a BCD character into a value
|
86 |
|
|
-- that can be displayed on an 8 segment display. The layout of which
|
87 |
|
|
-- is as follows:
|
88 |
|
|
--
|
89 |
|
|
-- A
|
90 |
|
|
-- ---
|
91 |
|
|
-- F | | B
|
92 |
|
|
-- |___|
|
93 |
|
|
-- E | G | C
|
94 |
|
|
-- |___| . DP
|
95 |
|
|
-- D
|
96 |
|
|
--
|
97 |
|
|
-- The following encoding is used to convert the input BCD character
|
98 |
|
|
-- into a value that can be put onto the display.
|
99 |
|
|
--
|
100 |
|
|
-- -----------------------------------------
|
101 |
|
|
-- | | DP| G | F | E | D | C | B | A | Hex |
|
102 |
|
|
-- |BCD| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |Hi Lo|
|
103 |
|
|
-- -----------------------------------------
|
104 |
|
|
-- | 0 | | | 1 | 1 | 1 | 1 | 1 | 1 | 3 F |
|
105 |
|
|
-- | 1 | | | | | | 1 | 1 | | 0 6 |
|
106 |
|
|
-- | 2 | | 1 | | 1 | 1 | | 1 | 1 | 5 B |
|
107 |
|
|
-- | 3 | | 1 | | | 1 | 1 | 1 | 1 | 4 F |
|
108 |
|
|
-- | 4 | | 1 | 1 | | | 1 | 1 | | 6 6 |
|
109 |
|
|
-- | 5 | | 1 | 1 | | 1 | 1 | | 1 | 6 D |
|
110 |
|
|
-- | 6 | | 1 | 1 | 1 | 1 | 1 | | 1 | 7 D |
|
111 |
|
|
-- | 7 | | | | | | 1 | 1 | 1 | 0 7 |
|
112 |
|
|
-- | 8 | | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 7 F |
|
113 |
|
|
-- | 9 | | 1 | 1 | | 1 | 1 | 1 | 1 | 6 F |
|
114 |
|
|
-- | | | | | | | | | | 0 0 |
|
115 |
|
|
-- | . | 1 | | | | | | | | 8 0 |
|
116 |
|
|
-- | - | | 1 | | | | | | | 4 0 |
|
117 |
|
|
-- -----------------------------------------
|
118 |
|
|
-- | A | | 1 | 1 | 1 | | 1 | 1 | 1 | 7 7 |
|
119 |
|
|
-- | b | | 1 | 1 | 1 | 1 | 1 | | | 7 C |
|
120 |
|
|
-- | C | | | 1 | 1 | 1 | | | 1 | 3 9 |
|
121 |
|
|
-- | d | | 1 | | 1 | 1 | 1 | 1 | | 5 E |
|
122 |
|
|
-- | E | | 1 | 1 | 1 | 1 | | | 1 | 7 9 |
|
123 |
|
|
-- | F | | 1 | 1 | 1 | | | | 1 | 7 1 |
|
124 |
|
|
-- -----------------------------------------
|
125 |
|
|
--
|
126 |
|
|
-- The table is then inverted before it goes to the output.
|
127 |
|
|
--
|
128 |
|
|
|
129 |
|
|
function hex_to_8segment(a: led_character) return led_8_segment is
|
130 |
|
|
variable r: std_ulogic_vector(7 downto 0);
|
131 |
|
|
begin
|
132 |
|
|
case a is
|
133 |
|
|
when "0000" => r := x"3F"; -- 0
|
134 |
|
|
when "0001" => r := x"06"; -- 1
|
135 |
|
|
when "0010" => r := x"5B"; -- 2
|
136 |
|
|
when "0011" => r := x"4F"; -- 3
|
137 |
|
|
when "0100" => r := x"66"; -- 4
|
138 |
|
|
when "0101" => r := x"6D"; -- 5
|
139 |
|
|
when "0110" => r := x"7D"; -- 6
|
140 |
|
|
when "0111" => r := x"07"; -- 7
|
141 |
|
|
when "1000" => r := x"7F"; -- 8
|
142 |
|
|
when "1001" => r := x"6F"; -- 9
|
143 |
|
|
when "1010" => r := x"77"; -- A
|
144 |
|
|
when "1011" => r := x"7C"; -- b
|
145 |
|
|
when "1100" => r := x"39"; -- C
|
146 |
|
|
when "1101" => r := x"5E"; -- d
|
147 |
|
|
when "1110" => r := x"79"; -- E
|
148 |
|
|
when "1111" => r := x"71"; -- F
|
149 |
|
|
when others => r := x"00"; -- Unused
|
150 |
|
|
end case;
|
151 |
|
|
return r;
|
152 |
|
|
end function;
|
153 |
|
|
|
154 |
|
|
function bcd_to_8segment(a: led_character) return led_8_segment is
|
155 |
|
|
variable r: std_ulogic_vector(7 downto 0);
|
156 |
|
|
begin
|
157 |
|
|
case a is
|
158 |
|
|
when "0000" => r := x"3F"; -- 0
|
159 |
|
|
when "0001" => r := x"06"; -- 1
|
160 |
|
|
when "0010" => r := x"5B"; -- 2
|
161 |
|
|
when "0011" => r := x"4F"; -- 3
|
162 |
|
|
when "0100" => r := x"66"; -- 4
|
163 |
|
|
when "0101" => r := x"6D"; -- 5
|
164 |
|
|
when "0110" => r := x"7D"; -- 6
|
165 |
|
|
when "0111" => r := x"07"; -- 7
|
166 |
|
|
when "1000" => r := x"7F"; -- 8
|
167 |
|
|
when "1001" => r := x"6F"; -- 9
|
168 |
|
|
when "1010" => r := x"00"; -- Blank
|
169 |
|
|
when "1011" => r := x"80"; -- .
|
170 |
|
|
when "1100" => r := x"40"; -- -
|
171 |
|
|
when "1101" => r := x"00"; -- Unused
|
172 |
|
|
when "1110" => r := x"00"; -- Unused
|
173 |
|
|
when "1111" => r := x"00"; -- Unused
|
174 |
|
|
when others => r := x"00"; -- Unused
|
175 |
|
|
end case;
|
176 |
|
|
return r;
|
177 |
|
|
end function;
|
178 |
|
|
|
179 |
|
|
function char_to_8segment(a: led_character) return led_8_segment is
|
180 |
|
|
begin
|
181 |
|
|
if use_bcd_not_hex then
|
182 |
|
|
return invert(bcd_to_8segment(a));
|
183 |
|
|
else
|
184 |
|
|
return invert(hex_to_8segment(a));
|
185 |
|
|
end if;
|
186 |
|
|
end function;
|
187 |
|
|
|
188 |
|
|
signal leds_o: std_ulogic_vector(leds'range) := (others => '0');
|
189 |
|
|
|
190 |
|
|
signal do_shift: std_ulogic := '0';
|
191 |
|
|
signal shift_reg: std_ulogic_vector(number_of_led_displays - 1 downto 0) := (0 => '1', others => '0');
|
192 |
|
|
|
193 |
|
|
|
194 |
|
|
signal leds_reg_o: std_ulogic_vector(leds'range) := (others => '0');
|
195 |
|
|
signal leds_reg_we_o: std_ulogic := '0';
|
196 |
|
|
begin
|
197 |
|
|
an <= invert(shift_reg);
|
198 |
|
|
|
199 |
|
|
segment_reg: entity work.reg
|
200 |
|
|
generic map(N => number_of_led_displays * character_length)
|
201 |
|
|
port map(
|
202 |
|
|
clk => clk,
|
203 |
|
|
rst => rst,
|
204 |
|
|
we => leds_we,
|
205 |
|
|
di => leds,
|
206 |
|
|
do => leds_reg_o);
|
207 |
|
|
|
208 |
|
|
segment_reg_re: entity work.reg
|
209 |
|
|
generic map(N => 1)
|
210 |
|
|
port map(
|
211 |
|
|
clk => clk,
|
212 |
|
|
rst => rst,
|
213 |
|
|
we => '1',
|
214 |
|
|
di(0) => leds_we,
|
215 |
|
|
do(0) => leds_reg_we_o);
|
216 |
|
|
|
217 |
|
|
led_gen: for i in number_of_led_displays - 1 downto 0 generate
|
218 |
|
|
led_i: entity work.reg
|
219 |
|
|
generic map(
|
220 |
|
|
N => character_length)
|
221 |
|
|
port map(
|
222 |
|
|
clk => clk,
|
223 |
|
|
rst => rst,
|
224 |
|
|
we => leds_reg_we_o,
|
225 |
|
|
di => leds_reg_o((i*character_length) + character_length - 1 downto (i*character_length)),
|
226 |
|
|
do => leds_o((i*character_length) + character_length - 1 downto (i*character_length)));
|
227 |
|
|
end generate;
|
228 |
|
|
|
229 |
|
|
timer: entity work.timer_us
|
230 |
|
|
generic map(
|
231 |
|
|
clock_frequency => clock_frequency,
|
232 |
|
|
timer_period_us => refresh_rate_us)
|
233 |
|
|
port map(
|
234 |
|
|
clk => clk,
|
235 |
|
|
rst => rst,
|
236 |
|
|
co => do_shift);
|
237 |
|
|
|
238 |
|
|
process(clk, do_shift, shift_reg)
|
239 |
|
|
begin
|
240 |
|
|
if rising_edge(clk) then
|
241 |
|
|
if do_shift = '1' then
|
242 |
|
|
shift_reg <= shift_reg(number_of_led_displays - 2 downto 0) & shift_reg(number_of_led_displays - 1);
|
243 |
|
|
end if;
|
244 |
|
|
else
|
245 |
|
|
shift_reg <= shift_reg;
|
246 |
|
|
end if;
|
247 |
|
|
end process;
|
248 |
|
|
|
249 |
|
|
process(leds_o, shift_reg)
|
250 |
|
|
begin
|
251 |
|
|
ka <= (others => '0');
|
252 |
|
|
|
253 |
|
|
for i in number_of_led_displays - 1 downto 0 loop
|
254 |
|
|
if '1' = shift_reg(number_of_led_displays - i - 1) then
|
255 |
|
|
ka <= char_to_8segment(leds_o(i*character_length + character_length - 1 downto (i*character_length)));
|
256 |
|
|
end if;
|
257 |
|
|
end loop;
|
258 |
|
|
end process;
|
259 |
|
|
end architecture;
|
260 |
|
|
|