PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

419 lines
20KB

  1. #ifndef __INC_CONTROLLER_H
  2. #define __INC_CONTROLLER_H
  3. ///@file controller.h
  4. /// base definitions used by led controllers for writing out led data
  5. #include "FastLED.h"
  6. #include "led_sysdefs.h"
  7. #include "pixeltypes.h"
  8. #include "color.h"
  9. #include <stddef.h>
  10. FASTLED_NAMESPACE_BEGIN
  11. #define RO(X) RGB_BYTE(RGB_ORDER, X)
  12. #define RGB_BYTE(RO,X) (((RO)>>(3*(2-(X)))) & 0x3)
  13. #define RGB_BYTE0(RO) ((RO>>6) & 0x3)
  14. #define RGB_BYTE1(RO) ((RO>>3) & 0x3)
  15. #define RGB_BYTE2(RO) ((RO) & 0x3)
  16. // operator byte *(struct CRGB[] arr) { return (byte*)arr; }
  17. #define DISABLE_DITHER 0x00
  18. #define BINARY_DITHER 0x01
  19. typedef uint8_t EDitherMode;
  20. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  21. //
  22. // LED Controller interface definition
  23. //
  24. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  25. /// Base definition for an LED controller. Pretty much the methods that every LED controller object will make available.
  26. /// Note that the showARGB method is not impelemented for all controllers yet. Note also the methods for eventual checking
  27. /// of background writing of data (I'm looking at you, teensy 3.0 DMA controller!). If you want to pass LED controllers around
  28. /// to methods, make them references to this type, keeps your code saner. However, most people won't be seeing/using these objects
  29. /// directly at all
  30. class CLEDController {
  31. protected:
  32. friend class CFastLED;
  33. CRGB *m_Data;
  34. CLEDController *m_pNext;
  35. CRGB m_ColorCorrection;
  36. CRGB m_ColorTemperature;
  37. EDitherMode m_DitherMode;
  38. int m_nLeds;
  39. static CLEDController *m_pHead;
  40. static CLEDController *m_pTail;
  41. /// set all the leds on the controller to a given color
  42. ///@param data the crgb color to set the leds to
  43. ///@param nLeds the numner of leds to set to this color
  44. ///@param scale the rgb scaling value for outputting color
  45. virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) = 0;
  46. /// write the passed in rgb data out to the leds managed by this controller
  47. ///@param data the rgb data to write out to the strip
  48. ///@param nLeds the number of leds being written out
  49. ///@param scale the rgb scaling to apply to each led before writing it out
  50. virtual void show(const struct CRGB *data, int nLeds, CRGB scale) = 0;
  51. public:
  52. /// create an led controller object, add it to the chain of controllers
  53. CLEDController() : m_Data(NULL), m_ColorCorrection(UncorrectedColor), m_ColorTemperature(UncorrectedTemperature), m_DitherMode(BINARY_DITHER), m_nLeds(0) {
  54. m_pNext = NULL;
  55. if(m_pHead==NULL) { m_pHead = this; }
  56. if(m_pTail != NULL) { m_pTail->m_pNext = this; }
  57. m_pTail = this;
  58. }
  59. ///initialize the LED controller
  60. virtual void init() = 0;
  61. ///clear out/zero out the given number of leds.
  62. virtual void clearLeds(int nLeds) { showColor(CRGB::Black, nLeds, CRGB::Black); }
  63. /// show function w/integer brightness, will scale for color correction and temperature
  64. void show(const struct CRGB *data, int nLeds, uint8_t brightness) {
  65. show(data, nLeds, getAdjustment(brightness));
  66. }
  67. /// show function w/integer brightness, will scale for color correction and temperature
  68. void showColor(const struct CRGB &data, int nLeds, uint8_t brightness) {
  69. showColor(data, nLeds, getAdjustment(brightness));
  70. }
  71. /// show function using the "attached to this controller" led data
  72. void showLeds(uint8_t brightness=255) {
  73. show(m_Data, m_nLeds, getAdjustment(brightness));
  74. }
  75. /// show the given color on the led strip
  76. void showColor(const struct CRGB & data, uint8_t brightness=255) {
  77. showColor(data, m_nLeds, getAdjustment(brightness));
  78. }
  79. /// get the first led controller in the chain of controllers
  80. static CLEDController *head() { return m_pHead; }
  81. /// get the next controller in the chain after this one. will return NULL at the end of the chain
  82. CLEDController *next() { return m_pNext; }
  83. /// set the default array of leds to be used by this controller
  84. CLEDController & setLeds(CRGB *data, int nLeds) {
  85. m_Data = data;
  86. m_nLeds = nLeds;
  87. return *this;
  88. }
  89. /// zero out the led data managed by this controller
  90. void clearLedData() {
  91. if(m_Data) {
  92. memset8((void*)m_Data, 0, sizeof(struct CRGB) * m_nLeds);
  93. }
  94. }
  95. /// How many leds does this controller manage?
  96. virtual int size() { return m_nLeds; }
  97. /// Pointer to the CRGB array for this controller
  98. CRGB* leds() { return m_Data; }
  99. /// Reference to the n'th item in the controller
  100. CRGB &operator[](int x) { return m_Data[x]; }
  101. /// set the dithering mode for this controller to use
  102. inline CLEDController & setDither(uint8_t ditherMode = BINARY_DITHER) { m_DitherMode = ditherMode; return *this; }
  103. /// get the dithering option currently set for this controller
  104. inline uint8_t getDither() { return m_DitherMode; }
  105. /// the the color corrction to use for this controller, expressed as an rgb object
  106. CLEDController & setCorrection(CRGB correction) { m_ColorCorrection = correction; return *this; }
  107. /// set the color correction to use for this controller
  108. CLEDController & setCorrection(LEDColorCorrection correction) { m_ColorCorrection = correction; return *this; }
  109. /// get the correction value used by this controller
  110. CRGB getCorrection() { return m_ColorCorrection; }
  111. /// set the color temperature, aka white point, for this controller
  112. CLEDController & setTemperature(CRGB temperature) { m_ColorTemperature = temperature; return *this; }
  113. /// set the color temperature, aka white point, for this controller
  114. CLEDController & setTemperature(ColorTemperature temperature) { m_ColorTemperature = temperature; return *this; }
  115. /// get the color temperature, aka whipe point, for this controller
  116. CRGB getTemperature() { return m_ColorTemperature; }
  117. /// Get the combined brightness/color adjustment for this controller
  118. CRGB getAdjustment(uint8_t scale) {
  119. return computeAdjustment(scale, m_ColorCorrection, m_ColorTemperature);
  120. }
  121. static CRGB computeAdjustment(uint8_t scale, const CRGB & colorCorrection, const CRGB & colorTemperature) {
  122. #if defined(NO_CORRECTION) && (NO_CORRECTION==1)
  123. return CRGB(scale,scale,scale);
  124. #else
  125. CRGB adj(0,0,0);
  126. if(scale > 0) {
  127. for(uint8_t i = 0; i < 3; i++) {
  128. uint8_t cc = colorCorrection.raw[i];
  129. uint8_t ct = colorTemperature.raw[i];
  130. if(cc > 0 && ct > 0) {
  131. uint32_t work = (((uint32_t)cc)+1) * (((uint32_t)ct)+1) * scale;
  132. work /= 0x10000L;
  133. adj.raw[i] = work & 0xFF;
  134. }
  135. }
  136. }
  137. return adj;
  138. #endif
  139. }
  140. virtual uint16_t getMaxRefreshRate() const { return 0; }
  141. };
  142. // Pixel controller class. This is the class that we use to centralize pixel access in a block of data, including
  143. // support for things like RGB reordering, scaling, dithering, skipping (for ARGB data), and eventually, we will
  144. // centralize 8/12/16 conversions here as well.
  145. template<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF>
  146. struct PixelController {
  147. const uint8_t *mData;
  148. int mLen,mLenRemaining;
  149. uint8_t d[3];
  150. uint8_t e[3];
  151. CRGB mScale;
  152. int8_t mAdvance;
  153. int mOffsets[LANES];
  154. PixelController(const PixelController & other) {
  155. d[0] = other.d[0];
  156. d[1] = other.d[1];
  157. d[2] = other.d[2];
  158. e[0] = other.e[0];
  159. e[1] = other.e[1];
  160. e[2] = other.e[2];
  161. mData = other.mData;
  162. mScale = other.mScale;
  163. mAdvance = other.mAdvance;
  164. mLenRemaining = mLen = other.mLen;
  165. for(int i = 0; i < LANES; i++) { mOffsets[i] = other.mOffsets[i]; }
  166. }
  167. void initOffsets(int len) {
  168. int nOffset = 0;
  169. for(int i = 0; i < LANES; i++) {
  170. mOffsets[i] = nOffset;
  171. if((1<<i) & MASK) { nOffset += (len * mAdvance); }
  172. }
  173. }
  174. PixelController(const uint8_t *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER, bool advance=true, uint8_t skip=0) : mData(d), mLen(len), mLenRemaining(len), mScale(s) {
  175. enable_dithering(dither);
  176. mData += skip;
  177. mAdvance = (advance) ? 3+skip : 0;
  178. initOffsets(len);
  179. }
  180. PixelController(const CRGB *d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)d), mLen(len), mLenRemaining(len), mScale(s) {
  181. enable_dithering(dither);
  182. mAdvance = 3;
  183. initOffsets(len);
  184. }
  185. PixelController(const CRGB &d, int len, CRGB & s, EDitherMode dither = BINARY_DITHER) : mData((const uint8_t*)&d), mLen(len), mLenRemaining(len), mScale(s) {
  186. enable_dithering(dither);
  187. mAdvance = 0;
  188. initOffsets(len);
  189. }
  190. void init_binary_dithering() {
  191. #if !defined(NO_DITHERING) || (NO_DITHERING != 1)
  192. // Set 'virtual bits' of dithering to the highest level
  193. // that is not likely to cause excessive flickering at
  194. // low brightness levels + low update rates.
  195. // These pre-set values are a little ambitious, since
  196. // a 400Hz update rate for WS2811-family LEDs is only
  197. // possible with 85 pixels or fewer.
  198. // Once we have a 'number of milliseconds since last update'
  199. // value available here, we can quickly calculate the correct
  200. // number of 'virtual bits' on the fly with a couple of 'if'
  201. // statements -- no division required. At this point,
  202. // the division is done at compile time, so there's no runtime
  203. // cost, but the values are still hard-coded.
  204. #define MAX_LIKELY_UPDATE_RATE_HZ 400
  205. #define MIN_ACCEPTABLE_DITHER_RATE_HZ 50
  206. #define UPDATES_PER_FULL_DITHER_CYCLE (MAX_LIKELY_UPDATE_RATE_HZ / MIN_ACCEPTABLE_DITHER_RATE_HZ)
  207. #define RECOMMENDED_VIRTUAL_BITS ((UPDATES_PER_FULL_DITHER_CYCLE>1) + \
  208. (UPDATES_PER_FULL_DITHER_CYCLE>2) + \
  209. (UPDATES_PER_FULL_DITHER_CYCLE>4) + \
  210. (UPDATES_PER_FULL_DITHER_CYCLE>8) + \
  211. (UPDATES_PER_FULL_DITHER_CYCLE>16) + \
  212. (UPDATES_PER_FULL_DITHER_CYCLE>32) + \
  213. (UPDATES_PER_FULL_DITHER_CYCLE>64) + \
  214. (UPDATES_PER_FULL_DITHER_CYCLE>128) )
  215. #define VIRTUAL_BITS RECOMMENDED_VIRTUAL_BITS
  216. // R is the digther signal 'counter'.
  217. static uint8_t R = 0;
  218. R++;
  219. // R is wrapped around at 2^ditherBits,
  220. // so if ditherBits is 2, R will cycle through (0,1,2,3)
  221. uint8_t ditherBits = VIRTUAL_BITS;
  222. R &= (0x01 << ditherBits) - 1;
  223. // Q is the "unscaled dither signal" itself.
  224. // It's initialized to the reversed bits of R.
  225. // If 'ditherBits' is 2, Q here will cycle through (0,128,64,192)
  226. uint8_t Q = 0;
  227. // Reverse bits in a byte
  228. {
  229. if(R & 0x01) { Q |= 0x80; }
  230. if(R & 0x02) { Q |= 0x40; }
  231. if(R & 0x04) { Q |= 0x20; }
  232. if(R & 0x08) { Q |= 0x10; }
  233. if(R & 0x10) { Q |= 0x08; }
  234. if(R & 0x20) { Q |= 0x04; }
  235. if(R & 0x40) { Q |= 0x02; }
  236. if(R & 0x80) { Q |= 0x01; }
  237. }
  238. // Now we adjust Q to fall in the center of each range,
  239. // instead of at the start of the range.
  240. // If ditherBits is 2, Q will be (0, 128, 64, 192) at first,
  241. // and this adjustment makes it (31, 159, 95, 223).
  242. if( ditherBits < 8) {
  243. Q += 0x01 << (7 - ditherBits);
  244. }
  245. // D and E form the "scaled dither signal"
  246. // which is added to pixel values to affect the
  247. // actual dithering.
  248. // Setup the initial D and E values
  249. for(int i = 0; i < 3; i++) {
  250. uint8_t s = mScale.raw[i];
  251. e[i] = s ? (256/s) + 1 : 0;
  252. d[i] = scale8(Q, e[i]);
  253. #if (FASTLED_SCALE8_FIXED == 1)
  254. if(d[i]) (d[i]--);
  255. #endif
  256. if(e[i]) e[i]--;
  257. }
  258. #endif
  259. }
  260. // Do we have n pixels left to process?
  261. __attribute__((always_inline)) inline bool has(int n) {
  262. return mLenRemaining >= n;
  263. }
  264. // toggle dithering enable
  265. void enable_dithering(EDitherMode dither) {
  266. switch(dither) {
  267. case BINARY_DITHER: init_binary_dithering(); break;
  268. default: d[0]=d[1]=d[2]=e[0]=e[1]=e[2]=0; break;
  269. }
  270. }
  271. __attribute__((always_inline)) inline int size() { return mLen; }
  272. // get the amount to advance the pointer by
  273. __attribute__((always_inline)) inline int advanceBy() { return mAdvance; }
  274. // advance the data pointer forward, adjust position counter
  275. __attribute__((always_inline)) inline void advanceData() { mData += mAdvance; mLenRemaining--;}
  276. // step the dithering forward
  277. __attribute__((always_inline)) inline void stepDithering() {
  278. // IF UPDATING HERE, BE SURE TO UPDATE THE ASM VERSION IN
  279. // clockless_trinket.h!
  280. d[0] = e[0] - d[0];
  281. d[1] = e[1] - d[1];
  282. d[2] = e[2] - d[2];
  283. }
  284. // Some chipsets pre-cycle the first byte, which means we want to cycle byte 0's dithering separately
  285. __attribute__((always_inline)) inline void preStepFirstByteDithering() {
  286. d[RO(0)] = e[RO(0)] - d[RO(0)];
  287. }
  288. template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc) { return pc.mData[RO(SLOT)]; }
  289. template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadByte(PixelController & pc, int lane) { return pc.mData[pc.mOffsets[lane] + RO(SLOT)]; }
  290. template<int SLOT> __attribute__((always_inline)) inline static uint8_t dither(PixelController & pc, uint8_t b) { return b ? qadd8(b, pc.d[RO(SLOT)]) : 0; }
  291. template<int SLOT> __attribute__((always_inline)) inline static uint8_t dither(PixelController & , uint8_t b, uint8_t d) { return b ? qadd8(b,d) : 0; }
  292. template<int SLOT> __attribute__((always_inline)) inline static uint8_t scale(PixelController & pc, uint8_t b) { return scale8(b, pc.mScale.raw[RO(SLOT)]); }
  293. template<int SLOT> __attribute__((always_inline)) inline static uint8_t scale(PixelController & , uint8_t b, uint8_t scale) { return scale8(b, scale); }
  294. // composite shortcut functions for loading, dithering, and scaling
  295. template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc))); }
  296. template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane))); }
  297. template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t d, uint8_t scale) { return scale8(pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane), d), scale); }
  298. template<int SLOT> __attribute__((always_inline)) inline static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t scale) { return scale8(pc.loadByte<SLOT>(pc, lane), scale); }
  299. template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc); }
  300. template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane); }
  301. template<int SLOT> __attribute__((always_inline)) inline static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane, uint8_t scale) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane, scale); }
  302. template<int SLOT> __attribute__((always_inline)) inline static uint8_t getd(PixelController & pc) { return pc.d[RO(SLOT)]; }
  303. template<int SLOT> __attribute__((always_inline)) inline static uint8_t getscale(PixelController & pc) { return pc.mScale.raw[RO(SLOT)]; }
  304. // Helper functions to get around gcc stupidities
  305. __attribute__((always_inline)) inline uint8_t loadAndScale0(int lane, uint8_t scale) { return loadAndScale<0>(*this, lane, scale); }
  306. __attribute__((always_inline)) inline uint8_t loadAndScale1(int lane, uint8_t scale) { return loadAndScale<1>(*this, lane, scale); }
  307. __attribute__((always_inline)) inline uint8_t loadAndScale2(int lane, uint8_t scale) { return loadAndScale<2>(*this, lane, scale); }
  308. __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane, uint8_t scale) { return advanceAndLoadAndScale<0>(*this, lane, scale); }
  309. __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane, uint8_t scale) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane, scale); }
  310. __attribute__((always_inline)) inline uint8_t loadAndScale0(int lane) { return loadAndScale<0>(*this, lane); }
  311. __attribute__((always_inline)) inline uint8_t loadAndScale1(int lane) { return loadAndScale<1>(*this, lane); }
  312. __attribute__((always_inline)) inline uint8_t loadAndScale2(int lane) { return loadAndScale<2>(*this, lane); }
  313. __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0(int lane) { return advanceAndLoadAndScale<0>(*this, lane); }
  314. __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0(int lane) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane); }
  315. __attribute__((always_inline)) inline uint8_t loadAndScale0() { return loadAndScale<0>(*this); }
  316. __attribute__((always_inline)) inline uint8_t loadAndScale1() { return loadAndScale<1>(*this); }
  317. __attribute__((always_inline)) inline uint8_t loadAndScale2() { return loadAndScale<2>(*this); }
  318. __attribute__((always_inline)) inline uint8_t advanceAndLoadAndScale0() { return advanceAndLoadAndScale<0>(*this); }
  319. __attribute__((always_inline)) inline uint8_t stepAdvanceAndLoadAndScale0() { stepDithering(); return advanceAndLoadAndScale<0>(*this); }
  320. __attribute__((always_inline)) inline uint8_t getScale0() { return getscale<0>(*this); }
  321. __attribute__((always_inline)) inline uint8_t getScale1() { return getscale<1>(*this); }
  322. __attribute__((always_inline)) inline uint8_t getScale2() { return getscale<2>(*this); }
  323. };
  324. template<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF> class CPixelLEDController : public CLEDController {
  325. protected:
  326. virtual void showPixels(PixelController<RGB_ORDER,LANES,MASK> & pixels) = 0;
  327. /// set all the leds on the controller to a given color
  328. ///@param data the crgb color to set the leds to
  329. ///@param nLeds the numner of leds to set to this color
  330. ///@param scale the rgb scaling value for outputting color
  331. virtual void showColor(const struct CRGB & data, int nLeds, CRGB scale) {
  332. PixelController<RGB_ORDER, LANES, MASK> pixels(data, nLeds, scale, getDither());
  333. showPixels(pixels);
  334. }
  335. /// write the passed in rgb data out to the leds managed by this controller
  336. ///@param data the rgb data to write out to the strip
  337. ///@param nLeds the number of leds being written out
  338. ///@param scale the rgb scaling to apply to each led before writing it out
  339. virtual void show(const struct CRGB *data, int nLeds, CRGB scale) {
  340. PixelController<RGB_ORDER, LANES, MASK> pixels(data, nLeds, scale, getDither());
  341. showPixels(pixels);
  342. }
  343. public:
  344. CPixelLEDController() : CLEDController() {}
  345. };
  346. FASTLED_NAMESPACE_END
  347. #endif