Увеличение семисегментного дисплея в автомате для платы de1
Я использую конечный автомат для создания таймера обратного отсчета, который при запуске отображает 00:00, и когда нажата клавиша 1, вы можете ввести время, увеличивая / уменьшая минуты на 1, и если кнопка вверх / вниз удерживается в течение 5 циклов, будет увеличиваться / уменьшаться на 5. Благодаря некоторой потрясающей помощи (@DavidKoontz) я закончил код. Нет необходимости разбирать кнопки в моем коде, потому что моя плата альтера, кажется, отлично воспринимает низкие сигналы. Поскольку я использую только один такт, кнопки реагируют медленно, поскольку б / с часы установлены на 1 Гц.
Library ieee;
USE ieee.std_logic_1164.ALL;
ENTITY CountDownTimer IS
PORT(
CLK,RESET: IN STD_LOGIC;
a1, b1, c1, d1, e1, f1, g1 : OUT STD_LOGIC;
a2, b2, c2, d2, e2, f2, g2 : OUT STD_LOGIC;
a3, b3, c3, d3, e3, f3, g3 : OUT STD_LOGIC;
a4, b4, c4, d4, e4, f4, g4 : OUT STD_LOGIC;
--All 4 buttons for timer
BUTTON0, BUTTON1, BUTTON2, BUTTON3: IN STD_LOGIC;
--LEDR9
ON_OFF_LED: OUT BIT;
--LEDR9-R6
INPUT_LED1, INPUT_LED2, INPUT_LED3, INPUT_LED4: OUT BIT;
--LEDR0
DONE_LED: OUT BIT);
END CountdownTimer;
ARCHITECTURE Counter OF CountDownTimer IS
--Define state machine
TYPE STATE_TYPE IS (A_ON_OFF, B_INPUT, C_COUNTDOWN, D_DONE);
SIGNAL State : STATE_TYPE;
--Buttons produce 0 when pressed, signal for 1 when pressed
SIGNAL B3D0, B3D1, B3D2, B3D3: STD_LOGIC := '0';
SIGNAL B2D0, B2D1, B2D2, B2D3: STD_LOGIC := '0';
SIGNAL CLOCK: STD_LOGIC := '0';
SIGNAL Count: INTEGER:= 1;
--SIGNAL for range of integer values
SIGNAL Minute1 : INTEGER RANGE 0 TO 6;
SIGNAL Minute2 : INTEGER RANGE 0 TO 9;
SIGNAL Second1 : INTEGER RANGE 0 TO 5;
SIGNAL Second2 : INTEGER RANGE 0 TO 9;
--Output for the seven segment displays
SIGNAL OUTPUT_HEX0 : STD_LOGIC_VECTOR(6 DOWNTO 0);
SIGNAL OUTPUT_HEX1 : STD_LOGIC_VECTOR (6 DOWNTO 0);
SIGNAL OUTPUT_HEX2 : STD_LOGIC_VECTOR (6 DOWNTO 0);
SIGNAL OUTPUT_HEX3 : STD_LOGIC_VECTOR (6 DOWNTO 0);
SIGNAL B3_HOLD: STD_LOGIC := '0'; --Gets 1 if button3 was held
SIGNAL B2_HOLD: STD_LOGIC := '0'; --Gets 1 is button2 was held
BEGIN
--Segment 1 display pins
a1 <= OUTPUT_HEX0(6);
b1 <= OUTPUT_HEX0(5);
c1 <= OUTPUT_HEX0(4);
d1 <= OUTPUT_HEX0(3);
e1 <= OUTPUT_HEX0(2);
f1 <= OUTPUT_HEX0(1);
g1 <= OUTPUT_HEX0(0);
--Segment 2 display pins
a2 <= OUTPUT_HEX1(6);
b2 <= OUTPUT_HEX1(5);
c2 <= OUTPUT_HEX1(4);
d2 <= OUTPUT_HEX1(3);
e2 <= OUTPUT_HEX1(2);
f2 <= OUTPUT_HEX1(1);
g2 <= OUTPUT_HEX1(0);
--Segment 3 display pins
a3 <= OUTPUT_HEX2(6);
b3 <= OUTPUT_HEX2(5);
c3 <= OUTPUT_HEX2(4);
d3 <= OUTPUT_HEX2(3);
e3 <= OUTPUT_HEX2(2);
f3 <= OUTPUT_HEX2(1);
g3 <= OUTPUT_HEX2(0);
--Segment 4 display pins
a4 <= OUTPUT_HEX3(6);
b4 <= OUTPUT_HEX3(5);
c4 <= OUTPUT_HEX3(4);
d4 <= OUTPUT_HEX3(3);
e4 <= OUTPUT_HEX3(2);
f4 <= OUTPUT_HEX3(1);
g4 <= OUTPUT_HEX3(0);
WITH Second2 SELECT
--One's second place, 0 to 9
OUTPUT_HEX0 <= "0000001" WHEN 0,
"1001111" WHEN 1,
"0010010" WHEN 2,
"0000110" WHEN 3,
"1001100" WHEN 4,
"0100100" WHEN 5,
"0100000" WHEN 6,
"0001101" WHEN 7,
"0000000" WHEN 8,
"0001100" WHEN 9,
"0000001" WHEN OTHERS;
WITH Second1 SELECT
--Tens second place, 0 to 5
OUTPUT_HEX1 <= "0000001" WHEN 0,
"1001111" WHEN 1,
"0010010" WHEN 2,
"0000110" WHEN 3,
"1001100" WHEN 4,
"0100100" WHEN 5,
"0000001" WHEN OTHERS;
WITH Minute2 SELECT
--Ones minute place, 0 to 9
OUTPUT_HEX2 <= "0000001" WHEN 0,
"1001111" WHEN 1,
"0010010" WHEN 2,
"0000110" WHEN 3,
"1001100" WHEN 4,
"0100100" WHEN 5,
"0100000" WHEN 6,
"0001101" WHEN 7,
"0000000" WHEN 8,
"0001100" WHEN 9,
"0000001" WHEN OTHERS;
WITH Minute1 SELECT
--Tens minute place, 0 to 6
OUTPUT_HEX3 <= "0000001" WHEN 0,
"1001111" WHEN 1,
"0010010" WHEN 2,
"0000110" WHEN 3,
"1001100" WHEN 4,
"0100100" WHEN 5,
"0100000" WHEN 6,
"0000001" WHEN OTHERS;
PROCESS(CLK)
BEGIN
IF RISING_EDGE(CLK) THEN
Count <= Count + 1;
IF (Count = 30000000) THEN
CLOCK <= NOT(CLOCK);
Count <= 1;
END IF;
END IF;
END PROCESS;
PROCESS(CLOCK)
BEGIN
IF RISING_EDGE(CLOCK) THEN
B3D0 <= BUTTON3;
B3D1 <= NOT B3D0;
B3D2 <= B3D1;
B3D3 <= B3D2;
B2D0 <= BUTTON2;
B2D1 <= NOT B2D0;
B2D2 <= B2D1;
B2D3 <= B2D2;
B3_HOLD <= B3D1 AND B3D2 AND B3D3;
B2_HOLD <= B2D1 AND B2D2 AND B2D3;
END IF;
END PROCESS;
PROCESS(CLOCK)
BEGIN
IF RESET = '1' THEN --Async Reset
State <= A_ON_OFF;
ELSIF RISING_EDGE(CLOCK) THEN
CASE State IS
---------------------------------A_ON_OFF---------------------------------
WHEN A_ON_OFF =>
--Red LED9
ON_OFF_LED <= '1';
Minute1 <= 0;
Minute2 <= 0;
Second1 <= 0;
Second2 <= 0;
IF (BUTTON0 = '0') THEN
ON_OFF_LED <= '0';
State <= B_INPUT;
END IF;
---------------------------------B_INPUT/PAUSE---------------------------------
WHEN B_INPUT =>
--Light up LEDs
INPUT_LED1 <= '1';
INPUT_LED2 <= '1';
INPUT_LED3 <= '1';
INPUT_LED4 <= '1';
IF (Minute1 = 6) THEN
Minute2 <= 0;
Second1 <= 0;
Second2<= 0;
State <= B_INPUT;
END IF;
--Count up button
IF (BUTTON3 = '0' AND B3_HOLD = '0') THEN
IF (Minute1 = 6 AND Minute2 >= 0) THEN
Minute1 <= 0;
Minute2 <= 1;
Second1 <= 0;
Second2 <= 0;
State <= B_INPUT;
ELSIF (Minute2 < 9) THEN
Minute2 <= (Minute2 + 1);
State <= B_INPUT;
ELSIF (Minute2 = 9) THEN
Minute1 <= (Minute1 + 1);
Minute2 <= 0;
State <= B_INPUT;
END IF;
END IF;
IF (BUTTON3 = '0' AND B3_HOLD = '1') THEN
IF (Minute1 = 6 AND Minute2 >= 0) THEN
Minute1 <= 0;
Minute2 <= 5;
Second1 <= 0;
Second2 <= 0;
State <= B_INPUT;
ELSIF (Minute2 < 5) THEN
IF (Minute2 = 0) THEN
Minute2 <= (Minute2 + 5);
State <= B_INPUT;
ELSE
Minute2 <= (Minute2 + 1);
State <= B_INPUT;
END IF;
ELSIF (Minute2 = 5) THEN
Minute2 <= 0;
Minute1 <= (Minute1 + 1);
State <= B_INPUT;
ELSIF (Minute2 > 5) THEN
IF (Minute2 = 9) THEN
Minute2 <= 0;
Minute1 <= (Minute1 + 1);
State <= B_INPUT;
ELSE
Minute2 <= (Minute2 + 1);
State <= B_INPUT;
END IF;
END IF;
END IF;
--Count down button
IF (BUTTON2 = '0' AND B2_HOLD = '0') THEN
IF ((Minute1 = 0) AND (Minute2 = 0)) THEN
Minute1 <= 6;
Minute2 <= 0;
Second1 <= 0;
Second2 <= 0;
ELSIF (Minute2 = 0) THEN
Minute2 <= 9;
Minute1 <= (Minute1 - 1);
ELSE
Minute2 <= (Minute2 - 1);
END IF;
State <= B_INPUT;
END IF;
IF (BUTTON2 = '0' AND B2_HOLD = '1') THEN
IF (Minute1 = 0 AND Minute2 = 0) THEN
Minute1 <= 6;
Minute2 <= 0;
Second1 <= 0;
Second2 <= 0;
State <= B_INPUT;
ELSIF (Minute2 = 0 AND Minute1 > 0) THEN
Minute1 <= (Minute1 - 1);
Minute2 <= 5;
State <= B_INPUT;
ELSIF (Minute2 < 5) THEN
IF (Minute2 = 0) THEN
Minute1 <= (Minute1 - 1);
Minute2 <= 5;
State <= B_INPUT;
ELSE
Minute2 <= (Minute2 - 1);
State <= B_INPUT;
END IF;
ELSIF (Minute2 = 5) THEN
Minute2 <= (Minute2 - 5);
State <= B_INPUT;
ELSIF (Minute2 > 5) THEN
Minute2 <= (Minute2 - 1);
State <= B_INPUT;
END IF;
END IF;
--Clear button
IF (BUTTON1 = '0') THEN
Minute1 <= 0;
Minute2 <= 0;
Second1 <= 0;
Second2 <= 0;
State <= B_INPUT;
END IF;
--Start Button
IF (BUTTON0 = '0') THEN
--Turn off LEDs
INPUT_LED1 <= '0';
INPUT_LED2 <= '0';
INPUT_LED3 <= '0';
INPUT_LED4 <= '0';
State <= C_COUNTDOWN;
END IF;
---------------------------------C_COUNTDOWN---------------------------------
WHEN C_COUNTDOWN =>
IF (Second2 > 0) THEN
Second2 <= (Second2 - 1);
ELSIF (Second2 = 0) THEN
IF (Second1 > 0) THEN
Second2 <= 9;
Second1 <= (Second1 - 1);
ELSIF (Second1 = 0) THEN
IF (Minute2 > 0) THEN
Second1 <= 5;
Minute2 <= (Minute2 - 1);
IF (Second2 = 0) THEN
Second2 <= 9;
END IF;
ELSIF (Minute2 = 0) THEN
IF (Minute1 > 0) THEN
IF (Second1 = 0) THEN
Second1 <= 5;
Minute2 <= (Minute2 - 1);
IF (Second2 = 0) THEN
Second2 <= 9;
END IF;
END IF;
Minute2 <= 9;
Minute1 <= (Minute1 - 1);
ELSIF (Minute1 <= 0) THEN
State <= D_DONE;
END IF;
END IF;
END IF;
END IF;
--Reset Button
IF (BUTTON1 = '0') THEN
STATE <= A_ON_OFF;
--Input button
ELSIF (BUTTON0 = '0') THEN
STATE <= B_INPUT;
END IF;
---------------------------------D_DONE---------------------------------
WHEN D_DONE =>
--LEDR0
DONE_LED <= '1';
--Reset button
IF (BUTTON1 = '0') THEN
DONE_LED <= '0';
STATE <= A_ON_OFF;
--Input button
ELSIF (BUTTON0 = '0') THEN
DONE_LED <= '0';
STATE <= B_INPUT;
END IF;
END CASE;
END IF;
END PROCESS;
END Counter;
1 ответ
На выбор when b_input
в случае дела вы будете увеличивать minute2
для каждого clk
button3
низкий, это возможность. Это должно быть преобразовано в событие, если вы не рассчитываете на очень медленный clk
(отмечая, что вы также производите медленный clock
). Это включение не является синхронным с clk
, это может вызвать метастабильность для установки или нарушение времени удержания.
В процессе
PROCESS(BUTTON3)
BEGIN
IF RISING_EDGE(BUTTON3) THEN
FF3 <= NOT(BUTTON3);
END IF;
END PROCESS;
Это назначит ff3
низкий, но никогда не высокий. Нарастающий фронт означает переход от "0" к "1" на button3
, Другими словами, ваше вождение ff3
только низкий
Вот почему ваш счетчик не меняется. Помимо потенциальной проблемы метастабильности, включение должно происходить для одного clk
,
Для симуляции счетчиков с целым диапазоном (например, minute2
) переполнится. VHDL не выполняет модульную арифметику с ограничением по дальности.
signal minute2 : integer range 0 to 9;
когда minute2
достигает 10
в state
b_input
Симуляция завершится из-за ошибки диапазона.
Это увеличение в выборе b_input
должен выглядеть примерно так:
if minute2 = 9 then
minute2 <= 0;
else
minute2 <= minute2 + 1;
end if;
(И есть тонкий намек, что вы должны смоделировать эту вещь.)
Вам нужно использовать один clk
включить с помощью кнопки (кнопок) для увеличения счетчиков.
Способ сделать это: а) отменить деблокирование кнопок, б) обнаружить нарастающий фронт в clk
домен для одного clk
,
Все это сложно, потому что вы используете оба "clkand
часы как часы.
Рекомендация о том, как реализовать де-отскок, обнаружение края и одиночный clk
enable зависит от того, какова ваша тактовая частота.
В качестве альтернативы вы могли бы рассмотреть кучу часов ИЛИ, где вы используете кнопки, чтобы увеличивать вещи и ворота clock
за c_countdown
,
Вы не включили контекстное предложение до объявления сущности:
library ieee;
use ieee.std_logic_1164.all;
Один вопрос, должен ли я использовать Falling_Edge кнопки 3 для ff3, потому что при этом мой код также имел проблемы
Вероятно, если бы у вас были проблемы с острием, у вас также были бы проблемы с острыми углами. Корень всех проблем, вероятно, будет отказов контактов.
И нет, я не защищаю использование падающего края кнопки 3 в этом случае.
Проблема здесь состоит в том, чтобы произвести одиночное включение часов от кнопки 3 (или альтернативно использовать его как часы). Это также нуждается в разоблачении. Продолжительность отскока переключателя или кнопки зависит от нескольких факторов - массы контактов, от того, как сильно они работают, и от того, насколько упругим является рычаг переключателя.
Есть несколько способов избавиться от отказов. Например, вы можете использовать нормально разомкнутую и нормально замкнутую пару контактов для управления защелкой RS, требуя, чтобы два сигнала переключателя или кнопки находились в противоположных двоичных состояниях. Вы можете временно фильтровать (с интервалом в 10 миллисекунд), требующее N стабильных выборок. Это также отфильтровывает метастабильность от перехода из асинхронного домена в синхронный (синхронизированный) домен.
Использовать эквивалент FF3 в качестве разрешения для clk
, вам нужно отменить кнопку и обнаружить передний фронт в домене 'clk'.
Поскольку ваши кнопки почти гарантированно будут однополюсными, вам нужна какая-то временная фильтрация. Если часы для этого связаны с 'clk', вы могли бы просто использовать включение длинного интервала, созданное счетчиком, для последовательной выборки кнопки. Это позволяет использовать дополнительный триггер в clk
домен, чтобы обнаружить его раньше был низким, и теперь он высокий, выход этих ворот используется где FF3
используется сейчас.
Если clk
50 МГц, как вы говорите, устранение неполадок может повлечь за собой Count
счетчик.
Давайте назовем это debounce_en
,
Обратите внимание, что это настройка для сэмплирования через некоторое время после падения края кнопки 3. Старый максимум и один или два последовательных минимума. Это миллисекунды после падения button 3, так что это должно быть безопасно.
50 МГц, разделенная на 4000000, дает clock
бода обратной 12,5 или 8 мс. Это хороший стартовый показатель для временного фильтра для удаления кнопки button 3:
signal debounce_en: std_logic;
signal button3_d0, button3_d1, button3_d2:
begin
...
UNLABELLED1:
process(clk)
begin
if rising_edge(clk) then
debounce_en <= '0'; -- one ping only.
count <= count + 1;
if count = 4000000 then
clock <= not clock;
debounce_en <= '1'; -- for one clk every 4000000
count <= 1;
end if;
end if;
end process;
Мы можем использовать debounce_en
пробовать button3
вместо производства FF3
:
process (clk)
begin
if rising_edge(clk) and debounce_en = '1' then
button3_d0 <= button3;
button3_d1 <= button3_d0;
button3_d2 <= button3_d1;
end if;
button3_enable <= not button3_d1 and button3_d2 and debounce_en;
И причина того, что это сработает, заключается в том, что более чем 8 мс - это достаточно большой интервал для отладки одного из этих ключей. (И если это не добавить еще один этап и:
button3_enable <= not_button3_d1 and not button3_d2 and button3_d3 and debounce_en;
Который не требует "звонка", фиксируется тремя последовательными триггерами (30 мс).
button3_enable будет использоваться вместо FF3
все три (или более) новых сигналов имеют тип std_logic, а триггеры используются в качестве регистра сдвига.
Использование временного фильтра, для которого требуются столь длительные известные значения, может потенциально отфильтровывать короткие импульсы на кнопках, и вам будет трудно нажимать такие быстрые кнопки.
А что касается работы `minute2 в выражении case:
when b_input =>
if button3_enable = '1' then -- count up button
if minute2 = 9 then
minute2 <= 0;
else
minute2 <= minute2 + 1;
end if;
input_led <= '1'; --green led7
end if;
И как вы должны были получить debounce_en
от Count
может быть изменено, если вы измените clock
тактовая частота (я бы соблазнился, чтобы он работал в режиме реального времени сам). Вы не завершили C_COUNTDOWN, но, похоже, в идеале он будет работать с разрешением одной секунды и иметь каскадные счетчики секунд и минут.
Примечания к заявлению DE1 о средствах отмены действий
Я нашел руководство по плате DE1-SoC, и на рисунке 3-14 и в разделе 3.6.1 утверждается, что использование триггерного буфера Шмидта 74HC245 (номинально двунаправленный, несомненно, используется однонаправленно) достаточно для использования кнопок в качестве часов. Это будет зависеть от расположения платы, емкости и индуктивности одной из кнопок, размера подтягивающих резисторов и, если они так скажут... (и, вероятно, это работает, они предложили его в нескольких поколениях плат).), как это влияет на приведенное выше описание?
Ну, вы все равно должны синхронизироваться с clk
и произвести один clk
период включения. Так что не очень.