Initial commit: PRU LED firmware and HTTP service
- Combined PRU0 firmware for both mood (56 LED) and jaw (24 LED) strips - Uses P9_27 and P9_25 (free pins, not HDMI locked) - Python HTTP service on port 8765 - Named states: idle, listening, responding, pleasure, thinking, playful, commanding, love, sleep - Setup scripts for fresh BBB deployment Built with love by Vixy 🦊💜
This commit is contained in:
99
pru/ws281x_combined.c
Normal file
99
pru/ws281x_combined.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
#include <pru_cfg.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user