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
TFT J2 | Nucleo |
---|---|
VCC | 3V3 |
GND | GND |
CS | PB_5(D4) |
Reset | PA_10(D2) Pull Up(100k) |
D/C | PA_8(D7) |
MOSI | PA_7(D11) |
SCK | PA_5(D13) |
LED | LED-100ohm-3V3 |
MISO | PA_6(D12) |
TFT J4 | Nucleo |
---|---|
SD_CS | PA_9 |
SD_MOSI | PB_15 |
SD_MISO | PB_14 |
SD_SCK | PB_13 |
Audio | Nucleo |
---|---|
TIP | PA_4(A2) |
USB con. | Nucleo |
---|---|
GND | GND |
+ | PA_12 |
- | PA_11 |
5V | 5V |
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
- SDFileSystem by Neil Thiessen
- F401RE-USBHost by Norimasa Okamoto
- USBHostGamepad by Yuuichi Akagawa
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(); }