1. Objetivos
  2. Requerimientos
  3. Procedimiento
    1. Descripción de Hardware.
    2. Qsys.
    3. Nios II.
    4. UCOS II.
    5. Secuencia de Sprite.
    6. Sintesis de Audio.
  4. Descargas
  5. Glosario
  6. Otros Resultados.
  7. Ejemplo de Sprites.

 

 

Objetivos:

  • Diseñar una plantilla general para el diseño de Juegos Retro Usando Verilog HDL.
  • Aprender a Diseñar Escenarios para Juegos Retro.
  • Hacer animaciones usando Sprites sobre la pantalla VGA usando Verilog HDL.
  • Hacer sintesis de sonido para juegos retro usando Divisores de frecuencia Verilog HDL.
  • Implementar Nios II para facilitar la logica y programación de un Juego Retro.
  • Implementar Acceleradores Hardware.
  • Implementar un sistema operativo de Tiempo Real(UCOS-II).
  • Implementar Controles SNES.
  • Dar la base inicial para hacer un Juegro Retro Sobre la DE0-NANO u otra FPGA.
 

Requerimientos: 

 
  1. VGA Retro Game Introduction - DE0-NANO

  2. SNES Controller Module - DE0-NANO-SOC

 

Procedimiento Base:

 Este procedimiento tiene como principal objetivo dar una serie de pasos para crear una plantilla general con la cual se puedan diseñar juegos retro usando Verilog HDL, Nios II, y C/C++. Usted es responsable de analizar y estudiar serie de pasos dados en este manual, para de esta manera absorber por medio de la practica las bases necesarias para diseñar juegos retro utilizando esta metodologia.
 
Como resultado de esta practica usted obtendra lo que se ve en el siguiente video
 
Resultados esperados
Tambien es importante resaltar que en base a esta practica algunas personas han decidido hacer sus propias versiones de video juegos retro usando diferentes sprites, sonidos y animaciones, las cuales usted podra ver en  Otros Resultados.
 
Nota: Recuerde que antes de iniciar esta serie de pasos usted debio haber entendido a cabalidad el siguiente manual https://fpgalover.com/projects/simple-video-game, ya que sin esto le sera mas dificil de enteder lo que se explica en este paso a paso. En adición, en ese manual aprendera a genera los sprites necesarios para esta practica.

  • Descargue la Download this file (generado_ram_imag (1).m)plantilla base con la cual se trabajara este laboratorio/pfinal.
  • Guardela en un lugar cercano a la Ruta "C:\" sin espacios, de lo contrario tendra dolores de cabeza.
    • Recuerde manejar todo como administrador tambien.
  • Debe tener montado el circuito de VGA ya montado a la FPGA.
  • Abra la plantilla, en ella vera que estan instanciados los modulos del laboratorio 7 de sincronizacion de video.
  • Ahora descargue el siguiente Download this file (generado_ram_imag (1).m)modulo, el cual es el encargado de tomar los diferentes botones de los controles de SNES.
    Agregue el modulo en esta plantilla(copielo) e Instancie el modulo dos veces de la siguiente manera:

    En este caso los dos controles de SNES se conectaran al GPIO_B, a diferentes pines como usted aprecia en la descripcion de hardware.
  • Ahora sintetice co compile el diseño, si todo esta conectado correctamente, usted podra ver en los LED[7:0] los botones menos significaivos del controls de SNES1 y 2, si pulsa el KEY[0] vera el control 2, si lo deja sin pulsar vera los botones del control1.
    Nota: recuerde que el si mira debajo de la tarjeta el pin 1 es el que tiene la soldadura cuadrada, tenga cuidado con esto al hacer las conexiones.
  • Ahora descargue el script en Matlab "Download this file (generado_ram_imag (1).m)generado_ram_imag_colors" y abralo para que generemos el .v para las siguientes imagenes, utilice como transparent "12'h0f0" o verde puro
  • Una vez generado los cuatro archivos Verilog para cada render, agreguelos e instancielos de la siguiente manera en la plantilla principal.



  • Si lo sintetizo bien, deberia ver cuatro gokus diferentes en la pantalla
  • Ahora descargue el siguiente Download this file (generado_ram_imag (1).m)modulo(Divisor de frecuencia) e instancielo en la plantilla
     
     
  • Ahora vamos a modificar el codigo para agregar una descripcion del procesador Nios II, para esto usted va a descargar el siguiente Download this file (generado_ram_imag (1).m)archivo
  • Una vez descargado, debe copiarlo y argegarlo a la plantilla como cualquier otro modulo.
  • Ahora abra el Qsys y con este abra el archivo "mi_nios.qsys", una vez abierto vaya a la plantilla principal, si se da cuenta cada imagen agregada tiene un valor fijo para la escala, el on_off, la posicion en x y y, la gracia de agregar el nios es modificar estos valores desde software de tal manera que sea mas facil para usted hacer modificaciones sobre el juego.
  • Para esto vamos a la ventana del Qsys y agregue un PIO de 4 bits de salida que usted llamara scale1 y conectelo respectivamente como lo hice en el lab6, al clk, al reset, el data_master de la cpu y exporte los pines por medio de Conduit, como se puede apreciar en la siguiente imagen:



  • Haga esto mismo 3 veces mas, llame el puerto scale2, scale3, y scale4, luego oprima en  System-> Assign Base Addresses



  • Ahora agregue otros 4 puertos de salida 1 bit cada uno llamados on_off1, on_off2, on_off3, on_off4



  • Ahora agregue otros 4 puertos de salida de 11 bit cada uno llamados goku_x1, goku_x2, goku_x3, goku_x4



  • Ahora agregue otros 4 puertos de salida de 11 bit cada uno llamados goku_y1, goku_y2, goku_y3, goku_y4



  • Ahora agregue otros 2 puertos de entrada de 12 bit cada uno llamados SNES_control1, y SNES_control2.


  • Ahora agregue un puerto de salida de 32 bits que se llame Nota.


  • Ahora guarde el Qsys y Genere, una vez generado debe instanciar el procesador Nios de la siguiente manera en la plantilla principal
  • Una vez se tenga todo instanciado y unido correctamente, compile todo el hardware para generar el .sof.
    Si tiene problemas puede bajar la solucion hasta el momento: Solucion hasta el momento(recuerde guardarla en una ruta cercana a C:\ sin espacios)
    Nota: La solucion tiene dos canales de Audio, usted puede usar uno para efectos de sonido del juego y otro para el sonido de fondo, uno esta conectado a GPIO_B[33] y el otro a GPIO_B[32].


  • Ahora abra el Nios II Eclipse como administrador y cree un proyecto en base a la descripcion de hardware que acaba de generar y llame este proyecto "Ejemplito", creelo de la siguiente manera:



  • Es muy importante que seleccione "Hello MicroC/OS-II", vamos a trabajar con multitasking para hacer el juego y el MicroC/OS-II es un Sistema operativo que se pondra en el procesador de tiempo real para poder realizar multitasking.
  • Ahora programe el .sof y compile el sofwtare del procesador para probar que funciona de la siguiene manera:


  • Compilado correctamente el software debera aparecer un nuevo archivo en la carpeta del proyecto eclipse "ejemplito" llamado ejemplito.elf


  • ahora de click derecho sobre la carpeta y programe el procesador Nios II:



  • Una vez programado deberia aparecer en la consola lo siguiente 


  • Ahora procedamos analizar el codigo C que esta como base del proyecto:


  • Para crear multiasking usando el procesador Nios II, estamos usando el MicroC/OS-II, en el main siempre se deben crear las tareas de la forma como se muestra anteriormente y luego se ejecuta el sistema operativo.
  • Cada tarea debe tener prioridad y el OS le dara mas tiempo entre mayor prioridad tenga, en el caso de que las dos tengan igual numero de prioridad estas seran igual de importantes, la estructura de las tareas se explica acontinuacion:


  • Ahora modifiquemos el codigo para prender y apagar las imagenes que estan en la FPGA usando el procesador Nios II que esta enlazado por medio de puertos a cada una de
    estas.
    #include <stdio.h>
    #include "includes.h"
    
    #include <system.h>
    #include "io.h"
    
    /* Definition of Task Stacks */
    #define   TASK_STACKSIZE       2048
    OS_STK    task1_stk[TASK_STACKSIZE];
    OS_STK    task2_stk[TASK_STACKSIZE];
    
    /* Definition of Task Priorities */
    
    #define TASK1_PRIORITY      1
    #define TASK2_PRIORITY      2
    
    
    void set_pos_image(int basex, int basey,int x, int y){
     IOWR(basex,0,x);
     IOWR(basey,0,y);
    }
    
    void init_game(void){
     // INICIALIZO LA ESCALA DE CADA IMAGEN
     IOWR(SCALE1_BASE,0,0);
     IOWR(SCALE2_BASE,0,0);
     IOWR(SCALE3_BASE,0,0);
     IOWR(SCALE4_BASE,0,0);
     //APAGAO CADA IMAGEN AL INICIO DEL JUEGO A MENOS DE QUE QUIERA MOSTRARLAS
     IOWR(ON_OFF1_BASE,0,0);
     IOWR(ON_OFF2_BASE,0,0);
     IOWR(ON_OFF3_BASE,0,0);
     IOWR(ON_OFF4_BASE,0,0);
     //INICIALIZO LA POSICION DE LAS IMAGENES
     set_pos_image(GOKU_X1_BASE,GOKU_Y1_BASE,0, 0);
     set_pos_image(GOKU_X2_BASE,GOKU_Y2_BASE,30, 0);
     set_pos_image(GOKU_X3_BASE,GOKU_Y3_BASE,60, 0);
     set_pos_image(GOKU_X4_BASE,GOKU_Y4_BASE,90, 0);
    }
    
    /* Prints "Hello World" and sleeps for three seconds */
    void task1(void* pdata)
    {
      unsigned char toggle_image1=0;
      while (1)
      { 
       toggle_image1 =toggle_image1^ 1;
    
       IOWR(ON_OFF1_BASE,0,toggle_image1);
    
       //tiempo de retrazo
          OSTimeDlyHMSM(0, 0, 3, 0);
      }
    }
    /* Prints "Hello World" and sleeps for three seconds */
    void task2(void* pdata)
    {
      unsigned char toggle_image2=0;
      while (1)
      { 
       toggle_image2 =toggle_image2^ 1;
    
       IOWR(ON_OFF2_BASE,0,toggle_image2);
    
        OSTimeDlyHMSM(0, 0, 5, 0);
      }
    }
    
    
    /* The main function creates two task and starts multi-tasking */
    int main(void)
    {
      
     //INICIALIZO JUEGO ANTES DE LANZARLO
     init_game();
    
      OSTaskCreateExt(task1,
                      NULL,
                      (void *)&task1_stk[TASK_STACKSIZE-1],
                      TASK1_PRIORITY,
                      TASK1_PRIORITY,
                      task1_stk,
                      TASK_STACKSIZE,
                      NULL,
                      0);
                  
                   
      OSTaskCreateExt(task2,
                      NULL,
                      (void *)&task2_stk[TASK_STACKSIZE-1],
                      TASK2_PRIORITY,
                      TASK2_PRIORITY,
                      task2_stk,
                      TASK_STACKSIZE,
                      NULL,
                      0);
      OSStart();
      return 0;
    }
    

 

  • Programe y analice el anterior codigo, se dara cuenta que la tarea 1 apaga y prende cada 3 segundos la imagen 1 de goku, y la tarea 2 prende y apaga la imagen de goku cada 5 segundos.(Modifiquelo apra que sea cada 10 ms)
  • Ahora modifiquemos las tareas para que hagan lo siguiente
    void task1(void* pdata)
    {
      unsigned char animation=0;
      // inicializo todas las imagenes en el mismo punto
     set_pos_image(GOKU_X1_BASE,GOKU_Y1_BASE,0, 0);
     set_pos_image(GOKU_X2_BASE,GOKU_Y2_BASE,0, 0);
     set_pos_image(GOKU_X3_BASE,GOKU_Y3_BASE,0, 0);
     set_pos_image(GOKU_X4_BASE,GOKU_Y4_BASE,0, 0);
      while (1)
      { 
       animation++;
       if(animation>3){animation=0;}
       switch(animation){
       case 0:  IOWR(ON_OFF1_BASE,0,1);
       IOWR(ON_OFF2_BASE,0,0);
       IOWR(ON_OFF3_BASE,0,0);
       IOWR(ON_OFF4_BASE,0,0);
          break;
       case  1:    IOWR(ON_OFF1_BASE,0,0);
       IOWR(ON_OFF2_BASE,0,1);
       IOWR(ON_OFF3_BASE,0,0);
       IOWR(ON_OFF4_BASE,0,0);
          break;
       case 2:    IOWR(ON_OFF1_BASE,0,0);
       IOWR(ON_OFF2_BASE,0,0);
       IOWR(ON_OFF3_BASE,0,1);
       IOWR(ON_OFF4_BASE,0,0);
          break;
       case 3:    IOWR(ON_OFF1_BASE,0,0);
       IOWR(ON_OFF2_BASE,0,0);
       IOWR(ON_OFF3_BASE,0,0);
       IOWR(ON_OFF4_BASE,0,1);
          break;
       }
    
    
    
       //tiempo de retrazo
          OSTimeDlyHMSM(0, 0, 0, 100);
      }
    }
    
    void task2(void* pdata)
    {
      unsigned int pos_x=0,pos_y=0;
      while (1)
      {
       pos_x++;
       pos_y++;
       set_pos_image(GOKU_X1_BASE,GOKU_Y1_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X2_BASE,GOKU_Y2_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X3_BASE,GOKU_Y3_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X4_BASE,GOKU_Y4_BASE,pos_x, pos_y);
    
        OSTimeDlyHMSM(0, 0, 0, 70);
      }
    }
  • Programe y analice el codigo, si se da cuenta una tarea esta encargada de animar a Goku y la otra tarea esta encargada de mover al goku.
  • Ahora modifiquemos el prorgama para que goku se mueva con las direcciones del control 1 usando el task2
    void task2(void* pdata)
    {
      unsigned int pos_x=0,pos_y=0;
      unsigned short snes1_control=0;
      unsigned char buttons_control1[12];
      while (1)
      {
    
       snes1_control=IORD(SNES_CONTROL1_BASE,0);
       buttons_control1[0]=snes1_control & 1;// B
       buttons_control1[1]=(snes1_control & 2)>>1;// Y
       buttons_control1[2]=(snes1_control & 4)>>2;// Start
       buttons_control1[3]=(snes1_control & 8)>>3;// select
       buttons_control1[4]=(snes1_control & 16)>>4;//UP
       buttons_control1[5]=(snes1_control & 32)>>5;//down
       buttons_control1[6]=(snes1_control & 64)>>6;//left
       buttons_control1[7]=(snes1_control & 128)>>7;//right
       buttons_control1[8]=(snes1_control & 256)>>8;//A
       buttons_control1[9]=(snes1_control & 512)>>9;//X
       buttons_control1[10]=(snes1_control & 1024)>>10;//L
       buttons_control1[11]=(snes1_control & 2048)>>11;//R
    
       if(buttons_control1[4]==0){
       pos_y=pos_y-10;
       }
       else if(buttons_control1[5]==0){
       pos_y=pos_y+10;
       }
    
       if(buttons_control1[6]==0){
          pos_x=pos_x-10;
       }
       else if(buttons_control1[7]==0){
       pos_x=pos_x+10;
       }
    
       set_pos_image(GOKU_X1_BASE,GOKU_Y1_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X2_BASE,GOKU_Y2_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X3_BASE,GOKU_Y3_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X4_BASE,GOKU_Y4_BASE,pos_x, pos_y);
    
        OSTimeDlyHMSM(0, 0, 0, 70);
      }
    }
  • Si modifica bien la tarea 2 vera a goku moverse usando las flechas del control 1.
  • Ahora modifique el codigo para lo siguiente:
    #include <stdio.h>
    #include "includes.h"
    
    #include <system.h>
    #include "io.h"
    
    /* Definition of Task Stacks */
    #define   TASK_STACKSIZE       2048
    OS_STK    task1_stk[TASK_STACKSIZE];
    OS_STK    task2_stk[TASK_STACKSIZE];
    OS_STK    task3_stk[TASK_STACKSIZE];
    
    /* Definition of Task Priorities */
    
    #define TASK1_PRIORITY      1
    #define TASK2_PRIORITY      2
    #define TASK3_PRIORITY      3
    
    
    void set_pos_image(int basex, int basey,int x, int y){// FUNCION PARA POSICIONAR IMAGENES
     IOWR(basex,0,x);
     IOWR(basey,0,y);
    }
    
    void init_game(void){// FUNCION PARA INICIALIZAR JUEGO
     // INICIALIZO LA ESCALA DE CADA IMAGEN
     IOWR(SCALE1_BASE,0,0);
     IOWR(SCALE2_BASE,0,0);
     IOWR(SCALE2_BASE,0,0);
     IOWR(SCALE2_BASE,0,0);
     //APAGAO CADA IMAGEN AL INICIO DEL JUEGO A MENOS DE QUE QUIERA MOSTRARLAS
     IOWR(ON_OFF1_BASE,0,0);
     IOWR(ON_OFF2_BASE,0,0);
     IOWR(ON_OFF3_BASE,0,0);
     IOWR(ON_OFF4_BASE,0,0);
     //INICIALIZO LA POSICION DE LAS IMAGENES
     set_pos_image(GOKU_X1_BASE,GOKU_Y1_BASE,0, 0);
     set_pos_image(GOKU_X2_BASE,GOKU_Y2_BASE,30, 0);
     set_pos_image(GOKU_X3_BASE,GOKU_Y3_BASE,60, 0);
     set_pos_image(GOKU_X4_BASE,GOKU_Y4_BASE,90, 0);
    }
    
    
    // VARIABLES GLOBALES DE BOTONES
    unsigned char buttons_control1[12];
    unsigned char buttons_control2[12];
    
    
    void task1(void* pdata)// TASK PARA ANIMAR A goku
    {
      unsigned char animation=0;
      // inicializo todas las imagenes en el mismo punto
     set_pos_image(GOKU_X1_BASE,GOKU_Y1_BASE,0, 0);
     set_pos_image(GOKU_X2_BASE,GOKU_Y2_BASE,0, 0);
     set_pos_image(GOKU_X3_BASE,GOKU_Y3_BASE,0, 0);
     set_pos_image(GOKU_X4_BASE,GOKU_Y4_BASE,0, 0);
      while (1)
      { 
       animation++;
       if(animation>3){animation=0;}
       switch(animation){
       case 0:  IOWR(ON_OFF1_BASE,0,1);
       IOWR(ON_OFF2_BASE,0,0);
       IOWR(ON_OFF3_BASE,0,0);
       IOWR(ON_OFF4_BASE,0,0);
          break;
       case  1:    IOWR(ON_OFF1_BASE,0,0);
       IOWR(ON_OFF2_BASE,0,1);
       IOWR(ON_OFF3_BASE,0,0);
       IOWR(ON_OFF4_BASE,0,0);
          break;
       case 2:    IOWR(ON_OFF1_BASE,0,0);
       IOWR(ON_OFF2_BASE,0,0);
       IOWR(ON_OFF3_BASE,0,1);
       IOWR(ON_OFF4_BASE,0,0);
          break;
       case 3:    IOWR(ON_OFF1_BASE,0,0);
       IOWR(ON_OFF2_BASE,0,0);
       IOWR(ON_OFF3_BASE,0,0);
       IOWR(ON_OFF4_BASE,0,1);
          break;
       }
    
    
    
       //tiempo de retrazo
          OSTimeDlyHMSM(0, 0, 0, 100);
      }
    }
    
    
    void task2(void* pdata)// TASK PARA ADQUIRIR BOTONES
    {
      unsigned short snes1_control=0;
    
      while (1)
      {
    
       snes1_control=IORD(SNES_CONTROL1_BASE,0);
       buttons_control1[0]=snes1_control & 1;// B
       buttons_control1[1]=(snes1_control & 2)>>1;// Y
       buttons_control1[2]=(snes1_control & 4)>>2;// Start
       buttons_control1[3]=(snes1_control & 8)>>3;// select
       buttons_control1[4]=(snes1_control & 16)>>4;//UP
       buttons_control1[5]=(snes1_control & 32)>>5;//down
       buttons_control1[6]=(snes1_control & 64)>>6;//left
       buttons_control1[7]=(snes1_control & 128)>>7;//right
       buttons_control1[8]=(snes1_control & 256)>>8;//A
       buttons_control1[9]=(snes1_control & 512)>>9;//X
       buttons_control1[10]=(snes1_control & 1024)>>10;//L
       buttons_control1[11]=(snes1_control & 2048)>>11;//R
       snes1_control=IORD(SNES_CONTROL2_BASE,0);
       buttons_control2[0]=snes1_control & 1;// B
       buttons_control2[1]=(snes1_control & 2)>>1;// Y
       buttons_control2[2]=(snes1_control & 4)>>2;// Start
       buttons_control2[3]=(snes1_control & 8)>>3;// select
       buttons_control2[4]=(snes1_control & 16)>>4;//UP
       buttons_control2[5]=(snes1_control & 32)>>5;//down
       buttons_control2[6]=(snes1_control & 64)>>6;//left
       buttons_control2[7]=(snes1_control & 128)>>7;//right
       buttons_control2[8]=(snes1_control & 256)>>8;//A
       buttons_control2[9]=(snes1_control & 512)>>9;//X
       buttons_control2[10]=(snes1_control & 1024)>>10;//L
       buttons_control2[11]=(snes1_control & 2048)>>11;//R
    
        OSTimeDlyHMSM(0, 0, 0, 70);
      }
    }
    
    
    void task3(void* pdata)// TASK PARA MOVER A GOKU
    {
      unsigned int pos_x=0,pos_y=0;
    
      while (1)
      {
       if(buttons_control1[4]==0){
          pos_y=pos_y-10;
       }
       else if(buttons_control1[5]==0){
       pos_y=pos_y+10;
       }
    
       if(buttons_control1[6]==0){
       pos_x=pos_x-10;
       }
       else if(buttons_control1[7]==0){
       pos_x=pos_x+10;
       }
    
       set_pos_image(GOKU_X1_BASE,GOKU_Y1_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X2_BASE,GOKU_Y2_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X3_BASE,GOKU_Y3_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X4_BASE,GOKU_Y4_BASE,pos_x, pos_y);
    
    
    
        OSTimeDlyHMSM(0, 0, 0, 70);
     }
    }
    
    /* The main function creates two task and starts multi-tasking */
    int main(void)
    {
      
     //INICIALIZO JUEGO ANTES DE LANZARLO
     init_game();
    
      OSTaskCreateExt(task1,
                      NULL,
                      (void *)&task1_stk[TASK_STACKSIZE-1],
                      TASK1_PRIORITY,
                      TASK1_PRIORITY,
                      task1_stk,
                      TASK_STACKSIZE,
                      NULL,
                      0);
                  
                   
      OSTaskCreateExt(task2,
                      NULL,
                      (void *)&task2_stk[TASK_STACKSIZE-1],
                      TASK2_PRIORITY,
                      TASK2_PRIORITY,
                      task2_stk,
                      TASK_STACKSIZE,
                      NULL,
                      0);
    
      OSTaskCreateExt(task3,
                        NULL,
                        (void *)&task3_stk[TASK_STACKSIZE-1],
                        TASK3_PRIORITY,
                        TASK3_PRIORITY,
                        task3_stk,
                        TASK_STACKSIZE,
                        NULL,
                        0);
    
      OSStart();
      return 0;
    }
  • Si analiza y ejecuta el codigo anterior, se dara cuenta que existen ahora tres tasks, uno para animar a goku, el otro para adquirir los botones de los dos controles de SNES, y el otro para mover a goku de acuerdo al control 1 del snes.
    //Ahora agreguemos un task mas para el audio del video juego
    void task4(void* pdata)// TASK PARA GENERAR AUDIO
    {
    
     unsigned int nota_div_freq[32]={56817,56817,47777,56817,42565,56817,37920,42565,47777,47777,37920,47777,31887,47777,37920,47777,63775,63775,50619,63775,47777,63775,42565,47777,71585,71585,56817,71585,50619,71585,47777,50619};
     unsigned char count=0;
     //init div_freq
     IOWR(NOTA_BASE,0,0);
      while (1)
      {
       count++;
       if(count>31){
       count=0;
       }
        IOWR(NOTA_BASE,0,nota_div_freq[count]);
    
          
      if(buttons_control1[0]==0){// si se oprime B
    		   OSTimeDlyHMSM(0, 0, 0, 170/4);
      }
      else if(buttons_control1[1]==0){// si se oprime Y
    			   OSTimeDlyHMSM(0, 0, 0, 170/2);
       }
       else{// si no se oprime nada
    		   OSTimeDlyHMSM(0, 0, 0, 170);
       }
     }
    }
  • Si agrego bien el task4, debe ahora sonar en paralelo una pequeña cancion repetitivamente, recuerde que el puerto nota[31:0] estaconectado por meido del Nios al divisor de frecuencia, con el cual usted puede variar para genear diferentes frecuencias por lo cual puede generar canciones o tonos diferentes.
    Recuerde la formula del divisor de frecuencia IN= (50Mhz/(2*fout))-1
  • El codigo completo en C esta en el Download this file (generado_ram_imag (1).m)siguiente link.
  • Modifique el task3, para que se vea como el siguiente y analice lo que sucede.
    void task3(void* pdata)// TASK PARA MOVER A GOKU
    {
      unsigned int pos_x=0,pos_y=0;
    
      while (1)
      {
       if(buttons_control1[4]==0){
       pos_y=pos_y-10;
       }
       else if(buttons_control1[5]==0){
       pos_y=pos_y+10;
       }
    
       if(buttons_control1[6]==0){
       pos_x=pos_x-10;
       }
       else if(buttons_control1[7]==0){
       pos_x=pos_x+10;
       }
            if (buttons_control1[8]==0)
       {
       int i;
       for(i=0;i<4;i++){
     IOWR(SCALE1_BASE,0,i);
     IOWR(SCALE2_BASE,0,i);
     IOWR(SCALE3_BASE,0,i);
     IOWR(SCALE4_BASE,0,i);
     OSTimeDlyHMSM(0, 0, 0, 10);
       }
       IOWR(SCALE1_BASE,0,0);
       IOWR(SCALE2_BASE,0,0);
       IOWR(SCALE3_BASE,0,0);
       IOWR(SCALE4_BASE,0,0);
       }
                if(buttons_control1[0]==0){
       int i;
       for(i=0;i<10;i++){
          pos_x=pos_x+10;
         set_pos_image(GOKU_X1_BASE,GOKU_Y1_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X2_BASE,GOKU_Y2_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X3_BASE,GOKU_Y3_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X4_BASE,GOKU_Y4_BASE,pos_x, pos_y);
       }
       }
       if(buttons_control1[1]==0){
       int i;
       for(i=0;i<10;i++){
       pos_x=pos_x-10;
       set_pos_image(GOKU_X1_BASE,GOKU_Y1_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X2_BASE,GOKU_Y2_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X3_BASE,GOKU_Y3_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X4_BASE,GOKU_Y4_BASE,pos_x, pos_y);
    
       }
       }
    
    
    
       set_pos_image(GOKU_X1_BASE,GOKU_Y1_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X2_BASE,GOKU_Y2_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X3_BASE,GOKU_Y3_BASE,pos_x, pos_y);
        set_pos_image(GOKU_X4_BASE,GOKU_Y4_BASE,pos_x, pos_y);
    
    
        OSTimeDlyHMSM(0, 0, 0, 70);
     }
    }

 

Resultados:


 
Enhorabuena!! usted tiene la base para empezar ha diseñar su propio juego Retro!
 
Nota: cuando modifique el hardware para agregar las nuevas imagenes y los puertos nuevos al nios II debe generar de nuevo el hw, como hizo cambios en el hw cuando vaya de nuevo al eclipse, recuerde hacer "clean en las dos carpetas" y luego generar el BSP en la segunda carpeta, depues si construir el software, de lo contrario el proyecto Eclipse no tomara los cambios realizados en el hw y vera todo como error.
Nota: La solucion tiene dos canales de Audio, usted puede usar uno para efectos de sonido del juego y otro para el sonido de fondo, uno esta conectado a GPIO_B[33] y el otro a GPIO_B[32], el pin GPIO_B[33] usa los Beep, Beep2 y el GPIO_B[32] usa el Beep3 y Beep4.
Hay cuatro canales de audio en total enviados al canal estereo de salida de la FPGA.
Beep==> Canal 1 ==> L
Beep2==> Canal 2 ==>L
Beep3==> Canal 3  ==>R
Beep4==> Canal 4 ==>R
 
El siguiente Download this file (generado_ram_imag (1).m)codigo contiene la cancion de mario cuandop se oprime la tecla A y la de tetris cuando se oprime la tecla X, usando la funcion Beep() de microsoft, si usted busca Beep() songs C/C++ encontrara varias canciones.
 
 
 
Ref1: 
http://en.wikipedia.org/wiki/Parallax_scrolling
 
Ref2:
youtube.com/watch?v=ltuRuGM271Q
 
 

Descargas

 
Todos los archivos necesarios para este proyecto puede encontrarlos en:
 
 

 Glosario:

 

Aqui pueden encontrar muchos SPRITES : http://spritedatabase.net/ ó http://www.spriters-resource.com/snes/ miren este http://spritedatabase.net/file/8612
 
Editor de Sprites :http://www.xnview.com/en/xnview/#downloads
Download this file (generado_ram_imag (1).m)Manual pare redimensionar sprites correctamente.
 
Recomendaciones: Si el la imagen es grande, usen la version "Download this file (generado_ram_imag (1).m)generado_ram_imag.", si no es muy grande usen "Download this file (generado_ram_imag (1).m)generado_ram_imag_colors_v2", y si es grande "Download this file (generado_ram_imag (1).m)generado_ram_imag_colors"
Para animaciones usen "Download this file (generado_ram_imag (1).m)generado_ram_imag_colors" ó usen el visto en https://fpgalover.com/projects/simple-video-game.
 
Ejemplo de crear un arbol con fondos y texturas simples: 
 

Otros Resultados

 
 
 
 
 
 
 

Sprites Ejemplos (https://spritedatabase.net/)