Merge pull request #541 from keyboardio/driver/led/ws2812
A reasonably generic WS2812 driverpull/553/head
commit
1cf25da48c
@ -0,0 +1,69 @@
|
|||||||
|
# kaleidoscope::driver::led::WS2812
|
||||||
|
|
||||||
|
This driver provides a generic base class for driving WS2812-based LED strips.
|
||||||
|
This is not a plugin, and is not meant to be user-facing. It is meant to be used
|
||||||
|
by developers, and hardware plugins in particular. See the [KBDFans
|
||||||
|
KBD4x][kbd4x] plugin for a practical, existing example about how to use the
|
||||||
|
driver.
|
||||||
|
|
||||||
|
[kbd4x]: ../../../src/kaleidoscope/hardware/kbdfans/KBD4x.h
|
||||||
|
|
||||||
|
## Using the driver
|
||||||
|
|
||||||
|
To use the driver, we need to include the header:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include <kaleidoscope/driver/led/WS2812.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
For performance reasons, the driver is templated, and requires three template
|
||||||
|
arguments:
|
||||||
|
|
||||||
|
1. `pin`, the PIN the driver will use to communicate with the LED strip.
|
||||||
|
2. `class Color`, the color class that determines the order of the RGB
|
||||||
|
components, and should match the component order the LED strip uses. We
|
||||||
|
provide three orders out of the box, all in the
|
||||||
|
`kaleidoscope::driver::led::color` namespace: `RGB`, `GRB`, and `BGR`.
|
||||||
|
3. `ledCount` is the number of LEDs on the strip this instance of the driver
|
||||||
|
should be able to address.
|
||||||
|
|
||||||
|
Armed with this knowledge, instantiating an object is as easy as:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
using Color = kaleidoscope::driver::led::color::GRB;
|
||||||
|
|
||||||
|
kaleidoscope::driver::led::WS2812<PIN_E2, Color, 6> LEDs;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Driver methods
|
||||||
|
|
||||||
|
The instantiated `WS2812` object will have the following methods:
|
||||||
|
|
||||||
|
### `.led_count()`
|
||||||
|
|
||||||
|
> Returns the number of LEDs, the same value as the `ledCount` template
|
||||||
|
> argument.
|
||||||
|
|
||||||
|
### `.sync()`
|
||||||
|
|
||||||
|
> Synchronises the internal LED state with the hardware, by sending over all of
|
||||||
|
> the LED data.
|
||||||
|
>
|
||||||
|
> It is recommended to call this at most once per cycle. Calling it less
|
||||||
|
> frequently isn't wrong either.
|
||||||
|
|
||||||
|
### `.setColorAt(index, color)`
|
||||||
|
### `.setColorAt(index, r, g, b)`
|
||||||
|
|
||||||
|
> Sets the color at the given `index` to the specified value. He value can
|
||||||
|
> either be a `Color` object (the same type as the template argument), or a list
|
||||||
|
> of RGB component values.
|
||||||
|
|
||||||
|
### `.getColorAt(index)`
|
||||||
|
|
||||||
|
> Returns the color at the given `index`, as a `Color` object.
|
||||||
|
|
||||||
|
## Further information
|
||||||
|
|
||||||
|
To have a better idea how to use the driver in practice, looking at the
|
||||||
|
[KBD4x][kbd4x] hardware library is recommended.
|
@ -0,0 +1,55 @@
|
|||||||
|
/* -*- mode: c++ -*-
|
||||||
|
* kaleidoscope::driver::led::color -- LED Color classes for Kaleidoscope
|
||||||
|
* Copyright (C) 2019 Keyboard.io, Inc
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by the Free Software
|
||||||
|
* Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
* details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
namespace driver {
|
||||||
|
namespace led {
|
||||||
|
namespace color {
|
||||||
|
|
||||||
|
struct RGB {
|
||||||
|
RGB(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||||
|
RGB() {}
|
||||||
|
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GRB {
|
||||||
|
GRB(uint8_t r_, uint8_t g_, uint8_t b_) : g(g_), r(r_), b(b_) {}
|
||||||
|
GRB() {}
|
||||||
|
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t b;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BGR {
|
||||||
|
BGR(uint8_t r_, uint8_t g_, uint8_t b_) : b(b_), g(g_), r(r_) {}
|
||||||
|
BGR() {}
|
||||||
|
|
||||||
|
uint8_t b;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t r;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* Based on Light_WS2812, from:
|
||||||
|
* https://github.com/cpldcpu/light_ws2812
|
||||||
|
*
|
||||||
|
* Original copyright:
|
||||||
|
*
|
||||||
|
* light weight WS2812 lib V2.1 - Arduino support
|
||||||
|
*
|
||||||
|
* Controls WS2811/WS2812/WS2812B RGB-LEDs
|
||||||
|
* Author: Matthias Riegler
|
||||||
|
*
|
||||||
|
* Mar 07 2014: Added Arduino and C++ Library
|
||||||
|
*
|
||||||
|
* September 6, 2014: Added option to switch between most popular color orders
|
||||||
|
* (RGB, GRB, and BRG) -- Windell H. Oskay
|
||||||
|
*
|
||||||
|
* License:
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTabILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "kaleidoscope/hardware/avr/pins_and_ports.h"
|
||||||
|
#include "kaleidoscope/driver/led/Color.h"
|
||||||
|
#include "ws2812/config.h"
|
||||||
|
|
||||||
|
namespace kaleidoscope {
|
||||||
|
namespace driver {
|
||||||
|
namespace led {
|
||||||
|
|
||||||
|
template <uint8_t pin, class Color, int8_t ledCount>
|
||||||
|
class WS2812 {
|
||||||
|
public:
|
||||||
|
WS2812() : pinmask_(_BV(pin & 0xF)) {}
|
||||||
|
|
||||||
|
int8_t led_count() {
|
||||||
|
return ledCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sync() {
|
||||||
|
if (!modified_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DDR_OUTPUT(pin);
|
||||||
|
|
||||||
|
sendArrayWithMask(pinmask_);
|
||||||
|
_delay_us(50);
|
||||||
|
modified_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setColorAt(int8_t index, Color color) {
|
||||||
|
if (index >= ledCount)
|
||||||
|
return;
|
||||||
|
modified_ = true;
|
||||||
|
leds_[index] = color;
|
||||||
|
}
|
||||||
|
void setColorAt(int8_t index, uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
if (index >= ledCount)
|
||||||
|
return;
|
||||||
|
modified_ = true;
|
||||||
|
leds_[index] = Color(r, g, b);
|
||||||
|
}
|
||||||
|
Color getColorAt(int8_t index) {
|
||||||
|
if (index >= ledCount)
|
||||||
|
return Color(0, 0, 0);
|
||||||
|
return leds_[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Color leds_[ledCount];
|
||||||
|
uint8_t pinmask_;
|
||||||
|
bool modified_ = false;
|
||||||
|
|
||||||
|
void sendArrayWithMask(uint8_t maskhi) {
|
||||||
|
uint8_t *data = (uint8_t *)leds_;
|
||||||
|
uint16_t datalen = ledCount * sizeof(Color);
|
||||||
|
uint8_t curbyte, ctr, masklo;
|
||||||
|
uint8_t sreg_prev;
|
||||||
|
|
||||||
|
masklo = ~ maskhi & PORT_REG_FOR_PIN(pin);
|
||||||
|
maskhi |= PORT_REG_FOR_PIN(pin);
|
||||||
|
|
||||||
|
sreg_prev = SREG;
|
||||||
|
cli();
|
||||||
|
|
||||||
|
while (datalen--) {
|
||||||
|
curbyte = (*data++);
|
||||||
|
|
||||||
|
asm volatile(
|
||||||
|
" ldi %0,8 \n\t"
|
||||||
|
"loop%=: \n\t"
|
||||||
|
" out %2,%3 \n\t" // '1' [01] '0' [01] - re
|
||||||
|
#if (w1_nops&1)
|
||||||
|
w_nop1
|
||||||
|
#endif
|
||||||
|
#if (w1_nops&2)
|
||||||
|
w_nop2
|
||||||
|
#endif
|
||||||
|
#if (w1_nops&4)
|
||||||
|
w_nop4
|
||||||
|
#endif
|
||||||
|
#if (w1_nops&8)
|
||||||
|
w_nop8
|
||||||
|
#endif
|
||||||
|
#if (w1_nops&16)
|
||||||
|
w_nop16
|
||||||
|
#endif
|
||||||
|
" sbrs %1,7 \n\t" // '1' [03] '0' [02]
|
||||||
|
" out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low
|
||||||
|
" lsl %1 \n\t" // '1' [04] '0' [04]
|
||||||
|
#if (w2_nops&1)
|
||||||
|
w_nop1
|
||||||
|
#endif
|
||||||
|
#if (w2_nops&2)
|
||||||
|
w_nop2
|
||||||
|
#endif
|
||||||
|
#if (w2_nops&4)
|
||||||
|
w_nop4
|
||||||
|
#endif
|
||||||
|
#if (w2_nops&8)
|
||||||
|
w_nop8
|
||||||
|
#endif
|
||||||
|
#if (w2_nops&16)
|
||||||
|
w_nop16
|
||||||
|
#endif
|
||||||
|
" out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high
|
||||||
|
#if (w3_nops&1)
|
||||||
|
w_nop1
|
||||||
|
#endif
|
||||||
|
#if (w3_nops&2)
|
||||||
|
w_nop2
|
||||||
|
#endif
|
||||||
|
#if (w3_nops&4)
|
||||||
|
w_nop4
|
||||||
|
#endif
|
||||||
|
#if (w3_nops&8)
|
||||||
|
w_nop8
|
||||||
|
#endif
|
||||||
|
#if (w3_nops&16)
|
||||||
|
w_nop16
|
||||||
|
#endif
|
||||||
|
|
||||||
|
" dec %0 \n\t" // '1' [+2] '0' [+2]
|
||||||
|
" brne loop%=\n\t" // '1' [+3] '0' [+4]
|
||||||
|
: "=&d"(ctr)
|
||||||
|
: "r"(curbyte), "I"(_SFR_IO_ADDR(PORT_REG_FOR_PIN(pin))), "r"(maskhi), "r"(masklo)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SREG = sreg_prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Based on Light_WS2812, from:
|
||||||
|
* https://github.com/cpldcpu/light_ws2812
|
||||||
|
*
|
||||||
|
* Original copyright:
|
||||||
|
*
|
||||||
|
* light weight WS2812 lib V2.1 - Arduino support
|
||||||
|
*
|
||||||
|
* Controls WS2811/WS2812/WS2812B RGB-LEDs
|
||||||
|
* Author: Matthias Riegler
|
||||||
|
*
|
||||||
|
* Mar 07 2014: Added Arduino and C++ Library
|
||||||
|
*
|
||||||
|
* September 6, 2014: Added option to switch between most popular color orders
|
||||||
|
* (RGB, GRB, and BRG) -- Windell H. Oskay
|
||||||
|
*
|
||||||
|
* License:
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTabILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Timing in ns
|
||||||
|
#define w_zeropulse 350
|
||||||
|
#define w_onepulse 900
|
||||||
|
#define w_totalperiod 1250
|
||||||
|
|
||||||
|
// Fixed cycles used by the inner loop
|
||||||
|
#define w_fixedlow 2
|
||||||
|
#define w_fixedhigh 4
|
||||||
|
#define w_fixedtotal 8
|
||||||
|
|
||||||
|
// Insert NOPs to match the timing, if possible
|
||||||
|
#define w_zerocycles (((F_CPU/1000)*w_zeropulse )/1000000)
|
||||||
|
#define w_onecycles (((F_CPU/1000)*w_onepulse +500000)/1000000)
|
||||||
|
#define w_totalcycles (((F_CPU/1000)*w_totalperiod +500000)/1000000)
|
||||||
|
|
||||||
|
// w1 - nops between rising edge and falling edge - low
|
||||||
|
#define w1 (w_zerocycles-w_fixedlow)
|
||||||
|
// w2 nops between fe low and fe high
|
||||||
|
#define w2 (w_onecycles-w_fixedhigh-w1)
|
||||||
|
// w3 nops to complete loop
|
||||||
|
#define w3 (w_totalcycles-w_fixedtotal-w1-w2)
|
||||||
|
|
||||||
|
#if w1>0
|
||||||
|
#define w1_nops w1
|
||||||
|
#else
|
||||||
|
#define w1_nops 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The only critical timing parameter is the minimum pulse length of the "0"
|
||||||
|
// Warn or throw error if this timing can not be met with current F_CPU settings.
|
||||||
|
#define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000)
|
||||||
|
#if w_lowtime>550
|
||||||
|
#error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?"
|
||||||
|
#elif w_lowtime>450
|
||||||
|
#warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)."
|
||||||
|
#warning "Please consider a higher clockspeed, if possible"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if w2>0
|
||||||
|
#define w2_nops w2
|
||||||
|
#else
|
||||||
|
#define w2_nops 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if w3>0
|
||||||
|
#define w3_nops w3
|
||||||
|
#else
|
||||||
|
#define w3_nops 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define w_nop1 "nop \n\t"
|
||||||
|
#define w_nop2 "rjmp .+0 \n\t"
|
||||||
|
#define w_nop4 w_nop2 w_nop2
|
||||||
|
#define w_nop8 w_nop4 w_nop4
|
||||||
|
#define w_nop16 w_nop8 w_nop8
|
Loading…
Reference in new issue