diff --git a/.github/workflows/push-master-pico.yml b/.github/workflows/push-master-pico.yml new file mode 100644 index 0000000..1dd217e --- /dev/null +++ b/.github/workflows/push-master-pico.yml @@ -0,0 +1,149 @@ +name: HyperSPI Pico CI Build + +on: [push] + +jobs: + +############################# +#### HyperSPI for Pico ###### +############################# + + HyperSpiPico: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./rp2040 + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install GNU Arm Embedded Toolchain + uses: carlosperate/arm-none-eabi-gcc-action@v1 + with: + release: '12.2.Rel1' + + - name: Build packages + shell: bash + run: | + mkdir build + cd build + cmake .. + cmake --build . --config Release + + - uses: actions/upload-artifact@v3 + name: Upload artifacts (commit) + if: (startsWith(github.event.ref, 'refs/tags') != true) + with: + path: | + rp2040/HyperSerialPico/firmware/*.uf2 + + - uses: actions/upload-artifact@v3 + name: Upload artifacts (release) + if: startsWith(github.ref, 'refs/tags/') + with: + name: firmware-release + path: | + rp2040/HyperSerialPico/firmware/*.uf2 + + # - name: Build packages for Adafruit Feather RP2040 Scorpio (release-only) + # if: startsWith(github.ref, 'refs/tags/') + # shell: bash + # run: | + # cd build + # rm *.* + # rm ../HyperSerialPico/firmware/* + # echo "Neopixel is using GPIO16(OUTPUT_DATA_PIN) on output 0." > ../firmware/Firmwares_for_Adafruit_Feather_RP2040_Scorpio.txt + # cmake -DOVERRIDE_BOOT_WORKAROUND=ON -DOVERRIDE_DATA_PIN=16 -DCMAKE_BUILD_TYPE=Release .. + # cmake --build . + # zip -j ../HyperSerialPico/firmware/Adafruit_Feather_RP2040_Scorpio.zip ../firmware/* + + # - uses: actions/upload-artifact@v3 + # name: Upload artifacts (release Adafruit_Feather) + # if: startsWith(github.ref, 'refs/tags/') + # with: + # name: firmware-release + # path: | + # rp2040/HyperSerialPico/firmware/*.zip + + # - name: Build packages for Adafruit ItsyBitsy RP2040 (release-only) + # if: startsWith(github.ref, 'refs/tags/') + # shell: bash + # run: | + # cd build + # rm *.* + # rm ../HyperSerialPico/firmware/* + # echo "Neopixel is using GPIO14(OUTPUT_DATA_PIN) on output 5." > ../firmware/Firmwares_for_Adafruit_ItsyBitsy_2040.txt + # cmake -DOVERRIDE_BOOT_WORKAROUND=ON -DOVERRIDE_DATA_PIN=14 -DCMAKE_BUILD_TYPE=Release .. + # cmake --build . + # rm ../firmware/*_Spi.uf2 + # zip -j ../HyperSerialPico/firmware/Adafruit_ItsyBitsy_2040.zip ../firmware/* + + # - uses: actions/upload-artifact@v3 + # name: Upload artifacts (release Adafruit_ItsyBitsy) + # if: startsWith(github.ref, 'refs/tags/') + # with: + # name: firmware-release + # path: | + # rp2040/HyperSerialPico/firmware/*.zip + + # - name: Build packages for Pimoroni Plasma 2040 (release-only) + # if: startsWith(github.ref, 'refs/tags/') + # shell: bash + # run: | + # cd build + # rm *.* + # rm ../HyperSerialPico/firmware/* + # echo "Neopixel is using GPIO15(OUTPUT_DATA_PIN) on output DA." > ../firmware/Firmwares_for_Pimoroni_Plasma_2040.txt + # cmake -DOVERRIDE_DATA_PIN=15 -DCMAKE_BUILD_TYPE=Release .. + # cmake --build . + # zip -j ../HyperSerialPico/firmware/Pimoroni_Plasma_2040.zip ../firmware/* + + # - uses: actions/upload-artifact@v3 + # name: Upload artifacts (release Pimoroni_Plasma) + # if: startsWith(github.ref, 'refs/tags/') + # with: + # name: firmware-release + # path: | + # rp2040/HyperSerialPico/firmware/*.zip + +################################ +###### Publish Releases ######## +################################ + + publish: + name: Publish Releases + if: startsWith(github.event.ref, 'refs/tags') + needs: [HyperSpiPico] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + # generate environment variables + - name: Generate environment variables from version and tag + run: | + echo "TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV + echo "preRelease=false" >> $GITHUB_ENV + + # If version contains alpha or beta, mark draft release as pre-release + - name: Mark release as pre-release + if: contains(env.VERSION, 'alpha') || contains(env.VERSION, 'beta') + run: echo "preRelease=true" >> $GITHUB_ENV + + - uses: actions/download-artifact@v3 + with: + name: firmware-release + + # create draft release and upload artifacts + - name: Create draft release + uses: softprops/action-gh-release@v1 + with: + name: HyperSPI ${{ env.VERSION }} + tag_name: ${{ env.TAG }} + files: | + *.uf2 + *.zip + draft: true + prerelease: ${{ env.preRelease }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index 80754ee..892fb3c 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -1,4 +1,4 @@ -name: HyperSPI CI Build +name: HyperSPI ESP32/8266 CI Build on: [push] diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a1e8b92 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "rp2040/HyperSerialPico"] + path = rp2040/HyperSerialPico + url = https://github.com/awawa-dev/HyperSerialPico diff --git a/rp2040/.gitignore b/rp2040/.gitignore new file mode 100644 index 0000000..1c26e7f --- /dev/null +++ b/rp2040/.gitignore @@ -0,0 +1,2 @@ +build +vscode diff --git a/rp2040/CMakeLists.txt b/rp2040/CMakeLists.txt new file mode 100644 index 0000000..6ad3b8c --- /dev/null +++ b/rp2040/CMakeLists.txt @@ -0,0 +1,42 @@ +set(CMAKE_SYSTEM_NAME Generic) +# User configuration section starts here + +# Some boards, such as the first Adafruit revisions, may have trouble booting properly +# due to bad componets used in the design. +# Turn this setting to ON if your rp2040 is not detected after firmware upload and reset +set(BOOT_WORKAROUND OFF) + +# Default output data pin for the non-SPI LED strips (only for sk6812/ws2812b) +set(OUTPUT_DATA_PIN 14) + +# Use multi-segment, starting index of second led strip or OFF to disable +set(SECOND_SEGMENT_INDEX OFF) + +# If multi-segment is used and it's reversed, set this option to ON to enable reversing +set(SECOND_SEGMENT_REVERSED OFF) + +# HyperSPI communication interface with Raspberry Pi +set(OUTPUT_SPI_DATA_PIN 4) +set(OUTPUT_SPI_CLOCK_PIN 2) +set(OUTPUT_SPI_CHIP_SELECT 5) +set(OUTPUT_SPI_INTERFACE spi0) + +# User configuration section ends here +# Usually you don't need to change anything below this section +cmake_minimum_required(VERSION 3.13) + +set(PICO_SDK_PATH ${CMAKE_CURRENT_SOURCE_DIR}/HyperSerialPico/sdk/pico) +set(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_SOURCE_DIR}/HyperSerialPico/sdk/freertos) +include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) +include(${FREERTOS_KERNEL_PATH}/portable/ThirdParty/GCC/RP2040/FreeRTOS_Kernel_import.cmake) + +project(HyperSPI C CXX ASM) +pico_sdk_init() + +set(PICO_PROGRAM_MAIN_ENTRY "../hyperspi.cpp") +set(DISABLE_SPI_LEDS ON) +add_definitions(-DSPI_INTERFACE=${OUTPUT_SPI_INTERFACE} + -DSPI_DATA_PIN=${OUTPUT_SPI_DATA_PIN} + -DSPI_CLOCK_PIN=${OUTPUT_SPI_CLOCK_PIN} + -DSPI_CHIP_SELECT=${OUTPUT_SPI_CHIP_SELECT}) +add_subdirectory(HyperSerialPico) diff --git a/rp2040/HyperSerialPico b/rp2040/HyperSerialPico new file mode 160000 index 0000000..5bf4754 --- /dev/null +++ b/rp2040/HyperSerialPico @@ -0,0 +1 @@ +Subproject commit 5bf4754b73110772325dabcc1590c45cdbeb918f diff --git a/rp2040/hyperspi.cpp b/rp2040/hyperspi.cpp new file mode 100644 index 0000000..2cb9b96 --- /dev/null +++ b/rp2040/hyperspi.cpp @@ -0,0 +1,215 @@ +/* hyperspi.cpp +* +* MIT License +* +* Copyright (c) 2023 awawa-dev +* +* https://github.com/awawa-dev/HyperSPI +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. + +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. + */ + +#define TUD_OPT_HIGH_SPEED + + +#include "FreeRTOS.h" +#include "task.h" +#include +#include +#include "pico/stdio/driver.h" +#include "pico/stdlib.h" +#include "pico/stdio.h" +#include "pico/stdio_usb.h" +#include "pico/multicore.h" +#include "pico/sem.h" +#include "leds.h" + +#define SPI_FRAME_SIZE 1536 +#define SPI_SPEED 20833333 + +uint8_t spiBuffer[SPI_FRAME_SIZE] {}; + +/////////////////////////////////////////////////////////////////////////// +// DO NOT EDIT THIS FILE. ADJUST THE CONFIGURATION IN THE CmakeList.txt // +/////////////////////////////////////////////////////////////////////////// + +#define _STR(x) #x +#define _XSTR(x) _STR(x) +#define VAR_NAME_VALUE(var) #var " = " _XSTR(var) +#define _XSTR2(x,y) _STR(x) _STR(y) +#define VAR_NAME_VALUE2(var) #var " = " _XSTR2(var) + +#if defined(BOOT_WORKAROUND) && defined(PICO_XOSC_STARTUP_DELAY_MULTIPLIER) + #pragma message("Enabling boot workaround") + #pragma message(VAR_NAME_VALUE(PICO_XOSC_STARTUP_DELAY_MULTIPLIER)) +#endif + + +#ifdef NEOPIXEL_RGBW + #pragma message(VAR_NAME_VALUE(NEOPIXEL_RGBW)) +#endif +#ifdef NEOPIXEL_RGB + #pragma message(VAR_NAME_VALUE(NEOPIXEL_RGB)) +#endif +#ifdef COLD_WHITE + #pragma message(VAR_NAME_VALUE(COLD_WHITE)) +#endif + +#ifdef NEOPIXEL_RGBW + #define LED_DRIVER sk6812 +#elif NEOPIXEL_RGB + #define LED_DRIVER ws2812 +#endif + +#pragma message(VAR_NAME_VALUE(DATA_PIN)) +#pragma message(VAR_NAME_VALUE(SPI_INTERFACE)) +#pragma message(VAR_NAME_VALUE(SPI_DATA_PIN)) +#pragma message(VAR_NAME_VALUE(SPI_CLOCK_PIN)) +#pragma message(VAR_NAME_VALUE(SPI_CHIP_SELECT)) + +#if defined(SECOND_SEGMENT_START_INDEX) + #pragma message("Using parallel mode for segments") + + #ifdef NEOPIXEL_RGBW + #undef LED_DRIVER + #define LED_DRIVER sk6812p + #define LED_DRIVER2 sk6812p + #elif NEOPIXEL_RGB + #undef LED_DRIVER + #define LED_DRIVER ws2812p + #define LED_DRIVER2 ws2812p + #else + #error "Parallel mode is unsupportd for selected LEDs configuration" + #endif + + #pragma message(VAR_NAME_VALUE(LED_DRIVER)) + #pragma message(VAR_NAME_VALUE(SECOND_SEGMENT_START_INDEX)) + #pragma message(VAR_NAME_VALUE(LED_DRIVER2)) + #pragma message(VAR_NAME_VALUE(SECOND_SEGMENT_REVERSED)) +#else + #pragma message(VAR_NAME_VALUE(LED_DRIVER)) + + typedef LedDriver LED_DRIVER2; +#endif + +///////////////////////////////////////////////////////////////////////// +#define delay(x) sleep_ms(x) +#define yield() busy_wait_us(100) +#define millis xTaskGetTickCount + +#include "main.h" + +static void core1() +{ + for( ;; ) + { + if (sem_acquire_timeout_us(&base.receiverSemaphore, portMAX_DELAY)) + { + processData(); + } + } +} + +static uint initSpi(uint baudrate, spi_inst_t* _spi, uint32_t spiMosipin, uint32_t spiClockpin, uint32_t spiSelectPin) +{ + uint selectedSpeed = spi_init(_spi, baudrate); + printf("Using baudrate: %i\n", selectedSpeed); + spi_set_format(_spi, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST); + hw_set_bits(&spi_get_hw(_spi)->dmacr, SPI_SSPDMACR_TXDMAE_BITS | SPI_SSPDMACR_RXDMAE_BITS); + spi_set_slave(_spi, true); + hw_set_bits(&spi_get_hw(_spi)->cr1, SPI_SSPCR1_SSE_BITS); + gpio_set_function(spiClockpin, GPIO_FUNC_SPI); + gpio_set_function(spiMosipin, GPIO_FUNC_SPI); + gpio_set_function(spiSelectPin, GPIO_FUNC_SPI); + gpio_set_function(3, GPIO_FUNC_SPI); + bi_decl(bi_4pins_with_func(spiMosipin, 3, spiClockpin, spiSelectPin, GPIO_FUNC_SPI)); + + uint dmaChannelNumber = dma_claim_unused_channel(true); + dma_channel_config channelConfig = dma_channel_get_default_config(dmaChannelNumber); + channel_config_set_transfer_data_size(&channelConfig, DMA_SIZE_8); + channel_config_set_dreq(&channelConfig, spi_get_dreq(_spi, false)); + channel_config_set_read_increment(&channelConfig, false); + channel_config_set_write_increment(&channelConfig, true); + dma_channel_configure(dmaChannelNumber, &channelConfig, + spiBuffer, + &spi_get_hw(_spi)->dr, + sizeof(spiBuffer), + false); + return dmaChannelNumber; +} + +static void core0( void *pvParameters ) +{ + bool regroupNeeded = false; + uint dmaChannelNumber = initSpi(SPI_SPEED, SPI_INTERFACE, SPI_DATA_PIN, SPI_CLOCK_PIN, SPI_CHIP_SELECT); + + while(true) + { + dma_channel_set_write_addr(dmaChannelNumber, spiBuffer, true); + dma_channel_wait_for_finish_blocking(dmaChannelNumber); + + if (frameState.getRegroup()) + { + if (regroupNeeded) + { + dma_channel_unclaim(dmaChannelNumber); + spi_deinit(SPI_INTERFACE); + dmaChannelNumber = initSpi(SPI_SPEED, SPI_INTERFACE, SPI_DATA_PIN, SPI_CLOCK_PIN, SPI_CHIP_SELECT); + } + regroupNeeded = false; + frameState.setRegroup(false); + } + else + regroupNeeded = true; + + + //printf("%i %i %i %i %i %i %i %i\n",spiBuffer[0],spiBuffer[1],spiBuffer[2],spiBuffer[3],spiBuffer[4],spiBuffer[5],spiBuffer[6],spiBuffer[7],spiBuffer[8]); + int remains = SPI_FRAME_SIZE; + int wanted, received; + do + { + wanted = std::min(MAX_BUFFER - base.queueEnd, MAX_BUFFER - 1); + received = std::min(remains, wanted); + memcpy((void*)&(base.buffer[base.queueEnd]), &(spiBuffer[SPI_FRAME_SIZE - remains]), received); + base.queueEnd = (base.queueEnd + received) % (MAX_BUFFER); + remains -= received; + }while(remains); + + sem_release(&base.receiverSemaphore); + } +} + +int main(void) +{ + stdio_init_all(); + + sem_init(&base.receiverSemaphore, 0, 1); + + multicore_launch_core1(core1); + + xTaskCreate(core0, + "HyperSPI:core0", + configMINIMAL_STACK_SIZE * 2, + NULL, + (configMAX_PRIORITIES - 1), + &base.processSerialHandle); + + vTaskStartScheduler(); + panic_unsupported(); +}