Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- the leds work on 11/13 pair. i don't see anything for 26/27
- 11:53 PM
- Good — that confirms the spi_output_template.h fix from earlier is still needed. The library is still routing everything through SPI0.
- Let's verify the fix actually got applied. Can you paste the current contents of:
- D:\Arduino\libraries\FastLED\src\platforms\arm\teensy\teensy4_common\spi_output_template.h
- If you haven't edited it yet, it will still show the original broken version:
- cpp
- template<fl::u8 _DATA_PIN, fl::u8 _CLOCK_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput : public SPIDeviceProxy<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER, SPI, 0> {};
- If that's what's there, replace the entire file contents with:
- cpp
- #pragma once
- #include "fl/int.h"
- #include "platforms/arm/mxrt1062/spi_device_proxy.h"
- namespace fl {
- // Default: SPI0 (clock pin 13, data pin 11)
- template<fl::u8 _DATA_PIN, fl::u8 _CLOCK_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput : public SPIDeviceProxy<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER, SPI, 0> {};
- // SPI1: clock pin 27 (data pin 26)
- template<fl::u8 _DATA_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput<_DATA_PIN, 27, _SPI_CLOCK_DIVIDER>
- : public SPIDeviceProxy<_DATA_PIN, 27, _SPI_CLOCK_DIVIDER, SPI1, 1> {};
- // SPI2: clock pin 45 (data pin 43)
- template<fl::u8 _DATA_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput<_DATA_PIN, 45, _SPI_CLOCK_DIVIDER>
- : public SPIDeviceProxy<_DATA_PIN, 45, _SPI_CLOCK_DIVIDER, SPI2, 2> {};
- // SPI2: clock pin 49 (data pin 50, alternate SPI2 pins)
- template<fl::u8 _DATA_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput<_DATA_PIN, 49, _SPI_CLOCK_DIVIDER>
- : public SPIDeviceProxy<_DATA_PIN, 49, _SPI_CLOCK_DIVIDER, SPI2, 2> {};
- } // namespace fl
- ```
- Then **delete the build cache** again:
- ```
- C:\Users\myles\AppData\Local\arduino\sketches\BD4C3A91B51A23E72D2835EEF8ABAEF5\core\
- And recompile. The 26/27 strip should come to life.
- #pragma once
- #include "fl/int.h"
- #include "platforms/arm/mxrt1062/spi_device_proxy.h"
- namespace fl {
- // Three explicit specializations, one catch-all
- // Clock pin 13 → SPI0 (pins 11/13)
- // Clock pin 27 → SPI1 (pins 26/27)
- // Clock pin 45 → SPI2 (pins 43/45)
- // Clock pin 49 → SPI2 (pins 50/49)
- template<fl::u8 _DATA_PIN, fl::u8 _CLOCK_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput : public SPIDeviceProxy<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER, SPI, 0> {};
- // SPI1: clock pin 27
- template<fl::u8 _DATA_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput<_DATA_PIN, 27, _SPI_CLOCK_DIVIDER>
- : public SPIDeviceProxy<_DATA_PIN, 27, _SPI_CLOCK_DIVIDER, SPI1, 1> {};
- // SPI2: clock pin 45
- template<fl::u8 _DATA_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput<_DATA_PIN, 45, _SPI_CLOCK_DIVIDER>
- : public SPIDeviceProxy<_DATA_PIN, 45, _SPI_CLOCK_DIVIDER, SPI2, 2> {};
- // SPI2: clock pin 49 (alternate SPI2 pins)
- template<fl::u8 _DATA_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput<_DATA_PIN, 49, _SPI_CLOCK_DIVIDER>
- : public SPIDeviceProxy<_DATA_PIN, 49, _SPI_CLOCK_DIVIDER, SPI2, 2> {};
- } // namespace fl
- Show more
- 11:57 PM
- The fix is already in place. So the template is correct but 26/27 still isn't outputting. Let me look deeper at the SPIDeviceProxy and bus manager to see what's actually happening at runtime when it tries to use SPI1.
- Ran 2 commands
- Ran 2 commands
- Now I can see exactly what's happening. The bus manager groups strips by clock pin — and since your two strips use different clock pins (13 and 27), each gets its own bus with only 1 device, which means each gets SINGLE_SPI. That's correct. But the SINGLE_SPI path in the proxy uses the Teensy4HardwareSPIOutput which is instantiated with whatever SPIObject was passed at template time — and it calls _SPIObject.begin() with no setSCK/setMOSI first.
- Check if fastspi_arm_mxrt1062 calls setSCK/setMOSI before begin
- Check if fastspi_arm_mxrt1062 calls setSCK/setMOSI before begin
- Found it. Line 35:
- cpp
- void init() { _SPIObject.begin(); }
- No setSCK() or setMOSI() call before begin().
- On Teensy 4.x, SPI1.begin() without first calling SPI1.setSCK(27) and SPI1.setMOSI(26) defaults to whatever pins it wants — and on SPI1 the library may not default to 26/27. The setSCK/setMOSI calls must happen before begin().
- The fix is in fastspi_arm_mxrt1062.h, in the init() method. Replace it with a version that configures the pins first:
- cpp
- void init() {
- _SPIObject.setSCK(_CLOCK_PIN);
- _SPIObject.setMOSI(_DATA_PIN);
- _SPIObject.begin();
- }
- ```
- That's the only change needed. The file is at:
- ```
- D:\Arduino\libraries\FastLED\src\platforms\arm\mxrt1062\fastspi_arm_mxrt1062.h
- Find this line (around line 35):
- cpp
- void init() { _SPIObject.begin(); }
- Replace it with:
- cpp
- void init() {
- _SPIObject.setSCK(_CLOCK_PIN);
- _SPIObject.setMOSI(_DATA_PIN);
- _SPIObject.begin();
- }
- ```
- Then delete the build cache again:
- ```
- C:\Users\myles\AppData\Local\arduino\sketches\BD4C3A91B51A23E72D2835EEF8ABAEF5\core\
- And recompile. The setSCK/setMOSI calls tell the Teensy SPI library exactly which physical pins to use before it initializes the LPSPI peripheral, so SPI1 will actually drive pins 26/27 instead of defaulting.
- still no leds 26/27
- 12:04 AM
- Let me dig deeper into the proxy's SINGLE_SPI path to trace exactly what happens at runtime.
- Ran 5 commands
- Ran 5 commands
- Now I can see the full picture. The createSingleSPI does nothing — it's just a passthrough. The actual work is supposed to happen back in SPIDeviceProxy::init() where it calls mSingleSPI->init() which calls setSCK/setMOSI/begin(). But there's a critical timing problem:
- mBusManager->initialize() is called inside the first proxy's init(), which runs at addLeds time for the first strip only. When the second strip's proxy calls init(), mInitialized on the bus manager is already true, so initialize() is a no-op — but the second strip's mSingleSPI hasn't been created yet at that point.
- Let me verify:
- Re-read the proxy init() to confirm the initialization order bug
- Re-read the proxy init() to confirm the initialization order bug
- There's the bug. mBusManager->initialize() is marked idempotent and only runs once — but mSingleSPI creation happens after initialize(). So for strip 1 (pins 11/13), init() runs, calls initialize(), sees SINGLE_SPI, creates mSingleSPI — works fine. For strip 2 (pins 26/27), init() runs, calls initialize() which does nothing because mInitialized is already true on the bus manager, but then checks bus->bus_type — and since strip 2 has its own bus (different clock pin), it correctly sees SINGLE_SPI and creates mSingleSPI too.
- So that path actually should work. Let me check whether getSPIBusManager() returns a shared singleton or a per-instance object.
- Ran 4 commands
- Ran 4 commands
- Now I see another issue — HD108 calls mSPI.endTransaction() at the end but SPIDeviceProxy has endTransaction() as an alias for release(). That's fine. But the real clue is the SPI_MODE. Look at this comment at the top of hd108.h:
- // SPI mode: MODE3 recommended (CPOL=1, CPHA=1)
- But Teensy4HardwareSPIOutput::select() hardcodes SPI_MODE0:
- cpp
- _SPIObject.beginTransaction(SPISettings(_SPI_CLOCK_RATE, MSBFIRST, SPI_MODE0));
- That's one issue but let's first confirm whether init() is even being reached for the second strip. The real problem may be simpler — let me check if finalizeTransmission() is being called, since HD108Controller calls mSPI.endTransaction() but never calls mSPI.finalizeTransmission(), which is required for the proxy to flush buffered writes on multi-lane paths.
- But for SINGLE_SPI it goes direct, so that shouldn't matter. Let me check one more thing — whether the DATA_PIN template parameter type mismatch could be causing a silent failure:
- Check for type mismatch between HD108 int DATA_PIN and SPIOutput fl::u8 template parameter
- Check for type mismatch between HD108 int DATA_PIN and SPIOutput fl::u8 template parameter
- Found it. HD108Controller declares DATA_PIN as int, but SPIOutput specializations use fl::u8. When fl::SPIOutput<DATA_PIN, CLOCK_PIN, SPI_SPEED> is instantiated with DATA_PIN as an int, it matches the primary template (which takes fl::u8 _DATA_PIN) through implicit conversion — but partial specializations on _CLOCK_PIN only match when _DATA_PIN is also fl::u8. Since DATA_PIN is int, the compiler can't match the partial specialization for clock pin 27, and falls through to the primary template — which always uses SPI, 0.
- The fix is in spi_output_template.h. The partial specializations need int for _DATA_PIN to match what HD108 actually passes, or you cast in the specializations. The cleanest fix is to change the primary template and all specializations to use int for _DATA_PIN:
- In D:\Arduino\libraries\FastLED\src\platforms\arm\teensy\teensy4_common\spi_output_template.h, replace the entire file with:
- cpp
- #pragma once
- #include "fl/int.h"
- #include "platforms/arm/mxrt1062/spi_device_proxy.h"
- namespace fl {
- // Primary template: SPI0 (clock pin 13, data pin 11)
- // DATA_PIN is int to match HD108Controller and other chipsets
- template<int _DATA_PIN, fl::u8 _CLOCK_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput : public SPIDeviceProxy<_DATA_PIN, _CLOCK_PIN, _SPI_CLOCK_DIVIDER, SPI, 0> {};
- // SPI1: clock pin 27 (data pin 26)
- template<int _DATA_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput<_DATA_PIN, 27, _SPI_CLOCK_DIVIDER>
- : public SPIDeviceProxy<_DATA_PIN, 27, _SPI_CLOCK_DIVIDER, SPI1, 1> {};
- // SPI2: clock pin 45 (data pin 43)
- template<int _DATA_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput<_DATA_PIN, 45, _SPI_CLOCK_DIVIDER>
- : public SPIDeviceProxy<_DATA_PIN, 45, _SPI_CLOCK_DIVIDER, SPI2, 2> {};
- // SPI2: clock pin 49 (data pin 50, alternate SPI2 pins)
- template<int _DATA_PIN, fl::u32 _SPI_CLOCK_DIVIDER>
- class SPIOutput<_DATA_PIN, 49, _SPI_CLOCK_DIVIDER>
- : public SPIDeviceProxy<_DATA_PIN, 49, _SPI_CLOCK_DIVIDER, SPI2, 2> {};
- } // namespace fl
- The only change is fl::u8 _DATA_PIN → int _DATA_PIN in all four declarations. That makes the partial specialization types match what HD108Controller actually instantiates, so clock pin 27 will correctly resolve to SPI1 instead of falling back to SPI0.
- Then delete the build cache and recompile.
Advertisement