Compare commits
329 Commits
Author | SHA1 | Date |
---|---|---|
Philip Howard | f3b53b6b5f | |
coadkins | 37c4d22527 | |
Philip Howard | 616b1cc8d6 | |
Phil Howard | 45a9925072 | |
Connor Linfoot | 32c10482d9 | |
Philip Howard | 4c44b77193 | |
Phil Howard | 5510c82564 | |
Philip Howard | 3a10b29f54 | |
Phil Howard | 8cf276b992 | |
Philip Howard | f1ea35fbbf | |
Philip Howard | c066325ca0 | |
Philip Howard | fd4eb165f8 | |
Phil Howard | 8fc8a8ee06 | |
Phil Howard | 3bfb548686 | |
Philip Howard | 9edcdcc126 | |
Philip Howard | e8e550b18b | |
thirdr | cdb7b4bf2c | |
Philip Howard | 4fc3095433 | |
Phil Howard | 9c5b529754 | |
ZodiusInfuser | a87d5581aa | |
ZodiusInfuser | 44d7875f7e | |
ZodiusInfuser | a90c31fb3b | |
ZodiusInfuser | 458b0ac209 | |
Phil Howard | a537672dd4 | |
Phil Howard | d34e692f51 | |
Phil Howard | 27b913124c | |
Phil Howard | c7b788cd1d | |
Philip Howard | c386b3e9cf | |
Philip Howard | a7a2e2bee0 | |
Phil Howard | 19fa8864cf | |
Phil Howard | 964cf5eedf | |
Phil Howard | eab1595352 | |
Phil Howard | 5dd76ed31b | |
Philip Howard | 6eb0f90e53 | |
Phil Howard | b0d53dadb3 | |
Phil Howard | ad518064e9 | |
Philip Howard | d83107474e | |
Phil Howard | c4f70df1cf | |
Phil Howard | 10221066dd | |
Philip Howard | ab64fcaccc | |
Hel Gibbons | 32c63c343d | |
Hel Gibbons | 8d964bce2c | |
ZodiusInfuser | 8ca47d6405 | |
Skyler Mansfield | b23a71b889 | |
Philip Howard | 6b23c1526d | |
Phil Howard | c19b2276f1 | |
Philip Howard | 392d75b00d | |
Philip Howard | 911cbb710e | |
Philip Howard | 4e3e2c836d | |
Philip Howard | 5bd5334379 | |
Philip Howard | 9ddbb17a82 | |
Phil Howard | 5126263f91 | |
Philip Howard | 0d3fce9b9d | |
Rob Berwick | 9e6a0725c0 | |
Rob Berwick | 3e81b245a1 | |
Phil Howard | bd6bd289d1 | |
Phil Howard | b6953c25a1 | |
Phil Howard | b5df0ac277 | |
Phil Howard | 116bbb1296 | |
Phil Howard | 6154116662 | |
Phil Howard | d45daef654 | |
Phil Howard | 1b3d9d9fb2 | |
Phil Howard | 4dd76525f6 | |
Phil Howard | 3bac13fcc8 | |
Hel Gibbons | bff245324b | |
Hel Gibbons | 400347b862 | |
Ray Bellis | da0ac1821f | |
Rob Berwick | 6dcc0d4fa0 | |
Erin Sparling | fc3f8e5654 | |
Erin Sparling | c001f9bb59 | |
Erin Sparling | 59fa0a1ff8 | |
Stefan Werder | a803c3cee4 | |
Ray Bellis | 6fd667b1ca | |
Paco Hope | 078d81312f | |
Hel Gibbons | a60c856ea8 | |
Philip Howard | b4451c3bdc | |
Phil Howard | ce42d814a7 | |
Philip Howard | ee7f2758dd | |
Phil Howard | 388d8af3dc | |
Phil Howard | 0f75a2839f | |
Phil Howard | e691628723 | |
Phil Howard | 08ce4fbb81 | |
Phil Howard | 20de8a3198 | |
Philip Howard | 9499b7e908 | |
Philip Howard | 65fd3b1c5a | |
Phil Howard | 4b3e83f2ff | |
Philip Howard | fc777ff0ca | |
Hel Gibbons | 5345cc42d2 | |
Hel Gibbons | 169ed9c763 | |
Philip Howard | 8eac60afc6 | |
Phil Howard | 93525a422f | |
Hel Gibbons | 36f3e3a69f | |
Hel Gibbons | 29a13305d2 | |
Philip Howard | 1f4704afdb | |
Phil Howard | ae7e6e8c6c | |
Phil Howard | 5f730ff400 | |
Philip Howard | c3919bd648 | |
Philip Howard | ed3ce45f00 | |
Philip Howard | 1a7deaab71 | |
Philip Howard | 9735402ee3 | |
Philip Howard | c045c405c3 | |
Mike Bell | 80e1e16782 | |
Mike Bell | cdd648f3f6 | |
Mike Bell | 81f42f25b6 | |
Mike Bell | 841c141ebf | |
Mike Bell | c812eec432 | |
Mike Bell | 41eb2b503e | |
Mike Bell | 581481c2ef | |
Mike Bell | e908d5e53e | |
Mike Bell | 34b8ac9f0c | |
Philip Howard | 9124b376d2 | |
Phil Howard | c725b4eee0 | |
Philip Howard | a334899b61 | |
Phil Howard | cca2d569e4 | |
Philip Howard | 3d8f8c9a83 | |
Phil Howard | 788f6c3232 | |
Phil Howard | 231ceb70f2 | |
Phil Howard | c9fd68ec58 | |
Hel Gibbons | b82429caf0 | |
Hel Gibbons | 386a3594c0 | |
Hel Gibbons | 9e0b3adc0c | |
Pete Favelle | 8c3a21ec1a | |
Pete Favelle | 2f44e85431 | |
Phil Howard | 5a92a9c735 | |
Pete Favelle | 8a9ef39158 | |
Phil Howard | c443f8d206 | |
Phil Howard | 591058fb12 | |
Phil Howard | cfe8b3c096 | |
Phil Howard | 9d0501a43c | |
Phil Howard | 7c5ebfce8c | |
Phil Howard | 61c9d7e9b6 | |
Phil Howard | c7d9fe411a | |
Phil Howard | 4671607b3a | |
Phil Howard | 95ab839ba5 | |
Phil Howard | 9e430fd68c | |
Phil Howard | c9a8d5ef49 | |
Phil Howard | 38aaa04c5d | |
Phil Howard | e8dba75aff | |
Phil Howard | 09a58b269f | |
Phil Howard | cc7219b44a | |
Philip Howard | 57042bfed3 | |
Hel Gibbons | dc4ee0d459 | |
Hel Gibbons | 157180c476 | |
Hel Gibbons | 7344e4d1a4 | |
Phil Howard | 1157e605a1 | |
Philip Howard | b82d16e8ae | |
Hel Gibbons | 095122c606 | |
Mike Bell | 211e0aa618 | |
Mike Bell | b8116fc371 | |
Mike Bell | 0cfcb22aa4 | |
Mike Bell | 3cdfe558e8 | |
Mike Bell | 103228a88d | |
Mike Bell | 3a5f069ec1 | |
Mike Bell | 765b8a6226 | |
Mike Bell | 3c2c7ccc94 | |
Mike Bell | b9cd998709 | |
Mike Bell | 1a54f7b77d | |
Mike Bell | 8f78e3d6bc | |
Mike Bell | a396512e7f | |
Mike Bell | 9a0b21d417 | |
Mike Bell | e9779fc0e7 | |
Mike Bell | 559ce08e04 | |
Phil Howard | cbc05863c0 | |
Phil Howard | 7d8bbf5c08 | |
Mike Bell | 7e9f16d80c | |
Mike Bell | 4b57162c06 | |
Phil Howard | de4aaa80b6 | |
Phil Howard | 4afe062d19 | |
Mike Bell | 3bc215074c | |
Mike Bell | a6bd626334 | |
Mike Bell | 575e806cc8 | |
Mike Bell | be943bd5a0 | |
Mike Bell | daf7232024 | |
Mike Bell | a7435c6a5e | |
Mike Bell | 5a6aa0186c | |
Mike Bell | 360588ff67 | |
Mike Bell | 4ed1d61336 | |
Mike Bell | 31b480d138 | |
Mike Bell | c7049f4ff1 | |
Mike Bell | 1d8c836635 | |
Mike Bell | e295e69c72 | |
Mike Bell | 5971bc9ad8 | |
Mike Bell | a1caa9495c | |
Mike Bell | f4b8bc9025 | |
Mike Bell | 2e8632f2b6 | |
Mike Bell | da36b0ad32 | |
Mike Bell | 9acc270418 | |
Mike Bell | 5f8e7556f0 | |
Philip Howard | a7efef0049 | |
Hel Gibbons | 5a5786d066 | |
Simon Prickett | 710863099d | |
Simon Prickett | 316957c263 | |
Simon Prickett | 9b18ed2594 | |
Simon Prickett | 658025a99b | |
Hel Gibbons | fef22530ea | |
Angus Logan | fc28845fb9 | |
Andrew Wilkinson | 14c7f6c9c8 | |
Simon Prickett | 14eabf360f | |
Simon Prickett | fe805a711a | |
Philip Howard | d93839d56a | |
Philip Howard | e1527c44d5 | |
Phil Howard | 4ad6df5cc3 | |
Phil Howard | 004c8de8eb | |
Philip Howard | 3639b46881 | |
Phil Howard | b744f78a46 | |
Philip Howard | 5bc85c0e6d | |
ZodiusInfuser | 6bd5a445ba | |
Irvin | 6306d5e753 | |
Irvin | f0bfc7c13b | |
Irvin | e1e467185a | |
Irvin | 7e65c15cfb | |
Irvin Makosa | 462724210c | |
Irvin Makosa | 0b0474e062 | |
Irvin Makosa | 90a2076b7b | |
Irvin Makosa | e14903dd27 | |
Irvin Makosa | f06d1035a4 | |
Hel Gibbons | d0c40af766 | |
Hel Gibbons | 439b97d96e | |
Irvin Makosa | 8bb5e17e65 | |
Irvin | b8cdff8f4f | |
Hel Gibbons | eb31f9b043 | |
Phil Howard | b368950f02 | |
Philip Howard | 51574f839d | |
Hel Gibbons | 03232bbeb5 | |
Phil Howard | 5ec6903f7f | |
helgibbons | c696cd5e2d | |
Phil Howard | 6db7a9a0a6 | |
Phil Howard | 1630ddbbb2 | |
Hel Gibbons | 25237c54ce | |
Alexander Wilde | 16c2dc0356 | |
Hel Gibbons | 8f5a94482b | |
thinkier | aa8b158ba3 | |
Hel Gibbons | 52df18e550 | |
Hel Gibbons | 1d1b521dfb | |
Hel Gibbons | 3786cbdfe6 | |
Hel Gibbons | 58cdc85b1f | |
helgibbons | 6b67f652c7 | |
helgibbons | 37638172ae | |
helgibbons | 3236503805 | |
helgibbons | ce24330842 | |
helgibbons | 130685eeeb | |
Hel Gibbons | 6afdfef45b | |
Angus Logan | 1e6e68356a | |
Angus Logan | d25c6953d0 | |
Angus Logan | 0a0b72701e | |
Hel Gibbons | 40f0554259 | |
Hel Gibbons | d9064f0162 | |
Hel Gibbons | dc1f000134 | |
Hel Gibbons | 32ae70d16d | |
Hel Gibbons | 8a6bb65d73 | |
LionsPhil | a0fe954b7c | |
Hel Gibbons | 951fe4d8b8 | |
Hel Gibbons | cbaf1fa27d | |
Hel Gibbons | 16f8f0ab05 | |
Hel Gibbons | d759522b08 | |
Hel Gibbons | 9307ea1360 | |
Philip Howard | 6fb35df544 | |
Phil Howard | b0d63ef777 | |
Philip Howard | d523eded0b | |
Phil Howard | 090ce9d2c6 | |
Philip Howard | 9d96d061e9 | |
Phil Howard | 70a1b26041 | |
Phil Howard | bff6bd023e | |
Phil Howard | 19c57ebb20 | |
Phil Howard | 94c5d74894 | |
ZodiusInfuser | 67152e32e5 | |
ZodiusInfuser | 7aa75e57a4 | |
ZodiusInfuser | 68f610184f | |
ZodiusInfuser | aabe789f21 | |
ZodiusInfuser | bd4238945d | |
ZodiusInfuser | ae252fbc6e | |
Philip Howard | d4609699ba | |
Philip Howard | 74064407e9 | |
Phil Howard | 0a2e099886 | |
Hel Gibbons | c8d3b6b7d1 | |
Hel Gibbons | 5aa227ff45 | |
Phil Howard | fba7b53c36 | |
Philip Howard | 00d1617947 | |
Phil Howard | 652de85f4d | |
Philip Howard | 8648196cc2 | |
Philip Howard | ec205fb045 | |
Mike Bell | 5b31e018ff | |
ZodiusInfuser | d00185d831 | |
ZodiusInfuser | 0120975b3c | |
ZodiusInfuser | 12e38c1157 | |
Philip Howard | 89699fd78f | |
Phil Howard | 05cad0c157 | |
ZodiusInfuser | 653090c89e | |
Philip Howard | 1c39da4997 | |
Philip Howard | 092fbe472f | |
Philip Howard | ecbd1e66de | |
Ray Bellis | bfb6490ec8 | |
Ray Bellis | 32dfdc6a20 | |
Ray Bellis | 67df015bfe | |
Hel Gibbons | 302d6ae0eb | |
Hel Gibbons | 1be888f397 | |
Philip Howard | 7491ced13b | |
Niko Kotilainen | 1dcad21ed2 | |
ZodiusInfuser | 8966cbf348 | |
Pete Favelle | a59aa35df5 | |
Philip Howard | b6499e71fc | |
ZodiusInfuser | 5619274d3d | |
ZodiusInfuser | 862806f357 | |
Phil Howard | 7951ef9668 | |
ZodiusInfuser | 4dadeb0d4d | |
Phil Howard | b30d9ca554 | |
ZodiusInfuser | e0a405739c | |
ZodiusInfuser | 9f925b5259 | |
Phil Howard | 8648597134 | |
ZodiusInfuser | e3f9f14dcf | |
ZodiusInfuser | 226e7507dd | |
ZodiusInfuser | 1cfae8b5f8 | |
ZodiusInfuser | 387df3bd12 | |
Phil Howard | 45a2e0f5b1 | |
Phil Howard | e90ae33a99 | |
ZodiusInfuser | d4d6cd1936 | |
Phil Howard | dd4347dac3 | |
ZodiusInfuser | 928c28b677 | |
ZodiusInfuser | 2d45ed6ace | |
ZodiusInfuser | 178afd1469 | |
ZodiusInfuser | 6464f44437 | |
ZodiusInfuser | cd83a51e8a | |
ZodiusInfuser | f353525090 | |
ZodiusInfuser | 7c11593f7c | |
ZodiusInfuser | e3c3692e31 | |
ZodiusInfuser | 15978e5ddc | |
ZodiusInfuser | 59d57a193b | |
Quitsoon | f6206876b7 | |
mutatrum | 7bdd85d677 |
|
@ -25,7 +25,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Compiler Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /home/runner/.ccache
|
||||
key: ccache-cmake-${{github.ref}}-${{matrix.board}}-${{github.sha}}
|
||||
|
@ -34,13 +34,13 @@ jobs:
|
|||
ccache-cmake-${{github.ref}}
|
||||
ccache-cmake
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
# Check out the Pico SDK
|
||||
- name: Checkout Pico SDK
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: raspberrypi/pico-sdk
|
||||
path: pico-sdk
|
||||
|
@ -48,7 +48,7 @@ jobs:
|
|||
|
||||
# Check out the Pico Extras
|
||||
- name: Checkout Pico Extras
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: raspberrypi/pico-extras
|
||||
path: pico-extras
|
||||
|
|
|
@ -7,70 +7,21 @@ on:
|
|||
types: [created]
|
||||
|
||||
env:
|
||||
MICROPYTHON_VERSION: v1.20.0
|
||||
MICROPYTHON_VERSION: v1.22.2
|
||||
|
||||
jobs:
|
||||
deps:
|
||||
runs-on: ubuntu-20.04
|
||||
name: Dependencies
|
||||
steps:
|
||||
- name: Workspace Cache
|
||||
id: cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{runner.workspace}}
|
||||
key: workspace-micropython-${{env.MICROPYTHON_VERSION}}-with-libs
|
||||
restore-keys: |
|
||||
workspace-micropython-${{env.MICROPYTHON_VERSION}}-with-libs
|
||||
|
||||
# Check out MicroPython
|
||||
- name: Checkout MicroPython
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: micropython/micropython
|
||||
ref: ${{env.MICROPYTHON_VERSION}}
|
||||
submodules: false # MicroPython submodules are hideously broken
|
||||
path: micropython
|
||||
|
||||
# Check out MicroPython Libs
|
||||
- name: Checkout MicroPython Libs
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: micropython/micropython-lib
|
||||
path: micropython-lib
|
||||
|
||||
- name: Fetch base MicroPython submodules
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: micropython
|
||||
run: git submodule update --init
|
||||
|
||||
- name: Fetch Pico SDK submodules
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: micropython/lib/pico-sdk
|
||||
run: git submodule update --init
|
||||
|
||||
- name: Build mpy-cross
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
working-directory: micropython/mpy-cross
|
||||
run: make
|
||||
|
||||
build:
|
||||
needs: deps
|
||||
name: ${{matrix.name}} (${{matrix.board}})
|
||||
name: ${{ matrix.name }} (${{ matrix.board }})
|
||||
runs-on: ubuntu-20.04
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: pico
|
||||
board: PICO
|
||||
board: RPI_PICO
|
||||
- name: picow
|
||||
board: PICO_W
|
||||
- name: tiny2040
|
||||
board: RPI_PICO_W
|
||||
- name: tiny2040_8mb
|
||||
board: PIMORONI_TINY2040
|
||||
- name: picolipo_4mb
|
||||
board: PIMORONI_PICOLIPO_4MB
|
||||
|
@ -78,114 +29,113 @@ jobs:
|
|||
board: PIMORONI_PICOLIPO_16MB
|
||||
- name: tufty2040
|
||||
board: PIMORONI_TUFTY2040
|
||||
local_board_dir: true
|
||||
- name: enviro
|
||||
board: PICO_W_ENVIRO
|
||||
local_board_dir: true
|
||||
patch: true
|
||||
- name: galactic_unicorn
|
||||
board: PICO_W
|
||||
board: RPI_PICO_W
|
||||
- name: cosmic_unicorn
|
||||
board: PICO_W
|
||||
board: RPI_PICO_W
|
||||
- name: stellar_unicorn
|
||||
board: RPI_PICO_W
|
||||
- name: inky_frame
|
||||
board: PICO_W_INKY
|
||||
local_board_dir: true
|
||||
patch: true
|
||||
|
||||
env:
|
||||
# MicroPython version will be contained in github.event.release.tag_name for releases
|
||||
RELEASE_FILE: pimoroni-${{matrix.name}}-${{github.event.release.tag_name || github.sha}}-micropython
|
||||
MICROPY_BOARD_DIR: "$GITHUB_WORKSPACE/pimoroni-pico-${{ github.sha }}/micropython/board/${{ matrix.BOARD }}"
|
||||
USER_C_MODULES: "$GITHUB_WORKSPACE/pimoroni-pico-${{ github.sha }}/micropython/modules/micropython-${{matrix.name}}.cmake"
|
||||
RELEASE_FILE: pimoroni-${{ matrix.name }}-${{ github.event.release.tag_name || github.sha }}-micropython
|
||||
PIMORONI_PICO_DIR: "${{ github.workspace }}/pimoroni-pico-${{ github.sha }}"
|
||||
MICROPY_BOARD_DIR: "${{ github.workspace }}/pimoroni-pico-${{ github.sha }}/micropython/board/${{ matrix.BOARD }}"
|
||||
USER_C_MODULES: "${{ github.workspace }}/pimoroni-pico-${{ github.sha }}/micropython/modules/micropython-${{ matrix.name }}.cmake"
|
||||
TAG_OR_SHA: ${{ github.event.release.tag_name || github.sha }}
|
||||
MICROPY_BOARD: ${{ matrix.board }}
|
||||
BOARD_NAME: ${{ matrix.name }}
|
||||
BUILD_TOOLS: pimoroni-pico-${{ github.sha }}/ci/micropython.sh
|
||||
|
||||
steps:
|
||||
- name: Compiler Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /home/runner/.ccache
|
||||
key: ccache-micropython-${{matrix.name}}-${{github.ref}}-${{github.sha}}
|
||||
key: ccache-micropython-${{ matrix.name }}-${{ github.ref }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
ccache-micropython-${{matrix.name}}-${{github.ref}}
|
||||
ccache-micropython-${{matrix.name}}-
|
||||
|
||||
- name: Workspace Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{runner.workspace}}
|
||||
key: workspace-micropython-${{env.MICROPYTHON_VERSION}}
|
||||
restore-keys: |
|
||||
workspace-micropython-${{env.MICROPYTHON_VERSION}}
|
||||
ccache-micropython-${{ matrix.name }}-${{ github.ref }}
|
||||
ccache-micropython-${{ matrix.name }}-
|
||||
|
||||
- name: Install Compiler & CCache
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt update && sudo apt install ccache gcc-arm-none-eabi
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
path: pimoroni-pico-${{ github.sha }}
|
||||
|
||||
- name: Set MicroPython Version Env Vars
|
||||
- name: Install Arm GNU Toolchain (arm-none-eabi-gcc)
|
||||
uses: carlosperate/arm-none-eabi-gcc-action@v1
|
||||
with:
|
||||
release: '9-2020-q2'
|
||||
|
||||
- name: Install CCache
|
||||
run: |
|
||||
source $BUILD_TOOLS
|
||||
apt_install_build_deps
|
||||
|
||||
- name: Checkout MicroPython & Submodules
|
||||
run: |
|
||||
source $BUILD_TOOLS
|
||||
micropython_clone
|
||||
|
||||
- name: "Py_Decl: Checkout py_decl"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: gadgetoid/py_decl
|
||||
ref: v0.0.1
|
||||
path: py_decl
|
||||
|
||||
- name: Build MPY Cross
|
||||
run: |
|
||||
source $BUILD_TOOLS
|
||||
micropython_build_mpy_cross
|
||||
|
||||
- name: "HACK: CMakeLists.txt Disable C++ Exceptions Patch"
|
||||
shell: bash
|
||||
run: |
|
||||
echo "MICROPY_GIT_TAG=$MICROPYTHON_VERSION, ${{matrix.name}} ${{github.event.release.tag_name || github.sha}}" >> $GITHUB_ENV
|
||||
echo "MICROPY_GIT_HASH=$MICROPYTHON_VERSION-${{github.event.release.tag_name || github.sha}}" >> $GITHUB_ENV
|
||||
|
||||
- name: "HACK: Revert Pico SDK Patch" # Avoid an already-patched MicroPython tree breaking our build
|
||||
shell: bash
|
||||
working-directory: micropython/lib/pico-sdk
|
||||
run: |
|
||||
git checkout .
|
||||
source $BUILD_TOOLS
|
||||
hack_patch_micropython_disable_exceptions
|
||||
|
||||
- name: "HACK: Pico SDK Patch"
|
||||
if: matrix.patch == true
|
||||
shell: bash
|
||||
working-directory: micropython
|
||||
run: |
|
||||
$GITHUB_WORKSPACE/pimoroni-pico-${{ github.sha }}/micropython/board/pico-sdk-patch.sh ${{matrix.board}}
|
||||
source $BUILD_TOOLS
|
||||
hack_patch_pico_sdk
|
||||
|
||||
- name: Configure MicroPython (Local Board Dir)
|
||||
if: matrix.local_board_dir == true
|
||||
- name: Configure MicroPython
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
cmake -S . -B build-${{matrix.name}} -DPICO_BUILD_DOCS=0 -DUSER_C_MODULES=${{env.USER_C_MODULES}} -DMICROPY_BOARD_DIR=${{env.MICROPY_BOARD_DIR}} -DMICROPY_C_HEAP_SIZE=4096 -DMICROPY_BOARD=${{matrix.board}} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Configure MicroPython (Upstream Board Dir)
|
||||
if: matrix.local_board_dir != true
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
cmake -S . -B build-${{matrix.name}} -DPICO_BUILD_DOCS=0 -DUSER_C_MODULES=${{env.USER_C_MODULES}} -DMICROPY_C_HEAP_SIZE=4096 -DMICROPY_BOARD=${{matrix.board}} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build MicroPython # Multiple simultaneous jobs trigger https://github.com/pimoroni/pimoroni-pico/issues/761
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2
|
||||
run: |
|
||||
ccache --zero-stats || true
|
||||
cmake --build build-${{matrix.name}} -j 1
|
||||
ccache --show-stats || true
|
||||
source $BUILD_TOOLS
|
||||
micropython_version
|
||||
cmake_configure
|
||||
|
||||
- name: Rename .uf2 for artifact
|
||||
- name: Build MicroPython
|
||||
shell: bash
|
||||
working-directory: micropython/ports/rp2/build-${{matrix.name}}
|
||||
run: |
|
||||
cp firmware.uf2 $RELEASE_FILE.uf2
|
||||
source $BUILD_TOOLS
|
||||
cmake_build
|
||||
|
||||
- name: "Py_Decl: Verify UF2"
|
||||
shell: bash
|
||||
run: |
|
||||
python3 py_decl/py_decl.py --to-json --verify build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2
|
||||
|
||||
- name: Store .uf2 as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{env.RELEASE_FILE}}.uf2
|
||||
path: micropython/ports/rp2/build-${{matrix.name}}/${{env.RELEASE_FILE}}.uf2
|
||||
name: ${{ env.RELEASE_FILE }}.uf2
|
||||
path: build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2
|
||||
|
||||
- name: Upload .uf2
|
||||
if: github.event_name == 'release'
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
asset_path: micropython/ports/rp2/build-${{matrix.name}}/firmware.uf2
|
||||
upload_url: ${{github.event.release.upload_url}}
|
||||
asset_name: ${{env.RELEASE_FILE}}.uf2
|
||||
asset_path: build-${{ matrix.name }}/firmware.uf2
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
asset_name: ${{ env.RELEASE_FILE }}.uf2
|
||||
asset_content_type: application/octet-stream
|
||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
|||
name: Python Linting
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Python Deps
|
||||
run: python3 -m pip install flake8
|
||||
|
|
|
@ -10,6 +10,8 @@ set(CMAKE_CXX_STANDARD 17)
|
|||
# Initialize the SDK
|
||||
pico_sdk_init()
|
||||
|
||||
pico_find_compiler(PICO_COMPILER_LD ${PICO_GCC_TRIPLE}-ld)
|
||||
|
||||
function(add_resource target file)
|
||||
get_filename_component(NAME ${ARGV1} NAME_WE)
|
||||
set(FILENAME ${ARGV1})
|
||||
|
@ -21,7 +23,7 @@ function(add_resource target file)
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
|
||||
COMMAND arm-none-eabi-ld -r -b binary -o ${NAME}.o ${FILENAME}
|
||||
COMMAND ${PICO_COMPILER_LD} -r -b binary -o ${NAME}.o ${FILENAME}
|
||||
DEPENDS ${FILENAME}
|
||||
)
|
||||
|
||||
|
|
|
@ -44,6 +44,10 @@ You can find MicroPython examples for supported sensors, packs and bases in the
|
|||
|
||||
* [MicroPython Examples](micropython/examples)
|
||||
|
||||
You can also install MicroPython stubs into Visual Studio Code to give you auto-complete, see:
|
||||
|
||||
* [MicroPython Stubs](https://github.com/pimoroni/pimoroni-pico-stubs)
|
||||
|
||||
# C/C++
|
||||
|
||||
Advanced users that want to unleash the full power of Pico can use our C++ libraries. If you know what you're doing and want to build your own Pimoroni Pico project then start with the [Pimoroni Pico SDK Boilerplate](https://github.com/pimoroni/pico-boilerplate).
|
||||
|
@ -116,6 +120,7 @@ We also maintain a C++/CMake boilerplate with GitHub workflows configured for te
|
|||
* MICS6814 - Gas Sensor - https://shop.pimoroni.com/products/mics6814-gas-sensor-breakout
|
||||
* RGB Potentiometer - https://shop.pimoroni.com/products/rgb-potentiometer-breakout
|
||||
* RGB Encoder - https://shop.pimoroni.com/products/rgb-encoder-breakout
|
||||
* RGB Encoder Wheel - https://shop.pimoroni.com/products/rgb-encoder-wheel-breakout
|
||||
* IO Expander - https://shop.pimoroni.com/products/io-expander
|
||||
* RV3028 - Real-Time Clock (RTC) - https://shop.pimoroni.com/products/rv3028-real-time-clock-rtc-breakout
|
||||
* ST7735 - 0.96" LCD - https://shop.pimoroni.com/products/0-96-spi-colour-lcd-160x80-breakout
|
||||
|
@ -139,6 +144,7 @@ We also maintain a C++/CMake boilerplate with GitHub workflows configured for te
|
|||
* ICP10125 - High Accuracy Pressure / Altitude / Temperature Sensor - https://shop.pimoroni.com/products/icp10125-air-pressure-breakout
|
||||
* SCD41 CO2 Sensor (Carbon Dioxide / Temperature / Humidity) - https://shop.pimoroni.com/products/scd41-co2-sensor-breakout
|
||||
* VL53L5CX 8x8 Time of Flight Array Sensor - https://shop.pimoroni.com/products/vl53l5cx-time-of-flight-tof-sensor-breakout
|
||||
* RGB Encoder Wheel - https://shop.pimoroni.com/products/rgb-encoder-wheel-breakout
|
||||
|
||||
## Kits
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
export TERM=${TERM:="xterm-256color"}
|
||||
|
||||
function log_success {
|
||||
echo -e "$(tput setaf 2)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
function log_inform {
|
||||
echo -e "$(tput setaf 6)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
function log_warning {
|
||||
echo -e "$(tput setaf 1)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
function micropython_clone {
|
||||
log_inform "Using MicroPython $MICROPYTHON_VERSION"
|
||||
git clone https://github.com/micropython/micropython --depth=1 --branch=$MICROPYTHON_VERSION
|
||||
cd micropython
|
||||
git submodule update --init lib/pico-sdk
|
||||
git submodule update --init lib/cyw43-driver
|
||||
git submodule update --init lib/lwip
|
||||
git submodule update --init lib/mbedtls
|
||||
git submodule update --init lib/micropython-lib
|
||||
git submodule update --init lib/tinyusb
|
||||
git submodule update --init lib/btstack
|
||||
cd ../
|
||||
}
|
||||
|
||||
function micropython_build_mpy_cross {
|
||||
cd micropython/mpy-cross
|
||||
ccache --zero-stats || true
|
||||
CROSS_COMPILE="ccache " make
|
||||
ccache --show-stats || true
|
||||
cd ../../
|
||||
}
|
||||
|
||||
function apt_install_build_deps {
|
||||
sudo apt update && sudo apt install ccache
|
||||
}
|
||||
|
||||
function micropython_version {
|
||||
echo "MICROPY_GIT_TAG=$MICROPYTHON_VERSION, $BOARD_NAME $TAG_OR_SHA" >> $GITHUB_ENV
|
||||
echo "MICROPY_GIT_HASH=$MICROPYTHON_VERSION-$TAG_OR_SHA" >> $GITHUB_ENV
|
||||
}
|
||||
|
||||
function hack_patch_micropython_disable_exceptions {
|
||||
cd micropython
|
||||
git apply $PIMORONI_PICO_DIR/micropython/micropython_nano_specs.patch
|
||||
cd ../
|
||||
}
|
||||
|
||||
function hack_patch_pico_sdk {
|
||||
# pico-sdk-patch.sh will apply the patch if it exists
|
||||
cd micropython
|
||||
$PIMORONI_PICO_DIR/micropython/board/pico-sdk-patch.sh $MICROPY_BOARD
|
||||
cd ../
|
||||
}
|
||||
|
||||
function cmake_configure {
|
||||
cmake -S micropython/ports/rp2 -B build-$BOARD_NAME \
|
||||
-DPICO_BUILD_DOCS=0 \
|
||||
-DUSER_C_MODULES=$USER_C_MODULES \
|
||||
-DMICROPY_BOARD_DIR=$MICROPY_BOARD_DIR \
|
||||
-DMICROPY_BOARD=$MICROPY_BOARD \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
}
|
||||
|
||||
function cmake_build {
|
||||
ccache --zero-stats || true
|
||||
cmake --build build-$BOARD_NAME -j 2
|
||||
ccache --show-stats || true
|
||||
cd build-$BOARD_NAME
|
||||
cp firmware.uf2 $RELEASE_FILE.uf2
|
||||
}
|
Binary file not shown.
|
@ -78,7 +78,7 @@ namespace pimoroni {
|
|||
return to_ms_since_boot(get_absolute_time());
|
||||
}
|
||||
|
||||
constexpr uint8_t GAMMA_8BIT[256] = {
|
||||
inline constexpr uint8_t GAMMA_8BIT[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
|
||||
2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5,
|
||||
|
@ -98,7 +98,7 @@ namespace pimoroni {
|
|||
|
||||
/* Moved from pico_unicorn.cpp
|
||||
v = (uint16_t)(powf((float)(n) / 255.0f, 2.2) * 16383.0f + 0.5f) */
|
||||
constexpr uint16_t GAMMA_14BIT[256] = {
|
||||
inline constexpr uint16_t GAMMA_14BIT[256] = {
|
||||
0, 0, 0, 1, 2, 3, 4, 6, 8, 10, 13, 16, 20, 23, 28, 32,
|
||||
37, 42, 48, 54, 61, 67, 75, 82, 90, 99, 108, 117, 127, 137, 148, 159,
|
||||
170, 182, 195, 207, 221, 234, 249, 263, 278, 294, 310, 326, 343, 361, 379, 397,
|
||||
|
|
|
@ -41,6 +41,6 @@ add_subdirectory(pms5003)
|
|||
add_subdirectory(sh1107)
|
||||
add_subdirectory(st7567)
|
||||
add_subdirectory(psram_display)
|
||||
add_subdirectory(inky73)
|
||||
add_subdirectory(shiftregister)
|
||||
add_subdirectory(mlx90640)
|
||||
add_subdirectory(inky73)
|
||||
add_subdirectory(mlx90640)
|
|
@ -11,7 +11,7 @@ namespace pimoroni {
|
|||
public:
|
||||
Analog(uint pin, float amplifier_gain = 1.0f, float resistor = 0.0f, float offset = 0.0f) :
|
||||
pin(pin), amplifier_gain(amplifier_gain), resistor(resistor), offset(offset) {
|
||||
adc_init();
|
||||
if (!(adc_hw->cs & ADC_CS_EN_BITS)) adc_init();
|
||||
|
||||
//Make sure GPIO is high-impedance, no pullups etc
|
||||
adc_gpio_init(pin);
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
#include "hardware/irq.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "encoder.hpp"
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "encoder.pio.h"
|
||||
#endif
|
||||
|
||||
#define LAST_STATE(state) ((state) & 0b0011)
|
||||
#define CURR_STATE(state) (((state) & 0b1100) >> 2)
|
||||
|
|
|
@ -113,12 +113,6 @@ void Hub75::FM6126A_setup() {
|
|||
|
||||
void Hub75::start(irq_handler_t handler) {
|
||||
if(handler) {
|
||||
dma_channel = 0;
|
||||
|
||||
// Try as I might, I can't seem to coax MicroPython into leaving PIO in a known state upon soft reset
|
||||
// check for claimed PIO and prepare a clean slate.
|
||||
stop(handler);
|
||||
|
||||
if (panel_type == PANEL_FM6126A) {
|
||||
FM6126A_setup();
|
||||
}
|
||||
|
@ -139,7 +133,7 @@ void Hub75::start(irq_handler_t handler) {
|
|||
// Prevent flicker in Python caused by the smaller dataset just blasting through the PIO too quickly
|
||||
pio_sm_set_clkdiv(pio, sm_data, width <= 32 ? 2.0f : 1.0f);
|
||||
|
||||
dma_channel_claim(dma_channel);
|
||||
dma_channel = dma_claim_unused_channel(true);
|
||||
dma_channel_config config = dma_channel_get_default_config(dma_channel);
|
||||
channel_config_set_transfer_data_size(&config, DMA_SIZE_32);
|
||||
channel_config_set_bswap(&config, false);
|
||||
|
@ -148,15 +142,13 @@ void Hub75::start(irq_handler_t handler) {
|
|||
|
||||
|
||||
// Same handler for both DMA channels
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, handler);
|
||||
irq_set_exclusive_handler(DMA_IRQ_1, handler);
|
||||
irq_add_shared_handler(DMA_IRQ_0, handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
|
||||
|
||||
dma_channel_set_irq0_enabled(dma_channel, true);
|
||||
|
||||
irq_set_enabled(pio_get_dreq(pio, sm_data, true), true);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
|
||||
|
||||
row = 0;
|
||||
bit = 0;
|
||||
|
||||
|
@ -169,10 +161,9 @@ void Hub75::start(irq_handler_t handler) {
|
|||
void Hub75::stop(irq_handler_t handler) {
|
||||
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
irq_set_enabled(DMA_IRQ_1, false);
|
||||
irq_set_enabled(pio_get_dreq(pio, sm_data, true), false);
|
||||
|
||||
if(dma_channel_is_claimed(dma_channel)) {
|
||||
if(dma_channel != -1 && dma_channel_is_claimed(dma_channel)) {
|
||||
dma_channel_set_irq0_enabled(dma_channel, false);
|
||||
irq_remove_handler(DMA_IRQ_0, handler);
|
||||
//dma_channel_wait_for_finish_blocking(dma_channel);
|
||||
|
@ -184,17 +175,21 @@ void Hub75::stop(irq_handler_t handler) {
|
|||
if(pio_sm_is_claimed(pio, sm_data)) {
|
||||
pio_sm_set_enabled(pio, sm_data, false);
|
||||
pio_sm_drain_tx_fifo(pio, sm_data);
|
||||
pio_remove_program(pio, &hub75_data_rgb888_program, data_prog_offs);
|
||||
pio_sm_unclaim(pio, sm_data);
|
||||
}
|
||||
|
||||
if(pio_sm_is_claimed(pio, sm_row)) {
|
||||
pio_sm_set_enabled(pio, sm_row, false);
|
||||
pio_sm_drain_tx_fifo(pio, sm_row);
|
||||
if (inverted_stb) {
|
||||
pio_remove_program(pio, &hub75_row_inverted_program, row_prog_offs);
|
||||
} else {
|
||||
pio_remove_program(pio, &hub75_row_program, row_prog_offs);
|
||||
}
|
||||
pio_sm_unclaim(pio, sm_row);
|
||||
}
|
||||
|
||||
pio_clear_instruction_memory(pio);
|
||||
|
||||
// Make sure the GPIO is in a known good state
|
||||
// since we don't know what the PIO might have done with it
|
||||
gpio_put_masked(0b111111 << pin_r0, 0);
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
#include "hardware/dma.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "hub75.pio.h"
|
||||
#endif
|
||||
|
||||
namespace pimoroni {
|
||||
const uint DATA_BASE_PIN = 0;
|
||||
|
@ -70,7 +73,7 @@ class Hub75 {
|
|||
Pixel background = 0;
|
||||
|
||||
// DMA & PIO
|
||||
uint dma_channel = 0;
|
||||
int dma_channel = -1;
|
||||
uint bit = 0;
|
||||
uint row = 0;
|
||||
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
#include "hardware/pio.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/irq.h"
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "hub75.pio.h"
|
||||
#endif
|
||||
|
||||
const uint DATA_BASE_PIN = 0;
|
||||
const uint DATA_N_PINS = 6;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET shiftregister)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../shiftregister/shiftregister.cmake)
|
||||
endif()
|
||||
|
||||
set(DRIVER_NAME inky73)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -47,8 +47,9 @@ namespace pimoroni {
|
|||
return !(sr.read() & 128);
|
||||
}
|
||||
|
||||
void Inky73::busy_wait() {
|
||||
while(is_busy()) {
|
||||
void Inky73::busy_wait(uint timeout_ms) {
|
||||
absolute_time_t timeout = make_timeout_time_ms(timeout_ms);
|
||||
while(is_busy() && !time_reached(timeout)) {
|
||||
tight_loop_contents();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace pimoroni {
|
|||
// Methods
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
void busy_wait();
|
||||
void busy_wait(uint timeout_ms=45000);
|
||||
void reset();
|
||||
void power_off();
|
||||
|
||||
|
|
|
@ -320,8 +320,21 @@ namespace pimoroni {
|
|||
Pin::adc(1, 7, 0)}
|
||||
{}
|
||||
|
||||
bool IOExpander::init(bool skipChipIdCheck) {
|
||||
bool succeeded = true;
|
||||
bool IOExpander::init(bool skipChipIdCheck, bool perform_reset) {
|
||||
if(!skipChipIdCheck) {
|
||||
uint16_t chip_id = get_chip_id();
|
||||
if(chip_id != CHIP_ID) {
|
||||
if(debug) {
|
||||
printf("Chip ID invalid: %04x expected: %04x\n", chip_id, CHIP_ID);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the chip if requested, to put it into a known state
|
||||
if(perform_reset && !reset()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(interrupt != PIN_UNUSED) {
|
||||
gpio_set_function(interrupt, GPIO_FUNC_SIO);
|
||||
|
@ -331,17 +344,36 @@ namespace pimoroni {
|
|||
enable_interrupt_out(true);
|
||||
}
|
||||
|
||||
if(!skipChipIdCheck) {
|
||||
uint16_t chip_id = get_chip_id();
|
||||
if(chip_id != CHIP_ID) {
|
||||
if(debug) {
|
||||
printf("Chip ID invalid: %04x expected: %04x\n", chip_id, CHIP_ID);
|
||||
}
|
||||
succeeded = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t IOExpander::check_reset() {
|
||||
uint8_t user_flash_reg = reg::USER_FLASH;
|
||||
uint8_t value;
|
||||
if(i2c_write_blocking(i2c->get_i2c(), address, &user_flash_reg, 1, false) == PICO_ERROR_GENERIC) {
|
||||
return 0x00;
|
||||
}
|
||||
if(i2c_read_blocking(i2c->get_i2c(), address, (uint8_t *)&value, sizeof(uint8_t), false) == PICO_ERROR_GENERIC) {
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool IOExpander::reset() {
|
||||
uint32_t start_time = millis();
|
||||
set_bits(reg::CTRL, ctrl_mask::RESET);
|
||||
// Wait for a register to read its initialised value
|
||||
while(check_reset() != 0x78) {
|
||||
sleep_ms(1);
|
||||
if(millis() - start_time >= RESET_TIMEOUT_MS) {
|
||||
if(debug)
|
||||
printf("Timed out waiting for Reset!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return succeeded;
|
||||
return true;
|
||||
}
|
||||
|
||||
i2c_inst_t* IOExpander::get_i2c() const {
|
||||
|
@ -370,7 +402,7 @@ namespace pimoroni {
|
|||
|
||||
void IOExpander::set_address(uint8_t address) {
|
||||
set_bit(reg::CTRL, 4);
|
||||
i2c->reg_write_uint8(address, reg::ADDR, address);
|
||||
i2c->reg_write_uint8(this->address, reg::ADDR, address);
|
||||
this->address = address;
|
||||
sleep_ms(250); //TODO Handle addr change IOError better
|
||||
//wait_for_flash()
|
||||
|
@ -491,13 +523,35 @@ namespace pimoroni {
|
|||
return divider_good;
|
||||
}
|
||||
|
||||
void IOExpander::set_pwm_period(uint16_t value, bool load) {
|
||||
void IOExpander::set_pwm_period(uint16_t value, bool load, bool wait_for_load) {
|
||||
value &= 0xffff;
|
||||
i2c->reg_write_uint8(address, reg::PWMPL, (uint8_t)(value & 0xff));
|
||||
i2c->reg_write_uint8(address, reg::PWMPH, (uint8_t)(value >> 8));
|
||||
|
||||
if(load)
|
||||
pwm_load();
|
||||
pwm_load(wait_for_load);
|
||||
}
|
||||
|
||||
uint16_t IOExpander::set_pwm_frequency(float frequency, bool load, bool wait_for_load) {
|
||||
uint32_t period = (uint32_t)(CLOCK_FREQ / frequency);
|
||||
if (period / 128 > MAX_PERIOD) {
|
||||
return MAX_PERIOD;
|
||||
}
|
||||
if (period < 2) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
uint8_t divider = 1;
|
||||
while ((period > MAX_PERIOD) && (divider < MAX_DIVIDER)) {
|
||||
period >>= 1;
|
||||
divider <<= 1;
|
||||
}
|
||||
|
||||
period = MIN(period, MAX_PERIOD); // Should be unnecessary because of earlier raised errors, but kept in case
|
||||
set_pwm_control(divider);
|
||||
set_pwm_period((uint16_t)(period - 1), load, wait_for_load);
|
||||
|
||||
return (uint16_t)period;
|
||||
}
|
||||
|
||||
uint8_t IOExpander::get_mode(uint8_t pin) {
|
||||
|
@ -669,7 +723,7 @@ namespace pimoroni {
|
|||
}
|
||||
}
|
||||
|
||||
void IOExpander::output(uint8_t pin, uint16_t value, bool load) {
|
||||
void IOExpander::output(uint8_t pin, uint16_t value, bool load, bool wait_for_load) {
|
||||
if(pin < 1 || pin > NUM_PINS) {
|
||||
printf("Pin should be in range 1-14.");
|
||||
return;
|
||||
|
@ -685,7 +739,7 @@ namespace pimoroni {
|
|||
i2c->reg_write_uint8(address, io_pin.reg_pwml, (uint8_t)(value & 0xff));
|
||||
i2c->reg_write_uint8(address, io_pin.reg_pwmh, (uint8_t)(value >> 8));
|
||||
if(load)
|
||||
pwm_load();
|
||||
pwm_load(wait_for_load);
|
||||
}
|
||||
else {
|
||||
if(value == LOW) {
|
||||
|
|
|
@ -26,6 +26,8 @@ namespace pimoroni {
|
|||
static const uint8_t PIN_MODE_PWM = 0b00101; // PWM, Output, Push-Pull mode
|
||||
static const uint8_t PIN_MODE_ADC = 0b01010; // ADC, Input-only (high-impedance)
|
||||
|
||||
static const uint32_t RESET_TIMEOUT_MS = 1000;
|
||||
|
||||
public:
|
||||
static const uint8_t DEFAULT_I2C_ADDRESS = 0x18;
|
||||
|
||||
|
@ -45,6 +47,10 @@ namespace pimoroni {
|
|||
static const uint16_t LOW = 0;
|
||||
static const uint16_t HIGH = 1;
|
||||
|
||||
static const uint32_t CLOCK_FREQ = 24000000;
|
||||
static const uint32_t MAX_PERIOD = (1 << 16) - 1;
|
||||
static const uint32_t MAX_DIVIDER = (1 << 7);
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// Subclasses
|
||||
|
@ -171,7 +177,12 @@ namespace pimoroni {
|
|||
// Methods
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
bool init(bool skip_chip_id_check = false);
|
||||
bool init(bool skip_chip_id_check = false, bool perform_reset = false);
|
||||
|
||||
private:
|
||||
uint8_t check_reset();
|
||||
public:
|
||||
bool reset();
|
||||
|
||||
// For print access in micropython
|
||||
i2c_inst_t* get_i2c() const;
|
||||
|
@ -198,7 +209,8 @@ namespace pimoroni {
|
|||
void pwm_clear(bool wait_for_clear = true);
|
||||
bool pwm_clearing();
|
||||
bool set_pwm_control(uint8_t divider);
|
||||
void set_pwm_period(uint16_t value, bool load = true);
|
||||
void set_pwm_period(uint16_t value, bool load = true, bool wait_for_load = true);
|
||||
uint16_t set_pwm_frequency(float frequency, bool load = true, bool wait_for_load = true);
|
||||
|
||||
uint8_t get_mode(uint8_t pin);
|
||||
void set_mode(uint8_t pin, uint8_t mode, bool schmitt_trigger = false, bool invert = false);
|
||||
|
@ -206,7 +218,7 @@ namespace pimoroni {
|
|||
int16_t input(uint8_t pin, uint32_t adc_timeout = 1);
|
||||
float input_as_voltage(uint8_t pin, uint32_t adc_timeout = 1);
|
||||
|
||||
void output(uint8_t pin, uint16_t value, bool load = true);
|
||||
void output(uint8_t pin, uint16_t value, bool load = true, bool wait_for_load = true);
|
||||
|
||||
void setup_rotary_encoder(uint8_t channel, uint8_t pin_a, uint8_t pin_b, uint8_t pin_c = 0, bool count_microsteps = false);
|
||||
int16_t read_rotary_encoder(uint8_t channel);
|
||||
|
|
|
@ -64,8 +64,8 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
void LTP305::set_character(uint8_t x, uint16_t ch) {
|
||||
uint8_t *data = nullptr;
|
||||
for(auto c : dotfont) {
|
||||
const uint8_t *data = nullptr;
|
||||
for(const auto& c : dotfont) {
|
||||
if(c.code == ch) {
|
||||
data = &c.data[0];
|
||||
break;
|
||||
|
@ -128,4 +128,4 @@ namespace pimoroni {
|
|||
i2c->reg_write_uint8(address, CMD_BRIGHTNESS, brightness);
|
||||
i2c->reg_write_uint8(address, CMD_UPDATE, 0x01);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET pwm)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pwm/pwm.cmake)
|
||||
endif()
|
||||
|
||||
set(DRIVER_NAME motor)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET pwm_cluster)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pwm/pwm_cluster.cmake)
|
||||
endif()
|
||||
|
||||
set(DRIVER_NAME motor_cluster)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -14,7 +14,9 @@ found here: https://github.com/raspberrypi/pico-examples/tree/master/pio/apa102
|
|||
#include <math.h>
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "apa102.pio.h"
|
||||
#endif
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/pio.h"
|
||||
|
|
|
@ -14,7 +14,9 @@ found here: https://github.com/raspberrypi/pico-examples/tree/master/pio/ws2812
|
|||
#include <math.h>
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "ws2812.pio.h"
|
||||
#endif
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/pio.h"
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#include "pwm_cluster.hpp"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "pwm_cluster.pio.h"
|
||||
#endif
|
||||
|
||||
// Uncomment the below line to enable debugging
|
||||
//#define DEBUG_MULTI_PWM
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
#define _PIO_SPI_H
|
||||
|
||||
#include "hardware/pio.h"
|
||||
#ifndef NO_QSTR
|
||||
#include "spi.pio.h"
|
||||
#endif
|
||||
|
||||
typedef struct pio_spi_inst {
|
||||
PIO pio;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET pwm)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pwm/pwm.cmake)
|
||||
endif()
|
||||
|
||||
set(DRIVER_NAME servo)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET pwm_cluster)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pwm/pwm_cluster.cmake)
|
||||
endif()
|
||||
|
||||
set(DRIVER_NAME servo_cluster)
|
||||
add_library(${DRIVER_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -89,11 +89,19 @@ namespace pimoroni {
|
|||
if(width == 320 && height == 240) {
|
||||
command(reg::GCTRL, 1, "\x35");
|
||||
command(reg::VCOMS, 1, "\x1f");
|
||||
command(0xd6, 1, "\xa1"); // ???
|
||||
command(reg::GMCTRP1, 14, "\xD0\x08\x11\x08\x0C\x15\x39\x33\x50\x36\x13\x14\x29\x2D");
|
||||
command(reg::GMCTRN1, 14, "\xD0\x08\x10\x08\x06\x06\x39\x44\x51\x0B\x16\x14\x2F\x31");
|
||||
}
|
||||
|
||||
if(width == 240 && height == 135) { // Pico Display Pack (1.14" 240x135)
|
||||
command(reg::VRHS, 1, "\x00"); // VRH Voltage setting
|
||||
command(reg::GCTRL, 1, "\x75"); // VGH and VGL voltages
|
||||
command(reg::VCOMS, 1, "\x3D"); // VCOM voltage
|
||||
command(0xd6, 1, "\xa1"); // ???
|
||||
command(reg::GMCTRP1, 14, "\x70\x04\x08\x09\x09\x05\x2A\x33\x41\x07\x13\x13\x29\x2f");
|
||||
command(reg::GMCTRN1, 14, "\x70\x03\x09\x0A\x09\x06\x2B\x34\x41\x07\x12\x14\x28\x2E");
|
||||
}
|
||||
|
||||
command(reg::INVON); // set inversion mode
|
||||
command(reg::SLPOUT); // leave sleep mode
|
||||
command(reg::DISPON); // turn display on
|
||||
|
@ -125,8 +133,6 @@ namespace pimoroni {
|
|||
|
||||
void ST7789::configure_display(Rotation rotate) {
|
||||
|
||||
bool rotate180 = rotate == ROTATE_180 || rotate == ROTATE_90;
|
||||
|
||||
if(rotate == ROTATE_90 || rotate == ROTATE_270) {
|
||||
std::swap(width, height);
|
||||
}
|
||||
|
@ -177,20 +183,30 @@ namespace pimoroni {
|
|||
// Pico Display
|
||||
if(width == 240 && height == 135) {
|
||||
caset[0] = 40; // 240 cols
|
||||
caset[1] = 279;
|
||||
raset[0] = 53; // 135 rows
|
||||
raset[1] = 187;
|
||||
madctl = rotate180 ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
||||
caset[1] = 40 + width - 1;
|
||||
raset[0] = 52; // 135 rows
|
||||
raset[1] = 52 + height - 1;
|
||||
if (rotate == ROTATE_0) {
|
||||
raset[0] += 1;
|
||||
raset[1] += 1;
|
||||
}
|
||||
madctl = rotate == ROTATE_180 ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
||||
madctl |= MADCTL::SWAP_XY | MADCTL::SCAN_ORDER;
|
||||
}
|
||||
|
||||
// Pico Display at 90 degree rotation
|
||||
if(width == 135 && height == 240) {
|
||||
caset[0] = 52; // 135 cols
|
||||
caset[1] = 186;
|
||||
caset[1] = 52 + width - 1;
|
||||
raset[0] = 40; // 240 rows
|
||||
raset[1] = 279;
|
||||
madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
||||
raset[1] = 40 + height - 1;
|
||||
madctl = 0;
|
||||
if (rotate == ROTATE_90) {
|
||||
caset[0] += 1;
|
||||
caset[1] += 1;
|
||||
madctl = MADCTL::COL_ORDER | MADCTL::ROW_ORDER;
|
||||
}
|
||||
madctl = rotate == ROTATE_90 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
||||
}
|
||||
|
||||
// Pico Display 2.0
|
||||
|
@ -199,7 +215,7 @@ namespace pimoroni {
|
|||
caset[1] = 319;
|
||||
raset[0] = 0;
|
||||
raset[1] = 239;
|
||||
madctl = rotate180 ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
||||
madctl = (rotate == ROTATE_180 || rotate == ROTATE_90) ? MADCTL::ROW_ORDER : MADCTL::COL_ORDER;
|
||||
madctl |= MADCTL::SWAP_XY | MADCTL::SCAN_ORDER;
|
||||
}
|
||||
|
||||
|
@ -209,7 +225,7 @@ namespace pimoroni {
|
|||
caset[1] = 239;
|
||||
raset[0] = 0;
|
||||
raset[1] = 319;
|
||||
madctl = rotate180 ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
||||
madctl = (rotate == ROTATE_180 || rotate == ROTATE_90) ? (MADCTL::COL_ORDER | MADCTL::ROW_ORDER) : 0;
|
||||
}
|
||||
|
||||
// Byte swap the 16bit rows/cols values
|
||||
|
@ -231,29 +247,13 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
void ST7789::write_blocking_parallel(const uint8_t *src, size_t len) {
|
||||
const uint8_t *p = src;
|
||||
while(len--) {
|
||||
// Does not byte align correctly
|
||||
//pio_sm_put_blocking(parallel_pio, parallel_sm, *p);
|
||||
while (pio_sm_is_tx_fifo_full(parallel_pio, parallel_sm))
|
||||
;
|
||||
*(volatile uint8_t*)¶llel_pio->txf[parallel_sm] = *p;
|
||||
p++;
|
||||
}
|
||||
write_blocking_dma(src, len);
|
||||
dma_channel_wait_for_finish_blocking(st_dma);
|
||||
|
||||
uint32_t sm_stall_mask = 1u << (parallel_sm + PIO_FDEBUG_TXSTALL_LSB);
|
||||
parallel_pio->fdebug = sm_stall_mask;
|
||||
while (!(parallel_pio->fdebug & sm_stall_mask))
|
||||
;
|
||||
/*uint32_t mask = 0xff << d0;
|
||||
while(len--) {
|
||||
gpio_put(wr_sck, false);
|
||||
uint8_t v = *src++;
|
||||
gpio_put_masked(mask, v << d0);
|
||||
//asm("nop;");
|
||||
gpio_put(wr_sck, true);
|
||||
asm("nop;");
|
||||
}*/
|
||||
// This may cause a race between PIO and the
|
||||
// subsequent chipselect deassert for the last pixel
|
||||
while(!pio_sm_is_tx_fifo_empty(parallel_pio, parallel_sm))
|
||||
;
|
||||
}
|
||||
|
||||
void ST7789::command(uint8_t command, size_t len, const char *data) {
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "st7789_parallel.pio.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ namespace pimoroni {
|
|||
i2c->read_blocking(address, (uint8_t *)&value, 4, false);
|
||||
|
||||
// TODO do we need to bswap this return value?
|
||||
return __bswap32(value);
|
||||
return __builtin_bswap32(value);
|
||||
}
|
||||
|
||||
// set distance mode to Short, Medium, or Long
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
add_subdirectory(breakout_dotmatrix)
|
||||
add_subdirectory(breakout_encoder)
|
||||
add_subdirectory(breakout_encoder_wheel)
|
||||
add_subdirectory(breakout_ioexpander)
|
||||
add_subdirectory(breakout_ltr559)
|
||||
add_subdirectory(breakout_colourlcd160x80)
|
||||
|
@ -60,3 +61,4 @@ add_subdirectory(encoder)
|
|||
add_subdirectory(galactic_unicorn)
|
||||
add_subdirectory(gfx_pack)
|
||||
add_subdirectory(cosmic_unicorn)
|
||||
add_subdirectory(stellar_unicorn)
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
add_subdirectory(buttons)
|
||||
add_subdirectory(chase_game)
|
||||
add_subdirectory(clock)
|
||||
add_subdirectory(colour_picker)
|
||||
add_subdirectory(encoder)
|
||||
add_subdirectory(gpio_pwm)
|
||||
add_subdirectory(interrupt)
|
||||
add_subdirectory(led_rainbow)
|
||||
add_subdirectory(stop_watch)
|
|
@ -0,0 +1,77 @@
|
|||
# RGB Encoder Wheel Breakout Examples (C++) <!-- omit in toc -->
|
||||
|
||||
- [Function Examples](#function-examples)
|
||||
- [Buttons](#buttons)
|
||||
- [Encoder](#encoder)
|
||||
- [Interrupt](#interrupt)
|
||||
- [LED Examples](#led-examples)
|
||||
- [LED Rainbow](#led-rainbow)
|
||||
- [Clock](#clock)
|
||||
- [Interactive Examples](#interactive-examples)
|
||||
- [Colour Picker](#colour-picker)
|
||||
- [Stop Watch](#stop-watch)
|
||||
- [Chase Game](#chase-game)
|
||||
- [GPIO Examples](#gpio-examples)
|
||||
- [GPIO PWM](#gpio-pwm)
|
||||
|
||||
|
||||
## Function Examples
|
||||
|
||||
### Buttons
|
||||
[buttons/buttons.cpp](buttons/buttons.cpp)
|
||||
|
||||
A demonstration of reading the 5 buttons on Encoder Wheel.
|
||||
|
||||
|
||||
### Encoder
|
||||
[encoder/encoder.cpp](encoder/encoder.cpp)
|
||||
|
||||
A demonstration of reading the rotary dial of the Encoder Wheel breakout.
|
||||
|
||||
|
||||
### Interrupt
|
||||
[interrupt/interrupt.cpp](interrupt/interrupt.cpp)
|
||||
|
||||
How to read the buttons and rotary dial of the Encoder Wheel breakout, only when an interrupt occurs.
|
||||
|
||||
|
||||
## LED Examples
|
||||
|
||||
### LED Rainbow
|
||||
[led_rainbow/led_rainbow.cpp](led_rainbow/led_rainbow.cpp)
|
||||
|
||||
Displays a rotating rainbow pattern on Encoder Wheel's LED ring.
|
||||
|
||||
|
||||
### Clock
|
||||
[clock/clock.cpp](clock/clock.cpp)
|
||||
|
||||
Displays a 12 hour clock on Encoder Wheel's LED ring, getting time from the system.
|
||||
|
||||
|
||||
## Interactive Examples
|
||||
|
||||
### Colour Picker
|
||||
[colour_picker/colour_picker.cpp](colour_picker/colour_picker.cpp)
|
||||
|
||||
Create a colour wheel on the Encoder Wheel's LED ring, and use all functions of the wheel to interact with it.
|
||||
|
||||
|
||||
### Stop Watch
|
||||
[stop_watch/stop_watch.cpp](stop_watch/stop_watch.cpp)
|
||||
|
||||
Display a circular stop-watch on the Encoder Wheel's LED ring.
|
||||
|
||||
|
||||
### Chase Game
|
||||
[chase_game/chase_game.cpp](chase_game/chase_game.cpp)
|
||||
|
||||
A simple alignment game. Use Encoder Wheel's rotary dial to align the coloured band to the white goal. The closer to the goal, the greener your coloured band will be. When you reach the goal, the goal will move to a new random position.
|
||||
|
||||
|
||||
## GPIO Examples
|
||||
|
||||
### GPIO PWM
|
||||
[gpio_pwm/gpio_pwm.cpp](gpio_pwm/gpio_pwm.cpp)
|
||||
|
||||
Output a sine wave PWM sequence on the Encoder Wheel's side GPIO pins.
|
|
@ -0,0 +1,13 @@
|
|||
set(OUTPUT_NAME encoderwheel_buttons)
|
||||
add_executable(${OUTPUT_NAME} buttons.cpp)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${OUTPUT_NAME}
|
||||
pico_stdlib
|
||||
breakout_encoder_wheel
|
||||
)
|
||||
|
||||
# enable usb output
|
||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||
|
||||
pico_add_extra_outputs(${OUTPUT_NAME})
|
|
@ -0,0 +1,96 @@
|
|||
#include <math.h>
|
||||
#include <string>
|
||||
#include "pimoroni_i2c.hpp"
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
#include "time.h"
|
||||
|
||||
using namespace pimoroni;
|
||||
using namespace encoderwheel;
|
||||
|
||||
/*
|
||||
A demonstration of reading the 5 buttons on Encoder Wheel.
|
||||
*/
|
||||
|
||||
// Constants
|
||||
const std::string BUTTON_NAMES[] = {"Up", "Down", "Left", "Right", "Centre"};
|
||||
|
||||
// Create a new BreakoutEncoderWheel
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
BreakoutEncoderWheel wheel(&i2c);
|
||||
|
||||
// Variables
|
||||
bool last_pressed[NUM_BUTTONS] = {false, false, false, false, false};
|
||||
bool pressed[NUM_BUTTONS] = {false, false, false, false, false};
|
||||
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Attempt to initialise the encoder wheel
|
||||
if(wheel.init()) {
|
||||
// Loop forever
|
||||
while(true) {
|
||||
// Read all of the encoder wheel's buttons
|
||||
for(int b = 0 ; b < NUM_BUTTONS; b++) {
|
||||
pressed[b] = wheel.pressed(b);
|
||||
if(pressed[b] != last_pressed[b]) {
|
||||
printf("%s %s\n", BUTTON_NAMES[b].c_str(), pressed[b] ? "Pressed" : "Released");
|
||||
}
|
||||
last_pressed[b] = pressed[b];
|
||||
}
|
||||
|
||||
// Clear the LED ring
|
||||
wheel.clear();
|
||||
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
if(i % 6 == 3) {
|
||||
wheel.set_rgb(i, 64, 64, 64);
|
||||
}
|
||||
}
|
||||
|
||||
// If up is pressed, set the top LEDs to yellow
|
||||
if(pressed[UP]) {
|
||||
int mid = NUM_LEDS;
|
||||
for(int i = mid - 2; i < mid + 3; i++) {
|
||||
wheel.set_rgb(i % NUM_LEDS, 255, 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// If right is pressed, set the right LEDs to red
|
||||
if(pressed[RIGHT]) {
|
||||
int mid = NUM_LEDS / 4;
|
||||
for(int i = mid - 2; i < mid + 3; i++) {
|
||||
wheel.set_rgb(i % NUM_LEDS, 255, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// If down is pressed, set the bottom LEDs to green
|
||||
if(pressed[DOWN]) {
|
||||
int mid = NUM_LEDS / 2;
|
||||
for(int i = mid - 2; i < mid + 3; i++) {
|
||||
wheel.set_rgb(i % NUM_LEDS, 0, 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// If left is pressed, set the left LEDs to blue
|
||||
if(pressed[LEFT]) {
|
||||
int mid = (NUM_LEDS * 3) / 4;
|
||||
for(int i = mid - 2; i < mid + 3; i++) {
|
||||
wheel.set_rgb(i % NUM_LEDS, 0, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
// If centre is pressed, set the diagonal LEDs to half white
|
||||
if(pressed[CENTRE]) {
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
if(i % 6 >= 2 && i % 6 <= 4) {
|
||||
wheel.set_rgb(i, 128, 128, 128);
|
||||
}
|
||||
}
|
||||
}
|
||||
wheel.show();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
set(OUTPUT_NAME encoderwheel_chase_game)
|
||||
add_executable(${OUTPUT_NAME} chase_game.cpp)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${OUTPUT_NAME}
|
||||
pico_stdlib
|
||||
breakout_encoder_wheel
|
||||
)
|
||||
|
||||
# enable usb output
|
||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||
|
||||
pico_add_extra_outputs(${OUTPUT_NAME})
|
|
@ -0,0 +1,148 @@
|
|||
#include <math.h>
|
||||
#include <string>
|
||||
#include "pimoroni_i2c.hpp"
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
#include "time.h"
|
||||
|
||||
using namespace pimoroni;
|
||||
using namespace encoderwheel;
|
||||
|
||||
/*
|
||||
A simple alignment game. Use Encoder Wheel's rotary dial to align the coloured band
|
||||
to the white goal. The closer to the goal, the greener your coloured band will be.
|
||||
When you reach the goal, the goal will move to a new random position.
|
||||
*/
|
||||
|
||||
// The band colour hues to show in Angle mode
|
||||
constexpr float GOAL_HUE = 0.333f;
|
||||
constexpr float FAR_HUE = 0.0f;
|
||||
|
||||
// The width and colour settings for the band
|
||||
constexpr float BAND_WIDTH = 5.0f;
|
||||
constexpr float BAND_SATURATION = 1.0f;
|
||||
constexpr float BAND_IN_GOAL_SATURATION = 0.5f;
|
||||
constexpr float BAND_BRIGHTNESS = 1.0f;
|
||||
|
||||
// The width and colour settings for the goal
|
||||
// Goal should be wider than the band by a small amount
|
||||
constexpr float GOAL_MARGIN = 1.0f;
|
||||
constexpr float GOAL_WIDTH = BAND_WIDTH + (2.0f * GOAL_MARGIN);
|
||||
constexpr float GOAL_BRIGHTNESS = 0.4f;
|
||||
|
||||
// Create a new BreakoutEncoderWheel
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
BreakoutEncoderWheel wheel(&i2c);
|
||||
|
||||
// Variables
|
||||
float goal_position = 0.0f;
|
||||
int16_t band_position = 0;
|
||||
|
||||
|
||||
// Maps a value from one range to another
|
||||
float map(float x, float in_min, float in_max, float out_min, float out_max) {
|
||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||
}
|
||||
|
||||
// Shows a band and goal with the given widths at the positions on the strip
|
||||
void colour_band(float centre_position, float width, float goal_position, float goal_width, float hue) {
|
||||
if(centre_position >= 0.0f && width > 0.0f && goal_width > 0.0) {
|
||||
float band_start = centre_position - (width / 2);
|
||||
float band_end = centre_position + (width / 2);
|
||||
float band_centre = centre_position;
|
||||
|
||||
float goal_start = goal_position - (goal_width / 2);
|
||||
float goal_end = goal_position + (goal_width / 2);
|
||||
|
||||
// Go through each led in the strip
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
// Set saturation and brightness values for if the led is inside or outside of the goal
|
||||
float saturation = BAND_SATURATION;
|
||||
float brightness = 0.0f;
|
||||
|
||||
if(i >= goal_start && i < goal_end) {
|
||||
saturation = BAND_IN_GOAL_SATURATION;
|
||||
brightness = GOAL_BRIGHTNESS;
|
||||
}
|
||||
if(goal_end >= NUM_LEDS && i + NUM_LEDS < goal_end) {
|
||||
saturation = BAND_IN_GOAL_SATURATION;
|
||||
brightness = GOAL_BRIGHTNESS;
|
||||
}
|
||||
if(goal_start < 0 && i - NUM_LEDS >= goal_start) {
|
||||
saturation = BAND_IN_GOAL_SATURATION;
|
||||
brightness = GOAL_BRIGHTNESS;
|
||||
}
|
||||
|
||||
float val = brightness;
|
||||
float sat = 0.0f;
|
||||
if(i >= band_start && i < band_end) {
|
||||
// Inside the band
|
||||
if(i < band_centre) {
|
||||
// Transition into the band
|
||||
val = map(i, band_centre, band_start, BAND_BRIGHTNESS, brightness);
|
||||
sat = map(i, band_centre, band_start, BAND_SATURATION, saturation);
|
||||
}
|
||||
else {
|
||||
val = map(i, band_centre, band_end, BAND_BRIGHTNESS, brightness);
|
||||
sat = map(i, band_centre, band_end, BAND_SATURATION, saturation);
|
||||
}
|
||||
}
|
||||
else if(band_end >= NUM_LEDS && i + NUM_LEDS < band_end && i < band_centre) {
|
||||
val = map(i + NUM_LEDS, band_centre, band_end, BAND_BRIGHTNESS, brightness);
|
||||
sat = map(i + NUM_LEDS, band_centre, band_end, BAND_SATURATION, saturation);
|
||||
}
|
||||
else if(band_start < 0 && i - NUM_LEDS >= band_start && i >= band_centre) {
|
||||
val = map(i - NUM_LEDS, band_centre, band_start, BAND_BRIGHTNESS, brightness);
|
||||
sat = map(i - NUM_LEDS, band_centre, band_start, BAND_SATURATION, saturation);
|
||||
}
|
||||
//else {
|
||||
// Outside of the band
|
||||
//}
|
||||
wheel.set_hsv(i, hue, sat, val);
|
||||
}
|
||||
wheel.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Attempt to initialise the encoder wheel
|
||||
if(wheel.init()) {
|
||||
// Loop forever
|
||||
while(true) {
|
||||
band_position = wheel.step();
|
||||
|
||||
// Convert the difference between the band and goal positions into a colour hue
|
||||
float diff1, diff2;
|
||||
if(band_position > goal_position) {
|
||||
diff1 = band_position - goal_position;
|
||||
diff2 = (goal_position + NUM_LEDS) - band_position;
|
||||
}
|
||||
else {
|
||||
diff1 = goal_position - band_position;
|
||||
diff2 = (band_position + NUM_LEDS) - goal_position;
|
||||
}
|
||||
|
||||
float position_diff = MIN(diff1, diff2);
|
||||
float hue = map(position_diff, 0, NUM_LEDS / 2.0f, GOAL_HUE, FAR_HUE);
|
||||
|
||||
// Convert the band and goal positions to positions on the LED strip
|
||||
float strip_band_position = map(band_position, 0, NUM_LEDS, 0.0f, (float)NUM_LEDS);
|
||||
float strip_goal_position = map(goal_position, 0, NUM_LEDS, 0.0f, (float)NUM_LEDS);
|
||||
|
||||
// Draw the band and goal
|
||||
colour_band(strip_band_position, BAND_WIDTH, strip_goal_position, GOAL_WIDTH, hue);
|
||||
|
||||
// Check if the band is within the goal, and if so, set a new goal
|
||||
if(band_position >= goal_position - GOAL_MARGIN && band_position <= goal_position + GOAL_MARGIN)
|
||||
goal_position = rand() % NUM_LEDS;
|
||||
if(band_position >= NUM_LEDS && band_position + NUM_LEDS < goal_position + GOAL_MARGIN)
|
||||
goal_position = rand() % NUM_LEDS;
|
||||
if(goal_position - GOAL_MARGIN < 0 && band_position - NUM_LEDS >= goal_position + GOAL_MARGIN)
|
||||
goal_position = rand() % NUM_LEDS;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
set(OUTPUT_NAME encoderwheel_clock)
|
||||
add_executable(${OUTPUT_NAME} clock.cpp)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${OUTPUT_NAME}
|
||||
pico_stdlib
|
||||
hardware_rtc
|
||||
breakout_encoder_wheel
|
||||
)
|
||||
|
||||
# enable usb output
|
||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||
|
||||
pico_add_extra_outputs(${OUTPUT_NAME})
|
|
@ -0,0 +1,114 @@
|
|||
#include <math.h>
|
||||
#include <string>
|
||||
#include "pimoroni_i2c.hpp"
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
#include "time.h"
|
||||
#include "hardware/rtc.h"
|
||||
#include "pico/util/datetime.h"
|
||||
|
||||
using namespace pimoroni;
|
||||
using namespace encoderwheel;
|
||||
|
||||
/*
|
||||
Displays a 12 hour clock on Encoder Wheel's LED ring, getting time from the system.
|
||||
*/
|
||||
|
||||
// Datetime Indices
|
||||
const uint HOUR = 4;
|
||||
const uint MINUTE = 5;
|
||||
const uint SECOND = 6;
|
||||
|
||||
// Constants
|
||||
constexpr float BRIGHTNESS = 1.0f; // The brightness of the LEDs
|
||||
const uint UPDATES = 50; // How many times the LEDs will be updated per second
|
||||
const uint UPDATE_RATE_US = 1000000 / UPDATES;
|
||||
|
||||
// Handy values for the number of milliseconds
|
||||
constexpr float MILLIS_PER_SECOND = 1000;
|
||||
constexpr float MILLIS_PER_MINUTE = MILLIS_PER_SECOND * 60;
|
||||
constexpr float MILLIS_PER_HOUR = MILLIS_PER_MINUTE * 60;
|
||||
constexpr float MILLIS_PER_HALF_DAY = MILLIS_PER_HOUR * 12;
|
||||
|
||||
// Create a new BreakoutEncoderWheel
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
BreakoutEncoderWheel wheel(&i2c);
|
||||
|
||||
|
||||
// Calculates the brightness of an LED based on its index and a position along the LED ring
|
||||
int led_brightness_at(int led, float position, float half_width = 1.0f, float span = 1.0f) {
|
||||
float brightness = 0.0f;
|
||||
float upper = position + half_width;
|
||||
float lower = position - half_width;
|
||||
if(led > position)
|
||||
brightness = CLAMP((upper - led) / span, 0.0f, 1.0f);
|
||||
else
|
||||
brightness = CLAMP((led - lower) / span, 0.0f, 1.0f);
|
||||
|
||||
// Handle the LEDs being in a circle
|
||||
if(upper >= NUM_LEDS)
|
||||
brightness = CLAMP(((upper - NUM_LEDS) - led) / span, brightness, 1.0f);
|
||||
else if(lower < 0.0f)
|
||||
brightness = CLAMP((led - (lower + NUM_LEDS)) / span, brightness, 1.0f);
|
||||
|
||||
return (int)(brightness * BRIGHTNESS * 255);
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Start on Thursday 4th of May 2023 14:20:00
|
||||
datetime_t now = {
|
||||
.year = 2023,
|
||||
.month = 05,
|
||||
.day = 04,
|
||||
.dotw = 4, // 0 is Sunday, so 4 is Thursday
|
||||
.hour = 14,
|
||||
.min = 20,
|
||||
.sec = 00
|
||||
};
|
||||
|
||||
// Start the RTC
|
||||
rtc_init();
|
||||
rtc_set_datetime(&now);
|
||||
|
||||
// clk_sys is >2000x faster than clk_rtc, so datetime is not updated immediately when rtc_get_datetime() is called.
|
||||
// tbe delay is up to 3 RTC clock cycles (which is 64us with the default clock settings)
|
||||
sleep_us(64);
|
||||
|
||||
// Attempt to initialise the encoder wheel
|
||||
if(wheel.init()) {
|
||||
// Loop forever
|
||||
while(true) {
|
||||
// Record the start time of this loop
|
||||
absolute_time_t start_time = get_absolute_time();
|
||||
|
||||
// Get the current system time
|
||||
rtc_get_datetime(&now);
|
||||
|
||||
// Convert the seconds, minutes, and hours into milliseconds (this is done to give a smoother animation, particularly for the seconds hand)
|
||||
uint sec_as_millis = (now.sec * MILLIS_PER_SECOND);
|
||||
uint min_as_millis = (now.min * MILLIS_PER_MINUTE) + sec_as_millis;
|
||||
uint hour_as_millis = ((now.hour % 12) * MILLIS_PER_HOUR) + min_as_millis;
|
||||
|
||||
// Calculate the position on the LED ring that the, second, minute, and hour hands should be
|
||||
float sec_pos = MIN(sec_as_millis / MILLIS_PER_MINUTE, 1.0f) * NUM_LEDS;
|
||||
float min_pos = MIN(min_as_millis / MILLIS_PER_HOUR, 1.0f) * NUM_LEDS;
|
||||
float hour_pos = MIN(hour_as_millis / MILLIS_PER_HALF_DAY, 1.0f) * NUM_LEDS;
|
||||
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
// Turn on the LEDs close to the position of the current second, minute, and hour
|
||||
int r = led_brightness_at(i, sec_pos);
|
||||
int g = led_brightness_at(i, min_pos);
|
||||
int b = led_brightness_at(i, hour_pos);
|
||||
wheel.set_rgb(i, r, g, b);
|
||||
}
|
||||
wheel.show();
|
||||
|
||||
// Sleep until the next update, accounting for how long the above operations took to perform
|
||||
sleep_until(delayed_by_us(start_time, UPDATE_RATE_US));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
set(OUTPUT_NAME encoderwheel_colour_picker)
|
||||
add_executable(${OUTPUT_NAME} colour_picker.cpp)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${OUTPUT_NAME}
|
||||
pico_stdlib
|
||||
breakout_encoder_wheel
|
||||
)
|
||||
|
||||
# enable usb output
|
||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||
|
||||
pico_add_extra_outputs(${OUTPUT_NAME})
|
|
@ -0,0 +1,168 @@
|
|||
#include <math.h>
|
||||
#include <string>
|
||||
#include "pimoroni_i2c.hpp"
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
#include "time.h"
|
||||
|
||||
using namespace pimoroni;
|
||||
using namespace encoderwheel;
|
||||
|
||||
/*
|
||||
Create a colour wheel on the Encoder Wheel's LED ring, and use all functions of the wheel to interact with it.
|
||||
|
||||
Rotate the wheel to select a Hue
|
||||
Press the up direction to increase Brightness
|
||||
Press the down direction to decrease Brightness
|
||||
Press the left direction to decrease Saturation
|
||||
Press the right direction to increase Saturation
|
||||
Press the centre to hide the selection marker
|
||||
*/
|
||||
|
||||
// Constants
|
||||
constexpr float BRIGHTNESS_STEP = 0.02f; // How much to increase or decrease the brightness each update
|
||||
constexpr float SATURATION_STEP = 0.02f; // How much to increase or decrease the saturation each update
|
||||
const uint UPDATES = 50; // How many times to update the LEDs per second
|
||||
const uint UPDATE_RATE_US = 1000000 / UPDATES;
|
||||
|
||||
// Create a new BreakoutEncoderWheel
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
BreakoutEncoderWheel wheel(&i2c);
|
||||
|
||||
// Variables
|
||||
float brightness = 1.0f;
|
||||
float saturation = 1.0f;
|
||||
int position = 0;
|
||||
bool changed = true;
|
||||
bool last_centre_pressed = false;
|
||||
|
||||
// Struct for storing RGB values
|
||||
struct Pixel {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
Pixel() : r(0), g(0), b(0) {};
|
||||
Pixel(uint8_t r, uint8_t g, uint8_t b) : r(r), g(g), b(b) {};
|
||||
};
|
||||
|
||||
// Basic function to convert Hue, Saturation and Value to an RGB colour
|
||||
Pixel hsv_to_rgb(float h, float s, float v) {
|
||||
if(h < 0.0f) {
|
||||
h = 1.0f + fmodf(h, 1.0f);
|
||||
}
|
||||
|
||||
int i = int(h * 6);
|
||||
float f = h * 6 - i;
|
||||
|
||||
v = v * 255.0f;
|
||||
|
||||
float sv = s * v;
|
||||
float fsv = f * sv;
|
||||
|
||||
auto p = uint8_t(-sv + v);
|
||||
auto q = uint8_t(-fsv + v);
|
||||
auto t = uint8_t(fsv - sv + v);
|
||||
|
||||
uint8_t bv = uint8_t(v);
|
||||
|
||||
switch (i % 6) {
|
||||
default:
|
||||
case 0: return Pixel(bv, t, p);
|
||||
case 1: return Pixel(q, bv, p);
|
||||
case 2: return Pixel(p, bv, t);
|
||||
case 3: return Pixel(p, q, bv);
|
||||
case 4: return Pixel(t, p, bv);
|
||||
case 5: return Pixel(bv, p, q);
|
||||
}
|
||||
}
|
||||
|
||||
// Simple function to clamp a value between 0.0 and 1.0
|
||||
float clamp01(float value) {
|
||||
return MAX(MIN(value, 1.0f), 0.0f);
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Attempt to initialise the encoder wheel
|
||||
if(wheel.init()) {
|
||||
// Loop forever
|
||||
while(true) {
|
||||
// Record the start time of this loop
|
||||
absolute_time_t start_time = get_absolute_time();
|
||||
|
||||
// If up is pressed, increase the brightness
|
||||
if(wheel.pressed(UP)) {
|
||||
brightness += BRIGHTNESS_STEP;
|
||||
changed = true; // Trigger a change
|
||||
}
|
||||
|
||||
// If down is pressed, decrease the brightness
|
||||
if(wheel.pressed(DOWN)) {
|
||||
brightness -= BRIGHTNESS_STEP;
|
||||
changed = true; // Trigger a change
|
||||
}
|
||||
|
||||
// If right is pressed, increase the saturation
|
||||
if(wheel.pressed(RIGHT)) {
|
||||
saturation += SATURATION_STEP;
|
||||
changed = true; // Trigger a change
|
||||
}
|
||||
|
||||
// If left is pressed, decrease the saturation
|
||||
if(wheel.pressed(LEFT)) {
|
||||
saturation -= SATURATION_STEP;
|
||||
changed = true; // Trigger a change
|
||||
}
|
||||
|
||||
// Limit the brightness and saturation between 0.0 and 1.0
|
||||
brightness = clamp01(brightness);
|
||||
saturation = clamp01(saturation);
|
||||
|
||||
// Check if the encoder has been turned
|
||||
if(wheel.delta() != 0) {
|
||||
// Update the position based on the count change
|
||||
position = wheel.step();
|
||||
changed = true; // Trigger a change
|
||||
}
|
||||
|
||||
// If centre is pressed, trigger a change
|
||||
bool centre_pressed = wheel.pressed(CENTRE);
|
||||
if(centre_pressed != last_centre_pressed) {
|
||||
changed = true;
|
||||
}
|
||||
last_centre_pressed = centre_pressed;
|
||||
|
||||
// Was a change triggered?
|
||||
if(changed) {
|
||||
// Print the colour at the current hue, saturation, and brightness
|
||||
Pixel pixel = hsv_to_rgb((float)position / NUM_LEDS, saturation, brightness);
|
||||
printf("Colour Code = #%02x%02x%02x\n", pixel.r, pixel.g, pixel.b);
|
||||
|
||||
// Set the LED at the current position to either the actual colour,
|
||||
// or an inverted version to show a "selection marker"
|
||||
if(centre_pressed)
|
||||
wheel.set_rgb(position, pixel.r, pixel.g, pixel.b);
|
||||
else
|
||||
wheel.set_rgb(position, 255 - pixel.r, 255 - pixel.g, 255 - pixel.b);
|
||||
|
||||
// Set the LEDs below the current position
|
||||
for(int i = 0; i < position; i++) {
|
||||
wheel.set_hsv(i, (float)i / NUM_LEDS, saturation, brightness);
|
||||
}
|
||||
|
||||
// Set the LEDs after the current position
|
||||
for(int i = position + 1; i < NUM_LEDS; i++) {
|
||||
wheel.set_hsv(i, (float)i / NUM_LEDS, saturation, brightness);
|
||||
}
|
||||
wheel.show();
|
||||
changed = false;
|
||||
}
|
||||
|
||||
// Sleep until the next update, accounting for how long the above operations took to perform
|
||||
sleep_until(delayed_by_us(start_time, UPDATE_RATE_US));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
set(OUTPUT_NAME encoderwheel_encoder)
|
||||
add_executable(${OUTPUT_NAME} encoder.cpp)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${OUTPUT_NAME}
|
||||
pico_stdlib
|
||||
breakout_encoder_wheel
|
||||
)
|
||||
|
||||
# enable usb output
|
||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||
|
||||
pico_add_extra_outputs(${OUTPUT_NAME})
|
|
@ -0,0 +1,60 @@
|
|||
#include <math.h>
|
||||
#include <string>
|
||||
#include "pimoroni_i2c.hpp"
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
#include "time.h"
|
||||
|
||||
using namespace pimoroni;
|
||||
using namespace encoderwheel;
|
||||
|
||||
/*
|
||||
A demonstration of reading the rotary dial of the Encoder Wheel breakout.
|
||||
*/
|
||||
|
||||
// Create a new BreakoutEncoderWheel
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
BreakoutEncoderWheel wheel(&i2c);
|
||||
|
||||
// Variables
|
||||
int position = 0;
|
||||
float hue = 0.0f;
|
||||
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Attempt to initialise the encoder wheel
|
||||
if(wheel.init()) {
|
||||
|
||||
// Set the first LED
|
||||
wheel.clear();
|
||||
wheel.set_hsv(position, hue, 1.0f, 1.0f);
|
||||
wheel.show();
|
||||
|
||||
// Loop forever
|
||||
while(true) {
|
||||
// Has the dial been turned since the last time we checked?
|
||||
int16_t change = wheel.delta();
|
||||
if(change != 0) {
|
||||
// Print out the direction the dial was turned, and the count
|
||||
if(change > 0)
|
||||
printf("Clockwise, Count = %d\n", wheel.count());
|
||||
else
|
||||
printf("Counter Clockwise, Count = %d\n", wheel.count());
|
||||
|
||||
// Record the new position (from 0 to 23)
|
||||
position = wheel.step();
|
||||
|
||||
// Record a colour hue from 0.0 to 1.0
|
||||
hue = fmodf(wheel.revolutions(), 1.0f);
|
||||
|
||||
// Set the LED at the new position to the new hue
|
||||
wheel.clear();
|
||||
wheel.set_hsv(position, hue, 1.0f, 1.0f);
|
||||
wheel.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
set(OUTPUT_NAME encoderwheel_gpio_pwm)
|
||||
add_executable(${OUTPUT_NAME} gpio_pwm.cpp)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${OUTPUT_NAME}
|
||||
pico_stdlib
|
||||
breakout_encoder_wheel
|
||||
)
|
||||
|
||||
# enable usb output
|
||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||
|
||||
pico_add_extra_outputs(${OUTPUT_NAME})
|
|
@ -0,0 +1,74 @@
|
|||
#include <math.h>
|
||||
#include <string>
|
||||
#include "pimoroni_i2c.hpp"
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
#include "time.h"
|
||||
|
||||
using namespace pimoroni;
|
||||
using namespace encoderwheel;
|
||||
|
||||
/*
|
||||
Output a sine wave PWM sequence on the Encoder Wheel's side GPIO pins.
|
||||
|
||||
Press the centre button to stop the program.
|
||||
*/
|
||||
|
||||
// Constants
|
||||
constexpr float SPEED = 5.0f; // The speed that the LEDs will cycle at
|
||||
const uint UPDATES = 50; // How many times the LEDs will be updated per second
|
||||
const uint UPDATE_RATE_US = 1000000 / UPDATES;
|
||||
constexpr float FREQUENCY = 1000.0f; // The frequency to run the PWM at
|
||||
|
||||
// Create a new BreakoutEncoderWheel
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
BreakoutEncoderWheel wheel(&i2c);
|
||||
|
||||
// Variables
|
||||
float offset = 0.0f;
|
||||
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Attempt to initialise the encoder wheel
|
||||
if(wheel.init()) {
|
||||
|
||||
// Set the PWM frequency for the GPIOs
|
||||
uint16_t period = wheel.gpio_pwm_frequency(FREQUENCY);
|
||||
|
||||
// Set the GPIO pins to PWM outputs
|
||||
for(int i = 0; i < NUM_GPIOS; i++) {
|
||||
wheel.gpio_pin_mode(GPIOS[i], IOExpander::PIN_PWM);
|
||||
}
|
||||
|
||||
// Loop forever
|
||||
while(!wheel.pressed(CENTRE)) {
|
||||
// Record the start time of this loop
|
||||
absolute_time_t start_time = get_absolute_time();
|
||||
|
||||
offset += SPEED / 1000.0f;
|
||||
|
||||
// Update all the PWMs
|
||||
for(int i = 0; i < NUM_GPIOS; i++) {
|
||||
float angle = (((float)i / NUM_GPIOS) + offset) * M_PI;
|
||||
uint16_t duty = (uint16_t)(((sinf(angle) / 2.0f) + 0.5f) * period);
|
||||
|
||||
// Set the GPIO pin to the new duty cycle, but do not load it yet
|
||||
wheel.gpio_pin_value(GPIOS[i], duty, false);
|
||||
}
|
||||
|
||||
// Have all the PWMs load at once
|
||||
wheel.gpio_pwm_load();
|
||||
|
||||
// Sleep until the next update, accounting for how long the above operations took to perform
|
||||
sleep_until(delayed_by_us(start_time, UPDATE_RATE_US));
|
||||
}
|
||||
|
||||
// Turn off the PWM outputs
|
||||
for(int i = 0; i < NUM_GPIOS; i++) {
|
||||
wheel.gpio_pin_value(GPIOS[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
set(OUTPUT_NAME encoderwheel_interrupt)
|
||||
add_executable(${OUTPUT_NAME} interrupt.cpp)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${OUTPUT_NAME}
|
||||
pico_stdlib
|
||||
breakout_encoder_wheel
|
||||
)
|
||||
|
||||
# enable usb output
|
||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||
|
||||
pico_add_extra_outputs(${OUTPUT_NAME})
|
|
@ -0,0 +1,84 @@
|
|||
#include <math.h>
|
||||
#include <string>
|
||||
#include "pimoroni_i2c.hpp"
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
#include "time.h"
|
||||
|
||||
using namespace pimoroni;
|
||||
using namespace encoderwheel;
|
||||
|
||||
/*
|
||||
How to read the buttons and rotary dial of the Encoder Wheel breakout, only when an interrupt occurs.
|
||||
*/
|
||||
|
||||
// Constants
|
||||
const std::string BUTTON_NAMES[] = {"Up", "Down", "Left", "Right", "Centre"};
|
||||
|
||||
// Create a new BreakoutEncoderWheel
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
BreakoutEncoderWheel wheel(&i2c,
|
||||
BreakoutEncoderWheel::DEFAULT_IOE_I2C_ADDRESS,
|
||||
BreakoutEncoderWheel::DEFAULT_LED_I2C_ADDRESS,
|
||||
3); // 3 for BG_BASE, 22 for EXPLORER_BASE, or 19 for some RP2040 boards
|
||||
// If wiring the breakout via the qw/st connector, use the below line instead
|
||||
// BreakoutEncoderWheel wheel(&i2c);
|
||||
|
||||
// Variables
|
||||
bool last_pressed[NUM_BUTTONS] = {false, false, false, false, false};
|
||||
bool pressed[NUM_BUTTONS] = {false, false, false, false, false};
|
||||
int position = 0;
|
||||
float hue = 0.0f;
|
||||
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Attempt to initialise the encoder wheel
|
||||
if(wheel.init()) {
|
||||
|
||||
// Set the first LED
|
||||
wheel.clear();
|
||||
wheel.set_hsv(position, hue, 1.0f, 1.0f);
|
||||
wheel.show();
|
||||
|
||||
// Clear any left over interrupt from previous code
|
||||
wheel.clear_interrupt_flag();
|
||||
|
||||
// Loop forever
|
||||
while(true) {
|
||||
|
||||
// Check if the interrupt has fired
|
||||
if(wheel.get_interrupt_flag()) {
|
||||
wheel.clear_interrupt_flag();
|
||||
|
||||
// Read all of the encoder wheel's buttons
|
||||
for(int b = 0 ; b < NUM_BUTTONS; b++) {
|
||||
pressed[b] = wheel.pressed(b);
|
||||
if(pressed[b] != last_pressed[b]) {
|
||||
printf("%s %s\n", BUTTON_NAMES[b].c_str(), pressed[b] ? "Pressed" : "Released");
|
||||
}
|
||||
last_pressed[b] = pressed[b];
|
||||
}
|
||||
|
||||
// The interrupt may have come from several sources,
|
||||
// so check if it was a position change
|
||||
int new_position = wheel.step();
|
||||
if(new_position != position) {
|
||||
// Record the new position (from 0 to 23)
|
||||
position = new_position;
|
||||
printf("Position = %d\n", position);
|
||||
|
||||
// Record a colour hue from 0.0 to 1.0
|
||||
hue = fmodf(wheel.revolutions(), 1.0f);
|
||||
|
||||
// Set the LED at the new position to the new hue
|
||||
wheel.clear();
|
||||
wheel.set_hsv(position, hue, 1.0f, 1.0f);
|
||||
wheel.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
set(OUTPUT_NAME encoderwheel_led_rainbow)
|
||||
add_executable(${OUTPUT_NAME} led_rainbow.cpp)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${OUTPUT_NAME}
|
||||
pico_stdlib
|
||||
breakout_encoder_wheel
|
||||
)
|
||||
|
||||
# enable usb output
|
||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||
|
||||
pico_add_extra_outputs(${OUTPUT_NAME})
|
|
@ -0,0 +1,54 @@
|
|||
#include <math.h>
|
||||
#include <string>
|
||||
#include "pimoroni_i2c.hpp"
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
#include "time.h"
|
||||
|
||||
using namespace pimoroni;
|
||||
using namespace encoderwheel;
|
||||
|
||||
/*
|
||||
Displays a rotating rainbow pattern on Encoder Wheel's LED ring.
|
||||
*/
|
||||
|
||||
// Constants
|
||||
constexpr float SPEED = 5.0f; // The speed that the LEDs will cycle at
|
||||
constexpr float BRIGHTNESS = 1.0f; // The brightness of the LEDs
|
||||
const uint UPDATES = 50; // How many times the LEDs will be updated per second
|
||||
const uint UPDATE_RATE_US = 1000000 / UPDATES;
|
||||
|
||||
// Create a new BreakoutEncoderWheel
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
BreakoutEncoderWheel wheel(&i2c);
|
||||
|
||||
// Variables
|
||||
float offset = 0.0;
|
||||
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Attempt to initialise the encoder wheel
|
||||
if(wheel.init()) {
|
||||
// Loop forever
|
||||
while(true) {
|
||||
|
||||
// Record the start time of this loop
|
||||
absolute_time_t start_time = get_absolute_time();
|
||||
|
||||
offset += SPEED / 1000.0f;
|
||||
|
||||
// Update all the LEDs
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
float hue = (float)i / NUM_LEDS;
|
||||
wheel.set_hsv(i, hue + offset, 1.0, BRIGHTNESS);
|
||||
}
|
||||
wheel.show();
|
||||
|
||||
// Sleep until the next update, accounting for how long the above operations took to perform
|
||||
sleep_until(delayed_by_us(start_time, UPDATE_RATE_US));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
set(OUTPUT_NAME encoderwheel_stop_watch)
|
||||
add_executable(${OUTPUT_NAME} stop_watch.cpp)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${OUTPUT_NAME}
|
||||
pico_stdlib
|
||||
breakout_encoder_wheel
|
||||
)
|
||||
|
||||
# enable usb output
|
||||
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
|
||||
|
||||
pico_add_extra_outputs(${OUTPUT_NAME})
|
|
@ -0,0 +1,151 @@
|
|||
#include <math.h>
|
||||
#include <string>
|
||||
#include "pimoroni_i2c.hpp"
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
#include "time.h"
|
||||
|
||||
using namespace pimoroni;
|
||||
using namespace encoderwheel;
|
||||
|
||||
/*
|
||||
Display a circular stop-watch on the Encoder Wheel's LED ring.
|
||||
|
||||
Press the centre button to start the stopwatch, then again to pause and resume.
|
||||
*/
|
||||
|
||||
// Constants
|
||||
constexpr float BRIGHTNESS = 1.0f; // The brightness of the LEDs
|
||||
const uint UPDATES = 50; // How many times the LEDs will be updated per second
|
||||
const uint MINUTE_UPDATES = UPDATES * 60; // How many times the LEDs will be updated per minute
|
||||
const uint HOUR_UPDATES = MINUTE_UPDATES * 60; // How many times the LEDs will be updated per hour
|
||||
const uint UPDATE_RATE_US = 1000000 / UPDATES;
|
||||
|
||||
constexpr float IDLE_PULSE_MIN = 0.2f; // The brightness (between 0.0 and 1.0) that the idle pulse will go down to
|
||||
constexpr float IDLE_PULSE_MAX = 0.5f; // The brightness (between 0.0 and 1.0) that the idle pulse will go up to
|
||||
constexpr float IDLE_PULSE_TIME = 2.0f; // The time (in seconds) to perform a complete idle pulse
|
||||
constexpr uint UPDATES_PER_PULSE = IDLE_PULSE_TIME * UPDATES;
|
||||
|
||||
// The state constants used for program flow
|
||||
enum State {
|
||||
IDLE = 0,
|
||||
COUNTING,
|
||||
PAUSED
|
||||
};
|
||||
|
||||
// Create a new BreakoutEncoderWheel
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
BreakoutEncoderWheel wheel(&i2c);
|
||||
|
||||
// Variables
|
||||
State state = IDLE;
|
||||
uint idle_update = 0;
|
||||
uint second_update = 0;
|
||||
uint minute_update = 0;
|
||||
uint hour_update = 0;
|
||||
bool last_centre_pressed = false;
|
||||
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Attempt to initialise the encoder wheel
|
||||
if(wheel.init()) {
|
||||
// Record the current time
|
||||
absolute_time_t current_time = get_absolute_time();
|
||||
|
||||
// Run the update loop forever
|
||||
while(true) {
|
||||
|
||||
// Record the start time of this loop
|
||||
absolute_time_t start_time = get_absolute_time();
|
||||
|
||||
// Read whether or not the wheen centre has been pressed
|
||||
bool centre_pressed = wheel.pressed(CENTRE);
|
||||
if(centre_pressed && centre_pressed != last_centre_pressed) {
|
||||
switch(state) {
|
||||
case IDLE: // If we're currently idle, switch to counting
|
||||
second_update = 0;
|
||||
minute_update = 0;
|
||||
hour_update = 0;
|
||||
state = COUNTING;
|
||||
break;
|
||||
case COUNTING: // If we're counting, switch to paused
|
||||
state = PAUSED;
|
||||
break;
|
||||
case PAUSED: // If we're paused, switch back to counting
|
||||
state = COUNTING;
|
||||
}
|
||||
}
|
||||
last_centre_pressed = centre_pressed;
|
||||
|
||||
switch(state) {
|
||||
// If we're idle, perform a pulsing animation to show the stopwatch is ready to go
|
||||
case IDLE:
|
||||
{
|
||||
float percent_along = MIN((float)idle_update / (float)UPDATES_PER_PULSE, 1.0f);
|
||||
float brightness = ((cosf(percent_along * M_PI * 2.0f) + 1.0f) / 2.0f) * ((IDLE_PULSE_MAX - IDLE_PULSE_MIN)) + IDLE_PULSE_MIN;
|
||||
// Update all the LEDs
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
wheel.set_hsv(i, 0.0, 0.0, brightness);
|
||||
}
|
||||
wheel.show();
|
||||
|
||||
// Advance to the next update, wrapping around to zero if at the end
|
||||
idle_update += 1;
|
||||
if(idle_update >= UPDATES_PER_PULSE) {
|
||||
idle_update = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// If we're counting, perform the stopwatch animation
|
||||
case COUNTING:
|
||||
{
|
||||
// Calculate how many LED channels should light, as a proportion of a second, minute, and hour
|
||||
float r_to_light = MIN((float)second_update / UPDATES, 1.0f) * 24.0f;
|
||||
float g_to_light = MIN((float)minute_update / MINUTE_UPDATES, 1.0f) * 24.0f;
|
||||
float b_to_light = MIN((float)hour_update / HOUR_UPDATES, 1.0f) * 24.0f;
|
||||
|
||||
// Set each LED, such that ones below the current time are fully lit, ones after
|
||||
// are off, and the one at the transition is at a percentage of the brightness
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
int r = (int)(CLAMP(r_to_light - i, 0.0f, 1.0f) * BRIGHTNESS * 255.0f);
|
||||
int g = (int)(CLAMP(g_to_light - i, 0.0f, 1.0f) * BRIGHTNESS * 255.0f);
|
||||
int b = (int)(CLAMP(b_to_light - i, 0.0f, 1.0f) * BRIGHTNESS * 255.0f);
|
||||
wheel.set_rgb(i, r, g, b);
|
||||
}
|
||||
wheel.show();
|
||||
|
||||
// Advance the second updates count, wrapping around to zero if at the end
|
||||
second_update += 1;
|
||||
if(second_update >= UPDATES) {
|
||||
second_update = 0;
|
||||
}
|
||||
|
||||
// Advance the minute updates count, wrapping around to zero if at the end
|
||||
minute_update += 1;
|
||||
if(minute_update >= MINUTE_UPDATES) {
|
||||
minute_update = 0;
|
||||
}
|
||||
|
||||
// Advance the hour updates count, wrapping around to zero if at the end
|
||||
hour_update += 1;
|
||||
if(hour_update >= HOUR_UPDATES) {
|
||||
hour_update = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PAUSED:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
// Sleep until the next update, accounting for how long the above operations took to perform
|
||||
current_time = delayed_by_us(start_time, UPDATE_RATE_US);
|
||||
sleep_until(current_time);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -3,7 +3,11 @@
|
|||
#include "pico_explorer.hpp"
|
||||
#include "pico/stdlib.h"
|
||||
#include "encoder.hpp"
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "quadrature_out.pio.h"
|
||||
#endif
|
||||
|
||||
#include "drivers/st7789/st7789.hpp"
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "button.hpp"
|
||||
|
|
|
@ -34,10 +34,15 @@ int main() {
|
|||
while(true) {
|
||||
|
||||
offset += float(SPEED) / 2000.0f;
|
||||
if (offset > 1.0) {
|
||||
offset -= 1.0;
|
||||
}
|
||||
|
||||
for(auto i = 0u; i < NUM_LEDS; ++i) {
|
||||
float hue = float(i) / NUM_LEDS;
|
||||
led_strip.set_hsv(i, hue + offset, 1.0f, 1.0f);
|
||||
hue += offset;
|
||||
hue -= floor(hue);
|
||||
led_strip.set_hsv(i, hue, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
sleep_ms(1000 / UPDATES);
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
add_executable(
|
||||
stellar_rainbow_text
|
||||
stellar_rainbow_text.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(stellar_rainbow_text pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics stellar_unicorn)
|
||||
pico_enable_stdio_usb(stellar_rainbow_text 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(stellar_rainbow_text)
|
||||
|
||||
|
||||
|
||||
add_executable(
|
||||
stellar_rainbow
|
||||
stellar_rainbow.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(stellar_rainbow pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics stellar_unicorn)
|
||||
pico_enable_stdio_usb(stellar_rainbow 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(stellar_rainbow)
|
||||
|
||||
|
||||
|
||||
|
||||
add_executable(
|
||||
stellar_eighties_super_computer
|
||||
stellar_eighties_super_computer.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(stellar_eighties_super_computer pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics stellar_unicorn)
|
||||
pico_enable_stdio_usb(stellar_eighties_super_computer 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(stellar_eighties_super_computer)
|
||||
|
||||
|
||||
|
||||
|
||||
add_executable(
|
||||
stellar_fire_effect
|
||||
stellar_fire_effect.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(stellar_fire_effect pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics stellar_unicorn)
|
||||
pico_enable_stdio_usb(stellar_fire_effect 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(stellar_fire_effect)
|
||||
|
||||
|
||||
|
||||
|
||||
add_executable(
|
||||
stellar_scroll_text
|
||||
stellar_scroll_text.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(stellar_scroll_text pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics stellar_unicorn)
|
||||
pico_enable_stdio_usb(stellar_scroll_text 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(stellar_scroll_text)
|
||||
|
||||
|
||||
add_executable(
|
||||
stellar_lava_lamp
|
||||
stellar_lava_lamp.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(stellar_lava_lamp pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics stellar_unicorn)
|
||||
pico_enable_stdio_usb(stellar_lava_lamp 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(stellar_lava_lamp)
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,67 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "stellar_unicorn.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(16, 16, nullptr);
|
||||
StellarUnicorn stellar_unicorn;
|
||||
|
||||
float lifetime[16][16];
|
||||
float age[16][16];
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
for(int y = 0; y < 16; y++) {
|
||||
for(int x = 0; x < 16; x++) {
|
||||
lifetime[x][y] = 1.0f + ((rand() % 10) / 100.0f);
|
||||
age[x][y] = ((rand() % 100) / 100.0f) * lifetime[x][y];
|
||||
}
|
||||
}
|
||||
|
||||
stellar_unicorn.init();
|
||||
|
||||
while(true) {
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
stellar_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
stellar_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.clear();
|
||||
|
||||
for(int y = 0; y < 16; y++) {
|
||||
for(int x = 0; x < 16; x++) {
|
||||
if(age[x][y] < lifetime[x][y] * 0.3f) {
|
||||
graphics.set_pen(230, 150, 0);
|
||||
graphics.pixel(Point(x, y));
|
||||
}else if(age[x][y] < lifetime[x][y] * 0.5f) {
|
||||
float decay = (lifetime[x][y] * 0.5f - age[x][y]) * 5.0f;
|
||||
graphics.set_pen(decay * 230, decay * 150, 0);
|
||||
graphics.pixel(Point(x, y));
|
||||
}
|
||||
|
||||
if(age[x][y] >= lifetime[x][y]) {
|
||||
age[x][y] = 0.0f;
|
||||
lifetime[x][y] = 1.0f + ((rand() % 10) / 100.0f);
|
||||
}
|
||||
|
||||
age[x][y] += 0.01f;
|
||||
}
|
||||
}
|
||||
|
||||
stellar_unicorn.update(&graphics);
|
||||
|
||||
sleep_ms(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "stellar_unicorn.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(16, 16, nullptr);
|
||||
StellarUnicorn stellar_unicorn;
|
||||
|
||||
// extra row of pixels for sourcing flames and averaging
|
||||
int width = 16;
|
||||
int height = 17;
|
||||
|
||||
// a buffer that's at least big enough to store 55 x 15 values (to allow for both orientations)
|
||||
float heat[2000] = {0.0f};
|
||||
|
||||
void set(int x, int y, float v) {
|
||||
heat[x + y * width] = v;
|
||||
}
|
||||
|
||||
float get(int x, int y) {
|
||||
/*if(x < 0 || x >= width || y < 0 || y >= height) {
|
||||
return 0.0f;
|
||||
}*/
|
||||
x = x < 0 ? 0 : x;
|
||||
x = x >= width ? width - 1 : x;
|
||||
|
||||
return heat[x + y * width];
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
stellar_unicorn.init();
|
||||
stellar_unicorn.set_brightness(0.2);
|
||||
|
||||
bool landscape = true;
|
||||
/*
|
||||
while(true) {
|
||||
stellar_unicorn.set_pixel(0, 0, 255, 0, 0);
|
||||
stellar_unicorn.set_pixel(1, 1, 0, 255, 0);
|
||||
stellar_unicorn.set_pixel(2, 2, 0, 0, 255);
|
||||
}*/
|
||||
|
||||
while(true) {
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
stellar_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
stellar_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
float value = get(x, y);
|
||||
|
||||
graphics.set_pen(0, 0, 0);
|
||||
if(value > 0.5f) {
|
||||
graphics.set_pen(255, 255, 180);
|
||||
}else if(value > 0.4f) {
|
||||
graphics.set_pen(220, 160, 0);
|
||||
}else if(value > 0.3f) {
|
||||
graphics.set_pen(180, 30, 0);
|
||||
}else if(value > 0.22f) {
|
||||
graphics.set_pen(20, 20, 20);
|
||||
}
|
||||
|
||||
if(landscape) {
|
||||
graphics.pixel(Point(x, y));
|
||||
}else{
|
||||
graphics.pixel(Point(y, x));
|
||||
}
|
||||
|
||||
// update this pixel by averaging the below pixels
|
||||
float average = (get(x, y) + get(x, y + 2) + get(x, y + 1) + get(x - 1, y + 1) + get(x + 1, y + 1)) / 5.0f;
|
||||
|
||||
// damping factor to ensure flame tapers out towards the top of the displays
|
||||
average *= 0.95f;
|
||||
|
||||
// update the heat map with our newly averaged value
|
||||
set(x, y, average);
|
||||
}
|
||||
}
|
||||
|
||||
stellar_unicorn.update(&graphics);
|
||||
|
||||
// clear the bottom row and then add a new fire seed to it
|
||||
for(int x = 0; x < width; x++) {
|
||||
set(x, height - 1, 0.0f);
|
||||
}
|
||||
|
||||
// add a new random heat source
|
||||
int source_count = landscape ? 5 : 1;
|
||||
for(int c = 0; c < source_count; c++) {
|
||||
int px = (rand() % (width - 4)) + 2;
|
||||
set(px , height - 2, 1.0f);
|
||||
set(px + 1, height - 2, 1.0f);
|
||||
set(px - 1, height - 2, 1.0f);
|
||||
set(px , height - 1, 1.0f);
|
||||
set(px + 1, height - 1, 1.0f);
|
||||
set(px - 1, height - 1, 1.0f);
|
||||
}
|
||||
|
||||
sleep_ms(20);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "stellar_unicorn.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(16, 16, nullptr);
|
||||
StellarUnicorn stellar_unicorn;
|
||||
|
||||
// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel
|
||||
// Outputs are rgb in the range 0-255 for each channel
|
||||
void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
|
||||
float i = floor(h * 6.0f);
|
||||
float f = h * 6.0f - i;
|
||||
v *= 255.0f;
|
||||
uint8_t p = v * (1.0f - s);
|
||||
uint8_t q = v * (1.0f - f * s);
|
||||
uint8_t t = v * (1.0f - (1.0f - f) * s);
|
||||
|
||||
switch (int(i) % 6) {
|
||||
case 0: r = v; g = t; b = p; break;
|
||||
case 1: r = q; g = v; b = p; break;
|
||||
case 2: r = p; g = v; b = t; break;
|
||||
case 3: r = p; g = q; b = v; break;
|
||||
case 4: r = t; g = p; b = v; break;
|
||||
case 5: r = v; g = p; b = q; break;
|
||||
}
|
||||
}
|
||||
|
||||
struct blob_t {
|
||||
float x, y;
|
||||
float r;
|
||||
float dx, dy;
|
||||
};
|
||||
|
||||
constexpr int blob_count = 20;
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
stellar_unicorn.init();
|
||||
stellar_unicorn.set_brightness(0.5);
|
||||
|
||||
// randomise blob start positions, directions, and size
|
||||
std::array<blob_t, blob_count> blobs;
|
||||
for(auto &blob : blobs) {
|
||||
blob.x = rand() % 16;
|
||||
blob.y = rand() % 16;
|
||||
blob.r = ((rand() % 40) / 10.0f) + 5.0f;
|
||||
blob.dx = ((rand() % 2) / 10.0f) - 0.05f;
|
||||
blob.dy = ((rand() % 3) / 10.0f) - 0.1f;
|
||||
}
|
||||
|
||||
float hue = 0.0f;
|
||||
|
||||
while(true) {
|
||||
// allow user to adjust brightness
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
stellar_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
stellar_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
uint start_ms = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
// calculate the influence of each blob on the liquid based
|
||||
// on their distance to each pixel. this causes blobs to
|
||||
// "merge" into each other when we use fixed thresholds to
|
||||
// determine which colour to draw with
|
||||
float liquid[16][16] = {0.0f};
|
||||
for(auto &blob : blobs) {
|
||||
float r_sq = blob.r * blob.r;
|
||||
for(int y = 0; y < 16; y++) {
|
||||
for(int x = 0; x < 16; x++) {
|
||||
float d_sq = (x - blob.x) * (x - blob.x) + (y - blob.y) * (y - blob.y);
|
||||
if(d_sq <= r_sq) {
|
||||
// add this blobs influence to this pixel
|
||||
liquid[x][y] += 1.0f - (d_sq / r_sq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the blob positions
|
||||
for(auto &blob : blobs) {
|
||||
blob.x += blob.dx;
|
||||
blob.y += blob.dy;
|
||||
|
||||
// if we hit the edge then bounce!
|
||||
if(blob.x < 0.0f || blob.x >= 11.0f) {
|
||||
blob.dx *= -1.0f;
|
||||
}
|
||||
if(blob.y < 0.0f || blob.y >= 53.0f) {
|
||||
blob.dy *= -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// rotate the hue
|
||||
hue += 0.001f;
|
||||
|
||||
// calculate dark, medium, and bright shades for rendering the
|
||||
// lava
|
||||
uint8_t dark_r, dark_g, dark_b;
|
||||
from_hsv(hue, 1.0f, 0.3f, dark_r, dark_g, dark_b);
|
||||
uint8_t mid_r, mid_g, mid_b;
|
||||
from_hsv(hue, 1.0f, 0.6f, mid_r, mid_g, mid_b);
|
||||
uint8_t bright_r, bright_g, bright_b;
|
||||
from_hsv(hue, 1.0f, 1.0f, bright_r, bright_g, bright_b);
|
||||
|
||||
// clear the canvas
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.clear();
|
||||
|
||||
// render the lava
|
||||
for(int y = 0; y < 16; y++) {
|
||||
for(int x = 0; x < 16; x++) {
|
||||
float v = liquid[x][y];
|
||||
|
||||
// select a colour for this pixel based on how much
|
||||
// "blobfluence" there is at this position in the liquid
|
||||
if(v >= 1.5f) {
|
||||
graphics.set_pen(bright_r, bright_g, bright_b);
|
||||
graphics.pixel(Point(y, x));
|
||||
}else if(v >= 1.25f) {
|
||||
graphics.set_pen(mid_r, mid_g, mid_b);
|
||||
graphics.pixel(Point(y, x));
|
||||
}else if(v >= 1.0f) {
|
||||
graphics.set_pen(dark_r, dark_g, dark_b);
|
||||
graphics.pixel(Point(y, x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint end_ms = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
printf("rendering took %dms\n", end_ms - start_ms);
|
||||
|
||||
stellar_unicorn.update(&graphics);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "stellar_unicorn.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(16, 16, nullptr);
|
||||
StellarUnicorn stellar_unicorn;
|
||||
|
||||
// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel
|
||||
// Outputs are rgb in the range 0-255 for each channel
|
||||
void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
|
||||
float i = floor(h * 6.0f);
|
||||
float f = h * 6.0f - i;
|
||||
v *= 255.0f;
|
||||
uint8_t p = v * (1.0f - s);
|
||||
uint8_t q = v * (1.0f - f * s);
|
||||
uint8_t t = v * (1.0f - (1.0f - f) * s);
|
||||
|
||||
switch (int(i) % 6) {
|
||||
case 0: r = v; g = t; b = p; break;
|
||||
case 1: r = q; g = v; b = p; break;
|
||||
case 2: r = p; g = v; b = t; break;
|
||||
case 3: r = p; g = q; b = v; break;
|
||||
case 4: r = t; g = p; b = v; break;
|
||||
case 5: r = v; g = p; b = q; break;
|
||||
}
|
||||
}
|
||||
|
||||
void text(std::string t, Point p, float s = 1.0f, float a = 1.0f) {
|
||||
int w = graphics.measure_text(t, s);
|
||||
p.x += (53 / 2) - (w / 2);
|
||||
p.y += (11 / 2);
|
||||
graphics.text(t, Point(p.x, p.y), -1, s, a);
|
||||
//graphics.text(t, Point(p.x + 1, p.y), -1, s, a);
|
||||
//graphics.text(t, Point(p.x + 1, p.y + 1), -1, s, a);
|
||||
//graphics.text(t, Point(p.x, p.y + 1), -1, s, a);
|
||||
}
|
||||
|
||||
struct star_t {
|
||||
float dx, dy, x, y, a;
|
||||
|
||||
uint8_t brightness() {
|
||||
int b = a / 5;
|
||||
return b > 15 ? 15 : b;
|
||||
}
|
||||
};
|
||||
|
||||
void init_star(star_t &s) {
|
||||
s.x = ((rand() % 100) / 5.0f) - 10.0f;
|
||||
s.y = ((rand() % 100) / 10.0f) - 5.0f;
|
||||
|
||||
s.dx = s.x / 10.0f;
|
||||
s.dy = s.y / 10.0f;
|
||||
s.a = 0;
|
||||
}
|
||||
|
||||
void step_star(star_t &s) {
|
||||
s.x += s.dx;
|
||||
s.y += s.dy;
|
||||
s.a++;
|
||||
|
||||
if(s.a > 100) {
|
||||
init_star(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
uint8_t hue_map[16][3];
|
||||
for(int i = 0; i < 16; i++) {
|
||||
from_hsv(i / 16.0f, 1.0f, 1.0f, hue_map[i][0], hue_map[i][1], hue_map[i][2]);
|
||||
}
|
||||
|
||||
star_t stars[100];
|
||||
for(int i = 0; i < 100; i++) {
|
||||
init_star(stars[i]);
|
||||
stars[i].a = i;
|
||||
}
|
||||
|
||||
gpio_set_function(28, GPIO_FUNC_SIO);
|
||||
gpio_set_dir(28, GPIO_OUT);
|
||||
|
||||
for(int i = 0; i < 10; i++) {
|
||||
gpio_put(28, !gpio_get(28));
|
||||
sleep_ms(100);
|
||||
}
|
||||
sleep_ms(1000);
|
||||
|
||||
gpio_put(28,true);
|
||||
|
||||
stellar_unicorn.init();
|
||||
|
||||
|
||||
|
||||
float i = 0;
|
||||
|
||||
float hue_offset = 0.0f;
|
||||
|
||||
bool animate = true;
|
||||
|
||||
float stripe_width = 3.0f;
|
||||
float speed = 1.0f;
|
||||
float curve = 0.0f;
|
||||
|
||||
while(true) {
|
||||
|
||||
if(animate) {
|
||||
i += speed;
|
||||
}
|
||||
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_VOLUME_UP)) {
|
||||
curve += 0.05;
|
||||
if(hue_offset > 1.0f) hue_offset = 1.0f;
|
||||
}
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_VOLUME_DOWN)) {
|
||||
curve -= 0.05;
|
||||
if(hue_offset < 0.0f) hue_offset = 0.0f;
|
||||
}
|
||||
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
stellar_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
stellar_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_SLEEP)) {
|
||||
animate = false;
|
||||
}
|
||||
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_A)) {
|
||||
speed += 0.05f;
|
||||
speed = speed >= 10.0f ? 10.0f : speed;
|
||||
animate = true;
|
||||
}
|
||||
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_B)) {
|
||||
speed -= 0.05f;
|
||||
speed = speed <= 0.0f ? 0.0f : speed;
|
||||
animate = true;
|
||||
}
|
||||
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_C)) {
|
||||
stripe_width += 0.05f;
|
||||
stripe_width = stripe_width >= 10.0f ? 10.0f : stripe_width;
|
||||
}
|
||||
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_D)) {
|
||||
stripe_width -= 0.05f;
|
||||
stripe_width = stripe_width <= 1.0f ? 1.0f : stripe_width;
|
||||
}
|
||||
|
||||
for(int x = 0; x < 16; x++) {
|
||||
for(int y = 0; y < 16; y++) {
|
||||
int v = ((sin((x + y) / stripe_width + (sin((y * 3.1415927f * 2.0f) / 11.0f) * curve) + i / 15.0f) + 1.5f) / 2.5f) * 255.0f;
|
||||
|
||||
uint8_t r = (hue_map[x][0] * v) / 256;
|
||||
uint8_t g = (hue_map[x][1] * v) / 256;
|
||||
uint8_t b = (hue_map[x][2] * v) / 256;
|
||||
|
||||
graphics.set_pen(r, g, b);
|
||||
graphics.pixel(Point(x, y));
|
||||
}
|
||||
}
|
||||
stellar_unicorn.update(&graphics);
|
||||
|
||||
printf("%d\n", stellar_unicorn.light());
|
||||
sleep_ms(20);
|
||||
}
|
||||
|
||||
|
||||
|
||||
printf("done\n");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "stellar_unicorn.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(16, 16, nullptr);
|
||||
StellarUnicorn stellar_unicorn;
|
||||
|
||||
// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel
|
||||
// Outputs are rgb in the range 0-255 for each channel
|
||||
void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
|
||||
float i = floor(h * 6.0f);
|
||||
float f = h * 6.0f - i;
|
||||
v *= 255.0f;
|
||||
uint8_t p = v * (1.0f - s);
|
||||
uint8_t q = v * (1.0f - f * s);
|
||||
uint8_t t = v * (1.0f - (1.0f - f) * s);
|
||||
|
||||
switch (int(i) % 6) {
|
||||
case 0: r = v; g = t; b = p; break;
|
||||
case 1: r = q; g = v; b = p; break;
|
||||
case 2: r = p; g = v; b = t; break;
|
||||
case 3: r = p; g = q; b = v; break;
|
||||
case 4: r = t; g = p; b = v; break;
|
||||
case 5: r = v; g = p; b = q; break;
|
||||
}
|
||||
}
|
||||
|
||||
void text(std::string t, Point p, float s = 1.0f, float a = 1.0f) {
|
||||
int w = graphics.measure_text(t, s);
|
||||
p.x += (16 / 2) - (w / 2);
|
||||
p.y += (16 / 2);
|
||||
graphics.text(t, Point(p.x, p.y), -1, s, a);
|
||||
graphics.text(t, Point(p.x + 1, p.y), -1, s, a);
|
||||
graphics.text(t, Point(p.x + 1, p.y + 1), -1, s, a);
|
||||
graphics.text(t, Point(p.x, p.y + 1), -1, s, a);
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
uint8_t hue_map[16][3];
|
||||
for(int i = 0; i < 16; i++) {
|
||||
from_hsv(i / 16.0f, 1.0f, 0.5f, hue_map[i][0], hue_map[i][1], hue_map[i][2]);
|
||||
}
|
||||
|
||||
stellar_unicorn.init();
|
||||
|
||||
graphics.set_font("sans");
|
||||
uint i = 0;
|
||||
|
||||
while(true) {
|
||||
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
stellar_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
stellar_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.clear();
|
||||
|
||||
float s = 0.4f;//0.65f + (sin(i / 25.0f) * 0.15f);
|
||||
float a = 1.0f;// (sin(i / 25.0f) * 100.0f);
|
||||
|
||||
float x = (sin((i) / 50.0f) * 45.0f);
|
||||
float y = (cos((i) / 40.0f) * 2.5f);
|
||||
graphics.set_pen(255, 255, 255);
|
||||
text("Galactic Unicorn", Point(x, y), s, a);
|
||||
uint8_t *p = (uint8_t *)graphics.frame_buffer;
|
||||
for(size_t i = 0; i < 16 * 16; i++) {
|
||||
int x = i % 16;
|
||||
int y = i / 16;
|
||||
uint r = *p++;
|
||||
uint g = *p++;
|
||||
uint b = *p++;
|
||||
p++;
|
||||
|
||||
if(r > 0) {
|
||||
r = hue_map[x][0];
|
||||
g = hue_map[x][1];
|
||||
b = hue_map[x][2];
|
||||
}
|
||||
|
||||
graphics.set_pen(r, g, b);
|
||||
graphics.pixel(Point(x, y));
|
||||
}
|
||||
|
||||
stellar_unicorn.update(&graphics);
|
||||
}
|
||||
|
||||
printf("done\n");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "stellar_unicorn.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
PicoGraphics_PenRGB888 graphics(16, 16, nullptr);
|
||||
StellarUnicorn stellar_unicorn;
|
||||
|
||||
std::string message = "Pirate. Monkey. Robot. Ninja.";
|
||||
|
||||
// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel
|
||||
// Outputs are rgb in the range 0-255 for each channel
|
||||
void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
|
||||
float i = floor(h * 6.0f);
|
||||
float f = h * 6.0f - i;
|
||||
v *= 255.0f;
|
||||
uint8_t p = v * (1.0f - s);
|
||||
uint8_t q = v * (1.0f - f * s);
|
||||
uint8_t t = v * (1.0f - (1.0f - f) * s);
|
||||
|
||||
switch (int(i) % 6) {
|
||||
case 0: r = v; g = t; b = p; break;
|
||||
case 1: r = q; g = v; b = p; break;
|
||||
case 2: r = p; g = v; b = t; break;
|
||||
case 3: r = p; g = q; b = v; break;
|
||||
case 4: r = t; g = p; b = v; break;
|
||||
case 5: r = v; g = p; b = q; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
stellar_unicorn.init();
|
||||
|
||||
float scroll = -16.0f;
|
||||
|
||||
while(true) {
|
||||
//uint time_ms = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_BRIGHTNESS_UP)) {
|
||||
stellar_unicorn.adjust_brightness(+0.01);
|
||||
}
|
||||
if(stellar_unicorn.is_pressed(stellar_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
|
||||
stellar_unicorn.adjust_brightness(-0.01);
|
||||
}
|
||||
|
||||
int width = graphics.measure_text(message, 1);
|
||||
scroll += 0.125f;
|
||||
|
||||
if(scroll > width) {
|
||||
scroll = -16.0f;
|
||||
}
|
||||
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.clear();
|
||||
|
||||
uint8_t r = 0, g = 0, b = 0;
|
||||
from_hsv(scroll / 100.0f, 1.0f, 0.5f, r, g, b);
|
||||
graphics.set_pen(r, g, b);
|
||||
graphics.text(message, Point(0 - scroll, 5), -1, 0.55);
|
||||
|
||||
stellar_unicorn.update(&graphics);
|
||||
|
||||
sleep_ms(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -2,6 +2,7 @@ add_subdirectory(hershey_fonts)
|
|||
add_subdirectory(bitmap_fonts)
|
||||
add_subdirectory(breakout_dotmatrix)
|
||||
add_subdirectory(breakout_encoder)
|
||||
add_subdirectory(breakout_encoder_wheel)
|
||||
add_subdirectory(breakout_ioexpander)
|
||||
add_subdirectory(breakout_ltr559)
|
||||
add_subdirectory(breakout_rgbmatrix5x5)
|
||||
|
@ -35,9 +36,11 @@ add_subdirectory(motor2040)
|
|||
add_subdirectory(inventor2040w)
|
||||
add_subdirectory(adcfft)
|
||||
add_subdirectory(jpegdec)
|
||||
add_subdirectory(pngdec)
|
||||
add_subdirectory(inky_frame)
|
||||
add_subdirectory(inky_frame_7)
|
||||
add_subdirectory(galactic_unicorn)
|
||||
add_subdirectory(gfx_pack)
|
||||
add_subdirectory(interstate75)
|
||||
add_subdirectory(cosmic_unicorn)
|
||||
add_subdirectory(stellar_unicorn)
|
||||
|
|
|
@ -47,7 +47,7 @@ void ADCFFT::init() {
|
|||
|
||||
// Initialize the ADC harware
|
||||
// (resets it, enables the clock, spins until the hardware is ready)
|
||||
adc_init();
|
||||
if (!(adc_hw->cs & ADC_CS_EN_BITS)) adc_init();
|
||||
|
||||
// Select analog mux input (0...3 are GPIO 26, 27, 28, 29; 4 is temp sensor)
|
||||
adc_select_input(adc_channel);
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
add_library(automation INTERFACE)
|
||||
|
||||
target_sources(automation INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/automation.cpp
|
||||
)
|
||||
|
||||
target_include_directories(automation INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
#include(${PIMORONI_PICO_PATH}/drivers/analog/analog.cmake)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(automation INTERFACE pico_stdlib hardware_pwm hardware_i2c pimoroni_i2c analog)
|
||||
if(NOT TARGET analog)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/analog/analog.cmake)
|
||||
endif()
|
||||
|
||||
add_library(automation INTERFACE)
|
||||
|
||||
target_sources(automation INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/automation.cpp
|
||||
)
|
||||
|
||||
target_include_directories(automation INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(automation INTERFACE pico_stdlib hardware_pwm hardware_i2c pimoroni_i2c analog)
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
set(LIB_NAME badger2040)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
target_sources(${LIB_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp
|
||||
)
|
||||
|
||||
target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${LIB_NAME} INTERFACE bitmap_fonts hershey_fonts pico_stdlib hardware_pwm uc8151_legacy)
|
||||
if(NOT TARGET uc8151_legacy)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/uc8151_legacy/uc8151_legacy.cmake)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET bitmap_fonts)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../bitmap_fonts/bitmap_fonts.cmake)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET hershey_fonts)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../hershey_fonts/hershey_fonts.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME badger2040)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
target_sources(${LIB_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp
|
||||
)
|
||||
|
||||
target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${LIB_NAME} INTERFACE bitmap_fonts hershey_fonts uc8151_legacy pico_stdlib hardware_pwm)
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
#include "bitmap_fonts.hpp"
|
||||
|
||||
namespace bitmap {
|
||||
int32_t measure_character(const font_t *font, const char c, const uint8_t scale, unicode_sorta::codepage_t codepage) {
|
||||
int32_t measure_character(const font_t *font, const char c, const uint8_t scale, unicode_sorta::codepage_t codepage, bool fixed_width) {
|
||||
if(c < 32 || c > 127 + 64) { // + 64 char remappings defined in unicode_sorta.hpp
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(fixed_width) {
|
||||
return font->max_width * scale;
|
||||
}
|
||||
|
||||
uint8_t char_index = c;
|
||||
|
||||
if(char_index > 127) {
|
||||
|
@ -21,7 +25,7 @@ namespace bitmap {
|
|||
return font->widths[char_index] * scale;
|
||||
}
|
||||
|
||||
int32_t measure_text(const font_t *font, const std::string_view &t, const uint8_t scale, const uint8_t letter_spacing) {
|
||||
int32_t measure_text(const font_t *font, const std::string_view &t, const uint8_t scale, const uint8_t letter_spacing, bool fixed_width) {
|
||||
int32_t text_width = 0;
|
||||
unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195;
|
||||
for(auto c : t) {
|
||||
|
@ -31,14 +35,14 @@ namespace bitmap {
|
|||
} else if (c == unicode_sorta::PAGE_195_START) {
|
||||
continue;
|
||||
}
|
||||
text_width += measure_character(font, c, scale, codepage);
|
||||
text_width += measure_character(font, c, scale, codepage, fixed_width);
|
||||
text_width += letter_spacing * scale;
|
||||
codepage = unicode_sorta::PAGE_195; // Reset back to default
|
||||
}
|
||||
return text_width;
|
||||
}
|
||||
|
||||
void character(const font_t *font, rect_func rectangle, const char c, const int32_t x, const int32_t y, const uint8_t scale, unicode_sorta::codepage_t codepage) {
|
||||
void character(const font_t *font, rect_func rectangle, const char c, const int32_t x, const int32_t y, const uint8_t scale, int32_t rotation, unicode_sorta::codepage_t codepage) {
|
||||
if(c < 32 || c > 127 + 64) { // + 64 char remappings defined in unicode_sorta.hpp
|
||||
return;
|
||||
}
|
||||
|
@ -85,7 +89,8 @@ namespace bitmap {
|
|||
uint8_t accent_offset = char_index < 65 ? offset_upper : offset_lower;
|
||||
|
||||
// Offset our y position to account for our column canvas being 32 pixels
|
||||
int y_offset = y - (8 * scale);
|
||||
// this gives us 8 "pixels" of headroom above the letters for diacritic marks
|
||||
int font_offset = (8 * scale);
|
||||
|
||||
// Iterate through each horizontal column of font (and accent) data
|
||||
for(uint8_t cx = 0; cx < font->widths[char_index]; cx++) {
|
||||
|
@ -94,6 +99,8 @@ namespace bitmap {
|
|||
// We shift the char down 8 pixels to make room for an accent above.
|
||||
uint32_t data = *d << 8;
|
||||
|
||||
int32_t o_x = cx * scale;
|
||||
|
||||
// For fonts that are taller than 8 pixels (up to 16) they need two bytes
|
||||
if(two_bytes_per_column) {
|
||||
d++;
|
||||
|
@ -109,7 +116,28 @@ namespace bitmap {
|
|||
// Draw the 32 pixel column
|
||||
for(uint8_t cy = 0; cy < 32; cy++) {
|
||||
if((1U << cy) & data) {
|
||||
rectangle(x + (cx * scale), y_offset + (cy * scale), scale, scale);
|
||||
int32_t o_y = cy * scale;
|
||||
int32_t px = 0;
|
||||
int32_t py = 0;
|
||||
switch (rotation) {
|
||||
case 0:
|
||||
px = x + o_x;
|
||||
py = y - font_offset + o_y;
|
||||
break;
|
||||
case 90:
|
||||
px = x + font_offset - o_y;
|
||||
py = y + o_x;
|
||||
break;
|
||||
case 180:
|
||||
px = x - o_x;
|
||||
py = y + font_offset - o_y;
|
||||
break;
|
||||
case 270:
|
||||
px = x - font_offset + o_y;
|
||||
py = y - o_x;
|
||||
break;
|
||||
}
|
||||
rectangle(px, py, scale, scale);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,10 +147,14 @@ namespace bitmap {
|
|||
}
|
||||
}
|
||||
|
||||
void text(const font_t *font, rect_func rectangle, const std::string_view &t, const int32_t x, const int32_t y, const int32_t wrap, const uint8_t scale, const uint8_t letter_spacing) {
|
||||
uint32_t co = 0, lo = 0; // character and line (if wrapping) offset
|
||||
void text(const font_t *font, rect_func rectangle, const std::string_view &t, const int32_t x, const int32_t y, const int32_t wrap, const uint8_t scale, const uint8_t letter_spacing, bool fixed_width, int32_t rotation) {
|
||||
uint32_t char_offset = 0;
|
||||
uint32_t line_offset = 0; // line (if wrapping) offset
|
||||
unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195;
|
||||
|
||||
int32_t space_width = measure_character(font, ' ', scale, codepage, fixed_width);
|
||||
space_width += letter_spacing * scale;
|
||||
|
||||
size_t i = 0;
|
||||
while(i < t.length()) {
|
||||
// find length of current word
|
||||
|
@ -148,38 +180,53 @@ namespace bitmap {
|
|||
} else if (t[j] == unicode_sorta::PAGE_195_START) {
|
||||
continue;
|
||||
}
|
||||
word_width += measure_character(font, t[j], scale, codepage);
|
||||
word_width += measure_character(font, t[j], scale, codepage, fixed_width);
|
||||
word_width += letter_spacing * scale;
|
||||
codepage = unicode_sorta::PAGE_195;
|
||||
}
|
||||
|
||||
// if this word would exceed the wrap limit then
|
||||
// move to the next line
|
||||
if(co != 0 && co + word_width > (uint32_t)wrap) {
|
||||
co = 0;
|
||||
lo += (font->height + 1) * scale;
|
||||
if(char_offset != 0 && char_offset + word_width > (uint32_t)wrap) {
|
||||
char_offset = 0;
|
||||
line_offset += (font->height + 1) * scale;
|
||||
}
|
||||
|
||||
// draw word
|
||||
for(size_t j = i; j < next_break; j++) {
|
||||
for(size_t j = i; j < std::min(next_break + 1, t.length()); j++) {
|
||||
if (t[j] == unicode_sorta::PAGE_194_START) {
|
||||
codepage = unicode_sorta::PAGE_194;
|
||||
continue;
|
||||
} else if (t[j] == unicode_sorta::PAGE_195_START) {
|
||||
continue;
|
||||
}
|
||||
if (t[j] == '\n') {
|
||||
lo += (font->height + 1) * scale;
|
||||
co = 0;
|
||||
if (t[j] == '\n') { // Linebreak
|
||||
line_offset += (font->height + 1) * scale;
|
||||
char_offset = 0;
|
||||
} else if (t[j] == ' ') { // Space
|
||||
char_offset += space_width;
|
||||
} else {
|
||||
character(font, rectangle, t[j], x + co, y + lo, scale, codepage);
|
||||
co += measure_character(font, t[j], scale, codepage);
|
||||
co += letter_spacing * scale;
|
||||
switch(rotation) {
|
||||
case 0:
|
||||
character(font, rectangle, t[j], x + char_offset, y + line_offset, scale, rotation, codepage);
|
||||
break;
|
||||
case 90:
|
||||
character(font, rectangle, t[j], x - line_offset, y + char_offset, scale, rotation, codepage);
|
||||
break;
|
||||
case 180:
|
||||
character(font, rectangle, t[j], x - char_offset, y - line_offset, scale, rotation, codepage);
|
||||
break;
|
||||
case 270:
|
||||
character(font, rectangle, t[j], x + line_offset, y - char_offset, scale, rotation, codepage);
|
||||
break;
|
||||
}
|
||||
char_offset += measure_character(font, t[j], scale, codepage, fixed_width);
|
||||
char_offset += letter_spacing * scale;
|
||||
}
|
||||
codepage = unicode_sorta::PAGE_195;
|
||||
}
|
||||
|
||||
// move character offset to end of word and add a space
|
||||
co += font->widths[0] * scale;
|
||||
// move character offset
|
||||
i = next_break += 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@ namespace bitmap {
|
|||
|
||||
typedef std::function<void(int32_t x, int32_t y, int32_t w, int32_t h)> rect_func;
|
||||
|
||||
int32_t measure_character(const font_t *font, const char c, const uint8_t scale, unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195);
|
||||
int32_t measure_text(const font_t *font, const std::string_view &t, const uint8_t scale = 2, const uint8_t letter_spacing = 1);
|
||||
int32_t measure_character(const font_t *font, const char c, const uint8_t scale, unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195, bool fixed_width = false);
|
||||
int32_t measure_text(const font_t *font, const std::string_view &t, const uint8_t scale = 2, const uint8_t letter_spacing = 1, bool fixed_width = false);
|
||||
|
||||
void character(const font_t *font, rect_func rectangle, const char c, const int32_t x, const int32_t y, const uint8_t scale = 2, unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195);
|
||||
void text(const font_t *font, rect_func rectangle, const std::string_view &t, const int32_t x, const int32_t y, const int32_t wrap, const uint8_t scale = 2, const uint8_t letter_spacing = 1);
|
||||
void character(const font_t *font, rect_func rectangle, const char c, const int32_t x, const int32_t y, const uint8_t scale = 2, int32_t rotation = 0, unicode_sorta::codepage_t codepage = unicode_sorta::PAGE_195);
|
||||
void text(const font_t *font, rect_func rectangle, const std::string_view &t, const int32_t x, const int32_t y, const int32_t wrap, const uint8_t scale = 2, const uint8_t letter_spacing = 1, bool fixed_width = false, int32_t rotation = 0);
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
const bitmap::font_t font8 {
|
||||
.height = 8,
|
||||
.max_width = 6,
|
||||
.max_width = 5,
|
||||
.widths = {
|
||||
3, 1, 3, 5, 4, 4, 4, 1, 3, 3, 3, 3, 2, 3, 2, 4,
|
||||
4, 3, 4, 4, 4, 4, 4, 4, 4, 4, 1, 2, 3, 3, 3, 4,
|
||||
|
@ -17,123 +17,123 @@ const bitmap::font_t font8 {
|
|||
5, 4, 4, 5, 4, 4, 4, 4, 3
|
||||
},
|
||||
.data = {
|
||||
0x00,0x00,0x00,0x00,0x00,0x00, //
|
||||
0x5f,0x00,0x00,0x00,0x00,0x00, // !
|
||||
0x03,0x00,0x03,0x00,0x00,0x00, // "
|
||||
0x28,0x7c,0x28,0x7c,0x28,0x00, // #
|
||||
0x24,0x7a,0x2f,0x12,0x00,0x00, // $
|
||||
0x66,0x10,0x08,0x66,0x00,0x00, // %
|
||||
0x36,0x49,0x49,0x7c,0x00,0x00, // &
|
||||
0x03,0x00,0x00,0x00,0x00,0x00, // '
|
||||
0x1c,0x22,0x41,0x00,0x00,0x00, // (
|
||||
0x41,0x22,0x1c,0x00,0x00,0x00, // )
|
||||
0x54,0x38,0x54,0x00,0x00,0x00, // *
|
||||
0x10,0x38,0x10,0x00,0x00,0x00, // +
|
||||
0x80,0x60,0x00,0x00,0x00,0x00, // ,
|
||||
0x10,0x10,0x10,0x00,0x00,0x00, // -
|
||||
0x60,0x60,0x00,0x00,0x00,0x00, // .
|
||||
0x60,0x18,0x06,0x01,0x00,0x00, // /
|
||||
0x3e,0x41,0x41,0x3e,0x00,0x00, // 0
|
||||
0x42,0x7f,0x40,0x00,0x00,0x00, // 1
|
||||
0x62,0x51,0x49,0x46,0x00,0x00, // 2
|
||||
0x21,0x49,0x4d,0x33,0x00,0x00, // 3
|
||||
0x18,0x16,0x11,0x7f,0x00,0x00, // 4
|
||||
0x4f,0x49,0x49,0x31,0x00,0x00, // 5
|
||||
0x3c,0x4a,0x49,0x30,0x00,0x00, // 6
|
||||
0x01,0x61,0x19,0x07,0x00,0x00, // 7
|
||||
0x36,0x49,0x49,0x36,0x00,0x00, // 8
|
||||
0x06,0x49,0x29,0x1e,0x00,0x00, // 9
|
||||
0x33,0x00,0x00,0x00,0x00,0x00, // :
|
||||
0x80,0x6c,0x00,0x00,0x00,0x00, // ;
|
||||
0x10,0x28,0x44,0x00,0x00,0x00, // <
|
||||
0x28,0x28,0x28,0x00,0x00,0x00, // =
|
||||
0x44,0x28,0x10,0x00,0x00,0x00, // >
|
||||
0x02,0x51,0x09,0x06,0x00,0x00, // ?
|
||||
0x3e,0x49,0x55,0x5e,0x00,0x00, // @
|
||||
0x7e,0x09,0x09,0x7e,0x00,0x00, // A
|
||||
0x7f,0x49,0x49,0x36,0x00,0x00, // B
|
||||
0x3e,0x41,0x41,0x22,0x00,0x00, // C
|
||||
0x7f,0x41,0x41,0x3e,0x00,0x00, // D
|
||||
0x7f,0x49,0x49,0x41,0x00,0x00, // E
|
||||
0x7f,0x09,0x09,0x01,0x00,0x00, // F
|
||||
0x3e,0x41,0x49,0x79,0x00,0x00, // G
|
||||
0x7f,0x08,0x08,0x7f,0x00,0x00, // H
|
||||
0x41,0x7f,0x41,0x00,0x00,0x00, // I
|
||||
0x30,0x40,0x40,0x3f,0x00,0x00, // J
|
||||
0x7f,0x08,0x14,0x63,0x00,0x00, // K
|
||||
0x7f,0x40,0x40,0x40,0x00,0x00, // L
|
||||
0x7f,0x02,0x04,0x02,0x7f,0x00, // M
|
||||
0x7f,0x02,0x04,0x7f,0x00,0x00, // N
|
||||
0x3e,0x41,0x41,0x3e,0x00,0x00, // O
|
||||
0x7f,0x09,0x09,0x06,0x00,0x00, // P
|
||||
0x3e,0x41,0x21,0x5e,0x00,0x00, // Q
|
||||
0x7f,0x09,0x19,0x66,0x00,0x00, // R
|
||||
0x46,0x49,0x49,0x31,0x00,0x00, // S
|
||||
0x01,0x01,0x7f,0x01,0x01,0x00, // T
|
||||
0x3f,0x40,0x40,0x3f,0x00,0x00, // U
|
||||
0x7f,0x40,0x20,0x1f,0x00,0x00, // V
|
||||
0x3f,0x40,0x20,0x40,0x3f,0x00, // W
|
||||
0x77,0x08,0x08,0x77,0x00,0x00, // X
|
||||
0x47,0x48,0x48,0x3f,0x00,0x00, // Y
|
||||
0x71,0x49,0x45,0x43,0x00,0x00, // Z
|
||||
0x7f,0x41,0x00,0x00,0x00,0x00, // [
|
||||
0x01,0x06,0x18,0x60,0x00,0x00, // "\"
|
||||
0x41,0x7f,0x00,0x00,0x00,0x00, // ]
|
||||
0x04,0x02,0x04,0x00,0x00,0x00, // ^
|
||||
0x40,0x40,0x40,0x00,0x00,0x00, // _
|
||||
0x01,0x01,0x00,0x00,0x00,0x00, // `
|
||||
0x20,0x54,0x54,0x78,0x00,0x00, // a
|
||||
0x7f,0x44,0x44,0x38,0x00,0x00, // b
|
||||
0x38,0x44,0x44,0x28,0x00,0x00, // c
|
||||
0x38,0x44,0x44,0x7f,0x00,0x00, // d
|
||||
0x38,0x54,0x54,0x58,0x00,0x00, // e
|
||||
0x7e,0x09,0x09,0x02,0x00,0x00, // f
|
||||
0x18,0xa4,0xa4,0x7c,0x00,0x00, // g
|
||||
0x7f,0x04,0x04,0x78,0x00,0x00, // h
|
||||
0x04,0x7d,0x40,0x00,0x00,0x00, // i
|
||||
0x60,0x80,0x80,0x7d,0x00,0x00, // j
|
||||
0x7f,0x10,0x28,0x44,0x00,0x00, // k
|
||||
0x01,0x7f,0x40,0x00,0x00,0x00, // l
|
||||
0x7c,0x04,0x78,0x04,0x78,0x00, // m
|
||||
0x7c,0x04,0x04,0x78,0x00,0x00, // n
|
||||
0x38,0x44,0x44,0x38,0x00,0x00, // o
|
||||
0xfc,0x24,0x24,0x18,0x00,0x00, // p
|
||||
0x18,0x24,0x24,0xfc,0x00,0x00, // q
|
||||
0x7c,0x08,0x04,0x04,0x00,0x00, // r
|
||||
0x48,0x54,0x54,0x24,0x00,0x00, // s
|
||||
0x3e,0x44,0x44,0x20,0x00,0x00, // t
|
||||
0x3c,0x40,0x40,0x7c,0x00,0x00, // u
|
||||
0x7c,0x40,0x20,0x1c,0x00,0x00, // v
|
||||
0x3c,0x40,0x20,0x40,0x3c,0x00, // w
|
||||
0x6c,0x10,0x10,0x6c,0x00,0x00, // x
|
||||
0x1c,0xa0,0xa0,0x7c,0x00,0x00, // y
|
||||
0x64,0x54,0x4c,0x00,0x00,0x00, // z
|
||||
0x08,0x3e,0x41,0x00,0x00,0x00, // {
|
||||
0x7f,0x00,0x00,0x00,0x00,0x00, // |
|
||||
0x41,0x3e,0x08,0x00,0x00,0x00, // }
|
||||
0x08,0x04,0x08,0x04,0x00,0x00, // ~
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00, //
|
||||
0x5f,0x00,0x00,0x00,0x00, // !
|
||||
0x03,0x00,0x03,0x00,0x00, // "
|
||||
0x28,0x7c,0x28,0x7c,0x28, // #
|
||||
0x24,0x7a,0x2f,0x12,0x00, // $
|
||||
0x66,0x10,0x08,0x66,0x00, // %
|
||||
0x36,0x49,0x49,0x7c,0x00, // &
|
||||
0x03,0x00,0x00,0x00,0x00, // '
|
||||
0x1c,0x22,0x41,0x00,0x00, // (
|
||||
0x41,0x22,0x1c,0x00,0x00, // )
|
||||
0x54,0x38,0x54,0x00,0x00, // *
|
||||
0x10,0x38,0x10,0x00,0x00, // +
|
||||
0x80,0x60,0x00,0x00,0x00, // ,
|
||||
0x10,0x10,0x10,0x00,0x00, // -
|
||||
0x60,0x60,0x00,0x00,0x00, // .
|
||||
0x60,0x18,0x06,0x01,0x00, // /
|
||||
0x3e,0x41,0x41,0x3e,0x00, // 0
|
||||
0x42,0x7f,0x40,0x00,0x00, // 1
|
||||
0x62,0x51,0x49,0x46,0x00, // 2
|
||||
0x21,0x49,0x4d,0x33,0x00, // 3
|
||||
0x18,0x16,0x11,0x7f,0x00, // 4
|
||||
0x4f,0x49,0x49,0x31,0x00, // 5
|
||||
0x3c,0x4a,0x49,0x30,0x00, // 6
|
||||
0x01,0x61,0x19,0x07,0x00, // 7
|
||||
0x36,0x49,0x49,0x36,0x00, // 8
|
||||
0x06,0x49,0x29,0x1e,0x00, // 9
|
||||
0x33,0x00,0x00,0x00,0x00, // :
|
||||
0x80,0x6c,0x00,0x00,0x00, // ;
|
||||
0x10,0x28,0x44,0x00,0x00, // <
|
||||
0x28,0x28,0x28,0x00,0x00, // =
|
||||
0x44,0x28,0x10,0x00,0x00, // >
|
||||
0x02,0x51,0x09,0x06,0x00, // ?
|
||||
0x3e,0x49,0x55,0x5e,0x00, // @
|
||||
0x7e,0x09,0x09,0x7e,0x00, // A
|
||||
0x7f,0x49,0x49,0x36,0x00, // B
|
||||
0x3e,0x41,0x41,0x22,0x00, // C
|
||||
0x7f,0x41,0x41,0x3e,0x00, // D
|
||||
0x7f,0x49,0x49,0x41,0x00, // E
|
||||
0x7f,0x09,0x09,0x01,0x00, // F
|
||||
0x3e,0x41,0x49,0x79,0x00, // G
|
||||
0x7f,0x08,0x08,0x7f,0x00, // H
|
||||
0x41,0x7f,0x41,0x00,0x00, // I
|
||||
0x30,0x40,0x40,0x3f,0x00, // J
|
||||
0x7f,0x08,0x14,0x63,0x00, // K
|
||||
0x7f,0x40,0x40,0x40,0x00, // L
|
||||
0x7f,0x02,0x04,0x02,0x7f, // M
|
||||
0x7f,0x02,0x04,0x7f,0x00, // N
|
||||
0x3e,0x41,0x41,0x3e,0x00, // O
|
||||
0x7f,0x09,0x09,0x06,0x00, // P
|
||||
0x3e,0x41,0x21,0x5e,0x00, // Q
|
||||
0x7f,0x09,0x19,0x66,0x00, // R
|
||||
0x46,0x49,0x49,0x31,0x00, // S
|
||||
0x01,0x01,0x7f,0x01,0x01, // T
|
||||
0x3f,0x40,0x40,0x3f,0x00, // U
|
||||
0x7f,0x40,0x20,0x1f,0x00, // V
|
||||
0x3f,0x40,0x20,0x40,0x3f, // W
|
||||
0x77,0x08,0x08,0x77,0x00, // X
|
||||
0x47,0x48,0x48,0x3f,0x00, // Y
|
||||
0x71,0x49,0x45,0x43,0x00, // Z
|
||||
0x7f,0x41,0x00,0x00,0x00, // [
|
||||
0x01,0x06,0x18,0x60,0x00, // "\"
|
||||
0x41,0x7f,0x00,0x00,0x00, // ]
|
||||
0x04,0x02,0x04,0x00,0x00, // ^
|
||||
0x40,0x40,0x40,0x00,0x00, // _
|
||||
0x01,0x01,0x00,0x00,0x00, // `
|
||||
0x20,0x54,0x54,0x78,0x00, // a
|
||||
0x7f,0x44,0x44,0x38,0x00, // b
|
||||
0x38,0x44,0x44,0x28,0x00, // c
|
||||
0x38,0x44,0x44,0x7f,0x00, // d
|
||||
0x38,0x54,0x54,0x58,0x00, // e
|
||||
0x7e,0x09,0x09,0x02,0x00, // f
|
||||
0x18,0xa4,0xa4,0x7c,0x00, // g
|
||||
0x7f,0x04,0x04,0x78,0x00, // h
|
||||
0x04,0x7d,0x40,0x00,0x00, // i
|
||||
0x60,0x80,0x80,0x7d,0x00, // j
|
||||
0x7f,0x10,0x28,0x44,0x00, // k
|
||||
0x01,0x7f,0x40,0x00,0x00, // l
|
||||
0x7c,0x04,0x78,0x04,0x78, // m
|
||||
0x7c,0x04,0x04,0x78,0x00, // n
|
||||
0x38,0x44,0x44,0x38,0x00, // o
|
||||
0xfc,0x24,0x24,0x18,0x00, // p
|
||||
0x18,0x24,0x24,0xfc,0x00, // q
|
||||
0x7c,0x08,0x04,0x04,0x00, // r
|
||||
0x48,0x54,0x54,0x24,0x00, // s
|
||||
0x3e,0x44,0x44,0x20,0x00, // t
|
||||
0x3c,0x40,0x40,0x7c,0x00, // u
|
||||
0x7c,0x40,0x20,0x1c,0x00, // v
|
||||
0x3c,0x40,0x20,0x40,0x3c, // w
|
||||
0x6c,0x10,0x10,0x6c,0x00, // x
|
||||
0x1c,0xa0,0xa0,0x7c,0x00, // y
|
||||
0x64,0x54,0x4c,0x00,0x00, // z
|
||||
0x08,0x3e,0x41,0x00,0x00, // {
|
||||
0x7f,0x00,0x00,0x00,0x00, // |
|
||||
0x41,0x3e,0x08,0x00,0x00, // }
|
||||
0x08,0x04,0x08,0x04,0x00, // ~
|
||||
0x00,0x00,0x00,0x00,0x00,
|
||||
// Extra
|
||||
0x7e,0x09,0x7f,0x49,0x49,0x00, // Æ
|
||||
0x7e,0x24,0x24,0x18,0x00,0x00, // Þ
|
||||
0x7e,0x09,0x49,0x36,0x00,0x00, // ß
|
||||
0x20,0x54,0x78,0x54,0x58,0x00, // æ
|
||||
0x7f,0x24,0x24,0x18,0x00,0x00, // þ
|
||||
0x08,0x7e,0x49,0x41,0x00,0x00, // £
|
||||
0x47,0x48,0x48,0x3f,0x00,0x00, // ¥
|
||||
0x38,0x44,0x44,0x28,0x00,0x00, // ©
|
||||
0x02,0x05,0x02,0x00,0x00,0x00, // °
|
||||
0x7e,0x09,0x7f,0x49,0x49, // Æ
|
||||
0x7e,0x24,0x24,0x18,0x00, // Þ
|
||||
0x7e,0x09,0x49,0x36,0x00, // ß
|
||||
0x20,0x54,0x78,0x54,0x58, // æ
|
||||
0x7f,0x24,0x24,0x18,0x00, // þ
|
||||
0x08,0x7e,0x49,0x41,0x00, // £
|
||||
0x47,0x48,0x48,0x3f,0x00, // ¥
|
||||
0x38,0x44,0x44,0x28,0x00, // ©
|
||||
0x02,0x05,0x02,0x00,0x00, // °
|
||||
// Accents + Offsets
|
||||
// All chars are shifted 8px down into a 32 pixel canvas for combining with accents.
|
||||
// Accent shift values (the first two numbers in each line below) move the accent down to meet them.
|
||||
// These are the shift values for lower and UPPER case letters respectively.
|
||||
6,4, 0x00,0x00,0x01,0x02,0x00,0x00, // Grave
|
||||
6,4, 0x00,0x00,0x02,0x01,0x00,0x00, // Acute
|
||||
6,4, 0x00,0x02,0x01,0x02,0x00,0x00, // Circumflex
|
||||
6,4, 0x00,0x01,0x02,0x01,0x02,0x00, // Tilde
|
||||
6,4, 0x00,0x01,0x00,0x01,0x00,0x00, // Diaresis
|
||||
6,4, 0x00,0x02,0x05,0x02,0x00,0x00, // Ring Above
|
||||
6,4, 0x00,0x80,0x40,0x00,0x00,0x00, // Stroke
|
||||
9,9, 0x00,0x00,0xa0,0x40,0x00,0x00 // Cedilla
|
||||
6,4, 0x00,0x00,0x01,0x02,0x00, // Grave
|
||||
6,4, 0x00,0x00,0x02,0x01,0x00, // Acute
|
||||
6,4, 0x00,0x02,0x01,0x02,0x00, // Circumflex
|
||||
6,4, 0x00,0x01,0x02,0x01,0x02, // Tilde
|
||||
6,4, 0x00,0x01,0x00,0x01,0x00, // Diaresis
|
||||
6,4, 0x00,0x02,0x05,0x02,0x00, // Ring Above
|
||||
6,4, 0x00,0x80,0x40,0x00,0x00, // Stroke
|
||||
9,9, 0x00,0x00,0xa0,0x40,0x00 // Cedilla
|
||||
}
|
||||
};
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET as7262)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/as7262/as7262.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_as7262)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET bh1745)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/bh1745/bh1745.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_bh1745)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET ltp305)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/ltp305/ltp305.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_dotmatrix)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET ioexpander)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/ioexpander/ioexpander.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_encoder)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
include(breakout_encoder_wheel.cmake)
|
|
@ -0,0 +1,334 @@
|
|||
# RGB Encoder Wheel Breakout (C++) <!-- omit in toc -->
|
||||
|
||||
This is the C++ library reference for the [Pimoroni RGB Encoder Wheel Breakout](https://shop.pimoroni.com/products/rgb-encoder-wheel-breakout).
|
||||
|
||||
|
||||
## Table of Content <!-- omit in toc -->
|
||||
- [Getting Started](#getting-started)
|
||||
- [Reading the Buttons](#reading-the-buttons)
|
||||
- [Reading the Encoder](#reading-the-encoder)
|
||||
- [Count and Angle](#count-and-angle)
|
||||
- [Count Delta](#count-delta)
|
||||
- [Step and Turn](#step-and-turn)
|
||||
- [Changing the Direction](#changing-the-direction)
|
||||
- [Resetting to Zero](#resetting-to-zero)
|
||||
- [LEDs](#leds)
|
||||
- [Setting an LED](#setting-an-led)
|
||||
- [RGB](#rgb)
|
||||
- [HSV](#hsv)
|
||||
- [Clear all LEDs](#clear-all-leds)
|
||||
- [Showing](#showing)
|
||||
- [GPIOs](#gpios)
|
||||
- [Setup](#setup)
|
||||
- [Mode](#mode)
|
||||
- [As Input or ADC](#as-input-or-adc)
|
||||
- [As Output](#as-output)
|
||||
- [As PWM](#as-pwm)
|
||||
- [Delayed Loading](#delayed-loading)
|
||||
- [Limitations](#limitations)
|
||||
- [Function Reference](#function-reference)
|
||||
- [Constants Reference](#constants-reference)
|
||||
- [Address Constants](#address-constants)
|
||||
- [Value Constants](#value-constants)
|
||||
- [Button Constants](#button-constants)
|
||||
- [GPIO Constants](#gpio-constants)
|
||||
- [Count Constants](#count-constants)
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
||||
To start coding for your Encoder Wheel breakout, you will first need to create an object for accessing the I2C bus that the breakout is connected to. The easiest way to do this is via the `PimoroniI2C` class, with one of the handy pin constants from `pimoroni`, like so:
|
||||
|
||||
```c++
|
||||
#include "pimoroni_i2c.hpp"
|
||||
using namespace pimoroni;
|
||||
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
```
|
||||
|
||||
This creates a `i2c` object that can be passed into the Encoder Wheel's class as part of its creation:
|
||||
```c++
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
using namespace encoderwheel;
|
||||
|
||||
BreakoutEncoderWheel wheel(&i2c);
|
||||
```
|
||||
|
||||
The above lines of code import the `BreakoutEncoderWheel` class and create an instance of it, called `wheel`. This will be used in the rest of the examples going forward.
|
||||
|
||||
|
||||
## Reading the Buttons
|
||||
|
||||
EncoderWheel has five buttons, covering up, down, left, right, and centre. These can be read using the `.pressed(button)` function, which accepts a button number between `0` and `4`. For convenience, each button can be referred to using these constants:
|
||||
|
||||
* `UP` = `0`
|
||||
* `DOWN` = `1`
|
||||
* `LEFT` = `2`
|
||||
* `RIGHT` = `3`
|
||||
* `CENTRE` = `4`
|
||||
|
||||
For example, to read the centre button you would write:
|
||||
|
||||
```c++
|
||||
bool centre_state = wheel.pressed(CENTRE);
|
||||
```
|
||||
|
||||
You can also get the number of buttons using the `NUM_BUTTONS` constant.
|
||||
|
||||
|
||||
## Reading the Encoder
|
||||
|
||||
Within the directional buttons of the Encoder Wheel breakout is a rotary encoder with 24 counts per revolution.
|
||||
|
||||
### Count and Angle
|
||||
|
||||
The current count can be read by calling `.count()`. It can also be read back as either the number of `.revolutions()` of the encoder, or the angle in `.degrees()` or `.radians()`.
|
||||
|
||||
Be aware that the count is stored as an integer, if it is continually increased or decreased it will eventually wrap at `+32767` and `-32768`. This will cause a jump in the returned by `.revolutions()`, `degrees()` and `.radians()`, that will need handling by your code.
|
||||
|
||||
|
||||
### Count Delta
|
||||
|
||||
Often you are not interested in the exact count that the encoder is at, but rather if the count has changed since the last time you checked. This change can be read by calling `.delta()` at regular intervals. The returned value can then be used with a check in code, for the value being non-zero.
|
||||
|
||||
|
||||
### Step and Turn
|
||||
|
||||
Sometimes it can be useful to know the encoder's position in the form of which step it is at and how many turns have occurred. The current step can be read by calling `.step()`, which returns a value from `0` to `23`, and the number of turns can be read by calling `.turn()`.
|
||||
|
||||
These functions differ from reading the `.count()` or `.revolutions()` by using separate counters, and so avoid the wrapping issue that these functions experience.
|
||||
|
||||
|
||||
### Changing the Direction
|
||||
|
||||
The counting direction of an encoder can be changed by calling `.direction(REVERSED_DIR)` at any time in code. The `REVERSED_DIR` constant comes from `pimoroni_common.hpp`. There is also a `NORMAL_DIR` constant, though this is the default.
|
||||
|
||||
|
||||
### Resetting to Zero
|
||||
|
||||
There are times where an encoder's count (and related values) need to be reset back to zero. This can be done by calling `.zero()`.
|
||||
|
||||
|
||||
## LEDs
|
||||
|
||||
The Encoder Wheel breakout features 24 RGB LEDs arranged in a ring around the wheel. This is the same number as there are steps on the wheel, letting you use the LEDs to show the current step of the wheel.
|
||||
|
||||
|
||||
### Setting an LED
|
||||
|
||||
You can set the colour of a LED on the ring in either the RGB colourspace, or HSV (Hue, Saturation, Value). HSV is useful for creating rainbow patterns.
|
||||
|
||||
#### RGB
|
||||
|
||||
Set the first LED - `0` - to Purple `255, 0, 255`:
|
||||
|
||||
```c++
|
||||
wheel.set_rgb(0, 255, 0, 255);
|
||||
```
|
||||
|
||||
#### HSV
|
||||
|
||||
Set the first LED - `0` - to Red `0.0`:
|
||||
|
||||
```c++
|
||||
wheel.set_hsv(0, 0.0f, 1.0f, 1.0f);
|
||||
```
|
||||
|
||||
|
||||
### Clear all LEDs
|
||||
|
||||
To turn off all the LEDs, the function `.clear()` can be called. This simply goes through each LED and sets its RGB colour to black, making them emit no light.
|
||||
|
||||
This function is useful to have at the end of your code to turn the lights off, otherwise they will continue to show the last colours they were given.
|
||||
|
||||
|
||||
### Showing
|
||||
|
||||
Changes to the LEDs do not get applied immediately, due to the amount of I2C communication involved. As such, to have the LEDs show what they have been set to after calling the `.set_rgb()`, `.set_hsv()`, and `.clear()` functions, a specific call to `.show()` needs to be made.
|
||||
|
||||
|
||||
## GPIOs
|
||||
|
||||
There are three spare GPIO pins on the edge of Encoder Wheel. These can be used as digital outputs, pwm outputs, digital inputs, and analog inputs.
|
||||
|
||||
|
||||
### Setup
|
||||
|
||||
To start using a GPIO pin, one of the handy constants from the `encoderwheel` namespace can be used to reference them (see [GPIO Constants](#gpio-constants)).
|
||||
|
||||
Then you need to import the constants for the pin mode to use. These are on the `IOExpander` class that Encoder Wheel is based on.
|
||||
|
||||
```c++
|
||||
#import "breakout_ioexpander.hpp"
|
||||
using namespace pimoroni;
|
||||
|
||||
// For input
|
||||
IOExpander::PIN_IN; // or PIN_IN_PU of a pull-up is wanted
|
||||
|
||||
// For output
|
||||
IOExpander::PIN_OUT;
|
||||
|
||||
// For PWM
|
||||
IOExpander::PIN_PWM;
|
||||
|
||||
// For ADC
|
||||
IOExpander::PIN_ADC;
|
||||
```
|
||||
|
||||
|
||||
### Mode
|
||||
|
||||
With the intended constants imported, the mode of a GPIO pin can be set by calling `.gpio_pin_mode(gpio, mode)`:
|
||||
|
||||
```c++
|
||||
wheel.gpio_pin_mode(GP7, IOExpander::PIN_<IN or IN_PU or OUT or PWM or ADC>);
|
||||
```
|
||||
|
||||
It is also possible to read the current mode of a GPIO pin by calling `.gpio_pin_mode(gpio)`:
|
||||
|
||||
```c++
|
||||
mode = wheel.gpio_pin_mode(GP7);
|
||||
```
|
||||
|
||||
|
||||
### As Input or ADC
|
||||
|
||||
The current value of an GPIO pin in input or ADC mode can be read by calling `.gpio_pin_value(gpio)`:
|
||||
|
||||
```c++
|
||||
value = wheel.gpio_pin_value(GP7);
|
||||
```
|
||||
|
||||
If the mode is digital, the value will either be `0` or `1`.
|
||||
If the mode is analog, the value will be a voltage from `0.0` to `3.3`.
|
||||
|
||||
|
||||
### As Output
|
||||
|
||||
The current value of a GPIO pin in output mode can be set by calling `.gpio_pin_value(gpio, value)`:
|
||||
|
||||
```c++
|
||||
wheel.gpio_pin_value(GP7, value);
|
||||
```
|
||||
|
||||
The expected value is either `0` or `1`, or `True` or `False`.
|
||||
|
||||
|
||||
### As PWM
|
||||
|
||||
The GPIO pins can also be set as PWM outputs. The `PIN_PWM` constant can be accessed from the `IOExpander` class, and passed into the `.gpio_pin_mode()` function.
|
||||
|
||||
The frequency of the PWM signal can then be configured by calling `.gpio_pwm_frequency()`, which accepts a frequency (in Hz). It returns the cycle period, which should be used to set duty cycles.
|
||||
|
||||
Finally, the duty cycle of the PWM signal can be set by calling `.gpio_pin_value()` and providing it with a value between `0` and the cycle period.
|
||||
|
||||
Below is an example of setting a gpio pin to output a 25KHz signal with a 50% duty cycle:
|
||||
|
||||
```c++
|
||||
#include "pimoroni_i2c.hpp"
|
||||
#include "breakout_encoder_wheel.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
using namespace encoderwheel;
|
||||
|
||||
// Initialise EncoderWheel
|
||||
I2C i2c(BOARD::BREAKOUT_GARDEN);
|
||||
BreakoutEncoderWheel wheel(&i2c);
|
||||
|
||||
// Setup the gpio pin as a PWM output
|
||||
wheel.gpio_pin_mode(GP7, IOExpander::PIN_PWM);
|
||||
|
||||
// Set the gpio pin's frequency to 25KHz, and record the cycle period
|
||||
uint16_t period = wheel.gpio_pwm_frequency(25000.0f);
|
||||
|
||||
// Output a 50% duty cycle square wave
|
||||
wheel.gpio_pin_value(GP7, (int)(period * 0.5f));
|
||||
```
|
||||
|
||||
|
||||
#### Delayed Loading
|
||||
|
||||
By default, changes to a gpio pin's frequency or value are applied immediately. However, sometimes this may not be wanted, and instead you want all pins to receive updated parameters at the same time, regardless of how long the code ran that calculated the update.
|
||||
|
||||
For this purpose, `.gpio_pwm_frequency()` and `.gpio_pin_value()` include an optional parameter `load`, which by default is `true`. To avoid this "loading" set the parameter to `false`. Then either the last call can include a `true`, or a specific call to `.gpio_pwm_load()` can be made.
|
||||
|
||||
In addition, any function that performs a load, including the `.gpio_pwm_load()` function, can be made to wait until the new PWM value has been sent out of the pins. By default this is disabled, but can be enabled by including setting the `wait_for_load` parameter to `true` in the relevant function calls.
|
||||
|
||||
|
||||
#### Limitations
|
||||
|
||||
All of Encoder Wheel's PWM outputs share the same timing parameters. This means that GP7, GP8, and GP9 share the same frequency. Keep this in mind if changing the frequency of one, as the others will not automatically know about the change, resulting in unexpected duty cycle outputs.
|
||||
|
||||
|
||||
## Function Reference
|
||||
|
||||
Here is the complete list of functions available on the `BreakoutEncoderWheel` class:
|
||||
```c++
|
||||
BreakoutEncoderWheel(uint8_t ioe_address = DEFAULT_IOE_I2C_ADDRESS, uint8_t led_address = DEFAULT_LED_I2C_ADDRESS);
|
||||
BreakoutEncoderWheel(I2C *i2c, uint8_t ioe_address = 0x13, uint8_t led_address = 0x77, uint interrupt = PIN_UNUSED, uint32_t timeout = 1, bool debug = false);
|
||||
bool init(bool skip_chip_id_check = false);
|
||||
void set_ioe_address(uint8_t address);
|
||||
bool get_interrupt_flag();
|
||||
void clear_interrupt_flag();
|
||||
bool pressed(uint button);
|
||||
int16_t count();
|
||||
int16_t delta();
|
||||
void zero();
|
||||
int16_t step();
|
||||
int16_t turn();
|
||||
float revolutions();
|
||||
float degrees();
|
||||
float radians();
|
||||
Direction direction();
|
||||
void direction(Direction direction);
|
||||
void set_rgb(int index, int r, int g, int b);
|
||||
void set_hsv(int index, float h, float s = 1.0f, float v = 1.0f);
|
||||
void clear();
|
||||
void show();
|
||||
uint8_t gpio_pin_mode(uint8_t gpio);
|
||||
void gpio_pin_mode(uint8_t gpio, uint8_t mode);
|
||||
int16_t gpio_pin_value(uint8_t gpio);
|
||||
float gpio_pin_value_as_voltage(uint8_t gpio);
|
||||
void gpio_pin_value(uint8_t gpio, uint16_t value, bool load = true, bool wait_for_load = false);
|
||||
void gpio_pwm_load(bool wait_for_load = true);
|
||||
uint16_t gpio_pwm_frequency(float frequency, bool load = true, bool wait_for_load = false);
|
||||
```
|
||||
|
||||
## Constants Reference
|
||||
|
||||
Here is the complete list of public constants on the `BreakoutEncoderWheel` class:
|
||||
|
||||
### Address Constants
|
||||
|
||||
* `DEFAULT_IOE_I2C_ADDR` = `0x13`
|
||||
* `DEFAULT_LED_I2C_ADDR` = `0x77`
|
||||
* `ALTERNATE_LED_I2C_ADDR` = `0x74`
|
||||
|
||||
### Value Constants
|
||||
|
||||
* `DEFAULT_DIRECTION` = `NORMAL_DIR`
|
||||
* `DEFAULT_TIMEOUT` = `1`
|
||||
|
||||
|
||||
Here is the complete list of public constants in the `encoderwheel` namespace:
|
||||
|
||||
### Button Constants
|
||||
|
||||
* `UP` = `0`
|
||||
* `DOWN` = `1`
|
||||
* `LEFT` = `2`
|
||||
* `RIGHT` = `3`
|
||||
* `CENTRE` = `4`
|
||||
|
||||
### GPIO Constants
|
||||
|
||||
* `GP7` = `7`
|
||||
* `GP8` = `8`
|
||||
* `GP9` = `9`
|
||||
* `GPIOS` = (`7`, `8`, `9`)
|
||||
|
||||
### Count Constants
|
||||
|
||||
* `NUM_LEDS` = `24`
|
||||
* `NUM_BUTTONS` = `5`
|
||||
* `NUM_GPIOS` = `5`
|
|
@ -0,0 +1,19 @@
|
|||
if(NOT TARGET ioexpander)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/ioexpander/ioexpander.cmake)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET is31fl3731)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/is31fl3731/is31fl3731.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_encoder_wheel)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
target_sources(${LIB_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp
|
||||
)
|
||||
|
||||
target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib ioexpander is31fl3731)
|
|
@ -0,0 +1,260 @@
|
|||
#include "breakout_encoder_wheel.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace pimoroni {
|
||||
namespace encoderwheel {
|
||||
|
||||
bool BreakoutEncoderWheel::init(bool skip_chip_id_check) {
|
||||
bool success = false;
|
||||
if(ioe.init(skip_chip_id_check, true) && led_ring.init()) {
|
||||
|
||||
ioe.setup_rotary_encoder(ENC_CHANNEL, ENC_TERM_A, ENC_TERM_B, 0, true); // count microsteps
|
||||
|
||||
ioe.set_mode(SW_UP, IOExpander::PIN_IN_PU);
|
||||
ioe.set_mode(SW_DOWN, IOExpander::PIN_IN_PU);
|
||||
ioe.set_mode(SW_LEFT, IOExpander::PIN_IN_PU);
|
||||
ioe.set_mode(SW_RIGHT, IOExpander::PIN_IN_PU);
|
||||
ioe.set_mode(SW_CENTRE, IOExpander::PIN_IN_PU);
|
||||
|
||||
ioe.set_pin_interrupt(SW_UP, true);
|
||||
ioe.set_pin_interrupt(SW_DOWN, true);
|
||||
ioe.set_pin_interrupt(SW_LEFT, true);
|
||||
ioe.set_pin_interrupt(SW_RIGHT, true);
|
||||
ioe.set_pin_interrupt(SW_CENTRE, true);
|
||||
|
||||
led_ring.enable({
|
||||
0b00000000, 0b10111111,
|
||||
0b00111110, 0b00111110,
|
||||
0b00111111, 0b10111110,
|
||||
0b00000111, 0b10000110,
|
||||
0b00110000, 0b00110000,
|
||||
0b00111111, 0b10111110,
|
||||
0b00111111, 0b10111110,
|
||||
0b01111111, 0b11111110,
|
||||
0b01111111, 0b00000000
|
||||
}, 0);
|
||||
|
||||
success = true;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
i2c_inst_t* BreakoutEncoderWheel::get_i2c() const {
|
||||
return ioe.get_i2c();
|
||||
}
|
||||
|
||||
int BreakoutEncoderWheel::get_ioe_address() const {
|
||||
return ioe.get_address();
|
||||
}
|
||||
|
||||
int BreakoutEncoderWheel::get_led_address() const {
|
||||
return led_ring.get_address();
|
||||
}
|
||||
|
||||
int BreakoutEncoderWheel::get_sda() const {
|
||||
return ioe.get_sda();
|
||||
}
|
||||
|
||||
int BreakoutEncoderWheel::get_scl() const {
|
||||
return ioe.get_scl();
|
||||
}
|
||||
|
||||
int BreakoutEncoderWheel::get_int() const {
|
||||
return ioe.get_int();
|
||||
}
|
||||
|
||||
void BreakoutEncoderWheel::set_ioe_address(uint8_t address) {
|
||||
ioe.set_address(address);
|
||||
}
|
||||
|
||||
bool BreakoutEncoderWheel::get_interrupt_flag() {
|
||||
return ioe.get_interrupt_flag();
|
||||
}
|
||||
|
||||
void BreakoutEncoderWheel::clear_interrupt_flag() {
|
||||
ioe.clear_interrupt_flag();
|
||||
}
|
||||
|
||||
bool BreakoutEncoderWheel::pressed(uint button) {
|
||||
switch(button) {
|
||||
case 0:
|
||||
return ioe.input(SW_UP) == 0;
|
||||
case 1:
|
||||
return ioe.input(SW_DOWN) == 0;
|
||||
case 2:
|
||||
return ioe.input(SW_LEFT) == 0;
|
||||
case 3:
|
||||
return ioe.input(SW_RIGHT) == 0;
|
||||
case 4:
|
||||
return ioe.input(SW_CENTRE) == 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t BreakoutEncoderWheel::count() {
|
||||
take_encoder_reading();
|
||||
return enc_count;
|
||||
}
|
||||
|
||||
int16_t BreakoutEncoderWheel::delta() {
|
||||
take_encoder_reading();
|
||||
|
||||
// Determine the change in counts since the last time this function was performed
|
||||
int16_t change = enc_count - last_delta_count;
|
||||
last_delta_count = enc_count;
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
void BreakoutEncoderWheel::zero() {
|
||||
ioe.clear_rotary_encoder(ENC_CHANNEL);
|
||||
enc_count = 0;
|
||||
enc_step = 0;
|
||||
enc_turn = 0;
|
||||
last_raw_count = 0;
|
||||
last_delta_count = 0;
|
||||
}
|
||||
|
||||
int16_t BreakoutEncoderWheel::step() {
|
||||
take_encoder_reading();
|
||||
return enc_step;
|
||||
}
|
||||
|
||||
int16_t BreakoutEncoderWheel::turn() {
|
||||
take_encoder_reading();
|
||||
return enc_turn;
|
||||
}
|
||||
|
||||
float BreakoutEncoderWheel::revolutions() {
|
||||
return (float)count() / (float)ENC_COUNTS_PER_REV;
|
||||
}
|
||||
|
||||
float BreakoutEncoderWheel::degrees() {
|
||||
return revolutions() * 360.0f;
|
||||
}
|
||||
|
||||
float BreakoutEncoderWheel::radians() {
|
||||
return revolutions() * M_PI * 2.0f;
|
||||
}
|
||||
|
||||
Direction BreakoutEncoderWheel::direction() {
|
||||
return enc_direction;
|
||||
}
|
||||
|
||||
void BreakoutEncoderWheel::direction(Direction direction) {
|
||||
enc_direction = direction;
|
||||
}
|
||||
|
||||
void BreakoutEncoderWheel::set_rgb(int index, int r, int g, int b) {
|
||||
RGBLookup rgb = lookup_table[index];
|
||||
led_ring.set(rgb.r, r);
|
||||
led_ring.set(rgb.g, g);
|
||||
led_ring.set(rgb.b, b);
|
||||
}
|
||||
|
||||
void BreakoutEncoderWheel::set_hsv(int index, float h, float s, float v) {
|
||||
int r, g, b;
|
||||
if(h < 0.0f) {
|
||||
h = 1.0f + fmodf(h, 1.0f);
|
||||
}
|
||||
|
||||
int i = int(h * 6);
|
||||
float f = h * 6 - i;
|
||||
|
||||
v = v * 255.0f;
|
||||
|
||||
float sv = s * v;
|
||||
float fsv = f * sv;
|
||||
|
||||
auto p = uint8_t(-sv + v);
|
||||
auto q = uint8_t(-fsv + v);
|
||||
auto t = uint8_t(fsv - sv + v);
|
||||
|
||||
uint8_t bv = uint8_t(v);
|
||||
|
||||
switch (i % 6) {
|
||||
default:
|
||||
case 0: r = bv; g = t; b = p; break;
|
||||
case 1: r = q; g = bv; b = p; break;
|
||||
case 2: r = p; g = bv; b = t; break;
|
||||
case 3: r = p; g = q; b = bv; break;
|
||||
case 4: r = t; g = p; b = bv; break;
|
||||
case 5: r = bv; g = p; b = q; break;
|
||||
}
|
||||
|
||||
set_rgb(index, r, g, b);
|
||||
}
|
||||
|
||||
void BreakoutEncoderWheel::clear() {
|
||||
led_ring.clear();
|
||||
}
|
||||
|
||||
void BreakoutEncoderWheel::show() {
|
||||
led_ring.update();
|
||||
}
|
||||
|
||||
uint8_t BreakoutEncoderWheel::gpio_pin_mode(uint8_t gpio) {
|
||||
assert(gpio < GP7 || gpio > GP9);
|
||||
return ioe.get_mode(gpio);
|
||||
}
|
||||
|
||||
void BreakoutEncoderWheel::gpio_pin_mode(uint8_t gpio, uint8_t mode) {
|
||||
assert(gpio < GP7 || gpio > GP9);
|
||||
ioe.set_mode(gpio, mode);
|
||||
}
|
||||
|
||||
int16_t BreakoutEncoderWheel::gpio_pin_value(uint8_t gpio) {
|
||||
assert(gpio < GP7 || gpio > GP9);
|
||||
return ioe.input(gpio);
|
||||
}
|
||||
|
||||
float BreakoutEncoderWheel::gpio_pin_value_as_voltage(uint8_t gpio) {
|
||||
assert(gpio < GP7 || gpio > GP9);
|
||||
return ioe.input_as_voltage(gpio);
|
||||
}
|
||||
|
||||
void BreakoutEncoderWheel::gpio_pin_value(uint8_t gpio, uint16_t value, bool load, bool wait_for_load) {
|
||||
assert(gpio < GP7 || gpio > GP9);
|
||||
ioe.output(gpio, value, load, wait_for_load);
|
||||
}
|
||||
|
||||
void BreakoutEncoderWheel::gpio_pwm_load(bool wait_for_load) {
|
||||
ioe.pwm_load(wait_for_load);
|
||||
}
|
||||
|
||||
uint16_t BreakoutEncoderWheel::gpio_pwm_frequency(float frequency, bool load, bool wait_for_load) {
|
||||
return ioe.set_pwm_frequency(frequency, load, wait_for_load);
|
||||
}
|
||||
|
||||
void BreakoutEncoderWheel::take_encoder_reading() {
|
||||
// Read the current count
|
||||
int16_t raw_count = ioe.read_rotary_encoder(ENC_CHANNEL) / ENC_COUNT_DIVIDER;
|
||||
int16_t raw_change = raw_count - last_raw_count;
|
||||
last_raw_count = raw_count;
|
||||
|
||||
// Invert the change
|
||||
if(enc_direction == REVERSED_DIR) {
|
||||
raw_change = 0 - raw_change;
|
||||
}
|
||||
|
||||
enc_count += raw_change;
|
||||
|
||||
enc_step += raw_change;
|
||||
if(raw_change > 0) {
|
||||
while(enc_step >= ENC_COUNTS_PER_REV) {
|
||||
enc_step -= ENC_COUNTS_PER_REV;
|
||||
enc_turn += 1;
|
||||
}
|
||||
}
|
||||
else if(raw_change < 0) {
|
||||
while(enc_step < 0) {
|
||||
enc_step += ENC_COUNTS_PER_REV;
|
||||
enc_turn -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
#pragma once
|
||||
|
||||
#include "drivers/ioexpander/ioexpander.hpp"
|
||||
#include "drivers/is31fl3731/is31fl3731.hpp"
|
||||
#include "common/pimoroni_common.hpp"
|
||||
|
||||
namespace pimoroni {
|
||||
namespace encoderwheel {
|
||||
static const uint8_t NUM_LEDS = 24;
|
||||
static const uint8_t NUM_BUTTONS = 5;
|
||||
static const uint8_t NUM_GPIOS = 3;
|
||||
|
||||
static const uint8_t UP = 0;
|
||||
static const uint8_t DOWN = 1;
|
||||
static const uint8_t LEFT = 2;
|
||||
static const uint8_t RIGHT = 3;
|
||||
static const uint8_t CENTRE = 4;
|
||||
|
||||
static const uint8_t GP7 = 7;
|
||||
static const uint8_t GP8 = 8;
|
||||
static const uint8_t GP9 = 9;
|
||||
static const uint8_t GPIOS[] = {GP7, GP8, GP9};
|
||||
|
||||
class BreakoutEncoderWheel {
|
||||
struct RGBLookup {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// Constants
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
static const uint8_t DEFAULT_IOE_I2C_ADDRESS = 0x13;
|
||||
static const uint8_t DEFAULT_LED_I2C_ADDRESS = 0x77;
|
||||
static const uint8_t ALTERNATE_LED_I2C_ADDRESS = 0x74;
|
||||
|
||||
static const Direction DEFAULT_DIRECTION = NORMAL_DIR;
|
||||
static const uint32_t DEFAULT_TIMEOUT = 1;
|
||||
|
||||
private:
|
||||
static const uint8_t ENC_CHANNEL = 1;
|
||||
static const uint8_t ENC_TERM_A = 3;
|
||||
static const uint8_t ENC_TERM_B = 12;
|
||||
static const uint8_t ENC_COUNTS_PER_REV = 24;
|
||||
static const uint8_t ENC_COUNT_DIVIDER = 2;
|
||||
|
||||
static const uint8_t SW_UP = 13;
|
||||
static const uint8_t SW_DOWN = 4;
|
||||
static const uint8_t SW_LEFT = 11;
|
||||
static const uint8_t SW_RIGHT = 2;
|
||||
static const uint8_t SW_CENTRE = 1;
|
||||
|
||||
// This wonderful lookup table maps the LEDs on the encoder wheel
|
||||
// from their 3x24 (remember, they're RGB) configuration to
|
||||
// their specific location in the 144 pixel buffer.
|
||||
static constexpr RGBLookup lookup_table[24] = {
|
||||
{128, 32, 48},
|
||||
{129, 33, 49},
|
||||
{130, 17, 50},
|
||||
{131, 18, 34},
|
||||
{132, 19, 35},
|
||||
{133, 20, 36},
|
||||
{134, 21, 37},
|
||||
{112, 80, 96},
|
||||
{113, 81, 97},
|
||||
{114, 82, 98},
|
||||
{115, 83, 99},
|
||||
{116, 84, 100},
|
||||
{117, 68, 101},
|
||||
{118, 69, 85},
|
||||
{127, 47, 63},
|
||||
{121, 41, 57},
|
||||
{122, 25, 58},
|
||||
{123, 26, 42},
|
||||
{124, 27, 43},
|
||||
{125, 28, 44},
|
||||
{126, 29, 45},
|
||||
{15, 95, 111},
|
||||
{8, 89, 105},
|
||||
{9, 90, 106},
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// Variables
|
||||
//--------------------------------------------------
|
||||
private:
|
||||
IOExpander ioe;
|
||||
IS31FL3731 led_ring;
|
||||
|
||||
Direction enc_direction = DEFAULT_DIRECTION;
|
||||
int16_t enc_count = 0;
|
||||
int16_t enc_step = 0;
|
||||
int16_t enc_turn = 0;
|
||||
int16_t last_raw_count = 0;
|
||||
int16_t last_delta_count = 0;
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// Constructors/Destructor
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
BreakoutEncoderWheel(uint8_t ioe_address = DEFAULT_IOE_I2C_ADDRESS, uint8_t led_address = DEFAULT_LED_I2C_ADDRESS)
|
||||
: BreakoutEncoderWheel(new I2C(), ioe_address, led_address) {}
|
||||
|
||||
BreakoutEncoderWheel(I2C *i2c, uint8_t ioe_address = DEFAULT_IOE_I2C_ADDRESS, uint8_t led_address = DEFAULT_LED_I2C_ADDRESS, uint interrupt = PIN_UNUSED, uint32_t timeout = DEFAULT_TIMEOUT, bool debug = false)
|
||||
: ioe(i2c, ioe_address, interrupt, timeout, debug), led_ring(i2c, led_address) {}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// Methods
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
bool init(bool skip_chip_id_check = false);
|
||||
|
||||
// For print access in micropython
|
||||
i2c_inst_t* get_i2c() const;
|
||||
int get_ioe_address() const;
|
||||
int get_led_address() const;
|
||||
int get_sda() const;
|
||||
int get_scl() const;
|
||||
int get_int() const;
|
||||
|
||||
// Calls through to IOExpander class
|
||||
void set_ioe_address(uint8_t address);
|
||||
bool get_interrupt_flag();
|
||||
void clear_interrupt_flag();
|
||||
|
||||
// Encoder breakout specific
|
||||
bool pressed(uint button);
|
||||
int16_t count();
|
||||
int16_t delta();
|
||||
void zero();
|
||||
int16_t step();
|
||||
int16_t turn();
|
||||
float revolutions();
|
||||
float degrees();
|
||||
float radians();
|
||||
Direction direction();
|
||||
void direction(Direction direction);
|
||||
void set_rgb(int index, int r, int g, int b);
|
||||
void set_hsv(int index, float h, float s = 1.0f, float v = 1.0f);
|
||||
void clear();
|
||||
void show();
|
||||
|
||||
uint8_t gpio_pin_mode(uint8_t gpio);
|
||||
void gpio_pin_mode(uint8_t gpio, uint8_t mode);
|
||||
int16_t gpio_pin_value(uint8_t gpio);
|
||||
float gpio_pin_value_as_voltage(uint8_t gpio);
|
||||
void gpio_pin_value(uint8_t gpio, uint16_t value, bool load = true, bool wait_for_load = false);
|
||||
void gpio_pwm_load(bool wait_for_load = true);
|
||||
uint16_t gpio_pwm_frequency(float frequency, bool load = true, bool wait_for_load = false);
|
||||
|
||||
private:
|
||||
void take_encoder_reading();
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET ioexpander)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/ioexpander/ioexpander.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_ioexpander)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET ltr559)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/ltr559/ltr559.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_ltr559)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET is31fl3731)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/is31fl3731/is31fl3731.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_matrix11x7)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET ioexpander)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/ioexpander/ioexpander.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_mics6814)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET msa301)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/msa301/msa301.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_msa301)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET pmw3901)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/pmw3901/pmw3901.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_paa5100)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET pmw3901)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/pmw3901/pmw3901.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_pmw3901)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET ioexpander)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/ioexpander/ioexpander.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_potentiometer)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET is31fl3731)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/is31fl3731/is31fl3731.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_rgbmatrix5x5)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET rv3028)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/rv3028/rv3028.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_rtc)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET sgp30)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/sgp30/sgp30.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_sgp30)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET trackball)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/trackball/trackball.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME breakout_trackball)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET pico_graphics)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake)
|
||||
endif()
|
||||
|
||||
add_library(cosmic_unicorn INTERFACE)
|
||||
|
||||
pico_generate_pio_header(cosmic_unicorn ${CMAKE_CURRENT_LIST_DIR}/cosmic_unicorn.pio)
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
#include "hardware/clocks.h"
|
||||
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "cosmic_unicorn.pio.h"
|
||||
#include "audio_i2s.pio.h"
|
||||
#endif
|
||||
|
||||
#include "cosmic_unicorn.hpp"
|
||||
|
||||
|
@ -31,17 +33,13 @@
|
|||
// for each row:
|
||||
// for each bcd frame:
|
||||
// 0: 00111111 // row pixel count (minus one)
|
||||
// 1 - 64: xxxxxbgr, xxxxxbgr, xxxxxbgr, ... // pixel data
|
||||
// 65 - 67: xxxxxxxx, xxxxxxxx, xxxxxxxx // dummy bytes to dword align
|
||||
// 68: xxxxrrrr // row select bits
|
||||
// 69 - 71: tttttttt, tttttttt, tttttttt // bcd tick count (0-65536)
|
||||
// 1: xxxxrrrr // row select bits
|
||||
// 2 - 65: xxxxxbgr, xxxxxbgr, xxxxxbgr, ... // pixel data
|
||||
// 66 - 67: xxxxxxxx, xxxxxxxx, // dummy bytes to dword align
|
||||
// 68 - 71: tttttttt, tttttttt, tttttttt // bcd tick count (0-65536)
|
||||
//
|
||||
// .. and back to the start
|
||||
|
||||
static uint16_t r_gamma_lut[256] = {0};
|
||||
static uint16_t g_gamma_lut[256] = {0};
|
||||
static uint16_t b_gamma_lut[256] = {0};
|
||||
|
||||
static uint32_t dma_channel;
|
||||
static uint32_t dma_ctrl_channel;
|
||||
static uint32_t audio_dma_channel;
|
||||
|
@ -122,19 +120,6 @@ namespace pimoroni {
|
|||
// Tear down the old GU instance's hardware resources
|
||||
partial_teardown();
|
||||
}
|
||||
|
||||
|
||||
// create 14-bit gamma luts
|
||||
for(uint16_t v = 0; v < 256; v++) {
|
||||
// gamma correct the provided 0-255 brightness value onto a
|
||||
// 0-65535 range for the pwm counter
|
||||
float r_gamma = 1.8f;
|
||||
r_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, r_gamma) * (float(1U << (BCD_FRAME_COUNT)) - 1.0f) + 0.5f);
|
||||
float g_gamma = 1.8f;
|
||||
g_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, g_gamma) * (float(1U << (BCD_FRAME_COUNT)) - 1.0f) + 0.5f);
|
||||
float b_gamma = 1.8f;
|
||||
b_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, b_gamma) * (float(1U << (BCD_FRAME_COUNT)) - 1.0f) + 0.5f);
|
||||
}
|
||||
|
||||
// for each row:
|
||||
// for each bcd frame:
|
||||
|
@ -154,18 +139,19 @@ namespace pimoroni {
|
|||
uint8_t *p = &bitstream[row * ROW_BYTES + (BCD_FRAME_BYTES * frame)];
|
||||
|
||||
p[ 0] = 64 - 1; // row pixel count
|
||||
p[68] = row; // row select
|
||||
p[ 1] = row; // row select
|
||||
|
||||
// set the number of bcd ticks for this frame
|
||||
uint32_t bcd_ticks = (1 << frame);
|
||||
p[69] = (bcd_ticks & 0xff) >> 0;
|
||||
p[70] = (bcd_ticks & 0xff00) >> 8;
|
||||
p[71] = (bcd_ticks & 0xff0000) >> 16;
|
||||
p[68] = (bcd_ticks & 0xff) >> 0;
|
||||
p[69] = (bcd_ticks & 0xff00) >> 8;
|
||||
p[70] = (bcd_ticks & 0xff0000) >> 16;
|
||||
p[71] = (bcd_ticks & 0xff000000) >> 24;
|
||||
}
|
||||
}
|
||||
|
||||
// setup light sensor adc
|
||||
adc_init();
|
||||
if (!(adc_hw->cs & ADC_CS_EN_BITS)) adc_init();
|
||||
adc_gpio_init(LIGHT_SENSOR);
|
||||
|
||||
gpio_init(COLUMN_CLOCK); gpio_set_dir(COLUMN_CLOCK, GPIO_OUT); gpio_put(COLUMN_CLOCK, false);
|
||||
|
@ -475,9 +461,9 @@ namespace pimoroni {
|
|||
g = (g * this->brightness) >> 8;
|
||||
b = (b * this->brightness) >> 8;
|
||||
|
||||
uint16_t gamma_r = r_gamma_lut[r];
|
||||
uint16_t gamma_g = g_gamma_lut[g];
|
||||
uint16_t gamma_b = b_gamma_lut[b];
|
||||
uint16_t gamma_r = GAMMA_14BIT[r];
|
||||
uint16_t gamma_g = GAMMA_14BIT[g];
|
||||
uint16_t gamma_b = GAMMA_14BIT[b];
|
||||
|
||||
// for each row:
|
||||
// for each bcd frame:
|
||||
|
@ -491,7 +477,7 @@ namespace pimoroni {
|
|||
|
||||
// set the appropriate bits in the separate bcd frames
|
||||
for(uint8_t frame = 0; frame < BCD_FRAME_COUNT; frame++) {
|
||||
uint8_t *p = &bitstream[y * ROW_BYTES + (BCD_FRAME_BYTES * frame) + 1 + x];
|
||||
uint8_t *p = &bitstream[y * ROW_BYTES + (BCD_FRAME_BYTES * frame) + 2 + x];
|
||||
|
||||
uint8_t red_bit = gamma_r & 0b1;
|
||||
uint8_t green_bit = gamma_g & 0b1;
|
||||
|
@ -508,11 +494,14 @@ namespace pimoroni {
|
|||
void CosmicUnicorn::set_brightness(float value) {
|
||||
value = value < 0.0f ? 0.0f : value;
|
||||
value = value > 1.0f ? 1.0f : value;
|
||||
// Max brightness is - in fact - 256 since it's applied with:
|
||||
// result = (channel * brightness) >> 8
|
||||
// eg: (255 * 256) >> 8 == 255
|
||||
this->brightness = floor(value * 256.0f);
|
||||
}
|
||||
|
||||
float CosmicUnicorn::get_brightness() {
|
||||
return this->brightness / 255.0f;
|
||||
return this->brightness / 256.0f;
|
||||
}
|
||||
|
||||
void CosmicUnicorn::adjust_brightness(float delta) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "hardware/pio.h"
|
||||
#include "pico_graphics.hpp"
|
||||
#include "common/pimoroni_common.hpp"
|
||||
#include "../pico_synth/pico_synth.hpp"
|
||||
|
||||
namespace pimoroni {
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
; for each row:
|
||||
; for each bcd frame:
|
||||
; 0: 00111111 // row pixel count (minus one)
|
||||
; 1 - 64: xxxxxbgr, xxxxxbgr, xxxxxbgr, ... // pixel data
|
||||
; 65 - 67: xxxxxxxx, xxxxxxxx, xxxxxxxx // dummy bytes to dword align
|
||||
; 68: xxxxrrrr // row select bits
|
||||
; 69 - 71: tttttttt, tttttttt, tttttttt // bcd tick count (0-65536)
|
||||
; 1: xxxxrrrr // row select bits
|
||||
; 2 - 65: xxxxxbgr, xxxxxbgr, xxxxxbgr, ... // pixel data
|
||||
; 66 - 67: xxxxxxxx, xxxxxxxx // dummy bytes to dword align
|
||||
; 68 - 71: tttttttt, tttttttt, tttttttt // bcd tick count (0-65536)
|
||||
;
|
||||
; .. and back to the start
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
|||
|
||||
; loop over row pixels
|
||||
out y, 8 ; get row pixel count (minus 1 because test is pre decrement)
|
||||
out pins, 8 ; output row select
|
||||
pixels:
|
||||
|
||||
; red bit
|
||||
|
@ -63,15 +64,13 @@ pixels:
|
|||
|
||||
jmp y-- pixels
|
||||
|
||||
out null, 24 ; discard dummy bytes
|
||||
|
||||
out pins, 8 ; output row select
|
||||
out null, 16 ; discard dummy bytes
|
||||
|
||||
set pins, 0b110 [5] ; latch high, blank high
|
||||
set pins, 0b000 ; blank low (enable output)
|
||||
|
||||
; loop over bcd delay period
|
||||
out y, 24 ; get bcd delay counter value
|
||||
out y, 32 ; get bcd delay counter value
|
||||
bcd_delay:
|
||||
jmp y-- bcd_delay
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT TARGET pico_graphics)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake)
|
||||
endif()
|
||||
|
||||
add_library(galactic_unicorn INTERFACE)
|
||||
|
||||
pico_generate_pio_header(galactic_unicorn ${CMAKE_CURRENT_LIST_DIR}/galactic_unicorn.pio)
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
#include "hardware/clocks.h"
|
||||
|
||||
|
||||
#ifndef NO_QSTR
|
||||
#include "galactic_unicorn.pio.h"
|
||||
#include "audio_i2s.pio.h"
|
||||
#endif
|
||||
|
||||
#include "galactic_unicorn.hpp"
|
||||
|
||||
|
@ -38,10 +40,6 @@
|
|||
//
|
||||
// .. and back to the start
|
||||
|
||||
static uint16_t r_gamma_lut[256] = {0};
|
||||
static uint16_t g_gamma_lut[256] = {0};
|
||||
static uint16_t b_gamma_lut[256] = {0};
|
||||
|
||||
static uint32_t dma_channel;
|
||||
static uint32_t dma_ctrl_channel;
|
||||
static uint32_t audio_dma_channel;
|
||||
|
@ -122,19 +120,6 @@ namespace pimoroni {
|
|||
// Tear down the old GU instance's hardware resources
|
||||
partial_teardown();
|
||||
}
|
||||
|
||||
|
||||
// create 14-bit gamma luts
|
||||
for(uint16_t v = 0; v < 256; v++) {
|
||||
// gamma correct the provided 0-255 brightness value onto a
|
||||
// 0-65535 range for the pwm counter
|
||||
float r_gamma = 1.8f;
|
||||
r_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, r_gamma) * (float(1U << (BCD_FRAME_COUNT)) - 1.0f) + 0.5f);
|
||||
float g_gamma = 1.8f;
|
||||
g_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, g_gamma) * (float(1U << (BCD_FRAME_COUNT)) - 1.0f) + 0.5f);
|
||||
float b_gamma = 1.8f;
|
||||
b_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, b_gamma) * (float(1U << (BCD_FRAME_COUNT)) - 1.0f) + 0.5f);
|
||||
}
|
||||
|
||||
// for each row:
|
||||
// for each bcd frame:
|
||||
|
@ -166,7 +151,7 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
// setup light sensor adc
|
||||
adc_init();
|
||||
if (!(adc_hw->cs & ADC_CS_EN_BITS)) adc_init();
|
||||
adc_gpio_init(LIGHT_SENSOR);
|
||||
|
||||
gpio_init(COLUMN_CLOCK); gpio_set_dir(COLUMN_CLOCK, GPIO_OUT); gpio_put(COLUMN_CLOCK, false);
|
||||
|
@ -470,9 +455,9 @@ namespace pimoroni {
|
|||
g = (g * this->brightness) >> 8;
|
||||
b = (b * this->brightness) >> 8;
|
||||
|
||||
uint16_t gamma_r = r_gamma_lut[r];
|
||||
uint16_t gamma_g = g_gamma_lut[g];
|
||||
uint16_t gamma_b = b_gamma_lut[b];
|
||||
uint16_t gamma_r = GAMMA_14BIT[r];
|
||||
uint16_t gamma_g = GAMMA_14BIT[g];
|
||||
uint16_t gamma_b = GAMMA_14BIT[b];
|
||||
|
||||
// for each row:
|
||||
// for each bcd frame:
|
||||
|
@ -503,11 +488,14 @@ namespace pimoroni {
|
|||
void GalacticUnicorn::set_brightness(float value) {
|
||||
value = value < 0.0f ? 0.0f : value;
|
||||
value = value > 1.0f ? 1.0f : value;
|
||||
// Max brightness is - in fact - 256 since it's applied with:
|
||||
// result = (channel * brightness) >> 8
|
||||
// eg: (255 * 256) >> 8 == 255
|
||||
this->brightness = floor(value * 256.0f);
|
||||
}
|
||||
|
||||
float GalacticUnicorn::get_brightness() {
|
||||
return this->brightness / 255.0f;
|
||||
return this->brightness / 256.0f;
|
||||
}
|
||||
|
||||
void GalacticUnicorn::adjust_brightness(float delta) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "hardware/pio.h"
|
||||
#include "pico_graphics.hpp"
|
||||
#include "common/pimoroni_common.hpp"
|
||||
#include "../pico_synth/pico_synth.hpp"
|
||||
|
||||
namespace pimoroni {
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
if(NOT TARGET st7567)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/st7567/st7567.cmake)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET button)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/button/button.cmake)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET rgbled)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/rgbled/rgbled.cmake)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET pico_graphics)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME gfx_pack)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
|
||||
namespace hershey {
|
||||
struct font_glyph_t {
|
||||
|
@ -50,4 +51,4 @@ namespace hershey {
|
|||
|
||||
bool has_font(std::string_view font);
|
||||
const font_t* font(std::string_view font);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,35 @@
|
|||
set(LIB_NAME inky_frame)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
target_sources(${LIB_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp
|
||||
)
|
||||
|
||||
target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${LIB_NAME} INTERFACE hardware_i2c pico_graphics hardware_spi hardware_pwm bitmap_fonts hershey_fonts pico_stdlib sdcard fatfs pcf85063a uc8159 jpegdec)
|
||||
if(NOT TARGET sdcard)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/sdcard/sdcard.cmake)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET fatfs)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/fatfs/fatfs.cmake)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET pcf85063a)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/pcf85063a/pcf85063a.cmake)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET uc8159)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../drivers/uc8159/uc8159.cmake)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET jpegdec)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../jpegdec/jpegdec.cmake)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET pico_graphics)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake)
|
||||
endif()
|
||||
|
||||
set(LIB_NAME inky_frame)
|
||||
add_library(${LIB_NAME} INTERFACE)
|
||||
|
||||
target_sources(${LIB_NAME} INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}.cpp
|
||||
)
|
||||
|
||||
target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(${LIB_NAME} INTERFACE hardware_i2c pico_graphics hardware_spi hardware_pwm bitmap_fonts hershey_fonts pico_stdlib sdcard fatfs pcf85063a uc8159 jpegdec)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue