kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Galactic Unicorn: Initial C++ experimentaion.
rodzic
241be438b8
commit
09846ab221
|
@ -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)
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
```
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
Ładowanie…
Reference in New Issue