SmartLeds.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /********************************************************************************
  2. * https://github.com/RoboticsBrno/SmartLeds
  3. *
  4. * MIT License
  5. *
  6. * Copyright (c) 2017 RoboticsBrno (RobotikaBrno)
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in all
  16. * copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  24. * SOFTWARE.
  25. *******************************************************************************/
  26. #pragma once
  27. /*
  28. * A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32.
  29. *
  30. * Jan "yaqwsx" Mrázek <email@honzamrazek.cz>
  31. *
  32. * Based on the work by Martin F. Falatic - https://github.com/FozzTexx/ws2812-demo
  33. */
  34. /*
  35. * Permission is hereby granted, free of charge, to any person obtaining a copy
  36. * of this software and associated documentation files (the "Software"), to deal
  37. * in the Software without restriction, including without limitation the rights
  38. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  39. * copies of the Software, and to permit persons to whom the Software is
  40. * furnished to do so, subject to the following conditions:
  41. *
  42. * The above copyright notice and this permission notice shall be included in
  43. * all copies or substantial portions of the Software.
  44. *
  45. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  46. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  47. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  48. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  49. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  50. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  51. * THE SOFTWARE.
  52. */
  53. #include <cassert>
  54. #include <cstring>
  55. #include <memory>
  56. #include <driver/gpio.h>
  57. #include <driver/spi_master.h>
  58. #include <esp_intr_alloc.h>
  59. #include <esp_ipc.h>
  60. #include <freertos/FreeRTOS.h>
  61. #include <freertos/semphr.h>
  62. #include "Color.h"
  63. #include "RmtDriver.h"
  64. using LedType = detail::TimingParams;
  65. // Times are in nanoseconds,
  66. // The RMT driver runs at 20MHz, so minimal representable time is 50 nanoseconds
  67. static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 };
  68. // longer reset time because https://blog.adafruit.com/2017/05/03/psa-the-ws2812b-rgb-led-has-been-revised-will-require-code-tweak/
  69. static const LedType LED_WS2812B = { 400, 800, 850, 450, 300000 }; // universal
  70. static const LedType LED_WS2812B_NEWVARIANT = { 200, 750, 750, 200, 300000 };
  71. static const LedType LED_WS2812B_OLDVARIANT = { 400, 800, 850, 450, 50000 };
  72. // This is timing from datasheet, but does not seem to actually work - try LED_WS2812B
  73. static const LedType LED_WS2812C = { 250, 550, 550, 250, 280000 };
  74. static const LedType LED_SK6812 = { 300, 600, 900, 600, 80000 };
  75. static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 };
  76. // Single buffer == can't touch the Rgbs between show() and wait()
  77. enum BufferType { SingleBuffer = 0, DoubleBuffer };
  78. enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2 };
  79. class SmartLed {
  80. public:
  81. friend class detail::RmtDriver;
  82. // The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds
  83. // can't fill the RMT buffer fast enough, resulting in rendering artifacts.
  84. // Usually, that means you have to set isrCore == CoreSecond.
  85. //
  86. // If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running,
  87. // so you can't use it if you define SmartLed as global variable.
  88. //
  89. // Does nothing on chips that only have one core.
  90. SmartLed(const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = DoubleBuffer,
  91. IsrCore isrCore = CoreCurrent)
  92. : _finishedFlag(xSemaphoreCreateBinary())
  93. , _driver(type, count, pin, channel, _finishedFlag)
  94. , _channel(channel)
  95. , _count(count)
  96. , _firstBuffer(new Rgb[count])
  97. , _secondBuffer(doubleBuffer ? new Rgb[count] : nullptr) {
  98. assert(channel >= 0 && channel < detail::CHANNEL_COUNT);
  99. assert(ledForChannel(channel) == nullptr);
  100. xSemaphoreGive(_finishedFlag);
  101. _driver.init();
  102. #if !defined(SOC_CPU_CORES_NUM) || SOC_CPU_CORES_NUM > 1
  103. if (!anyAlive() && isrCore != CoreCurrent) {
  104. _interruptCore = isrCore;
  105. ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, (void*)this));
  106. } else
  107. #endif
  108. {
  109. registerInterrupt((void*)this);
  110. }
  111. ledForChannel(channel) = this;
  112. }
  113. ~SmartLed() {
  114. ledForChannel(_channel) = nullptr;
  115. #if !defined(SOC_CPU_CORES_NUM) || SOC_CPU_CORES_NUM > 1
  116. if (!anyAlive() && _interruptCore != CoreCurrent) {
  117. ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, (void*)this));
  118. } else
  119. #endif
  120. {
  121. unregisterInterrupt((void*)this);
  122. }
  123. vSemaphoreDelete(_finishedFlag);
  124. }
  125. Rgb& operator[](int idx) { return _firstBuffer[idx]; }
  126. const Rgb& operator[](int idx) const { return _firstBuffer[idx]; }
  127. esp_err_t show() {
  128. esp_err_t err = startTransmission();
  129. swapBuffers();
  130. return err;
  131. }
  132. bool wait(TickType_t timeout = portMAX_DELAY) {
  133. if (xSemaphoreTake(_finishedFlag, timeout) == pdTRUE) {
  134. xSemaphoreGive(_finishedFlag);
  135. return true;
  136. }
  137. return false;
  138. }
  139. int size() const { return _count; }
  140. int channel() const { return _channel; }
  141. Rgb* begin() { return _firstBuffer.get(); }
  142. const Rgb* begin() const { return _firstBuffer.get(); }
  143. const Rgb* cbegin() const { return _firstBuffer.get(); }
  144. Rgb* end() { return _firstBuffer.get() + _count; }
  145. const Rgb* end() const { return _firstBuffer.get() + _count; }
  146. const Rgb* cend() const { return _firstBuffer.get() + _count; }
  147. private:
  148. static IsrCore _interruptCore;
  149. static void registerInterrupt(void* selfVoid) {
  150. auto* self = (SmartLed*)selfVoid;
  151. ESP_ERROR_CHECK(self->_driver.registerIsr(!anyAlive()));
  152. }
  153. static void unregisterInterrupt(void* selfVoid) {
  154. auto* self = (SmartLed*)selfVoid;
  155. ESP_ERROR_CHECK(self->_driver.unregisterIsr());
  156. }
  157. static SmartLed*& IRAM_ATTR ledForChannel(int channel);
  158. static bool anyAlive() {
  159. for (int i = 0; i != detail::CHANNEL_COUNT; i++)
  160. if (ledForChannel(i) != nullptr)
  161. return true;
  162. return false;
  163. }
  164. void swapBuffers() {
  165. if (_secondBuffer)
  166. _firstBuffer.swap(_secondBuffer);
  167. }
  168. esp_err_t startTransmission() {
  169. // Invalid use of the library, you must wait() fir previous frame to get processed first
  170. if (xSemaphoreTake(_finishedFlag, 0) != pdTRUE)
  171. abort();
  172. auto err = _driver.transmit(_firstBuffer.get());
  173. if (err != ESP_OK) {
  174. return err;
  175. }
  176. return ESP_OK;
  177. }
  178. SemaphoreHandle_t _finishedFlag;
  179. detail::RmtDriver _driver;
  180. int _channel;
  181. int _count;
  182. std::unique_ptr<Rgb[]> _firstBuffer;
  183. std::unique_ptr<Rgb[]> _secondBuffer;
  184. };
  185. #if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)
  186. #define _SMARTLEDS_SPI_HOST SPI2_HOST
  187. #define _SMARTLEDS_SPI_DMA_CHAN SPI_DMA_CH_AUTO
  188. #else
  189. #define _SMARTLEDS_SPI_HOST HSPI_HOST
  190. #define _SMARTLEDS_SPI_DMA_CHAN 1
  191. #endif
  192. class Apa102 {
  193. public:
  194. struct ApaRgb {
  195. ApaRgb(uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF)
  196. : v(0xE0 | v)
  197. , b(b)
  198. , g(g)
  199. , r(r) {}
  200. ApaRgb& operator=(const Rgb& o) {
  201. r = o.r;
  202. g = o.g;
  203. b = o.b;
  204. return *this;
  205. }
  206. ApaRgb& operator=(const Hsv& o) {
  207. *this = Rgb { o };
  208. return *this;
  209. }
  210. uint8_t v, b, g, r;
  211. };
  212. static const int FINAL_FRAME_SIZE = 4;
  213. static const int TRANS_COUNT = 2 + 8;
  214. Apa102(int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, int clock_speed_hz = 1000000)
  215. : _count(count)
  216. , _firstBuffer(new ApaRgb[count])
  217. , _secondBuffer(doubleBuffer ? new ApaRgb[count] : nullptr)
  218. , _transCount(0)
  219. , _initFrame(0) {
  220. spi_bus_config_t buscfg;
  221. memset(&buscfg, 0, sizeof(buscfg));
  222. buscfg.mosi_io_num = datapin;
  223. buscfg.miso_io_num = -1;
  224. buscfg.sclk_io_num = clkpin;
  225. buscfg.quadwp_io_num = -1;
  226. buscfg.quadhd_io_num = -1;
  227. buscfg.max_transfer_sz = 65535;
  228. spi_device_interface_config_t devcfg;
  229. memset(&devcfg, 0, sizeof(devcfg));
  230. devcfg.clock_speed_hz = clock_speed_hz;
  231. devcfg.mode = 0;
  232. devcfg.spics_io_num = -1;
  233. devcfg.queue_size = TRANS_COUNT;
  234. devcfg.pre_cb = nullptr;
  235. auto ret = spi_bus_initialize(_SMARTLEDS_SPI_HOST, &buscfg, _SMARTLEDS_SPI_DMA_CHAN);
  236. assert(ret == ESP_OK);
  237. ret = spi_bus_add_device(_SMARTLEDS_SPI_HOST, &devcfg, &_spi);
  238. assert(ret == ESP_OK);
  239. std::fill_n(_finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF);
  240. }
  241. ~Apa102() {
  242. // ToDo
  243. }
  244. ApaRgb& operator[](int idx) { return _firstBuffer[idx]; }
  245. const ApaRgb& operator[](int idx) const { return _firstBuffer[idx]; }
  246. void show() {
  247. _buffer = _firstBuffer.get();
  248. startTransmission();
  249. swapBuffers();
  250. }
  251. void wait() {
  252. for (int i = 0; i != _transCount; i++) {
  253. spi_transaction_t* t;
  254. spi_device_get_trans_result(_spi, &t, portMAX_DELAY);
  255. }
  256. }
  257. private:
  258. void swapBuffers() {
  259. if (_secondBuffer)
  260. _firstBuffer.swap(_secondBuffer);
  261. }
  262. void startTransmission() {
  263. for (int i = 0; i != TRANS_COUNT; i++) {
  264. _transactions[i].cmd = 0;
  265. _transactions[i].addr = 0;
  266. _transactions[i].flags = 0;
  267. _transactions[i].rxlength = 0;
  268. _transactions[i].rx_buffer = nullptr;
  269. }
  270. // Init frame
  271. _transactions[0].length = 32;
  272. _transactions[0].tx_buffer = &_initFrame;
  273. spi_device_queue_trans(_spi, _transactions + 0, portMAX_DELAY);
  274. // Data
  275. _transactions[1].length = 32 * _count;
  276. _transactions[1].tx_buffer = _buffer;
  277. spi_device_queue_trans(_spi, _transactions + 1, portMAX_DELAY);
  278. _transCount = 2;
  279. // End frame
  280. for (int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++) {
  281. _transactions[2 + i].length = 32 * FINAL_FRAME_SIZE;
  282. _transactions[2 + i].tx_buffer = _finalFrame;
  283. spi_device_queue_trans(_spi, _transactions + 2 + i, portMAX_DELAY);
  284. _transCount++;
  285. }
  286. }
  287. spi_device_handle_t _spi;
  288. int _count;
  289. std::unique_ptr<ApaRgb[]> _firstBuffer, _secondBuffer;
  290. ApaRgb* _buffer;
  291. spi_transaction_t _transactions[TRANS_COUNT];
  292. int _transCount;
  293. uint32_t _initFrame;
  294. uint32_t _finalFrame[FINAL_FRAME_SIZE];
  295. };
  296. class LDP8806 {
  297. public:
  298. struct LDP8806_GRB {
  299. LDP8806_GRB(uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0)
  300. : g(g_7bit)
  301. , r(r_7bit)
  302. , b(b_7bit) {}
  303. LDP8806_GRB& operator=(const Rgb& o) {
  304. //Convert 8->7bit colour
  305. r = (o.r * 127 / 256) | 0x80;
  306. g = (o.g * 127 / 256) | 0x80;
  307. b = (o.b * 127 / 256) | 0x80;
  308. return *this;
  309. }
  310. LDP8806_GRB& operator=(const Hsv& o) {
  311. *this = Rgb { o };
  312. return *this;
  313. }
  314. uint8_t g, r, b;
  315. };
  316. static const int LED_FRAME_SIZE_BYTES = sizeof(LDP8806_GRB);
  317. static const int LATCH_FRAME_SIZE_BYTES = 3;
  318. static const int TRANS_COUNT_MAX = 20; //Arbitrary, supports up to 600 LED
  319. LDP8806(
  320. int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000)
  321. : _count(count)
  322. , _firstBuffer(new LDP8806_GRB[count])
  323. , _secondBuffer(doubleBuffer ? new LDP8806_GRB[count] : nullptr)
  324. ,
  325. // one 'latch'/start-of-data mark frame for every 32 leds
  326. _latchFrames((count + 31) / 32) {
  327. spi_bus_config_t buscfg;
  328. memset(&buscfg, 0, sizeof(buscfg));
  329. buscfg.mosi_io_num = datapin;
  330. buscfg.miso_io_num = -1;
  331. buscfg.sclk_io_num = clkpin;
  332. buscfg.quadwp_io_num = -1;
  333. buscfg.quadhd_io_num = -1;
  334. buscfg.max_transfer_sz = 65535;
  335. spi_device_interface_config_t devcfg;
  336. memset(&devcfg, 0, sizeof(devcfg));
  337. devcfg.clock_speed_hz = clock_speed_hz;
  338. devcfg.mode = 0;
  339. devcfg.spics_io_num = -1;
  340. devcfg.queue_size = TRANS_COUNT_MAX;
  341. devcfg.pre_cb = nullptr;
  342. auto ret = spi_bus_initialize(_SMARTLEDS_SPI_HOST, &buscfg, _SMARTLEDS_SPI_DMA_CHAN);
  343. assert(ret == ESP_OK);
  344. ret = spi_bus_add_device(_SMARTLEDS_SPI_HOST, &devcfg, &_spi);
  345. assert(ret == ESP_OK);
  346. std::fill_n(_latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0);
  347. }
  348. ~LDP8806() {
  349. // noop
  350. }
  351. LDP8806_GRB& operator[](int idx) { return _firstBuffer[idx]; }
  352. const LDP8806_GRB& operator[](int idx) const { return _firstBuffer[idx]; }
  353. void show() {
  354. _buffer = _firstBuffer.get();
  355. startTransmission();
  356. swapBuffers();
  357. }
  358. void wait() {
  359. while (_transCount--) {
  360. spi_transaction_t* t;
  361. spi_device_get_trans_result(_spi, &t, portMAX_DELAY);
  362. }
  363. }
  364. private:
  365. void swapBuffers() {
  366. if (_secondBuffer)
  367. _firstBuffer.swap(_secondBuffer);
  368. }
  369. void startTransmission() {
  370. _transCount = 0;
  371. for (int i = 0; i != TRANS_COUNT_MAX; i++) {
  372. _transactions[i].cmd = 0;
  373. _transactions[i].addr = 0;
  374. _transactions[i].flags = 0;
  375. _transactions[i].rxlength = 0;
  376. _transactions[i].rx_buffer = nullptr;
  377. }
  378. // LED Data
  379. _transactions[0].length = (LED_FRAME_SIZE_BYTES * 8) * _count;
  380. _transactions[0].tx_buffer = _buffer;
  381. spi_device_queue_trans(_spi, _transactions + _transCount, portMAX_DELAY);
  382. _transCount++;
  383. // 'latch'/start-of-data marker frames
  384. for (int i = 0; i < _latchFrames; i++) {
  385. _transactions[_transCount].length = (LATCH_FRAME_SIZE_BYTES * 8);
  386. _transactions[_transCount].tx_buffer = _latchBuffer;
  387. spi_device_queue_trans(_spi, _transactions + _transCount, portMAX_DELAY);
  388. _transCount++;
  389. }
  390. }
  391. spi_device_handle_t _spi;
  392. int _count;
  393. std::unique_ptr<LDP8806_GRB[]> _firstBuffer, _secondBuffer;
  394. LDP8806_GRB* _buffer;
  395. spi_transaction_t _transactions[TRANS_COUNT_MAX];
  396. int _transCount;
  397. int _latchFrames;
  398. uint8_t _latchBuffer[LATCH_FRAME_SIZE_BYTES];
  399. };