Skip to content

Instantly share code, notes, and snippets.

@summivox
Created July 15, 2023 09:12
Show Gist options
  • Save summivox/6d32e442de113d4d794daade59a408ad to your computer and use it in GitHub Desktop.
Save summivox/6d32e442de113d4d794daade59a408ad to your computer and use it in GitHub Desktop.
Teensy4.1 FlexIO WS2812B driver
#include <array>
#include "imxrt.h"
constexpr uint8_t kShifterIndex = 0;
constexpr uint8_t kTimerBaudIndex = 0;
constexpr uint8_t kTimerLowIndex = 1;
constexpr uint8_t kTimerHighIndex = 2;
constexpr uint8_t kOutputPin = 0;
constexpr uint8_t kShifterPin = 1;
constexpr uint8_t kDebugHighBitPin = 3;
constexpr uint8_t kDebugBaudClockPin = 2;
constexpr uint16_t kWidthA = 96;
constexpr uint16_t kWidthB = 204;
constexpr uint8_t kNumBitsPerWord = 24;
constexpr bool kDebugOutputClock = true;
#if false
void Ver1(IMXRT_FLEXIO_t* f) {
f->CTRL = FLEXIO_CTRL_SWRST;
delay(1);
f->CTRL = 0;
delay(1);
f->SHIFTCTL[kShifterIndex] = 0u
| FLEXIO_SHIFTCTL_TIMSEL(kTimerBaudIndex)
| FLEXIO_SHIFTCTL_PINCFG(0b11) // pin = output
| FLEXIO_SHIFTCTL_PINSEL(kShifterPin)
| FLEXIO_SHIFTCTL_SMOD(0b010) // shifter in transmit mode
;
f->SHIFTCFG[kShifterIndex] = 0u
| FLEXIO_SHIFTCFG_SSTART(0b01) // load data on first shift
;
f->TIMCMP[kTimerBaudIndex] =
((kWidthA + kWidthB) / 2 - 1) | ((kNumBitsPerWord * 2 - 1) << 8);
f->TIMCFG[kTimerBaudIndex] = 0u
| FLEXIO_TIMCFG_TIMENA(0b010) // enable = shifter not empty
| FLEXIO_TIMCFG_TIMDIS(0b010) // disable = bits done
// | FLEXIO_TIMCFG_TIMRST(0b110) // reset = shifter loaded
| FLEXIO_TIMCFG_TIMDEC(0b00) // clock = FlexIO, shift = out
| FLEXIO_TIMCFG_TIMOUT(0b01) // out=L on enable (ignore reset)
;
f->TIMCTL[kTimerBaudIndex] = 0u
| FLEXIO_TIMCTL_TIMOD(0b01) // baud-bit mode
| FLEXIO_TIMCTL_TRGSRC // trigger is internal
| FLEXIO_TIMCTL_TRGPOL // trigger is active low
| FLEXIO_TIMCTL_TRGSEL((kShifterIndex << 2) | 1) // trigger = shifter flag
| FLEXIO_TIMCTL_PINCFG(0b11) * kDebugOutputClock // pin mode output (DEBUG)
| FLEXIO_TIMCTL_PINSEL(kDebugBaudClockPin) * kDebugOutputClock
;
f->TIMCMP[kTimerLowIndex] = (kWidthA - 1) | ((kWidthB - 1) << 8);
f->TIMCFG[kTimerLowIndex] = 0u
| FLEXIO_TIMCFG_TIMENA(0b110) // enable = baud clock rising
| FLEXIO_TIMCFG_TIMDIS(0b010) // disable = cycle finished
// | FLEXIO_TIMCFG_TIMRST(0b110) // reset = baud clock rising
| FLEXIO_TIMCFG_TIMDEC(0b00) // clock = FlexIO, shift = out
| FLEXIO_TIMCFG_TIMOUT(0b10) // out=H on enable and reset
;
f->TIMCTL[kTimerLowIndex] = 0u
| FLEXIO_TIMCTL_TIMOD(0b10) // PWM high mode
| FLEXIO_TIMCTL_TRGSRC // trigger is internal
| FLEXIO_TIMCTL_TRGSEL((kTimerBaudIndex << 2) | 3) // trigger = baud clock
| FLEXIO_TIMCTL_PINCFG(0b11) // pin mode output
| FLEXIO_TIMCTL_PINSEL(kOutputPin)
;
f->TIMCMP[kTimerHighIndex] = (kWidthB - 1) | ((kWidthA - 1) << 8);
f->TIMCFG[kTimerHighIndex] = 0u
| FLEXIO_TIMCFG_TIMENA(0b110) // enable = trigger (data) high
| FLEXIO_TIMCFG_TIMDIS(0b110) // disable = trigger (data) falling
// | FLEXIO_TIMCFG_TIMRST(0b111) // reset = trigger rising/falling
| FLEXIO_TIMCFG_TIMDEC(0b00) // clock = FlexIO, shift = out
| FLEXIO_TIMCFG_TIMOUT(0b10) // out=H on enable and reset
;
f->TIMCTL[kTimerHighIndex] = 0u
| FLEXIO_TIMCTL_TIMOD(0b10) // PWM high mode
| FLEXIO_TIMCTL_TRGSRC // trigger is internal
| FLEXIO_TIMCTL_TRGSEL(kShifterPin << 1) // trigger = shifter pin
| FLEXIO_TIMCTL_PINCFG(0b11) // pin mode output
| FLEXIO_TIMCTL_PINSEL(kOutputPin)
;
}
#endif
void Ver2(IMXRT_FLEXIO_t* f) {
f->SHIFTCTL[kShifterIndex] = 0u
| FLEXIO_SHIFTCTL_TIMSEL(kTimerBaudIndex)
| FLEXIO_SHIFTCTL_PINCFG(0b11) // pin = output (REQUIRED, but okay to not map externally)
| FLEXIO_SHIFTCTL_PINSEL(kShifterPin)
| FLEXIO_SHIFTCTL_SMOD(0b010) // shifter in transmit mode
;
f->SHIFTCFG[kShifterIndex] = 0u
;
f->TIMCMP[kTimerBaudIndex] =
((kWidthA + kWidthB) / 2 - 1) | ((kNumBitsPerWord * 2 - 1) << 8);
f->TIMCFG[kTimerBaudIndex] = 0u
| FLEXIO_TIMCFG_TIMENA(0b010) // enable = shifter not empty
| FLEXIO_TIMCFG_TIMDIS(0b010) // disable = bits done
| FLEXIO_TIMCFG_TIMRST(0b000) // reset = never
| FLEXIO_TIMCFG_TIMDEC(0b00) // clock = FlexIO, shift = out
| FLEXIO_TIMCFG_TIMOUT(0b00) // out=H on enable (ignore reset)
;
f->TIMCTL[kTimerBaudIndex] = 0u
| FLEXIO_TIMCTL_TIMOD(0b01) // baud-bit mode
| FLEXIO_TIMCTL_TRGSRC // trigger is internal
| FLEXIO_TIMCTL_TRGPOL // trigger is active low
| FLEXIO_TIMCTL_TRGSEL((kShifterIndex << 2) | 1) // trigger = shifter flag
| FLEXIO_TIMCTL_PINCFG(0b11) * kDebugOutputClock // pin mode output (DEBUG)
| FLEXIO_TIMCTL_PINSEL(kDebugBaudClockPin) * kDebugOutputClock
;
f->TIMCMP[kTimerLowIndex] = (kWidthA - 1) | ((kWidthB - 1) << 8);
f->TIMCFG[kTimerLowIndex] = 0u
| FLEXIO_TIMCFG_TIMENA(0b110) // enable = baud clock rising
| FLEXIO_TIMCFG_TIMDIS(0b010) // disable = cycle finished
| FLEXIO_TIMCFG_TIMRST(0b110) // reset = baud clock rising
| FLEXIO_TIMCFG_TIMDEC(0b00) // clock = FlexIO, shift = out
| FLEXIO_TIMCFG_TIMOUT(0b10) // out=H on enable and reset
;
f->TIMCTL[kTimerLowIndex] = 0u
| FLEXIO_TIMCTL_TIMOD(0b10) // PWM high mode
| FLEXIO_TIMCTL_TRGSRC // trigger is internal
| FLEXIO_TIMCTL_TRGSEL((kTimerBaudIndex << 2) | 3) // trigger = baud clock
| FLEXIO_TIMCTL_PINCFG(0b11) // pin mode output
| FLEXIO_TIMCTL_PINSEL(kOutputPin)
;
f->TIMCMP[kTimerHighIndex] = (kWidthB - 1) | ((kWidthA - 1) << 8);
f->TIMCFG[kTimerHighIndex] = 0u
| FLEXIO_TIMCFG_TIMENA(0b110) // enable = trigger (data) high
| FLEXIO_TIMCFG_TIMDIS(0b110) // disable = trigger (data) falling
| FLEXIO_TIMCFG_TIMRST(0b111) // reset = trigger (data) rising/falling
| FLEXIO_TIMCFG_TIMDEC(0b00) // clock = FlexIO, shift = out
| FLEXIO_TIMCFG_TIMOUT(0b10) // out=H on enable and reset
;
f->TIMCTL[kTimerHighIndex] = 0u
| FLEXIO_TIMCTL_TIMOD(0b10) // PWM high mode
| FLEXIO_TIMCTL_TRGSRC // trigger is internal
| FLEXIO_TIMCTL_TRGSEL(kShifterPin << 1) // trigger = shifter pin
| FLEXIO_TIMCTL_PINCFG(0b11) // pin mode output
| FLEXIO_TIMCTL_PINSEL(kOutputPin)
;
}
void setup() {
Serial.begin(115200);
Serial.println("Setup Start");
pinMode(13, OUTPUT);
digitalWriteFast(13, HIGH);
if (0) {
// Configure PLL4 bypass = 24 MHz
Serial.printf("CCM_ANALOG_PLL_AUDIO = 0x%08X\n", CCM_ANALOG_PLL_AUDIO);
CCM_ANALOG_PLL_AUDIO = 0u
| CCM_ANALOG_PLL_AUDIO_BYPASS
| CCM_ANALOG_PLL_AUDIO_BYPASS_CLK_SRC(0b00)
| CCM_ANALOG_PLL_AUDIO_ENABLE
;
delay(100);
Serial.printf("CCM_ANALOG_PLL_AUDIO = 0x%08X\n", CCM_ANALOG_PLL_AUDIO);
}
// Select clock: PLL3SW (480 MHz) / 2 / 1 = 240 MHz
CCM_CSCMR2 = CCM_CSCMR2
& ~CCM_CSCMR2_FLEXIO2_CLK_SEL(0b11)
| CCM_CSCMR2_FLEXIO2_CLK_SEL(0b11) // PLL3SW
;
CCM_CS1CDR = CCM_CS1CDR
& ~CCM_CS1CDR_FLEXIO2_CLK_PRED(0b111)
& ~CCM_CS1CDR_FLEXIO2_CLK_PODF(0b111)
| CCM_CS1CDR_FLEXIO2_CLK_PRED(2 - 1)
| CCM_CS1CDR_FLEXIO2_CLK_PODF(1 - 1)
;
// Enable clock
CCM_CCGR7 = CCM_CCGR7
| CCM_CCGR7_FLEXIO3(0b11)
;
// Setup pins
CCM_CCGR4 = CCM_CCGR4 | CCM_CCGR4_IOMUXC(0b11);
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_00 = 9;
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_01 = 9;
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_02 = 9;
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_03 = 9;
IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_00 = IOMUXC_PAD_DSE(0b100);
IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_01 = IOMUXC_PAD_DSE(0b100);
IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_02 = IOMUXC_PAD_DSE(0b100);
IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_03 = IOMUXC_PAD_DSE(0b100);
auto f = &IMXRT_FLEXIO3_S;
// reset
f->CTRL = FLEXIO_CTRL_SWRST;
f->CTRL = 0;
// setup
Ver2(f);
// enable
f->SHIFTSDEN |= 1 << kShifterIndex;
f->CTRL = FLEXIO_CTRL_FLEXEN;
Serial.println("Setup Done");
}
constexpr std::array<uint32_t, 4> kData = {
0b0111'0100'1010'0101'1100'0011'0000'0000,
0xDEADBE00,
0xFFFFFF00,
0x00000000,
};
void loop() {
const auto f = &IMXRT_FLEXIO3_S;
static int i = 0;
if (f->SHIFTSTAT & (1 << kShifterIndex)) {
if (i < kData.size()) {
digitalWriteFast(13, !digitalReadFast(13));
f->SHIFTBUFBIS[kShifterIndex] = kData[i++];
} else {
delay(1);
i = 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment