Porównaj commity

...

99 Commity

Autor SHA1 Wiadomość Data
Hel Gibbons f18f1ba259
Merge pull request #960 from johnbintz/patch-1
Add SPI import to SD card upload example
2024-06-14 11:27:52 +01:00
John Bintz f495fb8b70
Add SPI import to SD card upload example
As the code exists now, you won't be able to mount the SD card due to the missing import.
2024-06-14 06:08:11 -04:00
Philip Howard 7a2ebe5c0d
Merge pull request #952 from pimoroni/test/revert-1.23.0
TEST: Revert to v1.23.0.
2024-06-06 19:15:17 +01:00
Phil Howard 48f2446f6c CI: Cherry-pick our pins.csv patch. 2024-06-06 19:00:48 +01:00
Phil Howard 6eef96d9c9 CI: Revert to v1.23.0.
We'd moved past v1.23.0 to do our release in order to grab:

932f76c6ba

But this broke our builds due to:

https://github.com/micropython/micropython/issues/15220
2024-06-06 18:56:17 +01:00
Philip Howard 9771274e90
Merge pull request #950 from pimoroni/patch-getting-started-doc
Docs: Fix .uf2 names, add pico_usb.
2024-06-05 11:17:59 +01:00
Phil Howard 7c05770a44 Docs: Fix .uf2 names, add pico_usb. 2024-06-05 11:09:15 +01:00
Philip Howard 3440ab232c
Merge pull request #915 from pimoroni/feature/usb
MicroPython v1.23.0 + USB-compatible Pico firmware.
2024-06-04 20:31:18 +01:00
Phil Howard 7d0bb04d6d Keybow2040: Basic-ish USB examples. 2024-06-04 19:55:24 +01:00
Phil Howard 392890e99b RP_PICO_USB: New USB-enabled version of the stock Pico firmware.
⚠️ Warning: flash/firmware split is now 1Mb/1Mb to accomodate USB libs.

BACK UP YOUR PICO before flashing this build. It *will* trash your filesystem.

* Add new RP_PICO_USB board
* Use commit 932f76c6ba64c5a3e68de3324556d9979f09303b for 932f76c6ba
2024-06-04 19:55:24 +01:00
Phil Howard 7b7d9fe383 CI: Update MicroPython patch for > v1.22.2. 2024-06-04 19:55:24 +01:00
Phil Howard ccd00f6289 Ulab: Bump to 6.5.2 for MicroPython 1.23.0 compatibility. 2024-06-04 19:54:52 +01:00
Philip Howard 10fed7edaf
Merge pull request #946 from pimoroni/patch-sh1107-example
Fixed background in SH1107 example
2024-06-03 13:54:41 +01:00
Phil Howard 517f8ae1cd global: Remove the STATIC macro.
Reflect the changes proposed in micropython/micropython#13763.
2024-06-03 13:44:23 +01:00
thirdr 5b6903ce72 fixed background 2024-06-03 12:25:16 +01:00
Phil Howard 84cabe077d MicroPython: Switch QRCode to micropython/edge branch.
Includes fixes for:

* "mp_obj_malloc_with_finaliser" replacing "m_new_obj_with_finaliser"
* "STATIC" macro dropped in favour of "static"
2024-06-03 11:40:29 +01:00
Phil Howard 64e26dc6f4 CI: Bump MicroPython to master.
CI will always run against the latest MicroPython commit.
2024-06-03 11:40:29 +01:00
Phil Howard 47e3aed88f MicroPython: Switch to mp_obj_malloc_with_finaliser. 2024-06-03 11:40:29 +01:00
Philip Howard bf6fa4bc45
Merge pull request #916 from pimoroni/examples/audio
i2s audio examples
2024-06-03 11:35:59 +01:00
Philip Howard 0b133c750e
Merge pull request #931 from pimoroni/driver/as7343
AS7343: New driver for the 14 channel spectrometer
2024-06-03 11:34:45 +01:00
Philip Howard 5691573f89
Merge pull request #933 from pimoroni/feature/scannable-release-names
CI: Scannable artifact and release names.
2024-06-03 11:34:07 +01:00
Philip Howard b41bb4abf7
Merge pull request #943 from pimoroni/patch-bme280-examples
Fixed result status bug
2024-06-03 11:33:11 +01:00
Philip Howard 8b928d8891
Merge pull request #944 from pimoroni/patch-tufty-example
Fixed button polarity
2024-06-03 11:32:09 +01:00
Philip Howard 56e6d66c9b
Merge pull request #945 from pimoroni/patch-mics6814-pins
Corrected pin definitions for OX and RED
2024-06-03 11:31:15 +01:00
thirdr ffa12c2165 corrected pin defs 2024-06-03 10:31:46 +01:00
thirdr b9f3990995 fixed button polarity 2024-06-03 09:11:49 +01:00
thirdr cca4f3f7f1 fixed result status bug 2024-05-31 12:38:58 +01:00
Philip Howard 981a38b989
Merge pull request #928 from pimoroni/examples/pngdec
Examples/pngdec
2024-05-30 23:32:14 +01:00
Philip Howard 25786def35
Merge pull request #942 from pimoroni/patch-inky-launcher
Patch inky launcher
2024-05-30 19:21:28 +01:00
thirdr a387e45098 linting fix 2024-05-30 13:32:48 +01:00
thirdr 452d700ba1 Improved XML parsing/bug fix from Pico Vision 2024-05-30 13:29:48 +01:00
Hel Gibbons 9d7d651565
Merge pull request #940 from exussum12/patch-3
Add link to png
2024-05-24 16:57:25 +01:00
Scott Dutton b3a0c4fb9d
Add link to png 2024-05-21 11:13:42 +01:00
thirdr f962c3cd3c linting 2024-05-13 12:27:23 +01:00
thirdr b5a040f6cc correct layout on pico display 1 & 2 2024-05-13 12:27:23 +01:00
thirdr d790d3d344 fixed background colour 2024-05-13 12:18:36 +01:00
thirdr ba7b8388bf fixed background colour 2024-05-13 12:18:36 +01:00
thirdr 829f688285 offset palette example 2024-05-13 12:18:36 +01:00
thirdr 8fb17a3c26 adjustment to scale and location 2024-05-13 12:18:36 +01:00
thirdr 4db7cc61ab png palette offset example 2024-05-13 12:18:36 +01:00
thirdr c510a3c875 Changed the png used 2024-05-13 12:18:36 +01:00
thirdr 425f132096 png decode example for tufty 2024-05-13 12:18:36 +01:00
thirdr 76683acb0d png decode example for display pack 2024-05-13 12:18:36 +01:00
thirdr 7c287192de Error handling 2024-05-13 12:18:36 +01:00
thirdr f15c657b44 pngdec example for inky 2024-05-13 12:18:36 +01:00
Philip Howard f3b53b6b5f
Merge pull request #939 from coadkins/coadkins-patch-1
Add PNG File subsection to Pico Graphics documentation
2024-05-09 11:02:35 +01:00
coadkins 37c4d22527
Add PNG File subsection to Pico Graphics documentation
I added a subsection for PNG File support in Pico Graphics by copying and adapting the text from these release notes - https://github.com/pimoroni/pimoroni-pico/releases/tag/v1.20.4 - about the PNGdec functionality.
2024-05-08 11:22:07 -04:00
Philip Howard 616b1cc8d6
Merge pull request #938 from pimoroni/ci/py_decl
CI: Add py_decl verify step to catch binary overflows.
2024-05-03 12:25:04 +01:00
Phil Howard 45a9925072 CI: Add py_decl verify step to catch binary overflows. 2024-05-03 10:59:35 +01:00
thirdr 3998b0c8bf simple example for Pico Audio Pack 2024-04-22 15:35:53 +01:00
Connor Linfoot 32c10482d9
Add support for 96x48 display to Interstate75 (#867)
* Add DISPLAY_INTERSTATE75_96X48
2024-04-17 13:41:02 +01:00
Phil Howard 6ed743b32c Docs: Update MicroPython setup to reflect new names. 2024-04-17 13:06:04 +01:00
Phil Howard 17d59f4729 CI: Scannable artifact and release names.
When I go to fetch an artifact for a MicroPython build my brain is
always confounded by the repetition of "pimoroni-" before each file.

Change the names so the product is up-front, so the list is easier
to scan-read through.
2024-04-17 13:00:40 +01:00
Philip Howard 4c44b77193
Merge pull request #912 from pimoroni/patch-picodisplay-180
PicoDisplay: Fix misalignment on rotated Pico Displays (fixes #562.)
2024-04-17 12:54:18 +01:00
Phil Howard 5510c82564 PicoDisplay: Fix rotation offset for #562.
Pico Display would have a pixel offset at 90 and 180 degree rotations.

Add a special case offset tweak for these, and demystify the rotate_180 variable.
2024-04-17 12:44:40 +01:00
Philip Howard 3a10b29f54
Merge pull request #920 from pimoroni/patch-inky7-update-timeout
inky73: Add busy wait timeout.
2024-04-17 12:42:53 +01:00
Phil Howard 8cf276b992 inky73: Add busy wait timeout.
Add a timeout to fix Inky 7.3" hanging on batteries.

Basically assumes the update has finished if it takes > 45s, and allows a subsequent attempt
rather than hanging indefinitely.

Raised, texted and fixed by w3stbam: https://github.com/pimoroni/pimoroni-pico/pull/900

Rewritten as mentioned in the PR.
2024-04-17 12:33:24 +01:00
Phil Howard 8bb17087d9 AS7343: Tufty 2040 spectrometer example. 2024-04-15 11:57:40 +01:00
Phil Howard 6fcbaf5616 AS7343: MicroPython bindings. 2024-04-15 11:53:34 +01:00
Phil Howard 6803f00b31 AS7343: New 14-channel spectral sensor driver. 2024-04-15 11:53:34 +01:00
Phil Howard 6e71a62c65 Pimoroni I2C: Add reg_write_uint16. 2024-04-15 11:53:34 +01:00
Philip Howard f1ea35fbbf
Merge pull request #911 from pimoroni/patch-unicorn-brightness
G/S/C Unicorn: Fix get_brightness to use correct max value.
2024-04-11 17:45:48 +01:00
Philip Howard c066325ca0
Merge pull request #909 from pimoroni/patch-ltr559-interrupt
LTR559: Add interrupt.py demo from #169.
2024-04-11 17:41:58 +01:00
Philip Howard fd4eb165f8
Merge pull request #930 from pimoroni/patch-misc-ci-fixes
Slightly less frustrating MicroPython builds.
2024-04-11 17:08:44 +01:00
Phil Howard 8fc8a8ee06 CI: Rename tiny2040 to tiny2040_8mb.
It was not super obvious that this build is specific to the 8mb
version of Tiny 2040.
2024-04-11 17:01:21 +01:00
Phil Howard 3bfb548686 CI: Continue other MicroPython builds if one fails.
In almost all cases it's more useful to know if a given build
is likely to succeed rather than have *everything* fail. This
change adjusts the workflow to allow other builds to continue
if one fails.
2024-04-11 17:01:09 +01:00
Philip Howard 9edcdcc126
Merge pull request #919 from pimoroni/patch-pngdec-palette-offset
PNGdec: Add support for palette offsets and greyscale copy mode
2024-04-11 16:32:08 +01:00
Philip Howard e8e550b18b
Merge pull request #929 from pimoroni/patch/wordclock
Fixed arg order bug
2024-04-11 14:57:07 +01:00
thirdr cdb7b4bf2c fixed arg order bug 2024-04-11 14:02:26 +01:00
Philip Howard 4fc3095433
Merge pull request #925 from pimoroni/patch-actions-nodejs
CI: Update actions to fix nodejs deprecation warnings.
2024-04-08 12:58:00 +01:00
Phil Howard 9c5b529754 CI: Update actions to fix nodejs deprecation warnings. 2024-04-08 12:47:14 +01:00
ZodiusInfuser a87d5581aa
Merge pull request #923 from pimoroni/patch/inventor_encoders
Added example for reading speeds from Inventor 2040W's encoders
2024-04-03 14:57:41 +01:00
ZodiusInfuser 44d7875f7e Relocated example and updated readme 2024-04-03 14:37:26 +01:00
ZodiusInfuser a90c31fb3b More explanation of encoder capture 2024-04-03 14:29:17 +01:00
ZodiusInfuser 458b0ac209 Added a speed reading example for inventor 2024-04-03 14:29:01 +01:00
Phil Howard a537672dd4 PNGdec: Don't convert greys if mode=COPY. 2024-03-28 15:35:05 +00:00
Phil Howard d34e692f51 PNGdec: Don't add palette_offset twice. 2024-03-28 15:30:32 +00:00
Phil Howard 27b913124c PNGdec: Add copy support and offset to greyscale. 2024-03-28 15:04:06 +00:00
Phil Howard c7b788cd1d PNGdec: Add palette offset arg.
Allow index colour PNGs to be copied with a palette offset.

EG: a 4bit PNG could be offset 16 times for as many colour variations.
2024-03-28 15:04:02 +00:00
Philip Howard c386b3e9cf
Merge pull request #910 from pimoroni/patch-readme-stubs
README.md: Add link to pimoroni-pico-stubs.
2024-03-28 10:17:02 +00:00
thirdr b499296867 added amp enable to audio.py 2024-03-27 14:38:01 +00:00
thirdr 193adaca72 linting fix 2024-03-27 13:01:35 +00:00
Philip Howard a7a2e2bee0
Merge pull request #918 from pimoroni/patch-pngdec-1bit
PNGdec: Add greyscale support.
2024-03-27 12:59:25 +00:00
thirdr b0babcfe9f fixed audio 'pop' 2024-03-27 12:57:44 +00:00
Phil Howard 19fa8864cf PNGdec: Add greyscale support.
Add an optional MODE_PEN to draw the PNG in the current pen colour.

Best used with, but not limited to, 1bit PNG images.
2024-03-27 12:49:09 +00:00
thirdr e34b2420c6 i2s audio examples 2024-03-26 08:41:48 +00:00
Phil Howard 964cf5eedf G/S/C Unicorn: Fix get_brightness to use correct max value.
Add a comment noting that 256 is the correct maximum brightness.
2024-03-11 21:14:43 +00:00
Phil Howard eab1595352 README.md: Add link to pimoroni-pico-stubs. 2024-03-11 15:04:18 +00:00
Phil Howard 5dd76ed31b LTR559: Add interrupt.py demo from #169. 2024-03-11 13:38:07 +00:00
Philip Howard 6eb0f90e53
Merge pull request #904 from pimoroni/ci/micropython-1.22.2
CI: Bump MicroPython to v1.22.2.
2024-03-06 10:29:16 +00:00
Phil Howard b0d53dadb3 Hub75: avoid clobbering shared IRQ handlers.
MicroPython's DMA class uses shared IRQ handlers, which would be
clobbered by Hub75's use of an exclusive handler.

Additionally clean up some dead code (DMA_IRQ_1??), more epxlicitly
clean up the claimed PIOs and programs, and do not use a fixed
DMA channel. This seems to have fixed a bug whereupon Hub75 would
hardlock on the 5th soft reset.
2024-03-05 10:30:48 +00:00
Phil Howard ad518064e9 CI: Bump MicroPython to v1.22.2. 2024-02-27 16:43:47 +00:00
Philip Howard d83107474e
Merge pull request #907 from pimoroni/patch-pngdec-1bit
Fixes for PNGDEC on Badger 2040 / Badger 2040 W
2024-02-27 16:42:12 +00:00
Phil Howard c4f70df1cf Pen1BitY: Correct RGB to dither lookup conversion. 2024-02-27 13:54:25 +00:00
Phil Howard 10221066dd PNGDEC: Support for 1bpp. 2024-02-27 13:31:52 +00:00
Philip Howard ab64fcaccc
Merge pull request #899 from pimoroni/jpegdec/width_height_fix
JPEGDEC: Backport width/height changes from pngdec.
2024-02-27 12:21:17 +00:00
Hel Gibbons 32c63c343d
Merge pull request #905 from pimoroni/helgibbons-patch-3
Plasma Stick: add link
2024-02-26 14:24:49 +00:00
Hel Gibbons 8d964bce2c
Plasma Stick: add link 2024-02-26 14:08:56 +00:00
Skyler Mansfield b23a71b889 JPEGDEC: Backport width/height changes from pngdec.
Open JPEG file or stream to read width/height before decode.
2024-01-23 16:18:13 +00:00
175 zmienionych plików z 4087 dodań i 556 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -7,20 +7,23 @@ on:
types: [created]
env:
MICROPYTHON_VERSION: v1.22.1
MICROPYTHON_VERSION: v1.23.0
jobs:
build:
name: ${{ matrix.name }} (${{ matrix.board }})
runs-on: ubuntu-20.04
continue-on-error: true
strategy:
matrix:
include:
- name: pico
board: RPI_PICO
- name: pico_usb
board: RPI_PICO_USB
- name: picow
board: RPI_PICO_W
- name: tiny2040
- name: tiny2040_8mb
board: PIMORONI_TINY2040
- name: picolipo_4mb
board: PIMORONI_PICOLIPO_4MB
@ -41,7 +44,7 @@ jobs:
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
RELEASE_FILE: ${{ matrix.name }}-${{ github.event.release.tag_name || github.sha }}-pimoroni-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"
@ -52,7 +55,7 @@ jobs:
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 }}
@ -80,6 +83,13 @@ jobs:
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
@ -110,8 +120,13 @@ jobs:
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: build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2

Wyświetl plik

@ -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

Wyświetl plik

@ -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).

Wyświetl plik

@ -14,8 +14,10 @@ function log_warning {
function micropython_clone {
log_inform "Using MicroPython $MICROPYTHON_VERSION"
git clone https://github.com/micropython/micropython --depth=1 --branch=$MICROPYTHON_VERSION
git clone https://github.com/micropython/micropython
cd micropython
git checkout $MICROPYTHON_VERSION
git cherry-pick -n 932f76c6ba64c5a3e68de3324556d9979f09303b
git submodule update --init lib/pico-sdk
git submodule update --init lib/cyw43-driver
git submodule update --init lib/lwip
@ -72,4 +74,4 @@ function cmake_build {
ccache --show-stats || true
cd build-$BOARD_NAME
cp firmware.uf2 $RELEASE_FILE.uf2
}
}

Wyświetl plik

@ -40,6 +40,15 @@ namespace pimoroni {
i2c_write_blocking(i2c, address, buffer, 2, false);
}
void I2C::reg_write_uint16(uint8_t address, uint8_t reg, uint16_t value) {
uint8_t buffer[3] = {
reg,
(uint8_t)((value & 0xff00) >> 8),
(uint8_t)(value & 0x00ff)
};
i2c_write_blocking(i2c, address, buffer, 3, false);
}
uint8_t I2C::reg_read_uint8(uint8_t address, uint8_t reg) {
uint8_t value;
i2c_write_blocking(i2c, address, &reg, 1, false);

Wyświetl plik

@ -63,6 +63,7 @@ namespace pimoroni {
i2c_inst_t* pin_to_inst(uint pin);
void reg_write_uint8(uint8_t address, uint8_t reg, uint8_t value);
void reg_write_uint16(uint8_t address, uint8_t reg, uint16_t value);
uint8_t reg_read_uint8(uint8_t address, uint8_t reg);
uint16_t reg_read_uint16(uint8_t address, uint8_t reg);
int16_t reg_read_int16(uint8_t address, uint8_t reg);

Wyświetl plik

@ -16,6 +16,7 @@ add_subdirectory(is31fl3731)
add_subdirectory(fatfs)
add_subdirectory(sdcard)
add_subdirectory(as7262)
add_subdirectory(as7343)
add_subdirectory(bh1745)
add_subdirectory(bme68x)
add_subdirectory(bmp280)

Wyświetl plik

@ -0,0 +1 @@
include(as7343.cmake)

Wyświetl plik

@ -0,0 +1,10 @@
set(DRIVER_NAME as7343)
add_library(${DRIVER_NAME} INTERFACE)
target_sources(${DRIVER_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp)
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_i2c pimoroni_i2c)

Wyświetl plik

@ -0,0 +1,160 @@
#include <cstdlib>
#include <math.h>
#include <map>
#include <vector>
#include <cstring>
#include "as7343.hpp"
namespace pimoroni {
bool AS7343::init() {
if(interrupt != PIN_UNUSED) {
gpio_set_function(interrupt, GPIO_FUNC_SIO);
gpio_set_dir(interrupt, GPIO_IN);
gpio_pull_up(interrupt);
}
uint8_t aux_id, revision_id, hardware_id;
get_version(aux_id, revision_id, hardware_id);
if(hardware_id != HARDWARE_ID) {
return false;
}
reset();
bank_select(0);
// Enable all 7 channels in output FIFO
i2c->set_bits(address, reg::FIFO_MAP, 0, FIFO_MAP_CH5 | FIFO_MAP_CH4 | FIFO_MAP_CH3 | FIFO_MAP_CH2 | FIFO_MAP_CH1 | FIFO_MAP_CH0 | FIFO_MAP_ASTATUS);
// Set the PON bit
i2c->reg_write_uint8(address, reg::ENABLE, ENABLE_WEN | ENABLE_SMUXEN | ENABLE_SP_EN | ENABLE_PON);
return true;
}
void AS7343::reset() {
i2c->reg_write_uint8(address, reg::CONTROL, CONTROL_SW_RESET);
sleep_ms(1000);
}
i2c_inst_t* AS7343::get_i2c() const {
return i2c->get_i2c();
}
int AS7343::get_sda() const {
return i2c->get_sda();
}
int AS7343::get_scl() const {
return i2c->get_scl();
}
int AS7343::get_int() const {
return interrupt;
}
void AS7343::bank_select(uint8_t bank) {
if(bank == 1) {
i2c->set_bits(address, reg::CFG0, 0, CFG0_BANK);
} else {
i2c->clear_bits(address, reg::CFG0, 0, CFG0_BANK);
}
}
void AS7343::get_version(uint8_t &auxid, uint8_t &revid, uint8_t &hwid) {
bank_select(1);
auxid = i2c->reg_read_uint8(address, reg::AUXID) & 0b00001111;
revid = i2c->reg_read_uint8(address, reg::REVID) & 0b00000111;
hwid = i2c->reg_read_uint8(address, reg::ID);
bank_select(0);
}
void AS7343::set_gain(float gain) {
if(gain == 0.5f) {
i2c->reg_write_uint8(address, reg::CFG1, 0);
} else {
uint16_t ugain = (uint16_t)gain;
uint16_t bitlength = 0;
while(ugain > 0) {
bitlength++;
ugain >>= 1;
}
i2c->reg_write_uint8(address, reg::CFG1, bitlength & 0x1f);
}
}
void AS7343::set_channels(channel_count channel_count) {
this->read_cycles = uint8_t(channel_count) / 6;
this->ch_count = (uint8_t)channel_count;
uint8_t temp = i2c->reg_read_uint8(address, reg::CFG20) & ~CFG20_18_CH;
switch(channel_count) {
case channel_count::SIX_CHANNEL:
temp |= CFG20_6_CH;
break;
case channel_count::TWELVE_CHANNEL:
temp |= CFG20_12_CH;
break;
case channel_count::EIGHTEEN_CHANNEL:
temp |= CFG20_18_CH;
break;
}
i2c->reg_write_uint8(address, reg::CFG20, temp);
}
void AS7343::set_illumination_current(float current) {
current -= 4;
current /= 2.0f;
uint8_t temp = i2c->reg_read_uint8(address, reg::LED) & 0b10000000;
temp |= (uint8_t)current;
i2c->reg_write_uint8(address, reg::LED, temp);
}
void AS7343::set_illumination_led(bool state) {
uint8_t temp = i2c->reg_read_uint8(address, reg::LED) & 0b01111111;
temp |= state ? 0x80 : 0x00;
i2c->reg_write_uint8(address, reg::LED, temp);
}
void AS7343::set_measurement_time(float measurement_time_ms) {
constexpr float resolution = 2.78f;
i2c->reg_write_uint8(address, reg::WTIME, (uint8_t)((measurement_time_ms - resolution) / resolution));
}
void AS7343::set_integration_time(float integration_time_us, uint8_t repeat) {
constexpr float resolution = 2.78f;
constexpr float max_astep = (65534 + 1) * resolution;
if (integration_time_us <= max_astep) {
i2c->reg_write_uint8(address, reg::ATIME, repeat - 1);
i2c->reg_write_uint16(address, reg::ASTEP, (uint16_t)((integration_time_us - resolution) / resolution) & 0xfffe);
} else {
// Time out of range...
}
}
void AS7343::read_fifo(uint16_t *buf) {
uint16_t expected_results = read_cycles * 7;
uint16_t result_slot = 0;
while (i2c->reg_read_uint8(address, reg::FIFO_LVL) < expected_results) {
sleep_ms(1);
}
while (i2c->reg_read_uint8(address, reg::FIFO_LVL) > 0 && expected_results > 0) {
buf[result_slot] = i2c->reg_read_uint16(address, reg::FDATA);
expected_results--;
result_slot++;
}
}
AS7343::reading AS7343::read() {
reading result;
read_fifo((uint16_t *)&result);
return result;
}
}

Wyświetl plik

@ -0,0 +1,139 @@
#pragma once
#include <string>
#include "hardware/i2c.h"
#include "hardware/gpio.h"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_i2c.hpp"
#include "as7343_regs.hpp"
namespace pimoroni {
class AS7343 {
//--------------------------------------------------
// Constants
//--------------------------------------------------
public:
static const uint8_t DEFAULT_I2C_ADDRESS = 0x39;
//--------------------------------------------------
// Enums
//--------------------------------------------------
public:
enum class channel_count : uint8_t {
SIX_CHANNEL = 6,
TWELVE_CHANNEL = 12,
EIGHTEEN_CHANNEL = 18
};
/*
enum class COMPENSATION_GAIN : float {
F1 = 1.84f,
F2 = 6.03f,
FZ = 4.88f,
F3 = 13.74f,
F4 = 3.37f,
FY = 2.82f,
F5 = 6.72f,
FXL = 2.22f,
F6 = 3.17f,
F7 = 1.95f,
F8 = 12.25f,
NIR = 1.00f,
};
*/
//--------------------------------------------------
// Substructures
//--------------------------------------------------
public:
struct reading {
// Cycle 1
uint16_t FZ = 0; // 428-480 nm
uint16_t FY = 0; // 534-593 nm
uint16_t FXL = 0; // 593-628 nm
uint16_t NIR = 0; // 849-903 nm
uint16_t c1_vis_tl = 0; // Visible top-left
uint16_t c1_vis_br = 0; // Visible bottom-right
uint16_t c1_astatus = 0; // (c1_astatus >> 8) & 0b10001111
// 0b10000000 = saturated
// 0b00001111 = gain lowest nibble
// Cycle 2
uint16_t F2 = 0; // 408-448 nm
uint16_t F3 = 0; // 448-500 mn
uint16_t F4 = 0; // 500-534 nm
uint16_t F6 = 0; // 618-665 nm
uint16_t c2_vis_tl = 0;
uint16_t c2_vis_br = 0;
uint16_t c2_astatus = 0;
// Cycle 3
uint16_t F1 = 0; // 396-408 nm
uint16_t F5 = 0; // 531-594 nm
uint16_t F7 = 0; // 685-715 nm
uint16_t F8 = 0; // 715-766 nm
uint16_t c3_vis_tl = 0;
uint16_t c3_vis_br = 0;
uint16_t c3_astatus = 0;
};
//--------------------------------------------------
// Variables
//--------------------------------------------------
private:
I2C *i2c;
// interface pins with our standard defaults where appropriate
int8_t address = DEFAULT_I2C_ADDRESS;
uint interrupt = PIN_UNUSED;
uint8_t read_cycles = 1;
uint8_t ch_count = (uint8_t)channel_count::SIX_CHANNEL;
//--------------------------------------------------
// Constructors/Destructor
//--------------------------------------------------
public:
AS7343(uint interrupt = PIN_UNUSED) : AS7343(new I2C(), interrupt) {};
AS7343(I2C *i2c, uint interrupt = PIN_UNUSED) : i2c(i2c), interrupt(interrupt) {}
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
bool init();
void reset();
// For print access in micropython
i2c_inst_t* get_i2c() const;
int get_sda() const;
int get_scl() const;
int get_int() const;
void get_version(uint8_t &auxid, uint8_t &revid, uint8_t &hwid);
reading read();
void read_fifo(uint16_t *buf);
void set_gain(float gain);
void set_illumination_current(float current);
void set_illumination_led(bool state);
void set_measurement_time(float measurement_time_ms);
void set_integration_time(float integration_time_us, uint8_t repeat=1);
void set_channels(channel_count channel_count);
private:
void bank_select(uint8_t bank=0);
};
}

Wyświetl plik

@ -0,0 +1,157 @@
#pragma once
#include <stdint.h>
namespace pimoroni {
/***** Device registers and masks here *****/
enum reg {
// Bank 1
AUXID = 0x58, // AUXID = 0b00001111
REVID = 0x59, // REVID = 0b00000111
ID = 0x5A, // ID = 0b11111111
CFG12 = 0x66, // SP_TH_CH = 0b00000111
// Bank 0
ENABLE = 0x80, // FDEN = 0b01000000
// SMUXEN = 0b00010000
// WEN = 0b00001000
// SP_EN = 0b00000010
// PON = 0b00000001
ATIME = 0x81, // ATIME = 0b11111111
// Integration Time Step Size
// 0 = 2.87us
// n 2.87us x (n + 1)
ASTEP = 0xD4, // STEP = 0xFFFF
// Spectral Measurement Wait Time
// 0 = 1 cycle = 2.78ms
// n = 2.78ms x (n + 1)
WTIME = 0x83, // WTIME = 0b11111111
SP_TH = 0x84, // SP_TH_L = 0xFFFF0000 Spectral Low Threshold
// SP_TH_H = 0x0000FFFF Spectral High Threshold
STATUS = 0x93, // ASAT = 0b10000000 Spectral Saturation (if ASIEN set)
// AINT = 0b00001000 Spectral Channel Interrupt (if SP_IEN set)
// FINT = 0b00000100 FIFO Buffer Interrupt
// SINT = 0b00000001 System Interrupt
ASTATUS = 0x94,// ASAT = 0b10000000 Saturation Status
// AGAIN = 0b00000111 Gain Status
DATA = 0x95, // 36 bytes, fields 0 to 17 (2 bytes eac)
STATUS2 = 0x90,// AVALID = 0b01000000 Spectral Data Valid
// ASAT_DIG = 0b00010000 Digital Saturation
// ASAT_ANA = 0b00001000 Analog Saturation
// FDSAT_ANA= 0b00000010 Flicker Analog Saturation
// FDSAT_DIG= 0b00000001 Flicker Digital Saturation
STATUS3 = 0x91,// INT_SP_H = 0b00100000 Spectral Above High Threshold
// INT_SP_L = 0b00010000 Spectral Below Low Threshold
STATUS5 = 0xBB,// SINT_FD = 0b00001000 Flicker Detect Interrupt (if SIEN_FD set)
// SINT_SMUX= 0b00000100 SMUS Operation Interrupt (SMUX exec finished)
STATUS4 = 0xBC,// FIFO_OV = 0b10000000 FIFO Buffer Overflow
// OVTEMP = 0b00100000 Over Temperature
// FD_TRIG = 0b00010000 Flicker Detect Trigger Error
// SD_TRIG = 0b00000100 Spectral Trigger Error
// SAI_ACT = 0b00000010 Sleep After Interrupt Active
// INT_BUSY = 0b00000001 Initialization busy (1 for ~300us after power on)
CFG0 = 0xBF, // LOW_POWER= 0b00100000
// REG_BANK = 0b00010000 0 - Register 0x80 and above
// 1 - Register 0x20 to 0x7f
// WLONG = 0b00000100 Increase WTIME by factor of 16
// Spectral Engines Gain Setting
// 0 = 0.5x, # 1 = 1x, 2 = 2x, 12 = 2048x
// GAINx = 1 << (n - 1)
CFG1 = 0xC6,
CFG3 = 0xC7, // SAI = 0b00100000 Sleep after interrupt
CFG6 = 0xF5, // SMUS_CMD = 0b00011000 0 - ROM_init
// 1 - Read_SMUX
// 2 - Write_SMUX
CFG8 = 0xC9, // FIFO_TH = 0b11000000 0b00, 0b01, 0b10, 0b11
CFG9 = 0xCA, // SIEN_FD = 0b01000000 System Interrupt Flicker Detection
// SIEN_SMUX= 0b00010000 System Interrupt SMUX Operation
CFG10 = 0x65, // FD_PERS = 0b00000111 Flicker Detect Persistence
// Number of results that must differ before status change
PERS = 0xCF, // APERS = 0b00001111
GPIO = 0x6B, // GPIO_INV = 0b00001000 Invert GPIO output
// GPIO_IN_EN=0b00000100 Enable GPIO input
// GPIO_OUT = 0b00000010 GPIO Output
// GPIO_IN = 0b00000001 GPIO Input
CFG20 = 0xD6, // FD_FIFO_8b=0b10000000 Enable 8bit FIFO mode for Flicker Detect (FD_TIME < 256)
// auto_SMUX= 0b01100000 Auto channel read-out
LED = 0xCD, // LED_ACT = 0b10000000 External LED (LDR) Control
// LED_DRIVE= 0b01111111 External LED drive strength (N - 4) >> 1
// Flicker Detection AGC Gain Max
// Max = 2^N (0 = 0.5x)
AGC_GAIN_MAX = 0xD7, // ADC_FD_GAIN_MAX = 0b11110000
AZ_CONFIG = 0xDE, // AT_NTH_ITERATION = 0b11111111 Auto-zero Frequency
// 0 - Never (Not Recommended)
// n - Every n integration cycles
// 255 - only before first measurement cycle
FD_TIME_1 = 0xE0, // FD_TIME=0b11111111 Flicker Detection Integration Time (Do not change if FDEN = 1 & PON = 1)
FD_TIME_2 = 0xE2, // FD_GAIN=0b11111000 Flicker Detection Gain (0 = 0.5x, 1 = 1x, 2 = 2x, 12 = 2048x)
// FD_TIME=0b00000111 Flicker Detection Time (Do not change if FDEN = 1 & PON = 1)
FD_CFG0 = 0xDF, // FIFO_WRITE_FD = 0b10000000 Write flicker raw data to FIFO (1 byte per sample)
FD_STATUS = 0xE3, // FD_VALID=0b00100000
// FD_SAT = 0b00010000
// FD_120HZ_VALID = 0b00001000
// FD_100HZ_VALID = 0b00000100
// FD_120HZ = 0b00000010
// FD_100HZ = 0b00000001
INTERNAB = 0xF9, // ASIEN = 0b10000000 Saturation Interrupt Enable
// SP_IEN = 0b00001000 Spectral Interrupt Enable
// FIEN = 0b00000100 FIFO Buffer Interrupt Enable
// SIEN = 0b00000001 System Interrupt Enable
CONTROL = 0xFA, // SW_RESET = 0b00001000 Software Reset
// SP_MAN_AZ= 0b00000100 Spectral Manual Auto-zero
// FIFO_CLR = 0b00000010 FIFO Buffer Clear
// CLEAR_SAI_ACT = 0b00000001 Clear sleep-after-interrupt
FIFO_MAP = 0xFC, // CH5 = 0b01000000
// CH4 = 0b00100000
// CH3 = 0b00010000
// CH2 = 0b00001000
// CH1 = 0b00000100
// CH0 = 0b00000010
// ASTATUS = 0b00000001
FIFO_LVL = 0xFD,
FDATA = 0xFE // 0xFFFF
};
constexpr uint8_t HARDWARE_ID = 0b10000001;
constexpr uint8_t CFG0_LOW_POWER = 0b00100000;
constexpr uint8_t CFG0_BANK = 0b00010000;
constexpr uint8_t CFG0_WLONG = 0b00000100;
constexpr uint8_t CFG20_6_CH = 0b00000000;
constexpr uint8_t CFG20_12_CH = 0b01000000;
constexpr uint8_t CFG20_18_CH = 0b01100000;
constexpr uint8_t ENABLE_FDEN = 0b01000000;
constexpr uint8_t ENABLE_SMUXEN = 0b00010000;
constexpr uint8_t ENABLE_WEN = 0b00001000;
constexpr uint8_t ENABLE_SP_EN = 0b00000010;
constexpr uint8_t ENABLE_PON = 0b00000001;
constexpr uint8_t FD_CFG0_FIFO_WRITE = 0b10000000;
constexpr uint8_t FD_VALID = 0b00100000;
constexpr uint8_t FD_SAT = 0b00010000;
constexpr uint8_t FD_120HZ_VALID = 0b00001000;
constexpr uint8_t FD_100HZ_VALID = 0b00000100;
constexpr uint8_t FD_120HZ = 0b00000010;
constexpr uint8_t FD_100HZ = 0b00000001;
constexpr uint8_t INTERNAB_ASIEN = 0b10000000;
constexpr uint8_t INTERNAB_SP_IEN = 0b00001000;
constexpr uint8_t INTERNAB_FIEN = 0b00000100;
constexpr uint8_t INTERNAB_SIEN = 0b00000001;
constexpr uint8_t CONTROL_SW_RESET = 0b00001000;
constexpr uint8_t CONTROL_SP_MAN_AZ = 0b00000100;
constexpr uint8_t CONTROL_FIFO_CLR = 0b00000010;
constexpr uint8_t CONTROL_CLEAR_SAI_ACT = 0b00000001;
constexpr uint8_t FIFO_MAP_CH5 = 0b01000000;
constexpr uint8_t FIFO_MAP_CH4 = 0b00100000;
constexpr uint8_t FIFO_MAP_CH3 = 0b00010000;
constexpr uint8_t FIFO_MAP_CH2 = 0b00001000;
constexpr uint8_t FIFO_MAP_CH1 = 0b00000100;
constexpr uint8_t FIFO_MAP_CH0 = 0b00000010;
constexpr uint8_t FIFO_MAP_ASTATUS = 0b00000001;
}

Wyświetl plik

@ -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);

Wyświetl plik

@ -73,7 +73,7 @@ class Hub75 {
Pixel background = 0;
// DMA & PIO
uint dma_channel = 0;
int dma_channel = -1;
uint bit = 0;
uint row = 0;

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -70,7 +70,7 @@ namespace pimoroni {
// Methods
//--------------------------------------------------
public:
void busy_wait();
void busy_wait(uint timeout_ms=45000);
void reset();
void power_off();

Wyświetl plik

@ -133,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);
}
@ -185,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
@ -207,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;
}
@ -217,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

Wyświetl plik

@ -19,6 +19,7 @@ add_subdirectory(breakout_bme688)
add_subdirectory(breakout_bmp280)
add_subdirectory(breakout_bme280)
add_subdirectory(breakout_as7262)
add_subdirectory(as7343)
add_subdirectory(breakout_bh1745)
add_subdirectory(breakout_icp10125)
add_subdirectory(breakout_scd41)

Wyświetl plik

@ -0,0 +1 @@
include("${CMAKE_CURRENT_LIST_DIR}/as7343_demo.cmake")

Wyświetl plik

@ -0,0 +1,16 @@
set(OUTPUT_NAME as7343_demo)
add_executable(
${OUTPUT_NAME}
as7343_demo.cpp
)
# enable usb output, disable uart output
pico_enable_stdio_usb(${OUTPUT_NAME} 1)
pico_enable_stdio_uart(${OUTPUT_NAME} 1)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib as7343)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,69 @@
#include "pico/stdlib.h"
#include "common/pimoroni_common.hpp"
#include "as7343.hpp"
using namespace pimoroni;
I2C i2c(6, 7);
AS7343 as7343(&i2c);
int main() {
stdio_init_all();
printf("AS7343 Demo\n");
as7343.init();
printf("Init done...\n");
uint8_t aux_id;
uint8_t revision_id;
uint8_t hardware_id;
as7343.get_version(aux_id, revision_id, hardware_id);
printf("Aux: %d, Rev: %d, HW: %d\n", aux_id, revision_id, hardware_id);
printf("set_channels\n");
as7343.set_channels(AS7343::channel_count::EIGHTEEN_CHANNEL);
printf("set_gain\n");
as7343.set_gain(1024);
printf("set_measurement_time\n");
as7343.set_measurement_time(500);
printf("set_integration_time\n");
as7343.set_integration_time(27800);
printf("set_illumination_current\n");
as7343.set_illumination_current(4);
printf("set_illumination_led\n");
as7343.set_illumination_led(true);
printf("start...\n");
while(true) {
AS7343::reading reading = as7343.read();
printf("FZ: %d FY: %d FXL: %d NIR: %d F2 %d F3: %d F4: %d F5: %d F1: %d F5: %d F7: %d F8: %d \n",
reading.FZ,
reading.FY,
reading.FXL,
reading.NIR,
reading.F2,
reading.F3,
reading.F4,
reading.F6,
reading.F1,
reading.F5,
reading.F7,
reading.F8
);
sleep_ms(1000);
}
return 0;
}

Wyświetl plik

@ -25,7 +25,7 @@ int main() {
while (1) {
BME280::bme280_reading result = bme280.read_forced();
printf("%s %0.2lf deg C, %0.2lf hPa, %0.2lf%%\n", result.status == BME280_OK ? "OK" : "ER", result.temperature, result.pressure, result.humidity);
printf("%s %0.2lf deg C, %0.2lf hPa, %0.2lf%%\n", result.status ? "OK" : "ER", result.temperature, result.pressure, result.humidity);
sleep_ms(1000);
}

Wyświetl plik

@ -30,7 +30,7 @@ int main() {
// }
// }
graphics.set_pen(1);
graphics.set_pen(15);
graphics.clear();
float s = (sin(i / 10.0f) * 1.0f) + 1.5f;

Wyświetl plik

@ -45,7 +45,7 @@ int main() {
while(true) {
BME280::bme280_reading result = bme.read_forced();
printf("%s %0.2lf deg C, %0.2lf hPa, %0.2lf%%\n", result.status == BME280_OK ? "OK" : "ER", result.temperature, result.pressure, result.humidity);
printf("%s %0.2lf deg C, %0.2lf hPa, %0.2lf%%\n", result.status ? "OK" : "ER", result.temperature, result.pressure, result.humidity);
// calculates a colour
float hue = HUE_START + ((float)(result.temperature - MIN) * (float)(HUE_END - HUE_START) / (float)(MAX - MIN));

Wyświetl plik

@ -32,11 +32,11 @@ ST7789 st7789(
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
Button button_a(Tufty2040::A);
Button button_b(Tufty2040::B);
Button button_c(Tufty2040::C);
Button button_up(Tufty2040::UP);
Button button_down(Tufty2040::DOWN);
Button button_a(Tufty2040::A, Polarity::ACTIVE_HIGH);
Button button_b(Tufty2040::B, Polarity::ACTIVE_HIGH);
Button button_c(Tufty2040::C, Polarity::ACTIVE_HIGH);
Button button_up(Tufty2040::UP, Polarity::ACTIVE_HIGH);
Button button_down(Tufty2040::DOWN, Polarity::ACTIVE_HIGH);
uint32_t time() {
absolute_time_t t = get_absolute_time();

Wyświetl plik

@ -20,9 +20,9 @@ namespace pimoroni {
static const uint8_t LED_B = 2;
static const uint8_t MICS_VREF = 14;
static const uint8_t MICS_RED = 12;
static const uint8_t MICS_RED = 13;
static const uint8_t MICS_NH3 = 11;
static const uint8_t MICS_OX = 13;
static const uint8_t MICS_OX = 12;
static const uint8_t MICS_HEATER_EN = 1;
static const bool INVERT_OUTPUT = true; //true for common cathode, false for common anode

Wyświetl plik

@ -494,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) {

Wyświetl plik

@ -488,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) {

Wyświetl plik

@ -15,7 +15,7 @@ namespace pimoroni {
}
void PicoGraphics_Pen1BitY::set_pen(uint8_t r, uint8_t g, uint8_t b) {
color = std::max(r, std::max(g, b));
color = std::max(r, std::max(g, b)) >> 4;
}
void PicoGraphics_Pen1BitY::set_pixel(const Point &p) {

Wyświetl plik

@ -485,11 +485,14 @@ namespace pimoroni {
void StellarUnicorn::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 StellarUnicorn::get_brightness() {
return this->brightness / 255.0f;
return this->brightness / 256.0f;
}
void StellarUnicorn::adjust_brightness(float delta) {

Wyświetl plik

@ -0,0 +1,20 @@
{
"deploy": [
"../deploy.md"
],
"docs": "",
"features": [
"Breadboard friendly",
"Castellated Pads",
"Micro USB"
],
"id": "rp2-pico",
"images": [
"rp2-pico.jpg"
],
"mcu": "rp2040",
"product": "Pico",
"thumbnail": "",
"url": "https://www.raspberrypi.com/products/raspberry-pi-pico/",
"vendor": "Raspberry Pi"
}

Wyświetl plik

@ -0,0 +1,10 @@
include("$(PORT_DIR)/boards/manifest.py")
include("../manifest_pico.py")
require("usb-device")
require("usb-device-hid")
require("usb-device-cdc")
require("usb-device-keyboard")
require("usb-device-mouse")
require("usb-device-midi")

Wyświetl plik

@ -0,0 +1,7 @@
# cmake file for Raspberry Pi Pico
set(PICO_BOARD "pico")
# Board specific version of the frozen manifest
set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)
set(MICROPY_C_HEAP_SIZE 4096)

Wyświetl plik

@ -0,0 +1,4 @@
// Board and hardware specific configuration
#define MICROPY_HW_BOARD_NAME "Raspberry Pi Pico"
#define MICROPY_HW_FLASH_STORAGE_BYTES (1024 * 1024)
#define MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE 1

Wyświetl plik

@ -0,0 +1,28 @@
GP0,GPIO0
GP1,GPIO1
GP2,GPIO2
GP3,GPIO3
GP4,GPIO4
GP5,GPIO5
GP6,GPIO6
GP7,GPIO7
GP8,GPIO8
GP9,GPIO9
GP10,GPIO10
GP11,GPIO11
GP12,GPIO12
GP13,GPIO13
GP14,GPIO14
GP15,GPIO15
GP16,GPIO16
GP17,GPIO17
GP18,GPIO18
GP19,GPIO19
GP20,GPIO20
GP21,GPIO21
GP22,GPIO22
GP25,GPIO25
GP26,GPIO26
GP27,GPIO27
GP28,GPIO28
LED,GPIO25
1 GP0 GPIO0
2 GP1 GPIO1
3 GP2 GPIO2
4 GP3 GPIO3
5 GP4 GPIO4
6 GP5 GPIO5
7 GP6 GPIO6
8 GP7 GPIO7
9 GP8 GPIO8
10 GP9 GPIO9
11 GP10 GPIO10
12 GP11 GPIO11
13 GP12 GPIO12
14 GP13 GPIO13
15 GP14 GPIO14
16 GP15 GPIO15
17 GP16 GPIO16
18 GP17 GPIO17
19 GP18 GPIO18
20 GP19 GPIO19
21 GP20 GPIO20
22 GP21 GPIO21
23 GP22 GPIO22
24 GP25 GPIO25
25 GP26 GPIO26
26 GP27 GPIO27
27 GP28 GPIO28
28 LED GPIO25

Wyświetl plik

@ -2,4 +2,4 @@ freeze("../modules_py", "gfx_pack.py")
freeze("../modules_py", "interstate75.py")
freeze("../modules_py", "pimoroni.py")
freeze("../modules_py", "boot.py")
freeze("../modules_py", "boot.py")

Wyświetl plik

@ -0,0 +1,97 @@
from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_P4
from breakout_as7343 import BreakoutAS7343
from pimoroni_i2c import PimoroniI2C
display = PicoGraphics(DISPLAY_TUFTY_2040, pen_type=PEN_P4, rotate=90)
WIDTH, HEIGHT = display.get_bounds()
BLACK = display.create_pen(0, 0, 0)
FZ = display.create_pen(0, 0, 128)
FY = display.create_pen(0, 255, 0)
FXL = display.create_pen(255, 128, 0)
NIR = display.create_pen(128, 0, 0)
F2 = display.create_pen(128, 0, 128)
F3 = display.create_pen(0, 0, 255)
F4 = display.create_pen(0, 128, 255)
F6 = display.create_pen(128, 64, 0)
F1 = display.create_pen(196, 0, 196)
F5 = display.create_pen(64, 255, 64)
F7 = display.create_pen(255, 0, 0)
F8 = display.create_pen(196, 0, 0)
WHITE = display.create_pen(255, 255, 255)
i2c = PimoroniI2C(sda=4, scl=5)
as7343 = BreakoutAS7343(i2c)
as7343.set_channels(18)
as7343.set_gain(1024)
as7343.set_measurement_time(33) # Roughly 30fps at 16ms/measurement
as7343.set_integration_time(27800)
as7343.set_illumination_current(4)
as7343.set_illumination_led(True)
BAR_WIDTH = 18
BAR_SPACING = 4
MARGIN = display.measure_text("NIR") + 2
BAR_HEIGHT = WIDTH - MARGIN
OFFSET_LEFT = int((HEIGHT - ((BAR_WIDTH + BAR_SPACING) * 12)) / 2)
# Satrting max values for auto-ranging
# From figure 8 of the datasheet
MAX_VALUES = [
2711,
4684,
5970,
13226,
2371,
962,
3926,
4170,
7760,
1967,
6774,
1166
]
LABELS = [
"FZ",
"FY",
"FXL",
"NIR",
"F2",
"F3",
"F4",
"F6",
"F1",
"F5",
"F7",
"F8"
]
while True:
display.set_pen(0)
display.clear()
readings = as7343.read()
for i, reading in enumerate(readings):
MAX_VALUES[i] = max(reading, MAX_VALUES[i])
scaled = int(reading / MAX_VALUES[i] * BAR_HEIGHT)
y = i * (BAR_WIDTH + BAR_SPACING)
y += OFFSET_LEFT
display.set_pen(i + 1)
display.rectangle(MARGIN, y, scaled, BAR_WIDTH)
display.set_pen(WHITE)
display.text(LABELS[i], 0, y + 1)
display.update()

Wyświetl plik

@ -0,0 +1,30 @@
import time
from machine import Pin
from pimoroni_i2c import PimoroniI2C
from breakout_ltr559 import BreakoutLTR559
PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5}
PINS_PICO_EXPLORER = {"sda": 20, "scl": 21}
PIN_INTERRUPT = 22 # 3 for Breakout Garden
i2c = PimoroniI2C(**PINS_PICO_EXPLORER)
ltr = BreakoutLTR559(i2c, interrupt=PIN_INTERRUPT)
interrupt = Pin(PIN_INTERRUPT, Pin.IN, Pin.PULL_DOWN)
ltr.light_threshold(0, 10) # COUNTS, NOT LUX!!!
ltr.proximity_threshold(0, 10)
def read(pin):
reading = ltr.get_reading()
if reading is not None:
print("T: ", time.ticks_ms(), " Lux: ", reading[BreakoutLTR559.LUX], " Prox: ", reading[BreakoutLTR559.PROXIMITY])
interrupt.irq(trigger=Pin.IRQ_RISING, handler=read)
part_id = ltr.part_id()
print("Found LTR559. Part ID: 0x", '{:02x}'.format(part_id), sep="")
while True:
pass

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,276 @@
# SPDX-FileCopyrightText: 2023 Christopher Parrott for Pimoroni Ltd
#
# SPDX-License-Identifier: MIT
import os
import math
import struct
from machine import I2S, Pin
"""
A class for playing Wav files out of an I2S audio amp. It can also play pure tones.
This code is based heavily on the work of Mike Teachman, at:
https://github.com/miketeachman/micropython-i2s-examples/blob/master/examples/wavplayer.py
"""
class WavPlayer:
# Internal states
PLAY = 0
PAUSE = 1
FLUSH = 2
STOP = 3
NONE = 4
MODE_WAV = 0
MODE_TONE = 1
# Default buffer length
SILENCE_BUFFER_LENGTH = 1000
WAV_BUFFER_LENGTH = 10000
INTERNAL_BUFFER_LENGTH = 20000
TONE_SAMPLE_RATE = 44_100
TONE_BITS_PER_SAMPLE = 16
TONE_FULL_WAVES = 2
def __init__(self, id, sck_pin, ws_pin, sd_pin, amp_enable=None, ibuf_len=INTERNAL_BUFFER_LENGTH, root="/"):
self.__id = id
self.__sck_pin = sck_pin
self.__ws_pin = ws_pin
self.__sd_pin = sd_pin
self.__ibuf_len = ibuf_len
self.__enable = None
if amp_enable is not None:
self.__enable = Pin(amp_enable, Pin.OUT)
# Set the directory to search for files in
self.set_root(root)
self.__state = WavPlayer.NONE
self.__mode = WavPlayer.MODE_WAV
self.__wav_file = None
self.__loop_wav = False
self.__first_sample_offset = None
self.__flush_count = 0
self.__audio_out = None
# Allocate a small array of blank audio samples used for silence
self.__silence_samples = bytearray(self.SILENCE_BUFFER_LENGTH)
# Allocate a larger array for WAV audio samples, using a memoryview for more efficient access
self.__wav_samples_mv = memoryview(bytearray(self.WAV_BUFFER_LENGTH))
# Reserve a variable for audio samples used for tones
self.__tone_samples = None
self.__queued_samples = None
def set_root(self, root):
self.__root = root.rstrip("/") + "/"
def play_wav(self, wav_file, loop=False):
if os.listdir(self.__root).count(wav_file) == 0:
raise ValueError(f"'{wav_file}' not found")
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
self.__wav_file = open(self.__root + wav_file, "rb") # Open the chosen WAV file in read-only, binary mode
self.__loop_wav = loop # Record if the user wants the file to loop
# Parse the WAV file, returning the necessary parameters to initialise I2S communication
format, sample_rate, bits_per_sample, self.__first_sample_offset, self.sample_size = WavPlayer.__parse_wav(self.__wav_file)
# Keep a track of total bytes read from WAV File
self.total_bytes_read = 0
self.__wav_file.seek(self.__first_sample_offset) # Advance to first byte of sample data
self.__start_i2s(bits=bits_per_sample,
format=format,
rate=sample_rate,
state=WavPlayer.PLAY,
mode=WavPlayer.MODE_WAV)
def play_tone(self, frequency, amplitude):
if frequency < 20.0 or frequency > 20_000:
raise ValueError("frequency out of range. Expected between 20Hz and 20KHz")
if amplitude < 0.0 or amplitude > 1.0:
raise ValueError("amplitude out of range. Expected 0.0 to 1.0")
# Create a buffer containing the pure tone samples
samples_per_cycle = self.TONE_SAMPLE_RATE // frequency
sample_size_in_bytes = self.TONE_BITS_PER_SAMPLE // 8
samples = bytearray(self.TONE_FULL_WAVES * samples_per_cycle * sample_size_in_bytes)
range = pow(2, self.TONE_BITS_PER_SAMPLE) // 2
format = "<h" if self.TONE_BITS_PER_SAMPLE == 16 else "<l"
# Populate the buffer with multiple cycles to avoid it completing too quickly and causing drop outs
for i in range(samples_per_cycle * self.TONE_FULL_WAVES):
sample = int((range - 1) * (math.sin(2 * math.pi * i / samples_per_cycle)) * amplitude)
struct.pack_into(format, samples, i * sample_size_in_bytes, sample)
# Are we not already playing tones?
if not (self.__mode == WavPlayer.MODE_TONE and (self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE)):
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
self.__tone_samples = samples
self.__start_i2s(bits=self.TONE_BITS_PER_SAMPLE,
format=I2S.MONO,
rate=self.TONE_SAMPLE_RATE,
state=WavPlayer.PLAY,
mode=WavPlayer.MODE_TONE)
else:
self.__queued_samples = samples
self.__state = WavPlayer.PLAY
def pause(self):
if self.__state == WavPlayer.PLAY:
self.__state = WavPlayer.PAUSE # Enter the pause state on the next callback
def resume(self):
if self.__state == WavPlayer.PAUSE:
self.__state = WavPlayer.PLAY # Enter the play state on the next callback
def stop(self):
if self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE:
if self.__mode == WavPlayer.MODE_WAV:
# Enter the flush state on the next callback and close the file
# It is done in this order to prevent the callback entering the play
# state after we close the file but before we change the state)
self.__state = WavPlayer.FLUSH
self.__wav_file.close()
else:
self.__state = WavPlayer.STOP
def is_playing(self):
return self.__state != WavPlayer.NONE and self.__state != WavPlayer.STOP
def is_paused(self):
return self.__state == WavPlayer.PAUSE
def __start_i2s(self, bits=16, format=I2S.MONO, rate=44_100, state=STOP, mode=MODE_WAV):
import gc
gc.collect()
self.__audio_out = I2S(
self.__id,
sck=self.__sck_pin,
ws=self.__ws_pin,
sd=self.__sd_pin,
mode=I2S.TX,
bits=bits,
format=format,
rate=rate,
ibuf=self.__ibuf_len,
)
self.__state = state
self.__mode = mode
self.__flush_count = self.__ibuf_len // self.SILENCE_BUFFER_LENGTH + 1
self.__audio_out.irq(self.__i2s_callback)
self.__audio_out.write(self.__silence_samples)
if self.__enable is not None:
self.__enable.on()
def __stop_i2s(self):
self.stop() # Stop any active playback
while self.is_playing(): # and wait for it to complete
pass
if self.__enable is not None:
self.__enable.off()
if self.__audio_out is not None:
self.__audio_out.deinit() # Deinit any active I2S comms
self.__state == WavPlayer.NONE # Return to the none state
def __i2s_callback(self, arg):
# PLAY
if self.__state == WavPlayer.PLAY:
if self.__mode == WavPlayer.MODE_WAV:
num_read = self.__wav_file.readinto(self.__wav_samples_mv) # Read the next section of the WAV file
self.total_bytes_read += num_read
# Have we reached the end of the file?
if num_read == 0:
# Do we want to loop the WAV playback?
if self.__loop_wav:
_ = self.__wav_file.seek(self.__first_sample_offset) # Play again, so advance to first byte of sample data
else:
self.__wav_file.close() # Stop playing, so close the file
self.__state = WavPlayer.FLUSH # and enter the flush state on the next callback
self.__audio_out.write(self.__silence_samples) # In both cases play silence to end this callback
else:
if num_read > 0 and num_read < self.WAV_BUFFER_LENGTH:
num_read = num_read - (self.total_bytes_read - self.sample_size)
self.__audio_out.write(self.__wav_samples_mv[: num_read]) # We are within the file, so write out the next audio samples
else:
if self.__queued_samples is not None:
self.__tone_samples = self.__queued_samples
self.__queued_samples = None
self.__audio_out.write(self.__tone_samples)
# PAUSE or STOP
elif self.__state == WavPlayer.PAUSE or self.__state == WavPlayer.STOP:
self.__audio_out.write(self.__silence_samples) # Play silence
# FLUSH
elif self.__state == WavPlayer.FLUSH:
# Flush is used to allow the residual audio samples in the internal buffer to be written
# to the I2S peripheral. This step avoids part of the sound file from being cut off
if self.__flush_count > 0:
self.__flush_count -= 1
else:
self.__state = WavPlayer.STOP # Enter the stop state on the next callback
self.__audio_out.write(self.__silence_samples) # Play silence
# NONE
elif self.__state == WavPlayer.NONE:
pass
@staticmethod
def __parse_wav(wav_file):
chunk_ID = wav_file.read(4)
if chunk_ID != b"RIFF":
raise ValueError("WAV chunk ID invalid")
_ = wav_file.read(4) # chunk_size
format = wav_file.read(4)
if format != b"WAVE":
raise ValueError("WAV format invalid")
sub_chunk1_ID = wav_file.read(4)
if sub_chunk1_ID != b"fmt ":
raise ValueError("WAV sub chunk 1 ID invalid")
_ = wav_file.read(4) # sub_chunk1_size
_ = struct.unpack("<H", wav_file.read(2))[0] # audio_format
num_channels = struct.unpack("<H", wav_file.read(2))[0]
if num_channels == 1:
format = I2S.MONO
else:
format = I2S.STEREO
sample_rate = struct.unpack("<I", wav_file.read(4))[0]
# if sample_rate != 44_100 and sample_rate != 48_000:
# raise ValueError(f"WAV sample rate of {sample_rate} invalid. Only 44.1KHz or 48KHz audio are supported")
_ = struct.unpack("<I", wav_file.read(4))[0] # byte_rate
_ = struct.unpack("<H", wav_file.read(2))[0] # block_align
bits_per_sample = struct.unpack("<H", wav_file.read(2))[0]
# usually the sub chunk2 ID ("data") comes next, but
# some online MP3->WAV converters add
# binary data before "data". So, read a fairly large
# block of bytes and search for "data".
binary_block = wav_file.read(200)
offset = binary_block.find(b"data")
if offset == -1:
raise ValueError("WAV sub chunk 2 ID not found")
wav_file.seek(40)
sub_chunk2_size = struct.unpack("<I", wav_file.read(4))[0]
return (format, sample_rate, bits_per_sample, 44 + offset, sub_chunk2_size)

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,91 @@
from machine import Timer
from audio import WavPlayer
from cosmic import CosmicUnicorn
from picographics import PicoGraphics, DISPLAY_COSMIC_UNICORN as DISPLAY
import time
cu = CosmicUnicorn()
graphics = PicoGraphics(DISPLAY)
graphics.set_font("bitmap6")
WHITE = graphics.create_pen(255, 255, 255)
BLUE = graphics.create_pen(0, 0, 255)
CLEAR = graphics.create_pen(0, 0, 0)
RED = graphics.create_pen(255, 0, 0)
GREEN = graphics.create_pen(0, 255, 0)
cu.set_brightness(0.7)
audio = WavPlayer(0, 10, 11, 9, amp_enable=22)
class Countdown(object):
def __init__(self):
self.timer_running = False
self.total_seconds = 0
self.timer = None
def process_input(self):
if cu.is_pressed(CosmicUnicorn.SWITCH_VOLUME_UP):
self.total_seconds += 1
if cu.is_pressed(CosmicUnicorn.SWITCH_VOLUME_DOWN):
if self.total_seconds > 0:
self.total_seconds -= 1
if cu.is_pressed(CosmicUnicorn.SWITCH_SLEEP):
self.start_timer()
def display_time(self):
seconds = self.total_seconds % (24 * 3600)
seconds %= 3600
minutes = seconds // 60
seconds %= 60
# Add leading zeros to the minutes and seconds
if len(str(minutes)) == 1:
minutes = "0{}".format(minutes)
if len(str(seconds)) == 1:
seconds = "0{}".format(seconds)
return "{}:{}".format(minutes, seconds)
def draw(self):
graphics.set_pen(graphics.create_pen(0, 0, 0))
graphics.clear()
graphics.set_pen(BLUE)
graphics.circle(0, 0, 12)
graphics.set_pen(GREEN)
graphics.circle(25, 30, 5)
graphics.set_pen(RED)
graphics.circle(0, 32, 12)
graphics.set_pen(CLEAR)
graphics.rectangle(0, 11, CosmicUnicorn.WIDTH, 9)
graphics.set_pen(WHITE)
graphics.text(self.display_time(), 4, 12, -1, 1)
cu.update(graphics)
def start_timer(self):
if not self.timer_running:
self.timer = Timer(mode=Timer.PERIODIC, period=1000, callback=self.countdown)
self.timer_running = True
def reset(self):
self.timer.deinit()
self.timer_running = False
def countdown(self, arg):
if self.total_seconds == 0:
audio.play_wav("doorbell.wav", False)
self.reset()
else:
self.total_seconds -= 1
count = Countdown()
while 1:
count.process_input()
count.draw()
time.sleep(0.07)

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,74 @@
from audio import WavPlayer
from cosmic import CosmicUnicorn
from picographics import PicoGraphics, DISPLAY_COSMIC_UNICORN as DISPLAY
from time import sleep
cu = CosmicUnicorn()
graphics = PicoGraphics(DISPLAY)
audio = WavPlayer(0, 10, 11, 9, amp_enable=22)
WHITE = graphics.create_pen(255, 255, 255)
RED = graphics.create_pen(255, 0, 0)
class Menu(object):
def __init__(self):
self.items = ["Pew 1", "Pew 2", "Pew 3"]
self.selected = 0
# A function to draw only the menu elements.
# Helps to keep our main function tidy!
def draw_menu(self):
graphics.set_pen(WHITE)
for item in range(len(self.items)):
if self.selected == item:
graphics.set_pen(RED)
graphics.text(self.items[item], 0, 2 + item * 10, 31, 1)
graphics.set_pen(WHITE)
# Make changes based on the currently selected menu item
def process_selected(self):
if self.selected == 0:
audio.play_wav("Pew1.wav", False)
if self.selected == 1:
audio.play_wav("Pew2.wav", False)
if self.selected == 2:
audio.play_wav("Pew3.wav", False)
menu = Menu()
graphics.set_font("bitmap6")
cu.set_brightness(0.7)
while True:
graphics.set_pen(graphics.create_pen(0, 0, 0))
graphics.clear()
if cu.is_pressed(CosmicUnicorn.SWITCH_BRIGHTNESS_DOWN):
audio.play_wav("buttonbeep.wav", False)
if menu.selected + 1 < len(menu.items):
menu.selected += 1
else:
menu.selected = 0
if cu.is_pressed(CosmicUnicorn.SWITCH_BRIGHTNESS_UP):
audio.play_wav("buttonbeep.wav", False)
if menu.selected > 0:
menu.selected -= 1
else:
menu.selected = len(menu.items) - 1
if cu.is_pressed(CosmicUnicorn.SWITCH_SLEEP):
menu.process_selected()
menu.draw_menu()
cu.update(graphics)
sleep(0.2)

Wyświetl plik

@ -0,0 +1,8 @@
from audio import WavPlayer
sound = WavPlayer(0, 10, 11, 9, amp_enable=22)
sound.play_wav("beepboop.wav", False)
while sound.is_playing():
pass

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,276 @@
# SPDX-FileCopyrightText: 2023 Christopher Parrott for Pimoroni Ltd
#
# SPDX-License-Identifier: MIT
import os
import math
import struct
from machine import I2S, Pin
"""
A class for playing Wav files out of an I2S audio amp. It can also play pure tones.
This code is based heavily on the work of Mike Teachman, at:
https://github.com/miketeachman/micropython-i2s-examples/blob/master/examples/wavplayer.py
"""
class WavPlayer:
# Internal states
PLAY = 0
PAUSE = 1
FLUSH = 2
STOP = 3
NONE = 4
MODE_WAV = 0
MODE_TONE = 1
# Default buffer length
SILENCE_BUFFER_LENGTH = 1000
WAV_BUFFER_LENGTH = 10000
INTERNAL_BUFFER_LENGTH = 20000
TONE_SAMPLE_RATE = 44_100
TONE_BITS_PER_SAMPLE = 16
TONE_FULL_WAVES = 2
def __init__(self, id, sck_pin, ws_pin, sd_pin, amp_enable=None, ibuf_len=INTERNAL_BUFFER_LENGTH, root="/"):
self.__id = id
self.__sck_pin = sck_pin
self.__ws_pin = ws_pin
self.__sd_pin = sd_pin
self.__ibuf_len = ibuf_len
self.__enable = None
if amp_enable is not None:
self.__enable = Pin(amp_enable, Pin.OUT)
# Set the directory to search for files in
self.set_root(root)
self.__state = WavPlayer.NONE
self.__mode = WavPlayer.MODE_WAV
self.__wav_file = None
self.__loop_wav = False
self.__first_sample_offset = None
self.__flush_count = 0
self.__audio_out = None
# Allocate a small array of blank audio samples used for silence
self.__silence_samples = bytearray(self.SILENCE_BUFFER_LENGTH)
# Allocate a larger array for WAV audio samples, using a memoryview for more efficient access
self.__wav_samples_mv = memoryview(bytearray(self.WAV_BUFFER_LENGTH))
# Reserve a variable for audio samples used for tones
self.__tone_samples = None
self.__queued_samples = None
def set_root(self, root):
self.__root = root.rstrip("/") + "/"
def play_wav(self, wav_file, loop=False):
if os.listdir(self.__root).count(wav_file) == 0:
raise ValueError(f"'{wav_file}' not found")
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
self.__wav_file = open(self.__root + wav_file, "rb") # Open the chosen WAV file in read-only, binary mode
self.__loop_wav = loop # Record if the user wants the file to loop
# Parse the WAV file, returning the necessary parameters to initialise I2S communication
format, sample_rate, bits_per_sample, self.__first_sample_offset, self.sample_size = WavPlayer.__parse_wav(self.__wav_file)
# Keep a track of total bytes read from WAV File
self.total_bytes_read = 0
self.__wav_file.seek(self.__first_sample_offset) # Advance to first byte of sample data
self.__start_i2s(bits=bits_per_sample,
format=format,
rate=sample_rate,
state=WavPlayer.PLAY,
mode=WavPlayer.MODE_WAV)
def play_tone(self, frequency, amplitude):
if frequency < 20.0 or frequency > 20_000:
raise ValueError("frequency out of range. Expected between 20Hz and 20KHz")
if amplitude < 0.0 or amplitude > 1.0:
raise ValueError("amplitude out of range. Expected 0.0 to 1.0")
# Create a buffer containing the pure tone samples
samples_per_cycle = self.TONE_SAMPLE_RATE // frequency
sample_size_in_bytes = self.TONE_BITS_PER_SAMPLE // 8
samples = bytearray(self.TONE_FULL_WAVES * samples_per_cycle * sample_size_in_bytes)
range = pow(2, self.TONE_BITS_PER_SAMPLE) // 2
format = "<h" if self.TONE_BITS_PER_SAMPLE == 16 else "<l"
# Populate the buffer with multiple cycles to avoid it completing too quickly and causing drop outs
for i in range(samples_per_cycle * self.TONE_FULL_WAVES):
sample = int((range - 1) * (math.sin(2 * math.pi * i / samples_per_cycle)) * amplitude)
struct.pack_into(format, samples, i * sample_size_in_bytes, sample)
# Are we not already playing tones?
if not (self.__mode == WavPlayer.MODE_TONE and (self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE)):
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
self.__tone_samples = samples
self.__start_i2s(bits=self.TONE_BITS_PER_SAMPLE,
format=I2S.MONO,
rate=self.TONE_SAMPLE_RATE,
state=WavPlayer.PLAY,
mode=WavPlayer.MODE_TONE)
else:
self.__queued_samples = samples
self.__state = WavPlayer.PLAY
def pause(self):
if self.__state == WavPlayer.PLAY:
self.__state = WavPlayer.PAUSE # Enter the pause state on the next callback
def resume(self):
if self.__state == WavPlayer.PAUSE:
self.__state = WavPlayer.PLAY # Enter the play state on the next callback
def stop(self):
if self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE:
if self.__mode == WavPlayer.MODE_WAV:
# Enter the flush state on the next callback and close the file
# It is done in this order to prevent the callback entering the play
# state after we close the file but before we change the state)
self.__state = WavPlayer.FLUSH
self.__wav_file.close()
else:
self.__state = WavPlayer.STOP
def is_playing(self):
return self.__state != WavPlayer.NONE and self.__state != WavPlayer.STOP
def is_paused(self):
return self.__state == WavPlayer.PAUSE
def __start_i2s(self, bits=16, format=I2S.MONO, rate=44_100, state=STOP, mode=MODE_WAV):
import gc
gc.collect()
self.__audio_out = I2S(
self.__id,
sck=self.__sck_pin,
ws=self.__ws_pin,
sd=self.__sd_pin,
mode=I2S.TX,
bits=bits,
format=format,
rate=rate,
ibuf=self.__ibuf_len,
)
self.__state = state
self.__mode = mode
self.__flush_count = self.__ibuf_len // self.SILENCE_BUFFER_LENGTH + 1
self.__audio_out.irq(self.__i2s_callback)
self.__audio_out.write(self.__silence_samples)
if self.__enable is not None:
self.__enable.on()
def __stop_i2s(self):
self.stop() # Stop any active playback
while self.is_playing(): # and wait for it to complete
pass
if self.__enable is not None:
self.__enable.off()
if self.__audio_out is not None:
self.__audio_out.deinit() # Deinit any active I2S comms
self.__state == WavPlayer.NONE # Return to the none state
def __i2s_callback(self, arg):
# PLAY
if self.__state == WavPlayer.PLAY:
if self.__mode == WavPlayer.MODE_WAV:
num_read = self.__wav_file.readinto(self.__wav_samples_mv) # Read the next section of the WAV file
self.total_bytes_read += num_read
# Have we reached the end of the file?
if num_read == 0:
# Do we want to loop the WAV playback?
if self.__loop_wav:
_ = self.__wav_file.seek(self.__first_sample_offset) # Play again, so advance to first byte of sample data
else:
self.__wav_file.close() # Stop playing, so close the file
self.__state = WavPlayer.FLUSH # and enter the flush state on the next callback
self.__audio_out.write(self.__silence_samples) # In both cases play silence to end this callback
else:
if num_read > 0 and num_read < self.WAV_BUFFER_LENGTH:
num_read = num_read - (self.total_bytes_read - self.sample_size)
self.__audio_out.write(self.__wav_samples_mv[: num_read]) # We are within the file, so write out the next audio samples
else:
if self.__queued_samples is not None:
self.__tone_samples = self.__queued_samples
self.__queued_samples = None
self.__audio_out.write(self.__tone_samples)
# PAUSE or STOP
elif self.__state == WavPlayer.PAUSE or self.__state == WavPlayer.STOP:
self.__audio_out.write(self.__silence_samples) # Play silence
# FLUSH
elif self.__state == WavPlayer.FLUSH:
# Flush is used to allow the residual audio samples in the internal buffer to be written
# to the I2S peripheral. This step avoids part of the sound file from being cut off
if self.__flush_count > 0:
self.__flush_count -= 1
else:
self.__state = WavPlayer.STOP # Enter the stop state on the next callback
self.__audio_out.write(self.__silence_samples) # Play silence
# NONE
elif self.__state == WavPlayer.NONE:
pass
@staticmethod
def __parse_wav(wav_file):
chunk_ID = wav_file.read(4)
if chunk_ID != b"RIFF":
raise ValueError("WAV chunk ID invalid")
_ = wav_file.read(4) # chunk_size
format = wav_file.read(4)
if format != b"WAVE":
raise ValueError("WAV format invalid")
sub_chunk1_ID = wav_file.read(4)
if sub_chunk1_ID != b"fmt ":
raise ValueError("WAV sub chunk 1 ID invalid")
_ = wav_file.read(4) # sub_chunk1_size
_ = struct.unpack("<H", wav_file.read(2))[0] # audio_format
num_channels = struct.unpack("<H", wav_file.read(2))[0]
if num_channels == 1:
format = I2S.MONO
else:
format = I2S.STEREO
sample_rate = struct.unpack("<I", wav_file.read(4))[0]
# if sample_rate != 44_100 and sample_rate != 48_000:
# raise ValueError(f"WAV sample rate of {sample_rate} invalid. Only 44.1KHz or 48KHz audio are supported")
_ = struct.unpack("<I", wav_file.read(4))[0] # byte_rate
_ = struct.unpack("<H", wav_file.read(2))[0] # block_align
bits_per_sample = struct.unpack("<H", wav_file.read(2))[0]
# usually the sub chunk2 ID ("data") comes next, but
# some online MP3->WAV converters add
# binary data before "data". So, read a fairly large
# block of bytes and search for "data".
binary_block = wav_file.read(200)
offset = binary_block.find(b"data")
if offset == -1:
raise ValueError("WAV sub chunk 2 ID not found")
wav_file.seek(40)
sub_chunk2_size = struct.unpack("<I", wav_file.read(4))[0]
return (format, sample_rate, bits_per_sample, 44 + offset, sub_chunk2_size)

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,86 @@
from machine import Timer
from audio import WavPlayer
from galactic import GalacticUnicorn
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
import time
gu = GalacticUnicorn()
graphics = PicoGraphics(DISPLAY)
graphics.set_font("bitmap6")
WHITE = graphics.create_pen(255, 255, 255)
BLUE = graphics.create_pen(0, 0, 255)
CLEAR = graphics.create_pen(0, 0, 0)
RED = graphics.create_pen(255, 0, 0)
GREEN = graphics.create_pen(0, 255, 0)
gu.set_brightness(0.7)
audio = WavPlayer(0, 10, 11, 9, amp_enable=22)
class Countdown(object):
def __init__(self):
self.timer_running = False
self.total_seconds = 0
self.timer = None
def process_input(self):
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_UP):
self.total_seconds += 1
if gu.is_pressed(GalacticUnicorn.SWITCH_VOLUME_DOWN):
if self.total_seconds > 0:
self.total_seconds -= 1
if gu.is_pressed(GalacticUnicorn.SWITCH_SLEEP):
self.start_timer()
def display_time(self):
seconds = self.total_seconds % (24 * 3600)
seconds %= 3600
minutes = seconds // 60
seconds %= 60
# Add leading zeros to the minutes and seconds
if len(str(minutes)) == 1:
minutes = "0{}".format(minutes)
if len(str(seconds)) == 1:
seconds = "0{}".format(seconds)
return "{}:{}".format(minutes, seconds)
def draw(self):
graphics.set_pen(graphics.create_pen(0, 0, 0))
graphics.clear()
graphics.set_pen(BLUE)
graphics.circle(0, 0, 12)
graphics.set_pen(GREEN)
graphics.circle(50, 7, 6)
graphics.set_pen(WHITE)
graphics.text(self.display_time(), 15, 2, -1, 1)
gu.update(graphics)
def start_timer(self):
if not self.timer_running:
self.timer = Timer(mode=Timer.PERIODIC, period=1000, callback=self.countdown)
self.timer_running = True
def reset(self):
self.timer.deinit()
self.timer_running = False
def countdown(self, arg):
if self.total_seconds == 0:
audio.play_wav("doorbell.wav", False)
self.reset()
else:
self.total_seconds -= 1
count = Countdown()
while 1:
count.process_input()
count.draw()
time.sleep(0.07)

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,80 @@
from audio import WavPlayer
from galactic import GalacticUnicorn
from picographics import PicoGraphics, DISPLAY_GALACTIC_UNICORN as DISPLAY
from time import sleep
gu = GalacticUnicorn()
graphics = PicoGraphics(DISPLAY)
audio = WavPlayer(0, 10, 11, 9, amp_enable=22)
WHITE = graphics.create_pen(255, 255, 255)
RED = graphics.create_pen(255, 0, 0)
GREEN = graphics.create_pen(0, 255, 150)
CLEAR = graphics.create_pen(0, 0, 0)
class Menu(object):
def __init__(self):
self.items = ["Pew 1", "Pew 2", "Pew 3"]
self.selected = 0
# A function to draw only the menu elements.
# Helps to keep our main function tidy!
def draw_menu(self):
graphics.set_pen(WHITE)
graphics.set_pen(GREEN)
graphics.line(0, 5, GalacticUnicorn.WIDTH, 5)
graphics.set_pen(CLEAR)
graphics.rectangle(13, 2, 26, 5)
for item in range(len(self.items)):
if self.selected == item:
graphics.set_pen(WHITE)
graphics.text(self.items[self.selected], 14, 2, 31, 1)
# Make changes based on the currently selected menu item
def process_selected(self):
if self.selected == 0:
audio.play_wav("Pew1.wav", False)
if self.selected == 1:
audio.play_wav("Pew2.wav", False)
if self.selected == 2:
audio.play_wav("Pew3.wav", False)
menu = Menu()
graphics.set_font("bitmap6")
gu.set_brightness(0.7)
while True:
graphics.set_pen(graphics.create_pen(0, 0, 0))
graphics.clear()
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_DOWN):
audio.play_wav("buttonbeep.wav", False)
if menu.selected + 1 < len(menu.items):
menu.selected += 1
else:
menu.selected = 0
if gu.is_pressed(GalacticUnicorn.SWITCH_BRIGHTNESS_UP):
audio.play_wav("buttonbeep.wav", False)
if menu.selected > 0:
menu.selected -= 1
else:
menu.selected = len(menu.items) - 1
if gu.is_pressed(GalacticUnicorn.SWITCH_SLEEP):
menu.process_selected()
menu.draw_menu()
gu.update(graphics)
sleep(0.2)

Wyświetl plik

@ -0,0 +1,8 @@
from audio import WavPlayer
sound = WavPlayer(0, 10, 11, 9, amp_enable=22)
sound.play_wav("beepboop.wav", False)
while sound.is_playing():
pass

Wyświetl plik

@ -0,0 +1,36 @@
from picographics import PicoGraphics, DISPLAY_INKY_FRAME as DISPLAY # 5.7"
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_4 as DISPLAY # 4.0"
# from picographics import PicoGraphics, DISPLAY_INKY_FRAME_7 as DISPLAY # 7.3"
import pngdec
# Create a PicoGraphics instance
graphics = PicoGraphics(DISPLAY)
WIDTH, HEIGHT = graphics.get_bounds()
# Set the font
graphics.set_font("bitmap8")
# Create an instance of the PNG Decoder
png = pngdec.PNG(graphics)
# Clear the screen
graphics.set_pen(1)
graphics.clear()
graphics.set_pen(0)
# Few lines of text.
graphics.text("PNG Pencil", 70, 100, WIDTH, 3)
# Open our PNG File from flash. In this example we're using a cartoon pencil.
# You can use Thonny to transfer PNG Images to your Inky Frame.
try:
png.open_file("pencil_256x256.png")
# Decode our PNG file and set the X and Y
png.decode(200, 100)
except OSError:
graphics.text("Unable to find PNG file! Copy 'pencil_256x256.png' to your Inky Frame using Thonny :)", 10, 70, WIDTH, 3)
# Start the screen update
graphics.update()

Wyświetl plik

@ -31,7 +31,7 @@ Alternatively, you can transfer them using Thonny, but you will have to mount th
```python
import os
import sdcard
from machine import Pin
from machine import Pin, SPI
sd_spi = SPI(0, sck=Pin(18, Pin.OUT), mosi=Pin(19, Pin.OUT), miso=Pin(16, Pin.OUT))
sd = sdcard.SDCard(sd_spi, Pin(22))
os.mount(sd, "/sd")

Wyświetl plik

@ -17,18 +17,16 @@ HEIGHT = None
code = qrcode.QRCode()
def read_until(stream, char):
def read_until(stream, find):
result = b""
while True:
c = stream.read(1)
if c == char:
while len(c := stream.read(1)) > 0:
if c == find:
return result
result += c
def discard_until(stream, c):
while stream.read(1) != c:
pass
def discard_until(stream, find):
_ = read_until(stream, find)
def parse_xml_stream(s, accept_tags, group_by, max_items=3):
@ -36,6 +34,7 @@ def parse_xml_stream(s, accept_tags, group_by, max_items=3):
text = b""
count = 0
current = {}
while True:
char = s.read(1)
if len(char) == 0:
@ -79,9 +78,9 @@ def parse_xml_stream(s, accept_tags, group_by, max_items=3):
else:
current_tag = read_until(s, b">")
tag += [next_char + current_tag.split(b" ")[0]]
text = b""
gc.collect()
if not current_tag.endswith(b"/"):
tag += [next_char + current_tag.split(b" ")[0]]
text = b""
else:
text += char
@ -113,7 +112,7 @@ def get_rss():
except OSError as e:
print(e)
return False
return []
feed = None
@ -135,7 +134,7 @@ def draw():
graphics.set_pen(0)
# Draws 2 articles from the feed if they're available.
if feed:
if len(feed) > 0:
# Title
graphics.set_pen(graphics.create_pen(200, 0, 0))

Wyświetl plik

@ -95,7 +95,7 @@ def draw():
y += line_space
x = default_x
graphics.text(letter.upper(), x, y, 640, scale, spacing)
graphics.text(letter.upper(), x, y, 640, scale=scale, spacing=spacing)
x += letter_space
graphics.update()

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 5.4 KiB

Wyświetl plik

@ -4,6 +4,7 @@
- [Read ADCs](#read-adcs)
- [Read GPIOs](#read-gpios)
- [Read Encoders](#read-encoders)
- [Read Speeds](#read-speeds)
- [LED Rainbow](#led-rainbow)
- [Reset Inventor](#reset-inventor)
- [Motor Examples](#motor-examples)
@ -22,13 +23,14 @@
- [Velocity Tuning](#velocity-tuning)
- [Position on Velocity Tuning](#position-on-velocity-tuning)
- [Servo Examples](#servo-examples)
- [Single Servos](#single-servo)
- [Single Servo](#single-servo)
- [Multiple Servos](#multiple-servos)
- [Simple Easing](#simple-easing)
- [Servo Wave](#servo-wave)
- [Calibration](#calibration)
- [Audio Examples](#audio-examples)
- [Tone Song](#tone-song)
- [Motor Song](#motor-song)
## Function Examples
@ -50,6 +52,12 @@ Shows how to initialise and read the 6 GPIO headers of Inventor 2040 W.
Demonstrates how to read the angles of Inventor 2040 W's two encoders.
### Read Speeds
[read_speeds.py](read_speeds.py)
Demonstrates how to read the speeds of Inventor 2040 W's two encoders.
### LED Rainbow
[led_rainbow.py](led_rainbow.py)

Wyświetl plik

@ -0,0 +1,46 @@
import time
from inventor import Inventor2040W, NUM_MOTORS # , MOTOR_A, MOTOR_B
# from pimoroni import REVERSED_DIR
"""
Demonstrates how to read the speeds of Inventor 2040 W's two encoders.
Press "User" to exit the program.
"""
# Wheel friendly names
NAMES = ["LEFT", "RIGHT"]
# Constants
GEAR_RATIO = 50 # The gear ratio of the motor
SPEED = 1.0 # The speed to drive the motors at
SLEEP = 0.1 # The time to sleep between each capture
# Create a new Inventor2040W
board = Inventor2040W(motor_gear_ratio=GEAR_RATIO)
# Uncomment the below lines (and the top imports) to
# reverse the counting direction of an encoder
# encoders[MOTOR_A].direction(REVERSED_DIR)
# encoders[MOTOR_B].direction(REVERSED_DIR)
# Set both motors driving
for motor in board.motors:
motor.speed(SPEED)
# Variables for storing encoder captures
captures = [None] * NUM_MOTORS
# Read the encoders until the user button is pressed
while not board.switch_pressed():
# Capture the state of all the encoders since the last capture, SLEEP seconds ago
for i in range(NUM_MOTORS):
captures[i] = board.encoders[i].capture()
# Print out the speeds from each encoder
for i in range(NUM_MOTORS):
print(NAMES[i], "=", captures[i].revolutions_per_second, end=", ")
print()
time.sleep(SLEEP)

Wyświetl plik

@ -0,0 +1,236 @@
# Borrows heavily from : https://github.com/adafruit/Adafruit_CircuitPython_IS31FL3731/blob/main/adafruit_is31fl3731/keybow2040.py
# and : https://github.com/adafruit/micropython-adafruit-is31fl3731/blob/master/is31fl3731.py
import math
import utime
_MODE_REGISTER = const(0x00) # noqa: F821
_FRAME_REGISTER = const(0x01) # noqa: F821
_AUTOPLAY1_REGISTER = const(0x02) # noqa: F821
_AUTOPLAY2_REGISTER = const(0x03) # noqa: F821
_BLINK_REGISTER = const(0x05) # noqa: F821
_AUDIOSYNC_REGISTER = const(0x06) # noqa: F821
_BREATH1_REGISTER = const(0x08) # noqa: F821
_BREATH2_REGISTER = const(0x09) # noqa: F821
_SHUTDOWN_REGISTER = const(0x0a) # noqa: F821
_GAIN_REGISTER = const(0x0b) # noqa: F821
_ADC_REGISTER = const(0x0c) # noqa: F821
_CONFIG_BANK = const(0x0b) # noqa: F821
_BANK_ADDRESS = const(0xfd) # noqa: F821
_PICTURE_MODE = const(0x00) # noqa: F821
_AUTOPLAY_MODE = const(0x08) # noqa: F821
_AUDIOPLAY_MODE = const(0x18) # noqa: F821
_ENABLE_OFFSET = const(0x00) # noqa: F821
_BLINK_OFFSET = const(0x12) # noqa: F821
_COLOR_OFFSET = const(0x24) # noqa: F821
class Matrix:
width = 16
height = 9
def __init__(self, i2c, address=0x74):
self.i2c = i2c
self.address = address
self.reset()
self.init()
def _bank(self, bank=None):
if bank is None:
return self.i2c.readfrom_mem(self.address, _BANK_ADDRESS, 1)[0]
self.i2c.writeto_mem(self.address, _BANK_ADDRESS, bytearray([bank]))
def _register(self, bank, register, value=None):
self._bank(bank)
if value is None:
return self.i2c.readfrom_mem(self.address, register, 1)[0]
self.i2c.writeto_mem(self.address, register, bytearray([value]))
def _mode(self, mode=None):
return self._register(_CONFIG_BANK, _MODE_REGISTER, mode)
def init(self):
self._mode(_PICTURE_MODE)
self.frame(0)
for frame in range(8):
self.fill(0, False, frame=frame)
for col in range(18):
self._register(frame, _ENABLE_OFFSET + col, 0xff)
self.audio_sync(False)
def reset(self):
self.sleep(True)
utime.sleep_us(10)
self.sleep(False)
def sleep(self, value):
return self._register(_CONFIG_BANK, _SHUTDOWN_REGISTER, not value)
def autoplay(self, delay=0, loops=0, frames=0):
if delay == 0:
self._mode(_PICTURE_MODE)
return
delay //= 11
if not 0 <= loops <= 7:
raise ValueError("Loops out of range")
if not 0 <= frames <= 7:
raise ValueError("Frames out of range")
if not 1 <= delay <= 64:
raise ValueError("Delay out of range")
self._register(_CONFIG_BANK, _AUTOPLAY1_REGISTER, loops << 4 | frames)
self._register(_CONFIG_BANK, _AUTOPLAY2_REGISTER, delay % 64)
self._mode(_AUTOPLAY_MODE | self._frame)
def fade(self, fade_in=None, fade_out=None, pause=0):
if fade_in is None and fade_out is None:
self._register(_CONFIG_BANK, _BREATH2_REGISTER, 0)
elif fade_in is None:
fade_in = fade_out
elif fade_out is None:
fade_out = fade_in
fade_in = int(math.log(fade_in / 26, 2))
fade_out = int(math.log(fade_out / 26, 2))
pause = int(math.log(pause / 26, 2))
if not 0 <= fade_in <= 7:
raise ValueError("Fade in out of range")
if not 0 <= fade_out <= 7:
raise ValueError("Fade out out of range")
if not 0 <= pause <= 7:
raise ValueError("Pause out of range")
self._register(_CONFIG_BANK, _BREATH1_REGISTER, fade_out << 4 | fade_in)
self._register(_CONFIG_BANK, _BREATH2_REGISTER, 1 << 4 | pause)
def frame(self, frame=None, show=True):
if frame is None:
return self._frame
if not 0 <= frame <= 8:
raise ValueError("Frame out of range")
self._frame = frame
if show:
self._register(_CONFIG_BANK, _FRAME_REGISTER, frame)
def audio_sync(self, value=None):
return self._register(_CONFIG_BANK, _AUDIOSYNC_REGISTER, value)
def audio_play(self, sample_rate, audio_gain=0,
agc_enable=False, agc_fast=False):
if sample_rate == 0:
self._mode(_PICTURE_MODE)
return
sample_rate //= 46
if not 1 <= sample_rate <= 256:
raise ValueError("Sample rate out of range")
self._register(_CONFIG_BANK, _ADC_REGISTER, sample_rate % 256)
audio_gain //= 3
if not 0 <= audio_gain <= 7:
raise ValueError("Audio gain out of range")
self._register(_CONFIG_BANK, _GAIN_REGISTER,
bool(agc_enable) << 3 | bool(agc_fast) << 4 | audio_gain)
self._mode(_AUDIOPLAY_MODE)
def blink(self, rate=None):
if rate is None:
return (self._register(_CONFIG_BANK, _BLINK_REGISTER) & 0x07) * 270
elif rate == 0:
self._register(_CONFIG_BANK, _BLINK_REGISTER, 0x00)
return
rate //= 270
self._register(_CONFIG_BANK, _BLINK_REGISTER, rate & 0x07 | 0x08)
def fill(self, color=None, blink=None, frame=None):
if frame is None:
frame = self._frame
self._bank(frame)
if color is not None:
if not 0 <= color <= 255:
raise ValueError("Color out of range")
data = bytearray([color] * 24)
for row in range(6):
self.i2c.writeto_mem(self.address,
_COLOR_OFFSET + row * 24, data)
if blink is not None:
data = bool(blink) * 0xff
for col in range(18):
self._register(frame, _BLINK_OFFSET + col, data)
def write_frame(self, data, frame=None):
if len(data) > 144:
raise ValueError("Bytearray too large for frame")
if frame is None:
frame = self._frame
self._bank(frame)
self.i2c.writeto_mem(self.address, _COLOR_OFFSET, data)
def _pixel_addr(self, x, y):
return x + y * 16
def pixel(self, x, y, color=None, blink=None, frame=None):
if not 0 <= x <= self.width:
return
if not 0 <= y <= self.height:
return
pixel = self._pixel_addr(x, y)
if color is None and blink is None:
return self._register(self._frame, pixel)
if frame is None:
frame = self._frame
if color is not None:
if not 0 <= color <= 255:
raise ValueError("Color out of range")
self._register(frame, _COLOR_OFFSET + pixel, color)
if blink is not None:
addr, bit = divmod(pixel, 8)
bits = self._register(frame, _BLINK_OFFSET + addr)
if blink:
bits |= 1 << bit
else:
bits &= ~(1 << bit)
self._register(frame, _BLINK_OFFSET + addr, bits)
class Matrix_Keybow2040(Matrix):
width = 16
height = 3
def pixelrgb(self, x, y, r, g, b, blink=None, frame=None):
"""
Blink or brightness for x, y-pixel
:param x: horizontal pixel position
:param y: vertical pixel position
:param r: red brightness value 0->255
:param g: green brightness value 0->255
:param b: blue brightness value 0->255
:param blink: True to blink
:param frame: the frame to set the pixel
"""
x = (4 * (3 - x)) + y
super().pixel(x, 0, r, blink, frame)
super().pixel(x, 1, g, blink, frame)
super().pixel(x, 2, b, blink, frame)
def _pixel_addr(self, x, y):
lookup = [
(120, 88, 104), # 0, 0
(136, 40, 72), # 1, 0
(112, 80, 96), # 2, 0
(128, 32, 64), # 3, 0
(121, 89, 105), # 0, 1
(137, 41, 73), # 1, 1
(113, 81, 97), # 2, 1
(129, 33, 65), # 3, 1
(122, 90, 106), # 0, 2
(138, 25, 74), # 1, 2
(114, 82, 98), # 2, 2
(130, 17, 66), # 3, 2
(123, 91, 107), # 0, 3
(139, 26, 75), # 1, 3
(115, 83, 99), # 2, 3
(131, 18, 67), # 3, 3
]
return lookup[x][y]

Wyświetl plik

@ -0,0 +1,67 @@
import time
import usb.device
from usb.device.keyboard import KeyboardInterface, KeyCode
from pimoroni import Button
# A very basic HID Keyboard example for Keybow 2040
# Inspired by: https://github.com/micropython/micropython-lib/blob/master/micropython/usb/examples/device/keyboard_example.py
# This example requires a Pico USB compatible firmware, eg: pico_usb-1.23.0-pimoroni-micropython.uf2
# The pin order for Keybow 2040 is weird,
# But the below is laid out to match the pad with USB up
# from top left to bottom right.
KEYS = {
18: KeyCode.A, 14: KeyCode.B, 10: KeyCode.C, 6: KeyCode.D,
19: KeyCode.E, 15: KeyCode.F, 11: KeyCode.G, 7: KeyCode.H,
20: KeyCode.I, 16: KeyCode.J, 12: KeyCode.K, 8: KeyCode.L,
21: KeyCode.M, 17: KeyCode.N, 13: KeyCode.O, 9: KeyCode.P,
}
# We'll fill this with Button instances
BUTTONS = {}
class Keybow2040(KeyboardInterface):
def on_led_update(self, led_mask):
pass
def main():
for pin, keycode in KEYS.items():
BUTTONS[pin] = Button(pin)
k = Keybow2040()
usb.device.get().init(k, builtin_driver=True)
keys = [0, 0, 0, 0, 0, 0]
while True:
changed = False
if k.is_open():
for pin, code in KEYS.items():
pressed = BUTTONS[pin].read()
# If the key is pressed then try to insert
# it at the first zero. Otherwise (try to) replace
# its keycode with 0 to clear that press.
c = code if pressed else 0
i = 0 if pressed else code
try:
keys[keys.index(i)] = c
changed = True
except ValueError:
# Either our 6-key list is full
# or we're releasing a key that's not in it!
# Or handling a key that isn't pressed.
pass
if changed:
k.send_keys(keys)
time.sleep_ms(1)
if __name__ == "__main__":
print("Starting Keybow 2040...")
main()

Wyświetl plik

@ -0,0 +1,116 @@
import machine
import usb.device
from usb.device.keyboard import KeyboardInterface, KeyCode
from pimoroni import Button
from is31fl3731 import Matrix_Keybow2040
# A very basic HID Keyboard example for Keybow 2040
# Inspired by: https://github.com/micropython/micropython-lib/blob/master/micropython/usb/examples/device/keyboard_example.py
# This example requires a Pico USB compatible firmware, eg: pico_usb-1.23.0-pimoroni-micropython.uf2
# Don't forget to copy over is31fl3731.py too!
# The pin order for Keybow 2040 is weird,
# But the below is laid out to match the pad with USB up
# from top left to bottom right.
KEYS = {
18: KeyCode.A, 14: KeyCode.B, 10: KeyCode.C, 6: KeyCode.D,
19: KeyCode.E, 15: KeyCode.F, 11: KeyCode.G, 7: KeyCode.H,
20: KeyCode.I, 16: KeyCode.J, 12: KeyCode.K, 8: KeyCode.L,
21: KeyCode.M, 17: KeyCode.N, 13: KeyCode.O, 9: KeyCode.P,
}
# Another look up table for the balmy LED positioning
PIXELS = {
18: (3, 0), 14: (3, 1), 10: (3, 2), 6: (3, 3),
19: (2, 0), 15: (2, 1), 11: (2, 2), 7: (2, 3),
20: (1, 0), 16: (1, 1), 12: (1, 2), 8: (1, 3),
21: (0, 0), 17: (0, 1), 13: (0, 2), 9: (0, 3),
}
# We'll fill this with Button instances
BUTTONS = {}
VALUES = {}
MAX_ON_TIME = 20.0
# From CPython Lib/colorsys.py
def hsv_to_rgb(h, s, v):
if s == 0.0:
return v, v, v
i = int(h * 6.0)
f = (h * 6.0) - i
p = v * (1.0 - s)
q = v * (1.0 - s * f)
t = v * (1.0 - s * (1.0 - f))
i = i % 6
if i == 0:
return v, t, p
if i == 1:
return q, v, p
if i == 2:
return p, v, t
if i == 3:
return p, q, v
if i == 4:
return t, p, v
if i == 5:
return v, p, q
class Keybow2040(KeyboardInterface):
def on_led_update(self, led_mask):
pass
def main():
for pin, keycode in KEYS.items():
BUTTONS[pin] = Button(pin, repeat_time=0)
VALUES[pin] = 0
m = Matrix_Keybow2040(machine.I2C(0, 400000))
k = Keybow2040()
usb.device.get().init(k, builtin_driver=True)
keys = [0, 0, 0, 0, 0, 0]
while True:
changed = False
if k.is_open():
for pin, code in KEYS.items():
pressed = BUTTONS[pin].read()
if pressed:
VALUES[pin] = MAX_ON_TIME
# If the key is pressed then try to insert
# it at the first zero. Otherwise (try to) replace
# its keycode with 0 to clear that press.
c = code if pressed else 0
i = 0 if pressed else code
try:
keys[keys.index(i)] = c
changed = True
except ValueError:
# Either our 6-key list is full
# or we're releasing a key that's not in it!
# Or handling a key that isn't pressed.
pass
if changed:
k.send_keys(keys)
for pin, code in KEYS.items():
x, y = PIXELS[pin]
v = VALUES[pin] / MAX_ON_TIME
r, g, b = [int(c * 255) for c in hsv_to_rgb(x / 4.0, 0.7, 1.0 - v)]
m.pixelrgb(x, y, r, g, b)
if VALUES[pin] > 0:
VALUES[pin] -= 1
if __name__ == "__main__":
print("Starting Keybow 2040...")
main()

Wyświetl plik

@ -0,0 +1,276 @@
# SPDX-FileCopyrightText: 2023 Christopher Parrott for Pimoroni Ltd
#
# SPDX-License-Identifier: MIT
import os
import math
import struct
from machine import I2S, Pin
"""
A class for playing Wav files out of an I2S audio amp. It can also play pure tones.
This code is based heavily on the work of Mike Teachman, at:
https://github.com/miketeachman/micropython-i2s-examples/blob/master/examples/wavplayer.py
"""
class WavPlayer:
# Internal states
PLAY = 0
PAUSE = 1
FLUSH = 2
STOP = 3
NONE = 4
MODE_WAV = 0
MODE_TONE = 1
# Default buffer length
SILENCE_BUFFER_LENGTH = 1000
WAV_BUFFER_LENGTH = 10000
INTERNAL_BUFFER_LENGTH = 20000
TONE_SAMPLE_RATE = 44_100
TONE_BITS_PER_SAMPLE = 16
TONE_FULL_WAVES = 2
def __init__(self, id, sck_pin, ws_pin, sd_pin, amp_enable=None, ibuf_len=INTERNAL_BUFFER_LENGTH, root="/"):
self.__id = id
self.__sck_pin = sck_pin
self.__ws_pin = ws_pin
self.__sd_pin = sd_pin
self.__ibuf_len = ibuf_len
self.__enable = None
if amp_enable is not None:
self.__enable = Pin(amp_enable, Pin.OUT)
# Set the directory to search for files in
self.set_root(root)
self.__state = WavPlayer.NONE
self.__mode = WavPlayer.MODE_WAV
self.__wav_file = None
self.__loop_wav = False
self.__first_sample_offset = None
self.__flush_count = 0
self.__audio_out = None
# Allocate a small array of blank audio samples used for silence
self.__silence_samples = bytearray(self.SILENCE_BUFFER_LENGTH)
# Allocate a larger array for WAV audio samples, using a memoryview for more efficient access
self.__wav_samples_mv = memoryview(bytearray(self.WAV_BUFFER_LENGTH))
# Reserve a variable for audio samples used for tones
self.__tone_samples = None
self.__queued_samples = None
def set_root(self, root):
self.__root = root.rstrip("/") + "/"
def play_wav(self, wav_file, loop=False):
if os.listdir(self.__root).count(wav_file) == 0:
raise ValueError(f"'{wav_file}' not found")
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
self.__wav_file = open(self.__root + wav_file, "rb") # Open the chosen WAV file in read-only, binary mode
self.__loop_wav = loop # Record if the user wants the file to loop
# Parse the WAV file, returning the necessary parameters to initialise I2S communication
format, sample_rate, bits_per_sample, self.__first_sample_offset, self.sample_size = WavPlayer.__parse_wav(self.__wav_file)
# Keep a track of total bytes read from WAV File
self.total_bytes_read = 0
self.__wav_file.seek(self.__first_sample_offset) # Advance to first byte of sample data
self.__start_i2s(bits=bits_per_sample,
format=format,
rate=sample_rate,
state=WavPlayer.PLAY,
mode=WavPlayer.MODE_WAV)
def play_tone(self, frequency, amplitude):
if frequency < 20.0 or frequency > 20_000:
raise ValueError("frequency out of range. Expected between 20Hz and 20KHz")
if amplitude < 0.0 or amplitude > 1.0:
raise ValueError("amplitude out of range. Expected 0.0 to 1.0")
# Create a buffer containing the pure tone samples
samples_per_cycle = self.TONE_SAMPLE_RATE // frequency
sample_size_in_bytes = self.TONE_BITS_PER_SAMPLE // 8
samples = bytearray(self.TONE_FULL_WAVES * samples_per_cycle * sample_size_in_bytes)
range = pow(2, self.TONE_BITS_PER_SAMPLE) // 2
format = "<h" if self.TONE_BITS_PER_SAMPLE == 16 else "<l"
# Populate the buffer with multiple cycles to avoid it completing too quickly and causing drop outs
for i in range(samples_per_cycle * self.TONE_FULL_WAVES):
sample = int((range - 1) * (math.sin(2 * math.pi * i / samples_per_cycle)) * amplitude)
struct.pack_into(format, samples, i * sample_size_in_bytes, sample)
# Are we not already playing tones?
if not (self.__mode == WavPlayer.MODE_TONE and (self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE)):
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
self.__tone_samples = samples
self.__start_i2s(bits=self.TONE_BITS_PER_SAMPLE,
format=I2S.MONO,
rate=self.TONE_SAMPLE_RATE,
state=WavPlayer.PLAY,
mode=WavPlayer.MODE_TONE)
else:
self.__queued_samples = samples
self.__state = WavPlayer.PLAY
def pause(self):
if self.__state == WavPlayer.PLAY:
self.__state = WavPlayer.PAUSE # Enter the pause state on the next callback
def resume(self):
if self.__state == WavPlayer.PAUSE:
self.__state = WavPlayer.PLAY # Enter the play state on the next callback
def stop(self):
if self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE:
if self.__mode == WavPlayer.MODE_WAV:
# Enter the flush state on the next callback and close the file
# It is done in this order to prevent the callback entering the play
# state after we close the file but before we change the state)
self.__state = WavPlayer.FLUSH
self.__wav_file.close()
else:
self.__state = WavPlayer.STOP
def is_playing(self):
return self.__state != WavPlayer.NONE and self.__state != WavPlayer.STOP
def is_paused(self):
return self.__state == WavPlayer.PAUSE
def __start_i2s(self, bits=16, format=I2S.MONO, rate=44_100, state=STOP, mode=MODE_WAV):
import gc
gc.collect()
self.__audio_out = I2S(
self.__id,
sck=self.__sck_pin,
ws=self.__ws_pin,
sd=self.__sd_pin,
mode=I2S.TX,
bits=bits,
format=format,
rate=rate,
ibuf=self.__ibuf_len,
)
self.__state = state
self.__mode = mode
self.__flush_count = self.__ibuf_len // self.SILENCE_BUFFER_LENGTH + 1
self.__audio_out.irq(self.__i2s_callback)
self.__audio_out.write(self.__silence_samples)
if self.__enable is not None:
self.__enable.on()
def __stop_i2s(self):
self.stop() # Stop any active playback
while self.is_playing(): # and wait for it to complete
pass
if self.__enable is not None:
self.__enable.off()
if self.__audio_out is not None:
self.__audio_out.deinit() # Deinit any active I2S comms
self.__state == WavPlayer.NONE # Return to the none state
def __i2s_callback(self, arg):
# PLAY
if self.__state == WavPlayer.PLAY:
if self.__mode == WavPlayer.MODE_WAV:
num_read = self.__wav_file.readinto(self.__wav_samples_mv) # Read the next section of the WAV file
self.total_bytes_read += num_read
# Have we reached the end of the file?
if num_read == 0:
# Do we want to loop the WAV playback?
if self.__loop_wav:
_ = self.__wav_file.seek(self.__first_sample_offset) # Play again, so advance to first byte of sample data
else:
self.__wav_file.close() # Stop playing, so close the file
self.__state = WavPlayer.FLUSH # and enter the flush state on the next callback
self.__audio_out.write(self.__silence_samples) # In both cases play silence to end this callback
else:
if num_read > 0 and num_read < self.WAV_BUFFER_LENGTH:
num_read = num_read - (self.total_bytes_read - self.sample_size)
self.__audio_out.write(self.__wav_samples_mv[: num_read]) # We are within the file, so write out the next audio samples
else:
if self.__queued_samples is not None:
self.__tone_samples = self.__queued_samples
self.__queued_samples = None
self.__audio_out.write(self.__tone_samples)
# PAUSE or STOP
elif self.__state == WavPlayer.PAUSE or self.__state == WavPlayer.STOP:
self.__audio_out.write(self.__silence_samples) # Play silence
# FLUSH
elif self.__state == WavPlayer.FLUSH:
# Flush is used to allow the residual audio samples in the internal buffer to be written
# to the I2S peripheral. This step avoids part of the sound file from being cut off
if self.__flush_count > 0:
self.__flush_count -= 1
else:
self.__state = WavPlayer.STOP # Enter the stop state on the next callback
self.__audio_out.write(self.__silence_samples) # Play silence
# NONE
elif self.__state == WavPlayer.NONE:
pass
@staticmethod
def __parse_wav(wav_file):
chunk_ID = wav_file.read(4)
if chunk_ID != b"RIFF":
raise ValueError("WAV chunk ID invalid")
_ = wav_file.read(4) # chunk_size
format = wav_file.read(4)
if format != b"WAVE":
raise ValueError("WAV format invalid")
sub_chunk1_ID = wav_file.read(4)
if sub_chunk1_ID != b"fmt ":
raise ValueError("WAV sub chunk 1 ID invalid")
_ = wav_file.read(4) # sub_chunk1_size
_ = struct.unpack("<H", wav_file.read(2))[0] # audio_format
num_channels = struct.unpack("<H", wav_file.read(2))[0]
if num_channels == 1:
format = I2S.MONO
else:
format = I2S.STEREO
sample_rate = struct.unpack("<I", wav_file.read(4))[0]
# if sample_rate != 44_100 and sample_rate != 48_000:
# raise ValueError(f"WAV sample rate of {sample_rate} invalid. Only 44.1KHz or 48KHz audio are supported")
_ = struct.unpack("<I", wav_file.read(4))[0] # byte_rate
_ = struct.unpack("<H", wav_file.read(2))[0] # block_align
bits_per_sample = struct.unpack("<H", wav_file.read(2))[0]
# usually the sub chunk2 ID ("data") comes next, but
# some online MP3->WAV converters add
# binary data before "data". So, read a fairly large
# block of bytes and search for "data".
binary_block = wav_file.read(200)
offset = binary_block.find(b"data")
if offset == -1:
raise ValueError("WAV sub chunk 2 ID not found")
wav_file.seek(40)
sub_chunk2_size = struct.unpack("<I", wav_file.read(4))[0]
return (format, sample_rate, bits_per_sample, 44 + offset, sub_chunk2_size)

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,8 @@
from audio import WavPlayer
sound = WavPlayer(0, 10, 11, 9, amp_enable=29)
sound.play_wav("pirate-arrrr.wav", False)
while sound.is_playing():
pass

Wyświetl plik

@ -0,0 +1,40 @@
from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY, PEN_RGB332
import pngdec
# Create a PicoGraphics instance
display = PicoGraphics(display=DISPLAY_PICO_DISPLAY, pen_type=PEN_RGB332)
# Set the backlight so we can see it!
display.set_backlight(1.0)
# Create an instance of the PNG Decoder
png = pngdec.PNG(display)
# Create some pens for use later.
BG = display.create_pen(200, 200, 200)
TEXT = display.create_pen(0, 0, 0)
# Clear the screen
display.set_pen(BG)
display.clear()
display.set_pen(TEXT)
display.text("PNG Pencil", 15, 80)
try:
# Open our PNG File from flash. In this example we're using an image of a cartoon pencil.
# You can use Thonny to transfer PNG Images to your Pico.
png.open_file("pencil.png")
# Decode our PNG file and set the X and Y
png.decode(20, 100, scale=3)
# Handle the error if the image doesn't exist on the flash.
except OSError:
print("Error: PNG File missing. Copy the PNG file from the example folder to your Pico using Thonny and run the example again.")
display.update()
# We're not doing anything else with the display now but we want to keep the program running!
while True:
pass

Wyświetl plik

@ -0,0 +1,66 @@
from picographics import PicoGraphics, DISPLAY_PICO_DISPLAY_2, PEN_P8
import pngdec
# Create a PicoGraphics instance
display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2, pen_type=PEN_P8, rotate=270)
# Get the display width/height so we can position the PNGs
width, height = display.get_bounds()
# Set the backlight so we can see it!
display.set_backlight(1.0)
# Create an instance of the PNG Decoder
png = pngdec.PNG(display)
# Create some pens for use later.
BG = display.create_pen(200, 200, 200)
# 16 Reds
for i in range(16):
display.create_pen(i * 16, 0, 0)
# 16 Greens
for i in range(16):
display.create_pen(0, i * 16, 0)
# 16 Blues
for i in range(16):
display.create_pen(0, 0, i * 16)
# Adding in an white background colour at the beginning of each offset.
for i in range(3):
display.update_pen(i * 16, 200, 200, 200)
# Clear the screen
display.set_pen(BG)
display.clear()
try:
# Open our PNG File from flash. In this example we're using an image of a cartoon pencil.
# You can use Thonny to transfer PNG Images to your Pico.
png.open_file("pencil_gray.png")
# Horizontally/vertically center the three PNG Images.
png_w = png.get_width()
png_h = png.get_height()
offset_x = (width - png_w * 2) // 2
height_y = (height // 3)
offset_y = (height_y - png_h * 2) // 2
# Decode our PNG file and set the X and Y
png.decode(offset_x, offset_y, scale=2, mode=pngdec.PNG_COPY, palette_offset=0)
png.decode(offset_x, offset_y + height_y, scale=2, mode=pngdec.PNG_COPY, palette_offset=16)
png.decode(offset_x, offset_y + (height_y * 2), scale=2, mode=pngdec.PNG_COPY, palette_offset=32)
# Handle the error if the image doesn't exist on the flash.
except OSError:
print("Error: PNG File missing. Copy the PNG file from the example folder to your Pico using Thonny and run the example again.")
display.update()
# We're not doing anything else with the display now but we want to keep the program running!
while True:
pass

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.1 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 497 B

Wyświetl plik

@ -189,3 +189,4 @@ Here are some Plasma Stick community projects and resources that you might find
- :link: [MQTT Script for Plasma Stick](https://github.com/digitalurban/MQTT-Plasma-Stick-2040W)
- :link: [Pimoroni Wireless Plasma Kit - Server](https://github.com/brunon/Starlight)
- :link: [Plasma LEDs](https://github.com/bitcdr/plasma-leds)
- :link: [Album Art Bottle LEDs](https://github.com/heavyimage/Album-Art-Bottle-LEDs)

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,276 @@
# SPDX-FileCopyrightText: 2023 Christopher Parrott for Pimoroni Ltd
#
# SPDX-License-Identifier: MIT
import os
import math
import struct
from machine import I2S, Pin
"""
A class for playing Wav files out of an I2S audio amp. It can also play pure tones.
This code is based heavily on the work of Mike Teachman, at:
https://github.com/miketeachman/micropython-i2s-examples/blob/master/examples/wavplayer.py
"""
class WavPlayer:
# Internal states
PLAY = 0
PAUSE = 1
FLUSH = 2
STOP = 3
NONE = 4
MODE_WAV = 0
MODE_TONE = 1
# Default buffer length
SILENCE_BUFFER_LENGTH = 1000
WAV_BUFFER_LENGTH = 10000
INTERNAL_BUFFER_LENGTH = 20000
TONE_SAMPLE_RATE = 44_100
TONE_BITS_PER_SAMPLE = 16
TONE_FULL_WAVES = 2
def __init__(self, id, sck_pin, ws_pin, sd_pin, amp_enable=None, ibuf_len=INTERNAL_BUFFER_LENGTH, root="/"):
self.__id = id
self.__sck_pin = sck_pin
self.__ws_pin = ws_pin
self.__sd_pin = sd_pin
self.__ibuf_len = ibuf_len
self.__enable = None
if amp_enable is not None:
self.__enable = Pin(amp_enable, Pin.OUT)
# Set the directory to search for files in
self.set_root(root)
self.__state = WavPlayer.NONE
self.__mode = WavPlayer.MODE_WAV
self.__wav_file = None
self.__loop_wav = False
self.__first_sample_offset = None
self.__flush_count = 0
self.__audio_out = None
# Allocate a small array of blank audio samples used for silence
self.__silence_samples = bytearray(self.SILENCE_BUFFER_LENGTH)
# Allocate a larger array for WAV audio samples, using a memoryview for more efficient access
self.__wav_samples_mv = memoryview(bytearray(self.WAV_BUFFER_LENGTH))
# Reserve a variable for audio samples used for tones
self.__tone_samples = None
self.__queued_samples = None
def set_root(self, root):
self.__root = root.rstrip("/") + "/"
def play_wav(self, wav_file, loop=False):
if os.listdir(self.__root).count(wav_file) == 0:
raise ValueError(f"'{wav_file}' not found")
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
self.__wav_file = open(self.__root + wav_file, "rb") # Open the chosen WAV file in read-only, binary mode
self.__loop_wav = loop # Record if the user wants the file to loop
# Parse the WAV file, returning the necessary parameters to initialise I2S communication
format, sample_rate, bits_per_sample, self.__first_sample_offset, self.sample_size = WavPlayer.__parse_wav(self.__wav_file)
# Keep a track of total bytes read from WAV File
self.total_bytes_read = 0
self.__wav_file.seek(self.__first_sample_offset) # Advance to first byte of sample data
self.__start_i2s(bits=bits_per_sample,
format=format,
rate=sample_rate,
state=WavPlayer.PLAY,
mode=WavPlayer.MODE_WAV)
def play_tone(self, frequency, amplitude):
if frequency < 20.0 or frequency > 20_000:
raise ValueError("frequency out of range. Expected between 20Hz and 20KHz")
if amplitude < 0.0 or amplitude > 1.0:
raise ValueError("amplitude out of range. Expected 0.0 to 1.0")
# Create a buffer containing the pure tone samples
samples_per_cycle = self.TONE_SAMPLE_RATE // frequency
sample_size_in_bytes = self.TONE_BITS_PER_SAMPLE // 8
samples = bytearray(self.TONE_FULL_WAVES * samples_per_cycle * sample_size_in_bytes)
range = pow(2, self.TONE_BITS_PER_SAMPLE) // 2
format = "<h" if self.TONE_BITS_PER_SAMPLE == 16 else "<l"
# Populate the buffer with multiple cycles to avoid it completing too quickly and causing drop outs
for i in range(samples_per_cycle * self.TONE_FULL_WAVES):
sample = int((range - 1) * (math.sin(2 * math.pi * i / samples_per_cycle)) * amplitude)
struct.pack_into(format, samples, i * sample_size_in_bytes, sample)
# Are we not already playing tones?
if not (self.__mode == WavPlayer.MODE_TONE and (self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE)):
self.__stop_i2s() # Stop any active playback and terminate the I2S instance
self.__tone_samples = samples
self.__start_i2s(bits=self.TONE_BITS_PER_SAMPLE,
format=I2S.MONO,
rate=self.TONE_SAMPLE_RATE,
state=WavPlayer.PLAY,
mode=WavPlayer.MODE_TONE)
else:
self.__queued_samples = samples
self.__state = WavPlayer.PLAY
def pause(self):
if self.__state == WavPlayer.PLAY:
self.__state = WavPlayer.PAUSE # Enter the pause state on the next callback
def resume(self):
if self.__state == WavPlayer.PAUSE:
self.__state = WavPlayer.PLAY # Enter the play state on the next callback
def stop(self):
if self.__state == WavPlayer.PLAY or self.__state == WavPlayer.PAUSE:
if self.__mode == WavPlayer.MODE_WAV:
# Enter the flush state on the next callback and close the file
# It is done in this order to prevent the callback entering the play
# state after we close the file but before we change the state)
self.__state = WavPlayer.FLUSH
self.__wav_file.close()
else:
self.__state = WavPlayer.STOP
def is_playing(self):
return self.__state != WavPlayer.NONE and self.__state != WavPlayer.STOP
def is_paused(self):
return self.__state == WavPlayer.PAUSE
def __start_i2s(self, bits=16, format=I2S.MONO, rate=44_100, state=STOP, mode=MODE_WAV):
import gc
gc.collect()
self.__audio_out = I2S(
self.__id,
sck=self.__sck_pin,
ws=self.__ws_pin,
sd=self.__sd_pin,
mode=I2S.TX,
bits=bits,
format=format,
rate=rate,
ibuf=self.__ibuf_len,
)
self.__state = state
self.__mode = mode
self.__flush_count = self.__ibuf_len // self.SILENCE_BUFFER_LENGTH + 1
self.__audio_out.irq(self.__i2s_callback)
self.__audio_out.write(self.__silence_samples)
if self.__enable is not None:
self.__enable.on()
def __stop_i2s(self):
self.stop() # Stop any active playback
while self.is_playing(): # and wait for it to complete
pass
if self.__enable is not None:
self.__enable.off()
if self.__audio_out is not None:
self.__audio_out.deinit() # Deinit any active I2S comms
self.__state == WavPlayer.NONE # Return to the none state
def __i2s_callback(self, arg):
# PLAY
if self.__state == WavPlayer.PLAY:
if self.__mode == WavPlayer.MODE_WAV:
num_read = self.__wav_file.readinto(self.__wav_samples_mv) # Read the next section of the WAV file
self.total_bytes_read += num_read
# Have we reached the end of the file?
if num_read == 0:
# Do we want to loop the WAV playback?
if self.__loop_wav:
_ = self.__wav_file.seek(self.__first_sample_offset) # Play again, so advance to first byte of sample data
else:
self.__wav_file.close() # Stop playing, so close the file
self.__state = WavPlayer.FLUSH # and enter the flush state on the next callback
self.__audio_out.write(self.__silence_samples) # In both cases play silence to end this callback
else:
if num_read > 0 and num_read < self.WAV_BUFFER_LENGTH:
num_read = num_read - (self.total_bytes_read - self.sample_size)
self.__audio_out.write(self.__wav_samples_mv[: num_read]) # We are within the file, so write out the next audio samples
else:
if self.__queued_samples is not None:
self.__tone_samples = self.__queued_samples
self.__queued_samples = None
self.__audio_out.write(self.__tone_samples)
# PAUSE or STOP
elif self.__state == WavPlayer.PAUSE or self.__state == WavPlayer.STOP:
self.__audio_out.write(self.__silence_samples) # Play silence
# FLUSH
elif self.__state == WavPlayer.FLUSH:
# Flush is used to allow the residual audio samples in the internal buffer to be written
# to the I2S peripheral. This step avoids part of the sound file from being cut off
if self.__flush_count > 0:
self.__flush_count -= 1
else:
self.__state = WavPlayer.STOP # Enter the stop state on the next callback
self.__audio_out.write(self.__silence_samples) # Play silence
# NONE
elif self.__state == WavPlayer.NONE:
pass
@staticmethod
def __parse_wav(wav_file):
chunk_ID = wav_file.read(4)
if chunk_ID != b"RIFF":
raise ValueError("WAV chunk ID invalid")
_ = wav_file.read(4) # chunk_size
format = wav_file.read(4)
if format != b"WAVE":
raise ValueError("WAV format invalid")
sub_chunk1_ID = wav_file.read(4)
if sub_chunk1_ID != b"fmt ":
raise ValueError("WAV sub chunk 1 ID invalid")
_ = wav_file.read(4) # sub_chunk1_size
_ = struct.unpack("<H", wav_file.read(2))[0] # audio_format
num_channels = struct.unpack("<H", wav_file.read(2))[0]
if num_channels == 1:
format = I2S.MONO
else:
format = I2S.STEREO
sample_rate = struct.unpack("<I", wav_file.read(4))[0]
# if sample_rate != 44_100 and sample_rate != 48_000:
# raise ValueError(f"WAV sample rate of {sample_rate} invalid. Only 44.1KHz or 48KHz audio are supported")
_ = struct.unpack("<I", wav_file.read(4))[0] # byte_rate
_ = struct.unpack("<H", wav_file.read(2))[0] # block_align
bits_per_sample = struct.unpack("<H", wav_file.read(2))[0]
# usually the sub chunk2 ID ("data") comes next, but
# some online MP3->WAV converters add
# binary data before "data". So, read a fairly large
# block of bytes and search for "data".
binary_block = wav_file.read(200)
offset = binary_block.find(b"data")
if offset == -1:
raise ValueError("WAV sub chunk 2 ID not found")
wav_file.seek(40)
sub_chunk2_size = struct.unpack("<I", wav_file.read(4))[0]
return (format, sample_rate, bits_per_sample, 44 + offset, sub_chunk2_size)

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,93 @@
# Countdown Timer with Alarm
# i2s Audio Example
# Use VOL +/- to increase/decrease the amount of time
# Use the Sleep ZZZ button to start the countdown
from machine import Timer
from audio import WavPlayer
from stellar import StellarUnicorn
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
import time
su = StellarUnicorn()
graphics = PicoGraphics(DISPLAY)
graphics.set_font("bitmap6")
WHITE = graphics.create_pen(255, 255, 255)
BLUE = graphics.create_pen(0, 0, 255)
CLEAR = graphics.create_pen(0, 0, 0)
RED = graphics.create_pen(255, 0, 0)
GREEN = graphics.create_pen(0, 255, 0)
su.set_brightness(0.5)
audio = WavPlayer(0, 10, 11, 9, amp_enable=22)
class Countdown(object):
def __init__(self):
self.timer_running = False
self.total_seconds = 0
self.timer = None
def process_input(self):
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_UP):
self.total_seconds += 1
if su.is_pressed(StellarUnicorn.SWITCH_VOLUME_DOWN):
if self.total_seconds > 0:
self.total_seconds -= 1
if su.is_pressed(StellarUnicorn.SWITCH_SLEEP):
self.start_timer()
def display_time(self):
seconds = self.total_seconds % (24 * 3600)
seconds %= 3600
minutes = seconds // 60
seconds %= 60
# Add leading zeros to the minutes and seconds
if len(str(minutes)) == 1:
minutes = "0{}".format(minutes)
if len(str(seconds)) == 1:
seconds = "0{}".format(seconds)
return "{}\n{}".format(minutes, seconds)
def draw(self):
graphics.set_pen(graphics.create_pen(0, 0, 0))
graphics.clear()
graphics.set_pen(RED)
graphics.rectangle(0, 0, 16, 16)
graphics.set_pen(CLEAR)
graphics.rectangle(2, 2, StellarUnicorn.WIDTH - 4, 12)
graphics.set_pen(WHITE)
graphics.text(self.display_time(), 2, 1, -1, 1)
su.update(graphics)
def start_timer(self):
if not self.timer_running:
self.timer = Timer(mode=Timer.PERIODIC, period=1000, callback=self.countdown)
self.timer_running = True
def reset(self):
time.sleep(0.2)
self.timer.deinit()
self.timer_running = False
def countdown(self, arg):
if self.total_seconds == 0:
audio.play_wav("doorbell.wav", False)
self.reset()
else:
self.total_seconds -= 1
count = Countdown()
while 1:
count.process_input()
count.draw()
time.sleep(0.07)

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,86 @@
# Example Menu with Sound
# i2s Audio Example
# Use Brightness +/- to move up and down
# Press Sleep to play the selected sound
from audio import WavPlayer
from stellar import StellarUnicorn
from picographics import PicoGraphics, DISPLAY_STELLAR_UNICORN as DISPLAY
from time import sleep
su = StellarUnicorn()
graphics = PicoGraphics(DISPLAY)
audio = WavPlayer(0, 10, 11, 9, amp_enable=22)
WHITE = graphics.create_pen(255, 255, 255)
RED = graphics.create_pen(255, 0, 0)
GREEN = graphics.create_pen(0, 200, 200)
class Menu(object):
def __init__(self):
self.items = ["1", "2", "3"]
self.selected = 0
# A function to draw only the menu elements.
# Helps to keep our main function tidy!
def draw_menu(self):
graphics.set_pen(GREEN)
graphics.circle(0, 0, 3)
graphics.circle(16, 5, 4)
graphics.circle(3, 16, 3)
graphics.set_pen(WHITE)
for item in range(len(self.items)):
if self.selected == item:
graphics.set_pen(WHITE)
graphics.text(self.items[self.selected], 6, 4, 31, 1)
# Make changes based on the currently selected menu item
def process_selected(self):
if self.selected == 0:
audio.play_wav("Pew1.wav", False)
if self.selected == 1:
audio.play_wav("Pew2.wav", False)
if self.selected == 2:
audio.play_wav("Pew3.wav", False)
menu = Menu()
graphics.set_font("bitmap6")
su.set_brightness(0.7)
while True:
graphics.set_pen(graphics.create_pen(0, 0, 0))
graphics.clear()
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_DOWN):
audio.play_wav("buttonbeep.wav", False)
if menu.selected + 1 < len(menu.items):
menu.selected += 1
else:
menu.selected = 0
if su.is_pressed(StellarUnicorn.SWITCH_BRIGHTNESS_UP):
audio.play_wav("buttonbeep.wav", False)
if menu.selected > 0:
menu.selected -= 1
else:
menu.selected = len(menu.items) - 1
if su.is_pressed(StellarUnicorn.SWITCH_SLEEP):
menu.process_selected()
menu.draw_menu()
su.update(graphics)
sleep(0.2)

Wyświetl plik

@ -0,0 +1,8 @@
from audio import WavPlayer
sound = WavPlayer(0, 10, 11, 9, amp_enable=22)
sound.play_wav("beepboop.wav", False)
while sound.is_playing():
pass

Wyświetl plik

@ -0,0 +1,36 @@
from picographics import PicoGraphics, DISPLAY_TUFTY_2040
import pngdec
display = PicoGraphics(display=DISPLAY_TUFTY_2040)
# Create an instance of the PNG Decoder
png = pngdec.PNG(display)
# Create some pens for use later.
BG = display.create_pen(200, 200, 200)
TEXT = display.create_pen(0, 0, 0)
# Clear the screen
display.set_pen(BG)
display.clear()
display.set_pen(TEXT)
display.text("PNG Pencil", 105, 80)
try:
# Open our PNG File from flash. In this example we're using an image of a cartoon pencil.
# You can use Thonny to transfer PNG Images to your Pico.
png.open_file("pencil.png")
# Decode our PNG file and set the X and Y
png.decode(140, 100)
# Handle the error if the image doesn't exist on the flash.
except OSError:
print("Error: PNG File missing. Copy the PNG file from the example folder to your Pico using Thonny and run the example again.")
display.update()
# We're not doing anything else with the display now but we want to keep the program running!
while True:
pass

Wyświetl plik

@ -0,0 +1,54 @@
from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_P8
import pngdec
display = PicoGraphics(display=DISPLAY_TUFTY_2040, pen_type=PEN_P8)
# Create an instance of the PNG Decoder
png = pngdec.PNG(display)
# Create some pens for use later.
BG = display.create_pen(200, 200, 200)
TEXT = display.create_pen(0, 0, 0)
# 16 Reds
for i in range(15):
display.create_pen(i * 16, 0, 0)
# 16 Greens
for i in range(16):
display.create_pen(0, i * 16, 0)
# 16 Blues
for i in range(15):
display.create_pen(0, 0, i * 16)
# Adding in an white background colour at the beginning of each offset.
for i in range(3):
display.update_pen(i * 16, 200, 200, 200)
# Clear the screen
display.set_pen(BG)
display.clear()
display.set_pen(TEXT)
display.text("PNG Pencil \n& Offset Palette", 125, 115)
try:
# Open our PNG File from flash. In this example we're using an image of a cartoon pencil.
# You can use Thonny to transfer PNG Images to your Pico.
png.open_file("pencil_gray.png")
# Decode our PNG file and set the X and Y
png.decode(35, 10, scale=2, mode=pngdec.PNG_COPY, palette_offset=0)
png.decode(35, 90, scale=2, mode=pngdec.PNG_COPY, palette_offset=16)
png.decode(35, 170, scale=2, mode=pngdec.PNG_COPY, palette_offset=32)
# Handle the error if the image doesn't exist on the flash.
except OSError:
print("Error: PNG File missing. Copy the PNG file from the example folder to your Pico using Thonny and run the example again.")
display.update()
# We're not doing anything else with the display now but we want to keep the program running!
while True:
pass

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.1 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 497 B

Wyświetl plik

@ -1,9 +1,9 @@
diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt
index 281b0c3bc..7e04bb549 100644
index fcc435b7b..efabcb3a3 100644
--- a/ports/rp2/CMakeLists.txt
+++ b/ports/rp2/CMakeLists.txt
@@ -453,6 +453,16 @@ target_link_options(${MICROPY_TARGET} PRIVATE
-Wl,--wrap=dcd_event_handler
@@ -464,6 +464,16 @@ set_source_files_properties(
COMPILE_OPTIONS "-O2"
)
+# Do not include stack unwinding & exception handling for C++ user modules

Wyświetl plik

@ -5,13 +5,13 @@ MP_DEFINE_CONST_FUN_OBJ_1(adcfft_update_obj, adcfft_update);
MP_DEFINE_CONST_FUN_OBJ_3(adcfft_get_scaled_obj, adcfft_get_scaled);
MP_DEFINE_CONST_FUN_OBJ_1(adcfft__del__obj, adcfft__del__);
STATIC const mp_rom_map_elem_t adcfft_locals_dict_table[] = {
static const mp_rom_map_elem_t adcfft_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&adcfft__del__obj) },
{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&adcfft_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_scaled), MP_ROM_PTR(&adcfft_get_scaled_obj) },
};
STATIC MP_DEFINE_CONST_DICT(adcfft_locals_dict, adcfft_locals_dict_table);
static MP_DEFINE_CONST_DICT(adcfft_locals_dict, adcfft_locals_dict_table);
#ifdef MP_DEFINE_CONST_OBJ_TYPE
MP_DEFINE_CONST_OBJ_TYPE(
@ -31,11 +31,11 @@ const mp_obj_type_t adcfft_type = {
#endif
// Module
STATIC const mp_map_elem_t adcfft_globals_table[] = {
static const mp_map_elem_t adcfft_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_adcfft) }, // Module name
{ MP_OBJ_NEW_QSTR(MP_QSTR_ADCFFT), (mp_obj_t)&adcfft_type }, // Class name & type
};
STATIC MP_DEFINE_CONST_DICT(mp_module_adcfft_globals, adcfft_globals_table);
static MP_DEFINE_CONST_DICT(mp_module_adcfft_globals, adcfft_globals_table);
const mp_obj_module_t adcfft_user_cmodule = {

Wyświetl plik

@ -21,8 +21,7 @@ mp_obj_t adcfft_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
adcfft_obj_t *self = m_new_obj_with_finaliser(adcfft_obj_t);
self->base.type = &adcfft_type;
adcfft_obj_t *self = mp_obj_malloc_with_finaliser(adcfft_obj_t, &adcfft_type);
unsigned int adc_channel = args[ARG_adc_channel].u_int;
unsigned int adc_gpio = args[ARG_adc_gpio].u_int;

Wyświetl plik

@ -38,7 +38,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(Badger2040_woken_by_button_obj, Badger2040_woken_by_bu
MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_system_speed_obj, Badger2040_system_speed);
/***** Binding of Methods *****/
STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = {
static const mp_rom_map_elem_t Badger2040_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Badger2040___del___obj) },
{ MP_ROM_QSTR(MP_QSTR_is_busy), MP_ROM_PTR(&Badger2040_is_busy_obj) },
{ MP_ROM_QSTR(MP_QSTR_update_speed), MP_ROM_PTR(&Badger2040_update_speed_obj) },
@ -72,7 +72,7 @@ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_command), MP_ROM_PTR(&Badger2040_command_obj) },
};
STATIC MP_DEFINE_CONST_DICT(Badger2040_locals_dict, Badger2040_locals_dict_table);
static MP_DEFINE_CONST_DICT(Badger2040_locals_dict, Badger2040_locals_dict_table);
/***** Class Definition *****/
#ifdef MP_DEFINE_CONST_OBJ_TYPE
@ -94,7 +94,7 @@ const mp_obj_type_t Badger2040_type = {
/***** Globals Table *****/
STATIC const mp_rom_map_elem_t badger2040_globals_table[] = {
static const mp_rom_map_elem_t badger2040_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_badger2040) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_Badger2040), (mp_obj_t)&Badger2040_type },
@ -140,7 +140,7 @@ STATIC const mp_rom_map_elem_t badger2040_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_PIN_BATTERY), MP_ROM_INT(29) },
{ MP_ROM_QSTR(MP_QSTR_PIN_ENABLE_3V3), MP_ROM_INT(10) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_badger2040_globals, badger2040_globals_table);
static MP_DEFINE_CONST_DICT(mp_module_badger2040_globals, badger2040_globals_table);
/***** Module Definition *****/
const mp_obj_module_t badger2040_user_cmodule = {

Wyświetl plik

@ -98,8 +98,7 @@ mp_obj_t Badger2040_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
buffer = m_new(uint8_t, width * height / 8);
}
badger2040_obj = m_new_obj_with_finaliser(_Badger2040_obj_t);
badger2040_obj->base.type = &Badger2040_type;
badger2040_obj = mp_obj_malloc_with_finaliser(_Badger2040_obj_t, &Badger2040_type);
badger2040_obj->buf = buffer;
badger2040_obj->badger2040 = m_new_class(pimoroni::Badger2040, buffer);
badger2040_obj->badger2040->init();

Wyświetl plik

@ -19,7 +19,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7262_set_integration_time_obj, BreakoutAS726
MP_DEFINE_CONST_FUN_OBJ_3(BreakoutAS7262_set_leds_obj, BreakoutAS7262_set_leds);
/***** Binding of Methods *****/
STATIC const mp_rom_map_elem_t BreakoutAS7262_locals_dict_table[] = {
static const mp_rom_map_elem_t BreakoutAS7262_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&BreakoutAS7262_reset_obj) },
{ MP_ROM_QSTR(MP_QSTR_device_type), MP_ROM_PTR(&BreakoutAS7262_device_type_obj) },
{ MP_ROM_QSTR(MP_QSTR_hardware_version), MP_ROM_PTR(&BreakoutAS7262_hardware_version_obj) },
@ -54,7 +54,7 @@ STATIC const mp_rom_map_elem_t BreakoutAS7262_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_CONT_ROYGBR), MP_ROM_INT(MP_CONT_ROYGBR) },
{ MP_ROM_QSTR(MP_QSTR_ONESHOT), MP_ROM_INT(MP_ONESHOT) },
};
STATIC MP_DEFINE_CONST_DICT(BreakoutAS7262_locals_dict, BreakoutAS7262_locals_dict_table);
static MP_DEFINE_CONST_DICT(BreakoutAS7262_locals_dict, BreakoutAS7262_locals_dict_table);
/***** Class Definition *****/
#ifdef MP_DEFINE_CONST_OBJ_TYPE
@ -80,11 +80,11 @@ const mp_obj_type_t breakout_as7262_BreakoutAS7262_type = {
////////////////////////////////////////////////////////////////////////////////////////////////////
/***** Globals Table *****/
STATIC const mp_map_elem_t breakout_as7262_globals_table[] = {
static const mp_map_elem_t breakout_as7262_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_as7262) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutAS7262), (mp_obj_t)&breakout_as7262_BreakoutAS7262_type },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_as7262_globals, breakout_as7262_globals_table);
static MP_DEFINE_CONST_DICT(mp_module_breakout_as7262_globals, breakout_as7262_globals_table);
/***** Module Definition *****/
const mp_obj_module_t breakout_as7262_user_cmodule = {

Wyświetl plik

@ -0,0 +1,79 @@
#include "breakout_as7343.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// BreakoutAS7343 Class
////////////////////////////////////////////////////////////////////////////////////////////////////
/***** Methods *****/
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7343_reset_obj, BreakoutAS7343_reset);
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7343_version_obj, BreakoutAS7343_version);
MP_DEFINE_CONST_FUN_OBJ_1(BreakoutAS7343_read_obj, BreakoutAS7343_read);
MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7343_set_gain_obj, BreakoutAS7343_set_gain);
MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7343_set_integration_time_obj, BreakoutAS7343_set_integration_time);
MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7343_set_measurement_time_obj, BreakoutAS7343_set_measurement_time);
MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7343_set_illumination_current_obj, BreakoutAS7343_set_illumination_current);
MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7343_set_illumination_led_obj, BreakoutAS7343_set_illumination_led);
MP_DEFINE_CONST_FUN_OBJ_2(BreakoutAS7343_set_channels_obj, BreakoutAS7343_set_channels);
/***** Binding of Methods *****/
static const mp_rom_map_elem_t BreakoutAS7343_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&BreakoutAS7343_reset_obj) },
{ MP_ROM_QSTR(MP_QSTR_version), MP_ROM_PTR(&BreakoutAS7343_version_obj) },
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutAS7343_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_gain), MP_ROM_PTR(&BreakoutAS7343_set_gain_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_measurement_time), MP_ROM_PTR(&BreakoutAS7343_set_measurement_time_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_integration_time), MP_ROM_PTR(&BreakoutAS7343_set_integration_time_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_illumination_current), MP_ROM_PTR(&BreakoutAS7343_set_illumination_current_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_illumination_led), MP_ROM_PTR(&BreakoutAS7343_set_illumination_led_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_channels), MP_ROM_PTR(&BreakoutAS7343_set_channels_obj) },
};
static MP_DEFINE_CONST_DICT(BreakoutAS7343_locals_dict, BreakoutAS7343_locals_dict_table);
/***** Class Definition *****/
#ifdef MP_DEFINE_CONST_OBJ_TYPE
MP_DEFINE_CONST_OBJ_TYPE(
breakout_as7343_BreakoutAS7343_type,
MP_QSTR_BreakoutAS7343,
MP_TYPE_FLAG_NONE,
make_new, BreakoutAS7343_make_new,
locals_dict, (mp_obj_dict_t*)&BreakoutAS7343_locals_dict
);
#else
const mp_obj_type_t breakout_as7343_BreakoutAS7343_type = {
{ &mp_type_type },
.name = MP_QSTR_BreakoutAS7343,
.make_new = BreakoutAS7343_make_new,
.locals_dict = (mp_obj_dict_t*)&BreakoutAS7343_locals_dict,
};
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
// breakout_as7343 Module
////////////////////////////////////////////////////////////////////////////////////////////////////
/***** Globals Table *****/
static const mp_map_elem_t breakout_as7343_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_as7343) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutAS7343), (mp_obj_t)&breakout_as7343_BreakoutAS7343_type },
};
static MP_DEFINE_CONST_DICT(mp_module_breakout_as7343_globals, breakout_as7343_globals_table);
/***** Module Definition *****/
const mp_obj_module_t breakout_as7343_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_breakout_as7343_globals,
};
////////////////////////////////////////////////////////////////////////////////////////////////////
#if MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_breakout_as7343, breakout_as7343_user_cmodule, MODULE_BREAKOUT_AS7343_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_breakout_as7343, breakout_as7343_user_cmodule);
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

Wyświetl plik

@ -0,0 +1,140 @@
#include "drivers/as7343/as7343.hpp"
#include "common/pimoroni_i2c.hpp"
#include "micropython/modules/util.hpp"
using namespace pimoroni;
extern "C" {
#include "breakout_as7343.h"
#include "pimoroni_i2c.h"
/***** Variables Struct *****/
typedef struct _breakout_as7343_BreakoutAS7343_obj_t {
mp_obj_base_t base;
AS7343 *breakout;
_PimoroniI2C_obj_t *i2c;
} breakout_as7343_BreakoutAS7343_obj_t;
/***** Constructor *****/
mp_obj_t BreakoutAS7343_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
breakout_as7343_BreakoutAS7343_obj_t *self = nullptr;
enum { ARG_i2c, ARG_int };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_i2c, MP_ARG_OBJ, {.u_obj = nullptr} },
{ MP_QSTR_interrupt, MP_ARG_INT, {.u_int = PIN_UNUSED} },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
self = m_new_obj(breakout_as7343_BreakoutAS7343_obj_t);
self->base.type = &breakout_as7343_BreakoutAS7343_type;
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(AS7343, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_int].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutAS7343: breakout not found when initialising");
}
return MP_OBJ_FROM_PTR(self);
}
/***** Methods *****/
mp_obj_t BreakoutAS7343_reset(mp_obj_t self_in) {
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
self->breakout->reset();
return mp_const_none;
}
mp_obj_t BreakoutAS7343_version(mp_obj_t self_in) {
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
uint8_t aux_id, revision_id, hardware_id;
self->breakout->get_version(aux_id, revision_id, hardware_id);
mp_obj_t tuple[3];
tuple[0] = mp_obj_new_int(aux_id);
tuple[1] = mp_obj_new_int(revision_id);
tuple[2] = mp_obj_new_int(hardware_id);
return mp_obj_new_tuple(3, tuple);
}
mp_obj_t BreakoutAS7343_read(mp_obj_t self_in) {
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
AS7343::reading reading = self->breakout->read();
mp_obj_t tuple[12];
tuple[0] = mp_obj_new_float(reading.FZ);
tuple[1] = mp_obj_new_float(reading.FY);
tuple[2] = mp_obj_new_float(reading.FXL);
tuple[3] = mp_obj_new_float(reading.NIR);
tuple[4] = mp_obj_new_float(reading.F2);
tuple[5] = mp_obj_new_float(reading.F3);
tuple[6] = mp_obj_new_float(reading.F4);
tuple[7] = mp_obj_new_float(reading.F6);
tuple[8] = mp_obj_new_float(reading.F1);
tuple[9] = mp_obj_new_float(reading.F5);
tuple[10] = mp_obj_new_float(reading.F7);
tuple[11] = mp_obj_new_float(reading.F8);
return mp_obj_new_tuple(12, tuple);
}
mp_obj_t BreakoutAS7343_set_gain(mp_obj_t self_in, mp_obj_t value_in) {
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
self->breakout->set_gain(mp_obj_get_int(value_in));
return mp_const_none;
}
mp_obj_t BreakoutAS7343_set_measurement_time(mp_obj_t self_in, mp_obj_t value_in) {
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
self->breakout->set_measurement_time(mp_obj_get_float(value_in));
return mp_const_none;
}
mp_obj_t BreakoutAS7343_set_integration_time(mp_obj_t self_in, mp_obj_t value_in) {
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
self->breakout->set_integration_time(mp_obj_get_float(value_in));
return mp_const_none;
}
mp_obj_t BreakoutAS7343_set_illumination_current(mp_obj_t self_in, mp_obj_t value_in) {
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
self->breakout->set_illumination_current(mp_obj_get_int(value_in));
return mp_const_none;
}
mp_obj_t BreakoutAS7343_set_illumination_led(mp_obj_t self_in, mp_obj_t value_in) {
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
self->breakout->set_illumination_led(value_in == mp_const_true);
return mp_const_none;
}
mp_obj_t BreakoutAS7343_set_channels(mp_obj_t self_in, mp_obj_t value_in) {
breakout_as7343_BreakoutAS7343_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_as7343_BreakoutAS7343_obj_t);
self->breakout->set_channels((AS7343::channel_count)mp_obj_get_int(value_in));
return mp_const_none;
}
}

Wyświetl plik

@ -0,0 +1,23 @@
// Include MicroPython API.
#include "py/runtime.h"
/***** Constants *****/
/***** Extern of Class Definition *****/
extern const mp_obj_type_t breakout_as7343_BreakoutAS7343_type;
/***** Extern of Class Methods *****/
extern mp_obj_t BreakoutAS7343_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
extern mp_obj_t BreakoutAS7343_reset(mp_obj_t self_in);
extern mp_obj_t BreakoutAS7343_version(mp_obj_t self_in);
extern mp_obj_t BreakoutAS7343_read(mp_obj_t self_in);
extern mp_obj_t BreakoutAS7343_set_gain(mp_obj_t self_in, mp_obj_t value_in);
extern mp_obj_t BreakoutAS7343_set_measurement_time(mp_obj_t self_in, mp_obj_t value_in);
extern mp_obj_t BreakoutAS7343_set_integration_time(mp_obj_t self_in, mp_obj_t value_in);
extern mp_obj_t BreakoutAS7343_set_illumination_current(mp_obj_t self_in, mp_obj_t value_in);
extern mp_obj_t BreakoutAS7343_set_illumination_led(mp_obj_t self_in, mp_obj_t value_in);
extern mp_obj_t BreakoutAS7343_set_channels(mp_obj_t self_in, mp_obj_t value_in);

Some files were not shown because too many files have changed in this diff Show More