diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 0309105..8b471ff 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -25,7 +25,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: prawnblaster-firmware-${{ github.sha }} - path: build/prawn_do/prawn_do.uf2 + path: build_*/prawn_do/*.uf2 - name: Create release if: (github.event_name == 'push' && contains(github.ref, '/tags')) @@ -35,4 +35,5 @@ jobs: prerelease: false files: | LICENSE.txt - build/prawn_do/prawn_do.uf2 + build_rp2040/prawn_do/prawn_do_rp2040.uf2 + build_rp2350/prawn_do/prawn_do_rp2350.uf2 diff --git a/.gitignore b/.gitignore index 3b1f72b..cce8c1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -build/* -!build/*.uf2 +build*/* .vscode .DS_Store -cmake-* \ No newline at end of file +cmake-* +pico_sdk_import.cmake \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ad2348..bde005b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.17) include(pico_sdk_import.cmake) diff --git a/README.md b/README.md index 42af09b..1009d34 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,11 @@ This firmware turns pins 0-15 into programmable digital outputs. Their states ar Pin 20 is reserved for optional external clock input. +## Supported boards + +We support either the official [Raspberry Pi Pico (RP2040 chip)](https://www.raspberrypi.com/products/raspberry-pi-pico/) board or the official [Raspberry Pi Pico 2 (RP2350 chip)](https://www.raspberrypi.com/products/raspberry-pi-pico-2/) board. +We recommend the Pico 2 (RP2350) board due to its faster clock and larger RAM. + ## Specs All timings are given relative to the default system clock of 100 MHz. @@ -14,12 +19,21 @@ All timings are given relative to the default system clock of 100 MHz. * **Minimum Pulse Width**: 5 clock cycles (50 ns) * **Max Pulse Rate**: 1/10 system clock frequency (10 MHz) * **Maximum Pulse Width**: 2^32 - 1 clock cycles (42.94967295 s) -* **Max Instructions**: 30,000 +* **Max Instructions**: 60,000 (Pico 2 - RP2350) or 30,000 (Pico - RP2040) * Supports Indefinite Waits and Full Stops -* Max system clock frequency of 133 MHz +* Max system clock frequency of 150 MHz (Pico 2 - RP2350) or 133 MHz (Pico - RP2040) +* Support for referencing the system clock to an external clock source to synchronise with other devices (officially limited to 50MHz on the Pico and Pico 2, but testing has shown it works up to 133MHz). ## Installing the .uf2 file -Before plugging in usb, hold down the bootsel button, which should pop-up a window to drag/drop the .uf2 file into, and when that .uf2 file is added, the window should disappear. +Download the latest prawn_do.uf2 file: +- [Pico 2 - RP2350](https://github.com/labscript-suite/prawn_digital_out/releases/latest/download/prawn_do_rp2350.uf2) +- [Pico - RP2040](https://github.com/labscript-suite/prawn_digital_out/releases/latest/download/prawn_do_rp2040.uf2) + +On your Raspberry Pi Pico, hold down the "bootsel" button while plugging the Pico into USB port on a PC (that must already be turned on). +The Pico should mount as a mass storage device (if it doesn't, try again or consult the Pico documentation). +Drag and drop the `.uf2` file into the mounted mass storage device. +The mass storage device should unmount after the copy completes. +Your Pico is now running the Prawn Digital Output firmware! ## Serial Communication Commands must end with a newline character: `'\n'`. @@ -43,6 +57,7 @@ These commands can be run at any time (ie during sequence execution). * `deb` - Turns on debugging mode which adds printed output when adding instructions. By default, debugging is off. * `ndb` - Turns off debugging mode. * `ver` - Displays the version of the PrawnDO code. +* `brd` - Responds with a string containing the board version (`pico1` or `pico2`). * `abt` - Abort execution of a running sequence. These commands must be run when the running status is `STOPPED`. @@ -79,7 +94,7 @@ These commands must be run when the running status is `STOPPED`. * `len` - Print total number of instructions in the programmed sequence. * `cls` - Clear the current sequence of programmed outputs. -* `clk ` - Sets the system clock and frequency. Maximum frequency allowed is 133 MHz. Default is 100 MHz internal clock. External clock frequency input is GPIO pin 20. +* `clk ` - Sets the system clock and frequency. Maximum frequency allowed is 150 MHz (Pico 2 - RP2350) or 133 MHz (Pico - RP2040). Default is 100 MHz internal clock. External clock frequency input is GPIO pin 20. * `frq` - Measure and print system frequencies. * `prg` - Equivalent to disconnecting the Pico, holding down the "bootsel" button, and reconnecting the Pico. Places the Pico into firmware flashing mode; the PrawnDO serial port should disappear and the Pico should mount as a mass storage device. @@ -140,7 +155,7 @@ If you want to make changes to the firmware, or want to compile it yourself (bec 2. Clone this repository 3. Open a terminal with the current working directory set to the repository root (the `docker-compose.yaml`` file should be there) 4. Run `docker compose build --pull` to build the docker container -5. Run `docker compose up` to build the PrawnBlaster firmware. +5. Run `docker compose up` to build the PrawnDO firmware. Step 4 will take a while as it has to build the docker container. If it is slow to download packages from the Ubuntu package repositories, consider providing an explicit apt mirror that is fast for you: `docker compose build --pull --build-arg APT_MIRROR="http://azure.archive.ubuntu.com/ubuntu/"`. @@ -151,3 +166,8 @@ Just change the git tag of the pico SDK that gets cloned out by git, then rebuil Note once the docker container is built, you can run step 5 as many times as you like. You do not need to rebuild the container, even if you make changes to the source code. You only need to rebuild the docker container if you modify the `build/docker/Dockerfile` file. + +By default, running `docker compose up` builds the all variations of the firmware. +If you only want to build for a specific board, run either `docker compose up build_rp2040_firmware` or `docker compose up build_rp2350_firmware`. + +The firmware will be located in `build_rp2xxx/prawn_do/prawn_do_rp2xxx.uf2` where `rp2xxx` will be either `rp2040` or `rp2350`. diff --git a/docker-compose.yaml b/docker-compose.yaml index 33de80b..142d7b1 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,11 +1,30 @@ name: prawn_do services: - buildfirmware: + prawn_do_firmware_base: build: dockerfile: docker/Dockerfile - command: /bin/bash -c 'cmake .. && make' + image: prawn_digital_output/build-firmware volumes: - .:/prawn_digital_output - working_dir: /prawn_digital_output/build - init: true \ No newline at end of file + command: /bin/bash -c 'cp /pico/pico-sdk/external/pico_sdk_import.cmake /prawn_digital_output/' + container_name: prawn_do_firmware_base + + build_rp2040_firmware: + image: prawn_digital_output/build-firmware + command: /bin/bash -c 'cmake .. -D PICO_PLATFORM=rp2040 && make' + volumes: + - .:/prawn_digital_output + working_dir: /prawn_digital_output/build_rp2040 + init: true + depends_on: + - prawn_do_firmware_base + build_rp2350_firmware: + image: prawn_digital_output/build-firmware + command: /bin/bash -c 'cmake .. -D PICO_PLATFORM=rp2350 && make' + volumes: + - .:/prawn_digital_output + working_dir: /prawn_digital_output/build_rp2350 + init: true + depends_on: + - prawn_do_firmware_base diff --git a/docker/Dockerfile b/docker/Dockerfile index afcd2e7..dcef6e6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -6,24 +6,26 @@ ARG APT_MIRROR="mirror://mirrors.ubuntu.com/mirrors.txt" # ARG APT_MIRROR="http://mirror.aarnet.edu.au/pub/ubuntu/archive/" # Configure mirror. Pass --build-arg APT_MIRROR= to set a mirror if this is slow -RUN sed -i "s#htt[p|ps]://archive.ubuntu.com/ubuntu/#$APT_MIRROR#g" /etc/apt/sources.list +RUN sed -i "s#htt[p|ps]://archive.ubuntu.com/ubuntu/#$APT_MIRROR#g" /etc/apt/sources.list.d/ubuntu.sources # Install packages RUN \ apt update && \ apt install -y git python3 && \ - apt install -y cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential + # For Pico SDK + apt install -y cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib build-essential # Install Pico SDK into a new stage FROM base as buildtools +# Install Pico SDK RUN \ mkdir -p /pico/ && \ cd /pico/ && \ - git clone https://github.com/raspberrypi/pico-sdk.git --branch 1.5.1 && \ + git clone https://github.com/raspberrypi/pico-sdk.git --branch 2.0.0 && \ cd pico-sdk/ && \ git submodule update --init && \ cd / # Set the Pico SDK environment variable -ENV PICO_SDK_PATH=/pico/pico-sdk/ \ No newline at end of file +ENV PICO_SDK_PATH=/pico/pico-sdk/ diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake deleted file mode 100644 index ced6890..0000000 --- a/pico_sdk_import.cmake +++ /dev/null @@ -1,73 +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 () - # 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}) \ No newline at end of file diff --git a/prawn_do/CMakeLists.txt b/prawn_do/CMakeLists.txt index d3739e5..1910935 100644 --- a/prawn_do/CMakeLists.txt +++ b/prawn_do/CMakeLists.txt @@ -1,25 +1,49 @@ -add_executable(prawn_do - prawn_do.c - fast_serial.c - ) - -pico_generate_pio_header(prawn_do -${CMAKE_CURRENT_LIST_DIR}/prawn_do.pio -) - -# add local includes for fast_serial -target_include_directories(prawn_do PRIVATE ${CMAKE_CURRENT_LIST_DIR}) - -# pull in pico stdlib -target_link_libraries(prawn_do - pico_multicore - pico_stdlib - pico_unique_id - hardware_dma - hardware_pio - tinyusb_device - tinyusb_board -) - -# create map/bin/hex/uf2 files -pico_add_extra_outputs(prawn_do) +set(overclocks 0;) + +foreach (overclock IN LISTS overclocks) + # Compute firmware name + set(firmware_name prawn_do) + if(PICO_PLATFORM MATCHES "^rp2350") + set(firmware_name "${firmware_name}_rp2350") + else() + set(firmware_name "${firmware_name}_${PICO_PLATFORM}") + endif() + if(overclock) + set(firmware_name "${firmware_name}_overclock") + endif() + + add_executable(${firmware_name} prawn_do.c fast_serial.c) + + pico_generate_pio_header(${firmware_name} ${CMAKE_CURRENT_LIST_DIR}/prawn_do.pio) + + # Pass in number of instructions to firmware as a compiler definition + set(num_instructions 30000) + if(PICO_PLATFORM MATCHES "^rp2350") + set(num_instructions 60000) + endif() + target_compile_definitions(${firmware_name} PUBLIC "PRAWNDO_NUM_INSTRUCTIONS=${num_instructions}") + + # Pass in board type to firmware as a compiler definition. Note that PICO_BOARD is passed in by the SDK, but it's passed in a string which isn't valid and so I can't use it... + # This is also, to some extent, a duplicate of the above PRAWNDO_NUM_INSTRUCTIONS but I think it makes sense to keep these seperate. + if (PICO_BOARD STREQUAL "pico") + target_compile_definitions(${firmware_name} PUBLIC "PRAWNDO_PICO_BOARD=1") + elseif (PICO_BOARD STREQUAL "pico2") + target_compile_definitions(${firmware_name} PUBLIC "PRAWNDO_PICO_BOARD=2") + else () + message(FATAL_ERROR "Unsupported PICO_BOARD") + endif() + + + # Pass in overclock state to firmware as a compiler definition + if(overclock) + target_compile_definitions(${firmware_name} PUBLIC "PRAWNDO_OVERCLOCK=1") + endif() + + # Pull in our pico_stdlib which aggregates commonly used features + target_link_libraries(${firmware_name} pico_stdlib hardware_pio pico_multicore pico_unique_id hardware_clocks hardware_dma tinyusb_device tinyusb_board) + target_include_directories(${firmware_name} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) + + # create map/bin/hex/uf2 file etc. + pico_add_extra_outputs(${firmware_name}) + +endforeach() diff --git a/prawn_do/prawn_do.c b/prawn_do/prawn_do.c index 52c0c6f..52c4b49 100644 --- a/prawn_do/prawn_do.c +++ b/prawn_do/prawn_do.c @@ -30,9 +30,9 @@ enum COMMAND { MANUAL = 0 }; -#define MAX_DO_CMDS 60000 // two DO CMDS per INSTRUCTION -#define MAX_INSTR 30000 +#define MAX_INSTR PRAWNDO_NUM_INSTRUCTIONS +#define MAX_DO_CMDS (2*MAX_INSTR) uint32_t do_cmds[MAX_DO_CMDS]; uint32_t do_cmd_count = 0; @@ -54,7 +54,7 @@ int status; #define EXTERNAL 1 int clk_status = INTERNAL; unsigned short debug = 0; -const char ver[6] = "1.2.0"; +const char ver[6] = "1.3.0"; // Mutex for status static mutex_t status_mutex; @@ -142,7 +142,9 @@ void measure_freqs(void) uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI); uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB); uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC); +#ifdef CLOCKS_FC0_SRC_VALUE_CLK_RTC uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC); +#endif fast_serial_printf("pll_sys = %dkHz\r\n", f_pll_sys); fast_serial_printf("pll_usb = %dkHz\r\n", f_pll_usb); @@ -151,7 +153,9 @@ void measure_freqs(void) fast_serial_printf("clk_peri = %dkHz\r\n", f_clk_peri); fast_serial_printf("clk_usb = %dkHz\r\n", f_clk_usb); fast_serial_printf("clk_adc = %dkHz\r\n", f_clk_adc); +#ifdef CLOCKS_FC0_SRC_VALUE_CLK_RTC fast_serial_printf("clk_rtc = %dkHz\r\n", f_clk_rtc); +#endif } /* Resusitation function that restarts the clock internally if there are any @@ -268,9 +272,6 @@ int main(){ // Setup serial fast_serial_init(); - // Initialize clock functions - clocks_init(); - // By default, set the system clock to 100 MHz set_sys_clock_khz(100000, false); @@ -312,6 +313,9 @@ int main(){ if(strncmp(serial_buf, "ver", 3) == 0) { fast_serial_printf("Version: %s\r\n", ver); } + if(strncmp(serial_buf, "brd", 3) == 0) { + fast_serial_printf("board: pico%d\r\n", PRAWNDO_PICO_BOARD); + } // Status command: return running status else if(strncmp(serial_buf, "sts", 3) == 0){ fast_serial_printf("run-status:%d clock-status:%d\r\n", local_status, clk_status); @@ -630,7 +634,7 @@ int main(){ // FORMAT: clk else if (strncmp(serial_buf, "clk", 3) == 0){ unsigned int src; // 0 = internal, 1 = external (GPIO pin 20) - unsigned int freq; // in Hz (up to 133 MHz) + unsigned int freq; // in Hz (up to 133 MHz or 150 MHz depending on board) int parsed = sscanf(serial_buf, "%*s %u %u", &src, &freq); // validation checks of the inputs if (parsed < 2) { @@ -639,7 +643,13 @@ int main(){ } else if (src > 2) { fast_serial_printf("invalid clock source request\r\n"); continue; +#if PRAWNDO_PICO_BOARD == 1 } else if (freq > 133000000) { +#elif PRAWNDO_PICO_BOARD == 2 + } else if (freq > 150000000) { +#else +# error "Unsupported PICO_BOARD" +#endif // PRAWNDO_PICO_BOARD fast_serial_printf("invalid clock frequency request\r\n"); continue; }