From 950f06980746c705809f45e43e28fc10a183b8ae Mon Sep 17 00:00:00 2001 From: Awawa <69086569+awawa-dev@users.noreply.github.com> Date: Sun, 16 Apr 2023 15:55:22 +0000 Subject: [PATCH] HyperSerialPico first version --- .github/FUNDING.yml | 1 + .github/workflows/push-master.yml | 85 +++++ .gitignore | 355 +++++++++++++++++++ .gitmodules | 7 + CMakeLists.txt | 85 +++++ LICENSE | 23 ++ README.md | 51 +++ include/base.h | 172 +++++++++ include/calibration.h | 134 +++++++ include/framestate.h | 261 ++++++++++++++ include/leds.h | 565 ++++++++++++++++++++++++++++++ include/main.h | 264 ++++++++++++++ include/statistics.h | 166 +++++++++ pio/neopixel.pio | 46 +++ sdk/config/FreeRTOSConfig.h | 139 ++++++++ sdk/freertos | 1 + sdk/pico | 1 + source/main.cpp | 177 ++++++++++ 18 files changed, 2533 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/push-master.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 README.md create mode 100644 include/base.h create mode 100644 include/calibration.h create mode 100644 include/framestate.h create mode 100644 include/leds.h create mode 100644 include/main.h create mode 100644 include/statistics.h create mode 100644 pio/neopixel.pio create mode 100644 sdk/config/FreeRTOSConfig.h create mode 160000 sdk/freertos create mode 160000 sdk/pico create mode 100644 source/main.cpp diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..7743b06 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: awawa-dev diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml new file mode 100644 index 0000000..9a5944d --- /dev/null +++ b/.github/workflows/push-master.yml @@ -0,0 +1,85 @@ +name: HyperSerialPico CI Build + +on: [push] + +jobs: + +########################### +#### HyperSerialPico ###### +########################### + + HyperSerialPico: + runs-on: ubuntu-latest + + 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: | + firmwares/*.uf2 + + - uses: actions/upload-artifact@v3 + name: Upload artifacts (release) + if: startsWith(github.ref, 'refs/tags/') + with: + name: firmware-release + path: | + firmwares/*.uf2 + +################################ +###### Publish Releases ######## +################################ + + publish: + name: Publish Releases + if: startsWith(github.event.ref, 'refs/tags') + needs: [HyperSerialPico] + 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: HyperSerialPico ${{ env.VERSION }} + tag_name: ${{ env.TAG }} + files: | + *.uf2 + draft: true + prerelease: ${{ env.preRelease }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..97bdfaf --- /dev/null +++ b/.gitignore @@ -0,0 +1,355 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +sdk/pico +build/* +generated/ +firmwares/ + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..35e1430 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,7 @@ +[submodule "sdk/pico"] + path = sdk/pico + url = https://github.com/RaspberryPi/pico-sdk +[submodule "sdk/freertos"] + path = sdk/freertos + url = https://github.com/FreeRTOS/FreeRTOS-Kernel.git + branch = smp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c3121a7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,85 @@ +# User configuration section starts here + +# Default output data pin for the LED strip +set(OUTPUT_DATA_PIN 2) + +# Default output data pin for the LED strip (SPI leds only, not for sk6812/ws2812b) +set(OUTPUT_CLOCK_PIN 3) + +# 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) + +# User configuration section ends here +# Usually you don't need to change anything belowe this section + +cmake_minimum_required(VERSION 3.13) + +# initialize the SDK based on PICO_SDK_PATH +# note: this must happen before project() +set(PICO_SDK_PATH ${CMAKE_SOURCE_DIR}/sdk/pico) +set(FREERTOS_KERNEL_PATH ${CMAKE_SOURCE_DIR}/sdk/freertos) + +include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) +include(${FREERTOS_KERNEL_PATH}/portable/ThirdParty/GCC/RP2040/FreeRTOS_Kernel_import.cmake) + +project(HyperSerialPico C CXX ASM) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +IF(CMAKE_COMPILER_IS_GNUCC) + string(REGEX REPLACE "(\-O[011123456789])" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + string(REGEX REPLACE "(\-O[011123456789])" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Og") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Og") +ENDIF(CMAKE_COMPILER_IS_GNUCC) + +# initialize the Raspberry Pi Pico SDK +pico_sdk_init() + +# generic HyperSerialPico settings +set(HyperSerialPicoCompanionLibs FreeRTOS-Kernel FreeRTOS-Kernel-Heap1 pico_stdlib pico_multicore hardware_pio hardware_dma hardware_spi) +set(HyperSerialPicoCompanionIncludes ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sdk/config) +file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/generated) +file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/firmwares) + +macro(HyperSerialPicoTarget HyperSerialPicoTargetName) + add_executable(${HyperSerialPicoTargetName} ${CMAKE_SOURCE_DIR}/source/main.cpp) + target_include_directories(${HyperSerialPicoTargetName} PRIVATE ${HyperSerialPicoCompanionIncludes}) + target_link_libraries(${HyperSerialPicoTargetName} ${HyperSerialPicoCompanionLibs}) + pico_add_extra_outputs(${HyperSerialPicoTargetName}) + pico_enable_stdio_usb(${HyperSerialPicoTargetName} 1) + pico_enable_stdio_uart(${HyperSerialPicoTargetName} 0) + pico_generate_pio_header(${HyperSerialPicoTargetName} ${CMAKE_SOURCE_DIR}/pio/neopixel.pio OUTPUT_DIR ${CMAKE_SOURCE_DIR}/generated) + add_custom_command(TARGET ${HyperSerialPicoTargetName} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/${HyperSerialPicoTargetName}.uf2 ${CMAKE_SOURCE_DIR}/firmwares) +endmacro() + +# targets for different LED strips +IF(NOT SECOND_SEGMENT_INDEX) + HyperSerialPicoTarget("HyperSerialPico_Spi") + target_compile_definitions(HyperSerialPico_Spi PRIVATE -DSPILED_APA102 -DDATA_PIN=${OUTPUT_DATA_PIN} -DCLOCK_PIN=${OUTPUT_CLOCK_PIN}) + HyperSerialPicoTarget("HyperSerialPico_sk6812Cold") + target_compile_definitions(HyperSerialPico_sk6812Cold PRIVATE -DNEOPIXEL_RGBW -DCOLD_WHITE -DDATA_PIN=${OUTPUT_DATA_PIN}) + HyperSerialPicoTarget("HyperSerialPico_sk6812Neutral") + target_compile_definitions(HyperSerialPico_sk6812Neutral PRIVATE -DNEOPIXEL_RGBW -DDATA_PIN=${OUTPUT_DATA_PIN}) + HyperSerialPicoTarget("HyperSerialPico_ws2812") + target_compile_definitions(HyperSerialPico_ws2812 PRIVATE -DNEOPIXEL_RGB -DDATA_PIN=${OUTPUT_DATA_PIN}) +ELSE() + IF(NOT SECOND_SEGMENT_REVERSED) + HyperSerialPicoTarget("HyperSerialPico_sk6812Cold_multisegment_at_${SECOND_SEGMENT_INDEX}") + target_compile_definitions("HyperSerialPico_sk6812Cold_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGBW -DCOLD_WHITE -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX}) + HyperSerialPicoTarget("HyperSerialPico_sk6812Neutral_multisegment_at_${SECOND_SEGMENT_INDEX}") + target_compile_definitions("HyperSerialPico_sk6812Neutral_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGBW -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX}) + HyperSerialPicoTarget("HyperSerialPico_ws2812_multisegment_at_${SECOND_SEGMENT_INDEX}") + target_compile_definitions("HyperSerialPico_ws2812_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGB -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX}) + ELSE() + HyperSerialPicoTarget("HyperSerialPico_sk6812Cold_rev_multisegment_at_${SECOND_SEGMENT_INDEX}") + target_compile_definitions("HyperSerialPico_sk6812Cold_rev_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGBW -DCOLD_WHITE -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX} -DSECOND_SEGMENT_REVERSED) + HyperSerialPicoTarget("HyperSerialPico_sk6812Neutral_rev_multisegment_at_${SECOND_SEGMENT_INDEX}") + target_compile_definitions("HyperSerialPico_sk6812Neutral_rev_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGBW -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX} -DSECOND_SEGMENT_REVERSED) + HyperSerialPicoTarget("HyperSerialPico_ws2812_rev_multisegment_at_${SECOND_SEGMENT_INDEX}") + target_compile_definitions("HyperSerialPico_ws2812_rev_multisegment_at_${SECOND_SEGMENT_INDEX}" PRIVATE -DNEOPIXEL_RGB -DDATA_PIN=${OUTPUT_DATA_PIN} -DSECOND_SEGMENT_START_INDEX=${SECOND_SEGMENT_INDEX} -DSECOND_SEGMENT_REVERSED) + ENDIF() +ENDIF() diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ee4f13b --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2023 awawa-dev + +https://github.com/awawa-dev/HyperSerialPico + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3d4cc4a --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# HyperSerialPico +HyperHDR's highspeed adalight serial port LED driver implementation for Raspberry Pi Pico RP2040. + +# Example of supported boards using Rp2040 +

+ +# Supported LED strips +| LED strip / Device | Single lane | Multi-segment | +|--------------------------------|:----------------:|:------------------:| +| SK6812 cold white | yes | yes | +| SK6812 neutral white | yes | yes | +| WS281x | yes | yes | +| SPI (APA102, SK9812, HD107...) | yes | no* | + +`*` you don't need parallel mode for SPI LEDs, they are already using high speed 10Mb internal data line + +# How to flash it? +It's very easy and you don't need any special flasher. +Download the firmware from the [Release folder](https://github.com/awawa-dev/HyperSerialPico/releases). +* If you Pico board has only one button (`boot`) then press & hold it and connect the board to the USB port. Then you can release the button. +* If you Pico board has two buttons, connect it to the USB port. Then press & hold `boot` and `reset` buttons, then release `reset` and next release `boot` button. + +In the system file explorer you should find new drive (e.g. called `RPI-RP2` drive) exposed by the Pico board. Drap & drop the selected fimrware. +The Pico will reset automaticly after the upload and after few seconds it will be ready to use by HyperHDR as a serial port device using Adalight driver. + +# HyperHDR configuration +Make sure you have enabled `AWA protocol` and `Esp8266/ESP32/Rp2040 handshake` options. +You can leave the speed at `2000000` because the CDC driver should use the highest possible speed automatically (at least it happens on Windows). +![obraz](https://user-images.githubusercontent.com/69086569/236870662-12f67d14-c2ca-4ba1-b6a3-e34c27949d19.png) + +# Default pinout + +**LED output (SK6812/WS281x):** GPIO2 for Data +**LED output (SPI LEDs):** GPIO2 for Data, GPIO3 for Clock + +Pinout can be changed, but you need to make changes to CMakeList.txt (`OUTPUT_DATA_PIN`/`OUTPUT_CLOCK_PIN`) and recompile the project. Also multi-segment mode can be enabled in this file: `SECOND_SEGMENT_INDEX` option at the beginning and optionally `SECOND_SEGMENT_REVERSED`. +Once compiled, the results can be found in the `firmwares` folder. + +# Some benchmark results + +| Single RGBW LED strip | Max. refresh rate | +|------------------------------------------------|-------------------| +| 300LEDs RGBW
Refresh rate/continues output | 83 | +| 600LEDs RGBW
Refresh rate/continues output | 42 | +| 900LEDs RGBW
Refresh rate/continues output | 28 | + +| Parallel multi-segment mode | Max. refresh rate | +|---------------------------------------------------------------------------|----------------------| +| 300LEDs RGBW
Refresh rate/continues output
SECOND_SEGMENT_INDEX=150 | 100 | +| 600LEDs RGBW
Refresh rate/continues output
SECOND_SEGMENT_INDEX=300 | 83 | +| 900LEDs RGBW
Refresh rate/continues output
SECOND_SEGMENT_INDEX=450 | 55 | diff --git a/include/base.h b/include/base.h new file mode 100644 index 0000000..d5a148d --- /dev/null +++ b/include/base.h @@ -0,0 +1,172 @@ +/* base.h +* +* MIT License +* +* Copyright (c) 2023 awawa-dev +* +* https://github.com/awawa-dev/HyperSerialPico +* +* 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. + */ + +#ifndef BASE_H +#define BASE_H + +class Base +{ + // LED strip number + int ledsNumber = 0; + // NeoPixelBusLibrary primary object + LED_DRIVER* ledStrip1 = nullptr; + // NeoPixelBusLibrary second object + LED_DRIVER2* ledStrip2 = nullptr; + // frame is set and ready to render + bool readyToRender = false; + + public: + // static data buffer for the loop + volatile uint8_t buffer[MAX_BUFFER + 1] = {0}; + // handle to tasks + TaskHandle_t processDataHandle = nullptr; + TaskHandle_t processSerialHandle = nullptr; + // semaphore to synchronize them + semaphore_t serialSemaphore; + semaphore_t receiverSemaphore; + // current queue position + volatile int queueCurrent = 0; + // queue end position + volatile int queueEnd = 0; + + inline int getLedsNumber() + { + return ledsNumber; + } + + inline LED_DRIVER* getLedStrip1() + { + return ledStrip1; + } + + inline LED_DRIVER2* getLedStrip2() + { + return ledStrip2; + } + + void initLedStrip(int count) + { + if (ledStrip1 != nullptr) + { + delete ledStrip1; + ledStrip1 = nullptr; + } + + if (ledStrip2 != nullptr) + { + delete ledStrip2; + ledStrip2 = nullptr; + } + + ledsNumber = count; + + #if defined(SECOND_SEGMENT_START_INDEX) + if (ledsNumber > SECOND_SEGMENT_START_INDEX) + { + #if defined(NEOPIXEL_RGBW) || defined(NEOPIXEL_RGB) + ledStrip1 = new LED_DRIVER(SECOND_SEGMENT_START_INDEX, DATA_PIN); + ledStrip2 = new LED_DRIVER2(ledsNumber - SECOND_SEGMENT_START_INDEX, DATA_PIN); + #else + ledStrip1 = new LED_DRIVER(SECOND_SEGMENT_START_INDEX); + ledStrip1->Begin(CLOCK_PIN, 12, DATA_PIN, 15); + ledStrip2 = new LED_DRIVER2(ledsNumber - SECOND_SEGMENT_START_INDEX); + ledStrip2->Begin(SECOND_SEGMENT_CLOCK_PIN, 12, SECOND_SEGMENT_DATA_PIN, 15); + #endif + } + #endif + + if (ledStrip1 == nullptr) + { + #if defined(NEOPIXEL_RGBW) || defined(NEOPIXEL_RGB) + ledStrip1 = new LED_DRIVER(ledsNumber, DATA_PIN); + #else + ledStrip1 = new LED_DRIVER(ledsNumber, DATA_PIN, CLOCK_PIN); + #endif + } + } + + /** + * @brief Check if there is already prepared frame to display + * + * @return true + * @return false + */ + inline bool hasLateFrameToRender() + { + return readyToRender; + } + + inline void dropLateFrame() + { + readyToRender = false; + } + + inline void renderLeds(bool newFrame) + { + if (newFrame) + readyToRender = true; + + if (readyToRender && + (ledStrip1 != nullptr && ledStrip1->isReadyBlocking())) + { + statistics.increaseShow(); + readyToRender = false; + + // display segments + #if defined(SECOND_SEGMENT_START_INDEX) + ledStrip1->renderAllLanes(); + #else + ledStrip1->renderSingleLane(); + #endif + } + } + + inline bool setStripPixel(uint16_t pix, ColorDefinition &inputColor) + { + if (pix < ledsNumber) + { + #if defined(SECOND_SEGMENT_START_INDEX) + if (pix < SECOND_SEGMENT_START_INDEX) + ledStrip1->SetPixel(pix, inputColor); + else + { + #if defined(SECOND_SEGMENT_REVERSED) + ledStrip2->SetPixel(ledsNumber - pix - 1, inputColor); + #else + ledStrip2->SetPixel(pix - SECOND_SEGMENT_START_INDEX, inputColor); + #endif + } + #else + ledStrip1->SetPixel(pix, inputColor); + #endif + } + + return (pix + 1 < ledsNumber); + } +} base; + +#endif \ No newline at end of file diff --git a/include/calibration.h b/include/calibration.h new file mode 100644 index 0000000..154ba57 --- /dev/null +++ b/include/calibration.h @@ -0,0 +1,134 @@ +/* calibration.h +* +* MIT License +* +* Copyright (c) 2023 awawa-dev +* +* https://github.com/awawa-dev/HyperSerialPico +* +* 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. + */ + +#ifdef NEOPIXEL_RGBW + typedef ColorGrbw ColorDefinition; +#elif NEOPIXEL_RGB + #if defined(SECOND_SEGMENT_START_INDEX) + typedef ColorGrb ColorDefinition; + #else + typedef ColorGrb32 ColorDefinition; + #endif +#elif SPILED_APA102 + typedef ColorDotstartBgr ColorDefinition; +#endif + + +#if !defined(CALIBRATION_H) && (defined(NEOPIXEL_RGBW) || defined(HYPERSERIAL_TESTING)) +#define CALIBRATION_H + +#include +#include + +#define ROUND_DIVIDE(numer, denom) (((numer) + (denom) / 2) / (denom)) + +struct +{ + uint8_t white[256]; + uint8_t red[256]; + uint8_t green[256]; + uint8_t blue[256]; +} channelCorrection; + +class CalibrationConfig +{ + // calibration parameters + uint8_t gain = 0xFF; + uint8_t red = 0xA0; + uint8_t green = 0xA0; + uint8_t blue = 0xA0; + + /** + * @brief Build the LUT table using provided parameters + * + */ + void prepareCalibration() + { + // prepare LUT calibration table, cold white is much better than "neutral" white + for (uint32_t i = 0; i < 256; i++) + { + // color calibration + uint32_t _gain = gain * i; // adjust gain + uint32_t _red = red * i; // adjust red + uint32_t _green = green * i; // adjust green + uint32_t _blue = blue * i; // adjust blue + + channelCorrection.white[i] = (uint8_t)std::min(ROUND_DIVIDE(_gain, 0xFF), (uint32_t)0xFF); + channelCorrection.red[i] = (uint8_t)std::min(ROUND_DIVIDE(_red, 0xFF), (uint32_t)0xFF); + channelCorrection.green[i] = (uint8_t)std::min(ROUND_DIVIDE(_green,0xFF), (uint32_t)0xFF); + channelCorrection.blue[i] = (uint8_t)std::min(ROUND_DIVIDE(_blue, 0xFF), (uint32_t)0xFF); + } + } + + public: + CalibrationConfig() + { + prepareCalibration(); + } + + /** + * @brief Compare base calibration settings + * + */ + bool compareCalibrationSettings(uint8_t _gain, uint8_t _red, uint8_t _green, uint8_t _blue) + { + return _gain == gain && _red == red && _green == green && _blue == blue; + } + + /** + * @brief Set the parameters that define RGB to RGBW transformation + * + * @param _gain + * @param _red + * @param _green + * @param _blue + */ + void setParamsAndPrepareCalibration(uint8_t _gain, uint8_t _red, uint8_t _green, uint8_t _blue) + { + if (gain != _gain || red != _red || green != _green || blue != _blue) + { + gain = _gain; + red = _red; + green = _green; + blue = _blue; + prepareCalibration(); + } + } + + /** + * @brief print RGBW calibration parameters when no data is received + * + */ + void printCalibration() + { + char output[128]; + snprintf(output, sizeof(output),"RGBW => Gain: %i/255, red: %i, green: %i, blue: %i\r\n", gain, red, green, blue); + printf(output); + } +} calibrationConfig; +#endif + diff --git a/include/framestate.h b/include/framestate.h new file mode 100644 index 0000000..ba8d514 --- /dev/null +++ b/include/framestate.h @@ -0,0 +1,261 @@ +/* framestate.h +* +* MIT License +* +* Copyright (c) 2023 awawa-dev +* +* https://github.com/awawa-dev/HyperSerialPico +* +* 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. + */ + +#ifndef FRAMESTATE_H +#define FRAMESTATE_H + +/** + * @brief my AWA frame protocol definition + * + */ +enum class AwaProtocol +{ + HEADER_A, + HEADER_w, + HEADER_a, + HEADER_HI, + HEADER_LO, + HEADER_CRC, + VERSION2_GAIN, + VERSION2_RED, + VERSION2_GREEN, + VERSION2_BLUE, + RED, + GREEN, + BLUE, + FLETCHER1, + FLETCHER2, + FLETCHER_EXT +}; + +/** + * @brief Contains current state of the incoming frame + * + */ +class +{ + volatile AwaProtocol state = AwaProtocol::HEADER_A; + bool protocolVersion2 = false; + uint8_t CRC = 0; + uint16_t count = 0; + uint16_t currentLed = 0; + uint16_t fletcher1 = 0; + uint16_t fletcher2 = 0; + uint16_t fletcherExt = 0; + uint8_t position = 0; + + public: + ColorDefinition color; + + /** + * @brief Reset statistics for new frame + * + * @param input + */ + inline void init(uint8_t input) + { + currentLed = 0; + count = input * 0x100; + CRC = input; + fletcher1 = 0; + fletcher2 = 0; + fletcherExt = 0; + position = 0; + base.dropLateFrame(); + } + + /** + * @brief get computed CRC + * + * @return uint8_t + */ + inline uint8_t getCRC() + { + return CRC; + } + + /** + * @brief Get the color count reported by the frame + * + * @return uint16_t + */ + inline uint16_t getCount() + { + return count; + } + + /** + * @brief Get the Fletcher1 total sum + * + * @return uint16_t + */ + inline uint16_t getFletcher1() + { + return fletcher1; + } + + /** + * @brief Get the Fletcher2 total sum + * + * @return uint16_t + */ + inline uint16_t getFletcher2() + { + return fletcher2; + } + + /** + * @brief Get the FletcherExt total sum + * + * @return uint16_t + */ + inline uint16_t getFletcherExt() + { + return (fletcherExt != 0x41) ? fletcherExt : 0xaa; + } + + /** + * @brief Get and increase the current Led index + * + * @return uint16_t + */ + inline uint16_t getCurrentLedIndex() + { + return currentLed++; + } + + /** + * @brief Set if frame protocol version 2 (contains calibration data) + * + * @param newVer + */ + inline void setProtocolVersion2(bool newVer) + { + protocolVersion2 = newVer; + } + + /** + * @brief Verify if frame protocol version 2 (contains calibration data) + * + * @return true + * @return false + */ + inline bool isProtocolVersion2() + { + return protocolVersion2; + } + + /** + * @brief Set new AWA frame state + * + * @param newState + */ + inline void setState(AwaProtocol newState) + { + state = newState; + } + + /** + * @brief Get current AWA frame state + * + * @return AwaProtocol + */ + inline AwaProtocol getState() + { + return state; + } + + /** + * @brief Update CRC based on current and previuos input + * + * @param input + */ + inline void computeCRC(uint8_t input) + { + count += input; + CRC = CRC ^ input ^ 0x55; + } + + /** + * @brief Update Fletcher checksumn for incoming input + * + * @param input + */ + inline void addFletcher(uint8_t input) + { + fletcher1 = (fletcher1 + (uint16_t)input) % 255; + fletcher2 = (fletcher2 + fletcher1) % 255; + fletcherExt = (fletcherExt + (input ^ (position++))) % 255; + } + + /** + * @brief Check if the calibration data was updated and calculate new one + * + */ + inline void updateIncomingCalibration() + { + #ifdef NEOPIXEL_RGBW + if (protocolVersion2) + { + calibrationConfig.setParamsAndPrepareCalibration(calibration.gain, calibration.red, calibration.green, calibration.blue); + } + #endif + } + + + #ifdef NEOPIXEL_RGBW + /** + * @brief Compute && correct the white channel + * + */ + inline void rgb2rgbw() + { + color.W = std::min(channelCorrection.red[color.R], + std::min(channelCorrection.green[color.G], + channelCorrection.blue[color.B])); + color.R -= channelCorrection.red[color.W]; + color.G -= channelCorrection.green[color.W]; + color.B -= channelCorrection.blue[color.W]; + color.W = channelCorrection.white[color.W]; + } + #endif + + /** + * @brief Incoming calibration data + * + */ + struct + { + uint8_t gain = 0; + uint8_t red = 0; + uint8_t green = 0; + uint8_t blue = 0; + } calibration; + +} frameState; + +#endif \ No newline at end of file diff --git a/include/leds.h b/include/leds.h new file mode 100644 index 0000000..ec178af --- /dev/null +++ b/include/leds.h @@ -0,0 +1,565 @@ +#pragma once + +/* leds.h +* +* MIT License +* +* Copyright (c) 2023 awawa-dev +* +* https://github.com/awawa-dev/HyperSerialPico +* +* 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. + */ + +/* + HyperSerialPico led (aka PicoLada) library features: + - neopixel (rgb: ws2812b, ws2813..., rgbw: sk6812b) and dotstar (rgb: apa102, hd107, sk9822...) led strip support + - single and up to 8 lines parallel (neopixel) mode + - DMA + - PIO neopixel hardware processing + - using LUT tables for preparing PIO DMA parallel buffer + - SPI dotstar hardware support + - non-blocking rendering (check isReady if it's finished) + + Usage for sk6812 rgbw single lane: + ledStrip1 = new sk6812(ledsNumber, DATA_PIN); + ledStrip1->SetPixel(index, ColorGrbw(255)); + ledStrip1->renderSingleLane(); + + Usage for ws2812 rgb single lane: + ledStrip1 = new ws2812(ledsNumber, DATA_PIN); + ledStrip1->SetPixel(index, ColorGrb32(255)); + ledStrip1->renderSingleLane(); + + Usage for sk6812 rgbw multi lanes: + ledStrip1 = new sk6812(ledsNumber, DATA_PIN); // using DATA_PIN output + ledStrip2 = new sk6812(ledsNumber, DATA_PIN); // using DATA_PIN + 1 output + ledStrip1->SetPixel(index, ColorGrbw(255)); + ledStrip2->SetPixel(index, ColorGrbw(255)); + ledStrip1->renderAllLanes(); // renders ledStrip1 and ledStrip2 simoultaneusly + + Usage for ws2812 rgb multi lanes: + ledStrip1 = new ws2812(ledsNumber, DATA_PIN); // using DATA_PIN output + ledStrip2 = new ws2812(ledsNumber, DATA_PIN); // using DATA_PIN + 1 output + ledStrip1->SetPixel(index, ColorGrb(255)); + ledStrip2->SetPixel(index, ColorGrb(255)); + ledStrip1->renderAllLanes(); // renders ledStrip1 and ledStrip2 simoultaneusly + + Usage for dotstar rgb single line: + ledStrip1 = new apa102(ledsNumber, DATA_PIN, CLOCK_PIN); + ledStrip1->SetPixel(index, ColorDotstartBgr(255)); + ledStrip1->renderSingleLane(); +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct ColorGrb32 +{ + uint8_t notUsed; + uint8_t B; + uint8_t R; + uint8_t G; + + ColorGrb32(uint8_t gray) : + R(gray), G(gray), B(gray) + { + }; + + ColorGrb32() : R(0), G(0), B(0) + { + }; + + static bool isAlignedTo24() + { + return true; + }; +}; + +struct ColorGrb +{ + uint8_t B; + uint8_t R; + uint8_t G; + + + ColorGrb(uint8_t gray) : + R(gray), G(gray), B(gray) + { + }; + + ColorGrb() : R(0), G(0), B(0) + { + }; +}; + +struct ColorGrbw +{ + uint8_t W; + uint8_t B; + uint8_t R; + uint8_t G; + + ColorGrbw(uint8_t gray) : + R(gray), G(gray), B(gray), W(gray) + { + }; + + ColorGrbw() : R(0), G(0), B(0), W(0) + { + }; + + static bool isAlignedTo24() + { + return false; + }; +}; + +struct ColorDotstartBgr +{ + uint8_t brightness; + uint8_t B; + uint8_t G; + uint8_t R; + + ColorDotstartBgr(uint8_t gray) : + R(gray), G(gray), B(gray), brightness(gray | 0b11100000) + { + }; + + ColorDotstartBgr() : R(0), G(0), B(0), brightness(0xff) + { + }; +}; + +class LedDriver +{ + protected: + + int ledsNumber; + int pin; + int clockPin; + int dmaSize; + uint8_t* buffer; + uint8_t* dma; + + public: + + LedDriver(int _ledsNumber, int _pin, int _dmaSize): LedDriver(_ledsNumber, _pin, 0, _dmaSize) + { + + } + + LedDriver(int _ledsNumber, int _pin, int _clockPin, int _dmaSize) + { + LedDriverDmaReceiver = this; + ledsNumber = _ledsNumber; + pin = _pin; + clockPin = _clockPin; + dmaSize = _dmaSize; + if (dmaSize % 4) + dmaSize += (4 - (_dmaSize % 4)); + buffer = reinterpret_cast(calloc(dmaSize, 1)); + dma = reinterpret_cast(calloc(dmaSize, 1)); + } + + ~LedDriver() + { + free(buffer); + free(dma); + if (LedDriverDmaReceiver == this) + LedDriverDmaReceiver = nullptr; + } + + static LedDriver* LedDriverDmaReceiver; +}; + +LedDriver* LedDriver::LedDriverDmaReceiver = nullptr; + +class DmaClient +{ + protected: + + PIO selectedPIO; + uint stateIndex; + + static uint PICO_DMA_CHANNEL; + static volatile uint64_t lastRenderTime; + static volatile bool isDmaBusy; + + + DmaClient() + { + PICO_DMA_CHANNEL = dma_claim_unused_channel(true); + isDmaBusy = false; + lastRenderTime = 0; + }; + + ~DmaClient() + { + for(int i = 0; i < 10 && isDmaBusy; i++) + busy_wait_us(500); + + dma_channel_abort(PICO_DMA_CHANNEL); + dma_channel_set_irq0_enabled(PICO_DMA_CHANNEL, false); + irq_set_enabled(DMA_IRQ_0, false); + + dma_channel_unclaim(PICO_DMA_CHANNEL); + }; + + void dmaConfigure(PIO _selectedPIO, uint _sm) + { + selectedPIO = _selectedPIO; + stateIndex = _sm; + }; + + void initDmaPio(uint dataLenDword32) + { + dma_channel_config dmaConfig = dma_channel_get_default_config(PICO_DMA_CHANNEL); + channel_config_set_dreq(&dmaConfig, pio_get_dreq(selectedPIO, stateIndex, true)); + channel_config_set_transfer_data_size(&dmaConfig, DMA_SIZE_32); + channel_config_set_read_increment(&dmaConfig, true); + dma_channel_configure(PICO_DMA_CHANNEL, &dmaConfig, &selectedPIO->txf[stateIndex], NULL, dataLenDword32, false); + + assignDmaIrq(); + }; + + void initDmaSpi(uint dataLenByte8) + { + dma_channel_config dmaConfig = dma_channel_get_default_config(PICO_DMA_CHANNEL); + channel_config_set_transfer_data_size(&dmaConfig, DMA_SIZE_8); + channel_config_set_dreq(&dmaConfig, spi_get_dreq(spi_default, true)); + dma_channel_configure(PICO_DMA_CHANNEL, &dmaConfig,&spi_get_hw(spi_default)->dr, NULL, dataLenByte8, false); + + assignDmaIrq(); + }; + + void assignDmaIrq() + { + irq_set_exclusive_handler(DMA_IRQ_0, dmaFinishReceiver); + dma_channel_set_irq0_enabled(PICO_DMA_CHANNEL, true); + irq_set_enabled(DMA_IRQ_0, true); + }; + + public: + + bool isReadyBlocking() + { + int wait = 200; + while(isDmaBusy && wait-- > 0) + busy_wait_us(50); + + return !isDmaBusy; + } + + bool isReady() + { + return !isDmaBusy; + } + + static void dmaFinishReceiver() + { + if (dma_hw->ints0 & (1u<ints0 = (1u<= 1) + { + programAddress = pio_add_program(selectedPIO, &neopixel_parallel_program); + for(uint i=_pin; i<_pin + lanes; i++){ + pio_gpio_init(selectedPIO, i); + } + smConfig = neopixel_parallel_program_get_default_config(programAddress); + sm_config_set_out_pins(&smConfig, _pin, lanes); + sm_config_set_set_pins(&smConfig, _pin, lanes); + } + else + { + programAddress = pio_add_program(selectedPIO, &neopixel_program); + pio_gpio_init(selectedPIO, _pin); + smConfig = neopixel_program_get_default_config(programAddress); + sm_config_set_sideset_pins(&smConfig, _pin); + } + + pio_sm_set_consecutive_pindirs(selectedPIO, stateIndex, _pin, std::max(lanes, 1), true); + sm_config_set_out_shift(&smConfig, false, true, (alignTo24) ? 24: 32); + sm_config_set_fifo_join(&smConfig, PIO_FIFO_JOIN_TX); + float div = clock_get_hz(clk_sys) / (800000 * 12); + sm_config_set_clkdiv(&smConfig, div); + pio_sm_init(selectedPIO, stateIndex, programAddress, &smConfig); + pio_sm_set_enabled(selectedPIO, stateIndex, true); + + initDmaPio(dmaSize / 4); + } + + uint8_t* getBufferMemory() + { + return buffer; + } + + protected: + + void renderDma(bool resetBuffer) + { + if (isDmaBusy) + return; + + isDmaBusy = true; + + uint64_t currentTime = time_us_64(); + if (currentTime < resetTime + lastRenderTime) + busy_wait_us(std::min(resetTime + lastRenderTime - currentTime, resetTime)); + + memcpy(dma, buffer, dmaSize); + + dma_channel_set_read_addr(PICO_DMA_CHANNEL, dma, true); + + if (resetBuffer) + memset(buffer, 0, dmaSize); + } +}; + +template +class NeopixelType : public Neopixel +{ + public: + + NeopixelType(int _ledsNumber, int _pin) : Neopixel(0, RESET_TIME, _ledsNumber, _pin, _ledsNumber * sizeof(colorData), colorData::isAlignedTo24()) + { + } + + void SetPixel(int index, colorData color) + { + if (index >= ledsNumber) + return; + + *(reinterpret_cast(buffer)+index) = color; + } + + void renderSingleLane() + { + renderDma(false); + } +}; + + +class NeopixelParallel +{ + static Neopixel *muxer; + static int instances; + static int maxLeds; + + protected: + const uint8_t myLaneMask; + static uint8_t* buffer; + + public: + + NeopixelParallel(size_t pixelSize, uint64_t _resetTime, int _ledsNumber, int _pin): + myLaneMask(1 << (instances++)) + { + maxLeds = std::max(maxLeds, _ledsNumber); + + delete muxer; + muxer = new Neopixel(instances, _resetTime, maxLeds, _pin, maxLeds * 8 * pixelSize ); + buffer = muxer->getBufferMemory(); + } + + ~NeopixelParallel() + { + if (instances > 0) + instances--; + + if (instances == 0) + { + delete muxer; + muxer = nullptr; + buffer = nullptr; + maxLeds = 0; + } + } + + bool isReadyBlocking() + { + return muxer->isReadyBlocking(); + } + + bool isReady() + { + return muxer->isReady(); + } + + void renderAllLanes() + { + muxer->renderDma(true); + } +}; + +template +class NeopixelParallelType : public NeopixelParallel +{ + uint32_t lut[16]; + + public: + + NeopixelParallelType(int _ledsNumber, int _basePinForLanes) : NeopixelParallel(sizeof(colorData), RESET_TIME, + _ledsNumber, _basePinForLanes) + { + for (uint8_t a = 0; a < 16; a++) + { + uint8_t* target = reinterpret_cast(&(lut[a])); + for (uint8_t b = 0; b < 4; b++) + *(target++) = (uint8_t) ((a & (0b00000001 << b)) ? myLaneMask : 0); + } + } + + void SetPixel(int index, colorData color) + { + if (index >= maxLeds) + return; + + uint8_t* source = reinterpret_cast(&color); + uint32_t* target = reinterpret_cast(&(buffer[(index + 1) * 8 * sizeof(colorData)])); + + for(int i = 0; i < sizeof(colorData); i++) + { + *(--target) |= lut[ *(source) & 0b00001111]; + *(--target) |= lut[ *(source++) >> 4]; + } + } +}; + +class Dotstar : public LedDriver, public DmaClient +{ + uint64_t resetTime; + + friend class NeopixelParallel; + + public: + Dotstar(uint64_t _resetTime, int _ledsNumber, uint32_t _datapin, uint32_t _clockpin, int _dmaSize): + LedDriver(_ledsNumber, _datapin, _clockpin, _dmaSize) + { + dmaConfigure(pio0, 0); + resetTime = _resetTime; + + spi_init(spi_default, 10000000); + gpio_set_function(PICO_DEFAULT_SPI_RX_PIN, GPIO_FUNC_SPI); + gpio_init(PICO_DEFAULT_SPI_CSN_PIN); + gpio_set_function(_clockpin, GPIO_FUNC_SPI); + gpio_set_function(_datapin, GPIO_FUNC_SPI); + bi_decl(bi_3pins_with_func(PICO_DEFAULT_SPI_RX_PIN, _datapin, _clockpin, GPIO_FUNC_SPI)); + bi_decl(bi_1pin_with_name(PICO_DEFAULT_SPI_CSN_PIN, "SPI CS")); + + initDmaSpi(_dmaSize); + } + + uint8_t* getBufferMemory() + { + return buffer; + } + + protected: + + void renderDma() + { + if (isDmaBusy) + return; + + isDmaBusy = true; + + uint64_t currentTime = time_us_64(); + if (currentTime < resetTime + lastRenderTime) + busy_wait_us(std::min(resetTime + lastRenderTime - currentTime, resetTime)); + + memcpy(dma, buffer, dmaSize); + + dma_channel_set_read_addr(PICO_DMA_CHANNEL, dma, true); + } +}; + +template +class DotstarType : public Dotstar +{ + public: + + DotstarType(int _ledsNumber, int _dataPin, int _clockPin) : Dotstar(RESET_TIME, _ledsNumber, _dataPin, _clockPin, (_ledsNumber + 2) * sizeof(colorData)) + { + } + + void SetPixel(int index, colorData color) + { + if (index >= ledsNumber) + return; + + *(reinterpret_cast(buffer)+index+1) = color; + } + + void renderSingleLane() + { + memset(buffer,0 ,4); + *(reinterpret_cast(buffer)+ledsNumber+1) = colorData(0xff); + renderDma(); + } +}; + +Neopixel* NeopixelParallel::muxer = nullptr; +uint8_t* NeopixelParallel::buffer = nullptr; +int NeopixelParallel::instances = 0; +int NeopixelParallel::maxLeds = 0; +uint DmaClient::PICO_DMA_CHANNEL = 0; +volatile uint64_t DmaClient::lastRenderTime = 0; +volatile bool DmaClient::isDmaBusy = false; + + +// API classes +typedef NeopixelType<650, ColorGrb32> ws2812; +typedef NeopixelType<450, ColorGrbw> sk6812; +typedef NeopixelParallelType<300, ColorGrb> ws2812p; +typedef NeopixelParallelType<80, ColorGrbw> sk6812p; +typedef DotstarType<100, ColorDotstartBgr> apa102; + diff --git a/include/main.h b/include/main.h new file mode 100644 index 0000000..f114808 --- /dev/null +++ b/include/main.h @@ -0,0 +1,264 @@ +/* main.h +* +* MIT License +* +* Copyright (c) 2023 awawa-dev +* +* https://github.com/awawa-dev/HyperSerialPico +* +* 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. + */ + +#ifndef MAIN_H +#define MAIN_H + +#define MAX_BUFFER (3013 * 3 + 1) +#define HELLO_MESSAGE "\r\nWelcome!\r\nAwa driver 9.\r\n" + +#include "calibration.h" +#include "statistics.h" +#include "base.h" +#include "framestate.h" + +void updateMainStatistics(unsigned long currentTime, unsigned long deltaTime, bool hasData) +{ + if (hasData && deltaTime >= 1000 && deltaTime <= 1025 && statistics.getGoodFrames() > 3) + statistics.update(currentTime); + else if (deltaTime > 1025) + statistics.lightReset(currentTime, hasData); +} + +/** + * @brief process received data on core 0 + * + */ +void processData() +{ + // update and print statistics + unsigned long currentTime = millis(); + unsigned long deltaTime = currentTime - statistics.getStartTime(); + + updateMainStatistics(currentTime, deltaTime, base.queueCurrent != base.queueEnd); + + if (statistics.getStartTime() + 5000 < millis()) + { + frameState.setState(AwaProtocol::HEADER_A); + } + + // render waiting frame if available + if (base.hasLateFrameToRender()) + base.renderLeds(false); + + // process received data + while (base.queueCurrent != base.queueEnd) + { + uint8_t input = base.buffer[base.queueCurrent++]; + + if (base.queueCurrent >= MAX_BUFFER) + { + base.queueCurrent = 0; + yield(); + } + + switch (frameState.getState()) + { + case AwaProtocol::HEADER_A: + // assume it's protocol version 1, verify it later + frameState.setProtocolVersion2(false); + if (input == 'A') + frameState.setState(AwaProtocol::HEADER_w); + break; + + case AwaProtocol::HEADER_w: + if (input == 'w') + frameState.setState(AwaProtocol::HEADER_a); + else + frameState.setState(AwaProtocol::HEADER_A); + break; + + case AwaProtocol::HEADER_a: + // detect protocol version + if (input == 'a') + frameState.setState(AwaProtocol::HEADER_HI); + else if (input == 'A') + { + frameState.setState(AwaProtocol::HEADER_HI); + frameState.setProtocolVersion2(true); + } + else + frameState.setState(AwaProtocol::HEADER_A); + break; + + case AwaProtocol::HEADER_HI: + // initialize new frame properties + statistics.increaseTotal(); + frameState.init(input); + frameState.setState(AwaProtocol::HEADER_LO); + break; + + case AwaProtocol::HEADER_LO: + frameState.computeCRC(input); + frameState.setState(AwaProtocol::HEADER_CRC); + break; + + case AwaProtocol::HEADER_CRC: + // verify CRC and create/update LED driver if neccesery + if (frameState.getCRC() == input) + { + uint16_t ledSize = frameState.getCount() + 1; + + // sanity check + if (ledSize > 4096) + frameState.setState(AwaProtocol::HEADER_A); + else + { + if (ledSize != base.getLedsNumber()) + base.initLedStrip(ledSize); + + frameState.setState(AwaProtocol::RED); + } + } + else if (frameState.getCount() == 0x2aa2 && (input == 0x15 || input == 0x35)) + { + statistics.print(currentTime, base.processDataHandle, base.processSerialHandle); + + if (input == 0x15) + printf(HELLO_MESSAGE); + delay(10); + + currentTime = millis(); + statistics.reset(currentTime); + frameState.setState(AwaProtocol::HEADER_A); + } + else + frameState.setState(AwaProtocol::HEADER_A); + break; + + case AwaProtocol::RED: + frameState.color.R = input; + frameState.addFletcher(input); + + frameState.setState(AwaProtocol::GREEN); + break; + + case AwaProtocol::GREEN: + frameState.color.G = input; + frameState.addFletcher(input); + + frameState.setState(AwaProtocol::BLUE); + break; + + case AwaProtocol::BLUE: + frameState.color.B = input; + frameState.addFletcher(input); + + #ifdef NEOPIXEL_RGBW + // calculate RGBW from RGB using provided calibration data + frameState.rgb2rgbw(); + #endif + + // set pixel, increase the index and check if it was the last LED color to come + if (base.setStripPixel(frameState.getCurrentLedIndex(), frameState.color)) + { + frameState.setState(AwaProtocol::RED); + } + else + { + if (frameState.isProtocolVersion2()) + frameState.setState(AwaProtocol::VERSION2_GAIN); + else + frameState.setState(AwaProtocol::FLETCHER1); + } + + break; + + case AwaProtocol::VERSION2_GAIN: + frameState.calibration.gain = input; + frameState.addFletcher(input); + + frameState.setState(AwaProtocol::VERSION2_RED); + break; + + case AwaProtocol::VERSION2_RED: + frameState.calibration.red = input; + frameState.addFletcher(input); + + frameState.setState(AwaProtocol::VERSION2_GREEN); + break; + + case AwaProtocol::VERSION2_GREEN: + frameState.calibration.green = input; + frameState.addFletcher(input); + + frameState.setState(AwaProtocol::VERSION2_BLUE); + break; + + case AwaProtocol::VERSION2_BLUE: + frameState.calibration.blue = input; + frameState.addFletcher(input); + + frameState.setState(AwaProtocol::FLETCHER1); + break; + + case AwaProtocol::FLETCHER1: + // initial frame data integrity check + if (input != frameState.getFletcher1()) + frameState.setState(AwaProtocol::HEADER_A); + else + frameState.setState(AwaProtocol::FLETCHER2); + break; + + case AwaProtocol::FLETCHER2: + // initial frame data integrity check + if (input != frameState.getFletcher2()) + frameState.setState(AwaProtocol::HEADER_A); + else + frameState.setState(AwaProtocol::FLETCHER_EXT); + break; + + case AwaProtocol::FLETCHER_EXT: + // final frame data integrity check + if (input == frameState.getFletcherExt()) + { + statistics.increaseGood(); + + base.renderLeds(true); + + #ifdef NEOPIXEL_RGBW + // if received the calibration data, update it now + if (frameState.isProtocolVersion2()) + { + frameState.updateIncomingCalibration(); + } + #endif + + currentTime = millis(); + deltaTime = currentTime - statistics.getStartTime(); + updateMainStatistics(currentTime, deltaTime, true); + + yield(); + } + + frameState.setState(AwaProtocol::HEADER_A); + break; + } + } +} + +#endif \ No newline at end of file diff --git a/include/statistics.h b/include/statistics.h new file mode 100644 index 0000000..1558014 --- /dev/null +++ b/include/statistics.h @@ -0,0 +1,166 @@ +/* stats.h +* +* MIT License +* +* Copyright (c) 2023 awawa-dev +* +* https://github.com/awawa-dev/HyperSerialPico + +* 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. + */ + +#ifndef STATISTICS_H +#define STATISTICS_H + +// statistics (stats sent only when there is no communication) +class +{ + unsigned long startTime = 0; + uint16_t goodFrames = 0; + uint16_t showFrames = 0; + uint16_t totalFrames = 0; + uint16_t finalGoodFrames = 0; + uint16_t finalShowFrames = 0; + uint16_t finalTotalFrames = 0; + + public: + /** + * @brief Get the start time of the current period + * + * @return unsigned long + */ + inline unsigned long getStartTime() + { + return startTime; + } + + /** + * @brief Detected new frame + * + */ + inline void increaseTotal() + { + totalFrames++; + } + + /** + * @brief The frame is received and shown + * + */ + inline void increaseShow() + { + showFrames++; + } + + /** + * @brief The frame is received correctly (not yet displayed) + * + */ + inline void increaseGood() + { + goodFrames++; + } + + /** + * @brief Get number of correctly received frames + * + * @return uint16_t + */ + inline uint16_t getGoodFrames() + { + return goodFrames; + } + + /** + * @brief Period restart, save current statistics ans send them later if there is no incoming communication + * + * @param currentTime + */ + void update(unsigned long currentTime) + { + if (totalFrames > 0) + { + finalShowFrames = showFrames; + finalGoodFrames = std::min(goodFrames, totalFrames); + finalTotalFrames = totalFrames; + } + + startTime = currentTime; + goodFrames = 0; + totalFrames = 0; + showFrames = 0; + } + + /** + * @brief Print last saved statistics to the serial port + * + * @param curTime + * @param taskHandle + */ + void print(unsigned long curTime, TaskHandle_t taskHandle1, TaskHandle_t taskHandle2) + { + char output[128]; + + startTime = curTime; + goodFrames = 0; + totalFrames = 0; + showFrames = 0; + + snprintf(output, sizeof(output), "HyperHDR frames: %u (FPS), receiv.: %u, good: %u, incompl.: %u, mem1: %i, mem2: %i, heap: %zu\r\n", + finalShowFrames, finalTotalFrames,finalGoodFrames,(finalTotalFrames - finalGoodFrames), + (taskHandle1 != nullptr) ? uxTaskGetStackHighWaterMark(taskHandle1) : 0, + (taskHandle2 != nullptr) ? uxTaskGetStackHighWaterMark(taskHandle2) : 0, + xPortGetFreeHeapSize()); + printf(output); + + #if defined(NEOPIXEL_RGBW) + calibrationConfig.printCalibration(); + #endif + } + + /** + * @brief Reset statistics + * + */ + void reset(unsigned long currentTime) + { + startTime = currentTime; + + finalShowFrames = 0; + finalGoodFrames = 0; + finalTotalFrames = 0; + + goodFrames = 0; + totalFrames = 0; + showFrames = 0; + } + + void lightReset(unsigned long curTime, bool hasData) + { + if (hasData) + startTime = curTime; + + goodFrames = 0; + totalFrames = 0; + showFrames = 0; + } + +} statistics; + +#endif \ No newline at end of file diff --git a/pio/neopixel.pio b/pio/neopixel.pio new file mode 100644 index 0000000..d737bb4 --- /dev/null +++ b/pio/neopixel.pio @@ -0,0 +1,46 @@ +; MIT License +; +; Copyright (c) 2023 awawa-dev +; +; https://github.com/awawa-dev/HyperSerialPico +; +; 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. + + +.program neopixel +.side_set 1 + +.wrap_target +bitloop: + out x, 1 side 0 [4] + jmp !x do_zero side 1 [2] +do_one: + jmp bitloop side 1 [3] +do_zero: + nop side 0 [3] +.wrap + +.program neopixel_parallel + +.wrap_target + out x, 8 + mov pins, !null [2] + mov pins, x [3] + mov pins, null [3] +.wrap \ No newline at end of file diff --git a/sdk/config/FreeRTOSConfig.h b/sdk/config/FreeRTOSConfig.h new file mode 100644 index 0000000..1d90800 --- /dev/null +++ b/sdk/config/FreeRTOSConfig.h @@ -0,0 +1,139 @@ +/* + * FreeRTOS V202107.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html + *----------------------------------------------------------*/ + +/* Scheduler Related */ +#define configUSE_PREEMPTION 1 +#define configUSE_TICKLESS_IDLE 0 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) +#define configMAX_PRIORITIES 32 +#define configMINIMAL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 256 +#define configUSE_16_BIT_TICKS 0 + +#define configIDLE_SHOULD_YIELD 1 + +/* Synchronization Related */ +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configUSE_QUEUE_SETS 1 +#define configUSE_TIME_SLICING 1 +#define configUSE_NEWLIB_REENTRANT 0 +#define configENABLE_BACKWARD_COMPATIBILITY 0 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 + +/* System */ +#define configSTACK_DEPTH_TYPE uint32_t +#define configMESSAGE_BUFFER_LENGTH_TYPE size_t + +/* Memory allocation related definitions. */ +#define configSUPPORT_STATIC_ALLOCATION 0 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configTOTAL_HEAP_SIZE (128*1024) +#define configAPPLICATION_ALLOCATED_HEAP 0 + +/* Hook function related definitions. */ +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 + +/* Run time and task stats gathering related definitions. */ +#define configGENERATE_RUN_TIME_STATS 0 +#define configUSE_TRACE_FACILITY 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 + +/* Co-routine related definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES 1 + +/* Software timer related definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 10 +#define configTIMER_TASK_STACK_DEPTH 1024 + +/* Interrupt nesting behaviour configuration. */ +/* +#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] +#define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] +#define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application] +*/ + +/* SMP port only */ +#define configNUM_CORES 1 +#define configTICK_CORE 1 +#define configRUN_MULTIPLE_PRIORITIES 1 + +/* RP2040 specific */ +#define configSUPPORT_PICO_SYNC_INTEROP 1 +#define configSUPPORT_PICO_TIME_INTEROP 1 + +#include +/* Define to trap errors during development. */ +#define configASSERT(x) assert(x) + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xQueueGetMutexHolder 1 + +/* A header file that defines trace macro can be included here. */ + +#endif /* FREERTOS_CONFIG_H */ + diff --git a/sdk/freertos b/sdk/freertos new file mode 160000 index 0000000..570ade4 --- /dev/null +++ b/sdk/freertos @@ -0,0 +1 @@ +Subproject commit 570ade4001e50adbf06a074582ea993af562e0e1 diff --git a/sdk/pico b/sdk/pico new file mode 160000 index 0000000..f396d05 --- /dev/null +++ b/sdk/pico @@ -0,0 +1 @@ +Subproject commit f396d05f8252d4670d4ea05c8b7ac938ef0cd381 diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..a3b8a85 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,177 @@ +/* main.cpp +* +* MIT License +* +* Copyright (c) 2023 awawa-dev +* +* https://github.com/awawa-dev/HyperSerialPico +* +* 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" + + +/////////////////////////////////////////////////////////////////////////// +// 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) + +#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 SPILED_APA102 + #pragma message(VAR_NAME_VALUE(SPILED_APA102)) +#endif + +#ifdef NEOPIXEL_RGBW + #define LED_DRIVER sk6812 +#elif NEOPIXEL_RGB + #define LED_DRIVER ws2812 +#endif + +#ifdef SPILED_APA102 + #define LED_DRIVER apa102 +#endif + +#pragma message(VAR_NAME_VALUE(DATA_PIN)) +#ifdef CLOCK_PIN + #pragma message(VAR_NAME_VALUE(CLOCK_PIN)) +#endif + +#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.serialSemaphore, portMAX_DELAY)) + { + //printf("Core %d: Start index: %i, End index: %i, (%u)\n", get_core_num(), base.queueCurrent, base.queueEnd, millis()); + processData(); + //printf("Core %d: Start index: %i, End index: %i, (%u)\n", get_core_num(), base.queueCurrent, base.queueEnd, millis()); + } + } +} + +static void core0( void *pvParameters ) +{ + for( ;; ) + { + if (sem_acquire_timeout_us(&base.receiverSemaphore, portMAX_DELAY)) + { + int total, toRead; + do + { + toRead = MAX_BUFFER - base.queueEnd; + total = stdio_usb.in_chars((char*)(&(base.buffer[base.queueEnd])), MAX_BUFFER - base.queueEnd); + if (total > 0) + base.queueEnd = (base.queueEnd + total) % (MAX_BUFFER); + }while(toRead==total); + + sem_release(&base.serialSemaphore); + } + } +} + +static void serialEvent(void *) +{ + sem_release(&base.receiverSemaphore); +} + +int main(void) +{ + stdio_init_all(); + + sem_init(&base.serialSemaphore, 0, 1); + + sem_init(&base.receiverSemaphore, 0, 1); + + multicore_launch_core1(core1); + + stdio_set_chars_available_callback(serialEvent, nullptr); + + xTaskCreate(core0, + "HyperSerialPico:core0", + configMINIMAL_STACK_SIZE, + NULL, + (configMAX_PRIORITIES - 1), + &base.processSerialHandle); + + vTaskStartScheduler(); + panic_unsupported(); +} + + + +