2014-12-30 4 views
2

Im кодирования змея игру в VHDL, используя DE2-115 FPGA от Altera. Я подключил FPGA к монитору, используя протокол VGA, чтобы показать игру.игра Snake с помощью FPGA в VHDL

У меня есть проблема, чтобы показать змею и переместить его вокруг монитора со всем нажимным дном (ПЛИС имеет 4, вверх/вниз/влево/вправо)

В коде используются SQ_X1 и SQ_Y1, чтобы делать движение каждый момент, когда нижнее дно находится в низком состоянии.

Есть ли какой-либо другой способ сделать движение ? без прессования ни одного дна (с петлей или что-то в этом роде) и до изменение направления с нижним нажатием, чтобы имитировать движение змеи?

ARCHITECTURE behavior OF hw_image_generator IS 
    SIGNAL SQ_X1,SQ_Y1: INTEGER RANGE 0 TO 1688:=400; --initial position X  
    SIGNAL SQ_X2,SQ_Y2: INTEGER RANGE 0 TO 1688:=600; --initial position Y 
    SIGNAL DRAW: STD_LOGIC;           

BEGIN 
    SQ(column,row,SQ_X1,SQ_Y1,DRAW); -- function that print a square (head of snake with SQ variables) 
    PROCESS(disp_ena, row, column, down, up, left, right) 
    BEGIN 
     IF(disp_ena = '1') THEN  --display time 
      IF (column=19 or column=1901 or row=19 or row=1181) THEN -- print the border 
       red <= (OTHERS => '1'); 
       green <= (OTHERS => '0'); 
       blue <= (OTHERS => '0'); 
      ELSE 
       red <= (OTHERS => '0'); 
       green <= (OTHERS => '0'); 
       blue <= (OTHERS => '0'); 
      END IF; 

      IF (DRAW = '1') THEN -- it comes from a package that print a square (head of snake) 
       red <= (OTHERS => '1'); 
       green <= (OTHERS => '1'); 
       blue <= (OTHERS => '1'); 
      END IF; 

      --up, down right and left are input of FPGA (push bottom) 
      --SQs are the current postions of X and Y and the add is to make the movement 
      IF(up='0' AND down='1' AND right='1' AND left='1')THEN 
       IF (SQ_X1-40 > 20) THEN 
        SQ_X1<=SQ_X1-40; 
       END IF;   
      END IF; 

      IF(up='1' AND down='0' AND right='1' AND left='1')THEN 
       IF (SQ_X1+40 < 1180) THEN 
        SQ_X1<=SQ_X1+40; 
       END IF; 
      END IF; 
      IF(tup='1' AND down='1' AND right='0' AND left='1')THEN 
       IF (SQ_Y1+40 < 1900) THEN 
        SQ_Y1<=SQ_Y1+40; 
       END IF; 
      END IF; 
      IF(up='1' AND down='1' AND right='1' AND left='0')THEN 
       IF (SQ_Y1-40 > 20) THEN 
        SQ_Y1<=SQ_Y1-40; 
       END IF; 
      END IF; 
     END IF; 

    END PROCESS; 
END BEHAVIOR; 
+2

У этого дезина, похоже, нет часов !? Как он должен работать без часов? Вы моделировали свой код? – Paebbels

+0

Привет, Паббельс! часы находятся в другом модуле, которые обеспечивают текущую позицию строки и столбца, поэтому в этом модуле не нужны часы. – Beaver

+0

и извините за орфографию Бен Фойгт! Я все еще изучаю английский язык! XD – Beaver

ответ

1

Прежде всего, я рекомендую вам разделить дисплей и функциональность обновления позиции на отдельные процессы, чтобы сделать вещи более ясными.

У вас, похоже, есть в основном все, что вам нужно для процесса отображения, за исключением того, что, как отмечает Паббельс в своем комментарии, это должен быть синхронный (т. Е. Синхронизированный) процесс. Я предполагаю, что ваши часы названы clk.

PROCESS(clk) 
BEGIN 
    IF rising_edge(clk) THEN 
     --display time 
     IF(disp_ena = '1') THEN 
      -- print the border     
      IF (column=19 or column=1901 or row=19 or row=1181) THEN 
       red <= (OTHERS => '1'); 
       green <= (OTHERS => '0'); 
       blue <= (OTHERS => '0'); 
      ELSE 
       red <= (OTHERS => '0'); 
       green <= (OTHERS => '0'); 
       blue <= (OTHERS => '0'); 
      END IF; 

      IF (DRAW = '1') THEN 
       red <= (OTHERS => '1'); 
       green <= (OTHERS => '1'); 
       blue <= (OTHERS => '1'); 
      END IF; 
     END IF; 
    END IF; 
END PROCESS; 

Для обновления позиции, вам нужно две вещи:

  1. государства, которое представляет собой текущее направление змеи, и которые можно воздействовать с помощью кнопок.
  2. Некоторая форма базы времени для обновления позиции.

Первая точка проста в применении. Сначала объявите сигнал состояния направления.

type direction_t is (DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT); 
signal direction : direction_t; 

Затем создайте процесс состояния направления.

process(clk) begin 
    if rising_edge(clk) then 
     if rst = '1' then 
      direction <= DIR_UP; 
     else 
      if up = '0' then 
       direction <= DIR_UP; 
      elsif down = '0' then 
       direction <= DIR_DOWN; 
      elsif left = '0' then 
       direction <= DIR_LEFT; 
      elsif right = '0' then 
       direction <= DIR_RIGHT; 
      end if; 
     end if; 
    end if; 
end process; 

Я сделал предположение здесь, что у вас есть сигнал сброса доступного по имени rst и что этот сброс является синхронным активным высоким. Приспособьте на свой вкус.

Кроме того, обратите внимание, что, хотя и не является строго необходимым для этого конкретного примера, как общие лучшие практики:

  • up, down, left и right должны быть надлежащим образом повторно синхронизированы, если они приходят из другого домена часы , или полностью асинхронны (как кнопки).
  • up, down, left и right следует отменить, если они поступают непосредственно с кнопок или других «упругих» механических устройств.

Наконец, вам необходимо создать сигнал разрешения синхронизации, который периодически подает импульсы и служит временной базой для обновления положения. Простым способом создания такого сигнала обычно является использование счетчика для разделения ваших часов, но если часы слишком быстрые, как, вероятно, ваш случай, если это видеочасы, вы получаете слишком большой счетчик, что отрицательно влияет на тайминги. Существует различные решения, но в вашем случае, вы, вероятно, можете использовать ваши сигналы row и column генерировать импульс каждый раз при достижении конца видеокадра, что-то вроде этого:

signal update_ce : std_logic; 

-- (...) 

process(clk) begin 
    if rising_edge(clk) then 
     -- adapt the value to your taste 
     if disp_ena = '1' and column = 1901 and row = 1181 then 
      update_ce <= '1'; 
     else 
      update_ce <= '0'; 
     end if; 
    end if; 
end process; 

Вы могли бы иметь снова делить частоту этого сигнала с помощью счетчика, если обновление происходит слишком быстро, но по крайней мере ваш счетчик не должен будет рассчитывать миллионы. Учитывая этот сигнал, вы можете теперь обновить свою позицию следующим образом:

process(clk) 
begin 
    if rising_edge(clk) then 
     if rst = '1' then 
      -- adapt starting position to your taste 
      SQ_X1<= 100; 
      SQ_Y1<= 100; 
     elsif update_ce = '1' then 
      case direction is 
      when DIR_UP => 
       if SQ_X1 - 1 > 20 then 
        SQ_X1 <= SQ_X1 - 1; 
       end if; 

      when DIR_DOWN => 
       if SQ_X1 + 1 < 1180 then 
        SQ_X1 <= SQ_X1 + 1; 
       end if; 

      when DIR_LEFT => 
       if SQ_Y1 - 1 > 20 then 
        SQ_Y1 <= SQ_Y1 - 1; 
       end if; 

      when others => 
      -- DIR_RIGHT 
       if SQ_Y1 - 1 > 20 then 
        SQ_Y1 <= SQ_Y1 - 1; 
       end if;      
      end case; 
     end if; 
    end if; 
end process; 
+0

Поскольку не имеет значения, какой именно тактовый цикл включен, вы можете заменить весь процесс параллельным назначением 'update_ce <= disp_ena = '1' и column = 1901 и row = 1181;' –

+0

@ ben-voigt. Вы абсолютно правильно. Я бы сделал это в синхронном процессе в такой ситуации, потому что: 1) он помогает улучшить синхронизацию, разбивая логику процесса обновления на логику генерации часов, 2) она стоит всего один триггер, и 3) Задержка тактового цикла не является проблемой. Спасибо за ваш комментарий. Это заставило меня понять, что на самом деле я не использовал update_ce. Просто исправил это. :) –

+0

У меня есть другой модуль, который обеспечивает текущую строку и столбец каждые часы, поэтому в этом случае не требуется. Что касается мнения Филиппа, это было правильное решение, заранее спасибо за идею! Я использую часы для движения с правильным делителем, добавляя движение и назначая все направления с помощью четырех B-U-T-T-O-N-S XD, поэтому в этом случае он делает то, что я хочу. просто еще один вопрос (у меня есть случайная позиция X и Y, чтобы генерировать пищу змеи, чтобы сделать больше), как я мог сделать движение змей? Я имею в виду, когда змея поворачивается вправо/влево/вверх/вниз, весь хвост следует за движением головы ... Спасибо! – Beaver

Смежные вопросы