Galactic Unicorn: Initial C++ experimentaion.

pull/537/head
jon 2022-06-29 08:54:30 +01:00 zatwierdzone przez Phil Howard
rodzic 241be438b8
commit 09846ab221
8 zmienionych plików z 361 dodań i 619 usunięć

Wyświetl plik

@ -4,122 +4,7 @@ add_executable(
)
# Pull in pico libraries that we need
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)
target_link_libraries(galactic_unicorn_demo pico_stdlib hardware_pio hardware_dma pico_graphics galactic_unicorn)
# create map/bin/hex file etc.
pico_add_extra_outputs(galactic_unicorn_demo)
add_executable(
rainbow
rainbow.cpp
)
# Pull in pico libraries that we need
target_link_libraries(rainbow pico_stdlib hardware_pio hardware_adc hardware_dma pico_graphics galactic_unicorn)
pico_enable_stdio_usb(rainbow 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(rainbow)
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

@ -5,11 +5,10 @@
#include "libraries/pico_graphics/pico_graphics.hpp"
#include "galactic_unicorn.hpp"
#include "okcolor.hpp"
using namespace pimoroni;
PicoGraphics_PenRGB888 graphics(53, 11, nullptr);
PicoGraphics_PenRGB565 graphics(53, 11, nullptr);
GalacticUnicorn galactic_unicorn;
// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel
@ -107,86 +106,10 @@ gpio_set_function(28, GPIO_FUNC_SIO);
//uint i = 0;
//int v = 255;
float hue_offset = 0.0f;
float brightness = 0.5f;
float curve = 4.0f;
int x = 10;
int y = 5;
uint i = 0;
int v = 255;
while(true) {
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_VOLUME_UP)) {
hue_offset += 0.05;
if(hue_offset > 1.0f) hue_offset = 1.0f;
}
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_VOLUME_DOWN)) {
hue_offset -= 0.05;
if(hue_offset < 0.0f) hue_offset = 0.0f;
}
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_UP)) {
brightness += 0.05;
if(brightness > 1.0f) brightness = 1.0f;
}
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_BRIGHTNESS_DOWN)) {
brightness -= 0.05;
if(brightness < 0.0f) brightness = 0.0f;
}
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;
}
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;
float fy = (y / 11.0f) - 0.5f;
float twopi = M_PI * 2;
float hue = fy + (sin(fx * twopi / curve));
float fade = 1.0f;
hue += hue_offset;
while(hue < 0.0f) {hue += 1.0f;}
while(hue > 1.0f) {hue -= 1.0f;}
hue = 1.0f - hue;
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++;
i++;
graphics.set_pen(0, 0, 0);
if(galactic_unicorn.is_pressed(galactic_unicorn.SWITCH_A)) {graphics.set_pen(255, 0, 0);}
@ -228,7 +151,7 @@ graphics.set_pen(255, 255, 255);
}
galactic_unicorn.set_pixel(x, y, r, g, b);
}
*/
sleep_ms(10);
}

Wyświetl plik

@ -1 +1,12 @@
include(galactic_unicorn.cmake)
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)

Wyświetl plik

@ -17,7 +17,6 @@ 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
@ -99,16 +98,4 @@ 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,14 +1,12 @@
add_library(galactic_unicorn INTERFACE)
add_library(pico_unicorn INTERFACE)
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)
pico_generate_pio_header(pico_unicorn ${CMAKE_CURRENT_LIST_DIR}/pico_unicorn.pio)
target_sources(galactic_unicorn INTERFACE
${CMAKE_CURRENT_LIST_DIR}/galactic_unicorn.cpp
target_sources(pico_unicorn INTERFACE
${CMAKE_CURRENT_LIST_DIR}/pico_unicorn.cpp
)
target_include_directories(galactic_unicorn INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_include_directories(pico_unicorn INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(galactic_unicorn INTERFACE pico_stdlib pico_graphics hardware_adc hardware_pio hardware_dma)
target_link_libraries(pico_unicorn INTERFACE pico_stdlib hardware_pio hardware_dma)

Wyświetl plik

@ -2,47 +2,88 @@
#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 pins used are:
// the data consists of 11 rows each of which has 14 frames of
// bcd timing data
//
// - 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
// bits are output in order:
//
// the framebuffer data is structured like this:
// ROW_CLEAR, ROW_DATA1, ROW_DATA0, LED_BLANK, LED_LATCH, LED_CLOCK, LED_DATA0, LED_DATA1
//
// 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)
// 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
/*
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 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);
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);
// must be aligned for 32bit dma transfer
alignas(4) static uint8_t bitstream[BITSTREAM_LENGTH] = {0};
@ -52,7 +93,38 @@ static uint16_t g_gamma_lut[256] = {0};
static uint16_t b_gamma_lut[256] = {0};
static uint32_t dma_channel;
static uint32_t audio_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);
}
namespace pimoroni {
@ -82,124 +154,118 @@ 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 = 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 = 1.8f;
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
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);
}
// 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++) {
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)];
uint16_t row_offset = row * (ROW_BYTES + ROW_FRAME_BYTES * BCD_FRAMES);
p[ 0] = WIDTH - 1; // row pixel count
p[56] = row; // row select
// 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
// set the number of bcd ticks for this frame
uint32_t bcd_ticks = (1 << frame);
p[57] = (bcd_ticks & 0xff) >> 0;
p[58] = (bcd_ticks & 0xff00) >> 8;
p[59] = (bcd_ticks & 0xff0000) >> 16;
}
}
uint16_t bcd_ticks = frame == BCD_FRAMES - 1 ? 65535 : 1 << frame;
bitstream[frame_offset + 164] = (bcd_ticks & 0xff);
bitstream[frame_offset + 165] = (bcd_ticks & 0xff00) >> 8;
// setup light sensor adc
adc_init();
adc_gpio_init(LIGHT_SENSOR);
bitstream[frame_offset + 166] = 0b10010000; // BLANK high again to disable outputs
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);
// 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;
}
sleep_us(10);
gpio_put(COLUMN_CLOCK, true);
sleep_us(10);
gpio_put(COLUMN_CLOCK, false);
/*
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);
}*/
}
/*
for(size_t i = 0; i < sizeof(bitstream); i++) {
bitstream[i] = 0b11100000;
}*/
}
// 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_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_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_SLEEP); gpio_pull_up(SWITCH_SLEEP);
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_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);
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);
if(already_init) {
// stop and release the dma channel
@ -222,38 +288,9 @@ namespace pimoroni {
// setup the pio
bitstream_pio = pio0;
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);
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);
// setup dma transfer for pixel data to the pio
dma_channel = dma_claim_unused_channel(true);
@ -271,33 +308,6 @@ 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);
pio_gpio_init(audio_pio, I2S_DATA);
pio_gpio_init(audio_pio, I2S_BCLK);
pio_gpio_init(audio_pio, I2S_LRCLK);
audio_i2s_program_init(audio_pio, audio_sm, audio_sm_offset, I2S_DATA, I2S_BCLK);
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);
pio_sm_set_enabled(audio_pio, audio_sm, true);
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_16);
//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);
}
void GalacticUnicorn::clear() {
@ -308,44 +318,108 @@ namespace pimoroni {
}
}
void GalacticUnicorn::play_sample(uint8_t *data, uint32_t length) {
dma_channel_transfer_from_buffer_now(audio_dma_channel, data, length / 2);
}
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;
uint16_t gamma_r = r_gamma_lut[r];
uint16_t gamma_g = g_gamma_lut[g];
uint16_t gamma_b = b_gamma_lut[b];
// determine offset in the buffer for this row
uint16_t row_offset = y * (ROW_BYTES + ROW_FRAME_BYTES * BCD_FRAMES);
// 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
uint16_t bits[3] = {r_gamma_lut[r], g_gamma_lut[g], b_gamma_lut[b]};
//uint16_t gr = r_gamma_lut[r];
//uint16_t gg = g_gamma_lut[g];
//uint16_t gb = b_gamma_lut[b];
// set the appropriate bits in the separate bcd frames
for(uint8_t frame = 0; frame < BCD_FRAME_COUNT; frame++) {
uint8_t *p = &bitstream[y * ROW_BYTES + (BCD_FRAME_BYTES * frame) + 1 + x];
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;
uint8_t red_bit = gamma_r & 0b1;
uint8_t green_bit = gamma_g & 0b1;
uint8_t blue_bit = gamma_b & 0b1;
// 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
*p = (blue_bit << 2) | (green_bit << 1) | (red_bit << 0);
// work out the byte offset of this pixel
/*if(bit_offset >= 160) {
bit_offset -= 160;
}*/
gamma_r >>= 1;
gamma_g >>= 1;
gamma_b >>= 1;
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;
}
/* // setup pixel data and matching mask
uint8_t bit_position = x >= 26 ? 1 : 0;
uint8_t mask = 0b1 << bit_position;
uint8_t red = (gr & 0b1) << bit_position;
uint8_t green = (gg & 0b1) << bit_position;
uint8_t blue = (gb & 0b1) << bit_position;
// clear existing data and set new data
bitstream[offset + 0] &= ~mask;
bitstream[offset + 0] |= red;
bitstream[offset + 1] &= ~mask;
bitstream[offset + 1] |= red;
bitstream[offset + 2] &= ~mask;
bitstream[offset + 2] |= green;
bitstream[offset + 3] &= ~mask;
bitstream[offset + 3] |= green;
bitstream[offset + 4] &= ~mask;
bitstream[offset + 4] |= blue;
bitstream[offset + 5] &= ~mask;
bitstream[offset + 5] |= blue;*/
/*
uint8_t mask = 0b11;
uint8_t red = ((gr & 0b1) << 1) | (gr & 0b1) ;
uint8_t green = ((gg & 0b1) << 1) | (gg & 0b1) ;
uint8_t blue = ((gb & 0b1) << 1) | (gb & 0b1) ;
bitstream[offset + 0] &= ~mask;
bitstream[offset + 0] |= red;
bitstream[offset + 1] &= ~mask;
bitstream[offset + 1] |= red;
bitstream[offset + 2] &= ~mask;
bitstream[offset + 2] |= green;
bitstream[offset + 3] &= ~mask;
bitstream[offset + 3] |= green;
bitstream[offset + 4] &= ~mask;
bitstream[offset + 4] |= blue;
bitstream[offset + 5] &= ~mask;
bitstream[offset + 5] |= blue;
*/
/* gr >>= 1;
gg >>= 1;
gb >>= 1;*/
}
}
@ -353,75 +427,6 @@ 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 * 256.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) >> 8;
uint8_t g = (col & 0b0000011111100000) >> 3;
uint8_t b = (col & 0b0000000000011111) << 3;
p++;
r = (r * this->brightness) >> 8;
g = (g * this->brightness) >> 8;
b = (b * this->brightness) >> 8;
set_pixel(x, y, b, g, r);
}
}
void GalacticUnicorn::update(PicoGraphics_PenRGB888 &graphics) {
uint32_t *p = (uint32_t *)graphics.frame_buffer;
for(size_t j = 0; j < 53 * 11; j++) {
int x = j % 53;
int y = j / 53;
uint32_t col = *p;
uint8_t r = (col & 0xff0000) >> 16;
uint8_t g = (col & 0x00ff00) >> 8;
uint8_t b = (col & 0x0000ff) >> 0;
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,7 +1,6 @@
#pragma once
#include "hardware/pio.h"
#include "pico_graphics.hpp"
namespace pimoroni {
@ -9,82 +8,30 @@ namespace pimoroni {
public:
static const int WIDTH = 53;
static const int HEIGHT = 11;
// 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;
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;
private:
PIO bitstream_pio = pio0;
uint bitstream_sm = 0;
uint bitstream_sm_offset = 0;
PIO audio_pio = pio0;
uint audio_sm = 0;
uint audio_sm_offset = 0;
uint16_t brightness = 256;
uint16_t volume = 127;
uint sm_offset = 0;
public:
~GalacticUnicorn();
void init();
static inline void pio_program_init(PIO pio, uint sm, uint offset);
void clear();
void update(PicoGraphics_PenRGB565 &graphics);
void update(PicoGraphics_PenRGB888 &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,79 +2,65 @@
.side_set 1 opt
; out pins:
;
; - 3: row select bit 0
; - 4: row select bit 1
; - 5: row select bit 2
; - 6: row select bit 3
; 0: data1 (base)
; 1: data0
; 2: clock
; 3: latch
; 4: blank
; 5: row1
; 6: row2
; 7: row_clear
; 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
; loop over row pixels
out y, 8 ; get row pixel count (minus 1 because test is pre decrement)
; 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
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
; 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
out pins, 8 ; LATCH pixel data
out pins, 8 ; turn off BLANK signal
; 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
; 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
; pull bcd tick count into x register
out y, 16
;out null, 5 side 0 ; discard the five dummy bits for this pixel
bcd_count:
jmp y-- bcd_count ; loop until bcd delay complete
jmp y-- pixels
; 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
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)
jmp x-- bcd_frame ; loop to next bcd frame
.wrap