From ae48e23fb7e680494c84feffb23572b49b6f1ee9 Mon Sep 17 00:00:00 2001 From: Tobias Gunkel Date: Sun, 10 Aug 2025 17:10:26 +0200 Subject: [PATCH] convert to platformio and clean up --- .gitignore | 2 + CMakeLists.txt | 53 -- inc/gbcolors.h | 17 +- pico_sdk_import.cmake | 62 -- platformio.ini | 34 + src/.clang-format | 45 ++ src/card_loader.cpp | 263 +++++++ src/core.h | 34 + src/lcd_core.cpp | 142 ++++ src/main.c | 902 --------------------- src/main.cpp | 488 ++++++++++++ src/mk_ili9225.c | 1297 ------------------------------- src/tft-espi-config/tft_setup.h | 164 ++++ 13 files changed, 1184 insertions(+), 2319 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100644 pico_sdk_import.cmake create mode 100644 platformio.ini create mode 100644 src/.clang-format create mode 100644 src/card_loader.cpp create mode 100644 src/core.h create mode 100644 src/lcd_core.cpp delete mode 100644 src/main.c create mode 100644 src/main.cpp delete mode 100644 src/mk_ili9225.c create mode 100644 src/tft-espi-config/tft_setup.h diff --git a/.gitignore b/.gitignore index 754f7df..1a07a50 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ build uf2 +.pio +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index bee5b41..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,53 +0,0 @@ -cmake_minimum_required(VERSION 3.13...3.23) - -include(pico_sdk_import.cmake) - -project(RP2040_GB C CXX ASM) -set(CMAKE_C_STANDARD 11) -set(CMAKE_CXX_STANDARD 17) - -pico_sdk_init() - -add_subdirectory(ext/FatFs_SPI build) - -add_executable(RP2040_GB - src/main.c - src/mk_ili9225.c - ext/minigb_apu/minigb_apu.c - ext/i2s/i2s.c -) - -pico_generate_pio_header(RP2040_GB ${CMAKE_CURRENT_LIST_DIR}/ext/i2s/audio_i2s.pio) - -target_include_directories(RP2040_GB PRIVATE inc ext/minigb_apu ext/i2s) - -target_link_libraries(RP2040_GB - FatFs_SPI - pico_stdlib pico_stdio pico_bootrom pico_multicore pico_stdio pico_multicore - hardware_clocks hardware_pio hardware_vreg hardware_pio - hardware_sync hardware_pll hardware_spi hardware_irq hardware_dma - pico_binary_info) -target_compile_definitions(RP2040_GB PRIVATE - PARAM_ASSERTIONS_DISABLE_ALL=1 - PICO_ENTER_USB_BOOT_ON_EXIT=1 - PICO_STDIO_ENABLE_CRLF_SUPPORT=0 - PICO_STDIO_DEFAULT_CRLF=0 - PICO_PRINTF_SUPPORT_FLOAT=0 - PICO_PRINTF_SUPPORT_EXPONENTIAL=0 - PICO_PRINTF_SUPPORT_LONG_LONG=1 - PICO_PRINTF_SUPPORT_PTRDIFF_T=0) - -function(pico_add_verbose_dis_output TARGET) - add_custom_command(TARGET ${TARGET} POST_BUILD - COMMAND ${CMAKE_OBJDUMP} -h $ >$>,$,$>.dis - COMMAND ${CMAKE_OBJDUMP} -drwCSl $ >>$>,$,$>.dis - ) -endfunction() - -pico_set_binary_type(RP2040_GB copy_to_ram) -#pico_set_binary_type(RP2040_GB no_flash) -pico_enable_stdio_usb(RP2040_GB 1) -pico_enable_stdio_uart(RP2040_GB 0) -pico_add_verbose_dis_output(RP2040_GB) -pico_add_bin_output(RP2040_GB) -pico_add_uf2_output(RP2040_GB) diff --git a/inc/gbcolors.h b/inc/gbcolors.h index 90bfc15..c4fe3a7 100644 --- a/inc/gbcolors.h +++ b/inc/gbcolors.h @@ -34,6 +34,11 @@ typedef uint16_t palette_t[3][4]; +void manual_assign_palette(palette_t palette, uint8_t selection); +void auto_assign_palette(uint16_t palette[3][4], uint8_t game_checksum, const char *game_title); + +#ifndef GBCOLOR_HEADER_ONLY + /* * Get an RGB565 colour palette by entry ID & shuffling flags * @@ -46,7 +51,7 @@ typedef uint16_t palette_t[3][4]; */ void get_colour_palette(palette_t selected_palette,uint8_t table_entry,uint8_t shuffling_flags) { - printf("I get_colour_palette(table_entry=0x%02X,shuffling_flags=0x%02X)\n", + Serial.printf("I get_colour_palette(table_entry=0x%02X,shuffling_flags=0x%02X)\n", table_entry, shuffling_flags); if(table_entry==0x00 && shuffling_flags==0x01) @@ -571,7 +576,7 @@ void get_colour_palette(palette_t selected_palette,uint8_t table_entry,uint8_t s return; } /* default palette */ - printf("E get_colour_palette: No palette found for table_entry=0x%02X shuffling_flags=0x%02X\n", + Serial.printf("E get_colour_palette: No palette found for table_entry=0x%02X shuffling_flags=0x%02X\n", table_entry, shuffling_flags); /* Game Boy DMG palette (4 shades of green) */ @@ -600,7 +605,7 @@ void get_colour_palette(palette_t selected_palette,uint8_t table_entry,uint8_t s void auto_assign_palette(uint16_t palette[3][4], uint8_t game_checksum, const char *game_title) { char disambiguation_character=game_title[3]; /* e.g. 'METROID' -> R */ - printf("I auto_assign_palette(0x%02X,%s)\n", game_checksum,game_title); + Serial.printf("I auto_assign_palette(0x%02X,%s)\n", game_checksum,game_title); switch(game_checksum) { case 0x00: @@ -1239,7 +1244,7 @@ void auto_assign_palette(uint16_t palette[3][4], uint8_t game_checksum, const ch } default: { - printf("E auto_assign_palette: No palette found for checksum 0x%02X.\n", game_checksum); + Serial.printf("E auto_assign_palette: No palette found for checksum 0x%02X.\n", game_checksum); /* Original Game Boy DMG color palette (monochrome 4-shades of green!) */ get_colour_palette(palette,0xFF,0xFF); break; @@ -1351,4 +1356,6 @@ void manual_assign_palette(palette_t palette, uint8_t selection) break; } } -} \ No newline at end of file +} + +#endif diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake deleted file mode 100644 index 28efe9e..0000000 --- a/pico_sdk_import.cmake +++ /dev/null @@ -1,62 +0,0 @@ -# This is a copy of /external/pico_sdk_import.cmake - -# This can be dropped into an external project to help locate this SDK -# It should be include()ed prior to project() - -if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) - set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) - message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") -endif () - -if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) - set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) - message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") -endif () - -if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) - set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) - message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") -endif () - -set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") -set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") -set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") - -if (NOT PICO_SDK_PATH) - if (PICO_SDK_FETCH_FROM_GIT) - include(FetchContent) - set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) - if (PICO_SDK_FETCH_FROM_GIT_PATH) - get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") - endif () - FetchContent_Declare( - pico_sdk - GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk - GIT_TAG master - ) - if (NOT pico_sdk) - message("Downloading Raspberry Pi Pico SDK") - FetchContent_Populate(pico_sdk) - set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) - endif () - set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) - else () - message(FATAL_ERROR - "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." - ) - endif () -endif () - -get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") -if (NOT EXISTS ${PICO_SDK_PATH}) - message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") -endif () - -set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) -if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) - message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") -endif () - -set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) - -include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..4a2c3e3 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,34 @@ +[env] +monitor_speed = 115200 + +[pico-base] +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#651837d09a1a58c46bd1bcf4647468d8c5ed92c2 +framework = arduino + +board_build.core = earlephilhower +board_build.filesystem_size = 512k + +#upload_protocol = mbed +upload_protocol = cmsis-dap +debug_tool = cmsis-dap + +build_src_filter = + +<*.cpp> + +#lib_ldf_mode = chain+ +lib_deps = + bodmer/TFT_eSPI@^2.5.43 + +build_flags = + -I. + -Isrc/tft-espi-config/ + -Iinc + -Iext/minigb_apu + +[env:pico] +extends = pico-base +board = rpipico + +[env:pico2] +extends = pico-base +board = rpipico2 diff --git a/src/.clang-format b/src/.clang-format new file mode 100644 index 0000000..23669bf --- /dev/null +++ b/src/.clang-format @@ -0,0 +1,45 @@ +--- +BasedOnStyle: WebKit +IndentWidth: 2 +--- +Language: Cpp +Standard: Cpp11 +CommentPragmas: '^// Copyright|^// SPDX-License-Identifier' +# Pointers aligned to the left +DerivePointerAlignment: false +PointerAlignment: Left +AccessModifierOffset: -2 +AllowShortFunctionsOnASingleLine: Inline +AlwaysBreakTemplateDeclarations: true +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: true + BeforeCatch: false + BeforeElse: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +SpaceInEmptyBlock: false +BreakConstructorInitializers: BeforeColon +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +Cpp11BracedListStyle: true +FixNamespaceComments: true +IncludeBlocks: Regroup +IndentCaseLabels: false +# NamespaceIndentation: All +# SortIncludes: true +# SortUsingDeclarations: true +# SpaceAfterTemplateKeyword: true +SpacesInAngles: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +UseTab: Never diff --git a/src/card_loader.cpp b/src/card_loader.cpp new file mode 100644 index 0000000..d1e2e49 --- /dev/null +++ b/src/card_loader.cpp @@ -0,0 +1,263 @@ +#if ENABLE_SDCARD + +/** + * Load a save file from the SD card + */ +void read_cart_ram_file(struct gb_s* gb) { + char filename[16]; + uint_fast32_t save_size; + UINT br; + + gb_get_rom_name(gb, filename); + save_size = gb_get_save_size(gb); + if (save_size > 0) { + sd_card_t* pSD = sd_get_by_num(0); + FRESULT fr = f_mount(&pSD->fatfs, pSD->pcName, 1); + if (FR_OK != fr) { + printf("E f_mount error: %s (%d)\n", FRESULT_str(fr), fr); + return; + } + + FIL fil; + fr = f_open(&fil, filename, FA_READ); + if (fr == FR_OK) { + f_read(&fil, ram, f_size(&fil), &br); + } else { + printf("E f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr); + } + + fr = f_close(&fil); + if (fr != FR_OK) { + printf("E f_close error: %s (%d)\n", FRESULT_str(fr), fr); + } + f_unmount(pSD->pcName); + } + printf("I read_cart_ram_file(%s) COMPLETE (%lu bytes)\n", filename, save_size); +} + +/** + * Write a save file to the SD card + */ +void write_cart_ram_file(struct gb_s* gb) { + char filename[16]; + uint_fast32_t save_size; + UINT bw; + + gb_get_rom_name(gb, filename); + save_size = gb_get_save_size(gb); + if (save_size > 0) { + sd_card_t* pSD = sd_get_by_num(0); + FRESULT fr = f_mount(&pSD->fatfs, pSD->pcName, 1); + if (FR_OK != fr) { + printf("E f_mount error: %s (%d)\n", FRESULT_str(fr), fr); + return; + } + + FIL fil; + fr = f_open(&fil, filename, FA_CREATE_ALWAYS | FA_WRITE); + if (fr == FR_OK) { + f_write(&fil, ram, save_size, &bw); + } else { + printf("E f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr); + } + + fr = f_close(&fil); + if (fr != FR_OK) { + printf("E f_close error: %s (%d)\n", FRESULT_str(fr), fr); + } + f_unmount(pSD->pcName); + } + printf("I write_cart_ram_file(%s) COMPLETE (%lu bytes)\n", filename, save_size); +} + +/** + * Load a .gb rom file in flash from the SD card + */ +void load_cart_rom_file(char* filename) { + UINT br; + uint8_t buffer[FLASH_SECTOR_SIZE]; + bool mismatch = false; + sd_card_t* pSD = sd_get_by_num(0); + FRESULT fr = f_mount(&pSD->fatfs, pSD->pcName, 1); + if (FR_OK != fr) { + printf("E f_mount error: %s (%d)\n", FRESULT_str(fr), fr); + return; + } + FIL fil; + fr = f_open(&fil, filename, FA_READ); + if (fr == FR_OK) { + uint32_t flash_target_offset = FLASH_TARGET_OFFSET; + for (;;) { + f_read(&fil, buffer, sizeof buffer, &br); + if (br == 0) + break; /* end of file */ + + printf("I Erasing target region...\n"); + flash_range_erase(flash_target_offset, FLASH_SECTOR_SIZE); + printf("I Programming target region...\n"); + flash_range_program(flash_target_offset, buffer, FLASH_SECTOR_SIZE); + + /* Read back target region and check programming */ + printf("I Done. Reading back target region...\n"); + for (uint32_t i = 0; i < FLASH_SECTOR_SIZE; i++) { + if (rom[flash_target_offset + i] != buffer[i]) { + mismatch = true; + } + } + + /* Next sector */ + flash_target_offset += FLASH_SECTOR_SIZE; + } + if (mismatch) { + printf("I Programming successful!\n"); + } else { + printf("E Programming failed!\n"); + } + } else { + printf("E f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr); + } + + fr = f_close(&fil); + if (fr != FR_OK) { + printf("E f_close error: %s (%d)\n", FRESULT_str(fr), fr); + } + f_unmount(pSD->pcName); + + printf("I load_cart_rom_file(%s) COMPLETE (%lu bytes)\n", filename, br); +} + +/** + * Function used by the rom file selector to display one page of .gb rom files + */ +uint16_t rom_file_selector_display_page(char filename[22][256], uint16_t num_page) { + sd_card_t* pSD = sd_get_by_num(0); + DIR dj; + FILINFO fno; + FRESULT fr; + + fr = f_mount(&pSD->fatfs, pSD->pcName, 1); + if (FR_OK != fr) { + printf("E f_mount error: %s (%d)\n", FRESULT_str(fr), fr); + return 0; + } + + /* clear the filenames array */ + for (uint8_t ifile = 0; ifile < 22; ifile++) { + strcpy(filename[ifile], ""); + } + + /* search *.gb files */ + uint16_t num_file = 0; + fr = f_findfirst(&dj, &fno, "", "*.gb"); + + /* skip the first N pages */ + if (num_page > 0) { + while (num_file < num_page * 22 && fr == FR_OK && fno.fname[0]) { + num_file++; + fr = f_findnext(&dj, &fno); + } + } + + /* store the filenames of this page */ + num_file = 0; + while (num_file < 22 && fr == FR_OK && fno.fname[0]) { + strcpy(filename[num_file], fno.fname); + num_file++; + fr = f_findnext(&dj, &fno); + } + f_closedir(&dj); + f_unmount(pSD->pcName); + + /* display *.gb rom files on screen */ + lcd_fill(0x0000); + for (uint8_t ifile = 0; ifile < num_file; ifile++) { + lcd_text(filename[ifile], 0, ifile * 8, 0xFFFF, 0x0000); + } + return num_file; +} + +/** + * The ROM selector displays pages of up to 22 rom files + * allowing the user to select which rom file to start + * Copy your *.gb rom files to the root directory of the SD card + */ +void rom_file_selector() { + uint16_t num_page; + char filename[22][256]; + uint16_t num_file; + + /* display the first page with up to 22 rom files */ + num_file = rom_file_selector_display_page(filename, num_page); + + /* select the first rom */ + uint8_t selected = 0; + lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0xF800); + + /* get user's input */ + bool up, down, left, right, a, b, select, start; + while (true) { + up = gpio_get(GPIO_UP); + down = gpio_get(GPIO_DOWN); + left = gpio_get(GPIO_LEFT); + right = gpio_get(GPIO_RIGHT); + a = gpio_get(GPIO_A); + b = gpio_get(GPIO_B); + select = gpio_get(GPIO_SELECT); + start = gpio_get(GPIO_START); + if (!start) { + /* re-start the last game (no need to reprogram flash) */ + break; + } + if (!a | !b) { + /* copy the rom from the SD card to flash and start the game */ + load_cart_rom_file(filename[selected]); + break; + } + if (!down) { + /* select the next rom */ + lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0x0000); + selected++; + if (selected >= num_file) + selected = 0; + lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0xF800); + sleep_ms(150); + } + if (!up) { + /* select the previous rom */ + lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0x0000); + if (selected == 0) { + selected = num_file - 1; + } else { + selected--; + } + lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0xF800); + sleep_ms(150); + } + if (!right) { + /* select the next page */ + num_page++; + num_file = rom_file_selector_display_page(filename, num_page); + if (num_file == 0) { + /* no files in this page, go to the previous page */ + num_page--; + num_file = rom_file_selector_display_page(filename, num_page); + } + /* select the first file */ + selected = 0; + lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0xF800); + sleep_ms(150); + } + if ((!left) && num_page > 0) { + /* select the previous page */ + num_page--; + num_file = rom_file_selector_display_page(filename, num_page); + /* select the first file */ + selected = 0; + lcd_text(filename[selected], 0, selected * 8, 0xFFFF, 0xF800); + sleep_ms(150); + } + tight_loop_contents(); + } +} + +#endif diff --git a/src/core.h b/src/core.h new file mode 100644 index 0000000..367b29e --- /dev/null +++ b/src/core.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +#define GBCOLOR_HEADER_ONLY +#include "gbcolors.h" + +/* Multicore command structure. */ +union core_cmd +{ + struct + { + /* Does nothing. */ +#define CORE_CMD_NOP 0 + /* Set line "data" on the LCD. Pixel data is in pixels_buffer. */ +#define CORE_CMD_LCD_LINE 1 + /* Control idle mode on the LCD. Limits colours to 2 bits. */ +#define CORE_CMD_IDLE_SET 2 + /* Set a specific pixel. For debugging. */ +#define CORE_CMD_SET_PIXEL 3 + uint8_t cmd; + uint8_t unused1; + uint8_t unused2; + uint8_t data; + }; + uint32_t full; +}; + + +extern palette_t palette; // Colour palette + +void lcd_draw_line(struct gb_s *gb, const uint8_t *pixels, const uint_fast8_t line); + +void core1_init(); diff --git a/src/lcd_core.cpp b/src/lcd_core.cpp new file mode 100644 index 0000000..4518f7f --- /dev/null +++ b/src/lcd_core.cpp @@ -0,0 +1,142 @@ +#include + +#define PEANUT_GB_HEADER_ONLY +#include "peanut_gb.h" + +// Note: must be included before core +#include "gbcolors.h" +#include "core.h" + +static TFT_eSPI tft = TFT_eSPI(); + +static uint8_t scaledLineOffsetTable[LCD_HEIGHT]; // scaled to 240 lines + +/* Pixel data is stored in here. */ +static uint8_t pixels_buffer[LCD_WIDTH]; + +palette_t palette; // Colour palette + +static int lcd_line_busy = 0; + +#define IS_LINE_REPEATED(line) ((line % 2) || (line % 6 == 0)) +// #define IS_LINE_REPEATED(line) 0 + +static void calcExtraLineTable() { + uint8_t offset = 0; + for (uint8_t line = 0; line < LCD_HEIGHT; ++line) { + scaledLineOffsetTable[line] = offset; + offset += 1 + IS_LINE_REPEATED(line); + } +} + +void lcd_init(void) { + tft.init(); + // tft.initDMA(); + tft.setRotation(1); + tft.fillScreen(TFT_BLACK); +} + +void lcd_draw_line(struct gb_s* gb, const uint8_t pixels[LCD_WIDTH], + const uint_fast8_t line) { + union core_cmd cmd; + + /* Wait until previous line is sent. */ + while (__atomic_load_n(&lcd_line_busy, __ATOMIC_SEQ_CST)) + tight_loop_contents(); + + memcpy(pixels_buffer, pixels, LCD_WIDTH); + + /* Populate command. */ + cmd.cmd = CORE_CMD_LCD_LINE; + cmd.data = line; + + __atomic_store_n(&lcd_line_busy, 1, __ATOMIC_SEQ_CST); + multicore_fifo_push_blocking(cmd.full); +} + +void lcd_write_pixels(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) { + static uint16_t doubledPixels[320]; + uint16_t pos = 0; + for (int i = 0; i < nmemb; ++i) { + doubledPixels[pos++] = pixels[i]; + doubledPixels[pos++] = pixels[i]; + } + + uint8_t repeatedLines = IS_LINE_REPEATED(line); + // tft.setAddrWindow(0, scaledLineOffsetTable[line], nmemb * 2, 1 + repeatedLines); + tft.setAddrWindow(0, scaledLineOffsetTable[line], nmemb * 2, 1); + tft.pushColors((uint16_t*)doubledPixels, nmemb * 2, true); + if (repeatedLines) { + tft.setAddrWindow(0, scaledLineOffsetTable[line] + 1, nmemb * 2, 1); + tft.pushColors((uint16_t*)doubledPixels, nmemb * 2, true); + } +} + +void lcd_fill(uint16_t color) { + tft.fillScreen(color); +} + +void lcd_fill_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) { + tft.fillRect(0, 0, tft.width(), tft.height(), TFT_BLACK); +} + +void lcd_text(char* s, uint8_t x, uint8_t y, uint16_t color, uint16_t bgcolor) { + tft.setTextColor(TFT_WHITE, TFT_BLACK); // TODO + tft.drawString(s, x, y); +} + +void lcd_display_control(bool invert, int /*ili9225_color_mode_e*/ colour_mode) { + // TODO +} + +void core1_lcd_draw_line(const uint_fast8_t line) { + static uint16_t fb[LCD_WIDTH]; + + for (unsigned int x = 0; x < LCD_WIDTH; x++) { + fb[x] = palette[(pixels_buffer[x] & LCD_PALETTE_ALL) >> 4] + [pixels_buffer[x] & 3]; + } + + lcd_write_pixels(fb, line, LCD_WIDTH); + __atomic_store_n(&lcd_line_busy, 0, __ATOMIC_SEQ_CST); +} + +void core1DispatchLoop() { + union core_cmd cmd; + + /* Handle commands coming from core0. */ + cmd.full = multicore_fifo_pop_blocking(); + switch (cmd.cmd) { + case CORE_CMD_LCD_LINE: + core1_lcd_draw_line(cmd.data); + break; + + case CORE_CMD_IDLE_SET: + lcd_display_control(true, cmd.data); + break; + + case CORE_CMD_NOP: + default: + break; + } +} + +void core1_init() { + /* Initialise and control LCD on core 1. */ + lcd_init(); + + /* Clear LCD screen. */ + lcd_fill(0x0000); + + /* Set LCD window to DMG size. */ + lcd_fill_rect(31, 16, LCD_WIDTH, LCD_HEIGHT, 0x0000); + + calcExtraLineTable(); + + // Sleep used for debugging LCD window. + // sleep_ms(1000); + + while (true) { + core1DispatchLoop(); + } +} diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 33c7bfc..0000000 --- a/src/main.c +++ /dev/null @@ -1,902 +0,0 @@ -/** - * Copyright (C) 2022 by Mahyar Koshkouei - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -// Peanut-GB emulator settings -#define ENABLE_LCD 1 -#define ENABLE_SOUND 1 -#define ENABLE_SDCARD 1 -#define PEANUT_GB_HIGH_LCD_ACCURACY 1 -#define PEANUT_GB_USE_BIOS 0 - -/* Use DMA for all drawing to LCD. Benefits aren't fully realised at the moment - * due to busy loops waiting for DMA completion. */ -#define USE_DMA 0 - -/** - * Reducing VSYNC calculation to lower multiple. - * When setting a clock IRQ to DMG_CLOCK_FREQ_REDUCED, count to - * SCREEN_REFRESH_CYCLES_REDUCED to obtain the time required each VSYNC. - * DMG_CLOCK_FREQ_REDUCED = 2^18, and SCREEN_REFRESH_CYCLES_REDUCED = 4389. - * Currently unused. - */ -#define VSYNC_REDUCTION_FACTOR 16u -#define SCREEN_REFRESH_CYCLES_REDUCED (SCREEN_REFRESH_CYCLES/VSYNC_REDUCTION_FACTOR) -#define DMG_CLOCK_FREQ_REDUCED (DMG_CLOCK_FREQ/VSYNC_REDUCTION_FACTOR) - -/* C Headers */ -#include -#include -#include - -/* RP2040 Headers */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Project headers */ -#include "hedley.h" -#include "minigb_apu.h" -#include "peanut_gb.h" -#include "mk_ili9225.h" -#include "sdcard.h" -#include "i2s.h" -#include "gbcolors.h" - -/* GPIO Connections. */ -#define GPIO_UP 2 -#define GPIO_DOWN 3 -#define GPIO_LEFT 4 -#define GPIO_RIGHT 5 -#define GPIO_A 6 -#define GPIO_B 7 -#define GPIO_SELECT 8 -#define GPIO_START 9 -#define GPIO_CS 17 -#define GPIO_CLK 18 -#define GPIO_SDA 19 -#define GPIO_RS 20 -#define GPIO_RST 21 -#define GPIO_LED 22 - -#if ENABLE_SOUND -/** - * Global variables for audio task - * stream contains N=AUDIO_SAMPLES samples - * each sample is 32 bits - * 16 bits for the left channel + 16 bits for the right channel in stereo interleaved format) - * This is intended to be played at AUDIO_SAMPLE_RATE Hz - */ -uint16_t *stream; -#endif - -/** Definition of ROM data - * We're going to erase and reprogram a region 1Mb from the start of the flash - * Once done, we can access this at XIP_BASE + 1Mb. - * Game Boy DMG ROM size ranges from 32768 bytes (e.g. Tetris) to 1,048,576 bytes (e.g. Pokemod Red) - */ -#define FLASH_TARGET_OFFSET (1024 * 1024) -const uint8_t *rom = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET); -static unsigned char rom_bank0[65536]; - -static uint8_t ram[32768]; -static int lcd_line_busy = 0; -static palette_t palette; // Colour palette -static uint8_t manual_palette_selected=0; - -static struct -{ - unsigned a : 1; - unsigned b : 1; - unsigned select : 1; - unsigned start : 1; - unsigned right : 1; - unsigned left : 1; - unsigned up : 1; - unsigned down : 1; -} prev_joypad_bits; - -/* Multicore command structure. */ -union core_cmd { - struct { - /* Does nothing. */ -#define CORE_CMD_NOP 0 - /* Set line "data" on the LCD. Pixel data is in pixels_buffer. */ -#define CORE_CMD_LCD_LINE 1 - /* Control idle mode on the LCD. Limits colours to 2 bits. */ -#define CORE_CMD_IDLE_SET 2 - /* Set a specific pixel. For debugging. */ -#define CORE_CMD_SET_PIXEL 3 - uint8_t cmd; - uint8_t unused1; - uint8_t unused2; - uint8_t data; - }; - uint32_t full; -}; - -/* Pixel data is stored in here. */ -static uint8_t pixels_buffer[LCD_WIDTH]; - -#define putstdio(x) write(1, x, strlen(x)) - -/* Functions required for communication with the ILI9225. */ -void mk_ili9225_set_rst(bool state) -{ - gpio_put(GPIO_RST, state); -} - -void mk_ili9225_set_rs(bool state) -{ - gpio_put(GPIO_RS, state); -} - -void mk_ili9225_set_cs(bool state) -{ - gpio_put(GPIO_CS, state); -} - -void mk_ili9225_set_led(bool state) -{ - gpio_put(GPIO_LED, state); -} - -void mk_ili9225_spi_write16(const uint16_t *halfwords, size_t len) -{ - spi_write16_blocking(spi0, halfwords, len); -} - -void mk_ili9225_delay_ms(unsigned ms) -{ - sleep_ms(ms); -} - -/** - * Returns a byte from the ROM file at the given address. - */ -uint8_t gb_rom_read(struct gb_s *gb, const uint_fast32_t addr) -{ - (void) gb; - if(addr < sizeof(rom_bank0)) - return rom_bank0[addr]; - - return rom[addr]; -} - -/** - * Returns a byte from the cartridge RAM at the given address. - */ -uint8_t gb_cart_ram_read(struct gb_s *gb, const uint_fast32_t addr) -{ - (void) gb; - return ram[addr]; -} - -/** - * Writes a given byte to the cartridge RAM at the given address. - */ -void gb_cart_ram_write(struct gb_s *gb, const uint_fast32_t addr, - const uint8_t val) -{ - ram[addr] = val; -} - -/** - * Ignore all errors. - */ -void gb_error(struct gb_s *gb, const enum gb_error_e gb_err, const uint16_t addr) -{ -#if 1 - const char* gb_err_str[4] = { - "UNKNOWN", - "INVALID OPCODE", - "INVALID READ", - "INVALID WRITE" - }; - printf("Error %d occurred: %s at %04X\n.\n", gb_err, gb_err_str[gb_err], addr); -// abort(); -#endif -} - -#if ENABLE_LCD -void core1_lcd_draw_line(const uint_fast8_t line) -{ - static uint16_t fb[LCD_WIDTH]; - - for(unsigned int x = 0; x < LCD_WIDTH; x++) - { - fb[x] = palette[(pixels_buffer[x] & LCD_PALETTE_ALL) >> 4] - [pixels_buffer[x] & 3]; - } - - mk_ili9225_set_x(line + 16); - mk_ili9225_write_pixels(fb, LCD_WIDTH); - __atomic_store_n(&lcd_line_busy, 0, __ATOMIC_SEQ_CST); -} - -_Noreturn -void main_core1(void) -{ - union core_cmd cmd; - - /* Initialise and control LCD on core 1. */ - mk_ili9225_init(); - - /* Clear LCD screen. */ - mk_ili9225_fill(0x0000); - - /* Set LCD window to DMG size. */ - mk_ili9225_fill_rect(31,16,LCD_WIDTH,LCD_HEIGHT,0x0000); - - // Sleep used for debugging LCD window. - //sleep_ms(1000); - - /* Handle commands coming from core0. */ - while(1) - { - cmd.full = multicore_fifo_pop_blocking(); - switch(cmd.cmd) - { - case CORE_CMD_LCD_LINE: - core1_lcd_draw_line(cmd.data); - break; - - case CORE_CMD_IDLE_SET: - mk_ili9225_display_control(true, cmd.data); - break; - - case CORE_CMD_NOP: - default: - break; - } - } - - HEDLEY_UNREACHABLE(); -} -#endif - -#if ENABLE_LCD -void lcd_draw_line(struct gb_s *gb, const uint8_t pixels[LCD_WIDTH], - const uint_fast8_t line) -{ - union core_cmd cmd; - - /* Wait until previous line is sent. */ - while(__atomic_load_n(&lcd_line_busy, __ATOMIC_SEQ_CST)) - tight_loop_contents(); - - memcpy(pixels_buffer, pixels, LCD_WIDTH); - - /* Populate command. */ - cmd.cmd = CORE_CMD_LCD_LINE; - cmd.data = line; - - __atomic_store_n(&lcd_line_busy, 1, __ATOMIC_SEQ_CST); - multicore_fifo_push_blocking(cmd.full); -} -#endif - -#if ENABLE_SDCARD -/** - * Load a save file from the SD card - */ -void read_cart_ram_file(struct gb_s *gb) { - char filename[16]; - uint_fast32_t save_size; - UINT br; - - gb_get_rom_name(gb,filename); - save_size=gb_get_save_size(gb); - if(save_size>0) { - sd_card_t *pSD=sd_get_by_num(0); - FRESULT fr=f_mount(&pSD->fatfs,pSD->pcName,1); - if (FR_OK!=fr) { - printf("E f_mount error: %s (%d)\n",FRESULT_str(fr),fr); - return; - } - - FIL fil; - fr=f_open(&fil,filename,FA_READ); - if (fr==FR_OK) { - f_read(&fil,ram,f_size(&fil),&br); - } else { - printf("E f_open(%s) error: %s (%d)\n",filename,FRESULT_str(fr),fr); - } - - fr=f_close(&fil); - if(fr!=FR_OK) { - printf("E f_close error: %s (%d)\n", FRESULT_str(fr), fr); - } - f_unmount(pSD->pcName); - } - printf("I read_cart_ram_file(%s) COMPLETE (%lu bytes)\n",filename,save_size); -} - -/** - * Write a save file to the SD card - */ -void write_cart_ram_file(struct gb_s *gb) { - char filename[16]; - uint_fast32_t save_size; - UINT bw; - - gb_get_rom_name(gb,filename); - save_size=gb_get_save_size(gb); - if(save_size>0) { - sd_card_t *pSD=sd_get_by_num(0); - FRESULT fr=f_mount(&pSD->fatfs,pSD->pcName,1); - if (FR_OK!=fr) { - printf("E f_mount error: %s (%d)\n",FRESULT_str(fr),fr); - return; - } - - FIL fil; - fr=f_open(&fil,filename,FA_CREATE_ALWAYS | FA_WRITE); - if (fr==FR_OK) { - f_write(&fil,ram,save_size,&bw); - } else { - printf("E f_open(%s) error: %s (%d)\n",filename,FRESULT_str(fr),fr); - } - - fr=f_close(&fil); - if(fr!=FR_OK) { - printf("E f_close error: %s (%d)\n", FRESULT_str(fr), fr); - } - f_unmount(pSD->pcName); - } - printf("I write_cart_ram_file(%s) COMPLETE (%lu bytes)\n",filename,save_size); -} - -/** - * Load a .gb rom file in flash from the SD card - */ -void load_cart_rom_file(char *filename) { - UINT br; - uint8_t buffer[FLASH_SECTOR_SIZE]; - bool mismatch=false; - sd_card_t *pSD=sd_get_by_num(0); - FRESULT fr=f_mount(&pSD->fatfs,pSD->pcName,1); - if (FR_OK!=fr) { - printf("E f_mount error: %s (%d)\n",FRESULT_str(fr),fr); - return; - } - FIL fil; - fr=f_open(&fil,filename,FA_READ); - if (fr==FR_OK) { - uint32_t flash_target_offset=FLASH_TARGET_OFFSET; - for(;;) { - f_read(&fil,buffer,sizeof buffer,&br); - if(br==0) break; /* end of file */ - - printf("I Erasing target region...\n"); - flash_range_erase(flash_target_offset,FLASH_SECTOR_SIZE); - printf("I Programming target region...\n"); - flash_range_program(flash_target_offset,buffer,FLASH_SECTOR_SIZE); - - /* Read back target region and check programming */ - printf("I Done. Reading back target region...\n"); - for(uint32_t i=0;ipcName); - - printf("I load_cart_rom_file(%s) COMPLETE (%lu bytes)\n",filename,br); -} - -/** - * Function used by the rom file selector to display one page of .gb rom files - */ -uint16_t rom_file_selector_display_page(char filename[22][256],uint16_t num_page) { - sd_card_t *pSD=sd_get_by_num(0); - DIR dj; - FILINFO fno; - FRESULT fr; - - fr=f_mount(&pSD->fatfs,pSD->pcName,1); - if (FR_OK!=fr) { - printf("E f_mount error: %s (%d)\n",FRESULT_str(fr),fr); - return 0; - } - - /* clear the filenames array */ - for(uint8_t ifile=0;ifile<22;ifile++) { - strcpy(filename[ifile],""); - } - - /* search *.gb files */ - uint16_t num_file=0; - fr=f_findfirst(&dj, &fno, "", "*.gb"); - - /* skip the first N pages */ - if(num_page>0) { - while(num_filepcName); - - /* display *.gb rom files on screen */ - mk_ili9225_fill(0x0000); - for(uint8_t ifile=0;ifile=num_file) selected=0; - mk_ili9225_text(filename[selected],0,selected*8,0xFFFF,0xF800); - sleep_ms(150); - } - if(!up) { - /* select the previous rom */ - mk_ili9225_text(filename[selected],0,selected*8,0xFFFF,0x0000); - if(selected==0) { - selected=num_file-1; - } else { - selected--; - } - mk_ili9225_text(filename[selected],0,selected*8,0xFFFF,0xF800); - sleep_ms(150); - } - if(!right) { - /* select the next page */ - num_page++; - num_file=rom_file_selector_display_page(filename,num_page); - if(num_file==0) { - /* no files in this page, go to the previous page */ - num_page--; - num_file=rom_file_selector_display_page(filename,num_page); - } - /* select the first file */ - selected=0; - mk_ili9225_text(filename[selected],0,selected*8,0xFFFF,0xF800); - sleep_ms(150); - } - if((!left) && num_page>0) { - /* select the previous page */ - num_page--; - num_file=rom_file_selector_display_page(filename,num_page); - /* select the first file */ - selected=0; - mk_ili9225_text(filename[selected],0,selected*8,0xFFFF,0xF800); - sleep_ms(150); - } - tight_loop_contents(); - } -} - -#endif - -int main(void) -{ - static struct gb_s gb; - enum gb_init_error_e ret; - - /* Overclock. */ - { - const unsigned vco = 1596*1000*1000; /* 266MHz */ - const unsigned div1 = 6, div2 = 1; - - vreg_set_voltage(VREG_VOLTAGE_1_15); - sleep_ms(2); - set_sys_clock_pll(vco, div1, div2); - sleep_ms(2); - } - - /* Initialise USB serial connection for debugging. */ - stdio_init_all(); - time_init(); - // sleep_ms(5000); - putstdio("INIT: "); - - /* Initialise GPIO pins. */ - gpio_set_function(GPIO_UP, GPIO_FUNC_SIO); - gpio_set_function(GPIO_DOWN, GPIO_FUNC_SIO); - gpio_set_function(GPIO_LEFT, GPIO_FUNC_SIO); - gpio_set_function(GPIO_RIGHT, GPIO_FUNC_SIO); - gpio_set_function(GPIO_A, GPIO_FUNC_SIO); - gpio_set_function(GPIO_B, GPIO_FUNC_SIO); - gpio_set_function(GPIO_SELECT, GPIO_FUNC_SIO); - gpio_set_function(GPIO_START, GPIO_FUNC_SIO); - gpio_set_function(GPIO_CS, GPIO_FUNC_SIO); - gpio_set_function(GPIO_CLK, GPIO_FUNC_SPI); - gpio_set_function(GPIO_SDA, GPIO_FUNC_SPI); - gpio_set_function(GPIO_RS, GPIO_FUNC_SIO); - gpio_set_function(GPIO_RST, GPIO_FUNC_SIO); - gpio_set_function(GPIO_LED, GPIO_FUNC_SIO); - - gpio_set_dir(GPIO_UP, false); - gpio_set_dir(GPIO_DOWN, false); - gpio_set_dir(GPIO_LEFT, false); - gpio_set_dir(GPIO_RIGHT, false); - gpio_set_dir(GPIO_A, false); - gpio_set_dir(GPIO_B, false); - gpio_set_dir(GPIO_SELECT, false); - gpio_set_dir(GPIO_START, false); - gpio_set_dir(GPIO_CS, true); - gpio_set_dir(GPIO_RS, true); - gpio_set_dir(GPIO_RST, true); - gpio_set_dir(GPIO_LED, true); - gpio_set_slew_rate(GPIO_CLK, GPIO_SLEW_RATE_FAST); - gpio_set_slew_rate(GPIO_SDA, GPIO_SLEW_RATE_FAST); - - gpio_pull_up(GPIO_UP); - gpio_pull_up(GPIO_DOWN); - gpio_pull_up(GPIO_LEFT); - gpio_pull_up(GPIO_RIGHT); - gpio_pull_up(GPIO_A); - gpio_pull_up(GPIO_B); - gpio_pull_up(GPIO_SELECT); - gpio_pull_up(GPIO_START); - - /* Set SPI clock to use high frequency. */ - clock_configure(clk_peri, 0, - CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, - 125 * 1000 * 1000, 125 * 1000 * 1000); - spi_init(spi0, 30*1000*1000); - spi_set_format(spi0, 16, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); - -#if ENABLE_SOUND - // Allocate memory for the stream buffer - stream=malloc(AUDIO_BUFFER_SIZE_BYTES); - assert(stream!=NULL); - memset(stream,0,AUDIO_BUFFER_SIZE_BYTES); // Zero out the stream buffer - - // Initialize I2S sound driver - i2s_config_t i2s_config = i2s_get_default_config(); - i2s_config.sample_freq=AUDIO_SAMPLE_RATE; - i2s_config.dma_trans_count =AUDIO_SAMPLES; - i2s_volume(&i2s_config,2); - i2s_init(&i2s_config); -#endif - -while(true) -{ -#if ENABLE_LCD -#if ENABLE_SDCARD - /* ROM File selector */ - mk_ili9225_init(); - mk_ili9225_fill(0x0000); - rom_file_selector(); -#endif -#endif - - /* Initialise GB context. */ - memcpy(rom_bank0, rom, sizeof(rom_bank0)); - ret = gb_init(&gb, &gb_rom_read, &gb_cart_ram_read, - &gb_cart_ram_write, &gb_error, NULL); - putstdio("GB "); - - if(ret != GB_INIT_NO_ERROR) - { - printf("Error: %d\n", ret); - goto out; - } - - /* Automatically assign a colour palette to the game */ - char rom_title[16]; - auto_assign_palette(palette, gb_colour_hash(&gb),gb_get_rom_name(&gb,rom_title)); - -#if ENABLE_LCD - gb_init_lcd(&gb, &lcd_draw_line); - - /* Start Core1, which processes requests to the LCD. */ - putstdio("CORE1 "); - multicore_launch_core1(main_core1); - - putstdio("LCD "); -#endif - -#if ENABLE_SOUND - // Initialize audio emulation - audio_init(); - - putstdio("AUDIO "); -#endif - -#if ENABLE_SDCARD - /* Load Save File. */ - read_cart_ram_file(&gb); -#endif - - putstdio("\n> "); - uint_fast32_t frames = 0; - uint64_t start_time = time_us_64(); - while(1) - { - int input; - - gb.gb_frame = 0; - - do { - __gb_step_cpu(&gb); - tight_loop_contents(); - } while(HEDLEY_LIKELY(gb.gb_frame == 0)); - - frames++; -#if ENABLE_SOUND - if(!gb.direct.frame_skip) { - audio_callback(NULL, stream, AUDIO_BUFFER_SIZE_BYTES); - i2s_dma_write(&i2s_config, stream); - } -#endif - - /* Update buttons state */ - prev_joypad_bits.up=gb.direct.joypad_bits.up; - prev_joypad_bits.down=gb.direct.joypad_bits.down; - prev_joypad_bits.left=gb.direct.joypad_bits.left; - prev_joypad_bits.right=gb.direct.joypad_bits.right; - prev_joypad_bits.a=gb.direct.joypad_bits.a; - prev_joypad_bits.b=gb.direct.joypad_bits.b; - prev_joypad_bits.select=gb.direct.joypad_bits.select; - prev_joypad_bits.start=gb.direct.joypad_bits.start; - gb.direct.joypad_bits.up=gpio_get(GPIO_UP); - gb.direct.joypad_bits.down=gpio_get(GPIO_DOWN); - gb.direct.joypad_bits.left=gpio_get(GPIO_LEFT); - gb.direct.joypad_bits.right=gpio_get(GPIO_RIGHT); - gb.direct.joypad_bits.a=gpio_get(GPIO_A); - gb.direct.joypad_bits.b=gpio_get(GPIO_B); - gb.direct.joypad_bits.select=gpio_get(GPIO_SELECT); - gb.direct.joypad_bits.start=gpio_get(GPIO_START); - - /* hotkeys (select + * combo)*/ - if(!gb.direct.joypad_bits.select) { -#if ENABLE_SOUND - if(!gb.direct.joypad_bits.up && prev_joypad_bits.up) { - /* select + up: increase sound volume */ - i2s_increase_volume(&i2s_config); - } - if(!gb.direct.joypad_bits.down && prev_joypad_bits.down) { - /* select + down: decrease sound volume */ - i2s_decrease_volume(&i2s_config); - } -#endif - if(!gb.direct.joypad_bits.right && prev_joypad_bits.right) { - /* select + right: select the next manual color palette */ - if(manual_palette_selected<12) { - manual_palette_selected++; - manual_assign_palette(palette,manual_palette_selected); - } - } - if(!gb.direct.joypad_bits.left && prev_joypad_bits.left) { - /* select + left: select the previous manual color palette */ - if(manual_palette_selected>0) { - manual_palette_selected--; - manual_assign_palette(palette,manual_palette_selected); - } - } - if(!gb.direct.joypad_bits.start && prev_joypad_bits.start) { - /* select + start: save ram and resets to the game selection menu */ -#if ENABLE_SDCARD - write_cart_ram_file(&gb); -#endif - goto out; - } - if(!gb.direct.joypad_bits.a && prev_joypad_bits.a) { - /* select + A: enable/disable frame-skip => fast-forward */ - gb.direct.frame_skip=!gb.direct.frame_skip; - printf("I gb.direct.frame_skip = %d\n",gb.direct.frame_skip); - } - } - - /* Serial monitor commands */ - input = getchar_timeout_us(0); - if(input == PICO_ERROR_TIMEOUT) - continue; - - switch(input) - { -#if 0 - static bool invert = false; - static bool sleep = false; - static uint8_t freq = 1; - static ili9225_color_mode_e colour = ILI9225_COLOR_MODE_FULL; - - case 'i': - invert = !invert; - mk_ili9225_display_control(invert, colour); - break; - - case 'f': - freq++; - freq &= 0x0F; - mk_ili9225_set_drive_freq(freq); - printf("Freq %u\n", freq); - break; -#endif - case 'c': - { - static ili9225_color_mode_e mode = ILI9225_COLOR_MODE_FULL; - union core_cmd cmd; - - mode = !mode; - cmd.cmd = CORE_CMD_IDLE_SET; - cmd.data = mode; - multicore_fifo_push_blocking(cmd.full); - break; - } - - case 'i': - gb.direct.interlace = !gb.direct.interlace; - break; - - case 'f': - gb.direct.frame_skip = !gb.direct.frame_skip; - break; - - case 'b': - { - uint64_t end_time; - uint32_t diff; - uint32_t fps; - - end_time = time_us_64(); - diff = end_time-start_time; - fps = ((uint64_t)frames*1000*1000)/diff; - printf("Frames: %u\n" - "Time: %lu us\n" - "FPS: %lu\n", - frames, diff, fps); - stdio_flush(); - frames = 0; - start_time = time_us_64(); - break; - } - - case '\n': - case '\r': - { - gb.direct.joypad_bits.start = 0; - break; - } - - case '\b': - { - gb.direct.joypad_bits.select = 0; - break; - } - - case '8': - { - gb.direct.joypad_bits.up = 0; - break; - } - - case '2': - { - gb.direct.joypad_bits.down = 0; - break; - } - - case '4': - { - gb.direct.joypad_bits.left= 0; - break; - } - - case '6': - { - gb.direct.joypad_bits.right = 0; - break; - } - - case 'z': - case 'w': - { - gb.direct.joypad_bits.a = 0; - break; - } - - case 'x': - { - gb.direct.joypad_bits.b = 0; - break; - } - - case 'q': - goto out; - - default: - break; - } - } -out: - puts("\nEmulation Ended"); - /* stop lcd task running on core 1 */ - multicore_reset_core1(); - -} - -} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..8445703 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,488 @@ +/** + * Copyright (C) 2022 by Mahyar Koshkouei + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +// Peanut-GB emulator settings +#define ENABLE_LCD 1 +#define ENABLE_SOUND 0 // 1 +#define ENABLE_SDCARD 0 // 1 +#define PEANUT_GB_HIGH_LCD_ACCURACY 1 +#define PEANUT_GB_USE_BIOS 0 + +/* Use DMA for all drawing to LCD. Benefits aren't fully realised at the moment + * due to busy loops waiting for DMA completion. */ +#define USE_DMA 0 + +/** + * Reducing VSYNC calculation to lower multiple. + * When setting a clock IRQ to DMG_CLOCK_FREQ_REDUCED, count to + * SCREEN_REFRESH_CYCLES_REDUCED to obtain the time required each VSYNC. + * DMG_CLOCK_FREQ_REDUCED = 2^18, and SCREEN_REFRESH_CYCLES_REDUCED = 4389. + * Currently unused. + */ +#define VSYNC_REDUCTION_FACTOR 16u +#define SCREEN_REFRESH_CYCLES_REDUCED (SCREEN_REFRESH_CYCLES / VSYNC_REDUCTION_FACTOR) +#define DMG_CLOCK_FREQ_REDUCED (DMG_CLOCK_FREQ / VSYNC_REDUCTION_FACTOR) + +/* C Headers */ +#include +#include +#include + +/* RP2040 Headers */ +#include + +/* Project headers */ +#include "hedley.h" +#include "minigb_apu.h" +#include "peanut_gb.h" +// #include "sdcard.h" +#include "core.h" +#include "game_bin.h" +#include "i2s.h" + +/* GPIO Connections. */ +#define GPIO_UP 16 +#define GPIO_DOWN 16 +#define GPIO_LEFT 16 +#define GPIO_RIGHT 16 +#define GPIO_A 16 +#define GPIO_B 16 +#define GPIO_SELECT 16 +#define GPIO_START 16 + +#if ENABLE_SOUND +/** + * Global variables for audio task + * stream contains N=AUDIO_SAMPLES samples + * each sample is 32 bits + * 16 bits for the left channel + 16 bits for the right channel in stereo interleaved format) + * This is intended to be played at AUDIO_SAMPLE_RATE Hz + */ +uint16_t* stream; +#endif + +/** Definition of ROM data + * We're going to erase and reprogram a region 1Mb from the start of the flash + * Once done, we can access this at XIP_BASE + 1Mb. + * Game Boy DMG ROM size ranges from 32768 bytes (e.g. Tetris) to 1,048,576 bytes (e.g. Pokemod Red) + */ +// #define FLASH_TARGET_OFFSET (1024 * 1024) +// const uint8_t *rom = (const uint8_t *)(XIP_BASE + FLASH_TARGET_OFFSET); +const uint8_t* rom = GAME_DATA; +static unsigned char rom_bank0[65536]; + +static uint8_t ram[32768]; +static uint8_t manual_palette_selected = 0; + +static struct +{ + unsigned a : 1; + unsigned b : 1; + unsigned select : 1; + unsigned start : 1; + unsigned right : 1; + unsigned left : 1; + unsigned up : 1; + unsigned down : 1; +} prev_joypad_bits; + +static struct gb_s gb; + +#define putstdio(x) write(1, x, strlen(x)) + +/** + * Returns a byte from the ROM file at the given address. + */ +uint8_t gb_rom_read(struct gb_s* gb, const uint_fast32_t addr) { + (void)gb; + if (addr < sizeof(rom_bank0)) + return rom_bank0[addr]; + + return rom[addr]; +} + +/** + * Returns a byte from the cartridge RAM at the given address. + */ +uint8_t gb_cart_ram_read(struct gb_s* gb, const uint_fast32_t addr) { + (void)gb; + return ram[addr]; +} + +/** + * Writes a given byte to the cartridge RAM at the given address. + */ +void gb_cart_ram_write(struct gb_s* gb, const uint_fast32_t addr, + const uint8_t val) { + ram[addr] = val; +} + +/** + * Ignore all errors. + */ +void gb_error(struct gb_s* gb, const enum gb_error_e gb_err, const uint16_t addr) { +#if 1 + const char* gb_err_str[4] = { + "UNKNOWN", + "INVALID OPCODE", + "INVALID READ", + "INVALID WRITE"}; + Serial.printf("Error %d occurred: %s at %04X\n.\n", gb_err, gb_err_str[gb_err], addr); +// abort(); +#endif +} + +void stop(); + +void start() { +#if ENABLE_LCD +#if ENABLE_SDCARD + /* ROM File selector */ + lcd_init(); + lcd_fill(TFT_BLACK); // 0x0000); + rom_file_selector(); +#endif +#endif + + /* Initialise GB context. */ + memcpy(rom_bank0, rom, sizeof(rom_bank0)); + auto ret = gb_init(&gb, &gb_rom_read, &gb_cart_ram_read, + &gb_cart_ram_write, &gb_error, NULL); + Serial.println("GB "); + + if (ret != GB_INIT_NO_ERROR) { + Serial.printf("Error: %d\n", ret); + stop(); + } + + /* Automatically assign a colour palette to the game */ + char rom_title[16]; + auto_assign_palette(palette, gb_colour_hash(&gb), gb_get_rom_name(&gb, rom_title)); + +#if ENABLE_LCD + gb_init_lcd(&gb, &lcd_draw_line); + + /* Start Core1, which processes requests to the LCD. */ + Serial.println("CORE1 "); + multicore_launch_core1(core1_init); + + Serial.println("LCD "); +#endif + +#if ENABLE_SOUND + // Initialize audio emulation + audio_init(); + + Serial.println("AUDIO "); +#endif + +#if ENABLE_SDCARD + /* Load Save File. */ + read_cart_ram_file(&gb); +#endif + + Serial.print("\n> "); +} + +void stop() { + Serial.println("\nEmulation Ended"); + /* stop lcd task running on core 1 */ + multicore_reset_core1(); + while (true) + ; +} + +void setup(void) { + /* Overclock. */ + { + const unsigned vco = 1596 * 1000 * 1000; /* 266MHz */ + const unsigned div1 = 6, div2 = 1; + + vreg_set_voltage(VREG_VOLTAGE_1_15); + sleep_ms(2); + set_sys_clock_pll(vco, div1, div2); + sleep_ms(2); + } + + /* Initialise USB serial connection for debugging. */ + Serial.begin(115200); + while (!Serial) + ; + +#if ENABLE_SDCARD + time_init(); +#endif + // sleep_ms(5000); + Serial.println("INIT: "); + + /* Initialise GPIO pins. */ + gpio_set_function(GPIO_UP, GPIO_FUNC_SIO); + gpio_set_function(GPIO_DOWN, GPIO_FUNC_SIO); + gpio_set_function(GPIO_LEFT, GPIO_FUNC_SIO); + gpio_set_function(GPIO_RIGHT, GPIO_FUNC_SIO); + gpio_set_function(GPIO_A, GPIO_FUNC_SIO); + gpio_set_function(GPIO_B, GPIO_FUNC_SIO); + gpio_set_function(GPIO_SELECT, GPIO_FUNC_SIO); + gpio_set_function(GPIO_START, GPIO_FUNC_SIO); + + gpio_set_dir(GPIO_UP, false); + gpio_set_dir(GPIO_DOWN, false); + gpio_set_dir(GPIO_LEFT, false); + gpio_set_dir(GPIO_RIGHT, false); + gpio_set_dir(GPIO_A, false); + gpio_set_dir(GPIO_B, false); + gpio_set_dir(GPIO_SELECT, false); + gpio_set_dir(GPIO_START, false); + + gpio_pull_up(GPIO_UP); + gpio_pull_up(GPIO_DOWN); + gpio_pull_up(GPIO_LEFT); + gpio_pull_up(GPIO_RIGHT); + gpio_pull_up(GPIO_A); + gpio_pull_up(GPIO_B); + gpio_pull_up(GPIO_SELECT); + gpio_pull_up(GPIO_START); + +/* Set SPI clock to use high frequency. */ +#if 0 + clock_configure(clk_peri, 0, + CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, + 125 * 1000 * 1000, 125 * 1000 * 1000); + spi_init(spi0, 30*1000*1000); + spi_set_format(spi0, 16, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); +#endif + +#if ENABLE_SOUND + // Allocate memory for the stream buffer + stream = malloc(AUDIO_BUFFER_SIZE_BYTES); + assert(stream != NULL); + memset(stream, 0, AUDIO_BUFFER_SIZE_BYTES); // Zero out the stream buffer + + // Initialize I2S sound driver + i2s_config_t i2s_config = i2s_get_default_config(); + i2s_config.sample_freq = AUDIO_SAMPLE_RATE; + i2s_config.dma_trans_count = AUDIO_SAMPLES; + i2s_volume(&i2s_config, 2); + i2s_init(&i2s_config); +#endif + + start(); +} + +void nextPalette() { + manual_palette_selected = manual_palette_selected < 12 ? manual_palette_selected + 1 : 0; + manual_assign_palette(palette, manual_palette_selected); +} + +void prevPalette() { + manual_palette_selected = manual_palette_selected > 0 ? manual_palette_selected - 1 : 12; + manual_assign_palette(palette, manual_palette_selected); +} + +void handleInput(uint_fast32_t& frames) { + static uint64_t start_time = time_us_64(); + + /* Serial monitor commands */ + int input = Serial.read(); + if (input <= 0) { + return; + } + + switch (input) { +#if 0 + static bool invert = false; + static bool sleep = false; + static uint8_t freq = 1; + static ili9225_color_mode_e colour = ILI9225_COLOR_MODE_FULL; + + case 'i': + invert = !invert; + mk_ili9225_display_control(invert, colour); + break; + + case 'f': + freq++; + freq &= 0x0F; + mk_ili9225_set_drive_freq(freq); + Serial.printf("Freq %u\n", freq); + break; +#endif + case 'c': { +#if 0 + static ili9225_color_mode_e mode = ILI9225_COLOR_MODE_FULL; + union core_cmd cmd; + + mode = !mode; + cmd.cmd = CORE_CMD_IDLE_SET; + cmd.data = mode; + multicore_fifo_push_blocking(cmd.full); +#endif + break; + } + + case 'i': + gb.direct.interlace = !gb.direct.interlace; + break; + + case 'f': + gb.direct.frame_skip = !gb.direct.frame_skip; + break; + + case 'b': { + uint64_t end_time; + uint32_t diff; + uint32_t fps; + + end_time = time_us_64(); + diff = end_time - start_time; + fps = ((uint64_t)frames * 1000 * 1000) / diff; + Serial.printf("Frames: %u\n" + "Time: %lu us\n" + "FPS: %lu\n", + frames, diff, fps); + Serial.flush(); + frames = 0; + start_time = time_us_64(); + break; + } + + case '\n': + case '\r': { + gb.direct.joypad_bits.start = 0; + break; + } + + case '\b': { + gb.direct.joypad_bits.select = 0; + break; + } + + case '8': { + gb.direct.joypad_bits.up = 0; + break; + } + + case '2': { + gb.direct.joypad_bits.down = 0; + break; + } + + case '4': { + gb.direct.joypad_bits.left = 0; + break; + } + + case '6': { + gb.direct.joypad_bits.right = 0; + break; + } + + case 'z': + case 'w': { + gb.direct.joypad_bits.a = 0; + break; + } + + case 'x': { + gb.direct.joypad_bits.b = 0; + break; + } + + case 'q': + stop(); + + case 'p': + nextPalette(); + + default: + break; + } +} + +void handlePad() { + /* Update buttons state */ + prev_joypad_bits.up = gb.direct.joypad_bits.up; + prev_joypad_bits.down = gb.direct.joypad_bits.down; + prev_joypad_bits.left = gb.direct.joypad_bits.left; + prev_joypad_bits.right = gb.direct.joypad_bits.right; + prev_joypad_bits.a = gb.direct.joypad_bits.a; + prev_joypad_bits.b = gb.direct.joypad_bits.b; + prev_joypad_bits.select = gb.direct.joypad_bits.select; + prev_joypad_bits.start = gb.direct.joypad_bits.start; + gb.direct.joypad_bits.up = gpio_get(GPIO_UP); + gb.direct.joypad_bits.down = gpio_get(GPIO_DOWN); + gb.direct.joypad_bits.left = gpio_get(GPIO_LEFT); + gb.direct.joypad_bits.right = gpio_get(GPIO_RIGHT); + gb.direct.joypad_bits.a = gpio_get(GPIO_A); + gb.direct.joypad_bits.b = gpio_get(GPIO_B); + gb.direct.joypad_bits.select = gpio_get(GPIO_SELECT); + gb.direct.joypad_bits.start = gpio_get(GPIO_START); + + /* hotkeys (select + * combo)*/ + if (!gb.direct.joypad_bits.select) { +#if ENABLE_SOUND + if (!gb.direct.joypad_bits.up && prev_joypad_bits.up) { + /* select + up: increase sound volume */ + i2s_increase_volume(&i2s_config); + } + if (!gb.direct.joypad_bits.down && prev_joypad_bits.down) { + /* select + down: decrease sound volume */ + i2s_decrease_volume(&i2s_config); + } +#endif + if (!gb.direct.joypad_bits.right && prev_joypad_bits.right) { + /* select + right: select the next manual color palette */ + nextPalette(); + } + if (!gb.direct.joypad_bits.left && prev_joypad_bits.left) { + /* select + left: select the previous manual color palette */ + prevPalette(); + } + if (!gb.direct.joypad_bits.start && prev_joypad_bits.start) { + /* select + start: save ram and resets to the game selection menu */ +#if ENABLE_SDCARD + write_cart_ram_file(&gb); +#endif + stop(); + } + if (!gb.direct.joypad_bits.a && prev_joypad_bits.a) { + /* select + A: enable/disable frame-skip => fast-forward */ + gb.direct.frame_skip = !gb.direct.frame_skip; + Serial.printf("I gb.direct.frame_skip = %d\n", gb.direct.frame_skip); + } + } +} + +void loop() { + static uint_fast32_t frames = 0; + + gb.gb_frame = 0; + + do { + __gb_step_cpu(&gb); + tight_loop_contents(); + } while (HEDLEY_LIKELY(gb.gb_frame == 0)); + + frames++; +#if ENABLE_SOUND + if (!gb.direct.frame_skip) { + audio_callback(NULL, stream, AUDIO_BUFFER_SIZE_BYTES); + i2s_dma_write(&i2s_config, stream); + } +#endif + + handlePad(); + handleInput(frames); +} diff --git a/src/mk_ili9225.c b/src/mk_ili9225.c deleted file mode 100644 index e11b542..0000000 --- a/src/mk_ili9225.c +++ /dev/null @@ -1,1297 +0,0 @@ -/** - * Copyright (C) 2019-2022 by Mahyar Koshkouei - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include "mk_ili9225.h" - -/* Register Descriptions. */ -/** - * The index register (IR) stored the register address. - * The register selection signal (RS) selects register access. - * The read/write signals (nRD/nWR) select read or write command, respectively. - * The data bus D0-D17 are used to read or write data. Register access is - * 16-bit, so 18-bit bus is reduced to 16-bit only (see page 45). - * On writing data GRAM, the internal address is automatically incremented. - */ - -/* ILI9225 Registers. */ -/** - * [Register Name] ([RO,WO, or WR]) - * R ([Read mask]): [Description of what can be read at mask] - * W ([Write mask]): [Description of what can be written at mask] - */ -/** - * Driver Code Read (RW) - * R (0xFFFF): Reads the device code integer 0x9225. - * W: (0xXXX1): Starts (0x1) or stops (0x0) internal oscillator. - * At least 10ms must pass after starting the internal oscillator to allow it - * to stabilise. - */ -#define MK_ILI9225_REG_DRIVER_CODE_READ 0x00 -#define MK_ILI9225_REG_START_OSCILLATION 0x00 -/** - * Driver Output Control (WO) - * W (0x8XXX): VSPL: Inverts the polarity of VSYNC pin. "1" is high active. - * W (0x4XXX): HSPL: Inverts the polarity of HSYNC pin. "1" is high active. - * W (0x2XXX): DPL: Same as above for DOTCLK. - * W (0x1XXX): EPL: Same as above for ENABLE. - * W (0xX4XX): SM: Scan order by gate driver. - * W (0xX2XX): GS: Shift direction of gate driver. - * W (0xX1XX): SS: Shift direction from source driver. - * W (0xXX1F): NL: Set active gate driver line. Default: 0b11011. - */ -#define MK_ILI9225_REG_DRIVER_OUTPUT_CTRL 0x01 -/** - * LCD Driving Waveform Control (WO) - * Set LCD inversion method. - * W (0xX3XX): INV: Sets inversion method. - * W (0xXXX1): FLD: 3-field interlaced scanning function. - */ -#define MK_ILI9225_REG_LCD_AC_DRIVING_CTRL 0x02 -/** - * Entry Mode (WO) - * W (0x1XXX): BGR: Swap Red with Blue. - * W (0xX3XX): MDT: Data transmission format for 80-mode. - * W (0xXX2X): ID1: Control horizontal address counter increment (1) or - * decrement (0). - * W (0xXX1X): ID0: Control vertical address counter increment (1) or - * decrement (0). - * W (0xXXX8): AM: GRAM update direction. "1" is horizontal. - */ -#define MK_ILI9225_REG_ENTRY_MODE 0x03 -/** - * Display Control 1 (WO) - * W (0x1XXX): TEMON: Sets the frame flag output signal FLM. - * W (0xXX1X): GON: Set output level of gate driver. VGL = 0, Normal = 1. - * W (0xXXX8): CL: Selects 8-colour display mode when 1. Else 2^18 colours. - * W (0xXXX4): REV: Reverses greyscale levels if set. - * W (0xXXX3): D: Control display output. 0b11 to turn on display, else display - * is off. - */ -#define MK_ILI9225_REG_DISPLAY_CTRL 0x07 -/** - * Display Control 2 (WO) - * Used for I80, M68, and RGB modes only. - * W (0xXFXX): FP: Specify number of front porch lines. - * W (0xXXXF): BP: Specify number of back porch lines. - */ -#define MK_ILI9225_REG_BLANK_PERIOD_CTRL 0x08 -/** - * Frame Cycle Control (WO) - * Used for RGB mode only. - * W (0xFXXX): NO: Set amount of non-overlay for gate output. - * W (0xXFXX): SDT: Set delay from gate edge to source output. - * W (0xXXXF): RTN: Set number of clock cycles for one display line. - */ -#define MK_ILI9225_REG_FRAME_CYCLE_CTRL 0x0B -/** - * RGB Input Interface Control (W) - * Used for RGB mode only. - */ -#define MK_ILI9225_REG_INTERFACE_CTRL 0x0C -/** - * Oscillator Control (W) - * W (0xXFXX): FOSC: Oscillation frequency. - * W (0xXXX1): OSC_EN: Starts oscillation from halt state. 10ms wait required. - */ -#define MK_ILI9225_REG_OSC_CTRL 0x0F -/** - * Power Control 1 (W) - * W (0xXFXX): SAP: Set current level of driver. - * W (0xXXX2): DSTB: Enable deep standby mode. Writing to GRAM is prohibited. - * W (0xXXX1): STB: Enable sleep mode. Stops display and internal oscillator. - */ -#define MK_ILI9225_REG_PWR_CTRL1 0x10 -/** - * Power Control 2 (W) - * W (0x1XXX): APON: Use automatic boosting operation. - * W (0xXFXX): PON: Set boosting operation for specific circuits. - * W (0xXX2X): AON: Set operation of amplifier. - * W (0xXX1X): VCL1EN: Set operation of generation amplifier. - * W (0xXXXF): VC: Set VCl1 voltage output. - */ -#define MK_ILI9225_REG_PWR_CTRL2 0x11 -/** - * Power Control 3 (W) - * W (0x7XXX): BT: Set output factor of boost converter. - * W (0xX7XX): DC1: Operation frequency of circuit 1 boost converter. - * W (0xXX7X): DC2: Operation frequency of circuit 2 boost converter. - * W (0XXXX7): DC3: Operation frequency of circuit 3 boost converter. - */ -#define MK_ILI9225_REG_PWR_CTRL3 0x12 -/** - * Power Control 4 (W) - * W (0xXX7F): GVD: Set gamma voltage (GVDD) from 2.66V to 5.5V. - */ -#define MK_ILI9225_REG_PWR_CTRL4 0x13 -/** - * Power Control 5 (W) - * W (0x8XXX): VCOMG: Sets amplitude of VCOM signal. - * W (0x7FXX): VCM: Sets VCOMH voltage, from 0.4015 to 1.1000 * GVDD. - * W (0xXX7F): VML: Sets alternate amplitudes of VCOM from 0.534 to 1.200*GVDD. - */ -#define MK_ILI9225_REG_PWR_CTRL5 0x14 -/** - * VCI Recycling (W) - * W (0xXX7X): VCIR: Number of clock cycles in VCI recycling period. - */ -#define MK_ILI9225_REG_VCI_RECYCLING 0x15 -/** - * RAM Address Set 1 (W) - * W (0xXXFF): AD[7:0]: Set the initial value of the address counter. - */ -#define MK_ILI9225_REG_RAM_ADDR_SET1 0x20 -/** - * RAM Address Set 2 (W) - * W (0xXXFF): AD[15:8]: Set the initial value of the address counter. - */ -#define MK_ILI9225_REG_RAM_ADDR_SET2 0x21 -/** - * Read/Write GRAM Data (RW) - * W (0x3FFFF): Write data to GRAM. - * R (0x3FFFF): Read data from GRAM. - */ -#define MK_ILI9225_REG_GRAM_RW 0x22 -/** - * Software Reset (W) - * W (0xFFFF): Perform software reset when 0x00CE is written. - */ -#define MK_ILI9225_REG_SOFT_RESET 0x28 -/** - * Gate Scan Control (W) - * W (0xXX1F): SCN: Line to start gate scan from. - */ -#define MK_ILI9225_REG_GATE_SCAN_CTRL 0x30 -/** - * Vertical Scroll Control 1 (W) - * W (0xXXFF): SEA: Scroll end address. - */ -#define MK_ILI9225_REG_VERT_SCROLL_CTRL1 0x31 -/** - * Vertical Scroll Control 2 (W) - * W (0xXXFF): SSA: Scroll start address. - */ -#define MK_ILI9225_REG_VERT_SCROLL_CTRL2 0x32 -/** - * Vertical Scroll Control 3 (W) - * W (0xXXFF): SST: Scroll step. - */ -#define MK_ILI9225_REG_VERT_SCROLL_CTRL3 0x33 -/** - * Partial Screen Driving Position (W) - * W (0xXXFF): SE: High byte of screen end position. - */ -#define MK_ILI9225_REG_PART_DRIVING_POS1 0x34 -/** - * Partial Screen Driving Position (W) - * W (0xXXFF): SS: High byte of screen start position. - */ -#define MK_ILI9225_REG_PART_DRIVING_POS2 0x35 -/** - * Horizontal RAM Address Position (W) - * W (0xXXFF): HEA: End horizontal position. - */ -#define MK_ILI9225_REG_HORI_WIN_ADDR1 0x36 -/** - * Horizontal RAM Address Position (W) - * W (0xXXFF): HSA: Start horizontal position. - */ -#define MK_ILI9225_REG_HORI_WIN_ADDR2 0x37 -/** - * Vertical RAM Address Position (W) - * W (0xXXFF): VEA: End vertical position. - */ -#define MK_ILI9225_REG_VERT_WIN_ADDR1 0x38 -/** - * Vertical RAM Address Position (W) - * W (0xXXFF): VSA: Start vertical position. - */ -#define MK_ILI9225_REG_VERT_WIN_ADDR2 0x39 -/** - * Gamma Control (W) - */ -#define MK_ILI9225_REG_GAMMA_CTRL1 0x50 -#define MK_ILI9225_REG_GAMMA_CTRL2 0x51 -#define MK_ILI9225_REG_GAMMA_CTRL3 0x52 -#define MK_ILI9225_REG_GAMMA_CTRL4 0x53 -#define MK_ILI9225_REG_GAMMA_CTRL5 0x54 -#define MK_ILI9225_REG_GAMMA_CTRL6 0x55 -#define MK_ILI9225_REG_GAMMA_CTRL7 0x56 -#define MK_ILI9225_REG_GAMMA_CTRL8 0x57 -#define MK_ILI9225_REG_GAMMA_CTRL9 0x58 -#define MK_ILI9225_REG_GAMMA_CTRL10 0x59 - -#define MK_ILI9225_NV_MEM_DATA_PROG 0x60 -#define MK_ILI9225_NV_MEM_CTRL 0x61 -#define MK_ILI9225_NV_MEM_STAT 0x62 -#define MK_ILI9225_NV_MEM_PROTECTION_KEY 0x63 -#define MK_ILI9225_NV_MEM_ID_CODE 0x65 - -#define MK_ILI9225_REG_MTP_TEST_KEY 0x80 -#define MK_ILI9225_REG_MTP_CTRL_REG 0x81 -#define MK_ILI9225_REG_MTP_DATA_READ 0x82 - -/* Useful macros. */ -#define ARRAYSIZE(array) (sizeof(array)/sizeof(array[0])) - -/* Structure definitions. */ -struct reg_dat_pair { - uint16_t reg; - uint16_t dat; -}; - -/* Local functions. */ -static void write_register(uint16_t cmd); -static void write_data(uint16_t dat); -#if MK_ILI9225_READ_AVAILABLE -static uint16_t read_data(void); -static uint16_t get_register(uint16_t reg); -#endif - -static void write_register(uint16_t cmd) -{ - mk_ili9225_set_rs(0); - mk_ili9225_set_cs(0); - mk_ili9225_spi_write16(&cmd, 1); - mk_ili9225_set_cs(1); -} - -static void write_data(uint16_t dat) -{ - mk_ili9225_set_rs(1); - mk_ili9225_set_cs(0); - mk_ili9225_spi_write16(&dat, 1); - mk_ili9225_set_cs(1); -} - -#if MK_ILI9225_READ_AVAILABLE -static uint16_t read_data(void) -{ - uint16_t ret; - mk_ili9225_set_rs(1); - mk_ili9225_set_cs(0); - ret = mk_ili9225_spi_read16(); - mk_ili9225_set_cs(1); - return ret; -} - -static uint16_t get_register(uint16_t reg) -{ - write_register(reg); - return read_data(); -} -#endif - -static void set_register(uint16_t reg, uint16_t dat) -{ - write_register(reg); - write_data(dat); -} - -#if MK_ILI9225_READ_AVAILABLE -unsigned mk_ili9225_read_driving_line(void) -{ - unsigned line; - mk_ili9225_set_rs(0); - mk_ili9225_set_cs(0); - line = read_data(); - mk_ili9225_set_cs(1); - return (line >> 8); -} -#endif - -/* Public functions. */ -unsigned mk_ili9225_init(void) -{ - unsigned ret = 0x9225; - - /* Initialise reset pin as not reset. RST must be high before reset. */ - mk_ili9225_set_rst(1); - /* Initialise other pins too. */ - mk_ili9225_set_cs(1); - mk_ili9225_set_rs(0); - /* Make sure pin is initialised by setting small delay. */ - mk_ili9225_delay_ms(1); - - /* Put ILI9225 into reset. */ - mk_ili9225_set_rst(0); - /* Reset low-level width (Tres) is at least 1ms. */ - mk_ili9225_delay_ms(10); - - /* Bring ILI9225 out of reset. */ - mk_ili9225_set_rst(1); - mk_ili9225_delay_ms(50); - - /* Turn backlight off initially */ - mk_ili9225_set_led(0); - - /* Initialise power registers. */ - /* FIXME: This may not be required. All are initialised to 0x00, - * FIXME: but VCOMG is set to 1 on init apparently. */ - { - const uint8_t regs[] = { - MK_ILI9225_REG_PWR_CTRL1, MK_ILI9225_REG_PWR_CTRL2, - MK_ILI9225_REG_PWR_CTRL3, MK_ILI9225_REG_PWR_CTRL4, - MK_ILI9225_REG_PWR_CTRL5 - }; - - for(uint8_t i = 0; i < sizeof(regs); i++) - set_register(regs[i], 0x0000); - } - mk_ili9225_delay_ms(40); - - /* Switch on power control. */ - { - /* VCI set to 2.58V. */ - /* FIXME: CTRL3 VGH is set to 2.58*6=15.48, but it could be - * FIXME: lower ( 9 < VGH < 16.5). - * FIXME: CTRL3 VGL is set to 2.58*-4=-10.32, but it could be - * FIXME: higher ( -4 < VGL < -16.5). - * FIXME: BT could be set to 0b000 for VGH=10, VGL=-7.5. */ - /* CTRL4: GVDD is set to 4.68V. */ - /* CTRL5: Set VCM to 0.8030V. Set VML to 1.104V. */ - /* CTRL1: Set driving capability to "Medium Fast 1". */ - const uint8_t regs[] = { - MK_ILI9225_REG_PWR_CTRL2, MK_ILI9225_REG_PWR_CTRL3, - MK_ILI9225_REG_PWR_CTRL4, MK_ILI9225_REG_PWR_CTRL5, - MK_ILI9225_REG_PWR_CTRL1 - }; - const uint16_t dat[] = { - 0x0018, 0x6121, 0x006F, 0x495F, 0x0800 - }; - - for(uint8_t i = 0; i < sizeof(regs); i++) - set_register(regs[i], dat[i]); - } - mk_ili9225_delay_ms(10); - - /* Enable automatic booster operation, and amplifiers. - * Set VCI1 to 2.76V. - * FIXME: why is VCI1 changed from 2.58 to 2.76V? */ - set_register(MK_ILI9225_REG_PWR_CTRL2, 0x103B); - mk_ili9225_delay_ms(50); - - { - const struct reg_dat_pair cmds[] = { - /* Set shift direction SS from S528 to S1. - * Set active lines NL to 528 * 220 dots. */ - { MK_ILI9225_REG_DRIVER_OUTPUT_CTRL, 0x011C }, - /* Set LCD inversion to disabled. */ - { MK_ILI9225_REG_LCD_AC_DRIVING_CTRL, 0x0100 }, - /* Increment vertical and horizontal address. - * Use vertical image. */ - { MK_ILI9225_REG_ENTRY_MODE, 0x1018 }, - /* Turn off all display outputs. */ - { MK_ILI9225_REG_DISPLAY_CTRL, 0x0000 }, - /* Set porches to 8 lines. */ - { MK_ILI9225_REG_BLANK_PERIOD_CTRL, 0x0808 }, - /* Use 1-clock delay to gate output and edge. */ - { MK_ILI9225_REG_FRAME_CYCLE_CTRL, 0x1100 }, - /* Ignore RGB interface settings. */ - { MK_ILI9225_REG_INTERFACE_CTRL, 0x0000 }, - /* Set oscillation frequency to 266.6 kHz. */ - { MK_ILI9225_REG_OSC_CTRL, 0x0701 }, - /* Set VCI recycling to 2 clocks. */ - { MK_ILI9225_REG_VCI_RECYCLING, 0x0020 }, - /* Initialise RAM Address to 0x0 px. */ - { MK_ILI9225_REG_RAM_ADDR_SET1, 0x0000 }, - { MK_ILI9225_REG_RAM_ADDR_SET2, 0x0000 }, - - /* Set scanning position to full screen. */ - { MK_ILI9225_REG_GATE_SCAN_CTRL, 0x0000 }, - /* Set end scan position to 219 + 1 px (0xDB). */ - { MK_ILI9225_REG_VERT_SCROLL_CTRL1, 0x00DB }, - /* Set start scan position to 0 px. */ - { MK_ILI9225_REG_VERT_SCROLL_CTRL2, 0x0000 }, - /* Set scroll step to 0 px (no scrolling). */ - { MK_ILI9225_REG_VERT_SCROLL_CTRL3, 0x0000 }, - /* Set partial screen driving end to 219 + 1 px - * (0xDB). */ - { MK_ILI9225_REG_PART_DRIVING_POS1, 0x00DB }, - /* Set partial screen driving start to 0 px. */ - { MK_ILI9225_REG_PART_DRIVING_POS2, 0x0000 }, - /* Set window to 176 x 220 px (full screen). */ - { MK_ILI9225_REG_HORI_WIN_ADDR1, 0x00AF }, - { MK_ILI9225_REG_HORI_WIN_ADDR2, 0x0000 }, - { MK_ILI9225_REG_VERT_WIN_ADDR1, 0x00DB }, - { MK_ILI9225_REG_VERT_WIN_ADDR2, 0x0000 }, - - /* Gamma curve data. */ - { MK_ILI9225_REG_GAMMA_CTRL1, 0x0000 }, - { MK_ILI9225_REG_GAMMA_CTRL2, 0x0808 }, - { MK_ILI9225_REG_GAMMA_CTRL3, 0x080A }, - { MK_ILI9225_REG_GAMMA_CTRL4, 0x000A }, - { MK_ILI9225_REG_GAMMA_CTRL5, 0x0A08 }, - { MK_ILI9225_REG_GAMMA_CTRL6, 0x0808 }, - { MK_ILI9225_REG_GAMMA_CTRL7, 0x0000 }, - { MK_ILI9225_REG_GAMMA_CTRL8, 0x0A00 }, - { MK_ILI9225_REG_GAMMA_CTRL9, 0x0710 }, - { MK_ILI9225_REG_GAMMA_CTRL10, 0x0710 }, - - /* Enable full colour display. */ - { MK_ILI9225_REG_DISPLAY_CTRL, 0x0012 } - }; - - for(uint_fast8_t i = 0; i < (uint_fast8_t)ARRAYSIZE(cmds); i++) - set_register(cmds[i].reg, cmds[i].dat); - } - mk_ili9225_delay_ms(50); - - /** - * FIXME: TEMON is enabled but FLM isn't exposed? - * GON: Enable display. - * CL: Use full colour. - * REV: reverse greyscale levels. - * D: Switch on display. - */ - set_register(MK_ILI9225_REG_DISPLAY_CTRL, 0x1017); - mk_ili9225_delay_ms(50); - - /* Turn on backlight */ - mk_ili9225_set_led(1); - -#if MK_ILI9225_READ_AVAILABLE - /* ret should be 0 if the returned ID was 0x9225. */ - ret -= get_register(MK_ILI9225_REG_DRIVER_CODE_READ); -#else - ret = 0; -#endif - - return ret; -} - -void mk_ili9225_display_control(bool invert, ili9225_color_mode_e colour_mode) -{ - uint16_t dat = 0x0013; - dat |= ((uint16_t)invert << 2); - dat |= ((uint16_t)colour_mode << 3); - set_register(MK_ILI9225_REG_DISPLAY_CTRL, dat); -} - -void mk_ili9225_set_window(uint16_t hor_start, uint16_t hor_end, - uint16_t vert_start, uint16_t vert_end) -{ - assert(hor_start < hor_end); - assert(hor_end < SCREEN_SIZE_X); - assert(vert_start < vert_end); - assert(vert_end < SCREEN_SIZE_Y); - - set_register(MK_ILI9225_REG_HORI_WIN_ADDR1, hor_end); - set_register(MK_ILI9225_REG_HORI_WIN_ADDR2, hor_start); - set_register(MK_ILI9225_REG_VERT_WIN_ADDR1, vert_end); - set_register(MK_ILI9225_REG_VERT_WIN_ADDR2, vert_start); - set_register(MK_ILI9225_REG_RAM_ADDR_SET1, hor_start); - set_register(MK_ILI9225_REG_RAM_ADDR_SET2, vert_start); -} - -void mk_ili9225_set_x(uint8_t x) -{ - set_register(MK_ILI9225_REG_RAM_ADDR_SET1, x); -} - -void mk_ili9225_set_address(uint8_t x, uint8_t y) -{ - set_register(MK_ILI9225_REG_RAM_ADDR_SET1, x); - set_register(MK_ILI9225_REG_RAM_ADDR_SET2, y); -} - -void mk_ili9225_write_pixels(const uint16_t *pixels, uint_fast16_t nmemb) -{ - assert(pixels != NULL); - assert(nmemb > 0); - - write_register(MK_ILI9225_REG_GRAM_RW); - mk_ili9225_set_rs(1); - mk_ili9225_set_cs(0); - mk_ili9225_spi_write16(pixels, nmemb); - mk_ili9225_set_cs(1); -} - -void mk_ili9225_write_pixels_start(void) -{ - write_register(MK_ILI9225_REG_GRAM_RW); - mk_ili9225_set_rs(1); - mk_ili9225_set_cs(0); -} - -void mk_ili9225_write_pixels_end(void) -{ - mk_ili9225_set_cs(1); -} - -void mk_ili9225_power_control(uint8_t drive_power, bool sleep) -{ - uint16_t data; - data = (uint16_t)drive_power << 8; - data |= sleep; - set_register(MK_ILI9225_REG_PWR_CTRL1, data); -} - -void mk_ili9225_set_gate_scan(uint16_t hor_start, uint16_t hor_end) -{ - uint16_t dat = 0x0100; - uint16_t lcd_line_start = hor_end / 8; - set_register(MK_ILI9225_REG_DRIVER_OUTPUT_CTRL, dat | lcd_line_start); - set_register(MK_ILI9225_REG_GATE_SCAN_CTRL, hor_start / 8); -} - -void mk_ili9225_set_drive_freq(uint16_t f) -{ - f &= 0x000F; - f <<= 8; - f |= 1; - set_register(MK_ILI9225_REG_OSC_CTRL, f); -} - -void mk_ili9225_exit(void) -{ -} - -void mk_ili9225_fill_rect(uint8_t x,uint8_t y,uint8_t w,uint8_t h,uint16_t color) -{ - set_register(MK_ILI9225_REG_ENTRY_MODE,0x1018); - set_register(MK_ILI9225_REG_HORI_WIN_ADDR1, y+h-1); // y_max - set_register(MK_ILI9225_REG_HORI_WIN_ADDR2, y); // y_min - set_register(MK_ILI9225_REG_VERT_WIN_ADDR1, 219-x); // x_max - set_register(MK_ILI9225_REG_VERT_WIN_ADDR2, 219-(x+w-1)); // x_min - set_register(MK_ILI9225_REG_RAM_ADDR_SET1,y); - set_register(MK_ILI9225_REG_RAM_ADDR_SET2,219-x); - write_register(MK_ILI9225_REG_GRAM_RW); - mk_ili9225_set_rs(1); - mk_ili9225_set_cs(0); - for(uint16_t i=0;i27*8) { - break; - } - } -} \ No newline at end of file diff --git a/src/tft-espi-config/tft_setup.h b/src/tft-espi-config/tft_setup.h new file mode 100644 index 0000000..43688c1 --- /dev/null +++ b/src/tft-espi-config/tft_setup.h @@ -0,0 +1,164 @@ +// USER DEFINED SETTINGS +// Set driver type, fonts to be loaded, pins used and SPI control method etc. +// +// See the User_Setup_Select.h file if you wish to be able to define multiple +// setups and then easily select which setup file is used by the compiler. +// +// If this file is edited correctly then all the library example sketches should +// run without the need to make any more changes for a particular hardware setup! +// Note that some sketches are designed for a particular TFT pixel width/height + +// User defined information reported by "Read_User_Setup" test & diagnostics example +#define USER_SETUP_INFO "User_Setup" + +// Define to disable all #warnings in library (can be put in User_Setup_Select.h) +//#define DISABLE_ALL_LIBRARY_WARNINGS + +// ################################################################################## +// +// Section 1. Call up the right driver file and any options for it +// +// ################################################################################## + +// Tell the library to use parallel mode (otherwise SPI is assumed) +//#define TFT_PARALLEL_8_BIT +#define TFT_PARALLEL_16_BIT // **** 16-bit parallel ONLY for RP2040 processor **** + +// Display type - only define if RPi display +//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI + +// Only define one driver, the other ones must be commented out +#define ILI9341_DRIVER // Generic driver for common displays +//#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172 + +#if 0 +#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display +#define TFT_INVERSION_OFF +#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red +#endif + +// For ST7789, ST7735, ILI9163 and GC9A01 ONLY, define the pixel width and height in portrait orientation +#define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320 +#define TFT_HEIGHT 320 // ST7789 240 x 320 + +// ################################################################################## +// +// Section 2. Define the pins that are used to interface with the display here +// +// ################################################################################## + +// If a backlight control signal is available then define the TFT_BL pin in Section 2 +// below. The backlight will be turned ON when tft.begin() is called, but the library +// needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be +// driven with a PWM signal or turned OFF/ON then this must be handled by the user +// sketch. e.g. with digitalWrite(TFT_BL, LOW); + +// #define TFT_BL 32 // LED back-light control pin +// #define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW) + +#define TFT_D0 0 +#define TFT_D1 1 +#define TFT_D2 2 +#define TFT_D3 3 +#define TFT_D4 4 +#define TFT_D5 5 +#define TFT_D6 6 +#define TFT_D7 7 +#define TFT_WR 19 // Write strobe for modified Raspberry Pi TFT only +#define TFT_DC 20 // Data Command control pin +#define TFT_CS 21 // Chip select control pin D8 +#define TFT_RST 22 // Reset pin (could connect to NodeMCU RST, see next line) +//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V + +// ################################################################################## +// +// Section 3. Define the fonts that are to be used here +// +// ################################################################################## + +// Comment out the #defines below with // to stop that font being loaded +// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not +// normally necessary. If all fonts are loaded the extra FLASH space required is +// about 17Kbytes. To save FLASH space only enable the fonts you need! + +#if 0 +#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH +#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters +#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters +#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm +#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. +#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. +//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT +#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts + +// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded +// this will save ~20kbytes of FLASH +#define SMOOTH_FONT +#endif + +// ################################################################################## +// +// Section 4. Other options +// +// ################################################################################## + +// For RP2040 processor and SPI displays, uncomment the following line to use the PIO interface. +//#define RP2040_PIO_SPI // Leave commented out to use standard RP2040 SPI port interface + +// For RP2040 processor and 8 or 16-bit parallel displays: +// The parallel interface write cycle period is derived from a division of the CPU clock +// speed so scales with the processor clock. This means that the divider ratio may need +// to be increased when overclocking. It may also need to be adjusted dependant on the +// display controller type (ILI94341, HX8357C etc.). If RP2040_PIO_CLK_DIV is not defined +// the library will set default values which may not suit your display. +// The display controller data sheet will specify the minimum write cycle period. The +// controllers often work reliably for shorter periods, however if the period is too short +// the display may not initialise or graphics will become corrupted. +// PIO write cycle frequency = (CPU clock/(4 * RP2040_PIO_CLK_DIV)) +//#define RP2040_PIO_CLK_DIV 1 // 32ns write cycle at 125MHz CPU clock +//#define RP2040_PIO_CLK_DIV 2 // 64ns write cycle at 125MHz CPU clock +//#define RP2040_PIO_CLK_DIV 3 // 96ns write cycle at 125MHz CPU clock + +// For the RP2040 processor define the SPI port channel used (default 0 if undefined) +//#define TFT_SPI_PORT 1 // Set to 0 if SPI0 pins are used, or 1 if spi1 pins used + +// For the STM32 processor define the SPI port channel used (default 1 if undefined) +//#define TFT_SPI_PORT 2 // Set to 1 for SPI port 1, or 2 for SPI port 2 + +// Define the SPI clock frequency, this affects the graphics rendering speed. Too +// fast and the TFT driver will not keep up and display corruption appears. +// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails +// With a ST7735 display more than 27MHz may not work (spurious pixels and lines) +// With an ILI9163 display 27 MHz works OK. + +// #define SPI_FREQUENCY 1000000 +// #define SPI_FREQUENCY 5000000 +// #define SPI_FREQUENCY 10000000 +// #define SPI_FREQUENCY 20000000 +#define SPI_FREQUENCY 27000000 +// #define SPI_FREQUENCY 40000000 +// #define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz) +// #define SPI_FREQUENCY 80000000 + +// Optional reduced SPI frequency for reading TFT +#define SPI_READ_FREQUENCY 20000000 + +// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: +#define SPI_TOUCH_FREQUENCY 2500000 + +// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default. +// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) +// then uncomment the following line: +//#define USE_HSPI_PORT + +// Comment out the following #define if "SPI Transactions" do not need to be +// supported. When commented out the code size will be smaller and sketches will +// run slightly faster, so leave it commented out unless you need it! + +// Transaction support is needed to work with SD library but not needed with TFT_SdFat +// Transaction support is required if other SPI devices are connected. + +// Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) +// so changing it here has no effect + +// #define SUPPORT_TRANSACTIONS