/* * Combined WS281x LED Driver for PRU0 * Handles BOTH mood strip AND jaw LEDs from PRU0 * * Why combined? All PRU1 native pins (P8_27-46) are locked by HDMI on stock image. * Solution: Use two free PRU0 pins instead. * * Pins (both free and available): * P9_27 = pr1_pru0_pru_r30_5 (Mode 5) - Mood strip (56 LEDs) * P9_25 = pr1_pru0_pru_r30_7 (Mode 5) - Jaw LEDs (24 LEDs) * * Shared Memory Layout (0x00010000): * [0]: mood_num_leds (max 56) * [1]: mood_trigger (write 1 to update) * [2]: jaw_num_leds (max 24) * [3]: jaw_trigger (write 1 to update) * [4-171]: mood LED data (56 * 3 bytes, GRB format) * [172-243]: jaw LED data (24 * 3 bytes, GRB format) */ #include #include #define MOOD_PIN 5 /* P9_27 */ #define JAW_PIN 7 /* P9_25 */ #define MAX_MOOD_LEDS 56 #define MAX_JAW_LEDS 24 /* WS2812 timing at 200MHz (5ns per cycle) */ #define T0H 70 #define T0L 160 #define T1H 140 #define T1L 120 #define SHARED_MEM 0x00010000 #define MOOD_DATA_OFF 4 #define JAW_DATA_OFF 172 volatile uint8_t *shared = (volatile uint8_t *)SHARED_MEM; volatile register uint32_t __R30; static inline void delay_cycles(uint32_t cycles) { while (cycles--) { __asm(" NOP"); } } static void send_byte_on_pin(uint8_t byte, uint8_t pin) { for (int i = 7; i >= 0; i--) { if ((byte >> i) & 1) { __R30 |= (1 << pin); delay_cycles(T1H); __R30 &= ~(1 << pin); delay_cycles(T1L); } else { __R30 |= (1 << pin); delay_cycles(T0H); __R30 &= ~(1 << pin); delay_cycles(T0L); } } } static void send_strip(uint8_t pin, uint8_t *data, uint8_t num_leds) { for (int i = 0; i < num_leds; i++) { send_byte_on_pin(data[i * 3 + 0], pin); /* G */ send_byte_on_pin(data[i * 3 + 1], pin); /* R */ send_byte_on_pin(data[i * 3 + 2], pin); /* B */ } /* Reset pulse */ __R30 &= ~(1 << pin); delay_cycles(10000); } void main(void) { /* Enable OCP master port */ CT_CFG.SYSCFG_bit.STANDBY_INIT = 0; /* Clear output pins */ __R30 &= ~((1 << MOOD_PIN) | (1 << JAW_PIN)); while (1) { /* Check mood strip trigger */ if (shared[1] == 1) { uint8_t num = shared[0]; if (num > MAX_MOOD_LEDS) num = MAX_MOOD_LEDS; send_strip(MOOD_PIN, (uint8_t *)&shared[MOOD_DATA_OFF], num); shared[1] = 0; } /* Check jaw trigger */ if (shared[3] == 1) { uint8_t num = shared[2]; if (num > MAX_JAW_LEDS) num = MAX_JAW_LEDS; send_strip(JAW_PIN, (uint8_t *)&shared[JAW_DATA_OFF], num); shared[3] = 0; } } }