Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

send pre-formatted matrix to pru, rearrange based on config values

  • Loading branch information...
commit 71c60bc1bd831e966e6b3268845161cede98d653 1 parent cd6eb70
@osresearch authored
Showing with 209 additions and 147 deletions.
  1. +5 −10 fire.c
  2. +102 −11 ledscape.c
  3. +3 −9 ledscape.h
  4. +12 −15 matrix-test.c
  5. +87 −102 matrix.p
View
15 fire.c
@@ -76,10 +76,10 @@ hsv2rgb(
static const int w = 256;
-static const int h = 20;
+static const int h = 64;
// This will contain the pixels used to calculate the fire effect
-static uint8_t fire[256][32];
+static uint8_t fire[256][64];
// Flame colors
static uint32_t palette[255];
@@ -181,30 +181,25 @@ init_pallete(void)
int
main(void)
{
- const int num_pixels = 256;
- ledscape_t * const leds = ledscape_init(num_pixels);
+ ledscape_t * const leds = ledscape_init(w,h);
printf("init done\n");
time_t last_time = time(NULL);
unsigned last_i = 0;
unsigned i = 0;
init_pallete();
+ uint32_t * const p = calloc(w*h,4);
while (1)
{
// Alternate frame buffers on each draw command
- const unsigned frame_num = i++ % 2;
- ledscape_frame_t * const frame
- = ledscape_frame(leds, frame_num);
-
- uint32_t * const p = (void*) frame;
time_t now = time(NULL);
const uint32_t delta = now - last_time;
fire_draw(p);
sparkles(p, delta);
- ledscape_draw(leds, frame_num);
+ ledscape_draw(leds, p);
usleep(50000);
// wait for the previous frame to finish;
View
113 ledscape.c
@@ -69,16 +69,34 @@ typedef struct
volatile unsigned response;
} __attribute__((__packed__)) ws281x_command_t;
+typedef struct
+{
+ uint32_t x_offset;
+ uint32_t y_offset;
+} led_matrix_t;
+
+#define NUM_MATRIX 16
+
+typedef struct
+{
+ uint32_t matrix_width; // of a full chain
+ uint32_t matrix_height; // number of rows per-output (8 or 16)
+ led_matrix_t matrix[NUM_MATRIX];
+} led_matrix_config_t;
+
+
struct ledscape
{
ws281x_command_t * ws281x;
pru_t * pru;
- unsigned num_pixels;
- size_t frame_size;
+ unsigned width;;
+ unsigned height;;
+ led_matrix_config_t * matrix;
};
+#if 0
/** Retrieve one of the two frame buffers. */
ledscape_frame_t *
ledscape_frame(
@@ -91,16 +109,59 @@ ledscape_frame(
return (ledscape_frame_t*)((uint8_t*) leds->pru->ddr + leds->frame_size * frame);
}
-
+#endif
+
-/** Initiate the transfer of a frame to the LED strips */
+/** Translate the RGBA buffer to the correct output type and
+ * initiate the transfer of a frame to the LED strips.
+ *
+ * Matrix drivers shuffle to have consecutive bits, ws281x do bit slicing.
+ */
void
ledscape_draw(
ledscape_t * const leds,
- unsigned int frame
+ const void * const buffer
)
{
- leds->ws281x->pixels_dma = leds->pru->ddr_addr + leds->frame_size * frame;
+ unsigned int frame = 0;
+ const uint32_t * const in = buffer;
+ uint8_t * const out = leds->pru->ddr; // + leds->frame_size * frame;
+
+#if 1
+ // matrix packed is:
+ // p(0,0)p(0,8)p(64,0)p(64,8)....
+ // this way the PRU can read all sixteen output pixels in
+ // one LBBO and clock them out.
+ // there is an array of NUM_MATRIX output coordinates (one for each of
+ // the sixteen drivers).
+ for (unsigned i = 0 ; i < NUM_MATRIX ; i++)
+ {
+ const led_matrix_t * const m = &leds->matrix->matrix[i];
+
+ for (uint32_t y = 0 ; y < leds->matrix->matrix_height ; y++)
+ {
+ const uint32_t * const in_row = &in[(y+m->y_offset) * leds->width];
+ uint8_t * const out_row = &out[y * leds->matrix->matrix_width * 3 * NUM_MATRIX];
+
+ for (uint32_t x = 0 ; x < leds->matrix->matrix_width ; x++)
+ {
+ const uint8_t * const rgb = (const void*) &in_row[x + m->x_offset];
+ uint8_t * const out_rgb = &out_row[(i + x * NUM_MATRIX)*3];
+ out_rgb[0] = rgb[0];
+ out_rgb[1] = rgb[1];
+ out_rgb[2] = rgb[2];
+ }
+ }
+ }
+#else
+ static int i = 0;
+ memset(out, i++, leds->width *leds->height * 3);
+ out[0] = 0x80; out[1] = 0x00; out[2] = 0x00;
+ out[3] = 0x00; out[4] = 0x80; out[5] = 0x00;
+ out[6] = 0x00; out[7] = 0x00; out[8] = 0x80;
+#endif
+
+ leds->ws281x->pixels_dma = leds->pru->ddr_addr; // + leds->frame_size * frame;
#if 0
// Wait for any current command to have been acknowledged
while (leds->ws281x->command)
@@ -133,34 +194,64 @@ ledscape_wait(
ledscape_t *
ledscape_init(
- unsigned num_pixels
+ unsigned width,
+ unsigned height
)
{
pru_t * const pru = pru_init(0);
- const size_t frame_size = num_pixels * LEDSCAPE_NUM_STRIPS * 4;
+#if 0
+ const size_t frame_size = num_pixels * 16 * 3; //LEDSCAPE_NUM_STRIPS * 4;
if (2 *frame_size > pru->ddr_size)
die("Pixel data needs at least 2 * %zu, only %zu in DDR\n",
frame_size,
pru->ddr_size
);
+#endif
ledscape_t * const leds = calloc(1, sizeof(*leds));
*leds = (ledscape_t) {
.pru = pru,
- .num_pixels = num_pixels,
- .frame_size = frame_size,
+ .width = width,
+ .height = height,
.ws281x = pru->data_ram,
+ .matrix = calloc(sizeof(*leds->matrix), 1),
+ };
+
+ *(leds->matrix) = (led_matrix_config_t) {
+ .matrix_width = 128,
+ .matrix_height = 8,
+ .matrix = {
+ { 0, 0 },
+ { 0, 8 },
+ { 0, 16 },
+ { 0, 24 },
+ { 0, 32 },
+ { 0, 40 },
+ { 0, 48 },
+ { 0, 56 },
+ { 128, 0 },
+ { 128, 8 },
+ { 128, 16 },
+ { 128, 24 },
+ { 128, 32 },
+ { 128, 40 },
+ { 128, 48 },
+ { 128, 56 },
+ },
};
*(leds->ws281x) = (ws281x_command_t) {
.pixels_dma = 0, // will be set in draw routine
+ .num_pixels = (leds->matrix->matrix_width * 3) * 16,
.command = 0,
.response = 0,
- .num_pixels = leds->num_pixels,
};
+ printf("%d\n", leds->ws281x->num_pixels);
+
+
// Configure all of our output pins.
for (unsigned i = 0 ; i < ARRAY_COUNT(gpios0) ; i++)
pru_gpio(0, gpios0[i], 1, 0);
View
12 ledscape.h
@@ -47,21 +47,15 @@ typedef struct ledscape ledscape_t;
extern ledscape_t *
ledscape_init(
- unsigned num_pixels
-);
-
-
-extern ledscape_frame_t *
-ledscape_frame(
- ledscape_t * const leds,
- unsigned frame
+ unsigned width,
+ unsigned height
);
extern void
ledscape_draw(
ledscape_t * const leds,
- unsigned frame
+ const void * const rgb // 4-byte rgb data
);
View
27 matrix-test.c
@@ -91,18 +91,19 @@ static uint32_t rainbowColors[180];
static void
rainbow(
uint32_t * const pixels,
- unsigned num_leds,
+ unsigned width,
+ unsigned height,
unsigned phaseShift,
unsigned cycle
)
{
const unsigned color = cycle % 180;
- for (unsigned x=0; x < num_leds; x++) {
- for (unsigned y=0; y < 16; y++) {
+ for (unsigned x=0; x < width; x++) {
+ for (unsigned y=0; y < height; y++) {
const int index = (color + x + y*phaseShift/2) % 180;
const uint32_t in = rainbowColors[index];
- uint8_t * const out = &pixels[x + y*num_leds];
+ uint8_t * const out = &pixels[x + y*width];
#if 1
out[0] = ((in >> 0) & 0xFF); // * y / 16;
out[1] = ((in >> 8) & 0xFF); // * y / 16;
@@ -123,8 +124,9 @@ rainbow(
int
main(void)
{
- const int num_pixels = 256;
- ledscape_t * const leds = ledscape_init(num_pixels);
+ const int width = 256;
+ const int height = 64;
+ ledscape_t * const leds = ledscape_init(width, height);
printf("init done\n");
time_t last_time = time(NULL);
unsigned last_i = 0;
@@ -139,17 +141,12 @@ main(void)
}
unsigned i = 0;
+ uint32_t * const p = calloc(width*height,4);
+
while (1)
{
- // Alternate frame buffers on each draw command
- const unsigned frame_num = i++ % 2;
- ledscape_frame_t * const frame
- = ledscape_frame(leds, frame_num);
-
- uint32_t * const p = (void*) frame;
-
- rainbow(p, num_pixels, 10, i);
- ledscape_draw(leds, frame_num);
+ rainbow(p, width, height, 10, i++);
+ ledscape_draw(leds, p);
usleep(20000);
// wait for the previous frame to finish;
View
189 matrix.p
@@ -170,35 +170,26 @@
/** Register map */
#define data_addr r0
-#define row_skip_bytes r1
-#define gpio0_base r2
-#define gpio1_base r3
-#define gpio2_base r4
-#define gpio3_base r9
-#define row r5
-#define offset r6
-#define scan r7
-#define display_width_bytes r8
-#define out_clr r10 // must be one less than out_set
-#define out_set r11
-#define p2 r12
-#define bright r13
-#define gpio0_led_mask r14
-#define gpio1_led_mask r27
-#define gpio2_led_mask r15
-#define gpio3_led_mask r28
-#define gpio1_sel_mask r16
-#define pixel r17
-#define clock_pin r18
-#define latch_pin r19
-#define row11_ptr r20
-#define row12_ptr r21
-#define row21_ptr r22
-#define row22_ptr r23
-#define gpio0_set r24
-#define gpio1_set r25
-#define gpio2_set r26
-#define gpio3_set r29
+#define width r1
+#define row r2
+#define bright r3
+#define offset r4
+#define out_clr r5 // must be one less than out_set
+#define out_set r6
+#define gpio0_set r6 // overloaded with out_set
+#define gpio1_set r7
+#define gpio2_set r8
+#define gpio3_set r9
+#define gpio0_led_mask r10
+#define gpio1_led_mask r11
+#define gpio2_led_mask r13
+#define gpio3_led_mask r14
+#define clock_pin r15
+#define gpio0_base r16
+#define gpio1_base r17
+#define gpio2_base r18
+#define gpio3_base r19
+#define pixel_data r20 // the next 12 registers, too
#define BRIGHT_STEP 32
@@ -209,10 +200,12 @@
SBBO clock_pin, gpio1_base, GPIO_CLRDATAOUT, 4; \
#define LATCH_HI \
- SBBO latch_pin, gpio1_base, GPIO_SETDATAOUT, 4; \
+ MOV out_set, 1 << gpio1_latch; \
+ SBBO out_set, gpio1_base, GPIO_SETDATAOUT, 4; \
#define LATCH_LO \
- SBBO latch_pin, gpio1_base, GPIO_CLRDATAOUT, 4; \
+ MOV out_clr, 1 << gpio1_latch; \
+ SBBO out_clr, gpio1_base, GPIO_CLRDATAOUT, 4; \
#define DISPLAY_OFF \
MOV out_set, 0; \
@@ -257,10 +250,6 @@ START:
// handles the exit case if an invalid value is written to the start
// start position.
-#define DISPLAY_WIDTH 128
-#define DISPLAYS 2
-#define ROW_WIDTH (DISPLAYS * DISPLAY_WIDTH)
-
MOV bright, #0
MOV gpio0_base, GPIO0
@@ -268,8 +257,6 @@ START:
MOV gpio2_base, GPIO2
MOV gpio3_base, GPIO3
- MOV gpio1_sel_mask, GPIO1_SEL_MASK
-
MOV gpio0_led_mask, 0
MOV gpio1_led_mask, 0
MOV gpio2_led_mask, 0
@@ -332,19 +319,12 @@ START:
SET GPIO_MASK(g82_gpio), g82_pin
SET GPIO_MASK(b82_gpio), b82_pin
- MOV display_width_bytes, 4*DISPLAY_WIDTH
- MOV row_skip_bytes, 4*8*ROW_WIDTH
-
- MOV clock_pin, 0
- MOV latch_pin, 0
- SET clock_pin, gpio1_clock
- SET latch_pin, gpio1_latch
+ MOV clock_pin, 1 << gpio1_clock
PWM_LOOP:
// Load the pointer to the buffer from PRU DRAM into r0 and the
- // length (in bytes-bit words) into r1.
- // start command into r2
- LBCO data_addr, CONST_PRUDRAM, 0, 4
+ // length (in pixels) into r1.
+ LBCO data_addr, CONST_PRUDRAM, 0, 8
// Wait for a non-zero command
QBEQ PWM_LOOP, data_addr, #0
@@ -352,65 +332,69 @@ PWM_LOOP:
// Command of 0xFF is the signal to exit
QBEQ EXIT, data_addr, #0xFF
- MOV offset, 0
- MOV row, 0
+ // scale the width into number of bytes that we will read
+ // 16 outputs * 3 bytes per output
+/********
+ ADD offset, width, width
+ ADD offset, offset, width
+ LSL width, offset, 4
+*/
- // Store the pointers to each of the four outputs
- ADD row11_ptr, data_addr, 0
- ADD row12_ptr, row11_ptr, row_skip_bytes
- ADD row21_ptr, row11_ptr, display_width_bytes
- ADD row22_ptr, row21_ptr, row_skip_bytes
+ MOV row, 0
ROW_LOOP:
- // compute where we are in the image
- MOV pixel, 0
+ MOV offset, 0
+ // compute where we are in the image
PIXEL_LOOP:
- MOV out_set, 0
CLOCK_HI
+ // Load the sixteen RGB outputs into
+ // consecutive registers, starting at pixel_data.
+ // This takes about 250 ns
+ LBBO pixel_data, data_addr, offset, 3*16
+ MOV gpio0_set, 0
+ MOV gpio1_set, 0
+ MOV gpio2_set, 0
+ MOV gpio3_set, 0
#define GPIO(R) CAT3(gpio,R,_set)
- MOV gpio0_set, 0
- MOV gpio1_set, 0
- MOV gpio2_set, 0
- MOV gpio3_set, 0
-
-#define OUTPUT_ROW(N) \
- LBBO p2, row##N##_ptr, offset, 4; \
- QBGE skip_r##N, p2.b0, bright; \
+#define OUTPUT_ROW(N,reg_r,reg_g,reg_b) \
+ QBGE skip_r##N, reg_r, bright; \
SET GPIO(r##N##_gpio), r##N##_pin; \
skip_r##N: \
- QBGE skip_g##N, p2.b1, bright; \
+ QBGE skip_g##N, reg_g, bright; \
SET GPIO(g##N##_gpio), g##N##_pin; \
skip_g##N: \
- QBGE skip_b##N, p2.b2, bright; \
+ QBGE skip_b##N, reg_b, bright; \
SET GPIO(b##N##_gpio), b##N##_pin; \
skip_b##N: \
-#define OUTPUT_ROW2(P,N) \
- LBBO p2, row##P##_ptr, offset, 4; \
- QBGE skip_r##N, p2.b0, bright; \
- SET GPIO(r##N##_gpio), r##N##_pin; \
- skip_r##N: \
- QBGE skip_g##N, p2.b1, bright; \
- SET GPIO(g##N##_gpio), g##N##_pin; \
- skip_g##N: \
- QBGE skip_b##N, p2.b2, bright; \
- SET GPIO(b##N##_gpio), b##N##_pin; \
- skip_b##N: \
+ OUTPUT_ROW(11, r20.b0, r20.b1, r20.b2)
+ OUTPUT_ROW(12, r20.b3, r21.b0, r21.b1)
+ OUTPUT_ROW(21, r21.b2, r21.b3, r22.b0)
+ OUTPUT_ROW(22, r22.b1, r22.b2, r22.b3)
+
+ OUTPUT_ROW(31, r23.b0, r23.b1, r23.b2)
+ OUTPUT_ROW(32, r23.b3, r24.b0, r24.b1)
+ OUTPUT_ROW(41, r24.b2, r24.b3, r25.b0)
+ OUTPUT_ROW(42, r25.b1, r25.b2, r25.b3)
+
+ OUTPUT_ROW(51, r26.b0, r26.b1, r26.b2)
+ OUTPUT_ROW(52, r26.b3, r27.b0, r27.b1)
+ OUTPUT_ROW(61, r27.b2, r27.b3, r28.b0)
+ OUTPUT_ROW(62, r28.b1, r28.b2, r28.b3)
- OUTPUT_ROW(11)
- OUTPUT_ROW(12)
- OUTPUT_ROW(21)
- OUTPUT_ROW(22)
- OUTPUT_ROW2(11,81)
- OUTPUT_ROW2(12,82)
+ OUTPUT_ROW(71, r29.b0, r29.b1, r29.b2)
+ OUTPUT_ROW(72, r29.b3, r30.b0, r30.b1)
+ OUTPUT_ROW(81, r30.b2, r30.b3, r31.b0)
+ OUTPUT_ROW(82, r31.b1, r31.b2, r31.b3)
// All bits are configured;
// the non-set ones will be cleared
// We write 8 bytes since CLR and DATA are contiguous,
// which will write both the 0 and 1 bits in the
- // same instruction.
+ // same instruction. gpio0 and out_set are the same
+ // register, so they must be done first.
AND out_set, gpio0_set, gpio0_led_mask
XOR out_clr, out_set, gpio0_led_mask
SBBO out_clr, gpio0_base, GPIO_CLRDATAOUT, 8
@@ -435,33 +419,32 @@ PWM_LOOP:
// \todo: Test turning OE on and off every other,
// every fourth, every eigth, etc pixel based on
// the current brightness.
-#if 1
- LSL p2, pixel, 1
+#if 0
+ LSL out_sel, offset, 1
QBLT no_blank, bright, p2
DISPLAY_OFF
no_blank:
#endif
- ADD offset, offset, 4
- ADD pixel, pixel, 1
-#if DISPLAY_WIDTH > 0xFF
- MOV p2, DISPLAY_WIDTH
- QBNE PIXEL_LOOP, pixel, p2
-#else
- QBNE PIXEL_LOOP, pixel, DISPLAY_WIDTH
-#endif
+ ADD offset, offset, 3*16
+ QBNE PIXEL_LOOP, offset, width
// Disable output before we latch and set the address
// Unless we've just done a full image, in which case
// we treat this as a dummy row and go back to the top
DISPLAY_OFF
+#ifdef fix_dim
QBEQ NEXT_ROW, row, 8
+#endif
LATCH_HI
// set address; select pins in gpio1 are sequential
+ // xor with the select bit mask to set which ones should
LSL out_set, row, gpio1_sel0
- XOR out_clr, out_set, gpio1_sel_mask
+ MOV out_clr, GPIO1_SEL_MASK
+ AND out_set, out_set, out_clr // ensure no extra bits
+ XOR out_clr, out_clr, out_set // complement the bits into clr
SBBO out_clr, gpio1_base, GPIO_CLRDATAOUT, 8 // set both
// We have clocked out all of the pixels for
@@ -470,15 +453,17 @@ PWM_LOOP:
LATCH_LO
DISPLAY_ON
- // We have drawn half the image on each chain;
- // skip the second half
- ADD offset, offset, display_width_bytes
-
ADD row, row, 1
+ QBEQ LAST_ROW, row, 8
+
+ // Before going to the next row, increment our data_offset
+ // to the next row and reset our offset
+ ADD data_addr, data_addr, offset
+ MOV offset, 0
QBA ROW_LOOP
-NEXT_ROW:
- // We have clocked out all of the panels.
+LAST_ROW:
+ // We have clocked out this row to all of the panels.
// Celebrate and go back to the PWM loop
// Limit brightness to 0..MAX_BRIGHT
ADD bright, bright, BRIGHT_STEP
Please sign in to comment.
Something went wrong with that request. Please try again.