I have ported my old project “pNesX” game console emulator to the nucleo.

Dependencies:   SDFileSystem mbed

Intro

I have ported my old project “pNesX” to the STM32 Nucleo. The pNesX is a NES emulator for the PlayStation that I have created 16 years ago!

Emulation part was almost without change, the sound part was newly added.

Parts

STM32 Nucleo F446RE
QVGA 2.2 TFT SPI (with the SD card slot)
Audio jack(TS or TRS)
USB Connector
Register 100k, 10k, 4.7k, 100
Capacitor 0.01uF, 2.2uF
Breadboard
Wires
Computer Speakers
USB GamePad

Wiring diagram

/media/uploads/beaglescout007/nucleo_ex06_emu.png

TFT J2Nucleo
VCC3V3
GNDGND
CSPB_5(D4)
ResetPA_10(D2) Pull Up(100k)
D/CPA_8(D7)
MOSIPA_7(D11)
SCKPA_5(D13)
LEDLED-100ohm-3V3
MISOPA_6(D12)
TFT J4Nucleo
SD_CSPA_9
SD_MOSIPB_15
SD_MISOPB_14
SD_SCKPB_13
AudioNucleo
TIPPA_4(A2)
USB con.Nucleo
GNDGND
+PA_12
-PA_11
5V5V

https://youtu.be/jL24IjT6LnI

Limitations

  • Since the rest of the RAM is about 50kbyte, maximum capacity of the game ROM is about 50kbyte.
  • The length of the file name up to 32 characters.
  • The number of files in the folder is up to 100.

Used Library

TFT/tft.cpp

Committer:
beaglescout007
Date:
2016-04-03
Revision:
0:3dac1f1bc9e0

File content as of revision 0:3dac1f1bc9e0:

/*===================================================================*/
/*                                                                   */
/*  tft.cpp : TFT(ILI9341) function                                  */
/*                                                                   */
/*  2016/1/20  Racoon                                                */
/*                                                                   */
/*===================================================================*/

#include "mbed.h"
#include "tft.h"

DigitalOut cs(PB_5, PullUp); // TFT chipselect pin
DigitalOut dc(PA_8, PullUp); // TFT data command select pin
DigitalOut rst(PA_10,PullUp); // TFT reset pin

/*-------------------------------------------------------------------*/
/*  Write command                                                    */
/*-------------------------------------------------------------------*/
void write_cmd(uint8_t cmd)
{
    dc = 0;
    spi_write(cmd);
}
  
/*-------------------------------------------------------------------*/
/*  Write data                                                       */
/*-------------------------------------------------------------------*/
void write_data(uint8_t data)
{
    dc = 1;
    spi_write(data);
}

/*-------------------------------------------------------------------*/
/*  TFT reset                                                        */
/*-------------------------------------------------------------------*/
void tft_reset()
{
    wait_ms(200);
    cs = 1;
    dc = 1;
    rst = 1;
    wait_ms(200);
    rst = 0;
    wait_us(10);
    rst = 1;
    wait_ms(120);
    cs = 0;
    wait_ms(10);
        
    write_cmd(0x3A); // Pixel Format    
    write_data(0x55); // 16bit Color
    
    write_cmd(0xB1); // Frame Control    
    write_data(0);
    write_data(0x1f);
        
    write_cmd(0x36); // Memory Access Control 
    write_data(0xE8); // MY MX MV BGR

    write_cmd(0x11); // Sleep Out
    wait_ms(5);
        
    write_cmd(0x29); // Display On    
}

/*-------------------------------------------------------------------*/
/*  Set windows size, start memory write                             */
/*-------------------------------------------------------------------*/
void tft_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
  write_cmd(0x2A); // Column Address Set
  write_data(x0 >> 8);
  write_data(x0);
  write_data(x1 >> 8);
  write_data(x1);

  write_cmd(0x2B); // Page Address Set
  write_data(y0 >> 8);
  write_data(y0);
  write_data(y1 >> 8);
  write_data(y1);

  write_cmd(0x2C); // Memory Write
  
  wait_us(20);
  
  dc = 1;
}

/*-------------------------------------------------------------------*/
/*  Clear screen                                                     */
/*-------------------------------------------------------------------*/
void tft_clear(uint16_t color)
{
    tft_set_window(0, 0, TFT_WIDTH, TFT_HEIGHT);

    for (int i = 0; i < TFT_WIDTH * TFT_HEIGHT; ++i)
    {
        spi_writew(color);
    }    
}

/*-------------------------------------------------------------------*/
/*  Put char                                                         */
/*-------------------------------------------------------------------*/
void tft_put_char(int x, int y, char chr, uint16_t color, uint16_t bgcolor)
{
    if (chr < 0x20 || chr > 0x7f)
    {
        chr = 0x3f;
    }
    else
    {
        chr = (chr < 0x60) ? chr - 0x20 : chr - 0x40;
    }
        
    tft_set_window(x, y, x + 7, y + 6);
    
    for (int dy = 0; dy < 7; ++dy)
    {
        unsigned char img = chrimg[chr][dy];
        for ( int dx = 0; dx < 8; ++dx)
        {
            if (img & 0x80)
            {
                spi_writew(color);
            }
            else
            {
                spi_writew(bgcolor);                
            }
            img <<= 1;
        }
    }    
    
}
 
/*-------------------------------------------------------------------*/
/*  Text out                                                         */
/*-------------------------------------------------------------------*/
void tft_text(int x, int y, char *text, uint16_t color, uint16_t bgcolor)
{
    while (*text != 0)
    {
        tft_put_char(x, y, *text, color, bgcolor);
        x += 8;
        text++;
    }
}

/*-------------------------------------------------------------------*/
/*  Horizontal Line                                                        */
/*-------------------------------------------------------------------*/
void tft_hline(int x1, int y, int x2, uint16_t color)
{
    tft_set_window(x1, y, x2, y);
    
    for (;x1 < x2; ++x1)
    {
        spi_writew(color);
    }
}

/*-------------------------------------------------------------------*/
/*  Vertical Line                                                        */
/*-------------------------------------------------------------------*/
void tft_vline(int x, int y1, int y2, uint16_t color)
{
    tft_set_window(x, y1, x, y2);
    
    for (;y1 < y2; ++y1)
    {
        spi_writew(color);
    }
}

/*-------------------------------------------------------------------*/
/*  Box                                                              */
/*-------------------------------------------------------------------*/
void tft_box(int x1, int y1, int x2, int y2, uint16_t color)
{
    tft_hline(x1, y1, x2, color);
    tft_vline(x1, y1, y2, color);
    tft_vline(x2, y1, y2, color);
    tft_hline(x1, y2, x2, color);
}

/*-------------------------------------------------------------------*/
/*  Box Fill                                                        */
/*-------------------------------------------------------------------*/
void tft_boxfill(int x1, int y1, int x2, int y2, uint16_t color)
{
    tft_set_window(x1, y1, x2, y2);
    
    for (int i = 0; i < (x2 - x1 + 1) * (y2 - y1 + 1); ++i)
    {
        spi_writew(color);
    }
}

/*-------------------------------------------------------------------*/
/*  Draw 4bit BMP                                                    */
/*-------------------------------------------------------------------*/
bool draw_bmp_4bpp(const unsigned char *imgdata, int x, int y)
{
    BITMAPFILEHEADER *bf = (BITMAPFILEHEADER *)imgdata;
    BITMAPINFOHEADER *bi = (BITMAPINFOHEADER *)(imgdata + sizeof(BITMAPFILEHEADER));
       
    if (bi->biBitCount != 4)
    {
        return false;
    }

    unsigned char *pRGBPal = (unsigned char*)imgdata + sizeof(BITMAPFILEHEADER) + bi->biSize;
    unsigned short palette[16];

    for (int i = 0; pRGBPal < imgdata + bf->bfOffBits && i < 16; ++i)
    {
        unsigned short r,g,b;  
        b = *pRGBPal++ >> 3;
        g = *pRGBPal++ >> 2;
        r = *pRGBPal++ >> 3;
        pRGBPal++;
        palette[i] = ((g & 7) << 13) | (b << 8) | (r << 3) | (g >> 3);
    }

    unsigned short HLine[320];
    int linesize = (bi->biWidth / 2 + 3) & 0xfffc;
        
    tft_set_window(x, y, x + bi->biWidth - 1, y + bi->biHeight - 1);
    
    unsigned char *bmp;
    
    for (int y = bi->biHeight - 1; y >= 0; --y)
    {
        bmp = (unsigned char *)imgdata + bf->bfOffBits + y * linesize; 
        
        for (int x = 0; x < bi->biWidth; ++x)
        {
            char pal;
            if (x & 1)
            {
                pal = *bmp & 0xf;
                bmp++;
            }
            else
            {
                pal = *bmp >> 4;
            }
                
            HLine[x] = palette[pal];
        }
    
        HAL_SPI_Transmit(&SpiHandle, (uint8_t *)HLine, bi->biWidth * 2, 100);
    }

    return true;
}
  
/*-------------------------------------------------------------------*/
/*  Initialize TFT                                                   */
/*-------------------------------------------------------------------*/
void tft_init(void)
{
    spi_init();
        
    tft_reset();
}