17#include "is31fl3733.hpp"
34 : _hw(wire ? wire->getSercom() : nullptr), _addr(addr), _sdbPin(sdbPin), _irqPin(irqPin),
35 _currentPage(0xFF), _pwmEnqueued(0), _pwmLocked(false), _pwmBatchActive(false),
36 _abmEnqueued(0), _abmLocked(false), _cmdReturn(0), _cmdError(0), _syncComplete(false),
37 _syncStatus(0), _syncTargetCmd(-1), _begun(false), _lastISR(0), _crValue(CR_SSD),
38 _colorOrder(ColorOrder::GRB), _maskShortFaults(false) {
42 _crwlTx[1] = PSWL_ENABLE;
47 _cmdTxn[0].config = I2C_CFG_STOP;
48 _cmdTxn[0].address = _addr;
49 _cmdTxn[0].txPtr = _crwlTx;
50 _cmdTxn[0].length = 2;
51 _cmdCtx[0] = {
this, 0,
nullptr,
nullptr};
52 _cmdTxn[0].onComplete = _cmdCallback;
53 _cmdTxn[0].user = &_cmdCtx[0];
54 _cmdTxn[0].chainNext =
false;
57 _cmdTxn[1].config = I2C_CFG_STOP;
58 _cmdTxn[1].address = _addr;
59 _cmdTxn[1].txPtr = _pgSelTx;
60 _cmdTxn[1].length = 2;
61 _cmdCtx[1] = {
this, 1,
nullptr,
nullptr};
62 _cmdTxn[1].onComplete = _cmdCallback;
63 _cmdTxn[1].user = &_cmdCtx[1];
64 _cmdTxn[1].chainNext =
false;
67 _cmdTxn[2].config = I2C_CFG_STOP;
68 _cmdTxn[2].address = _addr;
69 _cmdTxn[2].txPtr = _cmdTx;
70 _cmdCtx[2] = {
this, 2,
nullptr,
nullptr};
71 _cmdTxn[2].onComplete = _cmdCallback;
72 _cmdTxn[2].user = &_cmdCtx[2];
73 _cmdTxn[2].chainNext =
false;
76 _cmdTxn[3].config = I2C_CFG_READ | I2C_CFG_STOP;
77 _cmdTxn[3].address = _addr;
78 _cmdTxn[3].rxPtr = _cmdRx;
79 _cmdCtx[3] = {
this, 3,
nullptr,
nullptr};
80 _cmdTxn[3].onComplete = _cmdCallback;
81 _cmdTxn[3].user = &_cmdCtx[3];
82 _cmdTxn[3].chainNext =
false;
85 _pwmTxn.config = I2C_CFG_STOP;
86 _pwmTxn.address = _addr;
88 _pwmTxn.txPtr =
nullptr;
89 _pwmTxn.onComplete = _txnCallback;
91 _pwmTxn.chainNext =
false;
94 _abmTxn.config = I2C_CFG_STOP;
95 _abmTxn.address = _addr;
97 _abmTxn.txPtr =
nullptr;
98 _abmTxn.onComplete = _txnModeCallback;
100 _abmTxn.chainNext =
false;
103 memset(_ledOn, 0xFF,
sizeof(_ledOn));
107 for (uint8_t row = 0; row < kHardwareRows; row++)
108 _pwm_matrix[row][0] =
static_cast<uint8_t
>(row * kHardwareCols);
110 for (uint8_t row = 0; row < kHardwareRows; row++)
111 _abm_matrix[row][0] =
static_cast<uint8_t
>(row * kHardwareCols);
128 if (_sdbPin != 0xFF) {
129 pinMode(_sdbPin, OUTPUT);
130 digitalWrite(_sdbPin, LOW);
132 digitalWrite(_sdbPin, HIGH);
138 pinMode(_irqPin, INPUT_PULLUP);
146 if (!_syncRead(RESET, &dummy, 1))
155 _crValue =
static_cast<uint8_t
>(CR_SSD | CR_PFS(pfs & 0x03));
156 if (!_syncWrite(CR, &_crValue, 1))
163 if (!_syncWrite(LEDONOFF, _ledOn, 24))
167 uint8_t gcc_osd = 0x01;
168 if (!_syncWrite(GCC, &gcc_osd, 1))
172 uint8_t cr_osd =
static_cast<uint8_t
>(_crValue | CR_OSD);
173 if (!_syncWrite(CR, &cr_osd, 1))
180 if (!_syncRead(LEDOPEN, _ledOpen, 24))
182 if (!_syncRead(LEDSHORT, _ledShort, 24))
186 uint8_t isr_status = 0;
187 if (_syncRead((uint16_t)ISR << 8, &isr_status, 1))
188 _lastISR = isr_status;
193 for (
size_t i = 0; i < 24; i++) {
195 _ledOn[i] =
static_cast<uint8_t
>(_ledOn[i] & ~_ledOpen[i]);
199 if (!_syncWrite(LEDONOFF, _ledOn, 24))
203 uint8_t gcc_normal = 0xFF;
204 if (!_syncWrite(GCC, &gcc_normal, 1))
208 if (!_syncWrite(CR, &_crValue, 1))
214 uint8_t pwmMode[kHardwareCols];
215 memset(pwmMode,
static_cast<uint8_t
>(ABMMode::PWM_MODE),
sizeof(pwmMode));
216 for (uint8_t row = 0u; row < kHardwareRows; ++row) {
217 if (!_syncWrite(
static_cast<uint16_t
>(LEDABM + (row * kHardwareCols)), pwmMode,
220 memset(_abm_matrix[row] + 1,
static_cast<uint8_t
>(ABMMode::PWM_MODE), kHardwareCols);
224 if (_irqPin != 0xFF) {
226 uint8_t imr_value = IMR_IO;
227 if (_maskShortFaults)
228 imr_value =
static_cast<uint8_t
>(imr_value | IMR_IS);
229 if (!_syncWrite((uint16_t)IMR << 8, &imr_value, 1))
234 uint8_t purValue = pur & 0b111;
235 uint8_t pdrValue = pdr & 0b111;
236 if (!_syncWrite(SWPUR, &purValue, 1))
238 if (!_syncWrite(CSPDR, &pdrValue, 1))
245 _cmdCtx[2].userCallback =
nullptr;
246 _cmdCtx[2].user =
nullptr;
247 _cmdCtx[3].userCallback =
nullptr;
248 _cmdCtx[3].user =
nullptr;
252 if (_irqPin != 0xFF) {
254 attachInterrupt(digitalPinToInterrupt(_irqPin), _irqCallback, FALLING);
266 _syncRead(CR, _cmdTx, 1);
271 if (_irqPin != 0xFF) {
272 detachInterrupt(digitalPinToInterrupt(_irqPin));
285 digitalWrite(_sdbPin, HIGH);
288 _asyncWrite(CR, &_crValue, 1,
nullptr,
nullptr);
290 _resumeDataTransfers();
294 const uint8_t cr_off = 0x00;
295 _syncWrite(CR, &cr_off, 1);
298 digitalWrite(_sdbPin, LOW);
302 _asyncWrite(GCC, &gcc, 1,
nullptr,
nullptr);
306 uint8_t purValue =
static_cast<uint8_t
>(pur & 0x07);
307 _asyncWrite(SWPUR, &purValue, 1,
nullptr,
nullptr);
311 uint8_t pdrValue =
static_cast<uint8_t
>(pdr & 0x07);
312 _asyncWrite(CSPDR, &pdrValue, 1,
nullptr,
nullptr);
316 _crValue =
static_cast<uint8_t
>((_crValue & ~0x60u) | CR_PFS(pfs & 0x03));
317 _asyncWrite(CR, &_crValue, 1,
nullptr,
nullptr);
321 uint8_t mask =
static_cast<uint8_t
>(imrMask & 0x0F);
322 _asyncWrite((uint16_t)IMR << 8, &mask, 1,
nullptr,
nullptr);
331 if (row > kHardwareRows || col > kHardwareCols || !row || !col)
335 uint8_t idx = row - 1;
338 _pwm_matrix[idx][col] = pwm;
341 uint16_t rowBit = 1 << idx;
342 if (_pwmEnqueued & rowBit)
345 _pwmEnqueued |= rowBit;
346 _pwmPendingRows.store(idx);
349 if (!_pwmTxn.txPtr && !_pwmLocked)
355 if (row > kHardwareRows || !row)
359 uint8_t idx = (row - 1) & 0x0F;
363 memcpy(_pwm_matrix[idx] + 1, pwmValues, kHardwareCols);
366 uint16_t rowBit = 1 << idx;
367 if (_pwmEnqueued & rowBit)
370 _pwmEnqueued |= rowBit;
371 _pwmPendingRows.store(idx);
374 if (!_pwmTxn.txPtr && !_pwmLocked)
379 _pwmBatchActive =
true;
383 _pwmBatchActive =
false;
385 if (flush && !_pwmTxn.txPtr && !_pwmLocked)
394 if (row > kLogicalRows || col > kHardwareCols || !row || !col)
398 uint8_t idx = row - 1;
403 uint8_t baseRow = idx * 3 + 1;
406 switch (_colorOrder) {
407 case ColorOrder::RGB:
408 SetPixelPWM(baseRow + 0, col, r);
409 SetPixelPWM(baseRow + 1, col, g);
410 SetPixelPWM(baseRow + 2, col, b);
412 case ColorOrder::GRB:
413 SetPixelPWM(baseRow + 0, col, g);
414 SetPixelPWM(baseRow + 1, col, r);
415 SetPixelPWM(baseRow + 2, col, b);
417 case ColorOrder::BRG:
418 SetPixelPWM(baseRow + 0, col, b);
419 SetPixelPWM(baseRow + 1, col, r);
420 SetPixelPWM(baseRow + 2, col, g);
422 case ColorOrder::RBG:
423 SetPixelPWM(baseRow + 0, col, r);
424 SetPixelPWM(baseRow + 1, col, b);
425 SetPixelPWM(baseRow + 2, col, g);
427 case ColorOrder::GBR:
428 SetPixelPWM(baseRow + 0, col, g);
429 SetPixelPWM(baseRow + 1, col, b);
430 SetPixelPWM(baseRow + 2, col, r);
432 case ColorOrder::BGR:
433 SetPixelPWM(baseRow + 0, col, b);
434 SetPixelPWM(baseRow + 1, col, g);
435 SetPixelPWM(baseRow + 2, col, r);
446 for (uint8_t row = 0; row < kHardwareRows; row++) {
447 memset(_pwm_matrix[row] + 1, pwm, kHardwareCols);
450 uint16_t rowBit = 1 << row;
451 if (!(_pwmEnqueued & rowBit)) {
452 _pwmEnqueued |= rowBit;
453 _pwmPendingRows.store(row);
458 if (!_pwmTxn.txPtr && !_pwmLocked)
468 if (row > kHardwareRows || col > kHardwareCols || !row || !col)
472 uint8_t idx = row - 1;
475 _abm_matrix[idx][col] =
static_cast<uint8_t
>(mode);
478 uint16_t rowBit = 1 << idx;
479 if (_abmEnqueued & rowBit)
482 _abmEnqueued |= rowBit;
483 _abmPendingRows.store(idx);
486 if (!_abmTxn.txPtr && !_abmLocked)
492 if (row > kHardwareRows || !row)
496 uint8_t idx = (row - 1) & 0x0F;
500 memset(_abm_matrix[idx] + 1,
static_cast<uint8_t
>(mode), kHardwareCols);
503 uint16_t rowBit = 1 << idx;
504 if (_abmEnqueued & rowBit)
507 _abmEnqueued |= rowBit;
508 _abmPendingRows.store(idx);
511 if (!_abmTxn.txPtr && !_abmLocked)
517 for (uint8_t row = 0; row < kHardwareRows; row++) {
518 memset(_abm_matrix[row] + 1,
static_cast<uint8_t
>(mode), kHardwareCols);
521 uint16_t rowBit = 1 << row;
522 if (!(_abmEnqueued & rowBit)) {
523 _abmEnqueued |= rowBit;
524 _abmPendingRows.store(row);
529 if (!_abmTxn.txPtr && !_abmLocked) {
536 if (row > kHardwareRows || col > kHardwareCols || !row || !col)
537 return ABMMode::PWM_MODE;
540 uint8_t idx = row - 1;
543 return static_cast<ABMMode
>(_abm_matrix[idx][col]);
547 if (row > kLogicalRows || col > kHardwareCols || !row || !col)
551 uint8_t idx = row - 1;
556 uint8_t baseRow = idx * 3 + 1;
559 SetPixelMode(baseRow + 0, col, mode);
560 SetPixelMode(baseRow + 1, col, mode);
561 SetPixelMode(baseRow + 2, col, mode);
570 if (_currentPage == page)
574 _cmdTxn[0].chainNext =
false;
575 _hw->enqueueWIRE(&_cmdTxn[0]);
579 _pgSelTx[1] = page & 0b11;
580 _cmdTxn[1].chainNext =
false;
581 _hw->enqueueWIRE(&_cmdTxn[1]);
595 _syncComplete =
false;
598 _cmdError &= ~((1u << 0) | (1u << 1) | (1u << 2));
599 _cmdCtx[2].initialStatus = 0;
601 _asyncWrite(pagereg, data, len,
nullptr,
nullptr);
603 constexpr uint32_t timeoutUs = 100000ul;
604 const uint32_t start = micros();
605 while (!_syncComplete && (
static_cast<uint32_t
>(micros() - start) < timeoutUs)) {
609 if (!_syncComplete) {
612 _syncComplete =
true;
616 return (_syncStatus == 0) && ((_cmdError & ((1u << 0) | (1u << 1) | (1u << 2))) == 0) &&
617 (_cmdCtx[2].initialStatus == 0);
621 _syncComplete =
false;
624 _cmdError &= ~((1u << 0) | (1u << 1) | (1u << 2) | (1u << 3));
625 _cmdCtx[3].initialStatus = 0;
627 _asyncRead(pagereg, dest, len,
nullptr,
nullptr);
629 constexpr uint32_t timeoutUs = 100000ul;
630 const uint32_t start = micros();
631 while (!_syncComplete && (
static_cast<uint32_t
>(micros() - start) < timeoutUs)) {
635 if (!_syncComplete) {
638 _syncComplete =
true;
642 return (_syncStatus == 0) &&
643 ((_cmdError & ((1u << 0) | (1u << 1) | (1u << 2) | (1u << 3))) == 0) &&
644 (_cmdCtx[3].initialStatus == 0);
652 if (abmNum < 1 || abmNum > 3)
655 _abmCallbacks[abmNum - 1] = callback;
659 if (abmNumber < 1 || abmNumber > 3)
661 uint16_t pagereg = 0;
675 const uint8_t cfg[4] = {
676 static_cast<uint8_t
>(
static_cast<uint8_t
>(config.
T1) |
static_cast<uint8_t
>(config.
T2)),
677 static_cast<uint8_t
>(
static_cast<uint8_t
>(config.
T3) |
static_cast<uint8_t
>(config.
T4)),
678 static_cast<uint8_t
>(
static_cast<uint8_t
>(config.
Tend) |
679 static_cast<uint8_t
>(config.
Tbegin) |
680 static_cast<uint8_t
>((config.
Times >> 8) & 0x0F)),
681 static_cast<uint8_t
>(config.
Times & 0xFF),
683 _asyncWrite(pagereg, cfg, 4,
nullptr,
nullptr);
687 uint8_t turPadding[1] = {0};
688 _asyncWrite(TUR, turPadding, 1,
nullptr,
nullptr);
692 ConfigureABM(1, config);
696 ConfigureABM(2, config);
700 ConfigureABM(3, config);
707 _crValue &=
static_cast<uint8_t
>(~CR_BEN);
709 _asyncWrite(CR, &_crValue, 1,
nullptr,
nullptr);
719 uint8_t tur_value = 0x00;
720 _asyncWrite(TUR, &tur_value, 1,
nullptr,
nullptr);
732 for (
size_t i = 0; i < 24; i++) {
733 self->_ledOn[i] =
static_cast<uint8_t
>(self->_ledOn[i] & ~self->_ledOpen[i]);
737 self->_asyncWrite(LEDONOFF, self->_ledOn, 24,
nullptr,
nullptr);
740 self->_resumeDataTransfers();
745 if (!context || !context->self)
749 const uint8_t bit =
static_cast<uint8_t
>(1u << context->index);
751 self->_cmdReturn |= bit;
753 self->_cmdError |= bit;
755 if (self->_syncTargetCmd ==
static_cast<int8_t
>(context->index)) {
756 self->_syncStatus = status;
757 self->_syncComplete =
true;
758 self->_syncTargetCmd = -1;
762 if (!context->isFinal && context->index < 3) {
764 self->_cmdCtx[context->index + 1].initialStatus |= status;
769 if (context->isFinal && context->userCallback)
770 context->userCallback(context->user, status);
778 if (_instance && _instance->_abmCallbacks[0])
779 _instance->_abmCallbacks[0]();
783 if (_instance && _instance->_abmCallbacks[1])
784 _instance->_abmCallbacks[1]();
788 if (_instance && _instance->_abmCallbacks[2])
789 _instance->_abmCallbacks[2]();
Asynchronous DMA-driven IS31FL3733 LED driver for SAMD SERCOM I2C.
void ConfigureABM2(const ABMConfig &config)
Configure ABM-2 control registers (Page 3 ABM2CR..ABM2CR+4).
static void _osbCallback(void *user, int status)
Open/Short detection callback (updates LED On/Off mask).
void SetMatrixMode(ABMMode mode)
Set mode for the entire matrix (Page 2).
void SetPFS(uint8_t pfs)
Set PWM frequency selection bits in CR (PFS[6:5]). Preserves other tracked CR bits (e....
void ConfigureABM3(const ABMConfig &config)
Configure ABM-3 control registers (Page 3 ABM3CR..ABM3CR+4).
void BeginPwmBatch()
Begin a batched PWM update.
void DeviceOff()
Disable the device (hardware and software shutdown).
void end()
RESET device and disable (hardware shutdown). Reads RESET register to trigger software reset,...
void SetPixelMode(uint8_t row, uint8_t col, ABMMode mode)
Set mode for a single LED (hardware coordinates, Page 2).
void EnableABM(bool enable=true)
Enable or disable ABM engine in CR (Page 3).
static IS31FL3733 * _instance
Static instance pointer for ISR access.
void SetRowPWM(uint8_t row, const uint8_t *pwmValues)
Set PWM values for an entire row (hardware coordinates).
bool begin(uint8_t pfs=0, uint8_t pur=0b111, uint8_t pdr=0b111)
Initialize the device (blocking for initial config).
static void _abm3CallbackWrapper()
Dispatch ABM3 completion callback on a safe context.
static void _abm1CallbackWrapper()
Static wrappers for ABM completion callback dispatch (PendSV-safe entry points).
void ConfigureABM(uint8_t abmNumber, const ABMConfig &config)
Configure ABM timing/loop registers for ABM1/2/3 on Page 3.
void SetSWPUR(uint8_t pur)
Set SW pull-up resistor control (Page 3 SWPUR).
~IS31FL3733()
Destroy the driver; calls end() if begin() succeeded.
void Fill(uint8_t pwm=0)
Fill the entire matrix with a PWM value.
void ConfigureABM1(const ABMConfig &config)
Configure ABM-1 control registers (Page 3 ABM1CR..ABM1CR+4).
void SetABMCallback(uint8_t abmNum, std::function< void()> callback)
Register callback for ABM1/2/3 completion.
static void _abm2CallbackWrapper()
Dispatch ABM2 completion callback on a safe context.
void EndPwmBatch(bool flush=true)
End a batched PWM update and optionally flush queued rows.
void _ensurePage(uint8_t page)
Ensure we're on the target page (skips if already there, enqueues unlock + page-select if needed).
bool _syncWrite(uint16_t pagereg, const uint8_t *data, uint8_t len)
Blocking write helper used by begin() setup flow.
void SetGCC(uint8_t gcc)
Set global current control register (Page 3 GCC).
ABMMode GetPixelMode(uint8_t row, uint8_t col) const
Get mode for a single LED (hardware coordinates, Page 2).
void _resumeDataTransfers()
Restore Page 1, clear _pwmLocked, resume PWM writes.
void SetCSPDR(uint8_t pdr)
Set CS pull-down resistor control (Page 3 CSPDR).
void SetIMR(uint8_t imrMask)
Set interrupt mask register (Common IMR).
void DeviceOn()
Enable the device (hardware and software startup).
bool _syncRead(uint16_t pagereg, uint8_t *dest, uint8_t len)
Blocking read helper used by begin() setup flow.
void SetPixelColor(uint8_t row, uint8_t col, uint8_t r, uint8_t g, uint8_t b)
Set RGB color for a logical pixel (with color order mapping).
void SetRowMode(uint8_t row, ABMMode mode)
Set mode for an entire row (hardware coordinates, Page 2).
static void _cmdCallback(void *user, int status)
Command transaction callback (triggered by SERCOM ISR).
void SetPixelColorMode(uint8_t row, uint8_t col, ABMMode mode)
Set mode for a logical RGB pixel (with color order mapping).
void TriggerABM()
Latch ABM timing updates by writing TUR on Page 3.
void SetPixelPWM(uint8_t row, uint8_t col, uint8_t pwm)
Set PWM duty cycle for a single LED (hardware coordinates).
High-level ABM configuration values before register packing.
AbmLoopBegin Tbegin
Loop begin segment selector.
AbmT1 T1
Fade-in segment duration selector.
AbmT4 T4
Off-time segment duration selector.
AbmT2 T2
Hold-high segment duration selector.
AbmLoopEnd Tend
Loop end segment selector.
AbmT3 T3
Fade-out segment duration selector.
uint16_t Times
Loop count (0 = kAbmLoopForever, max = kAbmLoopTimesMax)
Context passed to SERCOM ISR callback for command-chain transactions.