initial commit
This commit is contained in:
86
libraries/FastLED/src/third_party/arduinojson/json.h
vendored
Normal file
86
libraries/FastLED/src/third_party/arduinojson/json.h
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
// Arduino JSON may be included by the user, so we need to save the current state
|
||||
// of the macros and restore them after including the library
|
||||
#pragma push_macro("ARDUINO")
|
||||
#pragma push_macro("ARDUINOJSON_ENABLE_STD_STREAM")
|
||||
#pragma push_macro("ARDUINOJSON_ENABLE_STRING_VIEW")
|
||||
#pragma push_macro("ARDUINOJSON_ENABLE_STD_STRING")
|
||||
#pragma push_macro("ARDUINOJSON_ENABLE_ARDUINO_STRING")
|
||||
#pragma push_macro("ARDUINOJSON_ENABLE_ARDUINO_STREAM")
|
||||
#pragma push_macro("ARDUINOJSON_ENABLE_ARDUINO_PRINT")
|
||||
#pragma push_macro("ARDUINOJSON_ENABLE_PROGMEM")
|
||||
#pragma push_macro("min")
|
||||
#pragma push_macro("max")
|
||||
|
||||
#define ARDUINOJSON_USE_LONG_LONG 1
|
||||
|
||||
// Safely undefine FLArduinoJson macros if defined
|
||||
#ifdef ARDUINOJSON_ENABLE_STD_STREAM
|
||||
#undef ARDUINOJSON_ENABLE_STD_STREAM
|
||||
#endif
|
||||
#define ARDUINOJSON_ENABLE_STD_STREAM 0
|
||||
|
||||
#ifdef ARDUINOJSON_ENABLE_STRING_VIEW
|
||||
#undef ARDUINOJSON_ENABLE_STRING_VIEW
|
||||
#endif
|
||||
#define ARDUINOJSON_ENABLE_STRING_VIEW 0
|
||||
|
||||
#ifdef ARDUINOJSON_ENABLE_STD_STRING
|
||||
#undef ARDUINOJSON_ENABLE_STD_STRING
|
||||
#endif
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define ARDUINOJSON_ENABLE_STD_STRING 1
|
||||
#else
|
||||
#define ARDUINOJSON_ENABLE_STD_STRING 0
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINOJSON_ENABLE_ARDUINO_STRING
|
||||
#undef ARDUINOJSON_ENABLE_ARDUINO_STRING
|
||||
#endif
|
||||
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 0
|
||||
|
||||
#ifdef ARDUINOJSON_ENABLE_ARDUINO_STREAM
|
||||
#undef ARDUINOJSON_ENABLE_ARDUINO_STREAM
|
||||
#endif
|
||||
#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0
|
||||
|
||||
#ifdef ARDUINOJSON_ENABLE_ARDUINO_PRINT
|
||||
#undef ARDUINOJSON_ENABLE_ARDUINO_PRINT
|
||||
#endif
|
||||
#define ARDUINOJSON_ENABLE_ARDUINO_PRINT 0
|
||||
|
||||
#ifdef ARDUINOJSON_ENABLE_PROGMEM
|
||||
#undef ARDUINOJSON_ENABLE_PROGMEM
|
||||
#endif
|
||||
#define ARDUINOJSON_ENABLE_PROGMEM 0
|
||||
|
||||
#ifdef ARDUINO
|
||||
#undef ARDUINO
|
||||
#endif
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
|
||||
#define FASTLED_JSON_GUARD
|
||||
#include "json.hpp"
|
||||
#undef FASTLED_JSON_GUARD
|
||||
|
||||
|
||||
#pragma pop_macro("max")
|
||||
#pragma pop_macro("min")
|
||||
#pragma pop_macro("ARDUINOJSON_ENABLE_PROGMEM")
|
||||
#pragma pop_macro("ARDUINOJSON_ENABLE_ARDUINO_PRINT")
|
||||
#pragma pop_macro("ARDUINOJSON_ENABLE_ARDUINO_STREAM")
|
||||
#pragma pop_macro("ARDUINOJSON_ENABLE_ARDUINO_STRING")
|
||||
#pragma pop_macro("ARDUINOJSON_ENABLE_STD_STRING")
|
||||
#pragma pop_macro("ARDUINOJSON_ENABLE_STRING_VIEW")
|
||||
#pragma pop_macro("ARDUINOJSON_ENABLE_STD_STREAM")
|
||||
#pragma pop_macro("ARDUINO")
|
||||
8222
libraries/FastLED/src/third_party/arduinojson/json.hpp
vendored
Normal file
8222
libraries/FastLED/src/third_party/arduinojson/json.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
201
libraries/FastLED/src/third_party/cq_kernel/LICENSE
vendored
Normal file
201
libraries/FastLED/src/third_party/cq_kernel/LICENSE
vendored
Normal file
@@ -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.
|
||||
4
libraries/FastLED/src/third_party/cq_kernel/Makefile
vendored
Normal file
4
libraries/FastLED/src/third_party/cq_kernel/Makefile
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
CFLAGS :=
|
||||
|
||||
example: example.c cq_kernel.c kiss_fft.c kiss_fftr.c
|
||||
gcc $(CFLAGS) example.c cq_kernel.c kiss_fft.c kiss_fftr.c -lm -o bin/example
|
||||
80
libraries/FastLED/src/third_party/cq_kernel/README.md
vendored
Normal file
80
libraries/FastLED/src/third_party/cq_kernel/README.md
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
# cq_kernel
|
||||
|
||||
A fast C library that generates and applies spectral kernels on the output of an FFT, resulting in a constant Q transform overall and as specified in "An efficient algorithm for the calculation of a constant Q transform" by Brown (google it for reference). It is dependent on [kissfft](https://github.com/mborgerding/kissfft), so the v131 release is bundled here, but it can also be applied on the output of any FFT program.
|
||||
|
||||
While initalizing, this library is relatively slow and memory-heavy: it needs to execute an FFT for one band at a time, requiring two buffers for its real input and complex output. However, the library uses a little less (and not much more) at runtime by storing all the kernels as sparse arrays (throwing out the smallest values). Sparse arrays also make the library very fast at runtime, so embedded use is possible.
|
||||
|
||||
An example has been provided. Using `#define`'d parameters, it generates and dumps its kernels in a format that can be pasted into Excel or MATLAB. To get the floating-point version, in the command line run:
|
||||
|
||||
```
|
||||
make example
|
||||
./bin/example
|
||||
```
|
||||
|
||||
To get the Q15 version, in the command line run:
|
||||
|
||||
```
|
||||
make example CFLAGS="-D FIXED_POINT=16"
|
||||
./bin/example
|
||||
```
|
||||
|
||||
There is a Q31 version that is currently broken. To try that, in the command line run:
|
||||
```
|
||||
make example CFLAGS="-D FIXED_POINT=32"
|
||||
./bin/example
|
||||
```
|
||||
|
||||
Here is what that Excel plot of the kernels ("K_mag.txt") looks like. It is very similar to Figure 2 in the CQT paper:
|
||||
|
||||

|
||||
|
||||
**Be warned**, making the maximum frequency too close to half the sampling frequency causes the kernels to degenerate. Not having enough samples also causes the kernels to degenerate, so *please edit, compile, then run the example to make sure your desired parameters are valid!*
|
||||
|
||||
Now, to use this library in a project, retrieve the files
|
||||
|
||||
```
|
||||
_kiss_fft_guts.h
|
||||
cq_kernel.c
|
||||
cq_kernel.h
|
||||
kiss_fft.c
|
||||
kiss_fft.h
|
||||
kiss_fftr.c
|
||||
kiss_fftr.h
|
||||
```
|
||||
|
||||
and write something like this (if you're using the included kissfft library)
|
||||
|
||||
```C
|
||||
#include "kiss_fftr.h"
|
||||
#include "cq_kernel.h"
|
||||
|
||||
int main(){
|
||||
struct cq_kernel_cfg cq_cfg = {
|
||||
.samples = SAMPLES,
|
||||
.bands = BANDS,
|
||||
.fmin = MIN_FREQUENCY,
|
||||
.fmax = MAX_FREQUENCY,
|
||||
.fs = SAMPLING_FREQUENCY,
|
||||
.min_val = MIN_VAL
|
||||
};
|
||||
|
||||
kiss_fft_cfg fft_cfg = kiss_fftr_alloc(SAMPLES, 0, NULL, NULL);
|
||||
cq_kernels_t kernels = generate_kernels(cq_cfg);
|
||||
kernels = reallocate_kernels(kernels, cq_cfg); // optional
|
||||
|
||||
while(1){
|
||||
// <Generate/retreive "in" signal here>
|
||||
kiss_fft_cpx fft[SAMPLES] = {0};
|
||||
kiss_fftr(fft_cfg, in, fft);
|
||||
kiss_fft_cpx cq[BANDS] = {0};
|
||||
apply_kernels(fft, cq, kernels, cq_cfg);
|
||||
// <Process "cq" output here>
|
||||
}
|
||||
|
||||
free(fft_cfg);
|
||||
free_kernels(kernels, cq_cfg)
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
and compile it with the appropriate flag based on the desired format, as required by kissfft. There is no flag for floating point. For Q15, that is `-D FIXED_POINT=16`. For Q31, that is `-D FIXED_POINT=32`.
|
||||
160
libraries/FastLED/src/third_party/cq_kernel/_kiss_fft_guts.h
vendored
Normal file
160
libraries/FastLED/src/third_party/cq_kernel/_kiss_fft_guts.h
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* kiss_fft.h
|
||||
defines kiss_fft_scalar as either short or a float type
|
||||
and defines
|
||||
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
|
||||
#include "kiss_fft.h"
|
||||
#include <limits.h>
|
||||
|
||||
#define MAXFACTORS 32
|
||||
/* e.g. an fft of length 128 has 4 factors
|
||||
as far as kissfft is concerned
|
||||
4*4*4*2
|
||||
*/
|
||||
|
||||
struct kiss_fft_state{
|
||||
int nfft;
|
||||
int inverse;
|
||||
int factors[2*MAXFACTORS];
|
||||
kiss_fft_cpx twiddles[1];
|
||||
};
|
||||
|
||||
/*
|
||||
Explanation of macros dealing with complex math:
|
||||
|
||||
C_MUL(m,a,b) : m = a*b
|
||||
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
|
||||
C_SUB( res, a,b) : res = a - b
|
||||
C_SUBFROM( res , a) : res -= a
|
||||
C_ADDTO( res , a) : res += a
|
||||
* */
|
||||
#ifdef FIXED_POINT
|
||||
#if (FIXED_POINT==32)
|
||||
# define FRACBITS 31
|
||||
# define SAMPPROD int64_t
|
||||
#define SAMP_MAX 2147483647
|
||||
#else
|
||||
# define FRACBITS 15
|
||||
# define SAMPPROD int32_t
|
||||
#define SAMP_MAX 32767
|
||||
#endif
|
||||
|
||||
#define SAMP_MIN -SAMP_MAX
|
||||
|
||||
#if defined(CHECK_OVERFLOW)
|
||||
# define CHECK_OVERFLOW_OP(a,op,b) \
|
||||
if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \
|
||||
fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); }
|
||||
#endif
|
||||
|
||||
|
||||
# define smul(a,b) ( (SAMPPROD)(a)*(b) )
|
||||
# define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS )
|
||||
|
||||
# define S_MUL(a,b) sround( smul(a,b) )
|
||||
|
||||
# define C_MUL(m,a,b) \
|
||||
do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \
|
||||
(m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0)
|
||||
|
||||
# define DIVSCALAR(x,k) \
|
||||
(x) = sround( smul( x, SAMP_MAX/k ) )
|
||||
|
||||
# define C_FIXDIV(c,div) \
|
||||
do { DIVSCALAR( (c).r , div); \
|
||||
DIVSCALAR( (c).i , div); }while (0)
|
||||
|
||||
# define C_MULBYSCALAR( c, s ) \
|
||||
do{ (c).r = sround( smul( (c).r , s ) ) ;\
|
||||
(c).i = sround( smul( (c).i , s ) ) ; }while(0)
|
||||
|
||||
#else /* not FIXED_POINT*/
|
||||
|
||||
# define S_MUL(a,b) ( (a)*(b) )
|
||||
#define C_MUL(m,a,b) \
|
||||
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
|
||||
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
|
||||
# define C_FIXDIV(c,div) /* NOOP */
|
||||
# define C_MULBYSCALAR( c, s ) \
|
||||
do{ (c).r *= (s);\
|
||||
(c).i *= (s); }while(0)
|
||||
#endif
|
||||
|
||||
#ifndef CHECK_OVERFLOW_OP
|
||||
# define CHECK_OVERFLOW_OP(a,op,b) /* noop */
|
||||
#endif
|
||||
|
||||
#define C_ADD( res, a,b)\
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((a).r,+,(b).r)\
|
||||
CHECK_OVERFLOW_OP((a).i,+,(b).i)\
|
||||
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
|
||||
}while(0)
|
||||
#define C_SUB( res, a,b)\
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((a).r,-,(b).r)\
|
||||
CHECK_OVERFLOW_OP((a).i,-,(b).i)\
|
||||
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
|
||||
}while(0)
|
||||
#define C_ADDTO( res , a)\
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((res).r,+,(a).r)\
|
||||
CHECK_OVERFLOW_OP((res).i,+,(a).i)\
|
||||
(res).r += (a).r; (res).i += (a).i;\
|
||||
}while(0)
|
||||
|
||||
#define C_SUBFROM( res , a)\
|
||||
do {\
|
||||
CHECK_OVERFLOW_OP((res).r,-,(a).r)\
|
||||
CHECK_OVERFLOW_OP((res).i,-,(a).i)\
|
||||
(res).r -= (a).r; (res).i -= (a).i; \
|
||||
}while(0)
|
||||
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
# define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase))
|
||||
# define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase))
|
||||
# define HALF_OF(x) ((x)>>1)
|
||||
#elif defined(USE_SIMD)
|
||||
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
|
||||
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
|
||||
# define HALF_OF(x) ((x)*_mm_set1_ps(.5))
|
||||
#else
|
||||
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
|
||||
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
|
||||
# define HALF_OF(x) ((x)*.5)
|
||||
#endif
|
||||
|
||||
#define kf_cexp(x,phase) \
|
||||
do{ \
|
||||
(x)->r = KISS_FFT_COS(phase);\
|
||||
(x)->i = KISS_FFT_SIN(phase);\
|
||||
}while(0)
|
||||
|
||||
|
||||
/* a debugging function */
|
||||
#define pcpx(c)\
|
||||
fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) )
|
||||
|
||||
|
||||
#ifdef KISS_FFT_USE_ALLOCA
|
||||
// define this to allow use of alloca instead of malloc for temporary buffers
|
||||
// Temporary buffers are used in two case:
|
||||
// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5
|
||||
// 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform.
|
||||
#include <alloca.h>
|
||||
#define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes)
|
||||
#define KISS_FFT_TMP_FREE(ptr)
|
||||
#else
|
||||
#define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes)
|
||||
#define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr)
|
||||
#endif
|
||||
154
libraries/FastLED/src/third_party/cq_kernel/cq_kernel.cpp
vendored
Normal file
154
libraries/FastLED/src/third_party/cq_kernel/cq_kernel.cpp
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2020 Kenny Peng
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "cq_kernel.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
#endif
|
||||
|
||||
|
||||
void _generate_guassian(kiss_fft_scalar window[], int N);
|
||||
|
||||
void _generate_center_freqs(float freq[], int bands, float fmin, float fmax){
|
||||
float m = log(fmax/fmin);
|
||||
for(int i = 0; i < bands; i++) freq[i] = fmin*exp(m*i/(bands-1));
|
||||
}
|
||||
|
||||
void _generate_hamming(kiss_fft_scalar window[], int N){
|
||||
float a0 = 0.54;
|
||||
for(int i = 0; i < N; i++){
|
||||
#ifdef FIXED_POINT // If fixed_point, represent hamming window with integers
|
||||
window[i] = SAMP_MAX*(a0-(1-a0)*cos(2*M_PI*i/(N-1)));
|
||||
#else // Else if floating point, represent hamming window as-is
|
||||
window[i] = a0-(1-a0)*cos(2*M_PI*i/(N-1));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void _generate_guassian(kiss_fft_scalar window[], int N){
|
||||
float sigma = 0.5; // makes a window accurate to -30dB from peak, but smaller sigma is more accurate
|
||||
for(int i = 0; i < N; i++){
|
||||
#ifdef FIXED_POINT // If fixed_point, represent window with integers
|
||||
window[i] = SAMP_MAX*exp(-0.5*pow((i-N/2.0)/(sigma*N/2.0), 2));
|
||||
#else // Else if floating point, represent window as-is
|
||||
window[i] = exp(-0.5*pow((i-N/2.0)/(sigma*N/2.0), 2));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void _generate_kernel(kiss_fft_cpx kernel[], kiss_fftr_cfg cfg, enum window_type window_type, float f, float fmin, float fs, int N){
|
||||
// Generates window in the center and zero everywhere else
|
||||
float factor = f/fmin;
|
||||
int N_window = N/factor; // Scales inversely with frequency (see CQT paper)
|
||||
kiss_fft_scalar *time_K = (kiss_fft_scalar*)calloc(N, sizeof(kiss_fft_scalar));
|
||||
|
||||
switch(window_type){
|
||||
case HAMMING:
|
||||
_generate_hamming(&time_K[(N-N_window)/2], N_window);
|
||||
break;
|
||||
case GAUSSIAN:
|
||||
_generate_guassian(&time_K[(N-N_window)/2], N_window);
|
||||
break;
|
||||
}
|
||||
|
||||
// Fills window with f Hz wave sampled at fs Hz
|
||||
for(int i = 0; i < N; i++) time_K[i] *= cos(2*M_PI*(f/fs)*(i-N/2));
|
||||
|
||||
#ifdef FIXED_POINT // If using fixed point, just scale inversely to N after FFT (don't normalize)
|
||||
kiss_fftr(cfg, time_K, kernel); // Outputs garbage for Q31
|
||||
for(int i = 0; i < N; i++){
|
||||
kernel[i].r *= factor;
|
||||
kernel[i].i *= factor;
|
||||
}
|
||||
#else // Else if floating point, follow CQT paper more exactly (normalize with N before FFT)
|
||||
for(int i = 0; i < N; i++) time_K[i] /= N_window;
|
||||
kiss_fftr(cfg, time_K, kernel);
|
||||
#endif
|
||||
|
||||
free(time_K);
|
||||
}
|
||||
|
||||
kiss_fft_scalar _mag(kiss_fft_cpx x){
|
||||
return sqrt(x.r*x.r+x.i*x.i);
|
||||
}
|
||||
|
||||
struct sparse_arr* generate_kernels(struct cq_kernel_cfg cfg){
|
||||
float *freq = (float*)malloc(cfg.bands * sizeof(float));
|
||||
_generate_center_freqs(freq, cfg.bands, cfg.fmin, cfg.fmax);
|
||||
|
||||
kiss_fftr_cfg fft_cfg = kiss_fftr_alloc(cfg.samples, 0, NULL, NULL);
|
||||
struct sparse_arr* kernels = (struct sparse_arr*)malloc(cfg.bands*sizeof(struct sparse_arr));
|
||||
kiss_fft_cpx *temp_kernel = (kiss_fft_cpx*)malloc(cfg.samples*sizeof(kiss_fft_cpx));
|
||||
|
||||
for(int i = 0; i < cfg.bands; i++){
|
||||
// Clears temp_kernel before calling _generate_kernel on it
|
||||
for(int i = 0; i < cfg.samples; i++) temp_kernel[i].r = temp_kernel[i].i = 0;
|
||||
|
||||
_generate_kernel(temp_kernel, fft_cfg, cfg.window_type, freq[i], cfg.fmin, cfg.fs, cfg.samples);
|
||||
|
||||
// Counts number of elements with a complex magnitude above cfg.min_val in temp_kernel
|
||||
int n_elems = 0;
|
||||
for(int j = 0; j < cfg.samples; j++) if(_mag(temp_kernel[j]) > cfg.min_val) n_elems++;
|
||||
|
||||
// Generates sparse_arr holding n_elems sparse_arr_elem's
|
||||
kernels[i].n_elems = n_elems;
|
||||
kernels[i].elems = (struct sparse_arr_elem*)malloc(n_elems*sizeof(struct sparse_arr_elem));
|
||||
|
||||
// Generates sparse_arr_elem's from complex values counted before
|
||||
int k = 0;
|
||||
for(int j = 0; j < cfg.samples; j++){
|
||||
if(_mag(temp_kernel[j]) > cfg.min_val){
|
||||
kernels[i].elems[k].val = temp_kernel[j];
|
||||
kernels[i].elems[k].n = j;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(fft_cfg);
|
||||
free(temp_kernel);
|
||||
free(freq);
|
||||
|
||||
return kernels;
|
||||
}
|
||||
|
||||
struct sparse_arr* reallocate_kernels(struct sparse_arr *old_ptr, struct cq_kernel_cfg cfg){
|
||||
struct sparse_arr *new_ptr = (struct sparse_arr*)malloc(cfg.bands*sizeof(struct sparse_arr));
|
||||
for(int i = 0; i < cfg.bands; i++){
|
||||
new_ptr[i].n_elems = old_ptr[i].n_elems;
|
||||
new_ptr[i].elems = (struct sparse_arr_elem*)malloc(old_ptr[i].n_elems*sizeof(struct sparse_arr_elem));
|
||||
memcpy(new_ptr[i].elems, old_ptr[i].elems, old_ptr[i].n_elems*sizeof(struct sparse_arr_elem));
|
||||
free(old_ptr[i].elems);
|
||||
}
|
||||
free(old_ptr);
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
void apply_kernels(kiss_fft_cpx fft[], kiss_fft_cpx cq[], struct sparse_arr kernels[], struct cq_kernel_cfg cfg){
|
||||
for(int i = 0; i < cfg.bands; i++){
|
||||
for(int j = 0; j < kernels[i].n_elems; j++){
|
||||
kiss_fft_cpx weighted_val;
|
||||
C_MUL(weighted_val, fft[kernels[i].elems[j].n], kernels[i].elems[j].val);
|
||||
C_ADDTO(cq[i], weighted_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void free_kernels(struct sparse_arr *kernels, struct cq_kernel_cfg cfg){
|
||||
for(int i = 0; i < cfg.bands; i++) free(kernels[i].elems);
|
||||
free(kernels);
|
||||
}
|
||||
110
libraries/FastLED/src/third_party/cq_kernel/cq_kernel.h
vendored
Normal file
110
libraries/FastLED/src/third_party/cq_kernel/cq_kernel.h
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
// Copyright 2020 Kenny Peng
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef CQ_KERNEL_H
|
||||
#define CQ_KERNEL_H
|
||||
|
||||
#include <math.h>
|
||||
#include "_kiss_fft_guts.h"
|
||||
#include "kiss_fftr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum window_type{
|
||||
HAMMING,
|
||||
GAUSSIAN
|
||||
};
|
||||
|
||||
/*
|
||||
* cq_kernel_cfg - holds parameters used by cq_kernel
|
||||
*
|
||||
* Initialize with desired parameters and pass into cq_kernel functions
|
||||
* */
|
||||
struct cq_kernel_cfg{
|
||||
int samples; // kiss_fftr requires this must be even
|
||||
int bands;
|
||||
float fmin;
|
||||
float fmax;
|
||||
float fs;
|
||||
enum window_type window_type;
|
||||
kiss_fft_scalar min_val; // sparse matrix threshold, see CQT paper
|
||||
};
|
||||
|
||||
/*
|
||||
* sparse_arr_elem - holds one position and complex value pair in sparse array
|
||||
* sparse_arr - holds length and pointer of sparse array
|
||||
* */
|
||||
struct sparse_arr_elem{
|
||||
int n;
|
||||
kiss_fft_cpx val;
|
||||
};
|
||||
struct sparse_arr{
|
||||
int n_elems;
|
||||
struct sparse_arr_elem *elems;
|
||||
};
|
||||
|
||||
typedef struct sparse_arr *cq_kernels_t;
|
||||
|
||||
// private functions
|
||||
void _generate_center_freqs(float freq[], int bands, float fmin, float fmax);
|
||||
void _generate_hamming(kiss_fft_scalar window[], int N);
|
||||
void _generate_gaussian(kiss_fft_scalar window[], int N);
|
||||
void _generate_kernel(kiss_fft_cpx K[], kiss_fftr_cfg cfg, enum window_type window_type, float f, float fmin, float fs, int N);
|
||||
kiss_fft_scalar _mag(kiss_fft_cpx x);
|
||||
|
||||
// public functions
|
||||
/*
|
||||
* generate_kernels - generates constant q kernels from cfg
|
||||
*
|
||||
* typical usage: cq_kernels_t kernels = generate_kernels(cfg);
|
||||
*
|
||||
* Beware massive memory usage, uses more than 3*cfg.samples*sizeof(kiss_fft_scalar) bytes at a time
|
||||
* to calculate all of the kernels. However, it will return sparse arrays that use much less memory.
|
||||
* */
|
||||
struct sparse_arr* generate_kernels(struct cq_kernel_cfg cfg);
|
||||
|
||||
/*
|
||||
* reallocate_kernels - (optional) reallocates the kernels
|
||||
*
|
||||
* typical usage: kernels = reallocate_kernels(kernels, cfg);
|
||||
*
|
||||
* This can be used to eliminate the hole in heap left by generate_kernels
|
||||
* */
|
||||
struct sparse_arr* reallocate_kernels(struct sparse_arr* kernels, struct cq_kernel_cfg cfg);
|
||||
|
||||
/*
|
||||
* apply_kernels - calculates constant q from output of kiss_fft
|
||||
*
|
||||
* typical usage:
|
||||
* kiss_fft_cpx fft[SAMPLES] = {0};
|
||||
* kiss_fftr(fft_cfg, in, fft);
|
||||
* kiss_fft_cpx cq[BANDS] = {0};
|
||||
* apply_kernels(fft, cq, kernels, cfg);
|
||||
* */
|
||||
void apply_kernels(kiss_fft_cpx fft[], kiss_fft_cpx cq[], struct sparse_arr kernels[], struct cq_kernel_cfg cfg);
|
||||
|
||||
/*
|
||||
* free_kernels - frees memory used by the sparse arrays of kernels
|
||||
*
|
||||
* typical_usage: free_kernels(kernels, cfg);
|
||||
* */
|
||||
void free_kernels(struct sparse_arr *kernels, struct cq_kernel_cfg cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
79
libraries/FastLED/src/third_party/cq_kernel/example.c.disabled
vendored
Normal file
79
libraries/FastLED/src/third_party/cq_kernel/example.c.disabled
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
#include <stdio.h>
|
||||
#include "fl/stdint.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "kiss_fftr.h"
|
||||
#include "cq_kernel.h"
|
||||
|
||||
// Edit these parameters
|
||||
#define SAMPLES 512
|
||||
#define BANDS 57
|
||||
#define SAMPLING_FREQUENCY 11025
|
||||
#define MAX_FREQUENCY 4698.3
|
||||
#define MIN_FREQUENCY 174.6
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
#if (FIXED_POINT == 32)
|
||||
#warning cq_kernel currently bugged for FIXED_POINT=32
|
||||
#define MIN_VAL 0
|
||||
#else
|
||||
#define MIN_VAL 5000 // Equivalent to 0.15 in Q15
|
||||
#endif
|
||||
#else
|
||||
#define MIN_VAL 0.15
|
||||
#endif
|
||||
|
||||
void dump_kernels(struct sparse_arr kernels[]);
|
||||
|
||||
int main(){
|
||||
// Initialize kiss_fftr_cfg
|
||||
struct cq_kernel_cfg cfg = {
|
||||
.samples = SAMPLES,
|
||||
.bands = BANDS,
|
||||
.fmin = MIN_FREQUENCY,
|
||||
.fmax = MAX_FREQUENCY,
|
||||
.fs = SAMPLING_FREQUENCY,
|
||||
.window_type = HAMMING,
|
||||
.min_val = MIN_VAL
|
||||
};
|
||||
|
||||
cq_kernels_t kernels = generate_kernels(cfg);
|
||||
kernels = reallocate_kernels(kernels, cfg);
|
||||
dump_kernels(kernels);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dump_kernels(struct sparse_arr kernels[]){
|
||||
remove("K_real.txt");
|
||||
remove("K_imag.txt");
|
||||
remove("K_mag.txt");
|
||||
FILE *real_file = fopen("K_real.txt", "w"),
|
||||
*imag_file = fopen("K_imag.txt", "w"),
|
||||
*mag_file = fopen("K_mag.txt", "w");
|
||||
kiss_fft_cpx K_k[BANDS][SAMPLES/2] = {0};
|
||||
for(int i = 0; i < BANDS; i++){
|
||||
for(int j = 0; j < kernels[i].n_elems; j++){
|
||||
K_k[i][kernels[i].elems[j].n] = kernels[i].elems[j].val;
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < SAMPLES/2; i++){
|
||||
for(int j = 0; j < BANDS; j++){
|
||||
#ifdef FIXED_POINT
|
||||
fprintf(real_file, "%i\t", K_k[j][i].r);
|
||||
fprintf(imag_file, "%i\t", K_k[j][i].i);
|
||||
fprintf(mag_file, "%i\t", _mag(K_k[j][i]));
|
||||
#else
|
||||
fprintf(real_file, "%f\t", K_k[j][i].r);
|
||||
fprintf(imag_file, "%f\t", K_k[j][i].i);
|
||||
fprintf(mag_file, "%f\t", _mag(K_k[j][i]));
|
||||
#endif
|
||||
}
|
||||
fprintf(real_file, "\n");
|
||||
fprintf(imag_file, "\n");
|
||||
fprintf(mag_file, "\n");
|
||||
}
|
||||
fclose(real_file);
|
||||
fclose(imag_file);
|
||||
fclose(mag_file);
|
||||
}
|
||||
BIN
libraries/FastLED/src/third_party/cq_kernel/example_out_chart.png
vendored
Normal file
BIN
libraries/FastLED/src/third_party/cq_kernel/example_out_chart.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 339 KiB |
423
libraries/FastLED/src/third_party/cq_kernel/kiss_fft.cpp
vendored
Normal file
423
libraries/FastLED/src/third_party/cq_kernel/kiss_fft.cpp
vendored
Normal file
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#include "_kiss_fft_guts.h"
|
||||
/* The guts header contains all the multiplication and addition macros that are defined for
|
||||
fixed or floating point complex numbers. It also delares the kf_ internal functions.
|
||||
*/
|
||||
|
||||
static void kf_bfly2(
|
||||
kiss_fft_cpx * Fout,
|
||||
const size_t fstride,
|
||||
const kiss_fft_cfg st,
|
||||
int m
|
||||
)
|
||||
{
|
||||
kiss_fft_cpx * Fout2;
|
||||
kiss_fft_cpx * tw1 = st->twiddles;
|
||||
kiss_fft_cpx t;
|
||||
Fout2 = Fout + m;
|
||||
do{
|
||||
C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2);
|
||||
|
||||
C_MUL (t, *Fout2 , *tw1);
|
||||
tw1 += fstride;
|
||||
C_SUB( *Fout2 , *Fout , t );
|
||||
C_ADDTO( *Fout , t );
|
||||
++Fout2;
|
||||
++Fout;
|
||||
}while (--m);
|
||||
}
|
||||
|
||||
static void kf_bfly4(
|
||||
kiss_fft_cpx * Fout,
|
||||
const size_t fstride,
|
||||
const kiss_fft_cfg st,
|
||||
const size_t m
|
||||
)
|
||||
{
|
||||
kiss_fft_cpx *tw1,*tw2,*tw3;
|
||||
kiss_fft_cpx scratch[6];
|
||||
size_t k=m;
|
||||
const size_t m2=2*m;
|
||||
const size_t m3=3*m;
|
||||
|
||||
|
||||
tw3 = tw2 = tw1 = st->twiddles;
|
||||
|
||||
do {
|
||||
C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4);
|
||||
|
||||
C_MUL(scratch[0],Fout[m] , *tw1 );
|
||||
C_MUL(scratch[1],Fout[m2] , *tw2 );
|
||||
C_MUL(scratch[2],Fout[m3] , *tw3 );
|
||||
|
||||
C_SUB( scratch[5] , *Fout, scratch[1] );
|
||||
C_ADDTO(*Fout, scratch[1]);
|
||||
C_ADD( scratch[3] , scratch[0] , scratch[2] );
|
||||
C_SUB( scratch[4] , scratch[0] , scratch[2] );
|
||||
C_SUB( Fout[m2], *Fout, scratch[3] );
|
||||
tw1 += fstride;
|
||||
tw2 += fstride*2;
|
||||
tw3 += fstride*3;
|
||||
C_ADDTO( *Fout , scratch[3] );
|
||||
|
||||
if(st->inverse) {
|
||||
Fout[m].r = scratch[5].r - scratch[4].i;
|
||||
Fout[m].i = scratch[5].i + scratch[4].r;
|
||||
Fout[m3].r = scratch[5].r + scratch[4].i;
|
||||
Fout[m3].i = scratch[5].i - scratch[4].r;
|
||||
}else{
|
||||
Fout[m].r = scratch[5].r + scratch[4].i;
|
||||
Fout[m].i = scratch[5].i - scratch[4].r;
|
||||
Fout[m3].r = scratch[5].r - scratch[4].i;
|
||||
Fout[m3].i = scratch[5].i + scratch[4].r;
|
||||
}
|
||||
++Fout;
|
||||
}while(--k);
|
||||
}
|
||||
|
||||
static void kf_bfly3(
|
||||
kiss_fft_cpx * Fout,
|
||||
const size_t fstride,
|
||||
const kiss_fft_cfg st,
|
||||
size_t m
|
||||
)
|
||||
{
|
||||
size_t k=m;
|
||||
const size_t m2 = 2*m;
|
||||
kiss_fft_cpx *tw1,*tw2;
|
||||
kiss_fft_cpx scratch[5];
|
||||
kiss_fft_cpx epi3;
|
||||
epi3 = st->twiddles[fstride*m];
|
||||
|
||||
tw1=tw2=st->twiddles;
|
||||
|
||||
do{
|
||||
C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3);
|
||||
|
||||
C_MUL(scratch[1],Fout[m] , *tw1);
|
||||
C_MUL(scratch[2],Fout[m2] , *tw2);
|
||||
|
||||
C_ADD(scratch[3],scratch[1],scratch[2]);
|
||||
C_SUB(scratch[0],scratch[1],scratch[2]);
|
||||
tw1 += fstride;
|
||||
tw2 += fstride*2;
|
||||
|
||||
Fout[m].r = Fout->r - HALF_OF(scratch[3].r);
|
||||
Fout[m].i = Fout->i - HALF_OF(scratch[3].i);
|
||||
|
||||
C_MULBYSCALAR( scratch[0] , epi3.i );
|
||||
|
||||
C_ADDTO(*Fout,scratch[3]);
|
||||
|
||||
Fout[m2].r = Fout[m].r + scratch[0].i;
|
||||
Fout[m2].i = Fout[m].i - scratch[0].r;
|
||||
|
||||
Fout[m].r -= scratch[0].i;
|
||||
Fout[m].i += scratch[0].r;
|
||||
|
||||
++Fout;
|
||||
}while(--k);
|
||||
}
|
||||
|
||||
static void kf_bfly5(
|
||||
kiss_fft_cpx * Fout,
|
||||
const size_t fstride,
|
||||
const kiss_fft_cfg st,
|
||||
int m
|
||||
)
|
||||
{
|
||||
kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
|
||||
int u;
|
||||
kiss_fft_cpx scratch[13];
|
||||
kiss_fft_cpx * twiddles = st->twiddles;
|
||||
kiss_fft_cpx *tw;
|
||||
kiss_fft_cpx ya,yb;
|
||||
ya = twiddles[fstride*m];
|
||||
yb = twiddles[fstride*2*m];
|
||||
|
||||
Fout0=Fout;
|
||||
Fout1=Fout0+m;
|
||||
Fout2=Fout0+2*m;
|
||||
Fout3=Fout0+3*m;
|
||||
Fout4=Fout0+4*m;
|
||||
|
||||
tw=st->twiddles;
|
||||
for ( u=0; u<m; ++u ) {
|
||||
C_FIXDIV( *Fout0,5); C_FIXDIV( *Fout1,5); C_FIXDIV( *Fout2,5); C_FIXDIV( *Fout3,5); C_FIXDIV( *Fout4,5);
|
||||
scratch[0] = *Fout0;
|
||||
|
||||
C_MUL(scratch[1] ,*Fout1, tw[u*fstride]);
|
||||
C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]);
|
||||
C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]);
|
||||
C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]);
|
||||
|
||||
C_ADD( scratch[7],scratch[1],scratch[4]);
|
||||
C_SUB( scratch[10],scratch[1],scratch[4]);
|
||||
C_ADD( scratch[8],scratch[2],scratch[3]);
|
||||
C_SUB( scratch[9],scratch[2],scratch[3]);
|
||||
|
||||
Fout0->r += scratch[7].r + scratch[8].r;
|
||||
Fout0->i += scratch[7].i + scratch[8].i;
|
||||
|
||||
scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r);
|
||||
scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r);
|
||||
|
||||
scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i);
|
||||
scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i);
|
||||
|
||||
C_SUB(*Fout1,scratch[5],scratch[6]);
|
||||
C_ADD(*Fout4,scratch[5],scratch[6]);
|
||||
|
||||
scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r);
|
||||
scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r);
|
||||
scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i);
|
||||
scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i);
|
||||
|
||||
C_ADD(*Fout2,scratch[11],scratch[12]);
|
||||
C_SUB(*Fout3,scratch[11],scratch[12]);
|
||||
|
||||
++Fout0;++Fout1;++Fout2;++Fout3;++Fout4;
|
||||
}
|
||||
}
|
||||
|
||||
/* perform the butterfly for one stage of a mixed radix FFT */
|
||||
static void kf_bfly_generic(
|
||||
kiss_fft_cpx * Fout,
|
||||
const size_t fstride,
|
||||
const kiss_fft_cfg st,
|
||||
int m,
|
||||
int p
|
||||
)
|
||||
{
|
||||
int u,k,q1,q;
|
||||
kiss_fft_cpx * twiddles = st->twiddles;
|
||||
kiss_fft_cpx t;
|
||||
int Norig = st->nfft;
|
||||
|
||||
kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p);
|
||||
|
||||
for ( u=0; u<m; ++u ) {
|
||||
k=u;
|
||||
for ( q1=0 ; q1<p ; ++q1 ) {
|
||||
scratch[q1] = Fout[ k ];
|
||||
C_FIXDIV(scratch[q1],p);
|
||||
k += m;
|
||||
}
|
||||
|
||||
k=u;
|
||||
for ( q1=0 ; q1<p ; ++q1 ) {
|
||||
int twidx=0;
|
||||
Fout[ k ] = scratch[0];
|
||||
for (q=1;q<p;++q ) {
|
||||
twidx += fstride * k;
|
||||
if (twidx>=Norig) twidx-=Norig;
|
||||
C_MUL(t,scratch[q] , twiddles[twidx] );
|
||||
C_ADDTO( Fout[ k ] ,t);
|
||||
}
|
||||
k += m;
|
||||
}
|
||||
}
|
||||
KISS_FFT_TMP_FREE(scratch);
|
||||
}
|
||||
|
||||
static
|
||||
void kf_work(
|
||||
kiss_fft_cpx * Fout,
|
||||
const kiss_fft_cpx * f,
|
||||
const size_t fstride,
|
||||
int in_stride,
|
||||
int * factors,
|
||||
const kiss_fft_cfg st
|
||||
)
|
||||
{
|
||||
kiss_fft_cpx * Fout_beg=Fout;
|
||||
const int p=*factors++; /* the radix */
|
||||
const int m=*factors++; /* stage's fft length/p */
|
||||
const kiss_fft_cpx * Fout_end = Fout + p*m;
|
||||
|
||||
#ifdef _OPENMP
|
||||
// use openmp extensions at the
|
||||
// top-level (not recursive)
|
||||
if (fstride==1 && p<=5)
|
||||
{
|
||||
int k;
|
||||
|
||||
// execute the p different work units in different threads
|
||||
# pragma omp parallel for
|
||||
for (k=0;k<p;++k)
|
||||
kf_work( Fout +k*m, f+ fstride*in_stride*k,fstride*p,in_stride,factors,st);
|
||||
// all threads have joined by this point
|
||||
|
||||
switch (p) {
|
||||
case 2: kf_bfly2(Fout,fstride,st,m); break;
|
||||
case 3: kf_bfly3(Fout,fstride,st,m); break;
|
||||
case 4: kf_bfly4(Fout,fstride,st,m); break;
|
||||
case 5: kf_bfly5(Fout,fstride,st,m); break;
|
||||
default: kf_bfly_generic(Fout,fstride,st,m,p); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m==1) {
|
||||
do{
|
||||
*Fout = *f;
|
||||
f += fstride*in_stride;
|
||||
}while(++Fout != Fout_end );
|
||||
}else{
|
||||
do{
|
||||
// recursive call:
|
||||
// DFT of size m*p performed by doing
|
||||
// p instances of smaller DFTs of size m,
|
||||
// each one takes every mth sample
|
||||
// so0, s1, s2 become
|
||||
// so, sm, s2m, s3m, ...
|
||||
// s1, s(1+m), s(1+2m), ...
|
||||
// s2, s(2+m), s(2+2m), ...
|
||||
// etc.
|
||||
kf_work( Fout , f, fstride*p, in_stride, factors,st);
|
||||
f += fstride*in_stride;
|
||||
}while( (Fout += m) != Fout_end );
|
||||
}
|
||||
|
||||
Fout=Fout_beg;
|
||||
|
||||
// recombine the p smaller DFTs
|
||||
switch (p) {
|
||||
case 2: kf_bfly2(Fout,fstride,st,m); break;
|
||||
case 3: kf_bfly3(Fout,fstride,st,m); break;
|
||||
case 4: kf_bfly4(Fout,fstride,st,m); break;
|
||||
case 5: kf_bfly5(Fout,fstride,st,m); break;
|
||||
default: kf_bfly_generic(Fout,fstride,st,m,p); break;
|
||||
}
|
||||
}
|
||||
|
||||
/* facbuf is populated by p1,m1,p2,m2, ...
|
||||
where
|
||||
p[i] * m[i] = m[i-1]
|
||||
m0 = n */
|
||||
static
|
||||
void kf_factor(int n,int * facbuf)
|
||||
{
|
||||
int p=4;
|
||||
double floor_sqrt;
|
||||
floor_sqrt = floor( sqrt((double)n) );
|
||||
|
||||
/*factor out powers of 4, powers of 2, then any remaining primes */
|
||||
do {
|
||||
while (n % p) {
|
||||
switch (p) {
|
||||
case 4: p = 2; break;
|
||||
case 2: p = 3; break;
|
||||
default: p += 2; break;
|
||||
}
|
||||
if (p > floor_sqrt)
|
||||
p = n; /* no more factors, skip to end */
|
||||
}
|
||||
n /= p;
|
||||
*facbuf++ = p;
|
||||
*facbuf++ = n;
|
||||
} while (n > 1);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* User-callable function to allocate all necessary storage space for the fft.
|
||||
*
|
||||
* The return value is a contiguous block of memory, cast to a kiss_fft_cfg.
|
||||
*
|
||||
* As such, the first cfg->sizeof_cfg bytes contain the configuration information.
|
||||
* In a single-threaded program, subsequent calls to kiss_fft do not access this memory.
|
||||
*
|
||||
* The next section holds the twiddle factors for each stage.
|
||||
*
|
||||
* Note that the cfg is not guaranteed to be valid over time -- it should not be
|
||||
* shared between threads and should not be used after a subsequent call to kiss_fft_alloc.
|
||||
*
|
||||
* If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc.
|
||||
* The returned value should be free()d when done to avoid memory leaks.
|
||||
*
|
||||
* The state can be placed in a user-supplied buffer 'mem':
|
||||
* If mem==NULL, then a private internal buffer is allocated.
|
||||
* If mem!=NULL, then mem must have size bytes. *lenmem gets set to the size of the cfg.
|
||||
* On return, the first *lenmem words of mem will hold the configuration information.
|
||||
* The remainder is scratchpad memory, safe to ignore or overwrite.
|
||||
*
|
||||
*/
|
||||
kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem )
|
||||
{
|
||||
kiss_fft_cfg st=NULL;
|
||||
size_t memneeded = sizeof(struct kiss_fft_state)
|
||||
+ sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/
|
||||
|
||||
if ( lenmem==NULL ) {
|
||||
st = (kiss_fft_cfg) KISS_FFT_MALLOC( memneeded );
|
||||
}else{
|
||||
if (mem != NULL && *lenmem >= memneeded)
|
||||
st = (kiss_fft_cfg)mem;
|
||||
*lenmem = memneeded;
|
||||
}
|
||||
if (st) {
|
||||
int i;
|
||||
st->nfft=nfft;
|
||||
st->inverse = inverse_fft;
|
||||
|
||||
for (i=0;i<nfft;++i) {
|
||||
const double pi=3.141592653589793238462643383279502884197169399375105820974944;
|
||||
double phase = -2*pi*i / nfft;
|
||||
if (st->inverse)
|
||||
phase *= -1;
|
||||
kf_cexp(st->twiddles+i, phase );
|
||||
}
|
||||
|
||||
kf_factor(nfft,st->factors);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
|
||||
void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride)
|
||||
{
|
||||
if (fin == fout) {
|
||||
//NOTE: this is not really an in-place FFT algorithm.
|
||||
//It just performs an out-of-place FFT into a temp buffer
|
||||
kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft);
|
||||
kf_work(tmpbuf,fin,1,in_stride, st->factors,st);
|
||||
memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft);
|
||||
KISS_FFT_TMP_FREE(tmpbuf);
|
||||
}else{
|
||||
kf_work( fout, fin, 1, in_stride, st->factors,st );
|
||||
}
|
||||
}
|
||||
|
||||
void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
|
||||
{
|
||||
kiss_fft_stride(cfg,fin,fout,1);
|
||||
}
|
||||
|
||||
|
||||
void kiss_fft_cleanup(void)
|
||||
{
|
||||
// nothing needed any more
|
||||
}
|
||||
|
||||
int kiss_fft_next_fast_size(int n)
|
||||
{
|
||||
while(1) {
|
||||
int m=n;
|
||||
while ( (m%2) == 0 ) m/=2;
|
||||
while ( (m%3) == 0 ) m/=3;
|
||||
while ( (m%5) == 0 ) m/=5;
|
||||
if (m<=1)
|
||||
break; /* n is completely factorable by twos, threes, and fives */
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
145
libraries/FastLED/src/third_party/cq_kernel/kiss_fft.h
vendored
Normal file
145
libraries/FastLED/src/third_party/cq_kernel/kiss_fft.h
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#ifndef KISS_FFT_H
|
||||
#define KISS_FFT_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "fl/stdint.h"
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// Always use fixed point.
|
||||
#define FIXED_POINT 1
|
||||
|
||||
// WROVER and WROOM have 4MB of PSRAM or more.
|
||||
#if KISS_FFT_USE_ESP32_PSRAM
|
||||
#include "esp_heap_caps.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
ATTENTION!
|
||||
If you would like a :
|
||||
-- a utility that will handle the caching of fft objects
|
||||
-- real-only (no imaginary time component ) FFT
|
||||
-- a multi-dimensional FFT
|
||||
-- a command-line utility to perform ffts
|
||||
-- a command-line utility to perform fast-convolution filtering
|
||||
|
||||
Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c
|
||||
in the tools/ directory.
|
||||
*/
|
||||
|
||||
#if KISS_FFT_USE_ESP32_PSRAM
|
||||
#define KISS_FFT_MALLOC(nbytes) heap_caps_malloc(nbytes, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)
|
||||
#define KISS_FFT_FREE free
|
||||
#elif defined(USE_SIMD)
|
||||
# include <xmmintrin.h>
|
||||
# define kiss_fft_scalar __m128
|
||||
#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16)
|
||||
#define KISS_FFT_FREE _mm_free
|
||||
#else
|
||||
#define KISS_FFT_MALLOC malloc
|
||||
#define KISS_FFT_FREE free
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
#include <sys/types.h>
|
||||
# if (FIXED_POINT == 32)
|
||||
# define kiss_fft_scalar int32_t
|
||||
# else
|
||||
# define kiss_fft_scalar int16_t
|
||||
# endif
|
||||
#else
|
||||
# ifndef kiss_fft_scalar
|
||||
/* default is float */
|
||||
# define kiss_fft_scalar float
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
kiss_fft_scalar r;
|
||||
kiss_fft_scalar i;
|
||||
}kiss_fft_cpx;
|
||||
|
||||
typedef struct kiss_fft_state* kiss_fft_cfg;
|
||||
|
||||
/*
|
||||
* kiss_fft_alloc
|
||||
*
|
||||
* Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
|
||||
*
|
||||
* typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL);
|
||||
*
|
||||
* The return value from fft_alloc is a cfg buffer used internally
|
||||
* by the fft routine or NULL.
|
||||
*
|
||||
* If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc.
|
||||
* The returned value should be free()d when done to avoid memory leaks.
|
||||
*
|
||||
* The state can be placed in a user supplied buffer 'mem':
|
||||
* If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
|
||||
* then the function places the cfg in mem and the size used in *lenmem
|
||||
* and returns mem.
|
||||
*
|
||||
* If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
|
||||
* then the function returns NULL and places the minimum cfg
|
||||
* buffer size in *lenmem.
|
||||
* */
|
||||
|
||||
kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem);
|
||||
|
||||
/*
|
||||
* kiss_fft(cfg,in_out_buf)
|
||||
*
|
||||
* Perform an FFT on a complex input buffer.
|
||||
* for a forward FFT,
|
||||
* fin should be f[0] , f[1] , ... ,f[nfft-1]
|
||||
* fout will be F[0] , F[1] , ... ,F[nfft-1]
|
||||
* Note that each element is complex and can be accessed like
|
||||
f[k].r and f[k].i
|
||||
* */
|
||||
void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
|
||||
|
||||
/*
|
||||
A more generic version of the above function. It reads its input from every Nth sample.
|
||||
* */
|
||||
void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride);
|
||||
|
||||
/* If kiss_fft_alloc allocated a buffer, it is one contiguous
|
||||
buffer and can be simply free()d when no longer needed*/
|
||||
#define kiss_fft_free KISS_FFT_FREE
|
||||
|
||||
/*
|
||||
Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up
|
||||
your compiler output to call this before you exit.
|
||||
*/
|
||||
void kiss_fft_cleanup(void);
|
||||
|
||||
|
||||
/*
|
||||
* Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5)
|
||||
*/
|
||||
int kiss_fft_next_fast_size(int n);
|
||||
|
||||
/* for real ffts, we need an even size */
|
||||
#define kiss_fftr_next_fast_size_real(n) \
|
||||
(kiss_fft_next_fast_size( ((n)+1)>>1)<<1)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
159
libraries/FastLED/src/third_party/cq_kernel/kiss_fftr.cpp
vendored
Normal file
159
libraries/FastLED/src/third_party/cq_kernel/kiss_fftr.cpp
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#include "kiss_fftr.h"
|
||||
#include "_kiss_fft_guts.h"
|
||||
|
||||
struct kiss_fftr_state{
|
||||
kiss_fft_cfg substate;
|
||||
kiss_fft_cpx * tmpbuf;
|
||||
kiss_fft_cpx * super_twiddles;
|
||||
#ifdef USE_SIMD
|
||||
void * pad;
|
||||
#endif
|
||||
};
|
||||
|
||||
kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem)
|
||||
{
|
||||
int i;
|
||||
kiss_fftr_cfg st = NULL;
|
||||
size_t subsize = 0, memneeded;
|
||||
|
||||
if (nfft & 1) {
|
||||
fprintf(stderr,"Real FFT optimization must be even.\n");
|
||||
return NULL;
|
||||
}
|
||||
nfft >>= 1;
|
||||
|
||||
kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize);
|
||||
memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2);
|
||||
|
||||
if (lenmem == NULL) {
|
||||
st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded);
|
||||
} else {
|
||||
if (*lenmem >= memneeded)
|
||||
st = (kiss_fftr_cfg) mem;
|
||||
*lenmem = memneeded;
|
||||
}
|
||||
if (!st)
|
||||
return NULL;
|
||||
|
||||
st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-align"
|
||||
// This cast alginment might be a problem, kiss_fft_cpx has alignment-2.
|
||||
st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize);
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
st->super_twiddles = st->tmpbuf + nfft;
|
||||
kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize);
|
||||
|
||||
for (i = 0; i < nfft/2; ++i) {
|
||||
double phase =
|
||||
-3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5);
|
||||
if (inverse_fft)
|
||||
phase *= -1;
|
||||
kf_cexp (st->super_twiddles+i,phase);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata)
|
||||
{
|
||||
/* input buffer timedata is stored row-wise */
|
||||
int k,ncfft;
|
||||
kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc;
|
||||
|
||||
if ( st->substate->inverse) {
|
||||
fprintf(stderr,"kiss fft usage error: improper alloc\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ncfft = st->substate->nfft;
|
||||
|
||||
/*perform the parallel fft of two real signals packed in real,imag*/
|
||||
kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf );
|
||||
/* The real part of the DC element of the frequency spectrum in st->tmpbuf
|
||||
* contains the sum of the even-numbered elements of the input time sequence
|
||||
* The imag part is the sum of the odd-numbered elements
|
||||
*
|
||||
* The sum of tdc.r and tdc.i is the sum of the input time sequence.
|
||||
* yielding DC of input time sequence
|
||||
* The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1...
|
||||
* yielding Nyquist bin of input time sequence
|
||||
*/
|
||||
|
||||
tdc.r = st->tmpbuf[0].r;
|
||||
tdc.i = st->tmpbuf[0].i;
|
||||
C_FIXDIV(tdc,2);
|
||||
CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i);
|
||||
CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i);
|
||||
freqdata[0].r = tdc.r + tdc.i;
|
||||
freqdata[ncfft].r = tdc.r - tdc.i;
|
||||
#ifdef USE_SIMD
|
||||
freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0);
|
||||
#else
|
||||
freqdata[ncfft].i = freqdata[0].i = 0;
|
||||
#endif
|
||||
|
||||
for ( k=1;k <= ncfft/2 ; ++k ) {
|
||||
fpk = st->tmpbuf[k];
|
||||
fpnk.r = st->tmpbuf[ncfft-k].r;
|
||||
fpnk.i = - st->tmpbuf[ncfft-k].i;
|
||||
C_FIXDIV(fpk,2);
|
||||
C_FIXDIV(fpnk,2);
|
||||
|
||||
C_ADD( f1k, fpk , fpnk );
|
||||
C_SUB( f2k, fpk , fpnk );
|
||||
C_MUL( tw , f2k , st->super_twiddles[k-1]);
|
||||
|
||||
freqdata[k].r = HALF_OF(f1k.r + tw.r);
|
||||
freqdata[k].i = HALF_OF(f1k.i + tw.i);
|
||||
freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r);
|
||||
freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i);
|
||||
}
|
||||
}
|
||||
|
||||
void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata)
|
||||
{
|
||||
/* input buffer timedata is stored row-wise */
|
||||
int k, ncfft;
|
||||
|
||||
if (st->substate->inverse == 0) {
|
||||
fprintf (stderr, "kiss fft usage error: improper alloc\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
ncfft = st->substate->nfft;
|
||||
|
||||
st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r;
|
||||
st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r;
|
||||
C_FIXDIV(st->tmpbuf[0],2);
|
||||
|
||||
for (k = 1; k <= ncfft / 2; ++k) {
|
||||
kiss_fft_cpx fk, fnkc, fek, fok, tmp;
|
||||
fk = freqdata[k];
|
||||
fnkc.r = freqdata[ncfft - k].r;
|
||||
fnkc.i = -freqdata[ncfft - k].i;
|
||||
C_FIXDIV( fk , 2 );
|
||||
C_FIXDIV( fnkc , 2 );
|
||||
|
||||
C_ADD (fek, fk, fnkc);
|
||||
C_SUB (tmp, fk, fnkc);
|
||||
C_MUL (fok, tmp, st->super_twiddles[k-1]);
|
||||
C_ADD (st->tmpbuf[k], fek, fok);
|
||||
C_SUB (st->tmpbuf[ncfft - k], fek, fok);
|
||||
#ifdef USE_SIMD
|
||||
st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0);
|
||||
#else
|
||||
st->tmpbuf[ncfft - k].i *= -1;
|
||||
#endif
|
||||
}
|
||||
kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata);
|
||||
}
|
||||
54
libraries/FastLED/src/third_party/cq_kernel/kiss_fftr.h
vendored
Normal file
54
libraries/FastLED/src/third_party/cq_kernel/kiss_fftr.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#ifndef KISS_FTR_H
|
||||
#define KISS_FTR_H
|
||||
|
||||
#include "kiss_fft.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Real optimized version can save about 45% cpu time vs. complex fft of a real seq.
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
typedef struct kiss_fftr_state *kiss_fftr_cfg;
|
||||
|
||||
|
||||
kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem);
|
||||
/*
|
||||
nfft must be even
|
||||
|
||||
If you don't care to allocate space, use mem = lenmem = NULL
|
||||
*/
|
||||
|
||||
|
||||
void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata);
|
||||
/*
|
||||
input timedata has nfft scalar points
|
||||
output freqdata has nfft/2+1 complex points
|
||||
*/
|
||||
|
||||
void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata);
|
||||
/*
|
||||
input freqdata has nfft/2+1 complex points
|
||||
output timedata has nfft scalar points
|
||||
*/
|
||||
|
||||
#define kiss_fftr_free KISS_FFT_FREE
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
28
libraries/FastLED/src/third_party/espressif/led_strip/readme
vendored
Normal file
28
libraries/FastLED/src/third_party/espressif/led_strip/readme
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
From espressif led_strip component repository.
|
||||
|
||||
Version: 3.0.0
|
||||
|
||||
This library has been lightly modified to support async operations for both spi and rmt strip drivers.
|
||||
|
||||
The following files were added by FastLED:
|
||||
|
||||
strip_spi.h
|
||||
strip_spi.cpp
|
||||
strip_rmt.h
|
||||
strip_rmt.cpp
|
||||
|
||||
== External Pixel Buffer Support ==
|
||||
|
||||
The RMT LED strip driver now supports optional external pixel buffers. This allows callers to provide their own pixel buffer instead of having the driver allocate one internally.
|
||||
|
||||
Usage:
|
||||
- Set `external_pixel_buf` to `nullptr` (default) for internal allocation
|
||||
- Set `external_pixel_buf` to a valid buffer pointer for external allocation
|
||||
- When using external buffers, the caller is responsible for buffer management
|
||||
- The driver will NOT free external pixel buffers when destroyed
|
||||
|
||||
Benefits:
|
||||
- Allows custom memory management strategies
|
||||
- Enables buffer reuse across multiple strips
|
||||
- Supports specific memory alignment requirements
|
||||
- Prevents unwanted memory deallocation by the driver
|
||||
72
libraries/FastLED/src/third_party/espressif/led_strip/src/enabled.h
vendored
Normal file
72
libraries/FastLED/src/third_party/espressif/led_strip/src/enabled.h
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ESP32
|
||||
#include "sdkconfig.h"
|
||||
#include "platforms/esp/esp_version.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C2
|
||||
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
|
||||
#define FASTLED_ESP32_HAS_RMT 0
|
||||
#define FASTLED_ESP32_HAS_RMT5 0
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
|
||||
#define FASTLED_ESP32_HAS_RMT 1
|
||||
#define FASTLED_ESP32_HAS_RMT5 1
|
||||
#elif CONFIG_IDF_TARGET_ESP32C5
|
||||
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
|
||||
#define FASTLED_ESP32_HAS_RMT 1
|
||||
#define FASTLED_ESP32_HAS_RMT5 1
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
|
||||
#define FASTLED_ESP32_HAS_RMT 1
|
||||
#define FASTLED_ESP32_HAS_RMT5 1
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
|
||||
#define FASTLED_ESP32_HAS_RMT 1
|
||||
#define FASTLED_ESP32_HAS_RMT5 1
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
|
||||
#define FASTLED_ESP32_HAS_RMT 1
|
||||
#define FASTLED_ESP32_HAS_RMT5 1
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
|
||||
#define FASTLED_ESP32_HAS_RMT 1
|
||||
#define FASTLED_ESP32_HAS_RMT5 1
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
|
||||
#define FASTLED_ESP32_HAS_RMT 1
|
||||
#define FASTLED_ESP32_HAS_RMT5 1
|
||||
#elif CONFIG_IDF_TARGET_ESP8266
|
||||
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 0
|
||||
#define FASTLED_ESP32_HAS_RMT 0
|
||||
#define FASTLED_ESP32_HAS_RMT5 0
|
||||
#elif CONFIG_IDF_TARGET_ESP32 || defined(ARDUINO_ESP32_DEV)
|
||||
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
|
||||
#define FASTLED_ESP32_HAS_RMT 1
|
||||
#define FASTLED_ESP32_HAS_RMT5 1
|
||||
#else
|
||||
#warning "Unknown board, assuming support for clockless RMT5 and SPI chipsets. Please file an bug report with FastLED and tell them about your board type."
|
||||
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
|
||||
#define FASTLED_ESP32_HAS_RMT 1
|
||||
#define FASTLED_ESP32_HAS_RMT5 1
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#undef FASTLED_ESP32_HAS_RMT5
|
||||
#undef FASTLED_ESP32_HAS_CLOCKLESS_SPI
|
||||
#define FASTLED_ESP32_HAS_RMT5 0
|
||||
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 0
|
||||
#endif
|
||||
|
||||
|
||||
// Note that FASTLED_RMT5 is a legacy name,
|
||||
// so we keep it because "RMT" is specific to ESP32
|
||||
#ifndef FASTLED_RMT5
|
||||
#if FASTLED_ESP32_HAS_RMT5 && !defined(FASTLED_RMT5)
|
||||
#define FASTLED_RMT5 1
|
||||
#else
|
||||
#define FASTLED_RMT5 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // ESP32
|
||||
112
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip.h
vendored
Normal file
112
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip.h
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "fl/stdint.h"
|
||||
#include "esp_err.h"
|
||||
#include "led_strip_rmt.h"
|
||||
#include "led_strip_spi.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set RGB for a specific pixel
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGB for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
|
||||
|
||||
/**
|
||||
* @brief Set RGBW for a specific pixel
|
||||
*
|
||||
* @note Only call this function if your led strip does have the white component (e.g. SK6812-RGBW)
|
||||
* @note Also see `led_strip_set_pixel` if you only want to specify the RGB part of the color and bypass the white component
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
* @param white: separate white component
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGBW color for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
|
||||
* - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
|
||||
|
||||
/**
|
||||
* @brief Set HSV for a specific pixel
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param hue: hue part of color (0 - 360)
|
||||
* @param saturation: saturation part of color (0 - 255, rescaled from 0 - 1. e.g. saturation = 0.5, rescaled to 127)
|
||||
* @param value: value part of color (0 - 255, rescaled from 0 - 1. e.g. value = 0.5, rescaled to 127)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set HSV color for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set HSV color for a specific pixel failed because of an invalid argument
|
||||
* - ESP_FAIL: Set HSV color for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value);
|
||||
|
||||
/**
|
||||
* @brief Refresh memory colors to LEDs
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Refresh successfully
|
||||
* - ESP_FAIL: Refresh failed because some other error occurred
|
||||
*
|
||||
* @note:
|
||||
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||
*/
|
||||
esp_err_t led_strip_refresh(led_strip_handle_t strip);
|
||||
|
||||
|
||||
esp_err_t led_strip_refresh_async(led_strip_handle_t strip);
|
||||
|
||||
esp_err_t led_strip_refresh_wait_done(led_strip_handle_t strip);
|
||||
|
||||
/**
|
||||
* @brief Clear LED strip (turn off all LEDs)
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Clear LEDs successfully
|
||||
* - ESP_FAIL: Clear LEDs failed because some other error occurred
|
||||
*/
|
||||
esp_err_t led_strip_clear(led_strip_handle_t strip);
|
||||
|
||||
/**
|
||||
* @brief Free LED strip resources
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Free resources successfully
|
||||
* - ESP_FAIL: Free resources failed because error occurred
|
||||
*/
|
||||
esp_err_t led_strip_del(led_strip_handle_t strip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
118
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_api.c
vendored
Normal file
118
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_api.c
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
#include "enabled.h"
|
||||
|
||||
#if FASTLED_RMT5 || FASTLED_ESP32_HAS_CLOCKLESS_SPI
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
|
||||
static const char *TAG = "led_strip";
|
||||
|
||||
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->set_pixel(strip, index, red, green, blue);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
uint32_t red = 0;
|
||||
uint32_t green = 0;
|
||||
uint32_t blue = 0;
|
||||
|
||||
uint32_t rgb_max = value;
|
||||
uint32_t rgb_min = rgb_max * (255 - saturation) / 255.0f;
|
||||
|
||||
uint32_t i = hue / 60;
|
||||
uint32_t diff = hue % 60;
|
||||
|
||||
// RGB adjustment amount by hue
|
||||
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
red = rgb_max;
|
||||
green = rgb_min + rgb_adj;
|
||||
blue = rgb_min;
|
||||
break;
|
||||
case 1:
|
||||
red = rgb_max - rgb_adj;
|
||||
green = rgb_max;
|
||||
blue = rgb_min;
|
||||
break;
|
||||
case 2:
|
||||
red = rgb_min;
|
||||
green = rgb_max;
|
||||
blue = rgb_min + rgb_adj;
|
||||
break;
|
||||
case 3:
|
||||
red = rgb_min;
|
||||
green = rgb_max - rgb_adj;
|
||||
blue = rgb_max;
|
||||
break;
|
||||
case 4:
|
||||
red = rgb_min + rgb_adj;
|
||||
green = rgb_min;
|
||||
blue = rgb_max;
|
||||
break;
|
||||
default:
|
||||
red = rgb_max;
|
||||
green = rgb_min;
|
||||
blue = rgb_max - rgb_adj;
|
||||
break;
|
||||
}
|
||||
|
||||
return strip->set_pixel(strip, index, red, green, blue);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_refresh_wait_done(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->refresh_wait_done(strip);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->set_pixel_rgbw(strip, index, red, green, blue, white);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_refresh(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_ERROR(strip->refresh_async(strip), TAG, "refresh failed");
|
||||
ESP_RETURN_ON_ERROR(strip->refresh_wait_done(strip), TAG, "wait for done failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_refresh_async(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->refresh_async(strip);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_clear(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->clear(strip);
|
||||
}
|
||||
|
||||
esp_err_t led_strip_del(led_strip_handle_t strip)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
return strip->del(strip);
|
||||
}
|
||||
|
||||
#endif // FASTLED_RMT5 || FASTLED_ESP_HAS_CLOCKLESS_SPI
|
||||
|
||||
#endif // ESP32
|
||||
99
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_interface.h
vendored
Normal file
99
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_interface.h
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "fl/stdint.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct led_strip_t led_strip_t; /*!< Type of LED strip */
|
||||
|
||||
/**
|
||||
* @brief LED strip interface definition
|
||||
*/
|
||||
struct led_strip_t {
|
||||
/**
|
||||
* @brief Set RGB for a specific pixel
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGB for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
|
||||
|
||||
/**
|
||||
* @brief Set RGBW for a specific pixel. Similar to `set_pixel` but also set the white component
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param index: index of pixel to set
|
||||
* @param red: red part of color
|
||||
* @param green: green part of color
|
||||
* @param blue: blue part of color
|
||||
* @param white: separate white component
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Set RGBW color for a specific pixel successfully
|
||||
* - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
|
||||
* - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
|
||||
*/
|
||||
esp_err_t (*set_pixel_rgbw)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
|
||||
|
||||
/**
|
||||
* @brief Refresh memory colors to LEDs
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param timeout_ms: timeout value for refreshing task
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Refresh successfully
|
||||
* - ESP_FAIL: Refresh failed because some other error occurred
|
||||
*
|
||||
* @note:
|
||||
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||
*/
|
||||
esp_err_t (*refresh)(led_strip_t *strip);
|
||||
|
||||
|
||||
esp_err_t (*refresh_async)(led_strip_t *strip);
|
||||
esp_err_t (*refresh_wait_done)(led_strip_t *strip);
|
||||
|
||||
/**
|
||||
* @brief Clear LED strip (turn off all LEDs)
|
||||
*
|
||||
* @param strip: LED strip
|
||||
* @param timeout_ms: timeout value for clearing task
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Clear LEDs successfully
|
||||
* - ESP_FAIL: Clear LEDs failed because some other error occurred
|
||||
*/
|
||||
esp_err_t (*clear)(led_strip_t *strip);
|
||||
|
||||
/**
|
||||
* @brief Free LED strip resources
|
||||
*
|
||||
* @param strip: LED strip
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Free resources successfully
|
||||
* - ESP_FAIL: Free resources failed because error occurred
|
||||
*/
|
||||
esp_err_t (*del)(led_strip_t *strip);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
48
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_rmt.h
vendored
Normal file
48
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_rmt.h
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "fl/stdint.h"
|
||||
#include "esp_err.h"
|
||||
#include "led_strip_types.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "driver/rmt_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LED Strip RMT specific configuration
|
||||
*/
|
||||
typedef struct {
|
||||
rmt_clock_source_t clk_src; /*!< RMT clock source */
|
||||
uint32_t resolution_hz; /*!< RMT tick resolution, if set to zero, a default resolution (10MHz) will be applied */
|
||||
size_t mem_block_symbols; /*!< How many RMT symbols can one RMT channel hold at one time. Set to 0 will fallback to use the default size. */
|
||||
/*!< Extra RMT specific driver flags */
|
||||
struct led_strip_rmt_extra_config {
|
||||
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
|
||||
} flags; /*!< Extra driver flags */
|
||||
uint8_t interrupt_priority; /*!< RMT interrupt priority, 0-3 are valid values */
|
||||
} led_strip_rmt_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create LED strip based on RMT TX channel
|
||||
*
|
||||
* @param led_config LED strip configuration
|
||||
* @param rmt_config RMT specific configuration
|
||||
* @param ret_strip Returned LED strip handle
|
||||
* @return
|
||||
* - ESP_OK: create LED strip handle successfully
|
||||
* - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
|
||||
* - ESP_FAIL: create LED strip handle failed because some other error
|
||||
*/
|
||||
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
247
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_rmt_dev.c
vendored
Normal file
247
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_rmt_dev.c
vendored
Normal file
@@ -0,0 +1,247 @@
|
||||
#ifdef ESP32
|
||||
|
||||
#include "enabled.h"
|
||||
|
||||
#if FASTLED_RMT5
|
||||
|
||||
#include "fl/unused.h"
|
||||
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/rmt_tx.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
#include "led_strip_rmt_encoder.h"
|
||||
|
||||
#define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution
|
||||
#define LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE 4
|
||||
|
||||
#ifndef LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS_MULTIPLIER
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
// Fixes https://github.com/FastLED/FastLED/issues/1846
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS_MULTIPLIER 2
|
||||
#else
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS_MULTIPLIER 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// the memory size of each RMT channel, in words (4 bytes)
|
||||
#ifndef LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS
|
||||
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS (LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS_MULTIPLIER * 64)
|
||||
#else
|
||||
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS (LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS_MULTIPLIER * 48)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
static const char *TAG = "led_strip_rmt";
|
||||
|
||||
typedef struct {
|
||||
led_strip_t base;
|
||||
rmt_channel_handle_t rmt_chan;
|
||||
rmt_encoder_handle_t strip_encoder;
|
||||
rmt_transmit_config_t tx_conf; /*!< Transmit configuration, stored in the object to prevent stack access in interrupts */
|
||||
uint32_t strip_len;
|
||||
uint8_t bytes_per_pixel;
|
||||
led_color_component_format_t component_fmt;
|
||||
uint8_t *pixel_buf;
|
||||
bool pixel_buf_allocated_internally; /*!< Flag to track if pixel_buf was allocated by this driver */
|
||||
} led_strip_rmt_obj;
|
||||
|
||||
static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
|
||||
led_color_component_format_t component_fmt = rmt_strip->component_fmt;
|
||||
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||
uint8_t *pixel_buf = rmt_strip->pixel_buf;
|
||||
|
||||
pixel_buf[start + component_fmt.format.r_pos] = red & 0xFF;
|
||||
pixel_buf[start + component_fmt.format.g_pos] = green & 0xFF;
|
||||
pixel_buf[start + component_fmt.format.b_pos] = blue & 0xFF;
|
||||
if (component_fmt.format.num_components > 3) {
|
||||
pixel_buf[start + component_fmt.format.w_pos] = 0;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
led_color_component_format_t component_fmt = rmt_strip->component_fmt;
|
||||
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
ESP_RETURN_ON_FALSE(component_fmt.format.num_components == 4, ESP_ERR_INVALID_ARG, TAG, "led doesn't have 4 components");
|
||||
|
||||
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||
uint8_t *pixel_buf = rmt_strip->pixel_buf;
|
||||
|
||||
pixel_buf[start + component_fmt.format.r_pos] = red & 0xFF;
|
||||
pixel_buf[start + component_fmt.format.g_pos] = green & 0xFF;
|
||||
pixel_buf[start + component_fmt.format.b_pos] = blue & 0xFF;
|
||||
pixel_buf[start + component_fmt.format.w_pos] = white & 0xFF;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_refresh_async(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
|
||||
ESP_RETURN_ON_ERROR(rmt_enable(rmt_strip->rmt_chan), TAG, "enable RMT channel failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_transmit(rmt_strip->rmt_chan, rmt_strip->strip_encoder, rmt_strip->pixel_buf,
|
||||
rmt_strip->strip_len * rmt_strip->bytes_per_pixel, &rmt_strip->tx_conf), TAG, "transmit pixels by RMT failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_wait_for_done(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "wait for RMT done failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_refresh(led_strip_t *strip)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(led_strip_rmt_refresh_async(strip), TAG, "refresh async failed");
|
||||
ESP_RETURN_ON_ERROR(led_strip_rmt_wait_for_done(strip), TAG, "wait for done failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_clear(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
// Write zero to turn off all leds
|
||||
memset(rmt_strip->pixel_buf, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel);
|
||||
return led_strip_rmt_refresh(strip);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_rmt_del(led_strip_t *strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||
ESP_RETURN_ON_ERROR(rmt_del_channel(rmt_strip->rmt_chan), TAG, "delete RMT channel failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_del_encoder(rmt_strip->strip_encoder), TAG, "delete strip encoder failed");
|
||||
if (rmt_strip->pixel_buf_allocated_internally) {
|
||||
free(rmt_strip->pixel_buf);
|
||||
}
|
||||
free(rmt_strip);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip)
|
||||
{
|
||||
led_strip_rmt_obj *rmt_strip = NULL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
FASTLED_UNUSED(ret);
|
||||
ESP_GOTO_ON_FALSE(led_config && rmt_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
led_color_component_format_t component_fmt = led_config->color_component_format;
|
||||
// If R/G/B order is not specified, set default GRB order as fallback
|
||||
if (component_fmt.format_id == 0) {
|
||||
component_fmt = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
|
||||
}
|
||||
// check the validation of the color component format
|
||||
uint8_t mask = 0;
|
||||
if (component_fmt.format.num_components == 3) {
|
||||
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos);
|
||||
// Check for invalid values
|
||||
ESP_RETURN_ON_FALSE(mask == 0x07, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||
} else if (component_fmt.format.num_components == 4) {
|
||||
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos) | BIT(component_fmt.format.w_pos);
|
||||
// Check for invalid values
|
||||
ESP_RETURN_ON_FALSE(mask == 0x0F, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||
} else {
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "invalid number of color components: %d", component_fmt.format.num_components);
|
||||
}
|
||||
// TODO: we assume each color component is 8 bits, may need to support other configurations in the future, e.g. 10bits per color component?
|
||||
uint8_t bytes_per_pixel = component_fmt.format.num_components;
|
||||
rmt_strip = calloc(1, sizeof(led_strip_rmt_obj));
|
||||
ESP_GOTO_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for rmt strip");
|
||||
|
||||
// Check if external pixel buffer is provided
|
||||
if (led_config->external_pixel_buf != NULL) {
|
||||
// Use the external pixel buffer provided by caller
|
||||
rmt_strip->pixel_buf = led_config->external_pixel_buf;
|
||||
rmt_strip->pixel_buf_allocated_internally = false;
|
||||
} else {
|
||||
// Allocate our own pixel buffer
|
||||
rmt_strip->pixel_buf = calloc(led_config->max_leds * bytes_per_pixel, sizeof(uint8_t));
|
||||
ESP_GOTO_ON_FALSE(rmt_strip->pixel_buf, ESP_ERR_NO_MEM, err, TAG, "no mem for pixel buffer");
|
||||
rmt_strip->pixel_buf_allocated_internally = true;
|
||||
}
|
||||
uint32_t resolution = rmt_config->resolution_hz ? rmt_config->resolution_hz : LED_STRIP_RMT_DEFAULT_RESOLUTION;
|
||||
|
||||
// for backward compatibility, if the user does not set the clk_src, use the default value
|
||||
rmt_clock_source_t clk_src = RMT_CLK_SRC_DEFAULT;
|
||||
if (rmt_config->clk_src) {
|
||||
clk_src = rmt_config->clk_src;
|
||||
}
|
||||
size_t mem_block_symbols = LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS;
|
||||
// override the default value if the user sets it
|
||||
if (rmt_config->mem_block_symbols) {
|
||||
mem_block_symbols = rmt_config->mem_block_symbols;
|
||||
}
|
||||
rmt_tx_channel_config_t rmt_chan_config = {
|
||||
.clk_src = clk_src,
|
||||
.gpio_num = led_config->strip_gpio_num,
|
||||
.mem_block_symbols = mem_block_symbols,
|
||||
.resolution_hz = resolution,
|
||||
.trans_queue_depth = LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE,
|
||||
.flags.with_dma = rmt_config->flags.with_dma,
|
||||
.flags.invert_out = led_config->flags.invert_out,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&rmt_chan_config, &rmt_strip->rmt_chan), err, TAG, "create RMT TX channel failed");
|
||||
|
||||
led_strip_encoder_config_t strip_encoder_conf = {
|
||||
.resolution = resolution,
|
||||
.led_model = led_config->led_model,
|
||||
.timings = led_config->timings,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_led_strip_encoder(&strip_encoder_conf, &rmt_strip->strip_encoder), err, TAG, "create LED strip encoder failed");
|
||||
|
||||
rmt_strip->component_fmt = component_fmt;
|
||||
rmt_strip->bytes_per_pixel = bytes_per_pixel;
|
||||
rmt_strip->strip_len = led_config->max_leds;
|
||||
rmt_strip->tx_conf = (rmt_transmit_config_t){
|
||||
.loop_count = 0,
|
||||
};
|
||||
rmt_strip->base.set_pixel = led_strip_rmt_set_pixel;
|
||||
rmt_strip->base.set_pixel_rgbw = led_strip_rmt_set_pixel_rgbw;
|
||||
rmt_strip->base.refresh = led_strip_rmt_refresh;
|
||||
rmt_strip->base.refresh_async = led_strip_rmt_refresh_async;
|
||||
rmt_strip->base.refresh_wait_done = led_strip_rmt_wait_for_done;
|
||||
rmt_strip->base.clear = led_strip_rmt_clear;
|
||||
rmt_strip->base.del = led_strip_rmt_del;
|
||||
|
||||
*ret_strip = &rmt_strip->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (rmt_strip) {
|
||||
if (rmt_strip->rmt_chan) {
|
||||
rmt_del_channel(rmt_strip->rmt_chan);
|
||||
}
|
||||
if (rmt_strip->strip_encoder) {
|
||||
rmt_del_encoder(rmt_strip->strip_encoder);
|
||||
}
|
||||
if (rmt_strip->pixel_buf_allocated_internally) {
|
||||
free(rmt_strip->pixel_buf);
|
||||
}
|
||||
free(rmt_strip);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // FASTLED_RMT5
|
||||
|
||||
#endif // ESP32
|
||||
195
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_rmt_encoder.c
vendored
Normal file
195
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_rmt_encoder.c
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
#include "enabled.h"
|
||||
|
||||
#if FASTLED_RMT5
|
||||
|
||||
#include "fl/unused.h"
|
||||
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "led_strip_rmt_encoder.h"
|
||||
|
||||
static const char *TAG = "led_rmt_encoder";
|
||||
|
||||
typedef struct {
|
||||
rmt_encoder_t base;
|
||||
rmt_encoder_t *bytes_encoder;
|
||||
rmt_encoder_t *copy_encoder;
|
||||
int state;
|
||||
rmt_symbol_word_t reset_code;
|
||||
} rmt_led_strip_encoder_t;
|
||||
|
||||
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
|
||||
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
|
||||
rmt_encode_state_t session_state = 0;
|
||||
rmt_encode_state_t state = 0;
|
||||
size_t encoded_symbols = 0;
|
||||
switch (led_encoder->state) {
|
||||
case 0: // send RGB data
|
||||
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
|
||||
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||
led_encoder->state = 1; // switch to next state when current encoding session finished
|
||||
}
|
||||
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||
state |= RMT_ENCODING_MEM_FULL;
|
||||
goto out; // yield if there's no free space for encoding artifacts
|
||||
}
|
||||
// fall-through
|
||||
case 1: // send reset code
|
||||
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code,
|
||||
sizeof(led_encoder->reset_code), &session_state);
|
||||
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||
led_encoder->state = 0; // back to the initial encoding session
|
||||
state |= RMT_ENCODING_COMPLETE;
|
||||
}
|
||||
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||
state |= RMT_ENCODING_MEM_FULL;
|
||||
goto out; // yield if there's no free space for encoding artifacts
|
||||
}
|
||||
}
|
||||
out:
|
||||
*ret_state = state;
|
||||
return encoded_symbols;
|
||||
}
|
||||
|
||||
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||
rmt_del_encoder(led_encoder->copy_encoder);
|
||||
free(led_encoder);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
|
||||
{
|
||||
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||
rmt_encoder_reset(led_encoder->bytes_encoder);
|
||||
rmt_encoder_reset(led_encoder->copy_encoder);
|
||||
led_encoder->state = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
FASTLED_UNUSED(ret);
|
||||
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(config->led_model < LED_MODEL_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led model");
|
||||
// Create a temporary config with the same base values
|
||||
led_strip_encoder_config_t timing_config = *config;
|
||||
const bool has_encoder_timings = config->timings.t0h || config->timings.t0l || config->timings.t1h || config->timings.t1l || config->timings.reset;
|
||||
|
||||
if (!has_encoder_timings) {
|
||||
// Set the timing values based on LED model
|
||||
if (config->led_model == LED_MODEL_SK6812) {
|
||||
timing_config.timings = (led_strip_encoder_timings_t) {
|
||||
.t0h = 300, // 0.3us = 300ns
|
||||
.t0l = 900, // 0.9us = 900ns
|
||||
.t1h = 600, // 0.6us = 600ns
|
||||
.t1l = 600, // 0.6us = 600ns
|
||||
.reset = 280 // 280us
|
||||
};
|
||||
} else if (config->led_model == LED_MODEL_WS2812) {
|
||||
timing_config.timings = (led_strip_encoder_timings_t) {
|
||||
.t0h = 300, // 0.3us = 300ns
|
||||
.t0l = 900, // 0.9us = 900ns
|
||||
.t1h = 900, // 0.9us = 900ns
|
||||
.t1l = 300, // 0.3us = 300ns
|
||||
.reset = 280 // 280us
|
||||
};
|
||||
} else if (config->led_model == LED_MODEL_WS2811) {
|
||||
timing_config.timings = (led_strip_encoder_timings_t) {
|
||||
.t0h = 500, // 0.5us = 500ns
|
||||
.t0l = 2000, // 2.0us = 2000ns
|
||||
.t1h = 1200, // 1.2us = 1200ns
|
||||
.t1l = 1300, // 1.3us = 1300ns
|
||||
.reset = 50 // 50us
|
||||
};
|
||||
} else {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Delegate to the timing-based encoder creation
|
||||
return rmt_new_led_strip_encoder_with_timings(&timing_config, ret_encoder);
|
||||
|
||||
err:
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t rmt_new_led_strip_encoder_with_timings(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder) {
|
||||
esp_err_t ret = ESP_OK;
|
||||
FASTLED_UNUSED(ret);
|
||||
rmt_led_strip_encoder_t *led_encoder = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t));
|
||||
ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder");
|
||||
led_encoder->base.encode = rmt_encode_led_strip;
|
||||
led_encoder->base.del = rmt_del_led_strip_encoder;
|
||||
led_encoder->base.reset = rmt_led_strip_encoder_reset;
|
||||
|
||||
// Convert nanosecond timings to ticks using resolution
|
||||
rmt_bytes_encoder_config_t bytes_encoder_config = {
|
||||
.bit0 = {
|
||||
.level0 = 1,
|
||||
.duration0 = (float)config->timings.t0h * config->resolution / 1000000000, // Convert ns to ticks
|
||||
.level1 = 0,
|
||||
.duration1 = (float)config->timings.t0l * config->resolution / 1000000000,
|
||||
},
|
||||
.bit1 = {
|
||||
.level0 = 1,
|
||||
.duration0 = (float)config->timings.t1h * config->resolution / 1000000000,
|
||||
.level1 = 0,
|
||||
.duration1 = (float)config->timings.t1l * config->resolution / 1000000000,
|
||||
},
|
||||
.flags.msb_first = 1
|
||||
};
|
||||
|
||||
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
|
||||
rmt_copy_encoder_config_t copy_encoder_config = {};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
|
||||
|
||||
// Convert reset time from microseconds to ticks
|
||||
uint32_t reset_ticks = config->resolution / 1000000 * config->timings.reset / 2;
|
||||
led_encoder->reset_code = (rmt_symbol_word_t) {
|
||||
.level0 = 0,
|
||||
.duration0 = reset_ticks,
|
||||
.level1 = 0,
|
||||
.duration1 = reset_ticks,
|
||||
};
|
||||
|
||||
*ret_encoder = &led_encoder->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (led_encoder) {
|
||||
if (led_encoder->bytes_encoder) {
|
||||
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||
}
|
||||
if (led_encoder->copy_encoder) {
|
||||
rmt_del_encoder(led_encoder->copy_encoder);
|
||||
}
|
||||
free(led_encoder);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#endif // FASTLED_RMT5
|
||||
|
||||
#endif // ESP32
|
||||
52
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_rmt_encoder.h
vendored
Normal file
52
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_rmt_encoder.h
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "fl/stdint.h"
|
||||
#include "driver/rmt_encoder.h"
|
||||
#include "led_strip_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Type of led strip encoder configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t resolution; /*!< Encoder resolution, in Hz */
|
||||
led_model_t led_model; /*!< LED model */
|
||||
led_strip_encoder_timings_t timings; /*!< Encoder timings */
|
||||
} led_strip_encoder_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create RMT encoder for encoding LED strip pixels into RMT symbols
|
||||
*
|
||||
* @param[in] config Encoder configuration
|
||||
* @param[out] ret_encoder Returned encoder handle
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG for any invalid arguments
|
||||
* - ESP_ERR_NO_MEM out of memory when creating led strip encoder
|
||||
* - ESP_OK if creating encoder successfully
|
||||
*/
|
||||
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
|
||||
|
||||
/**
|
||||
* @brief Create RMT encoder for encoding LED strip pixels into RMT symbols with custom timings
|
||||
*
|
||||
* @param[in] config Encoder configuration including custom timings
|
||||
* @param[out] ret_encoder Returned encoder handle
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG for any invalid arguments
|
||||
* - ESP_ERR_NO_MEM out of memory when creating led strip encoder
|
||||
* - ESP_OK if creating encoder successfully
|
||||
*/
|
||||
esp_err_t rmt_new_led_strip_encoder_with_timings(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
47
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_spi.h
vendored
Normal file
47
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_spi.h
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "fl/stdint.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "led_strip_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LED Strip SPI specific configuration
|
||||
*/
|
||||
typedef struct {
|
||||
spi_clock_source_t clk_src; /*!< SPI clock source */
|
||||
spi_host_device_t spi_bus; /*!< SPI bus ID. Which buses are available depends on the specific chip */
|
||||
struct {
|
||||
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
|
||||
} flags; /*!< Extra driver flags */
|
||||
} led_strip_spi_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create LED strip based on SPI MOSI channel
|
||||
*
|
||||
* @note Although only the MOSI line is used for generating the signal, the whole SPI bus can't be used for other purposes.
|
||||
*
|
||||
* @param led_config LED strip configuration
|
||||
* @param spi_config SPI specific configuration
|
||||
* @param ret_strip Returned LED strip handle
|
||||
* @return
|
||||
* - ESP_OK: create LED strip handle successfully
|
||||
* - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
|
||||
* - ESP_ERR_NOT_SUPPORTED: create LED strip handle failed because of unsupported configuration
|
||||
* - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
|
||||
* - ESP_FAIL: create LED strip handle failed because some other error
|
||||
*/
|
||||
esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
302
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_spi_dev.c
vendored
Normal file
302
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_spi_dev.c
vendored
Normal file
@@ -0,0 +1,302 @@
|
||||
#ifdef ESP32
|
||||
|
||||
#include "enabled.h"
|
||||
|
||||
#if FASTLED_ESP32_HAS_CLOCKLESS_SPI
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "soc/spi_periph.h"
|
||||
#include "led_strip.h"
|
||||
#include "led_strip_interface.h"
|
||||
#include "fl/unused.h"
|
||||
|
||||
#define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution
|
||||
#define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4
|
||||
|
||||
#define SPI_BYTES_PER_COLOR_BYTE 3
|
||||
#define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8)
|
||||
|
||||
// #define FASTLED_ESP32_SPI_HACK_1949 0
|
||||
|
||||
// Work-around for: https://github.com/FastLED/FastLED/issues/1949
|
||||
// Looks like the SPI controller provided by espressif will attempt
|
||||
// to free memory it doesn't own. This hack fixes this but you'll
|
||||
// need to make sure you wait long enough for the transaction to complete.
|
||||
#ifndef FASTLED_ESP32_SPI_HACK_NO_TRANSACTION_WAIT
|
||||
#define FASTLED_ESP32_SPI_HACK_NO_TRANSACTION_WAIT 0
|
||||
#endif
|
||||
|
||||
static const char *TAG = "led_strip_spi";
|
||||
|
||||
typedef struct {
|
||||
led_strip_t base;
|
||||
spi_host_device_t spi_host;
|
||||
spi_device_handle_t spi_device;
|
||||
uint32_t strip_len;
|
||||
uint8_t bytes_per_pixel;
|
||||
led_color_component_format_t component_fmt;
|
||||
spi_transaction_t tx_trans; // Transaction object stored in the controller
|
||||
bool trans_pending; // Flag to track if transaction is pending
|
||||
uint8_t pixel_buf[];
|
||||
} led_strip_spi_obj;
|
||||
|
||||
// please make sure to zero-initialize the buf before calling this function
|
||||
static void __led_strip_spi_bit(uint8_t data, uint8_t *buf)
|
||||
{
|
||||
// Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110
|
||||
// So a color byte occupies 3 bytes of SPI.
|
||||
*(buf + 2) |= data & BIT(0) ? BIT(2) | BIT(1) : BIT(2);
|
||||
*(buf + 2) |= data & BIT(1) ? BIT(5) | BIT(4) : BIT(5);
|
||||
*(buf + 2) |= data & BIT(2) ? BIT(7) : 0x00;
|
||||
*(buf + 1) |= BIT(0);
|
||||
*(buf + 1) |= data & BIT(3) ? BIT(3) | BIT(2) : BIT(3);
|
||||
*(buf + 1) |= data & BIT(4) ? BIT(6) | BIT(5) : BIT(6);
|
||||
*(buf + 0) |= data & BIT(5) ? BIT(1) | BIT(0) : BIT(1);
|
||||
*(buf + 0) |= data & BIT(6) ? BIT(4) | BIT(3) : BIT(4);
|
||||
*(buf + 0) |= data & BIT(7) ? BIT(7) | BIT(6) : BIT(7);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
// 3 pixels take 72bits(9bytes)
|
||||
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
|
||||
uint8_t *pixel_buf = spi_strip->pixel_buf;
|
||||
led_color_component_format_t component_fmt = spi_strip->component_fmt;
|
||||
memset(pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||
|
||||
__led_strip_spi_bit(red, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.r_pos]);
|
||||
__led_strip_spi_bit(green, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.g_pos]);
|
||||
__led_strip_spi_bit(blue, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.b_pos]);
|
||||
if (component_fmt.format.num_components > 3) {
|
||||
__led_strip_spi_bit(0, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.w_pos]);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
led_color_component_format_t component_fmt = spi_strip->component_fmt;
|
||||
ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||
ESP_RETURN_ON_FALSE(component_fmt.format.num_components == 4, ESP_ERR_INVALID_ARG, TAG, "led doesn't have 4 components");
|
||||
|
||||
// LED_PIXEL_FORMAT_GRBW takes 96bits(12bytes)
|
||||
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
|
||||
uint8_t *pixel_buf = spi_strip->pixel_buf;
|
||||
memset(pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||
|
||||
__led_strip_spi_bit(red, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.r_pos]);
|
||||
__led_strip_spi_bit(green, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.g_pos]);
|
||||
__led_strip_spi_bit(blue, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.b_pos]);
|
||||
__led_strip_spi_bit(white, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.w_pos]);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t spi_led_strip_refresh_async(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
|
||||
// Wait for any pending transaction to complete before starting a new one
|
||||
if (spi_strip->trans_pending) {
|
||||
spi_transaction_t* trans_ptr;
|
||||
spi_device_get_trans_result(spi_strip->spi_device, &trans_ptr, pdMS_TO_TICKS(1000));
|
||||
spi_strip->trans_pending = false;
|
||||
}
|
||||
|
||||
// Initialize the transaction object stored in the controller
|
||||
memset(&spi_strip->tx_trans, 0, sizeof(spi_strip->tx_trans));
|
||||
spi_strip->tx_trans.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE;
|
||||
spi_strip->tx_trans.tx_buffer = spi_strip->pixel_buf;
|
||||
spi_strip->tx_trans.rx_buffer = NULL;
|
||||
|
||||
// Queue the transaction
|
||||
esp_err_t ret = spi_device_queue_trans(spi_strip->spi_device, &spi_strip->tx_trans, portMAX_DELAY);
|
||||
if (ret == ESP_OK) {
|
||||
spi_strip->trans_pending = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t spi_led_strip_refresh_wait_done(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
|
||||
// Only wait if there's a pending transaction
|
||||
if (spi_strip->trans_pending) {
|
||||
spi_transaction_t* trans_ptr;
|
||||
esp_err_t ret = spi_device_get_trans_result(spi_strip->spi_device, &trans_ptr, pdMS_TO_TICKS(1000));
|
||||
if (ret == ESP_OK) {
|
||||
spi_strip->trans_pending = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_refresh(led_strip_t *strip)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(spi_led_strip_refresh_async(strip), TAG, "refresh async failed");
|
||||
ESP_RETURN_ON_ERROR(spi_led_strip_refresh_wait_done(strip), TAG, "wait for done failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
static esp_err_t led_strip_spi_clear(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
//Write zero to turn off all leds
|
||||
memset(spi_strip->pixel_buf, 0, spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||
uint8_t *buf = spi_strip->pixel_buf;
|
||||
for (int index = 0; index < spi_strip->strip_len * spi_strip->bytes_per_pixel; index++) {
|
||||
__led_strip_spi_bit(0, buf);
|
||||
buf += SPI_BYTES_PER_COLOR_BYTE;
|
||||
}
|
||||
|
||||
return led_strip_spi_refresh(strip);
|
||||
}
|
||||
|
||||
static esp_err_t led_strip_spi_del(led_strip_t *strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||
|
||||
// Wait for any pending transaction to complete before destroying the strip
|
||||
if (spi_strip->trans_pending) {
|
||||
spi_transaction_t* trans_ptr;
|
||||
spi_device_get_trans_result(spi_strip->spi_device, &trans_ptr, pdMS_TO_TICKS(1000));
|
||||
spi_strip->trans_pending = false;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(spi_bus_remove_device(spi_strip->spi_device), TAG, "delete spi device failed");
|
||||
ESP_RETURN_ON_ERROR(spi_bus_free(spi_strip->spi_host), TAG, "free spi bus failed");
|
||||
|
||||
free(spi_strip);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip)
|
||||
{
|
||||
led_strip_spi_obj *spi_strip = NULL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
FASTLED_UNUSED(ret);
|
||||
ESP_GOTO_ON_FALSE(led_config && spi_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
led_color_component_format_t component_fmt = led_config->color_component_format;
|
||||
// If R/G/B order is not specified, set default GRB order as fallback
|
||||
if (component_fmt.format_id == 0) {
|
||||
component_fmt = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
|
||||
}
|
||||
// check the validation of the color component format
|
||||
uint8_t mask = 0;
|
||||
if (component_fmt.format.num_components == 3) {
|
||||
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos);
|
||||
// Check for invalid values
|
||||
ESP_RETURN_ON_FALSE(mask == 0x07, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||
} else if (component_fmt.format.num_components == 4) {
|
||||
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos) | BIT(component_fmt.format.w_pos);
|
||||
// Check for invalid values
|
||||
ESP_RETURN_ON_FALSE(mask == 0x0F, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||
} else {
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "invalid number of color components: %d", component_fmt.format.num_components);
|
||||
}
|
||||
// TODO: we assume each color component is 8 bits, may need to support other configurations in the future, e.g. 10bits per color component?
|
||||
uint8_t bytes_per_pixel = component_fmt.format.num_components;
|
||||
uint32_t mem_caps = MALLOC_CAP_DEFAULT;
|
||||
if (spi_config->flags.with_dma) {
|
||||
// DMA buffer must be placed in internal SRAM
|
||||
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
|
||||
}
|
||||
spi_strip = heap_caps_calloc(1, sizeof(led_strip_spi_obj) + led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE, mem_caps);
|
||||
|
||||
ESP_GOTO_ON_FALSE(spi_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for spi strip");
|
||||
|
||||
spi_strip->spi_host = spi_config->spi_bus;
|
||||
// for backward compatibility, if the user does not set the clk_src, use the default value
|
||||
spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT;
|
||||
if (spi_config->clk_src) {
|
||||
clk_src = spi_config->clk_src;
|
||||
}
|
||||
|
||||
spi_bus_config_t spi_bus_cfg = {
|
||||
.mosi_io_num = led_config->strip_gpio_num,
|
||||
//Only use MOSI to generate the signal, set -1 when other pins are not used.
|
||||
.miso_io_num = -1,
|
||||
.sclk_io_num = -1,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(spi_bus_initialize(spi_strip->spi_host, &spi_bus_cfg, spi_config->flags.with_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED), err, TAG, "create SPI bus failed");
|
||||
|
||||
if (led_config->flags.invert_out == true) {
|
||||
esp_rom_gpio_connect_out_signal(led_config->strip_gpio_num, spi_periph_signal[spi_strip->spi_host].spid_out, true, false);
|
||||
}
|
||||
|
||||
spi_device_interface_config_t spi_dev_cfg = {
|
||||
.clock_source = clk_src,
|
||||
.command_bits = 0,
|
||||
.address_bits = 0,
|
||||
.dummy_bits = 0,
|
||||
.clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION,
|
||||
.mode = 0,
|
||||
//set -1 when CS is not used
|
||||
.spics_io_num = -1,
|
||||
.queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE,
|
||||
};
|
||||
|
||||
ESP_GOTO_ON_ERROR(spi_bus_add_device(spi_strip->spi_host, &spi_dev_cfg, &spi_strip->spi_device), err, TAG, "Failed to add spi device");
|
||||
//ensure the reset time is enough
|
||||
esp_rom_delay_us(10);
|
||||
int clock_resolution_khz = 0;
|
||||
spi_device_get_actual_freq(spi_strip->spi_device, &clock_resolution_khz);
|
||||
// TODO: ideally we should decide the SPI_BYTES_PER_COLOR_BYTE by the real clock resolution
|
||||
// But now, let's fixed the resolution, the downside is, we don't support a clock source whose frequency is not multiple of LED_STRIP_SPI_DEFAULT_RESOLUTION
|
||||
// clock_resolution between 2.2MHz to 2.8MHz is supported
|
||||
ESP_GOTO_ON_FALSE((clock_resolution_khz < LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000 + 300) && (clock_resolution_khz > LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000 - 300), ESP_ERR_NOT_SUPPORTED, err,
|
||||
TAG, "unsupported clock resolution:%dKHz", clock_resolution_khz);
|
||||
|
||||
spi_strip->component_fmt = component_fmt;
|
||||
spi_strip->bytes_per_pixel = bytes_per_pixel;
|
||||
spi_strip->strip_len = led_config->max_leds;
|
||||
spi_strip->trans_pending = false; // Initialize transaction pending flag
|
||||
spi_strip->base.set_pixel = led_strip_spi_set_pixel;
|
||||
spi_strip->base.set_pixel_rgbw = led_strip_spi_set_pixel_rgbw;
|
||||
spi_strip->base.refresh = led_strip_spi_refresh;
|
||||
spi_strip->base.refresh_async = spi_led_strip_refresh_async;
|
||||
spi_strip->base.refresh_wait_done = spi_led_strip_refresh_wait_done;
|
||||
spi_strip->base.clear = led_strip_spi_clear;
|
||||
spi_strip->base.del = led_strip_spi_del;
|
||||
|
||||
*ret_strip = &spi_strip->base;
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (spi_strip) {
|
||||
if (spi_strip->spi_device) {
|
||||
spi_bus_remove_device(spi_strip->spi_device);
|
||||
}
|
||||
if (spi_strip->spi_host) {
|
||||
spi_bus_free(spi_strip->spi_host);
|
||||
}
|
||||
free(spi_strip);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#endif // FASTLED_ESP_HAS_CLOCKLESS_SPI
|
||||
|
||||
#endif // ESP32
|
||||
85
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_types.h
vendored
Normal file
85
libraries/FastLED/src/third_party/espressif/led_strip/src/led_strip_types.h
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of LED strip handle
|
||||
*/
|
||||
typedef struct led_strip_t *led_strip_handle_t;
|
||||
|
||||
/**
|
||||
* @brief LED strip model
|
||||
* @note Different led model may have different timing parameters, so we need to distinguish them.
|
||||
*/
|
||||
typedef enum {
|
||||
LED_MODEL_WS2812, /*!< LED strip model: WS2812 */
|
||||
LED_MODEL_SK6812, /*!< LED strip model: SK6812 */
|
||||
LED_MODEL_WS2811, /*!< LED strip model: WS2811 */
|
||||
LED_MODEL_INVALID /*!< Invalid LED strip model */
|
||||
} led_model_t;
|
||||
|
||||
/**
|
||||
* @brief LED strip encoder timings.
|
||||
* @note The timings are in nanoseconds. A zero filled structure will represent no timings given.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t t0h; /*!< High time for 0 bit, */
|
||||
uint32_t t1h; /*!< High time for 1 bit */
|
||||
uint32_t t0l; /*!< Low time for 0 bit */
|
||||
uint32_t t1l; /*!< Low time for 1 bit */
|
||||
uint32_t reset; /*!< Reset time, microseconds */
|
||||
} led_strip_encoder_timings_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief LED color component format
|
||||
* @note The format is used to specify the order of color components in each pixel, also the number of color components.
|
||||
*/
|
||||
typedef union {
|
||||
struct format_layout {
|
||||
uint32_t r_pos: 2; /*!< Position of the red channel in the color order: 0~3 */
|
||||
uint32_t g_pos: 2; /*!< Position of the green channel in the color order: 0~3 */
|
||||
uint32_t b_pos: 2; /*!< Position of the blue channel in the color order: 0~3 */
|
||||
uint32_t w_pos: 2; /*!< Position of the white channel in the color order: 0~3 */
|
||||
uint32_t reserved: 21; /*!< Reserved */
|
||||
uint32_t num_components: 3; /*!< Number of color components per pixel: 3 or 4. If set to 0, it will fallback to 3 */
|
||||
} format; /*!< Format layout */
|
||||
uint32_t format_id; /*!< Format ID */
|
||||
} led_color_component_format_t;
|
||||
|
||||
/// Helper macros to set the color component format
|
||||
#define LED_STRIP_COLOR_COMPONENT_FMT_GRB (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .num_components = 3}}
|
||||
#define LED_STRIP_COLOR_COMPONENT_FMT_GRBW (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .num_components = 4}}
|
||||
#define LED_STRIP_COLOR_COMPONENT_FMT_RGB (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .num_components = 3}}
|
||||
#define LED_STRIP_COLOR_COMPONENT_FMT_RGBW (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .num_components = 4}}
|
||||
|
||||
/**
|
||||
* @brief LED Strip common configurations
|
||||
* The common configurations are not specific to any backend peripheral.
|
||||
*/
|
||||
typedef struct {
|
||||
int strip_gpio_num; /*!< GPIO number that used by LED strip */
|
||||
uint32_t max_leds; /*!< Maximum number of LEDs that can be controlled in a single strip */
|
||||
led_model_t led_model; /*!< Specifies the LED strip model (e.g., WS2812, SK6812) */
|
||||
led_color_component_format_t color_component_format; /*!< Specifies the order of color components in each pixel.
|
||||
Use helper macros like `LED_STRIP_COLOR_COMPONENT_FMT_GRB` to set the format */
|
||||
uint8_t *external_pixel_buf; /*!< Optional external pixel buffer. If NULL, driver will allocate its own buffer */
|
||||
/*!< LED strip extra driver flags */
|
||||
struct led_strip_extra_flags {
|
||||
uint32_t invert_out: 1; /*!< Invert output signal */
|
||||
} flags; /*!< Extra driver flags */
|
||||
led_strip_encoder_timings_t timings; /*!< Encoder timings */
|
||||
} led_strip_config_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
6
libraries/FastLED/src/third_party/object_fled/readme
vendored
Normal file
6
libraries/FastLED/src/third_party/object_fled/readme
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
https://github.com/KurtMF/ObjectFLED
|
||||
|
||||
Version 1.10: https://github.com/KurtMF/ObjectFLED/releases/tag/v1.1.0
|
||||
downloaded date: 2025-Jan. 6th
|
||||
|
||||
Light modifications have been made to this library.
|
||||
206
libraries/FastLED/src/third_party/object_fled/src/ObjectFLED.h
vendored
Normal file
206
libraries/FastLED/src/third_party/object_fled/src/ObjectFLED.h
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
/* ObjectFLED - Teensy 4.x DMA to all pins for independent control of large and
|
||||
multiple LED display objects
|
||||
|
||||
Copyright (c) 2024 Kurt Funderburg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
OctoWS2811 library code was well-studied and substantial portions of it used
|
||||
to implement high-speed, non-blocking DMA for LED signal output in this library.
|
||||
See ObjectFLED.cpp for a summary of changes made to the original OctoWS2811.
|
||||
|
||||
OctoWS2811 - High Performance WS2811 LED Display Library
|
||||
Copyright (c) 2013 Paul Stoffregen, PJRC.COM, LLC
|
||||
http://www.pjrc.com/teensy/td_libs_OctoWS2811.html
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __IMXRT1062__
|
||||
#error "Sorry, ObjectFLED only works on Teensy 4.x boards."
|
||||
#endif
|
||||
#if TEENSYDUINO < 121
|
||||
#error "Teensyduino version 1.21 or later is required to compile this library."
|
||||
#endif
|
||||
#ifndef ObjectFLED_h
|
||||
#define ObjectFLED_h
|
||||
#include <WProgram.h>
|
||||
#include "DMAChannel.h"
|
||||
|
||||
//Experimentally found DSE=3, SPEED=0 gave best LED overclocking
|
||||
//boot defaults DSE=6, SPEED=2.
|
||||
#define OUTPUT_PAD_DSE 3 //Legal values 0-7
|
||||
#define OUTPUT_PAD_SPEED 0 //Legal values 0-3
|
||||
|
||||
// Ordinary RGB data is converted to GPIO bitmasks on-the-fly using
|
||||
// a transmit buffer sized for 2 DMA transfers. The larger this setting,
|
||||
// the more interrupt latency OctoWS2811 can tolerate, but the transmit
|
||||
// buffer grows in size. For good performance, the buffer should be kept
|
||||
// smaller than the half the Cortex-M7 data cache.
|
||||
//bitdata[B_P_D * 64] buffer holds data (10KB) for 80 LED bytes: 4DW * 8b = 32DW/LEDB = 96DW/LED
|
||||
//framebuffer_index = B_P_D * 2 = pointer to next block for transfer (80 LEDB / bitdata buffer)
|
||||
#define BYTES_PER_DMA 20 //= number of pairs of LEDB (40=80B) bitmasks in bitdata.
|
||||
|
||||
#define CORDER_RGB 0 //* WS2811, YF923
|
||||
#define CORDER_RBG 1
|
||||
#define CORDER_GRB 2 //* WS2811B, Most LED strips are wired this way
|
||||
#define CORDER_GBR 3 //*
|
||||
#define CORDER_BRG 4 //* Adafruit Product ID: 5984 As of November 5, 2024 - this strand has different 'internal' color ordering. It's now BRG not RGB,
|
||||
#define CORDER_BGR 5 //* Adafruit Dotstar LEDs SK9822 uses this CO but they use inverted start/stop bits
|
||||
#define CORDER_RGBW 6 //* Popular
|
||||
#define CORDER_RBGW 7
|
||||
#define CORDER_GRBW 8
|
||||
#define CORDER_GBRW 9
|
||||
#define CORDER_BRGW 10
|
||||
#define CORDER_BGRW 11 // Adafruit Dotstar LEDs SK9822 uses this CO but they use inverted start/stop bits
|
||||
#define CORDER_WRGB 12
|
||||
#define CORDER_WRBG 13
|
||||
#define CORDER_WGRB 14
|
||||
#define CORDER_WGBR 15
|
||||
#define CORDER_WBRG 16
|
||||
#define CORDER_WBGR 17
|
||||
#define CORDER_RWGB 18
|
||||
#define CORDER_RWBG 19
|
||||
#define CORDER_GWRB 20
|
||||
#define CORDER_GWBR 21
|
||||
#define CORDER_BWRG 22
|
||||
#define CORDER_BWGR 23
|
||||
#define CORDER_RGWB 24
|
||||
#define CORDER_RBWG 25
|
||||
#define CORDER_GRWB 26
|
||||
#define CORDER_GBWR 27
|
||||
#define CORDER_BRWG 28
|
||||
#define CORDER_BGWR 29
|
||||
|
||||
namespace fl {
|
||||
|
||||
|
||||
//Usage: ObjectFLED myCube ( Num_LEDs, *drawBuffer, LED_type, numPins, *pinList, serpentineNumber )
|
||||
class ObjectFLED {
|
||||
public:
|
||||
// Usage: ObjectFLED myCube ( Num_LEDs, *drawBuffer, LED_type, numPins, *pinList, serpentineNumber )
|
||||
// Example:
|
||||
// byte pinList[NUM_CHANNELS] = {1, 8, 14, 17, 24, 29, 20, 0, 15, 16, 18, 19, 21, 22, 23, 25};
|
||||
// byte pinListBlank[7] = {1, 8, 14, 17, 24, 29, 20};
|
||||
// CRGB testCube[NUM_PLANES][NUM_ROWS][PIX_PER_ROW];
|
||||
// CRGB blankLeds[7][8][8];
|
||||
// ObjectFLED leds(PIX_PER_ROW * NUM_ROWS * NUM_PLANES, testCube, CORDER_RGB, NUM_CHANNELS, pinList);
|
||||
// void setup() {
|
||||
// leds.begin(1.6, 72); //1.6 ocervlock factor, 72uS LED latch delay
|
||||
// leds.setBrightness(64);
|
||||
// }
|
||||
// void loop() {
|
||||
// leds.show();
|
||||
// delay(100);
|
||||
// }
|
||||
ObjectFLED(uint16_t numLEDs, void* drawBuf, uint8_t config, uint8_t numPins, const uint8_t* pinList, \
|
||||
uint8_t serpentine = 0);
|
||||
|
||||
~ObjectFLED() {
|
||||
// Wait for prior xmission to end, don't need to wait for latch time before deleting buffer
|
||||
while (micros() - update_begin_micros < numbytes * 8 * TH_TL / OC_FACTOR / 1000 + 5);
|
||||
delete frameBuffer;
|
||||
}
|
||||
|
||||
//begin() - Use defalut LED timing: 1.0 OC Factor, 1250 nS CLK (=800 KHz), 300 nS T0H, 750 nS T1H, 300 uS LED Latch Delay.
|
||||
void begin(void);
|
||||
|
||||
//begin(LED_Latch_Delay_uS) - sets the LED Latch Delay.
|
||||
void begin(uint16_t);
|
||||
|
||||
//begin(LED_Overclock_Factor, LED_Latch_Delay_uS) - divides default 1250 nS LED CLK (=800 KHz),
|
||||
// 300 nS T0H, 750 nS T1H; and optionally sets the LED Latch Delay.
|
||||
void begin(double, uint16_t = 300);
|
||||
|
||||
//begin(LED_CLK_nS, LED_T0H_nS, LED_T1H_nS, LED_Latch_Delay_uS) - specifies full LED waveform timing.
|
||||
void begin(uint16_t, uint16_t, uint16_t, uint16_t = 300);
|
||||
|
||||
void show(void);
|
||||
void waitForDmaToFinish() {
|
||||
while (!dma3.complete()) { // wait for dma to complete before reset/re-use
|
||||
delayMicroseconds(10);
|
||||
}
|
||||
}
|
||||
|
||||
int busy(void);
|
||||
|
||||
//Brightness values 0-255
|
||||
void setBrightness(uint8_t);
|
||||
|
||||
//Color Balance is 3-byte number in RGB order. Each byte is a brightness value for that color.
|
||||
void setBalance(uint32_t);
|
||||
|
||||
uint8_t getBrightness() { return brightness; }
|
||||
|
||||
uint32_t getBalance() { return colorBalance; }
|
||||
|
||||
private:
|
||||
static void isr(void);
|
||||
|
||||
void genFrameBuffer(uint32_t);
|
||||
|
||||
static uint8_t* frameBuffer; //isr()
|
||||
static uint32_t numbytes; //isr()
|
||||
static uint8_t numpins; //isr()
|
||||
static uint8_t pin_bitnum[NUM_DIGITAL_PINS]; //isr()
|
||||
static uint8_t pin_offset[NUM_DIGITAL_PINS]; //isr()
|
||||
DMAMEM static uint32_t bitdata[BYTES_PER_DMA * 64] __attribute__((used, aligned(32))); //isr()
|
||||
DMAMEM static uint32_t bitmask[4] __attribute__((used, aligned(32)));
|
||||
static DMAChannel dma1, dma2, dma3;
|
||||
static DMASetting dma2next;
|
||||
|
||||
uint32_t update_begin_micros = 0;
|
||||
uint8_t brightness = 255;
|
||||
uint32_t colorBalance = 0xFFFFFF;
|
||||
uint32_t rLevel = 65025;
|
||||
uint32_t gLevel = 65025;
|
||||
uint32_t bLevel = 65025;
|
||||
void* drawBuffer;
|
||||
uint16_t stripLen;
|
||||
uint8_t params;
|
||||
uint8_t pinlist[NUM_DIGITAL_PINS];
|
||||
uint16_t comp1load[3];
|
||||
uint8_t serpNumber;
|
||||
float OC_FACTOR = 1.0; //used to reduce period of LED output
|
||||
uint16_t TH_TL = 1250; //nS- period of LED output
|
||||
uint16_t T0H = 300; //nS- duration of T0H
|
||||
uint16_t T1H = 750; //nS- duration of T1H
|
||||
uint16_t LATCH_DELAY = 300; //uS time to hold output low for LED latch.
|
||||
|
||||
//for show context switch
|
||||
uint32_t bitmaskLocal[4];
|
||||
uint8_t numpinsLocal;
|
||||
uint8_t* frameBufferLocal;
|
||||
uint32_t numbytesLocal;
|
||||
uint8_t pin_bitnumLocal[NUM_DIGITAL_PINS];
|
||||
uint8_t pin_offsetLocal[NUM_DIGITAL_PINS];
|
||||
}; // class ObjectFLED
|
||||
|
||||
|
||||
//fadeToColorBy(RGB_array, LED_count, Color, FadeAmount)
|
||||
//Fades an RGB array towards the background color by amount.
|
||||
void fadeToColorBy(void*, uint16_t, uint32_t, uint8_t);
|
||||
|
||||
|
||||
//drawSquare(RGB_Array, LED_Rows, LED_Cols, Y_Corner, X_Corner, square_Size)
|
||||
//Draws square in a 2D RGB array with lower left corner at (Y_Corner, X_Corner).
|
||||
//Safe to specify -Y, -X corner, safe to draw a box which partially fits on LED plane.
|
||||
void drawSquare(void*, uint16_t, uint16_t, int, int, uint32_t, uint32_t);
|
||||
|
||||
} // namespace fl
|
||||
|
||||
#endif
|
||||
656
libraries/FastLED/src/third_party/object_fled/src/OjectFLED.cpp
vendored
Normal file
656
libraries/FastLED/src/third_party/object_fled/src/OjectFLED.cpp
vendored
Normal file
@@ -0,0 +1,656 @@
|
||||
/* ObjectFLED - Teensy 4.x DMA to all pins for independent control of large and
|
||||
multiple LED display objects
|
||||
|
||||
Copyright (c) 2024 Kurt Funderburg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
OctoWS2811 library code was well-studied and substantial portions of it used
|
||||
to implement high-speed, non-blocking DMA for LED signal output in this library.
|
||||
See below for a summary of changes made to the original OctoWS2811.
|
||||
|
||||
OctoWS2811 - High Performance WS2811 LED Display Library
|
||||
Copyright (c) 2013 Paul Stoffregen, PJRC.COM, LLC
|
||||
http://www.pjrc.com/teensy/td_libs_OctoWS2811.html
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
/*
|
||||
Teensy 4.0 pin - port assignments
|
||||
GPIO6List = { 0, 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 } //12 top, 4 bottom
|
||||
GPIO7List = { 6, 7, 8, 9, 10, 11, 12, 13, 32 } //8 top, 1 bottom
|
||||
GPIO8List = { 28, 30, 31, 34, 35, 36, 37, 38, 39 } //0 top, 9 bottom
|
||||
GOIO9List = { 2, 3, 4, 5, 29, 33 } //4 top, 2 bottom
|
||||
Teensy 4.1 pin - port assignments
|
||||
GPIO6List = { 0, 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 38, 39, 40, 41 } //20 top, 0 bottom
|
||||
GPIO7List = { 6, 7, 8, 9, 10, 11, 12, 13, 32, 34, 35, 36, 37 } //13 top, 0 bottom
|
||||
GPIO8List = { 28, 30, 31, 42, 43, 44, 45, 46, 47 } //3 top, 6 bottom
|
||||
GOIO9List = { 2, 3, 4, 5, 29, 33, 48, 49, 50, 51, 52, 53, 54 } //6 top, 7 bottom
|
||||
* 4 pin groups, 4 timer groups, !4 dma channel groups: only 1 DMA group (4 ch) maps to GPIO (DMAMUX
|
||||
* mapping) Also, only DMA ch0-3 have periodic mode for timer trigger (p77 manual). Separate objects
|
||||
* cannot DMA at once.
|
||||
*
|
||||
* CHANGES:
|
||||
* Moved some variables so class supports multiple instances with separate LED config params
|
||||
* Implemented context switching so multiple instances can show() independently
|
||||
* Re-parameterized TH_TL, T0H, T1H, OC_FACTOR; recalc time for latch at end of show()
|
||||
* Added genFrameBuffer() to implement RGB order, brightness, color balance, and serpentine
|
||||
* Added setBrightness(), setBalance()
|
||||
* FrameBuffer no longer passed in, constructor now creates buffer; destructor added
|
||||
* Added support for per-object setting of OC factor, TH+TL, T0H, T1H, and LATCH_DELAY in begin function
|
||||
* Set DSE=3, SPEED=0, SRE=0 on output pins per experiment & PJRC forum guidance
|
||||
* New default values for TH_TL, T0H, T1H, LATCH_DELAY to work with Audio lib and more LED types
|
||||
* Added wait for prior xmission to complete in destructor
|
||||
*/
|
||||
|
||||
#ifndef __IMXRT1062__
|
||||
// Do nothing for other platforms.
|
||||
#else
|
||||
#include "ObjectFLED.h"
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a)<(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
namespace fl {
|
||||
|
||||
volatile uint32_t framebuffer_index = 0; //isr()
|
||||
uint8_t* ObjectFLED::frameBuffer; //isr()
|
||||
uint32_t ObjectFLED::numbytes; //isr()
|
||||
uint8_t ObjectFLED::numpins; //isr()
|
||||
uint8_t ObjectFLED::pin_bitnum[NUM_DIGITAL_PINS]; //isr()
|
||||
uint8_t ObjectFLED::pin_offset[NUM_DIGITAL_PINS]; //isr()
|
||||
uint32_t ObjectFLED::bitdata[BYTES_PER_DMA * 64] __attribute__((used, aligned(32))); //isr()
|
||||
uint32_t ObjectFLED::bitmask[4] __attribute__((used, aligned(32)));
|
||||
|
||||
DMASetting ObjectFLED::dma2next;
|
||||
DMAChannel ObjectFLED::dma1;
|
||||
DMAChannel ObjectFLED::dma2;
|
||||
DMAChannel ObjectFLED::dma3;
|
||||
volatile bool dma_first;
|
||||
|
||||
|
||||
ObjectFLED::ObjectFLED(uint16_t numLEDs, void *drawBuf, uint8_t config, uint8_t numPins, \
|
||||
const uint8_t *pinList, uint8_t serpentine) {
|
||||
serpNumber = serpentine;
|
||||
drawBuffer = drawBuf;
|
||||
params = config;
|
||||
if (numPins > NUM_DIGITAL_PINS) numPins = NUM_DIGITAL_PINS;
|
||||
numpins = numPins; //static/isr
|
||||
stripLen = numLEDs / numpins;
|
||||
memcpy(pinlist, pinList, numpins);
|
||||
if ((params & 0x3F) < 6) {
|
||||
frameBuffer = new uint8_t[numLEDs * 3]; //static/isr
|
||||
numbytes = stripLen * 3; // RGB formats //static/isr
|
||||
}
|
||||
else {
|
||||
frameBuffer = new uint8_t[numLEDs * 4]; //static/isr
|
||||
numbytes = stripLen * 4; // RGBW formats //static/isr
|
||||
}
|
||||
|
||||
numpinsLocal = numPins;
|
||||
frameBufferLocal = frameBuffer;
|
||||
numbytesLocal = numbytes;
|
||||
} // ObjectFLED constructor
|
||||
|
||||
|
||||
extern "C" void xbar_connect(unsigned int input, unsigned int output); // in pwm.c
|
||||
static volatile uint32_t *standard_gpio_addr(volatile uint32_t *fastgpio) {
|
||||
return (volatile uint32_t *)((uint32_t)fastgpio - 0x01E48000);
|
||||
}
|
||||
|
||||
|
||||
void ObjectFLED::begin(uint16_t latchDelay) {
|
||||
LATCH_DELAY = latchDelay;
|
||||
begin();
|
||||
}
|
||||
|
||||
|
||||
void ObjectFLED::begin(double OCF, uint16_t latchDelay) {
|
||||
OC_FACTOR = (float)OCF;
|
||||
LATCH_DELAY = latchDelay;
|
||||
begin();
|
||||
}
|
||||
|
||||
|
||||
void ObjectFLED::begin(uint16_t period, uint16_t t0h, uint16_t t1h, uint16_t latchDelay) {
|
||||
TH_TL = period;
|
||||
T0H = t0h;
|
||||
T1H = t1h;
|
||||
LATCH_DELAY = latchDelay;
|
||||
begin();
|
||||
}
|
||||
|
||||
|
||||
// INPUT stripLen, frameBuffer, params, numPins, pinList
|
||||
// GPIOR bits set for pins[i] -> bitmask, pin_bitnum[i], pin_offset[i]
|
||||
// init timers, xbar to DMA, DMA bitdata -> GPIOR; clears frameBuffer (total LEDs * 3 bytes)
|
||||
void ObjectFLED::begin(void) {
|
||||
numpins = numpinsLocal; //needed to compute pin mask/offset & bitmask since static for isr
|
||||
// Set each pin's bitmask bit, store offset & bit# for pin
|
||||
memset(bitmask, 0, sizeof(bitmask));
|
||||
for (uint32_t i=0; i < numpins; i++) {
|
||||
uint8_t pin = pinlist[i];
|
||||
if (pin >= NUM_DIGITAL_PINS) continue; // ignore illegal pins
|
||||
uint8_t bit = digitalPinToBit(pin); // pin's bit index in word port DR
|
||||
// which GPIO R controls this pin: 0-3 map to GPIO6-9 then map to DMA compat GPIO1-4
|
||||
uint8_t offset = ((uint32_t)portOutputRegister(pin) - (uint32_t)&GPIO6_DR) >> 14;
|
||||
if (offset > 3) continue; //ignore unknown pins
|
||||
pin_bitnum[i] = bit; //static/isr
|
||||
pin_offset[i] = offset; //static/isr
|
||||
uint32_t mask = 1 << bit; //mask32 = bit set @position in GPIO DR
|
||||
bitmask[offset] |= mask; //bitmask32[0..3] = collective pin bit masks for each GPIO DR
|
||||
//bit7:6 SPEED; bit 5:3 DSE; bit0 SRE (default SPEED = 0b10; def. DSE = 0b110)
|
||||
*portControlRegister(pin) &= ~0xF9; //clear SPEED, DSE, SRE
|
||||
*portControlRegister(pin) |= ((OUTPUT_PAD_SPEED & 0x3) << 6) | \
|
||||
((OUTPUT_PAD_DSE & 0x7) << 3); //DSE = 0b011 for LED overclock
|
||||
//clear pin bit in IOMUX_GPR26 to map GPIO6-9 to GPIO1-4 for DMA
|
||||
*(&IOMUXC_GPR_GPR26 + offset) &= ~mask;
|
||||
*standard_gpio_addr(portModeRegister(pin)) |= mask; //GDIR? bit flag set output mode
|
||||
}
|
||||
//stash context for multi-show
|
||||
memcpy(bitmaskLocal, bitmask, 16);
|
||||
memcpy(pin_bitnumLocal, pin_bitnum, numpins);
|
||||
memcpy(pin_offsetLocal, pin_offset, numpins);
|
||||
|
||||
arm_dcache_flush_delete(bitmask, sizeof(bitmask)); //can't DMA from cached memory
|
||||
|
||||
// Set up 3 timers to create waveform timing events
|
||||
comp1load[0] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)TH_TL / OC_FACTOR );
|
||||
comp1load[1] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T0H / OC_FACTOR );
|
||||
comp1load[2] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T1H / (1.0 + ((OC_FACTOR - 1.0)/3)) );
|
||||
TMR4_ENBL &= ~7;
|
||||
TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR;
|
||||
TMR4_CSCTRL0 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN;
|
||||
TMR4_CNTR0 = 0;
|
||||
TMR4_LOAD0 = 0;
|
||||
TMR4_COMP10 = comp1load[0];
|
||||
TMR4_CMPLD10 = comp1load[0];
|
||||
TMR4_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3);
|
||||
TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
|
||||
TMR4_CNTR1 = 0;
|
||||
TMR4_LOAD1 = 0;
|
||||
TMR4_COMP11 = comp1load[1]; // T0H
|
||||
TMR4_CMPLD11 = comp1load[1];
|
||||
TMR4_CTRL1 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3);
|
||||
TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
|
||||
TMR4_CNTR2 = 0;
|
||||
TMR4_LOAD2 = 0;
|
||||
TMR4_COMP12 = comp1load[2]; // T1H
|
||||
TMR4_CMPLD12 = comp1load[2];
|
||||
TMR4_CTRL2 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3);
|
||||
|
||||
// route the timer outputs through XBAR to edge trigger DMA request: only 4 mappings avail.
|
||||
CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON);
|
||||
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_DMA_CH_MUX_REQ30);
|
||||
xbar_connect(XBARA1_IN_QTIMER4_TIMER1, XBARA1_OUT_DMA_CH_MUX_REQ31);
|
||||
xbar_connect(XBARA1_IN_QTIMER4_TIMER2, XBARA1_OUT_DMA_CH_MUX_REQ94);
|
||||
XBARA1_CTRL0 = XBARA_CTRL_STS1 | XBARA_CTRL_EDGE1(3) | XBARA_CTRL_DEN1 |
|
||||
XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0;
|
||||
XBARA1_CTRL1 = XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0;
|
||||
|
||||
// configure DMA channels
|
||||
dma1.begin();
|
||||
dma1.TCD->SADDR = bitmask; // source 4*32b GPIO pin mask
|
||||
dma1.TCD->SOFF = 8; // bytes offset added to SADDR after each transfer
|
||||
// SMOD(4) low bits of SADDR to update with adds of SOFF
|
||||
// SSIZE(3) code for 64 bit transfer size DSIZE(2) code for 32 bit transfer size
|
||||
dma1.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2);
|
||||
dma1.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | // Dest minor loop offsetting enable
|
||||
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
|
||||
DMA_TCD_NBYTES_MLOFFYES_NBYTES(16); // #bytes to tansfer, offsetting enabled
|
||||
dma1.TCD->SLAST = 0; // add to SADDR after xfer
|
||||
dma1.TCD->DADDR = &GPIO1_DR_SET;
|
||||
dma1.TCD->DOFF = 16384; //&GPIO1_DR_SET + DOFF = next &GPIO2_DR_SET
|
||||
dma1.TCD->CITER_ELINKNO = numbytes * 8; // CITER outer loop count (linking disabled) = # LED bits to write
|
||||
dma1.TCD->DLASTSGA = -65536; // add to DADDR after xfer
|
||||
dma1.TCD->BITER_ELINKNO = numbytes * 8; // Beginning CITER (not decremented by transfer)
|
||||
dma1.TCD->CSR = DMA_TCD_CSR_DREQ; // channel ERQ field cleared when minor loop completed
|
||||
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_0); // only 4 XBAR1 triggers (DMA MUX mapping)
|
||||
|
||||
dma2next.TCD->SADDR = bitdata; //uint32_t bitdata[BYTES_PER_DMA*64]
|
||||
dma2next.TCD->SOFF = 8;
|
||||
dma2next.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_DSIZE(2);
|
||||
dma2next.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
|
||||
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
|
||||
DMA_TCD_NBYTES_MLOFFYES_NBYTES(16);
|
||||
dma2next.TCD->SLAST = 0;
|
||||
dma2next.TCD->DADDR = &GPIO1_DR_CLEAR;
|
||||
dma2next.TCD->DOFF = 16384;
|
||||
dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
|
||||
dma2next.TCD->DLASTSGA = (int32_t)(dma2next.TCD);
|
||||
dma2next.TCD->BITER_ELINKNO = BYTES_PER_DMA * 8;
|
||||
dma2next.TCD->CSR = 0;
|
||||
|
||||
dma2.begin();
|
||||
dma2 = dma2next; // copies TCD
|
||||
dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_1);
|
||||
dma2.attachInterrupt(isr);
|
||||
|
||||
dma3.begin();
|
||||
dma3.TCD->SADDR = bitmask;
|
||||
dma3.TCD->SOFF = 8;
|
||||
dma3.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2);
|
||||
dma3.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
|
||||
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
|
||||
DMA_TCD_NBYTES_MLOFFYES_NBYTES(16);
|
||||
dma3.TCD->SLAST = 0;
|
||||
dma3.TCD->DADDR = &GPIO1_DR_CLEAR;
|
||||
dma3.TCD->DOFF = 16384;
|
||||
dma3.TCD->CITER_ELINKNO = numbytes * 8;
|
||||
dma3.TCD->DLASTSGA = -65536;
|
||||
dma3.TCD->BITER_ELINKNO = numbytes * 8;
|
||||
dma3.TCD->CSR = DMA_TCD_CSR_DREQ | DMA_TCD_CSR_DONE;
|
||||
dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_2);
|
||||
} // begin()
|
||||
|
||||
|
||||
//*dest = *bitdata + pin offset
|
||||
//*pixels = pin's block in frameBuffer
|
||||
//mask = pin's bit position in GPIOR
|
||||
//set a pin's mask32 for each color bit=0 at every 4*words32 in bitdata+offset
|
||||
void fillbits(uint32_t *dest, const uint8_t *pixels, int n, uint32_t mask) {
|
||||
do {
|
||||
uint8_t pix = *pixels++;
|
||||
if (!(pix & 0x80)) *dest |= mask;
|
||||
dest += 4;
|
||||
if (!(pix & 0x40)) *dest |= mask;
|
||||
dest += 4;
|
||||
if (!(pix & 0x20)) *dest |= mask;
|
||||
dest += 4;
|
||||
if (!(pix & 0x10)) *dest |= mask;
|
||||
dest += 4;
|
||||
if (!(pix & 0x08)) *dest |= mask;
|
||||
dest += 4;
|
||||
if (!(pix & 0x04)) *dest |= mask;
|
||||
dest += 4;
|
||||
if (!(pix & 0x02)) *dest |= mask;
|
||||
dest += 4;
|
||||
if (!(pix & 0x01)) *dest |= mask;
|
||||
dest += 4;
|
||||
} while (--n > 0);
|
||||
}
|
||||
|
||||
|
||||
void ObjectFLED::genFrameBuffer(uint32_t serp) {
|
||||
uint32_t j = 0;
|
||||
int jChange = -3;
|
||||
if (serp == 0) { // use faster loops if no serp
|
||||
switch (params & 0x3F) {
|
||||
case CORDER_RGBW: // R,G,B = R,G,B - MIN(R,G,B); W = MIN(R,G,B)
|
||||
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
|
||||
uint8_t minRGB = MIN(*((uint8_t*)drawBuffer + j) * rLevel / 65025, \
|
||||
*((uint8_t*)drawBuffer + j + 1) * rLevel / 65025);
|
||||
minRGB = MIN(minRGB, *((uint8_t*)drawBuffer + j + 2) * rLevel / 65025);
|
||||
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025 - minRGB;
|
||||
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
|
||||
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
|
||||
*(frameBuffer + i + 3) = minRGB;
|
||||
j += 3;
|
||||
} //for(leds in drawbuffer)
|
||||
break;
|
||||
case CORDER_GBR:
|
||||
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
|
||||
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
|
||||
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
|
||||
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
|
||||
j += 3;
|
||||
} //for(leds in drawbuffer)
|
||||
break;
|
||||
case CORDER_BGR:
|
||||
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
|
||||
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
|
||||
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
|
||||
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
|
||||
j += 3;
|
||||
} //for(leds in drawbuffer)
|
||||
break;
|
||||
case CORDER_BRG:
|
||||
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
|
||||
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
|
||||
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
|
||||
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
|
||||
j += 3;
|
||||
} //for(leds in drawbuffer)
|
||||
break;
|
||||
case CORDER_GRB:
|
||||
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
|
||||
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
|
||||
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
|
||||
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
|
||||
j += 3;
|
||||
} //for(leds in drawbuffer)
|
||||
break;
|
||||
case CORDER_RGB:
|
||||
default:
|
||||
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
|
||||
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
|
||||
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
|
||||
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
|
||||
j += 3;
|
||||
} //for(leds in drawbuffer)
|
||||
} // switch()
|
||||
} else { //serpentine
|
||||
switch (params & 0x3F) {
|
||||
case CORDER_RGBW: // R,G,B = R,G,B - MIN(R,G,B); W = MIN(R,G,B)
|
||||
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
|
||||
uint8_t minRGB = MIN(*((uint8_t*)drawBuffer + j) * rLevel / 65025, \
|
||||
* ((uint8_t*)drawBuffer + j + 1) * rLevel / 65025);
|
||||
minRGB = MIN(minRGB, *((uint8_t*)drawBuffer + j + 2) * rLevel / 65025);
|
||||
if (i % (serp * 4) == 0) {
|
||||
if (jChange < 0) { j = i / 4 * 3; jChange = 3; }
|
||||
else { j = (i / 4 + serp - 1) * 3; jChange = -3; }
|
||||
}
|
||||
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025 - minRGB;
|
||||
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
|
||||
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
|
||||
*(frameBuffer + i + 3) = minRGB;
|
||||
j += jChange;
|
||||
} //for(leds in drawbuffer)
|
||||
break;
|
||||
case CORDER_GBR:
|
||||
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
|
||||
if (i % (serp * 3) == 0) {
|
||||
if (jChange < 0) { j = i; jChange = 3; }
|
||||
else { j = i + (serp - 1) * 3; jChange = -3; }
|
||||
}
|
||||
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
|
||||
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
|
||||
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
|
||||
j += 3;
|
||||
} //for(leds in drawbuffer)
|
||||
break;
|
||||
case CORDER_BGR:
|
||||
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
|
||||
if (i % (serp * 3) == 0) {
|
||||
if (jChange < 0) { j = i; jChange = 3; }
|
||||
else { j = i + (serp - 1) * 3; jChange = -3; }
|
||||
}
|
||||
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
|
||||
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
|
||||
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
|
||||
j += 3;
|
||||
} //for(leds in drawbuffer)
|
||||
break;
|
||||
case CORDER_BRG:
|
||||
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
|
||||
if (i % (serp * 3) == 0) {
|
||||
if (jChange < 0) { j = i; jChange = 3; }
|
||||
else { j = i + (serp - 1) * 3; jChange = -3; }
|
||||
}
|
||||
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
|
||||
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
|
||||
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
|
||||
j += jChange;
|
||||
} //for(leds in drawbuffer)
|
||||
break;
|
||||
case CORDER_GRB:
|
||||
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
|
||||
if (i % (serp * 3) == 0) {
|
||||
if (jChange < 0) { j = i; jChange = 3; }
|
||||
else { j = i + (serp - 1) * 3; jChange = -3; }
|
||||
}
|
||||
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
|
||||
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
|
||||
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
|
||||
j += jChange;
|
||||
} //for(leds in drawbuffer)
|
||||
break;
|
||||
case CORDER_RGB:
|
||||
default:
|
||||
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
|
||||
if (i % (serp * 3) == 0) {
|
||||
if (jChange < 0) { j = i; jChange = 3; }
|
||||
else { j = i + (serp - 1) * 3; jChange = -3; }
|
||||
}
|
||||
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
|
||||
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
|
||||
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
|
||||
j += jChange;
|
||||
} //for(leds in drawbuffer)
|
||||
} // switch()
|
||||
} // else serpentine
|
||||
} //genFrameBuffer()
|
||||
|
||||
|
||||
// pre-show prior transfer wait, copies drawBuffer -> frameBuffer
|
||||
// resets timers, clears pending DMA reqs
|
||||
// fills bitdata[BYTES_PER_DMA * 64 * 4 bytes] from frameBuffer with 4-block bitmasks for 0's in led data
|
||||
// 4 word32s for each bit in (led data)/pin = 16 * 8 = 96 bitdata bytes for each LED byte: 288 bytes / LED
|
||||
// launches DMA with IRQ activation to reload bitdata from frameBuffer
|
||||
void ObjectFLED::show(void) {
|
||||
waitForDmaToFinish(); //wait for prior DMA to finish
|
||||
|
||||
//Restore context if needed
|
||||
if (frameBuffer != frameBufferLocal) {
|
||||
numpins = numpinsLocal;
|
||||
frameBuffer = frameBufferLocal;
|
||||
numbytes = numbytesLocal;
|
||||
memcpy(bitmask, bitmaskLocal, 16);
|
||||
memcpy(pin_bitnum, pin_bitnumLocal, numpins);
|
||||
memcpy(pin_offset, pin_offsetLocal, numpins);
|
||||
arm_dcache_flush_delete(bitmask, sizeof(bitmask)); //can't DMA from cached memory
|
||||
// Restore 3 timers to create waveform timing events
|
||||
TMR4_COMP10 = comp1load[0];
|
||||
TMR4_CMPLD10 = comp1load[0];
|
||||
TMR4_COMP11 = comp1load[1]; // T0H
|
||||
TMR4_CMPLD11 = comp1load[1];
|
||||
TMR4_COMP12 = comp1load[2]; // T1H
|
||||
TMR4_CMPLD12 = comp1load[2];
|
||||
//restore DMA loop control
|
||||
dma1.TCD->CITER_ELINKNO = numbytes * 8; // CITER outer loop count (linking disabled) = # LED bits to write
|
||||
dma1.TCD->BITER_ELINKNO = numbytes * 8; // Beginning CITER (not decremented by transfer)
|
||||
dma3.TCD->CITER_ELINKNO = numbytes * 8;
|
||||
dma3.TCD->BITER_ELINKNO = numbytes * 8;
|
||||
} //done restoring context
|
||||
|
||||
genFrameBuffer(serpNumber);
|
||||
|
||||
// disable timers
|
||||
uint16_t enable = TMR4_ENBL;
|
||||
TMR4_ENBL = enable & ~7;
|
||||
|
||||
// force all timer outputs to logic low
|
||||
TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR;
|
||||
TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
|
||||
TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
|
||||
|
||||
// clear any prior pending DMA requests
|
||||
XBARA1_CTRL0 |= XBARA_CTRL_STS1 | XBARA_CTRL_STS0;
|
||||
XBARA1_CTRL1 |= XBARA_CTRL_STS0;
|
||||
|
||||
// fill the DMA transmit buffer
|
||||
memset(bitdata, 0, sizeof(bitdata)); //BYTES_PER_DMA * 64 words32
|
||||
uint32_t count = numbytes; //bytes per strip
|
||||
if (count > BYTES_PER_DMA*2) count = BYTES_PER_DMA*2;
|
||||
framebuffer_index = count; //ptr to framebuffer last byte output
|
||||
|
||||
//Sets each pin mask in bitdata32[BYTES_PER_DMA*64] for every 0 bit of pin's frameBuffer block bytes
|
||||
for (uint32_t i=0; i < numpins; i++) { //for each pin
|
||||
fillbits(bitdata + pin_offset[i], (uint8_t *)frameBuffer + i*numbytes,
|
||||
count, 1<<pin_bitnum[i]);
|
||||
}
|
||||
arm_dcache_flush_delete(bitdata, count * 128); // don't need bitdata in cache for DMA
|
||||
|
||||
// set up DMA transfers
|
||||
if (numbytes <= BYTES_PER_DMA*2) {
|
||||
dma2.TCD->SADDR = bitdata;
|
||||
dma2.TCD->DADDR = &GPIO1_DR_CLEAR;
|
||||
dma2.TCD->CITER_ELINKNO = count * 8;
|
||||
dma2.TCD->CSR = DMA_TCD_CSR_DREQ;
|
||||
} else {
|
||||
dma2.TCD->SADDR = bitdata;
|
||||
dma2.TCD->DADDR = &GPIO1_DR_CLEAR;
|
||||
dma2.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
|
||||
dma2.TCD->CSR = 0;
|
||||
dma2.TCD->CSR = DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_ESG;
|
||||
dma2next.TCD->SADDR = bitdata + BYTES_PER_DMA*32;
|
||||
dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
|
||||
if (numbytes <= BYTES_PER_DMA*3) {
|
||||
dma2next.TCD->CSR = DMA_TCD_CSR_ESG;
|
||||
} else {
|
||||
dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR;
|
||||
}
|
||||
dma_first = true;
|
||||
}
|
||||
dma3.clearComplete();
|
||||
dma1.enable();
|
||||
dma2.enable();
|
||||
dma3.enable();
|
||||
|
||||
// initialize timers
|
||||
TMR4_CNTR0 = 0;
|
||||
TMR4_CNTR1 = comp1load[0] + 1;
|
||||
TMR4_CNTR2 = comp1load[0] + 1;
|
||||
|
||||
// wait for last LED reset to finish
|
||||
while (micros() - update_begin_micros < numbytes * 8 * TH_TL / OC_FACTOR / 1000 + LATCH_DELAY);
|
||||
|
||||
// start everything running!
|
||||
TMR4_ENBL = enable | 7;
|
||||
update_begin_micros = micros();
|
||||
} // show()
|
||||
|
||||
|
||||
//INPUT: dma2, dma2next, bitdata, framebuffer_inedex, numpins, numbytes, pin_offset[], pin_bitnum[]
|
||||
//Reads next block of framebuffer -> fillbits() -> bitdata
|
||||
//Checks for last block to transfer, next to last, or not to update dma2next major loop
|
||||
void ObjectFLED::isr(void)
|
||||
{
|
||||
// first ack the interrupt
|
||||
dma2.clearInterrupt();
|
||||
|
||||
// fill (up to) half the transmit buffer with new fillbits(frameBuffer data)
|
||||
//digitalWriteFast(12, HIGH);
|
||||
uint32_t *dest;
|
||||
if (dma_first) {
|
||||
dma_first = false;
|
||||
dest = bitdata;
|
||||
} else {
|
||||
dma_first = true;
|
||||
dest = bitdata + BYTES_PER_DMA*32;
|
||||
}
|
||||
memset(dest, 0, sizeof(bitdata)/2);
|
||||
uint32_t index = framebuffer_index;
|
||||
uint32_t count = numbytes - framebuffer_index;
|
||||
if (count > BYTES_PER_DMA) count = BYTES_PER_DMA;
|
||||
framebuffer_index = index + count;
|
||||
for (int i=0; i < numpins; i++) {
|
||||
fillbits(dest + pin_offset[i], (uint8_t *)frameBuffer + index + i*numbytes,
|
||||
count, 1<<pin_bitnum[i]);
|
||||
}
|
||||
arm_dcache_flush_delete(dest, count * 128);
|
||||
//digitalWriteFast(12, LOW);
|
||||
|
||||
// queue it for the next DMA transfer
|
||||
dma2next.TCD->SADDR = dest;
|
||||
dma2next.TCD->CITER_ELINKNO = count * 8;
|
||||
uint32_t remain = numbytes - (index + count);
|
||||
if (remain == 0) {
|
||||
dma2next.TCD->CSR = DMA_TCD_CSR_DREQ;
|
||||
} else if (remain <= BYTES_PER_DMA) {
|
||||
dma2next.TCD->CSR = DMA_TCD_CSR_ESG;
|
||||
} else {
|
||||
dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR;
|
||||
}
|
||||
} // isr()
|
||||
|
||||
|
||||
int ObjectFLED::busy(void)
|
||||
{
|
||||
if (micros() - update_begin_micros < numbytes * TH_TL / OC_FACTOR / 1000 * 8 + LATCH_DELAY) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void ObjectFLED::setBrightness(uint8_t brightLevel) {
|
||||
brightness = brightLevel;
|
||||
rLevel = brightness * (colorBalance >> 16);
|
||||
gLevel = brightness * ((colorBalance >> 8) & 0xFF);
|
||||
bLevel = brightness * (colorBalance & 0xFF);
|
||||
}
|
||||
|
||||
|
||||
void ObjectFLED::setBalance(uint32_t balMask) {
|
||||
colorBalance = balMask & 0xFFFFFF;
|
||||
rLevel = brightness * (colorBalance >> 16);
|
||||
gLevel = brightness * ((colorBalance >> 8) & 0xFF);
|
||||
bLevel = brightness * (colorBalance & 0xFF);
|
||||
}
|
||||
|
||||
|
||||
//Fades CRGB array towards the background color by amount.
|
||||
void fadeToColorBy(void* leds, uint16_t count, uint32_t color, uint8_t fadeAmt) {
|
||||
for (uint32_t x = 0; x < count * 3; x += 3) {
|
||||
//fade red
|
||||
*((uint8_t*)leds + x) = (( *((uint8_t*)leds + x) * (1 + (255 - fadeAmt))) >> 8) + \
|
||||
(( ((color >> 16) & 0xFF) * (1 + fadeAmt)) >> 8);
|
||||
//fade green
|
||||
*((uint8_t*)leds + x + 1) = (( *((uint8_t*)leds + x + 1) * (1 + (255 - fadeAmt))) >> 8) + \
|
||||
(( ((color >> 8) & 0xFF) * (1 + fadeAmt)) >> 8);
|
||||
//fade blue
|
||||
*((uint8_t*)leds + x + 2) = (( *((uint8_t*)leds + x + 2) * (1 + (255 - fadeAmt))) >> 8) + \
|
||||
(( (color & 0xFF) * (1 + fadeAmt)) >> 8);
|
||||
}
|
||||
} //fadeToColorBy()
|
||||
|
||||
|
||||
// Safely draws box even if partially offscreen on 2D CRGB array
|
||||
void drawSquare(void* leds, uint16_t planeY, uint16_t planeX, int yCorner, int xCorner, uint32_t size, uint32_t color) {
|
||||
if (size != 0) { size--; }
|
||||
else { return; }
|
||||
for (int x = xCorner; x <= xCorner + (int)size; x++) {
|
||||
// if validX { if validY+S {draw Y+S,X}; if validY {draw Y, X} }
|
||||
if ((x >= 0) && (x < planeX)) { //valid X
|
||||
if ((yCorner >= 0) && (yCorner < planeY)) {
|
||||
*((uint8_t*)leds + (yCorner * planeX + x) * 3) = ((color >> 16) & 0xFF);
|
||||
*((uint8_t*)leds + (yCorner * planeX + x) * 3 + 1) = ((color >> 8) & 0xFF);
|
||||
*((uint8_t*)leds + (yCorner * planeX + x) * 3 + 2) = (color & 0xFF);
|
||||
}
|
||||
if ((yCorner + size >= 0) && (yCorner + size < planeY)) {
|
||||
*((uint8_t*)leds + ((yCorner + size) * planeX + x) * 3) = ((color >> 16) & 0xFF);
|
||||
*((uint8_t*)leds + ((yCorner + size) * planeX + x) * 3 + 1) = ((color >> 8) & 0xFF);
|
||||
*((uint8_t*)leds + ((yCorner + size) * planeX + x) * 3 + 2) = (color & 0xFF);
|
||||
}
|
||||
} //if valid x
|
||||
} //for x
|
||||
for (int y = yCorner; y <= yCorner + (int)size; y++) {
|
||||
if ((y >= 0) && (y < planeY)) { //valid y
|
||||
if ((xCorner >= 0) && (xCorner < planeX)) {
|
||||
*((uint8_t*)leds + (xCorner + y * planeX) * 3) = ((color >> 16) & 0xFF);
|
||||
*((uint8_t*)leds + (xCorner + y * planeX) * 3 + 1) = ((color >> 8) & 0xFF);
|
||||
*((uint8_t*)leds + (xCorner + y * planeX) * 3 + 2) = (color & 0xFF);
|
||||
}
|
||||
if ((xCorner + size >= 0) && (xCorner + size < planeX)) {
|
||||
*((uint8_t*)leds + (xCorner + size + y * planeX) * 3) = ((color >> 16) & 0xFF);
|
||||
*((uint8_t*)leds + (xCorner + size + y * planeX) * 3 + 1) = ((color >> 8) & 0xFF);
|
||||
*((uint8_t*)leds + (xCorner + size + y * planeX) * 3 + 2) = (color & 0xFF);
|
||||
}
|
||||
} //if valid y
|
||||
} //for y
|
||||
} // drawSquare()
|
||||
|
||||
} // namespace fl
|
||||
|
||||
|
||||
#endif // __IMXRT1062__
|
||||
3
libraries/FastLED/src/third_party/yves/I2SClockLessLedDriveresp32s3/_readme
vendored
Normal file
3
libraries/FastLED/src/third_party/yves/I2SClockLessLedDriveresp32s3/_readme
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
Repo: https://github.com/hpwit/I2SClockLessLedDriveresp32s3
|
||||
commit: 48e9cb6d3db0e902703b0325bfaa153074f071d8
|
||||
11
libraries/FastLED/src/third_party/yves/I2SClockLessLedDriveresp32s3/driver.h
vendored
Normal file
11
libraries/FastLED/src/third_party/yves/I2SClockLessLedDriveresp32s3/driver.h
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef USE_FASTLED
|
||||
#define USE_FASTLED
|
||||
#endif // USE_FASTLED
|
||||
|
||||
#define COLOR_ORDER_RBG
|
||||
|
||||
#include "src/I2SClockLessLedDriveresp32s3.h"
|
||||
|
||||
|
||||
@@ -0,0 +1,562 @@
|
||||
|
||||
|
||||
#include "fl/has_include.h"
|
||||
|
||||
#if !FL_HAS_INCLUDE("esp_memory_utils.h")
|
||||
#error \
|
||||
"esp_memory_utils.h is not available, are you using esp-idf 4.4 or earlier?"
|
||||
#else
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wattributes"
|
||||
#pragma GCC diagnostic ignored "-Wvolatile"
|
||||
|
||||
#include "esp_attr.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_pm.h"
|
||||
#include "fl/stdint.h"
|
||||
#include "platforms/assert_defs.h"
|
||||
#include <string.h>
|
||||
// #include "esp_lcd_panel_io_interface.h"
|
||||
// #include "esp_lcd_panel_io.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "esp_private/gdma.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "hal/dma_types.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "soc/rtc.h" // for `rtc_clk_xtal_freq_get()`
|
||||
#include "soc/soc_caps.h"
|
||||
// #include "esp_private/periph_ctrl.h"
|
||||
|
||||
// #include "esp_lcd_common.h"
|
||||
#include "hal/lcd_hal.h"
|
||||
#include "hal/lcd_ll.h"
|
||||
#include "soc/lcd_periph.h"
|
||||
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_lcd_panel_interface.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_io_interface.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "soc/gdma_reg.h"
|
||||
#include "platforms/esp/esp_version.h"
|
||||
|
||||
#define IDF_5_4_OR_EARLIER (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0))
|
||||
|
||||
// According to bug reports, this driver does not work well with the new WS2812-v5b. This is
|
||||
// probably due to the extrrra long reset time requirements of this chipset. so we put in
|
||||
// a hack that will always add 300 uS to the reset time.
|
||||
#define FASTLED_EXPERIMENTAL_YVES_EXTRA_WAIT_MICROS 300
|
||||
|
||||
#ifndef NUMSTRIPS
|
||||
#define NUMSTRIPS 16
|
||||
#endif
|
||||
|
||||
#ifndef SNAKEPATTERN
|
||||
#define SNAKEPATTERN 1
|
||||
#endif
|
||||
|
||||
#ifndef ALTERNATEPATTERN
|
||||
#define ALTERNATEPATTERN 1
|
||||
#endif
|
||||
|
||||
#define I2S_DEVICE 0
|
||||
|
||||
#pragma push_macro("AA")
|
||||
#pragma push_macro("CC")
|
||||
#pragma push_macro("FF")
|
||||
#pragma push_macro("FF2")
|
||||
|
||||
// #ifndef AA
|
||||
// #define AA (0x00AA00AAL)
|
||||
// #endif
|
||||
|
||||
#ifdef AA
|
||||
#undef AA
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef BA
|
||||
#undef BA
|
||||
#endif
|
||||
|
||||
#ifdef CC
|
||||
#undef CC
|
||||
#endif
|
||||
|
||||
#ifdef FF
|
||||
#undef FF
|
||||
#endif
|
||||
|
||||
#ifdef FF2
|
||||
#undef FF2
|
||||
#endif
|
||||
|
||||
#define AA (0x00AA00AAL)
|
||||
#define CC (0x0000CCCCL)
|
||||
#define FF (0xF0F0F0F0L)
|
||||
#define FF2 (0x0F0F0F0FL)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// End scoped constants
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define __OFFSET 0 // (24*3*2*2*2+2)
|
||||
|
||||
#define __OFFSET_END (24 * 3 * 2 * 2 * 2 + 2)
|
||||
#ifndef HARDWARESPRITES
|
||||
#define HARDWARESPRITES 0
|
||||
#endif
|
||||
|
||||
#if HARDWARESPRITES == 1
|
||||
#include "hardwareSprite.h"
|
||||
#endif
|
||||
|
||||
#ifdef COLOR_ORDER_GRBW
|
||||
#define _p_r 1
|
||||
#define _p_g 0
|
||||
#define _p_b 2
|
||||
#define _nb_components 4
|
||||
#else
|
||||
#ifdef COLOR_ORDER_RGB
|
||||
#define _p_r 0
|
||||
#define _p_g 1
|
||||
#define _p_b 2
|
||||
#define _nb_components 3
|
||||
#else
|
||||
#ifdef COLOR_ORDER_RBG
|
||||
#define _p_r 0
|
||||
#define _p_g 2
|
||||
#define _p_b 1
|
||||
#define _nb_components 3
|
||||
#else
|
||||
#ifdef COLOR_ORDER_GBR
|
||||
#define _p_r 2
|
||||
#define _p_g 0
|
||||
#define _p_b 1
|
||||
#define _nb_components 3
|
||||
#else
|
||||
#ifdef COLOR_ORDER_BGR
|
||||
#define _p_r 2
|
||||
#define _p_g 1
|
||||
#define _p_b 0
|
||||
#define _nb_components 3
|
||||
#else
|
||||
#ifdef COLOR_ORDER_BRG
|
||||
#define _p_r 1
|
||||
#define _p_g 2
|
||||
#define _p_b 0
|
||||
#define _nb_components 3
|
||||
#else
|
||||
#ifdef COLOR_ORDER_GRB
|
||||
#define _p_r 1
|
||||
#define _p_g 0
|
||||
#define _p_b 2
|
||||
#define _nb_components 3
|
||||
#else
|
||||
|
||||
#define _p_r 1
|
||||
#define _p_g 0
|
||||
#define _p_b 2
|
||||
#define _nb_components 3
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_PIXELSLIB
|
||||
// #include "pixelslib.h"
|
||||
#else
|
||||
#include "___pixeltypes.h"
|
||||
#endif
|
||||
|
||||
#define LCD_DRIVER_PSRAM_DATA_ALIGNMENT 64
|
||||
|
||||
// Note: I2S REQUIRES that the FASTLED_OVERCLOCK factor is a a build-level-define and
|
||||
// not an include level define. This is easy if you are already using PlatformIO or CMake.
|
||||
// If you are using ArduinoIDE you'll have to download FastLED source code and hack the src
|
||||
// to make this work.
|
||||
#ifdef FASTLED_OVERCLOCK
|
||||
#define FASTLED_ESP32S3_I2S_CLOCK_HZ_SCALE (FASTLED_OVERCLOCK)
|
||||
#else
|
||||
#define FASTLED_ESP32S3_I2S_CLOCK_HZ_SCALE (1)
|
||||
#endif
|
||||
|
||||
#ifndef FASTLED_ESP32S3_I2S_CLOCK_HZ
|
||||
#define FASTLED_ESP32S3_I2S_CLOCK_HZ uint32_t((24 * 100 * 1000)*FASTLED_ESP32S3_I2S_CLOCK_HZ_SCALE)
|
||||
#endif
|
||||
|
||||
namespace fl {
|
||||
|
||||
static bool IRAM_ATTR flush_ready(esp_lcd_panel_io_handle_t panel_io,
|
||||
esp_lcd_panel_io_event_data_t *edata,
|
||||
void *user_ctx);
|
||||
|
||||
typedef union {
|
||||
uint8_t bytes[16];
|
||||
uint32_t shorts[8];
|
||||
uint32_t raw[2];
|
||||
} Lines;
|
||||
|
||||
enum colorarrangment {
|
||||
ORDER_GRBW,
|
||||
ORDER_RGB,
|
||||
ORDER_RBG,
|
||||
ORDER_GRB,
|
||||
ORDER_GBR,
|
||||
ORDER_BRG,
|
||||
ORDER_BGR,
|
||||
};
|
||||
|
||||
enum displayMode {
|
||||
NO_WAIT,
|
||||
WAIT,
|
||||
LOOP,
|
||||
LOOP_INTERUPT,
|
||||
};
|
||||
|
||||
bool DRIVER_READY = true;
|
||||
|
||||
typedef struct led_driver_t led_driver_t;
|
||||
|
||||
struct led_driver_t {
|
||||
size_t (*init)();
|
||||
void (*update)(uint8_t *colors, size_t len);
|
||||
};
|
||||
volatile xSemaphoreHandle I2SClocklessLedDriverS3_sem = NULL;
|
||||
volatile bool isDisplaying = false;
|
||||
volatile bool iswaiting = false;
|
||||
|
||||
static void IRAM_ATTR transpose16x1_noinline2(unsigned char *A, uint16_t *B) {
|
||||
|
||||
uint32_t x, y, x1, y1, t;
|
||||
|
||||
y = *(unsigned int *)(A);
|
||||
#if NUMSTRIPS > 4
|
||||
x = *(unsigned int *)(A + 4);
|
||||
#else
|
||||
x = 0;
|
||||
#endif
|
||||
|
||||
#if NUMSTRIPS > 8
|
||||
y1 = *(unsigned int *)(A + 8);
|
||||
#else
|
||||
y1 = 0;
|
||||
#endif
|
||||
#if NUMSTRIPS > 12
|
||||
x1 = *(unsigned int *)(A + 12);
|
||||
#else
|
||||
x1 = 0;
|
||||
#endif
|
||||
|
||||
// pre-transform x
|
||||
#if NUMSTRIPS > 4
|
||||
t = (x ^ (x >> 7)) & AA;
|
||||
x = x ^ t ^ (t << 7);
|
||||
t = (x ^ (x >> 14)) & CC;
|
||||
x = x ^ t ^ (t << 14);
|
||||
#endif
|
||||
#if NUMSTRIPS > 12
|
||||
t = (x1 ^ (x1 >> 7)) & AA;
|
||||
x1 = x1 ^ t ^ (t << 7);
|
||||
t = (x1 ^ (x1 >> 14)) & CC;
|
||||
x1 = x1 ^ t ^ (t << 14);
|
||||
#endif
|
||||
// pre-transform y
|
||||
t = (y ^ (y >> 7)) & AA;
|
||||
y = y ^ t ^ (t << 7);
|
||||
t = (y ^ (y >> 14)) & CC;
|
||||
y = y ^ t ^ (t << 14);
|
||||
#if NUMSTRIPS > 8
|
||||
t = (y1 ^ (y1 >> 7)) & AA;
|
||||
y1 = y1 ^ t ^ (t << 7);
|
||||
t = (y1 ^ (y1 >> 14)) & CC;
|
||||
y1 = y1 ^ t ^ (t << 14);
|
||||
#endif
|
||||
// final transform
|
||||
t = (x & FF) | ((y >> 4) & FF2);
|
||||
y = ((x << 4) & FF) | (y & FF2);
|
||||
x = t;
|
||||
|
||||
t = (x1 & FF) | ((y1 >> 4) & FF2);
|
||||
y1 = ((x1 << 4) & FF) | (y1 & FF2);
|
||||
x1 = t;
|
||||
|
||||
*((uint16_t *)(B)) =
|
||||
(uint16_t)(((x & 0xff000000) >> 8 | ((x1 & 0xff000000))) >> 16);
|
||||
*((uint16_t *)(B + 3)) =
|
||||
(uint16_t)(((x & 0xff0000) >> 16 | ((x1 & 0xff0000) >> 8)));
|
||||
*((uint16_t *)(B + 6)) =
|
||||
(uint16_t)(((x & 0xff00) | ((x1 & 0xff00) << 8)) >> 8);
|
||||
*((uint16_t *)(B + 9)) = (uint16_t)((x & 0xff) | ((x1 & 0xff) << 8));
|
||||
*((uint16_t *)(B + 12)) =
|
||||
(uint16_t)(((y & 0xff000000) >> 8 | ((y1 & 0xff000000))) >> 16);
|
||||
*((uint16_t *)(B + 15)) =
|
||||
(uint16_t)(((y & 0xff0000) | ((y1 & 0xff0000) << 8)) >> 16);
|
||||
*((uint16_t *)(B + 18)) =
|
||||
(uint16_t)(((y & 0xff00) | ((y1 & 0xff00) << 8)) >> 8);
|
||||
*((uint16_t *)(B + 21)) = (uint16_t)((y & 0xff) | ((y1 & 0xff) << 8));
|
||||
}
|
||||
|
||||
esp_lcd_panel_io_handle_t led_io_handle = NULL;
|
||||
|
||||
class I2SClocklessLedDriveresp32S3 {
|
||||
|
||||
public:
|
||||
int testcount;
|
||||
uint16_t *buffers[2];
|
||||
uint16_t *led_output = NULL;
|
||||
uint16_t *led_output2 = NULL;
|
||||
uint8_t *ledsbuff = NULL;
|
||||
int num_leds_per_strip;
|
||||
int _numstrips;
|
||||
int currentframe;
|
||||
|
||||
uint8_t __green_map[256];
|
||||
uint8_t __blue_map[256];
|
||||
uint8_t __red_map[256];
|
||||
uint8_t __white_map[256];
|
||||
uint8_t _brightness;
|
||||
float _gammar, _gammab, _gammag, _gammaw;
|
||||
|
||||
void setBrightness(int brightness) {
|
||||
_brightness = brightness;
|
||||
float tmp;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
tmp = powf((float)i / 255, 1 / _gammag);
|
||||
__green_map[i] = (uint8_t)(tmp * brightness);
|
||||
tmp = powf((float)i / 255, 1 / _gammab);
|
||||
__blue_map[i] = (uint8_t)(tmp * brightness);
|
||||
tmp = powf((float)i / 255, 1 / _gammar);
|
||||
__red_map[i] = (uint8_t)(tmp * brightness);
|
||||
tmp = powf((float)i / 255, 1 / _gammaw);
|
||||
__white_map[i] = (uint8_t)(tmp * brightness);
|
||||
}
|
||||
}
|
||||
|
||||
void setGamma(float gammar, float gammab, float gammag, float gammaw) {
|
||||
_gammag = gammag;
|
||||
_gammar = gammar;
|
||||
_gammaw = gammaw;
|
||||
_gammab = gammab;
|
||||
setBrightness(_brightness);
|
||||
}
|
||||
|
||||
void setGamma(float gammar, float gammab, float gammag) {
|
||||
_gammag = gammag;
|
||||
_gammar = gammar;
|
||||
_gammab = gammab;
|
||||
setBrightness(_brightness);
|
||||
}
|
||||
|
||||
void _initled(uint8_t *leds, const int *pins, int numstrip,
|
||||
int NUM_LED_PER_STRIP) {
|
||||
|
||||
// esp_lcd_panel_io_handle_t init_lcd_driver(unsigned int
|
||||
// FASTLED_ESP32S3_I2S_CLOCK_HZ, size_t _nb_components) {
|
||||
|
||||
esp_lcd_i80_bus_handle_t i80_bus = NULL;
|
||||
|
||||
esp_lcd_i80_bus_config_t bus_config;
|
||||
|
||||
bus_config.clk_src = LCD_CLK_SRC_PLL160M;
|
||||
bus_config.dc_gpio_num = 0;
|
||||
bus_config.wr_gpio_num = 0;
|
||||
// bus_config.data_gpio_nums = (int*)malloc(16*sizeof(int));
|
||||
for (int i = 0; i < numstrip; i++) {
|
||||
bus_config.data_gpio_nums[i] = pins[i];
|
||||
}
|
||||
if (numstrip < 16) {
|
||||
for (int i = numstrip; i < 16; i++) {
|
||||
bus_config.data_gpio_nums[i] = 0;
|
||||
}
|
||||
}
|
||||
bus_config.bus_width = 16;
|
||||
bus_config.max_transfer_bytes =
|
||||
_nb_components * NUM_LED_PER_STRIP * 8 * 3 * 2 + __OFFSET + __OFFSET_END;
|
||||
#if IDF_5_4_OR_EARLIER
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
// In IDF 5.3, psram_trans_align became deprecated. We kick the can down
|
||||
// the road a little bit and suppress the warning until idf 5.5 arrives.
|
||||
bus_config.psram_trans_align = LCD_DRIVER_PSRAM_DATA_ALIGNMENT;
|
||||
bus_config.sram_trans_align = 4;
|
||||
#if IDF_5_4_OR_EARLIER
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
esp_err_t bus_err = esp_lcd_new_i80_bus(&bus_config, &i80_bus);
|
||||
FASTLED_ASSERT(bus_err == ESP_OK, "Failed to create ESP32 I2S LCD bus");
|
||||
|
||||
esp_lcd_panel_io_i80_config_t io_config;
|
||||
|
||||
io_config.cs_gpio_num = -1;
|
||||
io_config.pclk_hz = FASTLED_ESP32S3_I2S_CLOCK_HZ;
|
||||
io_config.trans_queue_depth = 1;
|
||||
io_config.dc_levels = {
|
||||
.dc_idle_level = 0,
|
||||
.dc_cmd_level = 0,
|
||||
.dc_dummy_level = 0,
|
||||
.dc_data_level = 1,
|
||||
};
|
||||
//.on_color_trans_done = flush_ready,
|
||||
// .user_ctx = nullptr,
|
||||
io_config.lcd_cmd_bits = 0;
|
||||
io_config.lcd_param_bits = 0;
|
||||
io_config.user_ctx = this;
|
||||
|
||||
io_config.on_color_trans_done = flush_ready;
|
||||
esp_err_t panel_err = esp_lcd_new_panel_io_i80(i80_bus, &io_config, &led_io_handle);
|
||||
FASTLED_ASSERT(panel_err == ESP_OK, "Failed to create ESP32 I2S LCD panel IO");
|
||||
}
|
||||
|
||||
void initled(uint8_t *leds, const int *pins, int numstrip,
|
||||
int NUM_LED_PER_STRIP) {
|
||||
currentframe = 0;
|
||||
_gammab = 1;
|
||||
_gammar = 1;
|
||||
_gammag = 1;
|
||||
_gammaw = 1;
|
||||
setBrightness(255);
|
||||
if (I2SClocklessLedDriverS3_sem == NULL) {
|
||||
I2SClocklessLedDriverS3_sem = xSemaphoreCreateBinary();
|
||||
}
|
||||
// esp_lcd_panel_io_handle_t init_lcd_driver(unsigned int
|
||||
// FASTLED_ESP32S3_I2S_CLOCK_HZ, size_t _nb_components) {
|
||||
led_output = (uint16_t *)heap_caps_aligned_alloc(
|
||||
LCD_DRIVER_PSRAM_DATA_ALIGNMENT,
|
||||
8 * _nb_components * NUM_LED_PER_STRIP * 3 * 2 + __OFFSET +
|
||||
__OFFSET_END,
|
||||
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
memset(led_output, 0,
|
||||
8 * _nb_components * NUM_LED_PER_STRIP * 3 * 2 + __OFFSET +
|
||||
__OFFSET_END);
|
||||
|
||||
led_output2 = (uint16_t *)heap_caps_aligned_alloc(
|
||||
LCD_DRIVER_PSRAM_DATA_ALIGNMENT,
|
||||
8 * _nb_components * NUM_LED_PER_STRIP * 3 * 2 + __OFFSET +
|
||||
__OFFSET_END,
|
||||
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
memset(led_output2, 0,
|
||||
8 * _nb_components * NUM_LED_PER_STRIP * 3 * 2 + __OFFSET +
|
||||
__OFFSET_END);
|
||||
buffers[0] = led_output;
|
||||
buffers[1] = led_output2;
|
||||
// led_output[0] = 0xFFFF; //the +1 because it's like the first value
|
||||
// doesnt get pushed do not ask me why for now
|
||||
// led_output2[0] = 0xFFFF;
|
||||
led_output2 += __OFFSET / 2;
|
||||
led_output += __OFFSET / 2;
|
||||
|
||||
for (int i = 0; i < NUM_LED_PER_STRIP * _nb_components * 8; i++) {
|
||||
led_output[3 * i + 1] =
|
||||
0xFFFF; // the +1 because it's like the first value doesnt get
|
||||
// pushed do not ask me why for now
|
||||
led_output2[3 * i + 1] = 0xFFFF;
|
||||
}
|
||||
ledsbuff = leds;
|
||||
_numstrips = numstrip;
|
||||
num_leds_per_strip = NUM_LED_PER_STRIP;
|
||||
_initled(leds, pins, numstrip, NUM_LED_PER_STRIP);
|
||||
}
|
||||
|
||||
void transposeAll(uint16_t *ledoutput) {
|
||||
|
||||
uint16_t ledToDisplay = 0;
|
||||
Lines secondPixel[_nb_components];
|
||||
uint16_t *buff =
|
||||
ledoutput + 2; //+1 pour le premier empty +1 pour le 1 systématique
|
||||
uint16_t jump = num_leds_per_strip * _nb_components;
|
||||
for (int j = 0; j < num_leds_per_strip; j++) {
|
||||
uint8_t *poli = ledsbuff + ledToDisplay * _nb_components;
|
||||
for (int i = 0; i < _numstrips; i++) {
|
||||
|
||||
secondPixel[_p_g].bytes[i] = __green_map[*(poli + 1)];
|
||||
secondPixel[_p_r].bytes[i] = __red_map[*(poli + 0)];
|
||||
secondPixel[_p_b].bytes[i] = __blue_map[*(poli + 2)];
|
||||
if (_nb_components > 3)
|
||||
secondPixel[3].bytes[i] = __white_map[*(poli + 3)];
|
||||
// #endif
|
||||
poli += jump;
|
||||
}
|
||||
ledToDisplay++;
|
||||
transpose16x1_noinline2(secondPixel[0].bytes, buff);
|
||||
buff += 24;
|
||||
transpose16x1_noinline2(secondPixel[1].bytes, buff);
|
||||
buff += 24;
|
||||
transpose16x1_noinline2(secondPixel[2].bytes, buff);
|
||||
buff += 24;
|
||||
if (_nb_components > 3) {
|
||||
transpose16x1_noinline2(secondPixel[3].bytes, buff);
|
||||
buff += 24;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void show() {
|
||||
transposeAll(buffers[currentframe]);
|
||||
if (isDisplaying) {
|
||||
// Serial.println("on display dejà");
|
||||
iswaiting = true;
|
||||
if (I2SClocklessLedDriverS3_sem == NULL)
|
||||
I2SClocklessLedDriverS3_sem = xSemaphoreCreateBinary();
|
||||
xSemaphoreTake(I2SClocklessLedDriverS3_sem, portMAX_DELAY);
|
||||
}
|
||||
isDisplaying = true;
|
||||
|
||||
if (FASTLED_EXPERIMENTAL_YVES_EXTRA_WAIT_MICROS) {
|
||||
delayMicroseconds(FASTLED_EXPERIMENTAL_YVES_EXTRA_WAIT_MICROS);
|
||||
}
|
||||
|
||||
led_io_handle->tx_color(led_io_handle, 0x2C, buffers[currentframe],
|
||||
_nb_components * num_leds_per_strip * 8 * 3 *
|
||||
2 +
|
||||
__OFFSET + __OFFSET_END);
|
||||
|
||||
currentframe = (currentframe + 1) % 2;
|
||||
}
|
||||
};
|
||||
|
||||
static bool IRAM_ATTR flush_ready(esp_lcd_panel_io_handle_t panel_io,
|
||||
esp_lcd_panel_io_event_data_t *edata,
|
||||
void *user_ctx) {
|
||||
DRIVER_READY = true;
|
||||
isDisplaying = false;
|
||||
I2SClocklessLedDriveresp32S3 *cont =
|
||||
(I2SClocklessLedDriveresp32S3 *)user_ctx;
|
||||
cont->testcount++;
|
||||
if (iswaiting) {
|
||||
portBASE_TYPE HPTaskAwoken = 0;
|
||||
iswaiting = false;
|
||||
xSemaphoreGiveFromISR(I2SClocklessLedDriverS3_sem, &HPTaskAwoken);
|
||||
if (HPTaskAwoken == pdTRUE)
|
||||
portYIELD_FROM_ISR(HPTaskAwoken);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
} // namespace fl
|
||||
|
||||
#pragma pop_macro("AA")
|
||||
#pragma pop_macro("CC")
|
||||
#pragma pop_macro("FF")
|
||||
#pragma pop_macro("FF2")
|
||||
|
||||
#endif // FL_HAS_INCLUDE("esp_memory_utils.h")
|
||||
334
libraries/FastLED/src/third_party/yves/I2SClockLessLedDriveresp32s3/src/___pixeltypes.h
vendored
Normal file
334
libraries/FastLED/src/third_party/yves/I2SClockLessLedDriveresp32s3/src/___pixeltypes.h
vendored
Normal file
@@ -0,0 +1,334 @@
|
||||
#include "fl/stdint.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifdef USE_FASTLED
|
||||
#include "FastLED.h"
|
||||
#endif
|
||||
// #include "Arduino.h"
|
||||
|
||||
#define _OUT_OF_BOUND -12
|
||||
|
||||
namespace fl {
|
||||
|
||||
#ifdef COLOR_RGBW
|
||||
|
||||
struct Pixel {
|
||||
union {
|
||||
uint8_t raw[4];
|
||||
struct {
|
||||
uint8_t red;
|
||||
uint8_t green;
|
||||
uint8_t blue;
|
||||
uint8_t white;
|
||||
};
|
||||
};
|
||||
|
||||
inline Pixel(uint8_t r, uint8_t g, uint8_t b, uint8_t w)
|
||||
__attribute__((always_inline))
|
||||
: red(r), green(g), blue(b), white(w) {
|
||||
// brigthness =0xE0 |(br&31);
|
||||
}
|
||||
|
||||
inline Pixel(uint8_t r, uint8_t g, uint8_t b) __attribute__((always_inline))
|
||||
: red(r), green(g), blue(b) {
|
||||
white = MIN(red, green);
|
||||
white = MIN(white, blue);
|
||||
red = red - white;
|
||||
green = green - white;
|
||||
blue = blue - white;
|
||||
}
|
||||
|
||||
inline Pixel() __attribute__((always_inline)) {}
|
||||
|
||||
#ifdef USE_FASTLED
|
||||
inline Pixel &operator=(const CRGB &rhs) __attribute__((always_inline)) {
|
||||
|
||||
red = rhs.r;
|
||||
green = rhs.g;
|
||||
blue = rhs.b;
|
||||
white = MIN(red, green);
|
||||
white = MIN(white, blue);
|
||||
red = red - white;
|
||||
green = green - white;
|
||||
blue = blue - white;
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline Pixel(const Pixel &rhs) __attribute__((always_inline)) {
|
||||
// brigthness=rhs.brigthness;
|
||||
red = rhs.red;
|
||||
green = rhs.green;
|
||||
blue = rhs.blue;
|
||||
white = rhs.white;
|
||||
}
|
||||
inline Pixel &operator=(const uint32_t colorcode)
|
||||
__attribute__((always_inline)) {
|
||||
// rgb colorg;
|
||||
red = (colorcode >> 24) & 0xFF;
|
||||
green = (colorcode >> 16) & 0xFF;
|
||||
blue = (colorcode >> 8) & 0xFF;
|
||||
white = colorcode & 0xFF;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
#else
|
||||
|
||||
struct Pixel {
|
||||
union {
|
||||
uint8_t raw[3];
|
||||
struct {
|
||||
uint8_t red;
|
||||
uint8_t green;
|
||||
uint8_t blue;
|
||||
};
|
||||
};
|
||||
|
||||
inline Pixel(uint8_t r, uint8_t g, uint8_t b) __attribute__((always_inline))
|
||||
: red(r), green(g), blue(b) {
|
||||
// brigthness =0xE0 |(br&31);
|
||||
}
|
||||
|
||||
inline Pixel() __attribute__((always_inline)) {}
|
||||
|
||||
#ifdef USE_FASTLED
|
||||
inline Pixel &operator=(const CRGB &rhs) __attribute__((always_inline)) {
|
||||
red = rhs.r;
|
||||
green = rhs.g;
|
||||
blue = rhs.b;
|
||||
return *this;
|
||||
}
|
||||
inline Pixel &operator=(CRGB &rhs) __attribute__((always_inline)) {
|
||||
red = rhs.r;
|
||||
green = rhs.g;
|
||||
blue = rhs.b;
|
||||
return *this;
|
||||
}
|
||||
inline Pixel(const CRGB &rhs) __attribute__((always_inline)) {
|
||||
red = rhs.r;
|
||||
green = rhs.g;
|
||||
blue = rhs.b;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline Pixel(const Pixel &rhs) __attribute__((always_inline)) {
|
||||
// brigthness=rhs.brigthness;
|
||||
red = rhs.red;
|
||||
green = rhs.green;
|
||||
blue = rhs.blue;
|
||||
}
|
||||
inline Pixel &operator=(const uint32_t colorcode)
|
||||
__attribute__((always_inline)) {
|
||||
// rgb colorg;
|
||||
red = (colorcode >> 16) & 0xFF;
|
||||
green = (colorcode >> 8) & 0xFF;
|
||||
blue = (colorcode >> 0) & 0xFF;
|
||||
return *this;
|
||||
}
|
||||
inline __attribute__((always_inline)) bool operator==(const Pixel &rhs) {
|
||||
return (red == rhs.red) && (green == rhs.green) && (blue == rhs.blue);
|
||||
}
|
||||
inline __attribute__((always_inline)) bool operator!=(const Pixel &rhs) {
|
||||
return !((red == rhs.red) && (green == rhs.green) &&
|
||||
(blue == rhs.blue));
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
enum class leddirection { FORWARD, BACKWARD, MAP };
|
||||
|
||||
class Pixels {
|
||||
public:
|
||||
inline Pixels() __attribute__((always_inline)) {}
|
||||
inline Pixels(const Pixels &rhs) __attribute__((always_inline)) {
|
||||
_size = rhs._size;
|
||||
_direction = rhs._direction;
|
||||
_num_strips = rhs._num_strips;
|
||||
for (int i = 0; i < _num_strips; i++) {
|
||||
_sizes[i] = rhs._sizes[i];
|
||||
}
|
||||
ledpointer = rhs.ledpointer;
|
||||
mapFunction = rhs.mapFunction;
|
||||
|
||||
// parent=rhs.parent;
|
||||
}
|
||||
Pixels(int size, Pixel *ledpoi) {
|
||||
Pixels(size, ledpoi, leddirection::FORWARD);
|
||||
}
|
||||
|
||||
Pixels(int size, Pixel *ledpoi, leddirection direction) {
|
||||
__Pixels(size, ledpoi, direction, this);
|
||||
}
|
||||
|
||||
void __Pixels(int size, Pixel *ledpoi, leddirection direction,
|
||||
Pixels *pib) {
|
||||
pib->_size = size;
|
||||
pib->ledpointer = ledpoi;
|
||||
pib->_num_strips = 0;
|
||||
pib->_direction = direction;
|
||||
// pib->nb_child=0;
|
||||
}
|
||||
|
||||
Pixels(int num_led_per_strip, int num_strips) {
|
||||
int sizes[16];
|
||||
for (int i = 0; i < num_strips; i++) {
|
||||
sizes[i] = num_led_per_strip;
|
||||
}
|
||||
__Pixels(sizes, num_strips, leddirection::FORWARD, this);
|
||||
}
|
||||
|
||||
Pixels(int *sizes, int num_strips) {
|
||||
__Pixels(sizes, num_strips, leddirection::FORWARD, this);
|
||||
}
|
||||
|
||||
Pixels(int *sizes, int num_strips, leddirection direction) {
|
||||
__Pixels(sizes, num_strips, direction, this);
|
||||
}
|
||||
void __Pixels(int *sizes, int num_strips, leddirection direction,
|
||||
Pixels *pib) {
|
||||
int size = 0;
|
||||
for (int i = 0; i < num_strips; i++) {
|
||||
size += sizes[i];
|
||||
pib->_sizes[i] = sizes[i];
|
||||
}
|
||||
|
||||
pib->_num_strips = num_strips;
|
||||
|
||||
ledpointer = (Pixel *)calloc(size, sizeof(Pixel));
|
||||
if (ledpointer == NULL) {
|
||||
pib->_size = 0;
|
||||
} else {
|
||||
pib->_size = size;
|
||||
}
|
||||
pib->_direction = direction;
|
||||
}
|
||||
Pixel &operator[](int i) {
|
||||
switch (_direction) {
|
||||
|
||||
case (leddirection::FORWARD):
|
||||
|
||||
return *(ledpointer + i % _size);
|
||||
break;
|
||||
|
||||
case (leddirection::BACKWARD):
|
||||
|
||||
return *(ledpointer + (_size - i % (_size)-1));
|
||||
break;
|
||||
|
||||
case (leddirection::MAP):
|
||||
if (mapFunction) {
|
||||
int offset = mapFunction(i, arguments);
|
||||
// printf("%d %d\n",i,offset);
|
||||
if (offset == _OUT_OF_BOUND) {
|
||||
return offPixel;
|
||||
} else
|
||||
return *(ledpointer + (mapFunction(i, arguments) % _size));
|
||||
}
|
||||
|
||||
else
|
||||
return *(ledpointer);
|
||||
break;
|
||||
default:
|
||||
return *(ledpointer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void copy(Pixels ori) { copy(ori, leddirection::FORWARD); }
|
||||
|
||||
void copy(Pixels ori, leddirection dir) {
|
||||
leddirection ledd = _direction;
|
||||
if (_direction == leddirection::MAP)
|
||||
ledd = leddirection::FORWARD;
|
||||
for (int i = 0; i < ori._size; i++) {
|
||||
if (ledd == dir) {
|
||||
(*this)[i] = ori[i];
|
||||
} else {
|
||||
(*this)[i] = ori[ori._size - i % (ori._size) - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pixels getStrip(int num_strip, leddirection direction) {
|
||||
if (_num_strips == 0 or _num_strips < num_strip) {
|
||||
|
||||
int d[0];
|
||||
return Pixels(d, 1, direction);
|
||||
} else {
|
||||
uint32_t off = 0;
|
||||
for (int i = 0; i < num_strip % _num_strips; i++) {
|
||||
off += _sizes[i];
|
||||
}
|
||||
|
||||
return Pixels(_sizes[num_strip], ledpointer + off, direction);
|
||||
}
|
||||
}
|
||||
|
||||
Pixels getStrip(int num_strip) {
|
||||
return getStrip(num_strip, leddirection::FORWARD);
|
||||
}
|
||||
|
||||
int *getLengths() { return _sizes; }
|
||||
|
||||
int getNumStrip() { return _num_strips; }
|
||||
uint8_t *getPixels() { return (uint8_t *)ledpointer; }
|
||||
void clear() {
|
||||
// memset(ledpointer,0,_size*sizeof(Pixel));
|
||||
}
|
||||
|
||||
Pixels createSubset(int start, int length) {
|
||||
return createSubset(start, length, leddirection::FORWARD);
|
||||
}
|
||||
|
||||
Pixels createSubset(int start, leddirection direction) {
|
||||
if (start < 0)
|
||||
start = 0;
|
||||
return Pixels(_size, ledpointer + start, direction);
|
||||
}
|
||||
|
||||
Pixels createSubset(int start, int length, leddirection direction) {
|
||||
if (start < 0)
|
||||
start = 0;
|
||||
if (length <= 0)
|
||||
length = 1;
|
||||
return Pixels(length, ledpointer + start, direction);
|
||||
}
|
||||
/*
|
||||
Pixels getParent()
|
||||
{
|
||||
return *parent;
|
||||
}
|
||||
|
||||
Pixels * getChild(int i)
|
||||
{
|
||||
|
||||
return children[i%nb_child];
|
||||
}
|
||||
*/
|
||||
inline void setMapFunction(int (*fptr)(int i, void *args), void *args,
|
||||
int size) {
|
||||
mapFunction = fptr;
|
||||
if (arguments == NULL)
|
||||
arguments = (void *)malloc(sizeof(size));
|
||||
memcpy(arguments, args, size);
|
||||
}
|
||||
|
||||
private:
|
||||
Pixel *ledpointer;
|
||||
size_t _size = 0;
|
||||
int _sizes[16];
|
||||
int _num_strips = 0;
|
||||
leddirection _direction;
|
||||
// int nb_child;
|
||||
// Pixels *parent;
|
||||
void *arguments;
|
||||
// Pixels **children;
|
||||
int (*mapFunction)(int i, void *args);
|
||||
/*
|
||||
* this is the pixel to retuen when out of bound
|
||||
*/
|
||||
Pixel offPixel;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
Reference in New Issue
Block a user