diff --git a/rp2040/CMakeLists.txt b/rp2040/CMakeLists.txt new file mode 100644 index 0000000..86f649d --- /dev/null +++ b/rp2040/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.13) + +include(pico_sdk_import.cmake) + +project(par_spi C CXX ASM) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +pico_sdk_init() + +add_executable(par_spi par_spi.c) + +pico_add_extra_outputs(par_spi) + +target_link_libraries(par_spi pico_stdlib hardware_spi) diff --git a/rp2040/README.md b/rp2040/README.md new file mode 100644 index 0000000..3f1778a --- /dev/null +++ b/rp2040/README.md @@ -0,0 +1,19 @@ +# RP2040 version + +The code for the AVR microcontroller has been ported to the RP2040 microcontroller. +RP2040 is the microcontroller used on the Raspberry Pi Pico board. + +## Build instructions + +The [Raspberry Pi Pico SDK](https://github.com/raspberrypi/pico-sdk) must be installed. +Then, standing in the rp2040 subdirectory, execute: + +```bash +mkdir build +cd build +export PICO_SDK_PATH=~/pico/pico-sdk +cmake .. +make +``` + +Copy the generated file `build/par_spi.uf2` to the microcontroller's flash. diff --git a/rp2040/par_spi.c b/rp2040/par_spi.c new file mode 100644 index 0000000..be17cb8 --- /dev/null +++ b/rp2040/par_spi.c @@ -0,0 +1,206 @@ +/* + * Written in October 2022 by Niklas Ekström. + * + * Runs on RP2040 microcontroller instead of AVR as before, + * but uses the same protocol and Amiga software. + */ +#include "hardware/gpio.h" +#include "hardware/spi.h" + +// Pin name GPIO Direction Comment Description +#define PIN_D(x) (0+x) // In/out +#define PIN_IRQ 8 // Output Active low +#define PIN_ACT 9 // Output Active low +#define PIN_CLK 10 // Input +#define PIN_REQ 11 // Input Active low +#define PIN_MISO 16 // Input Pull-up +#define PIN_SS 17 // Output Active low +#define PIN_SCK 18 // Output +#define PIN_MOSI 19 // Output +#define PIN_CDET 20 // Input Pull-up Card Detect + +#define SPI_SLOW_FREQUENCY (400*1000) +#define SPI_FAST_FREQUENCY (16*1000*1000) + +static uint32_t prev_cdet; + +static void handle_request() { + uint32_t pins; + + while (1) { + pins = gpio_get_all(); + if (!(pins & (1 << PIN_REQ))) + break; + + if ((pins & (1 << PIN_CDET)) != prev_cdet) { + gpio_put(PIN_IRQ, false); + gpio_set_dir(PIN_IRQ, true); + prev_cdet = pins & (1 << PIN_CDET); + } + } + + uint32_t prev_clk = pins & (1 << PIN_CLK); + + if ((pins & 0xc0) != 0xc0) { + uint32_t byte_count = 0; + bool read = false; + + if (!(pins & 0x80)) { // READ1 or WRITE1 + read = !!(pins & 0x40); + byte_count = pins & 0x3f; + + gpio_put(PIN_ACT, 0); + } else { // READ2 or WRITE2 + byte_count = (pins & 0x3f) << 7; + + gpio_put(PIN_ACT, 0); + + while (1) { + pins = gpio_get_all(); + if ((pins & (1 << PIN_CLK)) != prev_clk) + break; + + if (pins & (1 << PIN_REQ)) + return; + } + + read = !!(pins & 0x80); + byte_count |= pins & 0x7f; + prev_clk = pins & (1 << PIN_CLK); + } + + if (read) { + spi_get_hw(spi0)->dr = 0xff; + + uint32_t prev_ss = pins & (1 << PIN_SS); + + while (1) { + while (!spi_is_readable(spi0)) + tight_loop_contents(); + + uint32_t value = spi_get_hw(spi0)->dr; + + while (1) { + pins = gpio_get_all(); + if ((pins & (1 << PIN_CLK)) != prev_clk) + break; + + if (pins & (1 << PIN_REQ)) + return; + } + + gpio_put_all(prev_ss | value); + gpio_set_dir_out_masked(0xff); + + if (!byte_count) + break; + + spi_get_hw(spi0)->dr = 0xff; + prev_clk = pins & (1 << PIN_CLK); + byte_count--; + } + } else { + while (1) { + while (1) { + pins = gpio_get_all(); + if ((pins & (1 << PIN_CLK)) != prev_clk) + break; + + if (pins & (1 << PIN_REQ)) + return; + } + + spi_get_hw(spi0)->dr = pins & 0xff; + + while (!spi_is_readable(spi0)) + tight_loop_contents(); + + (void)spi_get_hw(spi0)->dr; + + if (!byte_count) + break; + + prev_clk = pins & (1 << PIN_CLK); + byte_count--; + } + } + } else { + switch ((pins & 0x3e) >> 1) { + case 0: { // SPI_SELECT + gpio_put(PIN_SS, !(pins & 1)); + gpio_put(PIN_ACT, 0); + break; + } + case 1: { // CARD_PRESENT + gpio_set_dir(PIN_IRQ, false); + gpio_put(PIN_ACT, 0); + + while (1) { + pins = gpio_get_all(); + if ((pins & (1 << PIN_CLK)) != prev_clk) + break; + + if (pins & (1 << PIN_REQ)) + return; + } + + gpio_put(PIN_D(0), !gpio_get(PIN_CDET)); + gpio_set_dir_out_masked(0xff); + break; + } + case 2: { // SPEED + spi_set_baudrate(spi0, pins & 1 ? + SPI_FAST_FREQUENCY : + SPI_SLOW_FREQUENCY); + + gpio_put(PIN_ACT, 0); + break; + } + } + } + + while (1) { + pins = gpio_get_all(); + if (pins & (1 << PIN_REQ)) + break; + } +} + +int main() { + spi_init(spi0, SPI_SLOW_FREQUENCY); + + gpio_set_function(PIN_SCK, GPIO_FUNC_SPI); + gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI); + gpio_set_function(PIN_MISO, GPIO_FUNC_SPI); + gpio_pull_up(PIN_MISO); + + gpio_init(PIN_SS); + gpio_put(PIN_SS, 1); + gpio_set_dir(PIN_SS, GPIO_OUT); + + gpio_init(PIN_CDET); + gpio_pull_up(PIN_CDET); + + for (int i = 0; i < 12; i++) + gpio_init(i); + + gpio_put(PIN_ACT, 1); + gpio_set_dir(PIN_ACT, GPIO_OUT); + + prev_cdet = gpio_get_all() & (1 << PIN_CDET); + + while (1) { + handle_request(); + + gpio_set_dir_in_masked(0xff); + gpio_clr_mask(0xff); + + gpio_put(PIN_ACT, 1); + + while (spi_is_busy(spi0)) + tight_loop_contents(); + + if (spi_is_readable(spi0)) + (void)spi_get_hw(spi0)->dr; + } +} diff --git a/rp2040/pico_sdk_import.cmake b/rp2040/pico_sdk_import.cmake new file mode 100644 index 0000000..65f8a6f --- /dev/null +++ b/rp2040/pico_sdk_import.cmake @@ -0,0 +1,73 @@ +# 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 () + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + GIT_SUBMODULES_RECURSE FALSE + ) + else () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + endif () + + 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})