| 1625 | } |
| 1626 | |
| 1627 | bool ParlioEngine::initializeSpi(const fl::vector<int>& pins, |
| 1628 | u32 spiClockHz, |
| 1629 | size_t maxBytesPerChannel) FL_NOEXCEPT { |
| 1630 | // SPI-over-PARLIO requires exactly 2 pins (clock + data) |
| 1631 | if (pins.size() != 2) { |
| 1632 | FL_WARN("PARLIO_SPI: Expected 2 pins (clock, data), got " << pins.size()); |
| 1633 | return false; |
| 1634 | } |
| 1635 | |
| 1636 | // Detect config change: if already initialized with different mode or clock, deinit first |
| 1637 | bool needsReinit = false; |
| 1638 | if (mInitialized) { |
| 1639 | if (mEncodingMode != EncodingMode::SPI || mSpiClockHz != spiClockHz || |
| 1640 | mDataWidth != 2 || mPins.size() != pins.size() || |
| 1641 | (mPins.size() >= 2 && (mPins[0] != pins[0] || mPins[1] != pins[1]))) { |
| 1642 | needsReinit = true; |
| 1643 | } else if (mPeripheral && mPeripheral->isInitialized()) { |
| 1644 | // Same config, already initialized - reuse |
| 1645 | return true; |
| 1646 | } |
| 1647 | } |
| 1648 | |
| 1649 | if (needsReinit && mPeripheral && mPeripheral->isInitialized()) { |
| 1650 | if (mTxUnitEnabled) { |
| 1651 | mPeripheral->disable(); |
| 1652 | } |
| 1653 | mPeripheral->deinitialize(); |
| 1654 | mInitialized = false; |
| 1655 | mTxUnitEnabled = false; |
| 1656 | } |
| 1657 | |
| 1658 | // Set SPI-specific state before calling base initialize path |
| 1659 | mEncodingMode = EncodingMode::SPI; |
| 1660 | mSpiClockHz = spiClockHz; |
| 1661 | |
| 1662 | // Convert max bytes to max LEDs for buffer sizing (3 bytes per "LED") |
| 1663 | size_t maxLedsEquivalent = (maxBytesPerChannel + 2) / 3; |
| 1664 | if (maxLedsEquivalent == 0) maxLedsEquivalent = 1; |
| 1665 | |
| 1666 | // Store data width and pins directly (bypass full initialize which does Wave8 LUT) |
| 1667 | mDataWidth = 2; |
| 1668 | mPins = pins; |
| 1669 | mActualChannels = 2; |
| 1670 | mDummyLanes = 0; |
| 1671 | |
| 1672 | // Store timing parameters |
| 1673 | mTimingT1Ns = 0; |
| 1674 | mTimingT2Ns = 0; |
| 1675 | mTimingT3Ns = 0; |
| 1676 | mResetUs = 0; // SPI protocols use explicit start/end frames, no reset |
| 1677 | |
| 1678 | // Get peripheral |
| 1679 | if (mPeripheral == nullptr) { |
| 1680 | mPeripheral = getParlioPeripheral(); |
| 1681 | } |
| 1682 | |
| 1683 | // Allocate ISR context |
| 1684 | if (!mIsrContext) { |
no test coverage detected