kopia lustrzana https://github.com/AlexFWulff/awulff-pico-playground
Actually added new directories
rodzic
318cb25100
commit
a5b2f83603
|
@ -1 +0,0 @@
|
||||||
Subproject commit 5a825884753207656080b091d87490348452670c
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
build/
|
||||||
|
*.DS_Store
|
||||||
|
edge-impulse-sdk/
|
||||||
|
model-parameters/
|
||||||
|
tflite-model/
|
|
@ -0,0 +1,88 @@
|
||||||
|
cmake_minimum_required(VERSION 3.13.1)
|
||||||
|
|
||||||
|
set(MODEL_FOLDER .)
|
||||||
|
set(EI_SDK_FOLDER edge-impulse-sdk)
|
||||||
|
|
||||||
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
project(pico-voice C CXX ASM)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
add_executable(pico-voice
|
||||||
|
source/main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
include(${MODEL_FOLDER}/edge-impulse-sdk/cmake/utils.cmake)
|
||||||
|
|
||||||
|
# enable usb output, disable uart output
|
||||||
|
pico_enable_stdio_usb(pico-voice 1)
|
||||||
|
pico_enable_stdio_uart(pico-voice 0)
|
||||||
|
|
||||||
|
target_include_directories(pico-voice PRIVATE
|
||||||
|
${MODEL_FOLDER}
|
||||||
|
${MODEL_FOLDER}/classifer
|
||||||
|
${MODEL_FOLDER}/tflite-model
|
||||||
|
${MODEL_FOLDER}/model-parameters
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(pico-voice PRIVATE
|
||||||
|
${EI_SDK_FOLDER}
|
||||||
|
${EI_SDK_FOLDER}/third_party/ruy
|
||||||
|
${EI_SDK_FOLDER}/third_party/gemmlowp
|
||||||
|
${EI_SDK_FOLDER}/third_party/flatbuffers/include
|
||||||
|
${EI_SDK_FOLDER}/third_party
|
||||||
|
${EI_SDK_FOLDER}/tensorflow
|
||||||
|
${EI_SDK_FOLDER}/dsp
|
||||||
|
${EI_SDK_FOLDER}/classifier
|
||||||
|
${EI_SDK_FOLDER}/anomaly
|
||||||
|
${EI_SDK_FOLDER}/CMSIS/NN/Include
|
||||||
|
${EI_SDK_FOLDER}/CMSIS/DSP/PrivateInclude
|
||||||
|
${EI_SDK_FOLDER}/CMSIS/DSP/Include
|
||||||
|
${EI_SDK_FOLDER}/CMSIS/Core/Include
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(${INCLUDES})
|
||||||
|
|
||||||
|
# find model source files
|
||||||
|
RECURSIVE_FIND_FILE(MODEL_FILES "${MODEL_FOLDER}/tflite-model" "*.cpp")
|
||||||
|
RECURSIVE_FIND_FILE(SOURCE_FILES "${EI_SDK_FOLDER}" "*.cpp")
|
||||||
|
RECURSIVE_FIND_FILE(CC_FILES "${EI_SDK_FOLDER}" "*.cc")
|
||||||
|
RECURSIVE_FIND_FILE(S_FILES "${EI_SDK_FOLDER}" "*.s")
|
||||||
|
RECURSIVE_FIND_FILE(C_FILES "${EI_SDK_FOLDER}" "*.c")
|
||||||
|
list(APPEND SOURCE_FILES ${S_FILES})
|
||||||
|
list(APPEND SOURCE_FILES ${C_FILES})
|
||||||
|
list(APPEND SOURCE_FILES ${CC_FILES})
|
||||||
|
list(APPEND SOURCE_FILES ${MODEL_FILES})
|
||||||
|
|
||||||
|
# add all sources to the project
|
||||||
|
target_sources(pico-voice PRIVATE ${SOURCE_FILES})
|
||||||
|
|
||||||
|
# now do Neopixel Library
|
||||||
|
add_library(pico_neopixel INTERFACE)
|
||||||
|
|
||||||
|
pico_generate_pio_header(pico_neopixel ${CMAKE_CURRENT_LIST_DIR}/pico_neopixels/ws2812byte.pio)
|
||||||
|
|
||||||
|
target_sources(pico_neopixel INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/pico_neopixels/Adafruit_NeoPixel.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
pico_enable_stdio_usb(pico_neopixel 1)
|
||||||
|
pico_enable_stdio_uart(pico_neopixel 0)
|
||||||
|
|
||||||
|
target_include_directories(pico_neopixel INTERFACE ${CMAKE_CURRENT_LIST_DIR}/pico_neopixels/include)
|
||||||
|
|
||||||
|
target_link_libraries(pico_neopixel INTERFACE pico_stdlib hardware_pio pico_malloc pico_mem_ops)
|
||||||
|
|
||||||
|
# Finish up
|
||||||
|
target_link_libraries(pico-voice
|
||||||
|
hardware_adc
|
||||||
|
hardware_dma
|
||||||
|
pico_stdlib
|
||||||
|
pico_neopixel
|
||||||
|
pico_multicore
|
||||||
|
)
|
||||||
|
|
||||||
|
pico_add_extra_outputs(pico-voice)
|
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
|
@ -0,0 +1,15 @@
|
||||||
|
# pico-light-voice1
|
||||||
|
|
||||||
|
This program combines a voice recognition model with Neopixels to make a low-cost lighting controller. Much of the ML code is from Edge Impulse's pico [standalone repo](https://github.com/edgeimpulse/example-standalone-inferencing-pico), and the Neopixel code is from [this fantastic port](https://github.com/martinkooij/pi-pico-adafruit-neopixels) that converts the Neopixel routines to efficient PIO code.
|
||||||
|
|
||||||
|
To use this starting point, you need to drop in the `edge-impulse-sdk`, `model-parameters`, and `tflite-model` folders generated when exporting your model for C++.
|
||||||
|
|
||||||
|
## Program operation
|
||||||
|
|
||||||
|
This code really pushes the Pico to its limits! Simultaneously it's sampling from the ADC, controlling Neopixels using PIO, doing ML processing on one core, and operating a lighting state machine on the second core. Pretty neat!
|
||||||
|
|
||||||
|
## Modifications
|
||||||
|
|
||||||
|
If you train your model on floating-point WAV files sampled at 5 kHz (see the pico-daq folder in this repository) then you shouldn't need to change much other than the results of the inferencing.
|
||||||
|
|
||||||
|
If you trained your data on some other format, you will need to modify how data gets copied into the `features` buffer as well as things like the sample rate.
|
|
@ -0,0 +1,712 @@
|
||||||
|
/*!
|
||||||
|
* @file Adafruit_NeoPixel.cpp
|
||||||
|
*
|
||||||
|
* @mainpage Arduino Library for driving Adafruit NeoPixel addressable LEDs,
|
||||||
|
* FLORA RGB Smart Pixels and compatible devicess -- WS2811, WS2812, WS2812B,
|
||||||
|
* SK6812, etc.
|
||||||
|
*
|
||||||
|
* @section intro_sec Introduction
|
||||||
|
*
|
||||||
|
* This is the documentation for Adafruit's NeoPixel library for the
|
||||||
|
* Arduino platform, allowing a broad range of microcontroller boards
|
||||||
|
* (most AVR boards, many ARM devices, ESP8266 and ESP32, among others)
|
||||||
|
* to control Adafruit NeoPixels, FLORA RGB Smart Pixels and compatible
|
||||||
|
* devices -- WS2811, WS2812, WS2812B, SK6812, etc.
|
||||||
|
*
|
||||||
|
* Adafruit invests time and resources providing this open source code,
|
||||||
|
* please support Adafruit and open-source hardware by purchasing products
|
||||||
|
* from Adafruit!
|
||||||
|
*
|
||||||
|
* @section author Author
|
||||||
|
*
|
||||||
|
* Written by Phil "Paint Your Dragon" Burgess for Adafruit Industries,
|
||||||
|
* with contributions by PJRC, Michael Miller and other members of the
|
||||||
|
* open source community.
|
||||||
|
*
|
||||||
|
* @section license License
|
||||||
|
*
|
||||||
|
* This file is part of the Adafruit_NeoPixel library.
|
||||||
|
*
|
||||||
|
* Adafruit_NeoPixel is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Adafruit_NeoPixel is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with NeoPixel. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Adafruit_NeoPixel.hpp"
|
||||||
|
#include "pico/stdio.h"
|
||||||
|
#include "pico/malloc.h"
|
||||||
|
//#include "pico/mem_ops.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
//#define DEBUG0 // high level debugging
|
||||||
|
//#define DEBUG1 // low level debugging
|
||||||
|
|
||||||
|
#ifdef DEBUG0
|
||||||
|
#define PRINTF0(...) printf(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define PRINTF0(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG1
|
||||||
|
#define PRINTF1(...) printf(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define PRINTF1(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief NeoPixel constructor when length, pin and pixel type are known
|
||||||
|
at compile-time.
|
||||||
|
@param n Number of NeoPixels in strand.
|
||||||
|
@param p Arduino pin number which will drive the NeoPixel data in.
|
||||||
|
@param t Pixel type -- add together NEO_* constants defined in
|
||||||
|
Adafruit_NeoPixel.h, for example NEO_GRB+NEO_KHZ800 for
|
||||||
|
NeoPixels expecting an 800 KHz (vs 400 KHz) data stream
|
||||||
|
with color bytes expressed in green, red, blue order per
|
||||||
|
pixel.
|
||||||
|
@return Adafruit_NeoPixel object. Call the begin() function before use.
|
||||||
|
*/
|
||||||
|
Adafruit_NeoPixel::Adafruit_NeoPixel(uint16_t n, uint16_t p, neoPixelType t) :
|
||||||
|
begun(false), brightness(0), pixels(NULL), opixels(NULL), brightfr(NULL), brightfg(NULL), brightfb(NULL), brightfw(NULL) {
|
||||||
|
PRINTF1("In constructor 1\n");
|
||||||
|
endTime = get_absolute_time() ;
|
||||||
|
PRINTF1("In constructor 2\n");
|
||||||
|
setPin(p);
|
||||||
|
PRINTF1("In constructor 3\n");
|
||||||
|
updateType(t);
|
||||||
|
PRINTF1("In constructor 4\n");
|
||||||
|
updateLength(n);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief "Empty" NeoPixel constructor when length, pin and/or pixel type
|
||||||
|
are not known at compile-time, and must be initialized later with
|
||||||
|
updateType(), updateLength() and setPin().
|
||||||
|
@return Adafruit_NeoPixel object. Call the begin() function before use.
|
||||||
|
@note This function is deprecated, here only for old projects that
|
||||||
|
may still be calling it. New projects should instead use the
|
||||||
|
'new' keyword with the first constructor syntax (length, pin,
|
||||||
|
type).
|
||||||
|
*/
|
||||||
|
Adafruit_NeoPixel::Adafruit_NeoPixel() :
|
||||||
|
#if defined(NEO_KHZ400)
|
||||||
|
is800KHz(true),
|
||||||
|
#endif
|
||||||
|
begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0), pixels(NULL), opixels(NULL), brightfr(NULL), brightfg(NULL), brightfb(NULL), brightfw(NULL), rOffset(1), gOffset(0), bOffset(2), wOffset(1){
|
||||||
|
endTime = get_absolute_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Deallocate Adafruit_NeoPixel object, set data pin back to INPUT, unclaim the statemachine
|
||||||
|
*/
|
||||||
|
Adafruit_NeoPixel::~Adafruit_NeoPixel() {
|
||||||
|
PRINTF0("In destructor\n ===>\n");
|
||||||
|
memset(pixels, 0, numBytes);
|
||||||
|
show() ;
|
||||||
|
sleep_ms(20) ;
|
||||||
|
PRINTF1("End init = %d, pin = %d, 800kHz = %d, length = %d, pio= %d, sm = %d, offset = %d, no_sm = [%d, %d]\n ", begun, pin, is800KHz, numLEDs, pio_get_index(pio), sm, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset,pio_no_sm[0], pio_no_sm[1] );
|
||||||
|
PRINTF1("going to free\n");
|
||||||
|
free(pixels); // unclaim the memory for the pixels
|
||||||
|
free(opixels); // unclaim the memory for the pixels
|
||||||
|
PRINTF1("freed pixels\n");
|
||||||
|
pio_sm_unclaim(pio,sm); // unclaim the state machine
|
||||||
|
pio_no_sm[pio_get_index(pio)]-- ;
|
||||||
|
if (pio_no_sm[pio_get_index(pio)] == 0 ) { // if no sm on the pio instance, remove the program
|
||||||
|
pio_remove_program (pio, &ws2812byte_program, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset );
|
||||||
|
if (pio_get_index(pio) == 0) {pio0_offset = -1;} else {pio1_offset = -1;};
|
||||||
|
};
|
||||||
|
|
||||||
|
PRINTF0("End destruructor = %d, pin = %d, 800kHz = %d, length = %d, pio= %d, sm = %d, offset = %d, no_sm = [%d, %d]\n ", begun, pin, is800KHz, numLEDs, pio_get_index(pio), sm, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset,pio_no_sm[0], pio_no_sm[1] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Configure NeoPixel pin for output.
|
||||||
|
*/
|
||||||
|
void Adafruit_NeoPixel::begin(void) {
|
||||||
|
// backwards comptability
|
||||||
|
// PRINTF0("In begin Begun = %d, pin = %d, length = %d\n", begun, pin, numLEDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Change the length of a previously-declared Adafruit_NeoPixel
|
||||||
|
strip object. Old data is deallocated and new data is cleared.
|
||||||
|
Pin number and pixel format are unchanged.
|
||||||
|
@param n New length of strip, in pixels.
|
||||||
|
@note This function is deprecated, here only for old projects that
|
||||||
|
may still be calling it. New projects should instead use the
|
||||||
|
'new' keyword with the first constructor syntax (length, pin,
|
||||||
|
type).
|
||||||
|
*/
|
||||||
|
void Adafruit_NeoPixel::updateLength(uint16_t n) {
|
||||||
|
free(pixels); // Free existing data (if any)
|
||||||
|
|
||||||
|
// Allocate new data -- note: ALL PIXELS ARE CLEARED
|
||||||
|
numBytes = n * ((wOffset == rOffset) ? 3 : 4);
|
||||||
|
if((pixels = (uint8_t *)malloc(numBytes))) {
|
||||||
|
memset(pixels, 0, numBytes);
|
||||||
|
numLEDs = n;
|
||||||
|
} else {
|
||||||
|
numLEDs = numBytes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (brightfr != NULL) {
|
||||||
|
free(opixels) ;
|
||||||
|
if((opixels = (uint8_t *)malloc(numBytes))) {
|
||||||
|
memset(opixels, 0, numBytes);
|
||||||
|
numLEDs = n;
|
||||||
|
} else {
|
||||||
|
numLEDs = numBytes = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Change the pixel format of a previously-declared
|
||||||
|
Adafruit_NeoPixel strip object. If format changes from one of
|
||||||
|
the RGB variants to an RGBW variant (or RGBW to RGB), the old
|
||||||
|
data will be deallocated and new data is cleared. Otherwise,
|
||||||
|
the old data will remain in RAM and is not reordered to the
|
||||||
|
new format, so it's advisable to follow up with clear().
|
||||||
|
@param t Pixel type -- add together NEO_* constants defined in
|
||||||
|
Adafruit_NeoPixel.h, for example NEO_GRB+NEO_KHZ800 for
|
||||||
|
NeoPixels expecting an 800 KHz (vs 400 KHz) data stream
|
||||||
|
with color bytes expressed in green, red, blue order per
|
||||||
|
pixel.
|
||||||
|
@note This function is deprecated, here only for old projects that
|
||||||
|
may still be calling it. New projects should instead use the
|
||||||
|
'new' keyword with the first constructor syntax
|
||||||
|
(length, pin, type).
|
||||||
|
*/
|
||||||
|
void Adafruit_NeoPixel::updateType(neoPixelType t) {
|
||||||
|
bool oldThreeBytesPerPixel = (wOffset == rOffset); // false if RGBW
|
||||||
|
|
||||||
|
wOffset = (t >> 6) & 0b11; // See notes in header file
|
||||||
|
rOffset = (t >> 4) & 0b11; // regarding R/G/B/W offsets
|
||||||
|
gOffset = (t >> 2) & 0b11;
|
||||||
|
bOffset = t & 0b11;
|
||||||
|
#if defined(NEO_KHZ400)
|
||||||
|
is800KHz = (t < 256); // 400 KHz flag is 1<<8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// If bytes-per-pixel has changed (and pixel data was previously
|
||||||
|
// allocated), re-allocate to new size. Will clear any data.
|
||||||
|
if(pixels) {
|
||||||
|
bool newThreeBytesPerPixel = (wOffset == rOffset);
|
||||||
|
if(newThreeBytesPerPixel != oldThreeBytesPerPixel) updateLength(numLEDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Adafruit_NeoPixel::rp2040Init(uint8_t set_pin)
|
||||||
|
{
|
||||||
|
PRINTF0("IN RP2040 INIT now\n");
|
||||||
|
// get free sm & pio and store these in the protected variables of the class
|
||||||
|
int resultsm;
|
||||||
|
bool canpio;
|
||||||
|
resultsm = pio_claim_unused_sm(pio0,false);
|
||||||
|
PRINTF1("resultsm = %d\n",resultsm);
|
||||||
|
if (resultsm != -1) {
|
||||||
|
if (pio0_offset == -1) {
|
||||||
|
canpio = pio_can_add_program(pio0, &ws2812byte_program);
|
||||||
|
PRINTF1("canpio = %d\n",canpio);
|
||||||
|
if (canpio) pio0_offset = pio_add_program(pio0, &ws2812byte_program);
|
||||||
|
};
|
||||||
|
pio = pio0;
|
||||||
|
};
|
||||||
|
if (resultsm == -1 || pio0_offset == -1) {
|
||||||
|
resultsm = pio_claim_unused_sm(pio1,false);
|
||||||
|
if (resultsm != -1) {
|
||||||
|
if (pio1_offset == -1) {
|
||||||
|
canpio = pio_can_add_program(pio1, &ws2812byte_program);
|
||||||
|
if (canpio) pio1_offset = pio_add_program(pio0, &ws2812byte_program);
|
||||||
|
};
|
||||||
|
pio = pio1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (resultsm == -1 || (pio0_offset == -1 && pio1_offset == -1)) {
|
||||||
|
sm = -1 ;
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
pio_no_sm[pio_get_index(pio)]++ ;
|
||||||
|
|
||||||
|
pin = set_pin ;
|
||||||
|
sm = resultsm ;
|
||||||
|
|
||||||
|
PRINTF1("End init = %d, pin = %d, 800kHz = %d, length = %d, pio= %d, sm = %d, offset = %d, no_sm = [%d, %d]\n ", begun, pin, is800KHz, numLEDs, pio_get_index(pio), sm, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset,pio_no_sm[0], pio_no_sm[1] );
|
||||||
|
|
||||||
|
if (is800KHz)
|
||||||
|
{
|
||||||
|
// 800kHz, 8 bit transfers
|
||||||
|
ws2812byte_program_init(pio, sm, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset, pin, 800000, 8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 400kHz, 8 bit transfers
|
||||||
|
ws2812byte_program_init(pio, sm, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset, pin, 400000, 8);
|
||||||
|
} ;
|
||||||
|
begun = true ;
|
||||||
|
PRINTF0("exit INIT pio %d, sm %d\n", pio_get_index(pio),sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_NeoPixel::rp2040changepin(uint8_t set_pin)
|
||||||
|
{
|
||||||
|
pin = set_pin ;
|
||||||
|
int resultpio = pio_get_index(pio);
|
||||||
|
|
||||||
|
if (is800KHz)
|
||||||
|
{
|
||||||
|
// 800kHz, 8 bit transfers
|
||||||
|
ws2812byte_program_init(pio, sm, (resultpio == 0) ? pio0_offset : pio1_offset, pin, 800000, 8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 400kHz, 8 bit transfers
|
||||||
|
ws2812byte_program_init(pio, sm, (resultpio == 0) ? pio0_offset : pio1_offset, pin, 400000, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_NeoPixel::rp2040Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz)
|
||||||
|
{
|
||||||
|
PRINTF0("In Show,");
|
||||||
|
if (!begun)
|
||||||
|
{
|
||||||
|
// On first pass through initialise the PIO
|
||||||
|
rp2040Init(pin);
|
||||||
|
begun = true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sm == -1) { return ; }
|
||||||
|
|
||||||
|
// PRINTF1("START TO SHOW = %d, pin = %d, 800kHz = %d, length = %d, pio= %d, sm = %d, offset = %d, no_sm = [%d, %d]\n ", begun, pin, is800KHz, numLEDs, pio_get_index(pio), sm, (pio_get_index(pio) == 0) ? pio0_offset : pio1_offset,pio_no_sm[0], pio_no_sm[1] );
|
||||||
|
while(numBytes--)
|
||||||
|
// Bits for transmission must be shifted to top 8 bits
|
||||||
|
pio_sm_put_blocking(pio, sm, ((uint32_t)*pixels++)<< 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Transmit pixel data in RAM to NeoPixels.
|
||||||
|
@note On most architectures, interrupts are temporarily disabled in
|
||||||
|
order to achieve the correct NeoPixel signal timing. This means
|
||||||
|
that the Arduino millis() and micros() functions, which require
|
||||||
|
interrupts, will lose small intervals of time whenever this
|
||||||
|
function is called (about 30 microseconds per RGB pixel, 40 for
|
||||||
|
RGBW pixels). There's no easy fix for this, but a few
|
||||||
|
specialized alternative or companion libraries exist that use
|
||||||
|
very device-specific peripherals to work around it.
|
||||||
|
*/
|
||||||
|
void Adafruit_NeoPixel::show(void) {
|
||||||
|
|
||||||
|
if(!pixels) return;
|
||||||
|
|
||||||
|
rp2040Show(pin, pixels, numBytes, is800KHz);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Set/change the NeoPixel output pin number. Previous pin,
|
||||||
|
if any, is set to INPUT and the new pin is set to OUTPUT.
|
||||||
|
@param p pin number.
|
||||||
|
*/
|
||||||
|
void Adafruit_NeoPixel::setPin(uint16_t p) {
|
||||||
|
if (!begun) {
|
||||||
|
pin = p ;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
rp2040changepin(pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Set a pixel's color using separate red, green and blue
|
||||||
|
components. If using RGBW pixels, white will be set to 0.
|
||||||
|
@param n Pixel index, starting from 0.
|
||||||
|
@param r Red brightness, 0 = minimum (off), 255 = maximum.
|
||||||
|
@param g Green brightness, 0 = minimum (off), 255 = maximum.
|
||||||
|
@param b Blue brightness, 0 = minimum (off), 255 = maximum.
|
||||||
|
*/
|
||||||
|
void Adafruit_NeoPixel::setPixelColor(
|
||||||
|
uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
setPixelColor(n,r,g,b,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Set a pixel's color using separate red, green, blue and white
|
||||||
|
components (for RGBW NeoPixels only).
|
||||||
|
@param n Pixel index, starting from 0.
|
||||||
|
@param r Red brightness, 0 = minimum (off), 255 = maximum.
|
||||||
|
@param g Green brightness, 0 = minimum (off), 255 = maximum.
|
||||||
|
@param b Blue brightness, 0 = minimum (off), 255 = maximum.
|
||||||
|
@param w White brightness, 0 = minimum (off), 255 = maximum, ignored
|
||||||
|
if using RGB pixels.
|
||||||
|
*/
|
||||||
|
void Adafruit_NeoPixel::setPixelColor(
|
||||||
|
uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||||
|
|
||||||
|
if(n < numLEDs) {
|
||||||
|
if(brightness) { // See notes in setBrightness()
|
||||||
|
r = (r * brightness) >> 8;
|
||||||
|
g = (g * brightness) >> 8;
|
||||||
|
b = (b * brightness) >> 8;
|
||||||
|
w = (w * brightness) >> 8;
|
||||||
|
}
|
||||||
|
uint8_t *p, *po;
|
||||||
|
if (brightfr == NULL) {
|
||||||
|
if(wOffset == rOffset) { // Is an RGB-type strip
|
||||||
|
p = &pixels[n * 3]; // 3 bytes per pixel
|
||||||
|
} else { // Is a WRGB-type strip
|
||||||
|
p = &pixels[n * 4]; // 4 bytes per pixel
|
||||||
|
p[wOffset] = w; // set W
|
||||||
|
}
|
||||||
|
p[rOffset] = r; // R,G,B always stored
|
||||||
|
p[gOffset] = g;
|
||||||
|
p[bOffset] = b;
|
||||||
|
} else {
|
||||||
|
if(wOffset == rOffset) { // Is an RGB-type strip
|
||||||
|
po = &opixels[n * 3]; // 3 bytes per pixel
|
||||||
|
p = &pixels[n * 3];
|
||||||
|
} else { // Is a WRGB-type strip
|
||||||
|
po = &opixels[n * 4]; // 4 bytes per pixel
|
||||||
|
p = &pixels[n * 4];
|
||||||
|
po[wOffset] = w; // set W
|
||||||
|
p[wOffset] = brightfw(w);
|
||||||
|
}
|
||||||
|
po[rOffset] = r; // R,G,B always stored
|
||||||
|
po[gOffset] = g;
|
||||||
|
po[bOffset] = b;
|
||||||
|
p[rOffset] = brightfr(r);
|
||||||
|
p[gOffset] = brightfg(g);
|
||||||
|
p[bOffset] = brightfb(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Set a pixel's color using a 32-bit 'packed' RGB or RGBW value.
|
||||||
|
@param n Pixel index, starting from 0.
|
||||||
|
@param c 32-bit color value. Most significant byte is white (for RGBW
|
||||||
|
pixels) or ignored (for RGB pixels), next is red, then green,
|
||||||
|
and least significant byte is blue.
|
||||||
|
*/
|
||||||
|
void Adafruit_NeoPixel::setPixelColor(uint16_t n, uint32_t c) {
|
||||||
|
uint8_t w = (uint8_t)(c >> 24);
|
||||||
|
uint8_t r = (uint8_t)(c >> 16);
|
||||||
|
uint8_t g = (uint8_t)(c >> 8);
|
||||||
|
uint8_t b = (uint8_t)c;
|
||||||
|
setPixelColor(n,r,g,b,w);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Fill all or part of the NeoPixel strip with a color.
|
||||||
|
@param c 32-bit color value. Most significant byte is white (for
|
||||||
|
RGBW pixels) or ignored (for RGB pixels), next is red,
|
||||||
|
then green, and least significant byte is blue. If all
|
||||||
|
arguments are unspecified, this will be 0 (off).
|
||||||
|
@param first Index of first pixel to fill, starting from 0. Must be
|
||||||
|
in-bounds, no clipping is performed. 0 if unspecified.
|
||||||
|
@param count Number of pixels to fill, as a positive value. Passing
|
||||||
|
0 or leaving unspecified will fill to end of strip.
|
||||||
|
*/
|
||||||
|
void Adafruit_NeoPixel::fill(uint32_t c, uint16_t first, uint16_t count) {
|
||||||
|
uint16_t i, end;
|
||||||
|
|
||||||
|
if(first >= numLEDs) {
|
||||||
|
return; // If first LED is past end of strip, nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the index ONE AFTER the last pixel to fill
|
||||||
|
if(count == 0) {
|
||||||
|
// Fill to end of strip
|
||||||
|
end = numLEDs;
|
||||||
|
} else {
|
||||||
|
// Ensure that the loop won't go past the last pixel
|
||||||
|
end = first + count;
|
||||||
|
if(end > numLEDs) end = numLEDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = first; i < end; i++) {
|
||||||
|
this->setPixelColor(i, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Convert hue, saturation and value into a packed 32-bit RGB color
|
||||||
|
that can be passed to setPixelColor() or other RGB-compatible
|
||||||
|
functions.
|
||||||
|
@param hue An unsigned 16-bit value, 0 to 65535, representing one full
|
||||||
|
loop of the color wheel, which allows 16-bit hues to "roll
|
||||||
|
over" while still doing the expected thing (and allowing
|
||||||
|
more precision than the wheel() function that was common to
|
||||||
|
prior NeoPixel examples).
|
||||||
|
@param sat Saturation, 8-bit value, 0 (min or pure grayscale) to 255
|
||||||
|
(max or pure hue). Default of 255 if unspecified.
|
||||||
|
@param val Value (brightness), 8-bit value, 0 (min / black / off) to
|
||||||
|
255 (max or full brightness). Default of 255 if unspecified.
|
||||||
|
@return Packed 32-bit RGB with the most significant byte set to 0 -- the
|
||||||
|
white element of WRGB pixels is NOT utilized. Result is linearly
|
||||||
|
but not perceptually correct, so you may want to pass the result
|
||||||
|
through the gamma32() function (or your own gamma-correction
|
||||||
|
operation) else colors may appear washed out. This is not done
|
||||||
|
automatically by this function because coders may desire a more
|
||||||
|
refined gamma-correction function than the simplified
|
||||||
|
one-size-fits-all operation of gamma32(). Diffusing the LEDs also
|
||||||
|
really seems to help when using low-saturation colors.
|
||||||
|
*/
|
||||||
|
uint32_t Adafruit_NeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) {
|
||||||
|
|
||||||
|
uint8_t r, g, b;
|
||||||
|
|
||||||
|
// Remap 0-65535 to 0-1529. Pure red is CENTERED on the 64K rollover;
|
||||||
|
// 0 is not the start of pure red, but the midpoint...a few values above
|
||||||
|
// zero and a few below 65536 all yield pure red (similarly, 32768 is the
|
||||||
|
// midpoint, not start, of pure cyan). The 8-bit RGB hexcone (256 values
|
||||||
|
// each for red, green, blue) really only allows for 1530 distinct hues
|
||||||
|
// (not 1536, more on that below), but the full unsigned 16-bit type was
|
||||||
|
// chosen for hue so that one's code can easily handle a contiguous color
|
||||||
|
// wheel by allowing hue to roll over in either direction.
|
||||||
|
hue = (hue * 1530L + 32768) / 65536;
|
||||||
|
// Because red is centered on the rollover point (the +32768 above,
|
||||||
|
// essentially a fixed-point +0.5), the above actually yields 0 to 1530,
|
||||||
|
// where 0 and 1530 would yield the same thing. Rather than apply a
|
||||||
|
// costly modulo operator, 1530 is handled as a special case below.
|
||||||
|
|
||||||
|
// So you'd think that the color "hexcone" (the thing that ramps from
|
||||||
|
// pure red, to pure yellow, to pure green and so forth back to red,
|
||||||
|
// yielding six slices), and with each color component having 256
|
||||||
|
// possible values (0-255), might have 1536 possible items (6*256),
|
||||||
|
// but in reality there's 1530. This is because the last element in
|
||||||
|
// each 256-element slice is equal to the first element of the next
|
||||||
|
// slice, and keeping those in there this would create small
|
||||||
|
// discontinuities in the color wheel. So the last element of each
|
||||||
|
// slice is dropped...we regard only elements 0-254, with item 255
|
||||||
|
// being picked up as element 0 of the next slice. Like this:
|
||||||
|
// Red to not-quite-pure-yellow is: 255, 0, 0 to 255, 254, 0
|
||||||
|
// Pure yellow to not-quite-pure-green is: 255, 255, 0 to 1, 255, 0
|
||||||
|
// Pure green to not-quite-pure-cyan is: 0, 255, 0 to 0, 255, 254
|
||||||
|
// and so forth. Hence, 1530 distinct hues (0 to 1529), and hence why
|
||||||
|
// the constants below are not the multiples of 256 you might expect.
|
||||||
|
|
||||||
|
// Convert hue to R,G,B (nested ifs faster than divide+mod+switch):
|
||||||
|
if(hue < 510) { // Red to Green-1
|
||||||
|
b = 0;
|
||||||
|
if(hue < 255) { // Red to Yellow-1
|
||||||
|
r = 255;
|
||||||
|
g = hue; // g = 0 to 254
|
||||||
|
} else { // Yellow to Green-1
|
||||||
|
r = 510 - hue; // r = 255 to 1
|
||||||
|
g = 255;
|
||||||
|
}
|
||||||
|
} else if(hue < 1020) { // Green to Blue-1
|
||||||
|
r = 0;
|
||||||
|
if(hue < 765) { // Green to Cyan-1
|
||||||
|
g = 255;
|
||||||
|
b = hue - 510; // b = 0 to 254
|
||||||
|
} else { // Cyan to Blue-1
|
||||||
|
g = 1020 - hue; // g = 255 to 1
|
||||||
|
b = 255;
|
||||||
|
}
|
||||||
|
} else if(hue < 1530) { // Blue to Red-1
|
||||||
|
g = 0;
|
||||||
|
if(hue < 1275) { // Blue to Magenta-1
|
||||||
|
r = hue - 1020; // r = 0 to 254
|
||||||
|
b = 255;
|
||||||
|
} else { // Magenta to Red-1
|
||||||
|
r = 255;
|
||||||
|
b = 1530 - hue; // b = 255 to 1
|
||||||
|
}
|
||||||
|
} else { // Last 0.5 Red (quicker than % operator)
|
||||||
|
r = 255;
|
||||||
|
g = b = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply saturation and value to R,G,B, pack into 32-bit result:
|
||||||
|
uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255
|
||||||
|
uint16_t s1 = 1 + sat; // 1 to 256; same reason
|
||||||
|
uint8_t s2 = 255 - sat; // 255 to 0
|
||||||
|
return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) |
|
||||||
|
(((((g * s1) >> 8) + s2) * v1) & 0xff00) |
|
||||||
|
( ((((b * s1) >> 8) + s2) * v1) >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Query the color of a previously-set pixel.
|
||||||
|
@param n Index of pixel to read (0 = first).
|
||||||
|
@return 'Packed' 32-bit RGB or WRGB value. Most significant byte is white
|
||||||
|
(for RGBW pixels) or 0 (for RGB pixels), next is red, then green,
|
||||||
|
and least significant byte is blue.
|
||||||
|
@note If the strip brightness has been changed from the default value
|
||||||
|
of 255, the color read from a pixel may not exactly match what
|
||||||
|
was previously written with one of the setPixelColor() functions.
|
||||||
|
This gets more pronounced at lower brightness levels.
|
||||||
|
*/
|
||||||
|
uint32_t Adafruit_NeoPixel::getPixelColor(uint16_t n) const {
|
||||||
|
if(n >= numLEDs) return 0; // Out of bounds, return no color.
|
||||||
|
|
||||||
|
uint8_t *p, *po;
|
||||||
|
|
||||||
|
if (brightfr == NULL) {
|
||||||
|
if(wOffset == rOffset) { // Is RGB-type device
|
||||||
|
p = &pixels[n * 3];
|
||||||
|
if(brightness) {
|
||||||
|
// Stored color was decimated by setBrightness(). Returned value
|
||||||
|
// attempts to scale back to an approximation of the original 24-bit
|
||||||
|
// value used when setting the pixel color, but there will always be
|
||||||
|
// some error -- those bits are simply gone. Issue is most
|
||||||
|
// pronounced at low brightness levels.
|
||||||
|
return (((uint32_t)(p[rOffset] << 8) / brightness) << 16) |
|
||||||
|
(((uint32_t)(p[gOffset] << 8) / brightness) << 8) |
|
||||||
|
( (uint32_t)(p[bOffset] << 8) / brightness );
|
||||||
|
} else {
|
||||||
|
// No brightness adjustment has been made -- return 'raw' color
|
||||||
|
return ((uint32_t)p[rOffset] << 16) |
|
||||||
|
((uint32_t)p[gOffset] << 8) |
|
||||||
|
(uint32_t)p[bOffset];
|
||||||
|
}
|
||||||
|
} else { // Is RGBW-type device
|
||||||
|
p = &pixels[n * 4];
|
||||||
|
if(brightness) { // Return scaled color
|
||||||
|
return (((uint32_t)(p[wOffset] << 8) / brightness) << 24) |
|
||||||
|
(((uint32_t)(p[rOffset] << 8) / brightness) << 16) |
|
||||||
|
(((uint32_t)(p[gOffset] << 8) / brightness) << 8) |
|
||||||
|
( (uint32_t)(p[bOffset] << 8) / brightness );
|
||||||
|
} else { // Return raw color
|
||||||
|
return ((uint32_t)p[wOffset] << 24) |
|
||||||
|
((uint32_t)p[rOffset] << 16) |
|
||||||
|
((uint32_t)p[gOffset] << 8) |
|
||||||
|
(uint32_t)p[bOffset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // else take the values from the original array
|
||||||
|
if(wOffset == rOffset) { // Is RGB-type device
|
||||||
|
po = &opixels[n * 3];
|
||||||
|
return ((uint32_t)po[rOffset] << 16) |
|
||||||
|
((uint32_t)po[gOffset] << 8) |
|
||||||
|
(uint32_t)po[bOffset];
|
||||||
|
} else { // Is RGBW-type device
|
||||||
|
po = &opixels[n * 4];
|
||||||
|
return ((uint32_t)po[wOffset] << 24) |
|
||||||
|
((uint32_t)po[rOffset] << 16) |
|
||||||
|
((uint32_t)po[gOffset] << 8) |
|
||||||
|
(uint32_t)po[bOffset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Adjust output brightness. Does not immediately affect what's
|
||||||
|
currently displayed on the LEDs. The next call to show() will
|
||||||
|
refresh the LEDs at this level.
|
||||||
|
@param b Brightness setting, 0=minimum (off), 255=brightest.
|
||||||
|
@note This was intended for one-time use in one's setup() function,
|
||||||
|
not as an animation effect in itself. Because of the way this
|
||||||
|
library "pre-multiplies" LED colors in RAM, changing the
|
||||||
|
brightness is often a "lossy" operation -- what you write to
|
||||||
|
pixels isn't necessary the same as what you'll read back.
|
||||||
|
Repeated brightness changes using this function exacerbate the
|
||||||
|
problem. Smart programs therefore treat the strip as a
|
||||||
|
write-only resource, maintaining their own state to render each
|
||||||
|
frame of an animation, not relying on read-modify-write.
|
||||||
|
*/
|
||||||
|
void Adafruit_NeoPixel::setBrightness(uint8_t b) {
|
||||||
|
// Stored brightness value is different than what's passed.
|
||||||
|
// This simplifies the actual scaling math later, allowing a fast
|
||||||
|
// 8x8-bit multiply and taking the MSB. 'brightness' is a uint8_t,
|
||||||
|
// adding 1 here may (intentionally) roll over...so 0 = max brightness
|
||||||
|
// (color values are interpreted literally; no scaling), 1 = min
|
||||||
|
// brightness (off), 255 = just below max brightness.
|
||||||
|
uint8_t newBrightness = b + 1;
|
||||||
|
if(newBrightness != brightness) { // Compare against prior value
|
||||||
|
// Brightness has changed -- re-scale existing data in RAM,
|
||||||
|
// This process is potentially "lossy," especially when increasing
|
||||||
|
// brightness. The tight timing in the WS2811/WS2812 code means there
|
||||||
|
// aren't enough free cycles to perform this scaling on the fly as data
|
||||||
|
// is issued. So we make a pass through the existing color data in RAM
|
||||||
|
// and scale it (subsequent graphics commands also work at this
|
||||||
|
// brightness level). If there's a significant step up in brightness,
|
||||||
|
// the limited number of steps (quantization) in the old data will be
|
||||||
|
// quite visible in the re-scaled version. For a non-destructive
|
||||||
|
// change, you'll need to re-render the full strip data. C'est la vie.
|
||||||
|
uint8_t c,
|
||||||
|
*ptr = pixels,
|
||||||
|
oldBrightness = brightness - 1; // De-wrap old brightness value
|
||||||
|
uint16_t scale;
|
||||||
|
if(oldBrightness == 0) scale = 0; // Avoid /0
|
||||||
|
else if(b == 255) scale = 65535 / oldBrightness;
|
||||||
|
else scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
|
||||||
|
for(uint16_t i=0; i<numBytes; i++) {
|
||||||
|
c = *ptr;
|
||||||
|
*ptr++ = (c * scale) >> 8;
|
||||||
|
}
|
||||||
|
brightness = newBrightness;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_NeoPixel::setBrightnessFunctions(pBrightnessFunc fr, pBrightnessFunc fg, pBrightnessFunc fb, pBrightnessFunc fw) {
|
||||||
|
|
||||||
|
if (opixels == NULL & numLEDs != 0) {
|
||||||
|
opixels = (uint8_t *)malloc(numBytes);
|
||||||
|
memcpy(opixels,pixels,numBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
brightfr = fr;
|
||||||
|
brightfg = fg;
|
||||||
|
brightfb = fb;
|
||||||
|
brightfw = fw;
|
||||||
|
|
||||||
|
for (int i = 0 ; i < numLEDs ; i++) {
|
||||||
|
uint32_t pixel;
|
||||||
|
pixel = getPixelColor(i);
|
||||||
|
setPixelColor(i,pixel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Retrieve the last-set brightness value for the strip.
|
||||||
|
@return Brightness value: 0 = minimum (off), 255 = maximum.
|
||||||
|
*/
|
||||||
|
uint8_t Adafruit_NeoPixel::getBrightness(void) const {
|
||||||
|
return brightness - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Fill the whole NeoPixel strip with 0 / black / off.
|
||||||
|
*/
|
||||||
|
void Adafruit_NeoPixel::clear(void) {
|
||||||
|
memset(pixels, 0, numBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 32-bit variant of gamma8() that applies the same function
|
||||||
|
// to all components of a packed RGB or WRGB value.
|
||||||
|
uint32_t Adafruit_NeoPixel::gamma32(uint32_t x) {
|
||||||
|
uint8_t *y = (uint8_t *)&x;
|
||||||
|
// All four bytes of a 32-bit value are filtered even if RGB (not WRGB),
|
||||||
|
// to avoid a bunch of shifting and masking that would be necessary for
|
||||||
|
// properly handling different endianisms (and each byte is a fairly
|
||||||
|
// trivial operation, so it might not even be wasting cycles vs a check
|
||||||
|
// and branch for the RGB case). In theory this might cause trouble *if*
|
||||||
|
// someone's storing information in the unused most significant byte
|
||||||
|
// of an RGB value, but this seems exceedingly rare and if it's
|
||||||
|
// encountered in reality they can mask values going in or coming out.
|
||||||
|
for(uint8_t i=0; i<4; i++) y[i] = gamma8(y[i]);
|
||||||
|
return x; // Packed 32-bit return
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
add_library(pico_neopixel INTERFACE)
|
||||||
|
|
||||||
|
pico_generate_pio_header(pico_neopixel ${CMAKE_CURRENT_LIST_DIR}/ws2812byte.pio)
|
||||||
|
|
||||||
|
target_sources(pico_neopixel INTERFACE
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/Adafruit_NeoPixel.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
pico_enable_stdio_usb(pico_neopixel 1)
|
||||||
|
pico_enable_stdio_uart(pico_neopixel 0)
|
||||||
|
|
||||||
|
target_include_directories(pico_neopixel INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
|
||||||
|
|
||||||
|
# Pull in pico libraries that we need
|
||||||
|
target_link_libraries(pico_neopixel INTERFACE pico_stdlib hardware_pio pico_malloc pico_mem_ops)
|
|
@ -0,0 +1,350 @@
|
||||||
|
/*!
|
||||||
|
* @file Adafruit_NeoPixel.h
|
||||||
|
*
|
||||||
|
* This is part of Adafruit's NeoPixel library for the Arduino platform,
|
||||||
|
* allowing a broad range of microcontroller boards (most AVR boards,
|
||||||
|
* many ARM devices, ESP8266 and ESP32, among others) to control Adafruit
|
||||||
|
* NeoPixels, FLORA RGB Smart Pixels and compatible devices -- WS2811,
|
||||||
|
* WS2812, WS2812B, SK6812, etc.
|
||||||
|
*
|
||||||
|
* Adafruit invests time and resources providing this open source code,
|
||||||
|
* please support Adafruit and open-source hardware by purchasing products
|
||||||
|
* from Adafruit!
|
||||||
|
*
|
||||||
|
* Written by Phil "Paint Your Dragon" Burgess for Adafruit Industries,
|
||||||
|
* with contributions by PJRC, Michael Miller and other members of the
|
||||||
|
* open source community.
|
||||||
|
*
|
||||||
|
* This file is part of the Adafruit_NeoPixel library.
|
||||||
|
*
|
||||||
|
* Adafruit_NeoPixel is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Adafruit_NeoPixel is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with NeoPixel. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "Adafruit_NeoPixel.h"
|
||||||
|
#include "pico/stdlib"
|
||||||
|
#include "hardware/pio"
|
||||||
|
#include "pico/time"
|
||||||
|
#include "ws2812byte.pio.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// The order of primary colors in the NeoPixel data stream can vary among
|
||||||
|
// device types, manufacturers and even different revisions of the same
|
||||||
|
// item. The third parameter to the Adafruit_NeoPixel constructor encodes
|
||||||
|
// the per-pixel byte offsets of the red, green and blue primaries (plus
|
||||||
|
// white, if present) in the data stream -- the following #defines provide
|
||||||
|
// an easier-to-use named version for each permutation. e.g. NEO_GRB
|
||||||
|
// indicates a NeoPixel-compatible device expecting three bytes per pixel,
|
||||||
|
// with the first byte transmitted containing the green value, second
|
||||||
|
// containing red and third containing blue. The in-memory representation
|
||||||
|
// of a chain of NeoPixels is the same as the data-stream order; no
|
||||||
|
// re-ordering of bytes is required when issuing data to the chain.
|
||||||
|
// Most of these values won't exist in real-world devices, but it's done
|
||||||
|
// this way so we're ready for it (also, if using the WS2811 driver IC,
|
||||||
|
// one might have their pixels set up in any weird permutation).
|
||||||
|
|
||||||
|
// Bits 5,4 of this value are the offset (0-3) from the first byte of a
|
||||||
|
// pixel to the location of the red color byte. Bits 3,2 are the green
|
||||||
|
// offset and 1,0 are the blue offset. If it is an RGBW-type device
|
||||||
|
// (supporting a white primary in addition to R,G,B), bits 7,6 are the
|
||||||
|
// offset to the white byte...otherwise, bits 7,6 are set to the same value
|
||||||
|
// as 5,4 (red) to indicate an RGB (not RGBW) device.
|
||||||
|
// i.e. binary representation:
|
||||||
|
// 0bWWRRGGBB for RGBW devices
|
||||||
|
// 0bRRRRGGBB for RGB
|
||||||
|
|
||||||
|
// RGB NeoPixel permutations; white and red offsets are always same
|
||||||
|
// Offset: W R G B
|
||||||
|
#define NEO_RGB ((0<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B
|
||||||
|
#define NEO_RBG ((0<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G
|
||||||
|
#define NEO_GRB ((1<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B
|
||||||
|
#define NEO_GBR ((2<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R
|
||||||
|
#define NEO_BRG ((1<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G
|
||||||
|
#define NEO_BGR ((2<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R
|
||||||
|
|
||||||
|
// RGBW NeoPixel permutations; all 4 offsets are distinct
|
||||||
|
// Offset: W R G B
|
||||||
|
#define NEO_WRGB ((0<<6) | (1<<4) | (2<<2) | (3)) ///< Transmit as W,R,G,B
|
||||||
|
#define NEO_WRBG ((0<<6) | (1<<4) | (3<<2) | (2)) ///< Transmit as W,R,B,G
|
||||||
|
#define NEO_WGRB ((0<<6) | (2<<4) | (1<<2) | (3)) ///< Transmit as W,G,R,B
|
||||||
|
#define NEO_WGBR ((0<<6) | (3<<4) | (1<<2) | (2)) ///< Transmit as W,G,B,R
|
||||||
|
#define NEO_WBRG ((0<<6) | (2<<4) | (3<<2) | (1)) ///< Transmit as W,B,R,G
|
||||||
|
#define NEO_WBGR ((0<<6) | (3<<4) | (2<<2) | (1)) ///< Transmit as W,B,G,R
|
||||||
|
|
||||||
|
#define NEO_RWGB ((1<<6) | (0<<4) | (2<<2) | (3)) ///< Transmit as R,W,G,B
|
||||||
|
#define NEO_RWBG ((1<<6) | (0<<4) | (3<<2) | (2)) ///< Transmit as R,W,B,G
|
||||||
|
#define NEO_RGWB ((2<<6) | (0<<4) | (1<<2) | (3)) ///< Transmit as R,G,W,B
|
||||||
|
#define NEO_RGBW ((3<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B,W
|
||||||
|
#define NEO_RBWG ((2<<6) | (0<<4) | (3<<2) | (1)) ///< Transmit as R,B,W,G
|
||||||
|
#define NEO_RBGW ((3<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G,W
|
||||||
|
|
||||||
|
#define NEO_GWRB ((1<<6) | (2<<4) | (0<<2) | (3)) ///< Transmit as G,W,R,B
|
||||||
|
#define NEO_GWBR ((1<<6) | (3<<4) | (0<<2) | (2)) ///< Transmit as G,W,B,R
|
||||||
|
#define NEO_GRWB ((2<<6) | (1<<4) | (0<<2) | (3)) ///< Transmit as G,R,W,B
|
||||||
|
#define NEO_GRBW ((3<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B,W
|
||||||
|
#define NEO_GBWR ((2<<6) | (3<<4) | (0<<2) | (1)) ///< Transmit as G,B,W,R
|
||||||
|
#define NEO_GBRW ((3<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R,W
|
||||||
|
|
||||||
|
#define NEO_BWRG ((1<<6) | (2<<4) | (3<<2) | (0)) ///< Transmit as B,W,R,G
|
||||||
|
#define NEO_BWGR ((1<<6) | (3<<4) | (2<<2) | (0)) ///< Transmit as B,W,G,R
|
||||||
|
#define NEO_BRWG ((2<<6) | (1<<4) | (3<<2) | (0)) ///< Transmit as B,R,W,G
|
||||||
|
#define NEO_BRGW ((3<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G,W
|
||||||
|
#define NEO_BGWR ((2<<6) | (3<<4) | (1<<2) | (0)) ///< Transmit as B,G,W,R
|
||||||
|
#define NEO_BGRW ((3<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R,W
|
||||||
|
|
||||||
|
// Add NEO_KHZ400 to the color order value to indicate a 400 KHz device.
|
||||||
|
// All but the earliest v1 NeoPixels expect an 800 KHz data stream, this is
|
||||||
|
// the default if unspecified. Because flash space is very limited on ATtiny
|
||||||
|
// devices (e.g. Trinket, Gemma), v1 NeoPixels aren't handled by default on
|
||||||
|
// those chips, though it can be enabled by removing the ifndef/endif below,
|
||||||
|
// but code will be bigger. Conversely, can disable the NEO_KHZ400 line on
|
||||||
|
// other MCUs to remove v1 support and save a little space.
|
||||||
|
|
||||||
|
|
||||||
|
#define NEO_KHZ800 0x0000 ///< 800 KHz data transmission
|
||||||
|
#define NEO_KHZ400 0x0100 ///< 400 KHz data transmission
|
||||||
|
|
||||||
|
typedef uint16_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor
|
||||||
|
|
||||||
|
// These two tables are declared outside the Adafruit_NeoPixel class
|
||||||
|
// because some boards may require oldschool compilers that don't
|
||||||
|
// handle the C++11 constexpr keyword.
|
||||||
|
|
||||||
|
/* A PROGMEM (flash mem) table containing 8-bit unsigned sine wave (0-255).
|
||||||
|
Copy & paste this snippet into a Python REPL to regenerate:
|
||||||
|
import math
|
||||||
|
for x in range(256):
|
||||||
|
print("{:3},".format(int((math.sin(x/128.0*math.pi)+1.0)*127.5+0.5))),
|
||||||
|
if x&15 == 15: print
|
||||||
|
*/
|
||||||
|
static const uint8_t _NeoPixelSineTable[256] = {
|
||||||
|
128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,
|
||||||
|
176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
|
||||||
|
218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244,
|
||||||
|
245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
|
||||||
|
255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246,
|
||||||
|
245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
|
||||||
|
218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,
|
||||||
|
176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
|
||||||
|
128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82,
|
||||||
|
79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40,
|
||||||
|
37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11,
|
||||||
|
10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9,
|
||||||
|
10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
|
||||||
|
37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
|
||||||
|
79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124};
|
||||||
|
|
||||||
|
/* Similar to above, but for an 8-bit gamma-correction table.
|
||||||
|
Copy & paste this snippet into a Python REPL to regenerate:
|
||||||
|
import math
|
||||||
|
gamma=2.6
|
||||||
|
for x in range(256):
|
||||||
|
print("{:3},".format(int(math.pow((x)/255.0,gamma)*255.0+0.5))),
|
||||||
|
if x&15 == 15: print
|
||||||
|
*/
|
||||||
|
static const uint8_t _NeoPixelGammaTable[256] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
|
||||||
|
3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7,
|
||||||
|
7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12,
|
||||||
|
13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20,
|
||||||
|
20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29,
|
||||||
|
30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42,
|
||||||
|
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
|
||||||
|
58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75,
|
||||||
|
76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96,
|
||||||
|
97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120,
|
||||||
|
122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148,
|
||||||
|
150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,
|
||||||
|
182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215,
|
||||||
|
218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255};
|
||||||
|
|
||||||
|
// gloal hardware resource variables keeping track of configurations an usage on the pi Pico RP2040.
|
||||||
|
static int pio0_offset = -1; // offset of loaded pio neopixel program on pio0; -1 if no program loaded
|
||||||
|
static int pio1_offset = -1; // offset of loaded pio neopixel program on pio1; -1 if no program loaded
|
||||||
|
static int pio_no_sm[2] = {0,0} ; // number of state machines in use for Neopixel
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Class that stores state and functions for interacting with
|
||||||
|
Adafruit NeoPixels and compatible devices.
|
||||||
|
*/
|
||||||
|
class Adafruit_NeoPixel {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Constructor: number of LEDs, pin number, LED type
|
||||||
|
Adafruit_NeoPixel(uint16_t n, uint16_t pin=0,
|
||||||
|
neoPixelType type=NEO_GRB + NEO_KHZ800);
|
||||||
|
Adafruit_NeoPixel(void);
|
||||||
|
~Adafruit_NeoPixel();
|
||||||
|
|
||||||
|
void begin(void);
|
||||||
|
void show(void);
|
||||||
|
void setPin(uint16_t p);
|
||||||
|
void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b,
|
||||||
|
uint8_t w);
|
||||||
|
void setPixelColor(uint16_t n, uint32_t c);
|
||||||
|
void fill(uint32_t c=0, uint16_t first=0, uint16_t count=0);
|
||||||
|
void setBrightness(uint8_t);
|
||||||
|
void clear(void);
|
||||||
|
void updateLength(uint16_t n);
|
||||||
|
void updateType(neoPixelType t);
|
||||||
|
/*!
|
||||||
|
@brief Check whether a call to show() will start sending data
|
||||||
|
immediately or will 'block' for a required interval. NeoPixels
|
||||||
|
require a short quiet time (about 300 microseconds) after the
|
||||||
|
last bit is received before the data 'latches' and new data can
|
||||||
|
start being received. Usually one's sketch is implicitly using
|
||||||
|
this time to generate a new frame of animation...but if it
|
||||||
|
finishes very quickly, this function could be used to see if
|
||||||
|
there's some idle time available for some low-priority
|
||||||
|
concurrent task.
|
||||||
|
@return 1 or true if show() will start sending immediately, 0 or false
|
||||||
|
if show() would block (meaning some idle time is available).
|
||||||
|
*/
|
||||||
|
bool canShow(void) {
|
||||||
|
int64_t howlongago = absolute_time_diff_us (endTime, get_absolute_time());
|
||||||
|
return (howlongago >= 300L);
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
@brief Get a pointer directly to the NeoPixel data buffer in RAM.
|
||||||
|
Pixel data is stored in a device-native format (a la the NEO_*
|
||||||
|
constants) and is not translated here. Applications that access
|
||||||
|
this buffer will need to be aware of the specific data format
|
||||||
|
and handle colors appropriately.
|
||||||
|
@return Pointer to NeoPixel buffer (uint8_t* array).
|
||||||
|
@note This is for high-performance applications where calling
|
||||||
|
setPixelColor() on every single pixel would be too slow (e.g.
|
||||||
|
POV or light-painting projects). There is no bounds checking
|
||||||
|
on the array, creating tremendous potential for mayhem if one
|
||||||
|
writes past the ends of the buffer. Great power, great
|
||||||
|
responsibility and all that.
|
||||||
|
*/
|
||||||
|
uint8_t *getPixels(void) const { return pixels; };
|
||||||
|
uint8_t getBrightness(void) const;
|
||||||
|
/*!
|
||||||
|
@brief Retrieve the pin number used for NeoPixel data output.
|
||||||
|
@return Arduino pin number (-1 if not set).
|
||||||
|
*/
|
||||||
|
int16_t getPin(void) const { return pin; };
|
||||||
|
/*!
|
||||||
|
@brief Return the number of pixels in an Adafruit_NeoPixel strip object.
|
||||||
|
@return Pixel count (0 if not set).
|
||||||
|
*/
|
||||||
|
uint16_t numPixels(void) const { return numLEDs; }
|
||||||
|
uint32_t getPixelColor(uint16_t n) const;
|
||||||
|
/*!
|
||||||
|
@brief An 8-bit integer sine wave function, not directly compatible
|
||||||
|
with standard trigonometric units like radians or degrees.
|
||||||
|
@param x Input angle, 0-255; 256 would loop back to zero, completing
|
||||||
|
the circle (equivalent to 360 degrees or 2 pi radians).
|
||||||
|
One can therefore use an unsigned 8-bit variable and simply
|
||||||
|
add or subtract, allowing it to overflow/underflow and it
|
||||||
|
still does the expected contiguous thing.
|
||||||
|
@return Sine result, 0 to 255, or -128 to +127 if type-converted to
|
||||||
|
a signed int8_t, but you'll most likely want unsigned as this
|
||||||
|
output is often used for pixel brightness in animation effects.
|
||||||
|
*/
|
||||||
|
static uint8_t sine8(uint8_t x) {
|
||||||
|
return pgm_read_byte(&_NeoPixelSineTable[x]); // 0-255 in, 0-255 out
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
@brief An 8-bit gamma-correction function for basic pixel brightness
|
||||||
|
adjustment. Makes color transitions appear more perceptially
|
||||||
|
correct.
|
||||||
|
@param x Input brightness, 0 (minimum or off/black) to 255 (maximum).
|
||||||
|
@return Gamma-adjusted brightness, can then be passed to one of the
|
||||||
|
setPixelColor() functions. This uses a fixed gamma correction
|
||||||
|
exponent of 2.6, which seems reasonably okay for average
|
||||||
|
NeoPixels in average tasks. If you need finer control you'll
|
||||||
|
need to provide your own gamma-correction function instead.
|
||||||
|
*/
|
||||||
|
static uint8_t gamma8(uint8_t x) {
|
||||||
|
return pgm_read_byte(&_NeoPixelGammaTable[x]); // 0-255 in, 0-255 out
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
@brief Convert separate red, green and blue values into a single
|
||||||
|
"packed" 32-bit RGB color.
|
||||||
|
@param r Red brightness, 0 to 255.
|
||||||
|
@param g Green brightness, 0 to 255.
|
||||||
|
@param b Blue brightness, 0 to 255.
|
||||||
|
@return 32-bit packed RGB value, which can then be assigned to a
|
||||||
|
variable for later use or passed to the setPixelColor()
|
||||||
|
function. Packed RGB format is predictable, regardless of
|
||||||
|
LED strand color order.
|
||||||
|
*/
|
||||||
|
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
@brief Convert separate red, green, blue and white values into a
|
||||||
|
single "packed" 32-bit WRGB color.
|
||||||
|
@param r Red brightness, 0 to 255.
|
||||||
|
@param g Green brightness, 0 to 255.
|
||||||
|
@param b Blue brightness, 0 to 255.
|
||||||
|
@param w White brightness, 0 to 255.
|
||||||
|
@return 32-bit packed WRGB value, which can then be assigned to a
|
||||||
|
variable for later use or passed to the setPixelColor()
|
||||||
|
function. Packed WRGB format is predictable, regardless of
|
||||||
|
LED strand color order.
|
||||||
|
*/
|
||||||
|
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||||
|
return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
||||||
|
}
|
||||||
|
static uint32_t ColorHSV(uint16_t hue, uint8_t sat=255, uint8_t val=255);
|
||||||
|
/*!
|
||||||
|
@brief A gamma-correction function for 32-bit packed RGB or WRGB
|
||||||
|
colors. Makes color transitions appear more perceptially
|
||||||
|
correct.
|
||||||
|
@param x 32-bit packed RGB or WRGB color.
|
||||||
|
@return Gamma-adjusted packed color, can then be passed in one of the
|
||||||
|
setPixelColor() functions. Like gamma8(), this uses a fixed
|
||||||
|
gamma correction exponent of 2.6, which seems reasonably okay
|
||||||
|
for average NeoPixels in average tasks. If you need finer
|
||||||
|
control you'll need to provide your own gamma-correction
|
||||||
|
function instead.
|
||||||
|
*/
|
||||||
|
static uint32_t gamma32(uint32_t x);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void rp2040Init(uint8_t pin, bool is800KHz) ;
|
||||||
|
void rp2040Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
bool is800KHz; ///< true if 800 KHz pixels
|
||||||
|
bool begun; ///< true if begin() previously called
|
||||||
|
uint16_t numLEDs; ///< Number of RGB LEDs in strip
|
||||||
|
uint16_t numBytes; ///< Size of 'pixels' buffer below
|
||||||
|
int16_t pin; ///< Output pin number (-1 if not yet set)
|
||||||
|
uint8_t brightness; ///< Strip brightness 0-255 (stored as +1)
|
||||||
|
uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each)
|
||||||
|
uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel
|
||||||
|
uint8_t gOffset; ///< Index of green byte
|
||||||
|
uint8_t bOffset; ///< Index of blue byte
|
||||||
|
uint8_t wOffset; ///< Index of white (==rOffset if no white)
|
||||||
|
absolute_time_t endTime; ///< Latch timing reference
|
||||||
|
PIO pio; ///< chosen pio for this object
|
||||||
|
uint sm; ///<chosen state machine for this object; -1 if not yet set or none available.
|
||||||
|
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,360 @@
|
||||||
|
/*!
|
||||||
|
* @file Adafruit_NeoPixel.h
|
||||||
|
*
|
||||||
|
* This is part of Adafruit's NeoPixel library for the Arduino platform,
|
||||||
|
* allowing a broad range of microcontroller boards (most AVR boards,
|
||||||
|
* many ARM devices, ESP8266 and ESP32, among others) to control Adafruit
|
||||||
|
* NeoPixels, FLORA RGB Smart Pixels and compatible devices -- WS2811,
|
||||||
|
* WS2812, WS2812B, SK6812, etc.
|
||||||
|
*
|
||||||
|
* Adafruit invests time and resources providing this open source code,
|
||||||
|
* please support Adafruit and open-source hardware by purchasing products
|
||||||
|
* from Adafruit!
|
||||||
|
*
|
||||||
|
* Written by Phil "Paint Your Dragon" Burgess for Adafruit Industries,
|
||||||
|
* with contributions by PJRC, Michael Miller and other members of the
|
||||||
|
* open source community.
|
||||||
|
*
|
||||||
|
* This file is part of the Adafruit_NeoPixel library.
|
||||||
|
*
|
||||||
|
* Adafruit_NeoPixel is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Adafruit_NeoPixel is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with NeoPixel. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "pico/stdio.h"
|
||||||
|
#include "hardware/pio.h"
|
||||||
|
#include "pico/time.h"
|
||||||
|
#include "ws2812byte.pio.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// The order of primary colors in the NeoPixel data stream can vary among
|
||||||
|
// device types, manufacturers and even different revisions of the same
|
||||||
|
// item. The third parameter to the Adafruit_NeoPixel constructor encodes
|
||||||
|
// the per-pixel byte offsets of the red, green and blue primaries (plus
|
||||||
|
// white, if present) in the data stream -- the following #defines provide
|
||||||
|
// an easier-to-use named version for each permutation. e.g. NEO_GRB
|
||||||
|
// indicates a NeoPixel-compatible device expecting three bytes per pixel,
|
||||||
|
// with the first byte transmitted containing the green value, second
|
||||||
|
// containing red and third containing blue. The in-memory representation
|
||||||
|
// of a chain of NeoPixels is the same as the data-stream order; no
|
||||||
|
// re-ordering of bytes is required when issuing data to the chain.
|
||||||
|
// Most of these values won't exist in real-world devices, but it's done
|
||||||
|
// this way so we're ready for it (also, if using the WS2811 driver IC,
|
||||||
|
// one might have their pixels set up in any weird permutation).
|
||||||
|
|
||||||
|
// Bits 5,4 of this value are the offset (0-3) from the first byte of a
|
||||||
|
// pixel to the location of the red color byte. Bits 3,2 are the green
|
||||||
|
// offset and 1,0 are the blue offset. If it is an RGBW-type device
|
||||||
|
// (supporting a white primary in addition to R,G,B), bits 7,6 are the
|
||||||
|
// offset to the white byte...otherwise, bits 7,6 are set to the same value
|
||||||
|
// as 5,4 (red) to indicate an RGB (not RGBW) device.
|
||||||
|
// i.e. binary representation:
|
||||||
|
// 0bWWRRGGBB for RGBW devices
|
||||||
|
// 0bRRRRGGBB for RGB
|
||||||
|
|
||||||
|
// RGB NeoPixel permutations; white and red offsets are always same
|
||||||
|
// Offset: W R G B
|
||||||
|
#define NEO_RGB ((0<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B
|
||||||
|
#define NEO_RBG ((0<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G
|
||||||
|
#define NEO_GRB ((1<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B
|
||||||
|
#define NEO_GBR ((2<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R
|
||||||
|
#define NEO_BRG ((1<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G
|
||||||
|
#define NEO_BGR ((2<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R
|
||||||
|
|
||||||
|
// RGBW NeoPixel permutations; all 4 offsets are distinct
|
||||||
|
// Offset: W R G B
|
||||||
|
#define NEO_WRGB ((0<<6) | (1<<4) | (2<<2) | (3)) ///< Transmit as W,R,G,B
|
||||||
|
#define NEO_WRBG ((0<<6) | (1<<4) | (3<<2) | (2)) ///< Transmit as W,R,B,G
|
||||||
|
#define NEO_WGRB ((0<<6) | (2<<4) | (1<<2) | (3)) ///< Transmit as W,G,R,B
|
||||||
|
#define NEO_WGBR ((0<<6) | (3<<4) | (1<<2) | (2)) ///< Transmit as W,G,B,R
|
||||||
|
#define NEO_WBRG ((0<<6) | (2<<4) | (3<<2) | (1)) ///< Transmit as W,B,R,G
|
||||||
|
#define NEO_WBGR ((0<<6) | (3<<4) | (2<<2) | (1)) ///< Transmit as W,B,G,R
|
||||||
|
|
||||||
|
#define NEO_RWGB ((1<<6) | (0<<4) | (2<<2) | (3)) ///< Transmit as R,W,G,B
|
||||||
|
#define NEO_RWBG ((1<<6) | (0<<4) | (3<<2) | (2)) ///< Transmit as R,W,B,G
|
||||||
|
#define NEO_RGWB ((2<<6) | (0<<4) | (1<<2) | (3)) ///< Transmit as R,G,W,B
|
||||||
|
#define NEO_RGBW ((3<<6) | (0<<4) | (1<<2) | (2)) ///< Transmit as R,G,B,W
|
||||||
|
#define NEO_RBWG ((2<<6) | (0<<4) | (3<<2) | (1)) ///< Transmit as R,B,W,G
|
||||||
|
#define NEO_RBGW ((3<<6) | (0<<4) | (2<<2) | (1)) ///< Transmit as R,B,G,W
|
||||||
|
|
||||||
|
#define NEO_GWRB ((1<<6) | (2<<4) | (0<<2) | (3)) ///< Transmit as G,W,R,B
|
||||||
|
#define NEO_GWBR ((1<<6) | (3<<4) | (0<<2) | (2)) ///< Transmit as G,W,B,R
|
||||||
|
#define NEO_GRWB ((2<<6) | (1<<4) | (0<<2) | (3)) ///< Transmit as G,R,W,B
|
||||||
|
#define NEO_GRBW ((3<<6) | (1<<4) | (0<<2) | (2)) ///< Transmit as G,R,B,W
|
||||||
|
#define NEO_GBWR ((2<<6) | (3<<4) | (0<<2) | (1)) ///< Transmit as G,B,W,R
|
||||||
|
#define NEO_GBRW ((3<<6) | (2<<4) | (0<<2) | (1)) ///< Transmit as G,B,R,W
|
||||||
|
|
||||||
|
#define NEO_BWRG ((1<<6) | (2<<4) | (3<<2) | (0)) ///< Transmit as B,W,R,G
|
||||||
|
#define NEO_BWGR ((1<<6) | (3<<4) | (2<<2) | (0)) ///< Transmit as B,W,G,R
|
||||||
|
#define NEO_BRWG ((2<<6) | (1<<4) | (3<<2) | (0)) ///< Transmit as B,R,W,G
|
||||||
|
#define NEO_BRGW ((3<<6) | (1<<4) | (2<<2) | (0)) ///< Transmit as B,R,G,W
|
||||||
|
#define NEO_BGWR ((2<<6) | (3<<4) | (1<<2) | (0)) ///< Transmit as B,G,W,R
|
||||||
|
#define NEO_BGRW ((3<<6) | (2<<4) | (1<<2) | (0)) ///< Transmit as B,G,R,W
|
||||||
|
|
||||||
|
// Add NEO_KHZ400 to the color order value to indicate a 400 KHz device.
|
||||||
|
// All but the earliest v1 NeoPixels expect an 800 KHz data stream, this is
|
||||||
|
// the default if unspecified. Because flash space is very limited on ATtiny
|
||||||
|
// devices (e.g. Trinket, Gemma), v1 NeoPixels aren't handled by default on
|
||||||
|
// those chips, though it can be enabled by removing the ifndef/endif below,
|
||||||
|
// but code will be bigger. Conversely, can disable the NEO_KHZ400 line on
|
||||||
|
// other MCUs to remove v1 support and save a little space.
|
||||||
|
|
||||||
|
|
||||||
|
#define NEO_KHZ800 0x0000 ///< 800 KHz data transmission
|
||||||
|
#define NEO_KHZ400 0x0100 ///< 400 KHz data transmission
|
||||||
|
|
||||||
|
typedef uint16_t neoPixelType; ///< 3rd arg to Adafruit_NeoPixel constructor
|
||||||
|
typedef uint8_t (* pBrightnessFunc)(uint8_t value) ; // pointer to a brigness conversion function
|
||||||
|
|
||||||
|
// These two tables are declared outside the Adafruit_NeoPixel class
|
||||||
|
// because some boards may require oldschool compilers that don't
|
||||||
|
// handle the C++11 constexpr keyword.
|
||||||
|
|
||||||
|
/* A PROGMEM (flash mem) table containing 8-bit unsigned sine wave (0-255).
|
||||||
|
Copy & paste this snippet into a Python REPL to regenerate:
|
||||||
|
import math
|
||||||
|
for x in range(256):
|
||||||
|
print("{:3},".format(int((math.sin(x/128.0*math.pi)+1.0)*127.5+0.5))),
|
||||||
|
if x&15 == 15: print
|
||||||
|
*/
|
||||||
|
static const uint8_t _NeoPixelSineTable[256] = {
|
||||||
|
128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,
|
||||||
|
176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
|
||||||
|
218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244,
|
||||||
|
245,246,248,249,250,250,251,252,253,253,254,254,254,255,255,255,
|
||||||
|
255,255,255,255,254,254,254,253,253,252,251,250,250,249,248,246,
|
||||||
|
245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
|
||||||
|
218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,
|
||||||
|
176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
|
||||||
|
128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82,
|
||||||
|
79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40,
|
||||||
|
37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11,
|
||||||
|
10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9,
|
||||||
|
10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
|
||||||
|
37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
|
||||||
|
79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124};
|
||||||
|
|
||||||
|
/* Similar to above, but for an 8-bit gamma-correction table.
|
||||||
|
Copy & paste this snippet into a Python REPL to regenerate:
|
||||||
|
import math
|
||||||
|
gamma=2.6
|
||||||
|
for x in range(256):
|
||||||
|
print("{:3},".format(int(math.pow((x)/255.0,gamma)*255.0+0.5))),
|
||||||
|
if x&15 == 15: print
|
||||||
|
*/
|
||||||
|
static const uint8_t _NeoPixelGammaTable[256] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
|
||||||
|
3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7,
|
||||||
|
7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12,
|
||||||
|
13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20,
|
||||||
|
20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29,
|
||||||
|
30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42,
|
||||||
|
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
|
||||||
|
58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, 71, 72, 73, 75,
|
||||||
|
76, 77, 78, 80, 81, 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96,
|
||||||
|
97, 99,100,102,103,105,106,108,109,111,112,114,115,117,119,120,
|
||||||
|
122,124,125,127,129,130,132,134,136,137,139,141,143,145,146,148,
|
||||||
|
150,152,154,156,158,160,162,164,166,168,170,172,174,176,178,180,
|
||||||
|
182,184,186,188,191,193,195,197,199,202,204,206,209,211,213,215,
|
||||||
|
218,220,223,225,227,230,232,235,237,240,242,245,247,250,252,255};
|
||||||
|
|
||||||
|
// gloal hardware resource variables keeping track of configurations an usage on the pi Pico RP2040.
|
||||||
|
static int pio0_offset = -1; // offset of loaded pio neopixel program on pio0; -1 if no program loaded
|
||||||
|
static int pio1_offset = -1; // offset of loaded pio neopixel program on pio1; -1 if no program loaded
|
||||||
|
static int pio_no_sm[2] = {0,0} ; // number of state machines in use for Neopixel
|
||||||
|
|
||||||
|
static uint8_t neopixels_gamma8(uint8_t x) {
|
||||||
|
return _NeoPixelGammaTable[x]; // 0-255 in, 0-255 out
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Class that stores state and functions for interacting with
|
||||||
|
Adafruit NeoPixels and compatible devices.
|
||||||
|
*/
|
||||||
|
class Adafruit_NeoPixel {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Constructor: number of LEDs, pin number, LED type
|
||||||
|
Adafruit_NeoPixel(uint16_t n, uint16_t pin=0,
|
||||||
|
neoPixelType type=NEO_GRB + NEO_KHZ800);
|
||||||
|
Adafruit_NeoPixel(void);
|
||||||
|
~Adafruit_NeoPixel();
|
||||||
|
|
||||||
|
void begin(void);
|
||||||
|
void show(void);
|
||||||
|
void setBrightnessFunctions(pBrightnessFunc fr, pBrightnessFunc fg, pBrightnessFunc fb, pBrightnessFunc fw);
|
||||||
|
void setPin(uint16_t p);
|
||||||
|
void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b,
|
||||||
|
uint8_t w);
|
||||||
|
void setPixelColor(uint16_t n, uint32_t c);
|
||||||
|
void fill(uint32_t c=0, uint16_t first=0, uint16_t count=0);
|
||||||
|
void setBrightness(uint8_t);
|
||||||
|
void clear(void);
|
||||||
|
void updateLength(uint16_t n);
|
||||||
|
void updateType(neoPixelType t);
|
||||||
|
/*!
|
||||||
|
@brief Check whether a call to show() will start sending data
|
||||||
|
immediately or will 'block' for a required interval. NeoPixels
|
||||||
|
require a short quiet time (about 300 microseconds) after the
|
||||||
|
last bit is received before the data 'latches' and new data can
|
||||||
|
start being received. Usually one's sketch is implicitly using
|
||||||
|
this time to generate a new frame of animation...but if it
|
||||||
|
finishes very quickly, this function could be used to see if
|
||||||
|
there's some idle time available for some low-priority
|
||||||
|
concurrent task.
|
||||||
|
@return 1 or true if show() will start sending immediately, 0 or false
|
||||||
|
if show() would block (meaning some idle time is available).
|
||||||
|
*/
|
||||||
|
bool canShow(void) {
|
||||||
|
int64_t howlongago = absolute_time_diff_us (endTime, get_absolute_time());
|
||||||
|
return (howlongago >= 300L);
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
@brief Get a pointer directly to the NeoPixel data buffer in RAM.
|
||||||
|
Pixel data is stored in a device-native format (a la the NEO_*
|
||||||
|
constants) and is not translated here. Applications that access
|
||||||
|
this buffer will need to be aware of the specific data format
|
||||||
|
and handle colors appropriately.
|
||||||
|
@return Pointer to NeoPixel buffer (uint8_t* array).
|
||||||
|
@note This is for high-performance applications where calling
|
||||||
|
setPixelColor() on every single pixel would be too slow (e.g.
|
||||||
|
POV or light-painting projects). There is no bounds checking
|
||||||
|
on the array, creating tremendous potential for mayhem if one
|
||||||
|
writes past the ends of the buffer. Great power, great
|
||||||
|
responsibility and all that.
|
||||||
|
*/
|
||||||
|
uint8_t *getPixels(void) const { return pixels; };
|
||||||
|
uint8_t getBrightness(void) const;
|
||||||
|
/*!
|
||||||
|
@brief Retrieve the pin number used for NeoPixel data output.
|
||||||
|
@return Arduino pin number (-1 if not set).
|
||||||
|
*/
|
||||||
|
int16_t getPin(void) const { return pin; };
|
||||||
|
/*!
|
||||||
|
@brief Return the number of pixels in an Adafruit_NeoPixel strip object.
|
||||||
|
@return Pixel count (0 if not set).
|
||||||
|
*/
|
||||||
|
uint16_t numPixels(void) const { return numLEDs; }
|
||||||
|
uint32_t getPixelColor(uint16_t n) const;
|
||||||
|
/*!
|
||||||
|
@brief An 8-bit integer sine wave function, not directly compatible
|
||||||
|
with standard trigonometric units like radians or degrees.
|
||||||
|
@param x Input angle, 0-255; 256 would loop back to zero, completing
|
||||||
|
the circle (equivalent to 360 degrees or 2 pi radians).
|
||||||
|
One can therefore use an unsigned 8-bit variable and simply
|
||||||
|
add or subtract, allowing it to overflow/underflow and it
|
||||||
|
still does the expected contiguous thing.
|
||||||
|
@return Sine result, 0 to 255, or -128 to +127 if type-converted to
|
||||||
|
a signed int8_t, but you'll most likely want unsigned as this
|
||||||
|
output is often used for pixel brightness in animation effects.
|
||||||
|
*/
|
||||||
|
static uint8_t sine8(uint8_t x) {
|
||||||
|
return _NeoPixelSineTable[x]; // 0-255 in, 0-255 out
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
@brief An 8-bit gamma-correction function for basic pixel brightness
|
||||||
|
adjustment. Makes color transitions appear more perceptially
|
||||||
|
correct.
|
||||||
|
@param x Input brightness, 0 (minimum or off/black) to 255 (maximum).
|
||||||
|
@return Gamma-adjusted brightness, can then be passed to one of the
|
||||||
|
setPixelColor() functions. This uses a fixed gamma correction
|
||||||
|
exponent of 2.6, which seems reasonably okay for average
|
||||||
|
NeoPixels in average tasks. If you need finer control you'll
|
||||||
|
need to provide your own gamma-correction function instead.
|
||||||
|
*/
|
||||||
|
static uint8_t gamma8(uint8_t x) {
|
||||||
|
return _NeoPixelGammaTable[x]; // 0-255 in, 0-255 out
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
@brief Convert separate red, green and blue values into a single
|
||||||
|
"packed" 32-bit RGB color.
|
||||||
|
@param r Red brightness, 0 to 255.
|
||||||
|
@param g Green brightness, 0 to 255.
|
||||||
|
@param b Blue brightness, 0 to 255.
|
||||||
|
@return 32-bit packed RGB value, which can then be assigned to a
|
||||||
|
variable for later use or passed to the setPixelColor()
|
||||||
|
function. Packed RGB format is predictable, regardless of
|
||||||
|
LED strand color order.
|
||||||
|
*/
|
||||||
|
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
@brief Convert separate red, green, blue and white values into a
|
||||||
|
single "packed" 32-bit WRGB color.
|
||||||
|
@param r Red brightness, 0 to 255.
|
||||||
|
@param g Green brightness, 0 to 255.
|
||||||
|
@param b Blue brightness, 0 to 255.
|
||||||
|
@param w White brightness, 0 to 255.
|
||||||
|
@return 32-bit packed WRGB value, which can then be assigned to a
|
||||||
|
variable for later use or passed to the setPixelColor()
|
||||||
|
function. Packed WRGB format is predictable, regardless of
|
||||||
|
LED strand color order.
|
||||||
|
*/
|
||||||
|
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||||
|
return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
||||||
|
}
|
||||||
|
static uint32_t ColorHSV(uint16_t hue, uint8_t sat=255, uint8_t val=255);
|
||||||
|
/*!
|
||||||
|
@brief A gamma-correction function for 32-bit packed RGB or WRGB
|
||||||
|
colors. Makes color transitions appear more perceptially
|
||||||
|
correct.
|
||||||
|
@param x 32-bit packed RGB or WRGB color.
|
||||||
|
@return Gamma-adjusted packed color, can then be passed in one of the
|
||||||
|
setPixelColor() functions. Like gamma8(), this uses a fixed
|
||||||
|
gamma correction exponent of 2.6, which seems reasonably okay
|
||||||
|
for average NeoPixels in average tasks. If you need finer
|
||||||
|
control you'll need to provide your own gamma-correction
|
||||||
|
function instead.
|
||||||
|
*/
|
||||||
|
static uint32_t gamma32(uint32_t x);
|
||||||
|
|
||||||
|
|
||||||
|
void rp2040Init(uint8_t pin) ;
|
||||||
|
void rp2040Show(uint8_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz);
|
||||||
|
void rp2040changepin(uint8_t set_pin);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
bool is800KHz; ///< true if 800 KHz pixels
|
||||||
|
bool begun; ///< true if the state machines & pio has started (after the first show).
|
||||||
|
uint16_t numLEDs; ///< Number of RGB LEDs in strip
|
||||||
|
uint16_t numBytes; ///< Size of 'pixels' buffer below
|
||||||
|
int16_t pin; ///< Output pin number (-1 if not yet set)
|
||||||
|
uint8_t brightness; ///< Strip brightness 0-255 (stored as +1)
|
||||||
|
uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each)
|
||||||
|
uint8_t *opixels; ///< Hold originally set LED color values ( 3 or 4 bytes each)
|
||||||
|
uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel
|
||||||
|
uint8_t gOffset; ///< Index of green byte
|
||||||
|
uint8_t bOffset; ///< Index of blue byte
|
||||||
|
uint8_t wOffset; ///< Index of white (==rOffset if no white)
|
||||||
|
absolute_time_t endTime; ///< Latch timing reference
|
||||||
|
PIO pio; ///< chosen pio for this object
|
||||||
|
uint sm; ///<chosen state machine for this object; -1 if not yet set or none available.
|
||||||
|
pBrightnessFunc brightfr,
|
||||||
|
brightfg,
|
||||||
|
brightfb,
|
||||||
|
brightfw; /// pointer to user installed brightness function
|
||||||
|
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||||
|
|
||||||
|
# This can be dropped into an external project to help locate this SDK
|
||||||
|
# It should be include()ed prior to project()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||||
|
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||||
|
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||||
|
|
||||||
|
if (NOT PICO_SDK_PATH)
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT)
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||||
|
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
if (NOT pico_sdk)
|
||||||
|
message("Downloading Raspberry Pi Pico SDK")
|
||||||
|
FetchContent_Populate(pico_sdk)
|
||||||
|
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||||
|
endif ()
|
||||||
|
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||||
|
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||||
|
|
||||||
|
include(${PICO_SDK_INIT_CMAKE_FILE})
|
|
@ -0,0 +1,46 @@
|
||||||
|
;
|
||||||
|
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
;
|
||||||
|
; SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
;
|
||||||
|
|
||||||
|
.program ws2812byte
|
||||||
|
.side_set 1
|
||||||
|
|
||||||
|
.define public T1 2
|
||||||
|
.define public T2 5
|
||||||
|
.define public T3 3
|
||||||
|
|
||||||
|
|
||||||
|
.wrap_target
|
||||||
|
bitloop:
|
||||||
|
out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
|
||||||
|
jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
|
||||||
|
do_one:
|
||||||
|
jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
|
||||||
|
do_zero:
|
||||||
|
nop side 0 [T2 - 1] ; Or drive low, for a short pulse
|
||||||
|
.wrap
|
||||||
|
|
||||||
|
% c-sdk {
|
||||||
|
#include "hardware/clocks.h"
|
||||||
|
|
||||||
|
static inline void ws2812byte_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, uint bits) {
|
||||||
|
|
||||||
|
pio_gpio_init(pio, pin);
|
||||||
|
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
|
||||||
|
|
||||||
|
pio_sm_config c = ws2812byte_program_get_default_config(offset);
|
||||||
|
sm_config_set_sideset_pins(&c, pin);
|
||||||
|
sm_config_set_out_shift(&c, false, true, bits);
|
||||||
|
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||||
|
|
||||||
|
int cycles_per_bit = ws2812byte_T1 + ws2812byte_T2 + ws2812byte_T3;
|
||||||
|
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
|
||||||
|
sm_config_set_clkdiv(&c, div);
|
||||||
|
|
||||||
|
pio_sm_init(pio, sm, offset, &c);
|
||||||
|
pio_sm_set_enabled(pio, sm, true);
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||||
|
|
||||||
|
# This can be dropped into an external project to help locate this SDK
|
||||||
|
# It should be include()ed prior to project()
|
||||||
|
|
||||||
|
# todo document
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||||
|
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||||
|
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the PICO SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO SDK from git if not otherwise locatable")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||||
|
|
||||||
|
if (NOT PICO_SDK_PATH)
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT)
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||||
|
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
if (NOT pico_sdk)
|
||||||
|
message("Downloading PICO SDK")
|
||||||
|
FetchContent_Populate(pico_sdk)
|
||||||
|
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||||
|
endif ()
|
||||||
|
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"PICO SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||||
|
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the PICO SDK")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the PICO SDK" FORCE)
|
||||||
|
|
||||||
|
include(${PICO_SDK_INIT_CMAKE_FILE})
|
|
@ -0,0 +1,217 @@
|
||||||
|
#include "ei_run_classifier.h"
|
||||||
|
#include "Adafruit_NeoPixel.hpp"
|
||||||
|
|
||||||
|
#include <hardware/gpio.h>
|
||||||
|
#include <hardware/uart.h>
|
||||||
|
#include <hardware/adc.h>
|
||||||
|
#include <hardware/dma.h>
|
||||||
|
#include <pico/stdio_usb.h>
|
||||||
|
#include <pico/stdlib.h>
|
||||||
|
#include <pico/multicore.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// ############ ADC and Model Stuff ############
|
||||||
|
|
||||||
|
#define NSAMP 5000
|
||||||
|
// set this to determine sample rate
|
||||||
|
// 0 = 500,000 Hz
|
||||||
|
// 960 = 50,000 Hz
|
||||||
|
// 9600 = 5,000 Hz
|
||||||
|
#define CLOCK_DIV 9600
|
||||||
|
#define CAPTURE_CHANNEL 0
|
||||||
|
#define LED_PIN 25
|
||||||
|
|
||||||
|
float features[NSAMP];
|
||||||
|
uint16_t capture_buf[NSAMP];
|
||||||
|
|
||||||
|
// ############ Lights Stuff ############
|
||||||
|
#define PIN 7
|
||||||
|
|
||||||
|
// ############ Functions ############
|
||||||
|
int raw_feature_get_data(size_t offset, size_t length, float *out_ptr) {
|
||||||
|
memcpy(out_ptr, features + offset, length * sizeof(float));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void core1_entry() {
|
||||||
|
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN,
|
||||||
|
NEO_GRB + NEO_KHZ800);
|
||||||
|
strip.begin();
|
||||||
|
strip.setBrightness(64);
|
||||||
|
strip.show();
|
||||||
|
|
||||||
|
// tell the other core we're ready for data
|
||||||
|
multicore_fifo_push_blocking(0);
|
||||||
|
|
||||||
|
uint32_t state = 0;
|
||||||
|
bool sent_req = false;
|
||||||
|
uint32_t loop_idx = 0;
|
||||||
|
while (1) {
|
||||||
|
// check for new state information
|
||||||
|
if (multicore_fifo_rvalid()) {
|
||||||
|
uint32_t val = multicore_fifo_pop_blocking();
|
||||||
|
|
||||||
|
// 0 means state unchanged
|
||||||
|
if (val != 0) {
|
||||||
|
state = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
sent_req = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (!sent_req) {
|
||||||
|
// tell the other core we're ready for data
|
||||||
|
multicore_fifo_push_blocking(0);
|
||||||
|
// make sure we don't fill up the other queue
|
||||||
|
sent_req = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn on
|
||||||
|
if (state == 1) {
|
||||||
|
uint16_t i, j;
|
||||||
|
|
||||||
|
for(j=0; j<256; j++) { // 5 cycles of all colors on wheel
|
||||||
|
for(i=0; i< strip.numPixels(); i++) {
|
||||||
|
uint8_t WheelPos = ((i * 256 / strip.numPixels()) + j) & 255;
|
||||||
|
uint32_t c = 0;
|
||||||
|
|
||||||
|
WheelPos = 255 - WheelPos;
|
||||||
|
if(WheelPos < 85) {
|
||||||
|
c = strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
|
||||||
|
}
|
||||||
|
if(WheelPos < 170) {
|
||||||
|
WheelPos -= 85;
|
||||||
|
c = strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
|
||||||
|
}
|
||||||
|
WheelPos -= 170;
|
||||||
|
c = strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
|
||||||
|
|
||||||
|
strip.setPixelColor(i, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
strip.show();
|
||||||
|
sleep_ms(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn off
|
||||||
|
else if (state == 2) {
|
||||||
|
strip.clear();
|
||||||
|
strip.show();
|
||||||
|
sleep_ms(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
stdio_usb_init();
|
||||||
|
stdio_init_all();
|
||||||
|
|
||||||
|
multicore_launch_core1(core1_entry);
|
||||||
|
|
||||||
|
gpio_init(LED_PIN);
|
||||||
|
gpio_set_dir(LED_PIN, GPIO_OUT);
|
||||||
|
|
||||||
|
ei_impulse_result_t result = {nullptr};
|
||||||
|
|
||||||
|
signal_t features_signal;
|
||||||
|
features_signal.total_length = NSAMP;
|
||||||
|
features_signal.get_data = &raw_feature_get_data;
|
||||||
|
|
||||||
|
if (NSAMP != EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
|
||||||
|
while (1) {
|
||||||
|
printf("Input frame size incorrect!\n");
|
||||||
|
sleep_ms(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adc_gpio_init(26 + CAPTURE_CHANNEL);
|
||||||
|
|
||||||
|
adc_init();
|
||||||
|
adc_select_input(CAPTURE_CHANNEL);
|
||||||
|
adc_fifo_setup(
|
||||||
|
true, // Write conversions to the sample FIFO
|
||||||
|
true, // Enable DMA data request (DREQ)
|
||||||
|
1, // DREQ (and IRQ) true when >= 1 sample there
|
||||||
|
false, // Disable err bit
|
||||||
|
false // No 8-bit shift
|
||||||
|
);
|
||||||
|
|
||||||
|
// set sample rate
|
||||||
|
adc_set_clkdiv(CLOCK_DIV);
|
||||||
|
|
||||||
|
sleep_ms(1000);
|
||||||
|
// Set up the DMA to start xfer data as soon as it appears in FIFO
|
||||||
|
uint dma_chan = dma_claim_unused_channel(true);
|
||||||
|
dma_channel_config cfg = dma_channel_get_default_config(dma_chan);
|
||||||
|
|
||||||
|
// Reading from constant address, writing to incrementing byte address
|
||||||
|
channel_config_set_transfer_data_size(&cfg, DMA_SIZE_16);
|
||||||
|
channel_config_set_read_increment(&cfg, false);
|
||||||
|
channel_config_set_write_increment(&cfg, true);
|
||||||
|
|
||||||
|
// Pace transfers based on availability of ADC samples
|
||||||
|
channel_config_set_dreq(&cfg, DREQ_ADC);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
adc_fifo_drain();
|
||||||
|
adc_run(false);
|
||||||
|
|
||||||
|
dma_channel_configure(dma_chan, &cfg,
|
||||||
|
capture_buf, // dst
|
||||||
|
&adc_hw->fifo, // src
|
||||||
|
NSAMP, // transfer count
|
||||||
|
true // start immediately
|
||||||
|
);
|
||||||
|
|
||||||
|
gpio_put(LED_PIN, 1);
|
||||||
|
adc_run(true);
|
||||||
|
|
||||||
|
// invoke the impulse
|
||||||
|
EI_IMPULSE_ERROR res = run_classifier(&features_signal, &result,
|
||||||
|
false);
|
||||||
|
|
||||||
|
if (res != 0) {
|
||||||
|
printf("ERROR: Edge Impulse Model Returned %d", res);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EI_CLASSIFIER_HAS_ANOMALY == 1) printf("Anomaly!\n");
|
||||||
|
|
||||||
|
const float thresh = 0.9;
|
||||||
|
|
||||||
|
uint32_t state = 0;
|
||||||
|
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
|
||||||
|
if (ix == 0 && result.classification[ix].value > thresh) {
|
||||||
|
printf("GO\n");
|
||||||
|
state = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ix == 2 && result.classification[ix].value > thresh) {
|
||||||
|
printf("STOP\n");
|
||||||
|
state = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multicore_fifo_rvalid()) {
|
||||||
|
multicore_fifo_pop_blocking();
|
||||||
|
multicore_fifo_push_blocking(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_put(LED_PIN, 0);
|
||||||
|
dma_channel_wait_for_finish_blocking(dma_chan);
|
||||||
|
|
||||||
|
// copy everything to feature buffer to run model
|
||||||
|
// this is probably slow, idk
|
||||||
|
uint64_t sum = 0;
|
||||||
|
for (uint32_t i=0; i<NSAMP; i++) {
|
||||||
|
sum += capture_buf[i];
|
||||||
|
}
|
||||||
|
float dc_offset = (float)sum/NSAMP;
|
||||||
|
|
||||||
|
for (uint32_t i=0; i<NSAMP; i++) {
|
||||||
|
features[i] = (float)capture_buf[i]-dc_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 5a825884753207656080b091d87490348452670c
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
build/
|
||||||
|
*.DS_Store
|
||||||
|
edge-impulse-sdk/
|
||||||
|
model-parameters/
|
||||||
|
tflite-model/
|
|
@ -0,0 +1,68 @@
|
||||||
|
cmake_minimum_required(VERSION 3.13.1)
|
||||||
|
|
||||||
|
set(MODEL_FOLDER .)
|
||||||
|
set(EI_SDK_FOLDER edge-impulse-sdk)
|
||||||
|
|
||||||
|
include(pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
project(pico-voice C CXX ASM)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
add_executable(pico-voice
|
||||||
|
source/main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
include(${MODEL_FOLDER}/edge-impulse-sdk/cmake/utils.cmake)
|
||||||
|
|
||||||
|
target_link_libraries(pico-voice
|
||||||
|
hardware_adc
|
||||||
|
hardware_dma
|
||||||
|
pico_stdlib)
|
||||||
|
|
||||||
|
# enable usb output, disable uart output
|
||||||
|
pico_enable_stdio_usb(pico-voice 1)
|
||||||
|
pico_enable_stdio_uart(pico-voice 0)
|
||||||
|
|
||||||
|
target_include_directories(pico-voice PRIVATE
|
||||||
|
${MODEL_FOLDER}
|
||||||
|
${MODEL_FOLDER}/classifer
|
||||||
|
${MODEL_FOLDER}/tflite-model
|
||||||
|
${MODEL_FOLDER}/model-parameters
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(pico-voice PRIVATE
|
||||||
|
${EI_SDK_FOLDER}
|
||||||
|
${EI_SDK_FOLDER}/third_party/ruy
|
||||||
|
${EI_SDK_FOLDER}/third_party/gemmlowp
|
||||||
|
${EI_SDK_FOLDER}/third_party/flatbuffers/include
|
||||||
|
${EI_SDK_FOLDER}/third_party
|
||||||
|
${EI_SDK_FOLDER}/tensorflow
|
||||||
|
${EI_SDK_FOLDER}/dsp
|
||||||
|
${EI_SDK_FOLDER}/classifier
|
||||||
|
${EI_SDK_FOLDER}/anomaly
|
||||||
|
${EI_SDK_FOLDER}/CMSIS/NN/Include
|
||||||
|
${EI_SDK_FOLDER}/CMSIS/DSP/PrivateInclude
|
||||||
|
${EI_SDK_FOLDER}/CMSIS/DSP/Include
|
||||||
|
${EI_SDK_FOLDER}/CMSIS/Core/Include
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(${INCLUDES})
|
||||||
|
|
||||||
|
# find model source files
|
||||||
|
RECURSIVE_FIND_FILE(MODEL_FILES "${MODEL_FOLDER}/tflite-model" "*.cpp")
|
||||||
|
RECURSIVE_FIND_FILE(SOURCE_FILES "${EI_SDK_FOLDER}" "*.cpp")
|
||||||
|
RECURSIVE_FIND_FILE(CC_FILES "${EI_SDK_FOLDER}" "*.cc")
|
||||||
|
RECURSIVE_FIND_FILE(S_FILES "${EI_SDK_FOLDER}" "*.s")
|
||||||
|
RECURSIVE_FIND_FILE(C_FILES "${EI_SDK_FOLDER}" "*.c")
|
||||||
|
list(APPEND SOURCE_FILES ${S_FILES})
|
||||||
|
list(APPEND SOURCE_FILES ${C_FILES})
|
||||||
|
list(APPEND SOURCE_FILES ${CC_FILES})
|
||||||
|
list(APPEND SOURCE_FILES ${MODEL_FILES})
|
||||||
|
|
||||||
|
# add all sources to the project
|
||||||
|
target_sources(pico-voice PRIVATE ${SOURCE_FILES})
|
||||||
|
|
||||||
|
pico_add_extra_outputs(pico-voice)
|
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
|
@ -0,0 +1,12 @@
|
||||||
|
# pico-voice-v1
|
||||||
|
|
||||||
|
This program is a starting point for building voice models with Pico
|
||||||
|
using Edge Impulse. Much of this code is from Edge Impulse's pico [standalone repo](https://github.com/edgeimpulse/example-standalone-inferencing-pico).
|
||||||
|
|
||||||
|
To use this starting point, you need to drop in the `edge-impulse-sdk`, `model-parameters`, and `tflite-model` folders generated when exporting your model for C++.
|
||||||
|
|
||||||
|
## Modifications
|
||||||
|
|
||||||
|
If you train your model on floating-point WAV files sampled at 5 kHz (see the pico-daq folder in this repository) then you shouldn't need to change much other than the results of the inferencing.
|
||||||
|
|
||||||
|
If you trained your data on some other format, you will need to modify how data gets copied into the `features` buffer as well as things like the sample rate.
|
|
@ -0,0 +1,64 @@
|
||||||
|
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||||
|
|
||||||
|
# This can be dropped into an external project to help locate this SDK
|
||||||
|
# It should be include()ed prior to project()
|
||||||
|
|
||||||
|
# todo document
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||||
|
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||||
|
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||||
|
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the PICO SDK")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO SDK from git if not otherwise locatable")
|
||||||
|
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||||
|
|
||||||
|
if (NOT PICO_SDK_PATH)
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT)
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||||
|
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||||
|
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif ()
|
||||||
|
FetchContent_Declare(
|
||||||
|
pico_sdk
|
||||||
|
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||||
|
GIT_TAG master
|
||||||
|
)
|
||||||
|
if (NOT pico_sdk)
|
||||||
|
message("Downloading PICO SDK")
|
||||||
|
FetchContent_Populate(pico_sdk)
|
||||||
|
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||||
|
endif ()
|
||||||
|
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"PICO SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||||
|
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||||
|
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||||
|
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the PICO SDK")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the PICO SDK" FORCE)
|
||||||
|
|
||||||
|
include(${PICO_SDK_INIT_CMAKE_FILE})
|
|
@ -0,0 +1,143 @@
|
||||||
|
#include "ei_run_classifier.h"
|
||||||
|
|
||||||
|
#include <hardware/gpio.h>
|
||||||
|
#include <hardware/uart.h>
|
||||||
|
#include <hardware/adc.h>
|
||||||
|
#include <hardware/dma.h>
|
||||||
|
#include <pico/stdio_usb.h>
|
||||||
|
#include <pico/stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define NSAMP 5000
|
||||||
|
// set this to determine sample rate
|
||||||
|
// 0 = 500,000 Hz
|
||||||
|
// 960 = 50,000 Hz
|
||||||
|
// 9600 = 5,000 Hz
|
||||||
|
#define CLOCK_DIV 9600
|
||||||
|
|
||||||
|
#define CAPTURE_CHANNEL 0
|
||||||
|
#define LED_PIN 25
|
||||||
|
|
||||||
|
float features[NSAMP];
|
||||||
|
uint16_t capture_buf[NSAMP];
|
||||||
|
|
||||||
|
int raw_feature_get_data(size_t offset, size_t length, float *out_ptr)
|
||||||
|
{
|
||||||
|
memcpy(out_ptr, features + offset, length * sizeof(float));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
stdio_usb_init();
|
||||||
|
stdio_init_all();
|
||||||
|
|
||||||
|
gpio_init(LED_PIN);
|
||||||
|
gpio_set_dir(LED_PIN, GPIO_OUT);
|
||||||
|
|
||||||
|
ei_impulse_result_t result = {nullptr};
|
||||||
|
|
||||||
|
signal_t features_signal;
|
||||||
|
features_signal.total_length = NSAMP;
|
||||||
|
features_signal.get_data = &raw_feature_get_data;
|
||||||
|
|
||||||
|
if (NSAMP != EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
|
||||||
|
while (1) {
|
||||||
|
printf("Input frame size incorrect!\n");
|
||||||
|
sleep_ms(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adc_gpio_init(26 + CAPTURE_CHANNEL);
|
||||||
|
|
||||||
|
adc_init();
|
||||||
|
adc_select_input(CAPTURE_CHANNEL);
|
||||||
|
adc_fifo_setup(
|
||||||
|
true, // Write conversions to the sample FIFO
|
||||||
|
true, // Enable DMA data request (DREQ)
|
||||||
|
1, // DREQ (and IRQ) true when >= 1 sample there
|
||||||
|
false, // Disable err bit
|
||||||
|
false // No 8-bit shift
|
||||||
|
);
|
||||||
|
|
||||||
|
// set sample rate
|
||||||
|
adc_set_clkdiv(CLOCK_DIV);
|
||||||
|
|
||||||
|
sleep_ms(1000);
|
||||||
|
// Set up the DMA to start xfer data as soon as it appears in FIFO
|
||||||
|
uint dma_chan = dma_claim_unused_channel(true);
|
||||||
|
dma_channel_config cfg = dma_channel_get_default_config(dma_chan);
|
||||||
|
|
||||||
|
// Reading from constant address, writing to incrementing byte address
|
||||||
|
channel_config_set_transfer_data_size(&cfg, DMA_SIZE_16);
|
||||||
|
channel_config_set_read_increment(&cfg, false);
|
||||||
|
channel_config_set_write_increment(&cfg, true);
|
||||||
|
|
||||||
|
// Pace transfers based on availability of ADC samples
|
||||||
|
channel_config_set_dreq(&cfg, DREQ_ADC);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
adc_fifo_drain();
|
||||||
|
adc_run(false);
|
||||||
|
|
||||||
|
dma_channel_configure(dma_chan, &cfg,
|
||||||
|
capture_buf, // dst
|
||||||
|
&adc_hw->fifo, // src
|
||||||
|
NSAMP, // transfer count
|
||||||
|
true // start immediately
|
||||||
|
);
|
||||||
|
|
||||||
|
gpio_put(LED_PIN, 1);
|
||||||
|
adc_run(true);
|
||||||
|
|
||||||
|
// invoke the impulse
|
||||||
|
EI_IMPULSE_ERROR res = run_classifier(&features_signal, &result,
|
||||||
|
false);
|
||||||
|
|
||||||
|
if (res != 0) {
|
||||||
|
printf("run_classifier returned: %d\n", res);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// uncomment this for timing information
|
||||||
|
/*
|
||||||
|
printf("DSP: %d ms., Class.: %d ms., Anomaly: %d ms \n",
|
||||||
|
result.timing.dsp, result.timing.classification,
|
||||||
|
result.timing.anomaly);
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (EI_CLASSIFIER_HAS_ANOMALY == 1) printf("Anomaly!\n");
|
||||||
|
|
||||||
|
|
||||||
|
const float thresh = 0.9;
|
||||||
|
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
|
||||||
|
//printf("%.5f", result.classification[ix].value);
|
||||||
|
//if (ix != EI_CLASSIFIER_LABEL_COUNT - 1) printf(", ");
|
||||||
|
|
||||||
|
|
||||||
|
if (ix == 0 && result.classification[ix].value > thresh)
|
||||||
|
printf("GO\n");
|
||||||
|
if (ix == 2 && result.classification[ix].value > thresh)
|
||||||
|
printf("STOP\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("\n");
|
||||||
|
|
||||||
|
gpio_put(LED_PIN, 0);
|
||||||
|
dma_channel_wait_for_finish_blocking(dma_chan);
|
||||||
|
|
||||||
|
// Copy everything to feature buffer to run model. In my training,
|
||||||
|
// I fed the model float values from WAVs so we need to bring the
|
||||||
|
// sample level to zero and convert to floats
|
||||||
|
uint64_t sum = 0;
|
||||||
|
for (uint32_t i=0; i<NSAMP; i++) {
|
||||||
|
sum += capture_buf[i];
|
||||||
|
}
|
||||||
|
float dc_offset = (float)sum/NSAMP;
|
||||||
|
|
||||||
|
for (uint32_t i=0; i<NSAMP; i++) {
|
||||||
|
features[i] = (float)capture_buf[i]-dc_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Ładowanie…
Reference in New Issue