mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-06 11:36:51 +03:00
Update smartled driver (#3500)
* update-SmartLeds * Update sdkconfig.defaults
This commit is contained in:
@@ -1,7 +1,33 @@
|
||||
/********************************************************************************
|
||||
* https://github.com/RoboticsBrno/SmartLeds
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Color.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -29,7 +55,7 @@ int iRgbSqrt( int num ) {
|
||||
return res;
|
||||
}
|
||||
|
||||
Rgb::Rgb( Hsv y ) {
|
||||
Rgb::Rgb(const Hsv& y) {
|
||||
// https://stackoverflow.com/questions/24152553/hsv-to-rgb-and-back-without-floating-point-math-in-python
|
||||
// greyscale
|
||||
if (y.s == 0) {
|
||||
@@ -45,31 +71,56 @@ Rgb::Rgb( Hsv y ) {
|
||||
const int t = (y.v * (255 - ((y.s * (255 - remainder)) >> 8))) >> 8;
|
||||
|
||||
switch (region) {
|
||||
case 0: r = y.v; g = t; b = p; break;
|
||||
case 1: r = q; g = y.v; b = p; break;
|
||||
case 2: r = p; g = y.v; b = t; break;
|
||||
case 3: r = p; g = q; b = y.v; break;
|
||||
case 4: r = t; g = p; b = y.v; break;
|
||||
case 5: r = y.v; g = p; b = q; break;
|
||||
default: __builtin_trap();
|
||||
case 0:
|
||||
r = y.v;
|
||||
g = t;
|
||||
b = p;
|
||||
break;
|
||||
case 1:
|
||||
r = q;
|
||||
g = y.v;
|
||||
b = p;
|
||||
break;
|
||||
case 2:
|
||||
r = p;
|
||||
g = y.v;
|
||||
b = t;
|
||||
break;
|
||||
case 3:
|
||||
r = p;
|
||||
g = q;
|
||||
b = y.v;
|
||||
break;
|
||||
case 4:
|
||||
r = t;
|
||||
g = p;
|
||||
b = y.v;
|
||||
break;
|
||||
case 5:
|
||||
r = y.v;
|
||||
g = p;
|
||||
b = q;
|
||||
break;
|
||||
default:
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
a = y.a;
|
||||
}
|
||||
|
||||
Rgb& Rgb::operator=( Hsv hsv ) {
|
||||
Rgb& Rgb::operator=(const Hsv& hsv) {
|
||||
Rgb r { hsv };
|
||||
swap(r);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rgb Rgb::operator+( Rgb in ) const {
|
||||
Rgb Rgb::operator+(const Rgb& in) const {
|
||||
auto copy = *this;
|
||||
copy += in;
|
||||
return copy;
|
||||
}
|
||||
|
||||
Rgb& Rgb::operator+=( Rgb in ) {
|
||||
Rgb& Rgb::operator+=(const Rgb& in) {
|
||||
unsigned int red = r + in.r;
|
||||
r = (red < 255) ? red : 255;
|
||||
unsigned int green = g + in.g;
|
||||
@@ -79,7 +130,20 @@ Rgb& Rgb::operator+=( Rgb in ) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rgb& Rgb::blend( Rgb in ) {
|
||||
Rgb Rgb::operator-(const Rgb& in) const {
|
||||
auto copy = *this;
|
||||
copy -= in;
|
||||
return copy;
|
||||
}
|
||||
|
||||
Rgb& Rgb::operator-=(const Rgb& in) {
|
||||
r = (in.r > r) ? 0 : r - in.r;
|
||||
g = (in.g > g) ? 0 : g - in.g;
|
||||
b = (in.b > b) ? 0 : b - in.b;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rgb& Rgb::blend(const Rgb& in) {
|
||||
unsigned int inAlpha = in.a * (255 - a);
|
||||
unsigned int alpha = a + inAlpha;
|
||||
r = iRgbSqrt(((r * r * a) + (in.r * in.r * inAlpha)) / alpha);
|
||||
@@ -89,16 +153,7 @@ Rgb& Rgb::blend( Rgb in ) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8_t IRAM_ATTR Rgb::getGrb( int idx ) {
|
||||
switch ( idx ) {
|
||||
case 0: return g;
|
||||
case 1: return r;
|
||||
case 2: return b;
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Hsv::Hsv( Rgb r ) {
|
||||
Hsv::Hsv(const Rgb& r) {
|
||||
int min = std::min(r.r, std::min(r.g, r.b));
|
||||
int max = std::max(r.r, std::max(r.g, r.b));
|
||||
int chroma = max - min;
|
||||
@@ -125,7 +180,7 @@ Hsv::Hsv( Rgb r ) {
|
||||
a = r.a;
|
||||
}
|
||||
|
||||
Hsv& Hsv::operator=( Rgb rgb ) {
|
||||
Hsv& Hsv::operator=(const Rgb& rgb) {
|
||||
Hsv h { rgb };
|
||||
swap(h);
|
||||
return *this;
|
||||
|
||||
@@ -1,34 +1,77 @@
|
||||
/********************************************************************************
|
||||
* https://github.com/RoboticsBrno/SmartLeds
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef COLOR_H
|
||||
#define COLOR_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "esp_attr.h"
|
||||
#include <cstdint>
|
||||
union Hsv;
|
||||
|
||||
union Rgb {
|
||||
struct __attribute__((packed)) {
|
||||
uint8_t r, g, b, a;
|
||||
uint8_t g, r, b, a;
|
||||
};
|
||||
uint32_t value;
|
||||
|
||||
Rgb( uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255 ) : r( r ), g( g ), b( b ), a( a ) {}
|
||||
Rgb( Hsv c );
|
||||
Rgb& operator=( Rgb rgb ) { swap( rgb ); return *this; }
|
||||
Rgb& operator=( Hsv hsv );
|
||||
Rgb operator+( Rgb in ) const;
|
||||
Rgb& operator+=( Rgb in );
|
||||
bool operator==( Rgb in ) const { return in.value == value; }
|
||||
Rgb& blend( Rgb in );
|
||||
void swap( Rgb& o ) { value = o.value; }
|
||||
Rgb(uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255)
|
||||
: g(g)
|
||||
, r(r)
|
||||
, b(b)
|
||||
, a(a) {}
|
||||
Rgb(const Hsv& c);
|
||||
Rgb(const Rgb&) = default;
|
||||
Rgb& operator=(const Rgb& rgb) {
|
||||
swap(rgb);
|
||||
return *this;
|
||||
}
|
||||
Rgb& operator=(const Hsv& hsv);
|
||||
Rgb operator+(const Rgb& in) const;
|
||||
Rgb& operator+=(const Rgb& in);
|
||||
Rgb operator-(const Rgb& in) const;
|
||||
Rgb& operator-=(const Rgb& in);
|
||||
bool operator==(const Rgb& in) const { return in.value == value; }
|
||||
Rgb& blend(const Rgb& in);
|
||||
void swap(const Rgb& o) { value = o.value; }
|
||||
void linearize() {
|
||||
r = channelGamma(r);
|
||||
g = channelGamma(g);
|
||||
b = channelGamma(b);
|
||||
}
|
||||
|
||||
uint8_t IRAM_ATTR getGrb( int idx );
|
||||
inline uint8_t IRAM_ATTR getGrb(int idx) {
|
||||
switch (idx) {
|
||||
case 0:
|
||||
return g;
|
||||
case 1:
|
||||
return r;
|
||||
case 2:
|
||||
return b;
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void stretchChannels(uint8_t maxR, uint8_t maxG, uint8_t maxB) {
|
||||
r = stretch(r, maxR);
|
||||
@@ -36,14 +79,10 @@ union Rgb {
|
||||
b = stretch(b, maxB);
|
||||
}
|
||||
|
||||
void stretchChannelsEvenly( uint8_t max ) {
|
||||
stretchChannels( max, max, max );
|
||||
}
|
||||
void stretchChannelsEvenly(uint8_t max) { stretchChannels(max, max, max); }
|
||||
|
||||
private:
|
||||
uint8_t stretch( int value, uint8_t max ) {
|
||||
return ( value * max ) >> 8;
|
||||
}
|
||||
uint8_t stretch(int value, uint8_t max) { return (value * max) >> 8; }
|
||||
|
||||
uint8_t channelGamma(int channel) {
|
||||
/* The optimal gamma correction is x^2.8. However, this is expensive to
|
||||
@@ -63,12 +102,17 @@ union Hsv {
|
||||
};
|
||||
uint32_t value;
|
||||
|
||||
Hsv( uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255 ) : h( h ), s( s ), v( v ), a( a ) {}
|
||||
Hsv( Rgb r );
|
||||
Hsv& operator=( Hsv h ) { swap( h ); return *this; }
|
||||
Hsv& operator=( Rgb rgb );
|
||||
bool operator==( Hsv in ) const { return in.value == value; }
|
||||
void swap( Hsv& o ) { value = o.value; }
|
||||
Hsv(uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255)
|
||||
: h(h)
|
||||
, s(s)
|
||||
, v(v)
|
||||
, a(a) {}
|
||||
Hsv(const Rgb& r);
|
||||
Hsv& operator=(const Hsv& h) {
|
||||
swap(h);
|
||||
return *this;
|
||||
}
|
||||
Hsv& operator=(const Rgb& rgb);
|
||||
bool operator==(const Hsv& in) const { return in.value == value; }
|
||||
void swap(const Hsv& o) { value = o.value; }
|
||||
};
|
||||
|
||||
#endif //COLOR_H
|
||||
|
||||
60
code/components/jomjol_controlGPIO/RmtDriver.h
Normal file
60
code/components/jomjol_controlGPIO/RmtDriver.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/********************************************************************************
|
||||
* https://github.com/RoboticsBrno/SmartLeds
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <esp_system.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(ESP_IDF_VERSION)
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#define SMARTLEDS_NEW_RMT_DRIVER 1
|
||||
#else
|
||||
#define SMARTLEDS_NEW_RMT_DRIVER 0
|
||||
#endif
|
||||
#else
|
||||
#define SMARTLEDS_NEW_RMT_DRIVER 0
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct TimingParams {
|
||||
uint32_t T0H;
|
||||
uint32_t T1H;
|
||||
uint32_t T0L;
|
||||
uint32_t T1L;
|
||||
uint32_t TRS;
|
||||
};
|
||||
|
||||
using LedType = TimingParams;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#if SMARTLEDS_NEW_RMT_DRIVER
|
||||
#include "RmtDriver5.h"
|
||||
#else
|
||||
#include "RmtDriver4.h"
|
||||
#endif
|
||||
143
code/components/jomjol_controlGPIO/RmtDriver4.cpp
Normal file
143
code/components/jomjol_controlGPIO/RmtDriver4.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/********************************************************************************
|
||||
* https://github.com/RoboticsBrno/SmartLeds
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "RmtDriver4.h"
|
||||
|
||||
#if !SMARTLEDS_NEW_RMT_DRIVER
|
||||
#include "SmartLeds.h"
|
||||
|
||||
namespace detail {
|
||||
|
||||
// 8 still seems to work, but timings become marginal
|
||||
static const int DIVIDER = 4;
|
||||
// minimum time of a single RMT duration based on clock ns
|
||||
static const double RMT_DURATION_NS = 12.5;
|
||||
|
||||
RmtDriver::RmtDriver(const LedType& timing, int count, int pin, int channel_num, SemaphoreHandle_t finishedFlag)
|
||||
: _timing(timing)
|
||||
, _count(count)
|
||||
, _pin((gpio_num_t)pin)
|
||||
, _finishedFlag(finishedFlag)
|
||||
, _channel((rmt_channel_t)channel_num) {
|
||||
_bitToRmt[0].level0 = 1;
|
||||
_bitToRmt[0].level1 = 0;
|
||||
_bitToRmt[0].duration0 = _timing.T0H / (RMT_DURATION_NS * DIVIDER);
|
||||
_bitToRmt[0].duration1 = _timing.T0L / (RMT_DURATION_NS * DIVIDER);
|
||||
|
||||
_bitToRmt[1].level0 = 1;
|
||||
_bitToRmt[1].level1 = 0;
|
||||
_bitToRmt[1].duration0 = _timing.T1H / (RMT_DURATION_NS * DIVIDER);
|
||||
_bitToRmt[1].duration1 = _timing.T1L / (RMT_DURATION_NS * DIVIDER);
|
||||
}
|
||||
|
||||
esp_err_t RmtDriver::init() {
|
||||
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(_pin, _channel);
|
||||
config.rmt_mode = RMT_MODE_TX;
|
||||
config.clk_div = DIVIDER;
|
||||
config.mem_block_num = 1;
|
||||
|
||||
return rmt_config(&config);
|
||||
}
|
||||
|
||||
esp_err_t RmtDriver::registerIsr(bool isFirstRegisteredChannel) {
|
||||
auto err = rmt_driver_install(_channel, 0,
|
||||
#if defined(CONFIG_RMT_ISR_IRAM_SAFE)
|
||||
ESP_INTR_FLAG_IRAM
|
||||
#else
|
||||
0
|
||||
#endif
|
||||
);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (isFirstRegisteredChannel) {
|
||||
rmt_register_tx_end_callback(txEndCallback, NULL);
|
||||
}
|
||||
|
||||
err = rmt_translator_init(_channel, translateSample);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
return rmt_translator_set_context(_channel, this);
|
||||
}
|
||||
|
||||
esp_err_t RmtDriver::unregisterIsr() { return rmt_driver_uninstall(_channel); }
|
||||
|
||||
void IRAM_ATTR RmtDriver::txEndCallback(rmt_channel_t channel, void* arg) {
|
||||
xSemaphoreGiveFromISR(SmartLed::ledForChannel(channel)->_finishedFlag, nullptr);
|
||||
}
|
||||
|
||||
void IRAM_ATTR RmtDriver::translateSample(const void* src, rmt_item32_t* dest, size_t src_size,
|
||||
size_t wanted_rmt_items_num, size_t* out_consumed_src_bytes, size_t* out_used_rmt_items) {
|
||||
RmtDriver* self;
|
||||
ESP_ERROR_CHECK(rmt_translator_get_context(out_used_rmt_items, (void**)&self));
|
||||
|
||||
const auto& _bitToRmt = self->_bitToRmt;
|
||||
const auto src_offset = self->_translatorSourceOffset;
|
||||
|
||||
auto* src_components = (const uint8_t*)src;
|
||||
size_t consumed_src_bytes = 0;
|
||||
size_t used_rmt_items = 0;
|
||||
|
||||
while (consumed_src_bytes < src_size && used_rmt_items + 7 < wanted_rmt_items_num) {
|
||||
uint8_t val = *src_components;
|
||||
|
||||
// each bit, from highest to lowest
|
||||
for (uint8_t j = 0; j != 8; j++, val <<= 1) {
|
||||
dest->val = _bitToRmt[val >> 7].val;
|
||||
++dest;
|
||||
}
|
||||
|
||||
used_rmt_items += 8;
|
||||
++src_components;
|
||||
++consumed_src_bytes;
|
||||
|
||||
// skip alpha byte
|
||||
if (((src_offset + consumed_src_bytes) % 4) == 3) {
|
||||
++src_components;
|
||||
++consumed_src_bytes;
|
||||
|
||||
// TRST delay after last pixel in strip
|
||||
if (consumed_src_bytes == src_size) {
|
||||
(dest - 1)->duration1 = self->_timing.TRS / (detail::RMT_DURATION_NS * detail::DIVIDER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self->_translatorSourceOffset = src_offset + consumed_src_bytes;
|
||||
*out_consumed_src_bytes = consumed_src_bytes;
|
||||
*out_used_rmt_items = used_rmt_items;
|
||||
}
|
||||
|
||||
esp_err_t RmtDriver::transmit(const Rgb* buffer) {
|
||||
static_assert(sizeof(Rgb) == 4); // The translator code above assumes RGB is 4 bytes
|
||||
|
||||
_translatorSourceOffset = 0;
|
||||
return rmt_write_sample(_channel, (const uint8_t*)buffer, _count * 4, false);
|
||||
}
|
||||
};
|
||||
#endif // !SMARTLEDS_NEW_RMT_DRIVER
|
||||
68
code/components/jomjol_controlGPIO/RmtDriver4.h
Normal file
68
code/components/jomjol_controlGPIO/RmtDriver4.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/********************************************************************************
|
||||
* https://github.com/RoboticsBrno/SmartLeds
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RmtDriver.h"
|
||||
|
||||
#if !SMARTLEDS_NEW_RMT_DRIVER
|
||||
#include "Color.h"
|
||||
#include <driver/rmt.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr const int CHANNEL_COUNT = RMT_CHANNEL_MAX;
|
||||
|
||||
class RmtDriver {
|
||||
public:
|
||||
RmtDriver(const LedType& timing, int count, int pin, int channel_num, SemaphoreHandle_t finishedFlag);
|
||||
RmtDriver(const RmtDriver&) = delete;
|
||||
|
||||
esp_err_t init();
|
||||
esp_err_t registerIsr(bool isFirstRegisteredChannel);
|
||||
esp_err_t unregisterIsr();
|
||||
esp_err_t transmit(const Rgb* buffer);
|
||||
|
||||
private:
|
||||
static void IRAM_ATTR txEndCallback(rmt_channel_t channel, void* arg);
|
||||
|
||||
static void IRAM_ATTR translateSample(const void* src, rmt_item32_t* dest, size_t src_size,
|
||||
size_t wanted_rmt_items_num, size_t* out_consumed_src_bytes, size_t* out_used_rmt_items);
|
||||
|
||||
const LedType& _timing;
|
||||
int _count;
|
||||
gpio_num_t _pin;
|
||||
SemaphoreHandle_t _finishedFlag;
|
||||
|
||||
rmt_channel_t _channel;
|
||||
rmt_item32_t _bitToRmt[2];
|
||||
size_t _translatorSourceOffset;
|
||||
};
|
||||
|
||||
};
|
||||
#endif // !SMARTLEDS_NEW_RMT_DRIVER
|
||||
202
code/components/jomjol_controlGPIO/RmtDriver5.cpp
Normal file
202
code/components/jomjol_controlGPIO/RmtDriver5.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
/********************************************************************************
|
||||
* https://github.com/RoboticsBrno/SmartLeds
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "RmtDriver5.h"
|
||||
|
||||
#if SMARTLEDS_NEW_RMT_DRIVER
|
||||
#include <cstddef>
|
||||
|
||||
#include "SmartLeds.h"
|
||||
|
||||
namespace detail {
|
||||
|
||||
static constexpr const uint32_t RMT_RESOLUTION_HZ = 20 * 1000 * 1000; // 20 MHz
|
||||
static constexpr const uint32_t RMT_NS_PER_TICK = 1000000000LLU / RMT_RESOLUTION_HZ;
|
||||
|
||||
static RmtEncoderWrapper* IRAM_ATTR encSelf(rmt_encoder_t* encoder) {
|
||||
return (RmtEncoderWrapper*)(((intptr_t)encoder) - offsetof(RmtEncoderWrapper, base));
|
||||
}
|
||||
|
||||
static size_t IRAM_ATTR encEncode(rmt_encoder_t* encoder, rmt_channel_handle_t tx_channel, const void* primary_data,
|
||||
size_t data_size, rmt_encode_state_t* ret_state) {
|
||||
auto* self = encSelf(encoder);
|
||||
|
||||
// Delay after last pixel
|
||||
if ((self->last_state & RMT_ENCODING_COMPLETE) && self->frame_idx == data_size) {
|
||||
*ret_state = (rmt_encode_state_t)0;
|
||||
return self->copy_encoder->encode(
|
||||
self->copy_encoder, tx_channel, (const void*)&self->reset_code, sizeof(self->reset_code), ret_state);
|
||||
}
|
||||
|
||||
if (self->last_state & RMT_ENCODING_COMPLETE) {
|
||||
Rgb* pixel = ((Rgb*)primary_data) + self->frame_idx;
|
||||
self->buffer_len = sizeof(self->buffer);
|
||||
for (size_t i = 0; i < sizeof(self->buffer); ++i) {
|
||||
self->buffer[i] = pixel->getGrb(self->component_idx);
|
||||
if (++self->component_idx == 3) {
|
||||
self->component_idx = 0;
|
||||
if (++self->frame_idx == data_size) {
|
||||
self->buffer_len = i + 1;
|
||||
break;
|
||||
}
|
||||
++pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self->last_state = (rmt_encode_state_t)0;
|
||||
auto encoded_symbols = self->bytes_encoder->encode(
|
||||
self->bytes_encoder, tx_channel, (const void*)&self->buffer, self->buffer_len, &self->last_state);
|
||||
if (self->last_state & RMT_ENCODING_MEM_FULL) {
|
||||
*ret_state = RMT_ENCODING_MEM_FULL;
|
||||
} else {
|
||||
*ret_state = (rmt_encode_state_t)0;
|
||||
}
|
||||
|
||||
return encoded_symbols;
|
||||
}
|
||||
|
||||
static esp_err_t encReset(rmt_encoder_t* encoder) {
|
||||
auto* self = encSelf(encoder);
|
||||
rmt_encoder_reset(self->bytes_encoder);
|
||||
rmt_encoder_reset(self->copy_encoder);
|
||||
self->last_state = RMT_ENCODING_COMPLETE;
|
||||
self->frame_idx = 0;
|
||||
self->component_idx = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t encDelete(rmt_encoder_t* encoder) {
|
||||
auto* self = encSelf(encoder);
|
||||
rmt_del_encoder(self->bytes_encoder);
|
||||
rmt_del_encoder(self->copy_encoder);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
RmtDriver::RmtDriver(const LedType& timing, int count, int pin, int channel_num, SemaphoreHandle_t finishedFlag)
|
||||
: _timing(timing)
|
||||
, _count(count)
|
||||
, _pin(pin)
|
||||
, _finishedFlag(finishedFlag)
|
||||
, _channel(nullptr)
|
||||
, _encoder {} {}
|
||||
|
||||
esp_err_t RmtDriver::init() {
|
||||
_encoder.base.encode = encEncode;
|
||||
_encoder.base.reset = encReset;
|
||||
_encoder.base.del = encDelete;
|
||||
|
||||
_encoder.reset_code.duration0 = _timing.TRS / RMT_NS_PER_TICK;
|
||||
|
||||
rmt_bytes_encoder_config_t bytes_cfg = {
|
||||
.bit0 = {
|
||||
.duration0 = uint16_t(_timing.T0H / RMT_NS_PER_TICK),
|
||||
.level0 = 1,
|
||||
.duration1 = uint16_t(_timing.T0L / RMT_NS_PER_TICK),
|
||||
.level1 = 0,
|
||||
},
|
||||
.bit1 = {
|
||||
.duration0 = uint16_t(_timing.T1H / RMT_NS_PER_TICK),
|
||||
.level0 = 1,
|
||||
.duration1 = uint16_t(_timing.T1L / RMT_NS_PER_TICK),
|
||||
.level1 = 0,
|
||||
},
|
||||
.flags = {
|
||||
.msb_first = 1,
|
||||
},
|
||||
};
|
||||
|
||||
auto err = rmt_new_bytes_encoder(&bytes_cfg, &_encoder.bytes_encoder);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
rmt_copy_encoder_config_t copy_cfg = {};
|
||||
err = rmt_new_copy_encoder(©_cfg, &_encoder.copy_encoder);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// The config must be in registerIsr, because rmt_new_tx_channel
|
||||
// registers the ISR
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t RmtDriver::registerIsr(bool isFirstRegisteredChannel) {
|
||||
rmt_tx_channel_config_t conf = {
|
||||
.gpio_num = (gpio_num_t)_pin,
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT, //.clk_src = RMT_CLK_SRC_APB,
|
||||
.resolution_hz = RMT_RESOLUTION_HZ,
|
||||
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
|
||||
.trans_queue_depth = 1,
|
||||
.flags = {},
|
||||
};
|
||||
|
||||
auto err = rmt_new_tx_channel(&conf, &_channel);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
rmt_tx_event_callbacks_t callbacks_cfg = {};
|
||||
callbacks_cfg.on_trans_done = txDoneCallback;
|
||||
|
||||
err = rmt_tx_register_event_callbacks(_channel, &callbacks_cfg, this);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return rmt_enable(_channel);
|
||||
}
|
||||
|
||||
esp_err_t RmtDriver::unregisterIsr() {
|
||||
auto err = rmt_del_encoder(&_encoder.base);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = rmt_disable(_channel);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return rmt_del_channel(_channel);
|
||||
}
|
||||
|
||||
bool IRAM_ATTR RmtDriver::txDoneCallback(
|
||||
rmt_channel_handle_t tx_chan, const rmt_tx_done_event_data_t* edata, void* user_ctx) {
|
||||
auto* self = (RmtDriver*)user_ctx;
|
||||
auto taskWoken = pdTRUE;
|
||||
xSemaphoreGiveFromISR(self->_finishedFlag, &taskWoken);
|
||||
return taskWoken == pdTRUE;
|
||||
}
|
||||
|
||||
esp_err_t RmtDriver::transmit(const Rgb* buffer) {
|
||||
rmt_encoder_reset(&_encoder.base);
|
||||
rmt_transmit_config_t cfg = {};
|
||||
return rmt_transmit(_channel, &_encoder.base, buffer, _count, &cfg);
|
||||
}
|
||||
};
|
||||
#endif // !SMARTLEDS_NEW_RMT_DRIVER
|
||||
91
code/components/jomjol_controlGPIO/RmtDriver5.h
Normal file
91
code/components/jomjol_controlGPIO/RmtDriver5.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/********************************************************************************
|
||||
* https://github.com/RoboticsBrno/SmartLeds
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RmtDriver.h"
|
||||
|
||||
#if SMARTLEDS_NEW_RMT_DRIVER
|
||||
#include <driver/rmt_tx.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include "Color.h"
|
||||
|
||||
#if !defined(CONFIG_RMT_ISR_IRAM_SAFE) && !defined(SMARTLEDS_DISABLE_IRAM_WARNING)
|
||||
#warning "Please enable CONFIG_RMT_ISR_IRAM_SAFE IDF option." \
|
||||
"without it, the IDF driver is not able to supply data fast enough."
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr const int CHANNEL_COUNT = SOC_RMT_GROUPS * SOC_RMT_CHANNELS_PER_GROUP;
|
||||
|
||||
class RmtDriver;
|
||||
|
||||
// This is ridiculous
|
||||
struct RmtEncoderWrapper {
|
||||
struct rmt_encoder_t base;
|
||||
struct rmt_encoder_t* bytes_encoder;
|
||||
struct rmt_encoder_t* copy_encoder;
|
||||
RmtDriver* driver;
|
||||
rmt_symbol_word_t reset_code;
|
||||
|
||||
uint8_t buffer[SOC_RMT_MEM_WORDS_PER_CHANNEL / 8];
|
||||
rmt_encode_state_t last_state;
|
||||
size_t frame_idx;
|
||||
uint8_t component_idx;
|
||||
uint8_t buffer_len;
|
||||
};
|
||||
|
||||
static_assert(std::is_standard_layout<RmtEncoderWrapper>::value == true);
|
||||
|
||||
class RmtDriver {
|
||||
public:
|
||||
RmtDriver(const LedType& timing, int count, int pin, int channel_num, SemaphoreHandle_t finishedFlag);
|
||||
RmtDriver(const RmtDriver&) = delete;
|
||||
|
||||
esp_err_t init();
|
||||
esp_err_t registerIsr(bool isFirstRegisteredChannel);
|
||||
esp_err_t unregisterIsr();
|
||||
esp_err_t transmit(const Rgb* buffer);
|
||||
|
||||
private:
|
||||
static bool IRAM_ATTR txDoneCallback(
|
||||
rmt_channel_handle_t tx_chan, const rmt_tx_done_event_data_t* edata, void* user_ctx);
|
||||
|
||||
const LedType& _timing;
|
||||
int _count;
|
||||
int _pin;
|
||||
SemaphoreHandle_t _finishedFlag;
|
||||
|
||||
rmt_channel_handle_t _channel;
|
||||
RmtEncoderWrapper _encoder;
|
||||
};
|
||||
|
||||
};
|
||||
#endif // !SMARTLEDS_NEW_RMT_DRIVER
|
||||
@@ -1,90 +1,35 @@
|
||||
/********************************************************************************
|
||||
* https://github.com/RoboticsBrno/SmartLeds
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*******************************************************************************/
|
||||
|
||||
#include "SmartLeds.h"
|
||||
|
||||
|
||||
/* PlatformIO 6 (ESP IDF 5) does no longer allow access to RMTMEM,
|
||||
see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/release-5.x/5.0/peripherals.html?highlight=rmtmem#id5
|
||||
As a dirty workaround, we copy the needed structures from rmt_struct.h
|
||||
In the long run, this should be replaced! */
|
||||
typedef struct rmt_item32_s {
|
||||
union {
|
||||
struct {
|
||||
uint32_t duration0 :15;
|
||||
uint32_t level0 :1;
|
||||
uint32_t duration1 :15;
|
||||
uint32_t level1 :1;
|
||||
};
|
||||
uint32_t val;
|
||||
};
|
||||
} rmt_item32_t;
|
||||
|
||||
//Allow access to RMT memory using RMTMEM.chan[0].data32[8]
|
||||
typedef volatile struct rmt_mem_s {
|
||||
struct {
|
||||
rmt_item32_t data32[64];
|
||||
} chan[8];
|
||||
} rmt_mem_t;
|
||||
extern rmt_mem_t RMTMEM;
|
||||
|
||||
|
||||
|
||||
IsrCore SmartLed::_interruptCore = CoreCurrent;
|
||||
intr_handle_t SmartLed::_interruptHandle = NULL;
|
||||
|
||||
SmartLed*& IRAM_ATTR SmartLed::ledForChannel(int channel) {
|
||||
static SmartLed* table[8] = { nullptr };
|
||||
assert( channel < 8 );
|
||||
static SmartLed* table[detail::CHANNEL_COUNT] = {};
|
||||
assert(channel < detail::CHANNEL_COUNT);
|
||||
return table[channel];
|
||||
}
|
||||
|
||||
void IRAM_ATTR SmartLed::interruptHandler(void*) {
|
||||
for (int channel = 0; channel != 8; channel++) {
|
||||
auto self = ledForChannel( channel );
|
||||
|
||||
if ( RMT.int_st.val & (1 << (24 + channel ) ) ) { // tx_thr_event
|
||||
if ( self )
|
||||
self->copyRmtHalfBlock();
|
||||
RMT.int_clr.val |= 1 << ( 24 + channel );
|
||||
} else if ( RMT.int_st.val & ( 1 << (3 * channel ) ) ) { // tx_end
|
||||
if ( self )
|
||||
xSemaphoreGiveFromISR( self->_finishedFlag, nullptr );
|
||||
RMT.int_clr.val |= 1 << ( 3 * channel );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR SmartLed::copyRmtHalfBlock() {
|
||||
int offset = detail::MAX_PULSES * _halfIdx;
|
||||
_halfIdx = !_halfIdx;
|
||||
int len = 3 - _componentPosition + 3 * ( _count - 1 );
|
||||
len = std::min( len, detail::MAX_PULSES / 8 );
|
||||
|
||||
if ( !len ) {
|
||||
for ( int i = 0; i < detail::MAX_PULSES; i++) {
|
||||
RMTMEM.chan[ _channel].data32[i + offset ].val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int i;
|
||||
for ( i = 0; i != len && _pixelPosition != _count; i++ ) {
|
||||
uint8_t val = _buffer[ _pixelPosition ].getGrb( _componentPosition );
|
||||
for ( int j = 0; j != 8; j++, val <<= 1 ) {
|
||||
int bit = val >> 7;
|
||||
int idx = i * 8 + offset + j;
|
||||
RMTMEM.chan[ _channel ].data32[ idx ].val = _bitToRmt[ bit & 0x01 ].value;
|
||||
}
|
||||
if ( _pixelPosition == _count - 1 && _componentPosition == 2 ) {
|
||||
RMTMEM.chan[ _channel ].data32[ i * 8 + offset + 7 ].duration1 =
|
||||
_timing.TRS / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
}
|
||||
|
||||
_componentPosition++;
|
||||
if ( _componentPosition == 3 ) {
|
||||
_componentPosition = 0;
|
||||
_pixelPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i *= 8; i != detail::MAX_PULSES; i++ ) {
|
||||
RMTMEM.chan[ _channel ].data32[ i + offset ].val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,30 @@
|
||||
#pragma once
|
||||
/********************************************************************************
|
||||
* https://github.com/RoboticsBrno/SmartLeds
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 SMARTLEDS_H
|
||||
#define SMARTLEDS_H
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32.
|
||||
@@ -31,164 +54,75 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "esp_idf_version.h"
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#include "soc/periph_defs.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/gpio_periph.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
|
||||
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
|
||||
#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d)
|
||||
#define ets_delay_us(a) esp_rom_delay_us(a)
|
||||
#endif
|
||||
|
||||
#if defined ( ARDUINO )
|
||||
extern "C" { // ...someone forgot to put in the includes...
|
||||
#include "esp32-hal.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_ipc.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "soc/rmt_struct.h"
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/spi_master.h>
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
|
||||
#include "soc/dport_reg.h"
|
||||
#endif
|
||||
}
|
||||
#elif defined ( ESP_PLATFORM )
|
||||
extern "C" { // ...someone forgot to put in the includes...
|
||||
#include <esp_intr_alloc.h>
|
||||
#include <esp_ipc.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <soc/dport_reg.h>
|
||||
#include <soc/gpio_sig_map.h>
|
||||
#include <soc/rmt_struct.h>
|
||||
#include <driver/spi_master.h>
|
||||
}
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR > 1)
|
||||
#include "hal/gpio_ll.h"
|
||||
#else
|
||||
#include "soc/gpio_periph.h"
|
||||
#define esp_rom_delay_us ets_delay_us
|
||||
static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
|
||||
{
|
||||
if (gpio_num < 32) {
|
||||
return (hw->in >> gpio_num) & 0x1;
|
||||
} else {
|
||||
return (hw->in1.data >> (gpio_num - 32)) & 0x1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||
#if !(configENABLE_BACKWARD_COMPATIBILITY == 1)
|
||||
#define xSemaphoreHandle SemaphoreHandle_t
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "Color.h"
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct TimingParams {
|
||||
uint32_t T0H;
|
||||
uint32_t T1H;
|
||||
uint32_t T0L;
|
||||
uint32_t T1L;
|
||||
uint32_t TRS;
|
||||
};
|
||||
|
||||
union RmtPulsePair {
|
||||
struct {
|
||||
int duration0:15;
|
||||
int level0:1;
|
||||
int duration1:15;
|
||||
int level1:1;
|
||||
};
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
static const int DIVIDER = 4; // 8 still seems to work, but timings become marginal
|
||||
static const int MAX_PULSES = 32; // A channel has a 64 "pulse" buffer - we use half per pass
|
||||
static const double RMT_DURATION_NS = 12.5; // minimum time of a single RMT duration based on clock ns
|
||||
|
||||
} // namespace detail
|
||||
#include "RmtDriver.h"
|
||||
|
||||
using LedType = detail::TimingParams;
|
||||
|
||||
// Times are in nanoseconds,
|
||||
// The RMT driver runs at 20MHz, so minimal representable time is 50 nanoseconds
|
||||
static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 };
|
||||
static const LedType LED_WS2812B = { 400, 850, 850, 400, 50100 };
|
||||
// longer reset time because https://blog.adafruit.com/2017/05/03/psa-the-ws2812b-rgb-led-has-been-revised-will-require-code-tweak/
|
||||
static const LedType LED_WS2812B = { 400, 800, 850, 450, 300000 }; // universal
|
||||
static const LedType LED_WS2812B_NEWVARIANT = { 200, 750, 750, 200, 300000 };
|
||||
static const LedType LED_WS2812B_OLDVARIANT = { 400, 800, 850, 450, 50000 };
|
||||
// This is timing from datasheet, but does not seem to actually work - try LED_WS2812B
|
||||
static const LedType LED_WS2812C = { 250, 550, 550, 250, 280000 };
|
||||
static const LedType LED_SK6812 = { 300, 600, 900, 600, 80000 };
|
||||
static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 };
|
||||
|
||||
// Single buffer == can't touch the Rgbs between show() and wait()
|
||||
enum BufferType { SingleBuffer = 0, DoubleBuffer };
|
||||
|
||||
enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2 };
|
||||
|
||||
class SmartLed {
|
||||
public:
|
||||
friend class detail::RmtDriver;
|
||||
|
||||
// The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds
|
||||
// can't fill the RMT buffer fast enough, resulting in rendering artifacts.
|
||||
// Usually, that means you have to set isrCore == CoreSecond.
|
||||
//
|
||||
// If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running,
|
||||
// so you can't use it if you define SmartLed as global variable.
|
||||
SmartLed( const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = SingleBuffer, IsrCore isrCore = CoreCurrent)
|
||||
: _timing( type ),
|
||||
_channel( channel ),
|
||||
_count( count ),
|
||||
_firstBuffer( new Rgb[ count ] ),
|
||||
_secondBuffer( doubleBuffer ? new Rgb[ count ] : nullptr ),
|
||||
_finishedFlag( xSemaphoreCreateBinary() )
|
||||
{
|
||||
assert( channel >= 0 && channel < 8 );
|
||||
//
|
||||
// Does nothing on chips that only have one core.
|
||||
SmartLed(const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = DoubleBuffer,
|
||||
IsrCore isrCore = CoreCurrent)
|
||||
: _finishedFlag(xSemaphoreCreateBinary())
|
||||
, _driver(type, count, pin, channel, _finishedFlag)
|
||||
, _channel(channel)
|
||||
, _count(count)
|
||||
, _firstBuffer(new Rgb[count])
|
||||
, _secondBuffer(doubleBuffer ? new Rgb[count] : nullptr) {
|
||||
assert(channel >= 0 && channel < detail::CHANNEL_COUNT);
|
||||
assert(ledForChannel(channel) == nullptr);
|
||||
|
||||
xSemaphoreGive(_finishedFlag);
|
||||
|
||||
DPORT_SET_PERI_REG_MASK( DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN );
|
||||
DPORT_CLEAR_PERI_REG_MASK( DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST );
|
||||
_driver.init();
|
||||
|
||||
PIN_FUNC_SELECT( GPIO_PIN_MUX_REG[ pin ], 2 );
|
||||
gpio_set_direction( static_cast< gpio_num_t >( pin ), GPIO_MODE_OUTPUT );
|
||||
gpio_matrix_out( static_cast< gpio_num_t >( pin ), RMT_SIG_OUT0_IDX + _channel, 0, 0 );
|
||||
initChannel( _channel );
|
||||
|
||||
RMT.tx_lim_ch[ _channel ].limit = detail::MAX_PULSES;
|
||||
RMT.int_ena.val |= 1 << ( 24 + _channel );
|
||||
RMT.int_ena.val |= 1 << ( 3 * _channel );
|
||||
|
||||
_bitToRmt[ 0 ].level0 = 1;
|
||||
_bitToRmt[ 0 ].level1 = 0;
|
||||
_bitToRmt[ 0 ].duration0 = _timing.T0H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
_bitToRmt[ 0 ].duration1 = _timing.T0L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
|
||||
_bitToRmt[ 1 ].level0 = 1;
|
||||
_bitToRmt[ 1 ].level1 = 0;
|
||||
_bitToRmt[ 1 ].duration0 = _timing.T1H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
_bitToRmt[ 1 ].duration1 = _timing.T1L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
|
||||
if ( !anyAlive() ) {
|
||||
#if !defined(SOC_CPU_CORES_NUM) || SOC_CPU_CORES_NUM > 1
|
||||
if (!anyAlive() && isrCore != CoreCurrent) {
|
||||
_interruptCore = isrCore;
|
||||
if(isrCore != CoreCurrent) {
|
||||
ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, NULL));
|
||||
} else {
|
||||
registerInterrupt(NULL);
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, (void*)this));
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
registerInterrupt((void*)this);
|
||||
}
|
||||
|
||||
ledForChannel(channel) = this;
|
||||
@@ -196,28 +130,25 @@ public:
|
||||
|
||||
~SmartLed() {
|
||||
ledForChannel(_channel) = nullptr;
|
||||
if ( !anyAlive() ) {
|
||||
if(_interruptCore != CoreCurrent) {
|
||||
ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, NULL));
|
||||
} else {
|
||||
unregisterInterrupt(NULL);
|
||||
}
|
||||
#if !defined(SOC_CPU_CORES_NUM) || SOC_CPU_CORES_NUM > 1
|
||||
if (!anyAlive() && _interruptCore != CoreCurrent) {
|
||||
ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, (void*)this));
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
unregisterInterrupt((void*)this);
|
||||
}
|
||||
vSemaphoreDelete(_finishedFlag);
|
||||
}
|
||||
|
||||
Rgb& operator[]( int idx ) {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
Rgb& operator[](int idx) { return _firstBuffer[idx]; }
|
||||
|
||||
const Rgb& operator[]( int idx ) const {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
const Rgb& operator[](int idx) const { return _firstBuffer[idx]; }
|
||||
|
||||
void show() {
|
||||
_buffer = _firstBuffer.get();
|
||||
startTransmission();
|
||||
esp_err_t show() {
|
||||
esp_err_t err = startTransmission();
|
||||
swapBuffers();
|
||||
return err;
|
||||
}
|
||||
|
||||
bool wait(TickType_t timeout = portMAX_DELAY) {
|
||||
@@ -228,9 +159,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
int size() const {
|
||||
return _count;
|
||||
}
|
||||
int size() const { return _count; }
|
||||
int channel() const { return _channel; }
|
||||
|
||||
Rgb* begin() { return _firstBuffer.get(); }
|
||||
const Rgb* begin() const { return _firstBuffer.get(); }
|
||||
@@ -241,85 +171,69 @@ public:
|
||||
const Rgb* cend() const { return _firstBuffer.get() + _count; }
|
||||
|
||||
private:
|
||||
static intr_handle_t _interruptHandle;
|
||||
static IsrCore _interruptCore;
|
||||
|
||||
static void initChannel( int channel ) {
|
||||
RMT.apb_conf.fifo_mask = 1; //enable memory access, instead of FIFO mode.
|
||||
RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer
|
||||
RMT.conf_ch[ channel ].conf0.div_cnt = detail::DIVIDER;
|
||||
RMT.conf_ch[ channel ].conf0.mem_size = 1;
|
||||
RMT.conf_ch[ channel ].conf0.carrier_en = 0;
|
||||
RMT.conf_ch[ channel ].conf0.carrier_out_lv = 1;
|
||||
RMT.conf_ch[ channel ].conf0.mem_pd = 0;
|
||||
|
||||
RMT.conf_ch[ channel ].conf1.rx_en = 0;
|
||||
RMT.conf_ch[ channel ].conf1.mem_owner = 0;
|
||||
RMT.conf_ch[ channel ].conf1.tx_conti_mode = 0; //loop back mode.
|
||||
RMT.conf_ch[ channel ].conf1.ref_always_on = 1; // use apb clock: 80M
|
||||
RMT.conf_ch[ channel ].conf1.idle_out_en = 1;
|
||||
RMT.conf_ch[ channel ].conf1.idle_out_lv = 0;
|
||||
static void registerInterrupt(void* selfVoid) {
|
||||
auto* self = (SmartLed*)selfVoid;
|
||||
ESP_ERROR_CHECK(self->_driver.registerIsr(!anyAlive()));
|
||||
}
|
||||
|
||||
static void registerInterrupt(void *) {
|
||||
ESP_ERROR_CHECK(esp_intr_alloc( ETS_RMT_INTR_SOURCE, 0, interruptHandler, nullptr, &_interruptHandle));
|
||||
}
|
||||
|
||||
static void unregisterInterrupt(void*) {
|
||||
esp_intr_free( _interruptHandle );
|
||||
static void unregisterInterrupt(void* selfVoid) {
|
||||
auto* self = (SmartLed*)selfVoid;
|
||||
ESP_ERROR_CHECK(self->_driver.unregisterIsr());
|
||||
}
|
||||
|
||||
static SmartLed*& IRAM_ATTR ledForChannel(int channel);
|
||||
static void IRAM_ATTR interruptHandler( void* );
|
||||
|
||||
void IRAM_ATTR copyRmtHalfBlock();
|
||||
static bool anyAlive() {
|
||||
for (int i = 0; i != detail::CHANNEL_COUNT; i++)
|
||||
if (ledForChannel(i) != nullptr)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void swapBuffers() {
|
||||
if (_secondBuffer)
|
||||
_firstBuffer.swap(_secondBuffer);
|
||||
}
|
||||
|
||||
void startTransmission() {
|
||||
// Invalid use of the library
|
||||
esp_err_t startTransmission() {
|
||||
// Invalid use of the library, you must wait() fir previous frame to get processed first
|
||||
if (xSemaphoreTake(_finishedFlag, 0) != pdTRUE)
|
||||
abort();
|
||||
|
||||
_pixelPosition = _componentPosition = _halfIdx = 0;
|
||||
copyRmtHalfBlock();
|
||||
if ( _pixelPosition < _count )
|
||||
copyRmtHalfBlock();
|
||||
|
||||
RMT.conf_ch[ _channel ].conf1.mem_rd_rst = 1;
|
||||
RMT.conf_ch[ _channel ].conf1.tx_start = 1;
|
||||
auto err = _driver.transmit(_firstBuffer.get());
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool anyAlive() {
|
||||
for ( int i = 0; i != 8; i++ )
|
||||
if ( ledForChannel( i ) != nullptr ) return true;
|
||||
return false;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const LedType& _timing;
|
||||
SemaphoreHandle_t _finishedFlag;
|
||||
detail::RmtDriver _driver;
|
||||
int _channel;
|
||||
detail::RmtPulsePair _bitToRmt[ 2 ];
|
||||
int _count;
|
||||
std::unique_ptr<Rgb[]> _firstBuffer;
|
||||
std::unique_ptr<Rgb[]> _secondBuffer;
|
||||
Rgb *_buffer;
|
||||
|
||||
xSemaphoreHandle _finishedFlag;
|
||||
|
||||
int _pixelPosition;
|
||||
int _componentPosition;
|
||||
int _halfIdx;
|
||||
};
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#define _SMARTLEDS_SPI_HOST SPI2_HOST
|
||||
#define _SMARTLEDS_SPI_DMA_CHAN SPI_DMA_CH_AUTO
|
||||
#else
|
||||
#define _SMARTLEDS_SPI_HOST HSPI_HOST
|
||||
#define _SMARTLEDS_SPI_DMA_CHAN 1
|
||||
#endif
|
||||
|
||||
class Apa102 {
|
||||
public:
|
||||
struct ApaRgb {
|
||||
ApaRgb(uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF)
|
||||
: v( 0xE0 | v ), b( b ), g( g ), r( r )
|
||||
{}
|
||||
: v(0xE0 | v)
|
||||
, b(b)
|
||||
, g(g)
|
||||
, r(r) {}
|
||||
|
||||
ApaRgb& operator=(const Rgb& o) {
|
||||
r = o.r;
|
||||
@@ -339,12 +253,12 @@ public:
|
||||
static const int FINAL_FRAME_SIZE = 4;
|
||||
static const int TRANS_COUNT = 2 + 8;
|
||||
|
||||
Apa102( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer )
|
||||
: _count( count ),
|
||||
_firstBuffer( new ApaRgb[ count ] ),
|
||||
_secondBuffer( doubleBuffer ? new ApaRgb[ count ] : nullptr ),
|
||||
_initFrame( 0 )
|
||||
{
|
||||
Apa102(int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, int clock_speed_hz = 1000000)
|
||||
: _count(count)
|
||||
, _firstBuffer(new ApaRgb[count])
|
||||
, _secondBuffer(doubleBuffer ? new ApaRgb[count] : nullptr)
|
||||
, _transCount(0)
|
||||
, _initFrame(0) {
|
||||
spi_bus_config_t buscfg;
|
||||
memset(&buscfg, 0, sizeof(buscfg));
|
||||
buscfg.mosi_io_num = datapin;
|
||||
@@ -356,16 +270,16 @@ public:
|
||||
|
||||
spi_device_interface_config_t devcfg;
|
||||
memset(&devcfg, 0, sizeof(devcfg));
|
||||
devcfg.clock_speed_hz = 1000000;
|
||||
devcfg.clock_speed_hz = clock_speed_hz;
|
||||
devcfg.mode = 0;
|
||||
devcfg.spics_io_num = -1;
|
||||
devcfg.queue_size = TRANS_COUNT;
|
||||
devcfg.pre_cb = nullptr;
|
||||
|
||||
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
|
||||
auto ret = spi_bus_initialize(_SMARTLEDS_SPI_HOST, &buscfg, _SMARTLEDS_SPI_DMA_CHAN);
|
||||
assert(ret == ESP_OK);
|
||||
|
||||
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
|
||||
ret = spi_bus_add_device(_SMARTLEDS_SPI_HOST, &devcfg, &_spi);
|
||||
assert(ret == ESP_OK);
|
||||
|
||||
std::fill_n(_finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF);
|
||||
@@ -375,13 +289,9 @@ public:
|
||||
// ToDo
|
||||
}
|
||||
|
||||
ApaRgb& operator[]( int idx ) {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
ApaRgb& operator[](int idx) { return _firstBuffer[idx]; }
|
||||
|
||||
const ApaRgb& operator[]( int idx ) const {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
const ApaRgb& operator[](int idx) const { return _firstBuffer[idx]; }
|
||||
|
||||
void show() {
|
||||
_buffer = _firstBuffer.get();
|
||||
@@ -395,6 +305,7 @@ public:
|
||||
spi_device_get_trans_result(_spi, &t, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void swapBuffers() {
|
||||
if (_secondBuffer)
|
||||
@@ -444,9 +355,9 @@ public:
|
||||
struct LDP8806_GRB {
|
||||
|
||||
LDP8806_GRB(uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0)
|
||||
: g( g_7bit ), r( r_7bit ), b( b_7bit )
|
||||
{
|
||||
}
|
||||
: g(g_7bit)
|
||||
, r(r_7bit)
|
||||
, b(b_7bit) {}
|
||||
|
||||
LDP8806_GRB& operator=(const Rgb& o) {
|
||||
//Convert 8->7bit colour
|
||||
@@ -468,13 +379,14 @@ public:
|
||||
static const int LATCH_FRAME_SIZE_BYTES = 3;
|
||||
static const int TRANS_COUNT_MAX = 20; //Arbitrary, supports up to 600 LED
|
||||
|
||||
LDP8806( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000 )
|
||||
: _count( count ),
|
||||
_firstBuffer( new LDP8806_GRB[ count ] ),
|
||||
_secondBuffer( doubleBuffer ? new LDP8806_GRB[ count ] : nullptr ),
|
||||
LDP8806(
|
||||
int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000)
|
||||
: _count(count)
|
||||
, _firstBuffer(new LDP8806_GRB[count])
|
||||
, _secondBuffer(doubleBuffer ? new LDP8806_GRB[count] : nullptr)
|
||||
,
|
||||
// one 'latch'/start-of-data mark frame for every 32 leds
|
||||
_latchFrames( ( count + 31 ) / 32 )
|
||||
{
|
||||
_latchFrames((count + 31) / 32) {
|
||||
spi_bus_config_t buscfg;
|
||||
memset(&buscfg, 0, sizeof(buscfg));
|
||||
buscfg.mosi_io_num = datapin;
|
||||
@@ -492,10 +404,10 @@ public:
|
||||
devcfg.queue_size = TRANS_COUNT_MAX;
|
||||
devcfg.pre_cb = nullptr;
|
||||
|
||||
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
|
||||
auto ret = spi_bus_initialize(_SMARTLEDS_SPI_HOST, &buscfg, _SMARTLEDS_SPI_DMA_CHAN);
|
||||
assert(ret == ESP_OK);
|
||||
|
||||
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
|
||||
ret = spi_bus_add_device(_SMARTLEDS_SPI_HOST, &devcfg, &_spi);
|
||||
assert(ret == ESP_OK);
|
||||
|
||||
std::fill_n(_latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0);
|
||||
@@ -505,13 +417,9 @@ public:
|
||||
// noop
|
||||
}
|
||||
|
||||
LDP8806_GRB& operator[]( int idx ) {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
LDP8806_GRB& operator[](int idx) { return _firstBuffer[idx]; }
|
||||
|
||||
const LDP8806_GRB& operator[]( int idx ) const {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
const LDP8806_GRB& operator[](int idx) const { return _firstBuffer[idx]; }
|
||||
|
||||
void show() {
|
||||
_buffer = _firstBuffer.get();
|
||||
@@ -525,6 +433,7 @@ public:
|
||||
spi_device_get_trans_result(_spi, &t, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void swapBuffers() {
|
||||
if (_secondBuffer)
|
||||
@@ -566,5 +475,3 @@ private:
|
||||
int _latchFrames;
|
||||
uint8_t _latchBuffer[LATCH_FRAME_SIZE_BYTES];
|
||||
};
|
||||
|
||||
#endif //SMARTLEDS_H
|
||||
|
||||
@@ -127,7 +127,25 @@ CONFIG_MQTT_USE_CUSTOM_CONFIG=y
|
||||
#CONFIG_MQTT_OUTBOX_EXPIRED_TIMEOUT_MS=5000
|
||||
#CONFIG_MQTT_CUSTOM_OUTBOX=y # -> Use custom outbox in components/jomjol_mqtt/mqtt_outbox.h/cpp. If USE_PSRAM is enabled in there, it will save 10 kBytes of internal RAM. How ever it also leads to memory fragmentation, see https://github.com/jomjol/AI-on-the-edge-device/issues/2200
|
||||
|
||||
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=n
|
||||
#
|
||||
# ESP-Driver:LEDC Configurations
|
||||
#
|
||||
CONFIG_LEDC_CTRL_FUNC_IN_IRAM=y
|
||||
# end of ESP-Driver:LEDC Configurations
|
||||
|
||||
#
|
||||
# Legacy RMT Driver Configurations
|
||||
#
|
||||
CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
|
||||
# end of Legacy RMT Driver Configurations
|
||||
|
||||
#
|
||||
# ESP-Driver:RMT Configurations
|
||||
#
|
||||
CONFIG_RMT_ISR_IRAM_SAFE=y
|
||||
CONFIG_RMT_RECV_FUNC_IN_IRAM=y
|
||||
# CONFIG_RMT_ENABLE_DEBUG_LOG is not set
|
||||
# end of ESP-Driver:RMT Configurations
|
||||
|
||||
CONFIG_CAMERA_CORE0=n
|
||||
CONFIG_CAMERA_CORE1=y
|
||||
@@ -156,6 +174,7 @@ CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=4864
|
||||
#CONFIG_FREERTOS_USE_TRACE_FACILITY=1
|
||||
#CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
|
||||
#CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
|
||||
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=n
|
||||
|
||||
#force disable HIMEM as not used in default config, can be enabled with [env:esp32cam-dev-himem]
|
||||
#free 256kb of internal memory :
|
||||
|
||||
Reference in New Issue
Block a user