// // picostrip.cpp // #include "picostrip.h" #include "ws2812.pio.h" #include "pico/stdlib.h" #include #include // WS2812B bit cell is 1.25us; the PIO program spends 10 cycles per bit. static const float WS2812_FREQ = 800000.0f; // Datasheet reset is >50us of low. 300us is the commonly used safe value and // costs nothing at this frame rate. static const uint64_t WS2812_RESET_US = 300; CPicoLEDStrip::CPicoLEDStrip (unsigned nLEDCount, unsigned nPin) : m_nLEDCount (nLEDCount), m_nPin (nPin), m_pBuffer (nullptr), m_PIO (nullptr), m_nSM (0), m_nOffset (0), m_bInitialized (false), m_nLastFrameUs (0) { } CPicoLEDStrip::~CPicoLEDStrip (void) { free (m_pBuffer); m_pBuffer = nullptr; } bool CPicoLEDStrip::Initialize (void) { m_pBuffer = (uint32_t *) calloc (m_nLEDCount, sizeof (uint32_t)); if (m_pBuffer == nullptr) { return false; } // Let the SDK place the program on whichever PIO block has room, so // this cannot collide with anything else added later. if (!pio_claim_free_sm_and_add_program_for_gpio_range ( &ws2812_program, &m_PIO, &m_nSM, &m_nOffset, m_nPin, 1, true)) { return false; } ws2812_program_init (m_PIO, m_nSM, m_nOffset, m_nPin, WS2812_FREQ); m_bInitialized = true; m_nLastFrameUs = time_us_64 (); return true; } void CPicoLEDStrip::SetLED (unsigned nIndex, uint8_t nRed, uint8_t nGreen, uint8_t nBlue) { if (nIndex >= m_nLEDCount) { return; } // WS2812B wants GRB. The PIO shifts out MSB first with a 24-bit // threshold, so the triple is left-justified in the word. m_pBuffer[nIndex] = ((uint32_t) nGreen << 24) | ((uint32_t) nRed << 16) | ((uint32_t) nBlue << 8); } void CPicoLEDStrip::WaitForLatch (void) { uint64_t nElapsed = time_us_64 () - m_nLastFrameUs; if (nElapsed < WS2812_RESET_US) { busy_wait_us (WS2812_RESET_US - nElapsed); } } bool CPicoLEDStrip::Update (void) { if (!m_bInitialized) { return false; } WaitForLatch (); for (unsigned i = 0; i < m_nLEDCount; i++) { // Blocking push. The FIFO drains at 800kbit/s, so this paces the // loop naturally and needs no DMA at this strip length. pio_sm_put_blocking (m_PIO, m_nSM, m_pBuffer[i]); } m_nLastFrameUs = time_us_64 (); return true; } bool CPicoLEDStrip::Blackout (void) { memset (m_pBuffer, 0, m_nLEDCount * sizeof (uint32_t)); return Update (); }