Created
July 15, 2023 09:12
-
-
Save summivox/6d32e442de113d4d794daade59a408ad to your computer and use it in GitHub Desktop.
Teensy4.1 FlexIO WS2812B driver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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