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