Galactic Unicorn: Examples + features.

pull/537/head
jon 2022-07-25 11:24:00 +01:00 zatwierdzone przez Phil Howard
rodzic 5fdd08438f
commit eaaed2e862
9 zmienionych plików z 580 dodań i 387 usunięć

Wyświetl plik

@ -4,7 +4,8 @@ add_executable(
)
# Pull in pico libraries that we need
target_link_libraries(galactic_unicorn_demo pico_stdlib hardware_pio hardware_dma pico_graphics galactic_unicorn)
target_link_libraries(galactic_unicorn_demo pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
pico_enable_stdio_usb(galactic_unicorn_demo 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(galactic_unicorn_demo)
@ -17,7 +18,108 @@ add_executable(
)
# Pull in pico libraries that we need
target_link_libraries(galactic_unicorn_demo2 pico_stdlib hardware_pio hardware_dma pico_graphics galactic_unicorn)
target_link_libraries(galactic_unicorn_demo2 pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
pico_enable_stdio_usb(galactic_unicorn_demo2 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(galactic_unicorn_demo2)
add_executable(
nostalgia_prompt
nostalgia_prompt.cpp
)
# Pull in pico libraries that we need
target_link_libraries(nostalgia_prompt pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
pico_enable_stdio_usb(nostalgia_prompt 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(nostalgia_prompt)
add_executable(
eighties_super_computer
eighties_super_computer.cpp
)
# Pull in pico libraries that we need
target_link_libraries(eighties_super_computer pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
pico_enable_stdio_usb(eighties_super_computer 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(eighties_super_computer)
add_executable(
fire_effect
fire_effect.cpp
)
# Pull in pico libraries that we need
target_link_libraries(fire_effect pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
pico_enable_stdio_usb(fire_effect 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(fire_effect)
add_executable(
scroll_text
scroll_text.cpp
)
# Pull in pico libraries that we need
target_link_libraries(scroll_text pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
pico_enable_stdio_usb(scroll_text 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(scroll_text)
add_executable(
balls
balls.cpp
)
# Pull in pico libraries that we need
target_link_libraries(balls pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
pico_enable_stdio_usb(balls 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(balls)
add_executable(
lava_lamp
lava_lamp.cpp
)
# Pull in pico libraries that we need
target_link_libraries(lava_lamp pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
pico_enable_stdio_usb(lava_lamp 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(lava_lamp)
add_executable(
feature_test
feature_test.cpp
audio_samples.cpp
)
# Pull in pico libraries that we need
target_link_libraries(feature_test pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
pico_enable_stdio_usb(feature_test 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(feature_test)

Wyświetl plik

@ -114,6 +114,9 @@ gpio_set_function(28, GPIO_FUNC_SIO);
float brightness = 0.5f;
float curve = 4.0f;
int x = 10;
int y = 5;
while(true) {
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_VOLUME_UP)) {
hue_offset += 0.05;
@ -143,6 +146,27 @@ gpio_set_function(28, GPIO_FUNC_SIO);
if(curve < 0.5) curve = 0.5;
}
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_A)) {
x -= 1;
sleep_ms(250);
}
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_B)) {
x += 1;
sleep_ms(250);
}
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_C)) {
y -= 1;
sleep_ms(250);
}
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_D)) {
y += 1;
sleep_ms(250);
}
/*
for(int y = 0; y < 11; y++) {
for(int x = 0; x < 53; x++) {
float fx = x / 53.0f;
@ -150,27 +174,18 @@ gpio_set_function(28, GPIO_FUNC_SIO);
float twopi = M_PI * 2;
float hue = fy + (sin(fx * twopi / curve));
float fade = 1.0f;
/* if(hue < 0.0f) {
fade += (hue * 10.0f);
if(fade < 0.0f) fade = 0.0f;
}
if(hue > 1.0f) {
fade -= ((hue - 1.0f) * 10.0f);
if(fade < 0.0f) fade = 0.0f;
}*/
hue += hue_offset;
while(hue < 0.0f) {hue += 1.0f;}
while(hue > 1.0f) {hue -= 1.0f;}
hue = 1.0f - hue;
/*
uint8_t r = 0, g = 0, b = 0;
from_hsv(hue, 1.0f, brightness * fade, r, g, b);*/
ok_color::HSL hsl{.h = hue, .s = 1.0f, .l = brightness * fade};
ok_color::RGB rgb = ok_color::okhsl_to_srgb(hsl);
galactic_unicorn.set_pixel(x, y, rgb.r * 255.0f, rgb.g * 255.0f, rgb.b * 255.0f);
}
}
}*/
galactic_unicorn.set_pixel(x, y, 255, 255, 255);
/*i++;
graphics.set_pen(0, 0, 0);

Wyświetl plik

@ -37,9 +37,9 @@ void text(std::string t, Point p, float s = 1.0f, float a = 1.0f) {
p.x += (53 / 2) - (w / 2);
p.y += (11 / 2);
graphics.text(t, Point(p.x, p.y), -1, s, a);
graphics.text(t, Point(p.x + 1, p.y), -1, s, a);
graphics.text(t, Point(p.x + 1, p.y + 1), -1, s, a);
graphics.text(t, Point(p.x, p.y + 1), -1, s, a);
//graphics.text(t, Point(p.x + 1, p.y), -1, s, a);
//graphics.text(t, Point(p.x + 1, p.y + 1), -1, s, a);
//graphics.text(t, Point(p.x, p.y + 1), -1, s, a);
}
struct star_t {
@ -73,6 +73,8 @@ void step_star(star_t &s) {
int main() {
stdio_init_all();
uint8_t hue_map[53][3];
for(int i = 0; i < 53; i++) {
from_hsv(i / 53.0f, 1.0f, 1.0f, hue_map[i][0], hue_map[i][1], hue_map[i][2]);
@ -103,18 +105,17 @@ gpio_set_function(28, GPIO_FUNC_SIO);
bool x_pressed = false;
bool y_pressed = false;
*/
graphics.set_font("sans");
graphics.set_font("bitmap8");
uint i = 0;
int v = 255;
float hue_offset = 0.0f;
float brightness = 0.5f;
float curve = 4.0f;
while(true) {
i++;
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_VOLUME_UP)) {
hue_offset += 0.05;
if(hue_offset > 1.0f) hue_offset = 1.0f;
@ -125,69 +126,37 @@ gpio_set_function(28, GPIO_FUNC_SIO);
}
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_UP)) {
brightness += 0.05;
if(brightness > 1.0f) brightness = 1.0f;
galactic_unicorn.adjust_brightness(+0.01);
}
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
brightness -= 0.05;
if(brightness < 0.0f) brightness = 0.0f;
galactic_unicorn.adjust_brightness(-0.01);
}
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_A)) {
curve += 0.5;
if(curve > 100.0f) curve = 100.0f;
}
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_B)) {
curve -= 0.5;
if(curve < 0.5) curve = 0.5;
}
i++;
graphics.set_pen(0, 0, 0);
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_A)) {graphics.set_pen(255, 0, 0);}
graphics.clear();
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_DOWN)) {v = v == 0 ? 0 : v - 1;}
for(int i = 0; i < 100; i++) {
star_t &star = stars[i];
step_star(star);
uint b = star.brightness();
graphics.set_pen(b, b, b);
//graphics.pixel(Point(star.x + (53 / 2), star.y + (11 / 2)));
}
graphics.set_pen(255, 255, 255);
float s = 0.8f;//0.65f + (sin(i / 25.0f) * 0.15f);
/*
graphics.set_pen(255, 255, 255);
float s = 0.65f;//0.65f + (sin(i / 25.0f) * 0.15f);
float a = 1.0f;// (sin(i / 25.0f) * 100.0f);
float x = (sin(i / 25.0f) * 40.0f) * s;
float y = (cos(i / 15.0f) * 10.0f) * s;
text("Galactic", Point(x, y), s, a);
uint16_t *p = (uint16_t *)graphics.frame_buffer;
for(size_t i = 0; i < 53 * 11; i++) {
int x = i % 53;
int y = i / 53;
uint r = ((*p & 0b1111100000000000) >> 11) << 3;
uint g = ((*p & 0b0000011111100000) >> 5) << 2;
uint b = ((*p & 0b0000000000011111) >> 0) << 3;
p++;
float x = (sin(i / 74.0f) * 80.0f) * s;
float y = (cos(i / 43.0f) * 6.0f) * s;
text("Chester smells!", Point(x, y - 3), s, a);
*/
if(r > 200 && g > 200 && b > 200) {
r = hue_map[x][0];
g = hue_map[x][1];
b = hue_map[x][2];
for(int x = 0; x < 53; x++) {
for(int y = 0; y < 11; y++) {
int v = ((sin((x + y) / 3.0f + i / 15.0f) + 1.5f) / 2.5f) * 255.0f;
graphics.set_pen(v, v, v);
graphics.pixel(Point(x, y));
}
galactic_unicorn.set_pixel(x, y, r, g, b);
}
galactic_unicorn.update(graphics);
sleep_ms(10);
printf("%d\n", galactic_unicorn.light());
sleep_ms(20);
}

Wyświetl plik

@ -1,12 +1 @@
add_library(galactic_unicorn INTERFACE)
pico_generate_pio_header(galactic_unicorn ${CMAKE_CURRENT_LIST_DIR}/galactic_unicorn.pio)
target_sources(galactic_unicorn INTERFACE
${CMAKE_CURRENT_LIST_DIR}/galactic_unicorn.cpp
)
target_include_directories(galactic_unicorn INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(galactic_unicorn INTERFACE pico_stdlib hardware_pio hardware_dma)
include(galactic_unicorn.cmake)

Wyświetl plik

@ -17,6 +17,7 @@ We've included helper functions to handle every aspect of drawing to the display
- [init](#init)
- [set_pixel](#set_pixel)
- [is_pressed](#is_pressed)
- [Examples](#examples)
## Example Program
@ -98,4 +99,16 @@ The button vaule should be a `uint8_t` denoting a pin, and constants `A`, `B`, `
```c++
bool is_a_button_pressed = pico_unicorn.is_pressed(PicoUnicorn::A)
```
```
# Examples
## Game of Life
## Retro Super-computer
Random LEDs blink on and off mimicing the look of a movie super computer doing it's work in the eighties.
## Nostalgia Terminal
A collection of copies of classic terminal styles including C64, MS-DOS, Spectrum, and more.

Wyświetl plik

@ -1,12 +1,14 @@
add_library(pico_unicorn INTERFACE)
add_library(galactic_unicorn INTERFACE)
pico_generate_pio_header(pico_unicorn ${CMAKE_CURRENT_LIST_DIR}/pico_unicorn.pio)
pico_generate_pio_header(galactic_unicorn ${CMAKE_CURRENT_LIST_DIR}/galactic_unicorn.pio)
pico_generate_pio_header(galactic_unicorn ${CMAKE_CURRENT_LIST_DIR}/audio_i2s.pio)
target_sources(pico_unicorn INTERFACE
${CMAKE_CURRENT_LIST_DIR}/pico_unicorn.cpp
target_sources(galactic_unicorn INTERFACE
${CMAKE_CURRENT_LIST_DIR}/galactic_unicorn.cpp
)
target_include_directories(pico_unicorn INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_include_directories(galactic_unicorn INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(pico_unicorn INTERFACE pico_stdlib hardware_pio hardware_dma)
target_link_libraries(galactic_unicorn INTERFACE pico_stdlib pico_graphics hardware_adc hardware_pio hardware_dma)

Wyświetl plik

@ -2,88 +2,47 @@
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "hardware/adc.h"
#include "hardware/clocks.h"
#include "galactic_unicorn.pio.h"
#include "audio_i2s.pio.h"
#include "galactic_unicorn.hpp"
// pixel data is stored as a stream of bits delivered in the
// order the PIO needs to manage the shift registers, row
// selects, delays, and latching/blanking
//
// the data consists of 11 rows each of which has 14 frames of
// bcd timing data
// the pins used are:
//
// bits are output in order:
// - 13: column clock (sideset)
// - 14: column data (out base)
// - 15: column latch
// - 16: column blank
// - 17: row select bit 0
// - 18: row select bit 1
// - 19: row select bit 2
// - 20: row select bit 3
//
// ROW_CLEAR, ROW_DATA1, ROW_DATA0, LED_BLANK, LED_LATCH, LED_CLOCK, LED_DATA0, LED_DATA1
// the framebuffer data is structured like this:
//
// the data is structured like this:
//
// loop through the eleven rows of the display...
//
// 1rr00000 // set row select bit on rows 0 and 8 (side set the clock)
// 00000000 00000000 00000000 // dummy bytes to align to dwords
//
// within this row we loop through the 14 bcd frames for this row...
//
// 0 - 161: 100100rr, 100101rr, 100100gg, 100101gg, 100100bb, 100101bb, ... x 27 # left+right half rgb pixel data doubled for clock pulses, keep BLANK high
// 162: 10011000 // LATCH pixel data
// 163: 10000000 // turn off BLANK to output pixel data - now at 164 bytes (41 dwords)
// 164 - 165: 00001111, 11111111, # bcd tick count (0-65536)
// 166: 10010000 // turn BLANK back on
// 167: 00000000 // dummy byte to ensure dword aligned
// for each row:
// for each bcd frame:
// 0: 00110110 // row pixel count (minus one)
// 1 - 53: xxxxxbgr, xxxxxbgr, xxxxxbgr, ... // pixel data
// 54 - 55: xxxxxxxx, xxxxxxxx // dummy bytes to dword align
// 56: xxxxrrrr // row select bits
// 57 - 59: tttttttt, tttttttt, tttttttt // bcd tick count (0-65536)
//
// .. and back to the start
/*
enum pin {
LED_DATA = 8,
LED_CLOCK = 9,
LED_LATCH = 10,
LED_BLANK = 11,
ROW_0 = 22,
ROW_1 = 21,
ROW_2 = 20,
ROW_3 = 19,
ROW_4 = 18,
ROW_5 = 17,
ROW_6 = 16,
A = 12,
B = 13,
X = 14,
Y = 15,
};*/
enum pin {
LED_DATA1 = 12,
LED_DATA0 = 13,
LED_CLOCK = 14,
LED_LATCH = 15,
LED_BLANK = 16,
ROW_DATA0 = 17,
ROW_DATA1 = 18,
ROW_CLEAR = 19,
ROW_CLOCK = 20,
SWITCH_A = 0,
SWITCH_B = 1,
SWITCH_C = 3,
SWITCH_D = 6,
SWITCH_E = 2,
SWITCH_VOLUME_UP = 21,
SWITCH_VOLUME_DOWN = 26,
SWITCH_BRIGHTNESS_UP = 7,
SWITCH_BRIGHTNESS_DOWN = 8
};
constexpr uint32_t ROW_COUNT = 11;
constexpr uint32_t ROW_BYTES = 4;
constexpr uint32_t ROW_FRAME_BYTES = 168;
constexpr uint32_t BCD_FRAMES = 15; // includes fet discharge frame
constexpr uint32_t BITSTREAM_LENGTH = (ROW_COUNT * ROW_BYTES + ROW_COUNT * ROW_FRAME_BYTES * BCD_FRAMES);
constexpr uint32_t BCD_FRAME_COUNT = 14;
constexpr uint32_t BCD_FRAME_BYTES = 60;
constexpr uint32_t ROW_BYTES = BCD_FRAME_COUNT * BCD_FRAME_BYTES;
constexpr uint32_t BITSTREAM_LENGTH = (ROW_COUNT * ROW_BYTES);
// must be aligned for 32bit dma transfer
alignas(4) static uint8_t bitstream[BITSTREAM_LENGTH] = {0};
@ -93,38 +52,7 @@ static uint16_t g_gamma_lut[256] = {0};
static uint16_t b_gamma_lut[256] = {0};
static uint32_t dma_channel;
static inline void unicorn_jetpack_program_init(PIO pio, uint sm, uint offset) {
pio_gpio_init(pio, pin::LED_DATA1);
pio_gpio_init(pio, pin::LED_DATA0);
pio_gpio_init(pio, pin::LED_CLOCK);
pio_gpio_init(pio, pin::LED_LATCH);
pio_gpio_init(pio, pin::LED_BLANK);
pio_gpio_init(pio, pin::ROW_DATA0);
pio_gpio_init(pio, pin::ROW_DATA1);
pio_gpio_init(pio, pin::ROW_CLEAR);
pio_gpio_init(pio, pin::ROW_CLOCK);
pio_sm_set_consecutive_pindirs(pio, sm, pin::LED_DATA1, 5, true);
pio_sm_set_consecutive_pindirs(pio, sm, pin::ROW_DATA0, 4, true);
pio_sm_config c = galactic_unicorn_program_get_default_config(offset);
// osr shifts right, autopull on, autopull threshold 8
sm_config_set_out_shift(&c, true, false, 32);
// configure out, set, and sideset pins
sm_config_set_out_pins(&c, pin::LED_DATA1, 8);
sm_config_set_sideset_pins(&c, pin::ROW_CLOCK);
// join fifos as only tx needed (gives 8 deep fifo instead of 4)
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
///pio_sm_set_clkdiv(pio, sm, 4.0f);
}
static uint32_t audio_dma_channel;
namespace pimoroni {
@ -154,118 +82,124 @@ namespace pimoroni {
pio_sm_restart(bitstream_pio, bitstream_sm);
}
uint16_t GalacticUnicorn::light() {
adc_select_input(2);
return adc_read();
}
void GalacticUnicorn::init() {
// todo: shouldn't need to do this if things were cleaned up properly but without
// this any attempt to run a micropython script twice will fail
static bool already_init = false;
// setup pins
gpio_init(pin::LED_DATA0); gpio_set_dir(pin::LED_DATA0, GPIO_OUT);
gpio_init(pin::LED_DATA1); gpio_set_dir(pin::LED_DATA1, GPIO_OUT);
gpio_init(pin::LED_CLOCK); gpio_set_dir(pin::LED_CLOCK, GPIO_OUT);
gpio_init(pin::LED_LATCH); gpio_set_dir(pin::LED_LATCH, GPIO_OUT);
gpio_init(pin::LED_BLANK); gpio_set_dir(pin::LED_BLANK, GPIO_OUT);
gpio_init(pin::ROW_DATA0); gpio_set_dir(pin::ROW_DATA0, GPIO_OUT);
gpio_init(pin::ROW_DATA1); gpio_set_dir(pin::ROW_DATA1, GPIO_OUT);
gpio_init(pin::ROW_CLEAR); gpio_set_dir(pin::ROW_CLEAR, GPIO_OUT);
gpio_init(pin::ROW_CLOCK); gpio_set_dir(pin::ROW_CLOCK, GPIO_OUT);
// create 14-bit gamma luts
for(uint16_t v = 0; v < 256; v++) {
// gamma correct the provided 0-255 brightness value onto a
// 0-65535 range for the pwm counter
float r_gamma = 2.8f;
r_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, r_gamma) * 16383.0f + 0.5f);
float g_gamma = 2.8f;
g_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, g_gamma) * 16383.0f + 0.5f);
float b_gamma = 2.8f;
b_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, b_gamma) * 16383.0f + 0.5f);
float r_gamma = 1.8f;
r_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, r_gamma) * (float(1U << (BCD_FRAME_COUNT)) - 1.0f) + 0.5f);
float g_gamma = 2.0f;
g_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, g_gamma) * (float(1U << (BCD_FRAME_COUNT)) - 1.0f) + 0.5f);
float b_gamma = 1.8f;
b_gamma_lut[v] = (uint16_t)(powf((float)(v) / 255.0f, b_gamma) * (float(1U << (BCD_FRAME_COUNT)) - 1.0f) + 0.5f);
}
// for each row:
// for each bcd frame:
// 0: 00110110 // row pixel count (minus one)
// 1 - 53: xxxxxbgr, xxxxxbgr, xxxxxbgr, ... // pixel data
// 54 - 55: xxxxxxxx, xxxxxxxx // dummy bytes to dword align
// 56: xxxxrrrr // row select bits
// 57 - 59: tttttttt, tttttttt, tttttttt // bcd tick count (0-65536)
//
// .. and back to the start
// the data is structured like this:
//
// loop through the eleven rows of the display...
//
// 1rr00000 // set row select bit on rows 0 and 8 (side set the clock)
// 00000000 00000000 00000000 // dummy bytes to align to dwords
//
// within this row we loop through the 14 bcd frames for this row...
//
// 0 - 161: 100100rr, 100101rr, 100100gg, 100101gg, 100100bb, 100101bb, ... x 27 # left+right half rgb pixel data doubled for clock pulses, keep BLANK high
// 162: 10011000 // LATCH pixel data
// 163: 10000000 // turn off BLANK to output pixel data - now at 164 bytes (41 dwords)
// 164 - 165: 00001111, 11111111, # bcd tick count (0-65536)
// 166: 10010000 // turn BLANK back on
// 167: 00000000 // dummy byte to ensure dword aligned
//
// .. and back to the start
// initialise the bcd timing values and row selects in the bitstream
for(uint8_t row = 0; row < HEIGHT; row++) {
uint16_t row_offset = row * (ROW_BYTES + ROW_FRAME_BYTES * BCD_FRAMES);
for(uint8_t frame = 0; frame < BCD_FRAME_COUNT; frame++) {
// find the offset of this row and frame in the bitstream
uint8_t *p = &bitstream[row * ROW_BYTES + (BCD_FRAME_BYTES * frame)];
// setup row select on rows 0 and 8
uint8_t row_select = row == 0 ? 0b10111000 : (row == 8 ? 0b11011000 : 0b10011000);
bitstream[row_offset + 0] = row_select;
for(uint8_t frame = 0; frame < BCD_FRAMES; frame++) {
uint16_t frame_offset = row_offset + ROW_BYTES + (ROW_FRAME_BYTES * frame);
bitstream[frame_offset + 162] = 0b10011000; // LATCH pixel data
bitstream[frame_offset + 163] = 0b10001000; // BLANK low to enable column outputs
p[ 0] = WIDTH - 1; // row pixel count
p[56] = row; // row select
// set the number of bcd ticks for this frame
uint16_t bcd_ticks = frame == BCD_FRAMES - 1 ? 1 : 1 << frame;
bitstream[frame_offset + 164] = (bcd_ticks & 0xff);
bitstream[frame_offset + 165] = (bcd_ticks & 0xff00) >> 8;
bitstream[frame_offset + 166] = 0b10010000; // BLANK high again to disable outputs
// setup empty pixels with BLANK high and a clock pulse
for(uint8_t col = 0; col < 162; col += 2) {
bitstream[frame_offset + col + 0] = 0b10010000;
bitstream[frame_offset + col + 1] = 0b10010100;
}
/*
uint16_t row_select_offset = offset + 164;
uint16_t bcd_offset = offset + 165;
// the last bcd frame is used to allow the fets to discharge to avoid ghosting
if(frame == BCD_FRAMES - 1) {
uint16_t bcd_ticks = 65535;
bitstream[bcd_offset + 1] = (bcd_ticks & 0xff00) >> 8;
bitstream[bcd_offset] = (bcd_ticks & 0xff);
}else{
uint8_t row_select = row == 0 ? 0b01000000 : (row == 8 ? 0b00100000 : 0b00000000);
bitstream[row_select_offset] = row_select;
uint16_t bcd_ticks = 1 << frame;
bitstream[bcd_offset + 1] = (bcd_ticks & 0xff00) >> 8;
bitstream[bcd_offset] = (bcd_ticks & 0xff);
}*/
uint32_t bcd_ticks = (1 << frame);
p[57] = (bcd_ticks & 0xff) >> 0;
p[58] = (bcd_ticks & 0xff00) >> 8;
p[59] = (bcd_ticks & 0xff0000) >> 16;
}
/*
for(size_t i = 0; i < sizeof(bitstream); i++) {
bitstream[i] = 0b11100000;
}*/
}
// setup light sensor adc
adc_init();
adc_gpio_init(LIGHT_SENSOR);
pio_gpio_init(bitstream_pio, COLUMN_CLOCK);
pio_gpio_init(bitstream_pio, COLUMN_DATA);
pio_gpio_init(bitstream_pio, COLUMN_LATCH);
gpio_init(COLUMN_CLOCK); gpio_set_dir(COLUMN_CLOCK, GPIO_OUT); gpio_put(COLUMN_CLOCK, false);
gpio_init(COLUMN_DATA); gpio_set_dir(COLUMN_DATA, GPIO_OUT); gpio_put(COLUMN_DATA, false);
gpio_init(COLUMN_LATCH); gpio_set_dir(COLUMN_LATCH, GPIO_OUT); gpio_put(COLUMN_LATCH, false);
sleep_ms(100);
// configure full output current in register 2
uint16_t reg1 = 0b1111111111001110;
// clock the register value to the first 9 driver chips
for(int j = 0; j < 9; j++) {
for(int i = 0; i < 16; i++) {
if(reg1 & (1U << (15 - i))) {
gpio_put(COLUMN_DATA, true);
}else{
gpio_put(COLUMN_DATA, false);
}
sleep_us(10);
gpio_put(COLUMN_CLOCK, true);
sleep_us(10);
gpio_put(COLUMN_CLOCK, false);
}
}
// clock the last chip and latch the value
for(int i = 0; i < 16; i++) {
if(reg1 & (1U << (15 - i))) {
gpio_put(COLUMN_DATA, true);
}else{
gpio_put(COLUMN_DATA, false);
}
sleep_us(10);
gpio_put(COLUMN_CLOCK, true);
sleep_us(10);
gpio_put(COLUMN_CLOCK, false);
if(i == 4) {
gpio_put(COLUMN_LATCH, true);
}
}
gpio_put(COLUMN_LATCH, false);
gpio_init(MUTE); gpio_set_dir(MUTE, GPIO_OUT); gpio_put(MUTE, true);
// setup button inputs
gpio_set_function(pin::SWITCH_A, GPIO_FUNC_SIO); gpio_set_dir(pin::SWITCH_A, GPIO_IN); gpio_pull_up(pin::SWITCH_A);
gpio_set_function(pin::SWITCH_B, GPIO_FUNC_SIO); gpio_set_dir(pin::SWITCH_B, GPIO_IN); gpio_pull_up(pin::SWITCH_B);
gpio_set_function(pin::SWITCH_C, GPIO_FUNC_SIO); gpio_set_dir(pin::SWITCH_C, GPIO_IN); gpio_pull_up(pin::SWITCH_C);
gpio_set_function(pin::SWITCH_D, GPIO_FUNC_SIO); gpio_set_dir(pin::SWITCH_D, GPIO_IN); gpio_pull_up(pin::SWITCH_D);
gpio_set_function(pin::SWITCH_E, GPIO_FUNC_SIO); gpio_set_dir(pin::SWITCH_E, GPIO_IN); gpio_pull_up(pin::SWITCH_E);
gpio_init(SWITCH_A); gpio_pull_up(SWITCH_A);
gpio_init(SWITCH_B); gpio_pull_up(SWITCH_B);
gpio_init(SWITCH_C); gpio_pull_up(SWITCH_C);
gpio_init(SWITCH_D); gpio_pull_up(SWITCH_D);
gpio_set_function(pin::SWITCH_BRIGHTNESS_UP, GPIO_FUNC_SIO); gpio_set_dir(pin::SWITCH_BRIGHTNESS_UP, GPIO_IN); gpio_pull_up(pin::SWITCH_BRIGHTNESS_UP);
gpio_set_function(pin::SWITCH_BRIGHTNESS_DOWN, GPIO_FUNC_SIO); gpio_set_dir(pin::SWITCH_BRIGHTNESS_DOWN, GPIO_IN); gpio_pull_up(pin::SWITCH_BRIGHTNESS_DOWN);
gpio_init(SWITCH_SLEEP); gpio_pull_up(SWITCH_SLEEP);
gpio_set_function(pin::SWITCH_VOLUME_UP, GPIO_FUNC_SIO); gpio_set_dir(pin::SWITCH_VOLUME_UP, GPIO_IN); gpio_pull_up(pin::SWITCH_VOLUME_UP);
gpio_set_function(pin::SWITCH_VOLUME_DOWN, GPIO_FUNC_SIO); gpio_set_dir(pin::SWITCH_VOLUME_DOWN, GPIO_IN); gpio_pull_up(pin::SWITCH_VOLUME_DOWN);
gpio_init(SWITCH_BRIGHTNESS_UP); gpio_pull_up(SWITCH_BRIGHTNESS_UP);
gpio_init(SWITCH_BRIGHTNESS_DOWN); gpio_pull_up(SWITCH_BRIGHTNESS_DOWN);
gpio_init(SWITCH_VOLUME_UP); gpio_pull_up(SWITCH_VOLUME_UP);
gpio_init(SWITCH_VOLUME_DOWN); gpio_pull_up(SWITCH_VOLUME_DOWN);
if(already_init) {
// stop and release the dma channel
@ -288,9 +222,38 @@ namespace pimoroni {
// setup the pio
bitstream_pio = pio0;
bitstream_sm = pio_claim_unused_sm(pio0, true);
sm_offset = pio_add_program(bitstream_pio, &galactic_unicorn_program);
unicorn_jetpack_program_init(bitstream_pio, bitstream_sm, sm_offset);
bitstream_sm = pio_claim_unused_sm(bitstream_pio, true);
bitstream_sm_offset = pio_add_program(bitstream_pio, &galactic_unicorn_program);
pio_gpio_init(bitstream_pio, COLUMN_CLOCK);
pio_gpio_init(bitstream_pio, COLUMN_DATA);
pio_gpio_init(bitstream_pio, COLUMN_LATCH);
pio_gpio_init(bitstream_pio, COLUMN_BLANK);
pio_gpio_init(bitstream_pio, ROW_BIT_0);
pio_gpio_init(bitstream_pio, ROW_BIT_1);
pio_gpio_init(bitstream_pio, ROW_BIT_2);
pio_gpio_init(bitstream_pio, ROW_BIT_3);
// set all led driving pins as outputs
pio_sm_set_consecutive_pindirs(bitstream_pio, bitstream_sm, COLUMN_CLOCK, 8, true);
pio_sm_config c = galactic_unicorn_program_get_default_config(bitstream_sm_offset);
// osr shifts right, autopull on, autopull threshold 8
sm_config_set_out_shift(&c, true, true, 32);
// configure out, set, and sideset pins
sm_config_set_out_pins(&c, ROW_BIT_0, 4);
sm_config_set_set_pins(&c, COLUMN_DATA, 3);
sm_config_set_sideset_pins(&c, COLUMN_CLOCK);
// join fifos as only tx needed (gives 8 deep fifo instead of 4)
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
pio_sm_init(bitstream_pio, bitstream_sm, bitstream_sm_offset, &c);
pio_sm_set_enabled(bitstream_pio, bitstream_sm, true);
// setup dma transfer for pixel data to the pio
dma_channel = dma_claim_unused_channel(true);
@ -308,6 +271,52 @@ namespace pimoroni {
dma_channel_set_read_addr(dma_channel, bitstream, true);
already_init = true;
// setup audio pio program
/*
audio_pio = pio0;
audio_sm = pio_claim_unused_sm(audio_pio, true);
audio_sm_offset = pio_add_program(audio_pio, &audio_i2s_program);
audio_i2s_program_init(audio_pio, audio_sm, audio_sm_offset, I2S_DATA, I2S_BCLK);
//pio_sm_set_enabled(audio_pio, audio_sm, true);
uint32_t system_clock_frequency = clock_get_hz(clk_sys);
uint32_t divider = system_clock_frequency * 4 / 22050; // avoid arithmetic overflow
pio_sm_set_clkdiv_int_frac(audio_pio, audio_sm, divider >> 8u, divider & 0xffu);
audio_dma_channel = dma_claim_unused_channel(true);
dma_channel_config audio_config = dma_channel_get_default_config(audio_dma_channel);
channel_config_set_transfer_data_size(&audio_config, DMA_SIZE_32);
channel_config_set_bswap(&audio_config, false); // byte swap to reverse little endian
channel_config_set_dreq(&audio_config, pio_get_dreq(audio_pio, audio_sm, true));
dma_channel_configure(audio_dma_channel, &audio_config, &audio_pio->txf[audio_sm], NULL, 0, false);
dma_channel_set_irq0_enabled(audio_dma_channel, true);
irq_set_enabled(pio_get_dreq(audio_pio, audio_sm, true), true);*/
//irq_set_exclusive_handler(DMA_IRQ_0, dma_complete);
//irq_set_enabled(DMA_IRQ_0, true);
/* dma_channel_set_trans_count(audio_dma_channel, BITSTREAM_LENGTH / 4, false);
dma_channel_set_read_addr(audio_dma_channel, bitstream, true);*/
//pio_sm_config audio_i2s_config = audio_i2s_program_get_default_config(audio_sm_offset);
// osr shifts right, autopull on, autopull threshold 8
//sm_config_set_out_shift(&audio_i2s_config, true, true, 32);
// // configure out, set, and sideset pins
// sm_config_set_out_pins(&audio_i2s_config, ROW_BIT_0, 4);
// sm_config_set_set_pins(&audio_i2s_config, COLUMN_DATA, 3);
// sm_config_set_sideset_pins(&audio_i2s_config, COLUMN_CLOCK);
// // join fifos as only tx needed (gives 8 deep fifo instead of 4)
// sm_config_set_fifo_join(&audio_i2s_config, PIO_FIFO_JOIN_TX);
//pio_sm_init(audio_pio, audio_sm, audio_sm_offset, &audio_i2s_config);
//pio_sm_set_enabled(audio_pio, audio_sm, true);
}
void GalacticUnicorn::clear() {
@ -318,65 +327,44 @@ namespace pimoroni {
}
}
void GalacticUnicorn::set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) {
void GalacticUnicorn::play_sample(uint8_t *data, uint32_t length) {
dma_channel_transfer_from_buffer_now(audio_dma_channel, data, length / 4);
}
void GalacticUnicorn::set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) {
if(x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return;
// make those coordinates sane
x = (WIDTH - 1) - x;
y = (HEIGHT - 1) - y;
// determine offset in the buffer for this row
uint16_t row_offset = y * (ROW_BYTES + ROW_FRAME_BYTES * BCD_FRAMES);
uint16_t gamma_r = r_gamma_lut[r];
uint16_t gamma_g = g_gamma_lut[g];
uint16_t gamma_b = b_gamma_lut[b];
uint16_t bits[3] = {r_gamma_lut[b], g_gamma_lut[g], b_gamma_lut[r]};
//uint16_t gr = r_gamma_lut[r];
//uint16_t gg = g_gamma_lut[g];
//uint16_t gb = b_gamma_lut[b];
// for each row:
// for each bcd frame:
// 0: 00110110 // row pixel count (minus one)
// 1 - 53: xxxxxbgr, xxxxxbgr, xxxxxbgr, ... // pixel data
// 54 - 55: xxxxxxxx, xxxxxxxx // dummy bytes to dword align
// 56: xxxxrrrr // row select bits
// 57 - 59: tttttttt, tttttttt, tttttttt // bcd tick count (0-65536)
//
// .. and back to the start
// set the appropriate bits in the separate bcd frames
for(uint8_t frame = 0; frame < BCD_FRAMES; frame++) {
uint16_t frame_offset = (ROW_FRAME_BYTES * frame) + 4;
uint16_t offset = row_offset + frame_offset;// + byte_offset;
for(uint8_t frame = 0; frame < BCD_FRAME_COUNT; frame++) {
uint8_t *p = &bitstream[y * ROW_BYTES + (BCD_FRAME_BYTES * frame) + 1 + x];
// loop through the eleven rows of the display...
//
// 1rr00000 // set row select bit on rows 0 and 8 (side set the clock)
// 00000000 00000000 00000000 // dummy bytes to align to dwords
//
// within this row we loop through the 14 bcd frames for this row...
//
// 0 - 161: 100100rr, 100101rr, 100100gg, 100101gg, 100100bb, 100101bb, ... x 27 # left+right half rgb pixel data doubled for clock pulses, keep BLANK high
// 162: 10011000 // LATCH pixel data
// 163: 10000000 // turn off BLANK to output pixel data - now at 164 bytes (41 dwords)
// 164 - 165: 00001111, 11111111, # bcd tick count (0-65536)
// 166: 10010000 // turn BLANK back on
// 167: 00000000 // dummy byte to ensure dword aligned
//
// .. and back to the start
uint8_t red_bit = gamma_r & 0b1;
uint8_t green_bit = gamma_g & 0b1;
uint8_t blue_bit = gamma_b & 0b1;
// work out the byte offset of this pixel
/*if(bit_offset >= 160) {
bit_offset -= 160;
}*/
*p = (blue_bit << 2) | (green_bit << 1) | (red_bit << 0);
for(int bit = 0; bit < 3; bit++) {
int16_t bit_offset = x * 6 + 4 + (bit * 2);
uint8_t bit_position = bit_offset >= 160 ? 1 : 0;
uint8_t mask = 0b1 << bit_position;
uint8_t value = (bits[bit] & 0b1) << bit_position;
bitstream[offset + (bit_offset % 160) + 0] &= ~mask;
bitstream[offset + (bit_offset % 160) + 0] |= value;
bitstream[offset + (bit_offset % 160) + 1] &= ~mask;
bitstream[offset + (bit_offset % 160) + 1] |= value;
//bit_offset += 2;
bits[bit] >>= 1;
}
gamma_r >>= 1;
gamma_g >>= 1;
gamma_b >>= 1;
}
}
@ -384,6 +372,55 @@ namespace pimoroni {
set_pixel(x, y, v, v, v);
}
void GalacticUnicorn::set_brightness(float value) {
value = value < 0.0f ? 0.0f : value;
value = value > 1.0f ? 1.0f : value;
this->brightness = floor(value * 255.0f);
}
float GalacticUnicorn::get_brightness() {
return this->brightness / 255.0f;
}
void GalacticUnicorn::adjust_brightness(float delta) {
this->set_brightness(this->get_brightness() + delta);
}
void GalacticUnicorn::set_volume(float value) {
value = value < 0.0f ? 0.0f : value;
value = value > 1.0f ? 1.0f : value;
this->volume = floor(value * 255.0f);
}
float GalacticUnicorn::get_volume() {
return this->volume / 255.0f;
}
void GalacticUnicorn::adjust_volume(float delta) {
this->set_volume(this->get_volume() + delta);
}
void GalacticUnicorn::update(PicoGraphics_PenRGB565 &graphics) {
uint16_t *p = (uint16_t *)graphics.frame_buffer;
for(size_t j = 0; j < 53 * 11; j++) {
int x = j % 53;
int y = j / 53;
uint16_t col = __builtin_bswap16(*p);
uint8_t r = ((col & 0b1111100000000000) >> 11) << 3;
uint8_t g = ((col & 0b0000011111100000) >> 5) << 2;
uint8_t b = ((col & 0b0000000000011111) >> 0) << 3;
p++;
r = (r * this->brightness) >> 8;
g = (g * this->brightness) >> 8;
b = (b * this->brightness) >> 8;
set_pixel(x, y, b, g, r);
}
}
bool GalacticUnicorn::is_pressed(uint8_t button) {
return !gpio_get(button);
}

Wyświetl plik

@ -1,6 +1,7 @@
#pragma once
#include "hardware/pio.h"
#include "pico_graphics.hpp"
namespace pimoroni {
@ -8,30 +9,81 @@ namespace pimoroni {
public:
static const int WIDTH = 53;
static const int HEIGHT = 11;
static const uint8_t SWITCH_A = 0;
static const uint8_t SWITCH_B = 1;
static const uint8_t SWITCH_C = 3;
static const uint8_t SWITCH_D = 6;
static const uint8_t SWITCH_E = 2;
static const uint8_t SWITCH_VOLUME_UP = 21;
static const uint8_t SWITCH_VOLUME_DOWN = 26;
static const uint8_t SWITCH_BRIGHTNESS_UP = 7;
static const uint8_t SWITCH_BRIGHTNESS_DOWN = 8;
// pin assignments
static const uint8_t COLUMN_CLOCK = 13;
static const uint8_t COLUMN_DATA = 14;
static const uint8_t COLUMN_LATCH = 15;
static const uint8_t COLUMN_BLANK = 16;
static const uint8_t ROW_BIT_0 = 17;
static const uint8_t ROW_BIT_1 = 18;
static const uint8_t ROW_BIT_2 = 19;
static const uint8_t ROW_BIT_3 = 20;
static const uint8_t LIGHT_SENSOR = 28;
static const uint8_t MUTE = 22;
static const uint8_t I2S_DATA = 9;
static const uint8_t I2S_BCLK = 10;
static const uint8_t I2S_LRCLK = 11;
static const uint8_t I2C_SDA = 4;
static const uint8_t I2C_SCL = 5;
static const uint8_t SWITCH_A = 0;
static const uint8_t SWITCH_B = 1;
static const uint8_t SWITCH_C = 3;
static const uint8_t SWITCH_D = 6;
static const uint8_t SWITCH_SLEEP = 27;
static const uint8_t SWITCH_VOLUME_UP = 7;
static const uint8_t SWITCH_VOLUME_DOWN = 8;
static const uint8_t SWITCH_BRIGHTNESS_UP = 21;
static const uint8_t SWITCH_BRIGHTNESS_DOWN = 26;
private:
PIO bitstream_pio = pio0;
uint bitstream_sm = 0;
uint sm_offset = 0;
uint bitstream_sm_offset = 0;
PIO audio_pio = pio0;
uint audio_sm = 0;
uint audio_sm_offset = 0;
uint8_t brightness = 255;
uint8_t volume = 127;
public:
~GalacticUnicorn();
void init();
static inline void pio_program_init(PIO pio, uint sm, uint offset);
void clear();
void update(PicoGraphics_PenRGB565 &graphics);
void set_brightness(float value);
float get_brightness();
void adjust_brightness(float delta);
void set_volume(float value);
float get_volume();
void adjust_volume(float delta);
void set_pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b);
void set_pixel(int x, int y, uint8_t v);
uint16_t light();
bool is_pressed(uint8_t button);
void play_sample(uint8_t *data, uint32_t length);
};
}

Wyświetl plik

@ -2,65 +2,79 @@
.side_set 1 opt
; out pins:
; 0: data1 (base)
; 1: data0
; 2: clock
; 3: latch
; 4: blank
; 5: row1
; 6: row2
; 7: row_clear
;
; - 3: row select bit 0
; - 4: row select bit 1
; - 5: row select bit 2
; - 6: row select bit 3
; set pins:
;
; - 0: column data (base)
; - 1: column latch
; - 2: column blank
; sideset pin:
;
; - 0: column clock
; for each row:
; for each bcd frame:
; 0: 00110110 // row pixel count (minus one)
; 1 - 53: xxxxxbgr, xxxxxbgr, xxxxxbgr, ... // pixel data
; 54 - 55: xxxxxxxx, xxxxxxxx // dummy bytes to dword align
; 56: xxxxrrrr // row select bits
; 57 - 59: tttttttt, tttttttt, tttttttt, // bcd tick count (0-65536)
;
; .. and back to the start
; sideset pin: row_clock
.wrap_target
; set row select pins
pull
out pins, 8 side 0 [4]
nop side 1 [4] ; pulse row select clock
out null, 24 ; discard dummy data
; loop for bcd frames
set x, 14
bcd_frame:
; clock out 53 pixels worth of data - two pixels at a time, so 27 (actually 26.5) bits of data
set y, 26 ; 26 because `jmp` test is pre decrement
; loop over row pixels
out y, 8 ; get row pixel count (minus 1 because test is pre decrement)
pixels:
pull ifempty
out pins, 8 [1] ; two red bits..
out pins, 8 [1] ; ..with clock pulse
pull ifempty
out pins, 8 [1] ; two green bits..
out pins, 8 [1] ; ..with green pulse
pull ifempty
out pins, 8 [1] ; two blue bits..
out pins, 8 [1] ; ..with blue pulse
jmp y-- pixels
out pins, 8 ; LATCH pixel data
out pins, 8 ; turn off BLANK signal
; red bit
out x, 1 side 0 [1] ; pull in blue bit from OSR into register x, clear clock
set pins, 0b100 ; clear data bit, blank high
jmp !x endb ; if bit was zero jump
set pins, 0b101 ; set data bit, blank high
endb:
nop side 1 [2] ; clock in bit
pull
; green bit
out x, 1 side 0 [1] ; pull in green bit from OSR into register X, clear clock
set pins, 0b100 ; clear data bit, blank high
jmp !x endg ; if bit was zero jump
set pins, 0b101 ; set data bit, blank high
endg:
nop side 1 [2] ; clock in bit
; pull bcd tick count into x register
out y, 16
; blue bit
out x, 1 side 0 [1] ; pull in red bit from OSR into register X, clear clock
set pins, 0b100 ; clear data bit, blank high
jmp !x endr ; if bit was zero jump
set pins, 0b101 ; set data bit, blank high
endr:
out null, 5 side 1 [2] ; clock in bit
bcd_count:
jmp y-- bcd_count ; loop until bcd delay complete
;out null, 5 side 0 ; discard the five dummy bits for this pixel
; disable led output (blank) and clear latch pin
out pins, 8 ; turn off BLANK signal and clear row output
out null, 8 ; discard dummy data
jmp y-- pixels
jmp x-- bcd_frame ; loop to next bcd frame
out null, 16 ; discard dummy bytes
out pins, 8 ; output row select
set pins, 0b110 [5] ; latch high, blank high
set pins, 0b000 ; blank low (enable output)
; loop over bcd delay period
out y, 24 ; get bcd delay counter value
bcd_delay:
jmp y-- bcd_delay
set pins 0b100 ; blank high (disable output)
.wrap