feat: resolve SPI startup glitch by writing data to SPDAT prior to enabling SPEN in SPCTL

This commit is contained in:
2026-07-08 16:49:17 +08:00
parent 2b574bd764
commit 61b6ecb55d

View File

@@ -145,43 +145,38 @@ void WS2812_EncodeByte(u8 v, u8 *buf)
void WS2812_Write24Bit(u8 g, u8 r, u8 b) void WS2812_Write24Bit(u8 g, u8 r, u8 b)
{ {
u8 data buf[10]; u8 data buf[10];
u16 timeout; u8 b0, b1, b2, b3, b4, b5, b6, b7, b8, b9;
buf[0] = 0x00; // Prepend dummy 0x00 to absorb SPI hardware startup delay/glitch buf[0] = 0x00; // Prepend dummy 0x00 to absorb SPI hardware startup delay/glitch
WS2812_EncodeByte(g, &buf[1]); WS2812_EncodeByte(g, &buf[1]);
WS2812_EncodeByte(r, &buf[4]); WS2812_EncodeByte(r, &buf[4]);
WS2812_EncodeByte(b, &buf[7]); WS2812_EncodeByte(b, &buf[7]);
b0 = buf[0]; b1 = buf[1]; b2 = buf[2]; b3 = buf[3];
b4 = buf[4]; b5 = buf[5]; b6 = buf[6]; b7 = buf[7];
b8 = buf[8]; b9 = buf[9];
EA = 0; EA = 0;
// Ensure P2.3 (MOSI) and P2.5 (SCLK) are configured as push-pull output // Ensure P2.3 (MOSI) and P2.5 (SCLK) are configured as push-pull output
P2M1 &= ~((1 << 3) | (1 << 5)); P2M1 &= ~((1 << 3) | (1 << 5));
P2M0 |= ((1 << 3) | (1 << 5)); P2M0 |= ((1 << 3) | (1 << 5));
SPSTAT = 0xC0; // Clear flags SPSTAT = 0xC0; // Clear flags first
SPCTL = 0xD0; // Enable SPI at SYSCLK/4 (3.0 MHz actual clock on board) SPDAT = b0; // Write data first!
SPCTL = 0xD0; // Enable SPI after writing data. Should start shifting b0 immediately!
SPDAT = buf[0]; // Start transmitting dummy 0x00 (absorbs startup delay at MOSI=0) // Unrolled 10-byte transmission with minimal gap (~300ns)
// Wait 30 NOPs to ensure buf[0] has moved to the shifter while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b1;
_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b2;
_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b3;
_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b4;
_nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b5;
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b6;
SPDAT = buf[1]; // Buffer buf[1] (zero gap!) while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b7;
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b8;
// Transmission loop unrolled with double-buffering and timeout protection while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b9;
timeout = 2000; while (!(SPSTAT & 0x80) && --timeout); SPSTAT = 0xC0; SPDAT = buf[2]; while (!(SPSTAT & 0x80)); SPSTAT = 0xC0;
timeout = 2000; while (!(SPSTAT & 0x80) && --timeout); SPSTAT = 0xC0; SPDAT = buf[3];
timeout = 2000; while (!(SPSTAT & 0x80) && --timeout); SPSTAT = 0xC0; SPDAT = buf[4];
timeout = 2000; while (!(SPSTAT & 0x80) && --timeout); SPSTAT = 0xC0; SPDAT = buf[5];
timeout = 2000; while (!(SPSTAT & 0x80) && --timeout); SPSTAT = 0xC0; SPDAT = buf[6];
timeout = 2000; while (!(SPSTAT & 0x80) && --timeout); SPSTAT = 0xC0; SPDAT = buf[7];
timeout = 2000; while (!(SPSTAT & 0x80) && --timeout); SPSTAT = 0xC0; SPDAT = buf[8];
timeout = 2000; while (!(SPSTAT & 0x80) && --timeout); SPSTAT = 0xC0; SPDAT = buf[9];
timeout = 2000; while (!(SPSTAT & 0x80) && --timeout); SPSTAT = 0xC0; // Wait for buf[8] to finish shifting, buf[9] moves to shifter
timeout = 2000; while (!(SPSTAT & 0x80) && --timeout); SPSTAT = 0xC0; // Wait for buf[9] to finish shifting
SPCTL = 0x90; // Disable SPI SPCTL = 0x90; // Disable SPI
WS2812_DI = 0; WS2812_DI = 0;