pi5-pio-protomatter/include/piomatter/piomatter.h

164 lines
5.2 KiB
C++

#pragma once
#include <thread>
#include "hardware/pio.h"
#include "piomatter/pins.h"
#include "piomatter/buffer_manager.h"
#include "piomatter/render.h"
#include "piomatter/matrixmap.h"
#include "piomatter/protomatter.pio.h"
namespace piomatter {
constexpr size_t MAX_XFER = 65532;
void pio_sm_xfer_data_large(PIO pio, int sm, int direction, size_t size, uint32_t *databuf) {
while(size) {
size_t xfersize = std::min(size_t{MAX_XFER}, size);
int r = pio_sm_xfer_data(pio, sm, direction, xfersize, databuf);
if (r) {
throw std::runtime_error("pio_sm_xfer_data (reboot may be required)");
}
size -= xfersize;
databuf += xfersize / sizeof(*databuf);
}
}
struct piomatter_base {
virtual ~piomatter_base() {}
virtual void show() = 0;
};
template<class pinout=adafruit_matrix_bonnet_pinout, class colorspace=colorspace_rgb888>
struct piomatter : piomatter_base {
using buffer_type = std::vector<uint32_t>;
piomatter(std::span<typename colorspace::data_type const> framebuffer, const matrix_geometry &geometry) : framebuffer(framebuffer), geometry{geometry}, converter{}, blitter_thread{&piomatter::blit_thread, this} {
if (geometry.n_addr_lines > std::size(pinout::PIN_ADDR)) {
throw std::runtime_error("too many address lines requested");
}
program_init();
show();
}
void show() override {
int buffer_idx = manager.get_free_buffer();
auto &buffer = buffers[buffer_idx];
auto converted = converter.convert(framebuffer);
protomatter_render_rgb10<pinout>(buffer, geometry, converted.data());
manager.put_filled_buffer(buffer_idx);
}
~piomatter() {
if (pio != NULL && sm >= 0) {
pin_deinit_one(pinout::PIN_OE);
pin_deinit_one(pinout::PIN_CLK);
pin_deinit_one(pinout::PIN_LAT);
for(const auto p : pinout::PIN_RGB)
pin_deinit_one(p);
for(size_t i=0; i<geometry.n_addr_lines; i++) {
pin_deinit_one(pinout::PIN_ADDR[i]);
}
pio_sm_unclaim(pio, sm);
}
manager.request_exit();
if (blitter_thread.joinable()) {
blitter_thread.join();
}
}
private:
void program_init() {
pio = pio0;
sm = pio_claim_unused_sm(pio, true);
if (sm < 0) {
throw std::runtime_error("pio_claim_unused_sm");
}
int r = pio_sm_config_xfer(pio, sm, PIO_DIR_TO_SM, MAX_XFER, 2);
if (r) {
throw std::runtime_error("pio_sm_config_xfer");
}
static const struct pio_program protomatter_program = {
.instructions = protomatter,
.length = 32,
.origin = -1,
};
uint offset = pio_add_program(pio, &protomatter_program);
if (offset== PIO_ORIGIN_INVALID) {
throw std::runtime_error("pio_add_program");
}
pio_sm_clear_fifos(pio, sm);
pio_sm_set_clkdiv(pio, sm, 1.0);
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + protomatter_wrap_target, offset + protomatter_wrap);
sm_config_set_out_shift(&c, /* shift_right= */ false, /* auto_pull = */ true, 32);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
sm_config_set_clkdiv(&c, 1.0);
sm_config_set_out_pins(&c, 0, 28);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
pin_init_one(pinout::PIN_OE);
pin_init_one(pinout::PIN_CLK);
pin_init_one(pinout::PIN_LAT);
for(const auto p : pinout::PIN_RGB)
pin_init_one(p);
for(size_t i=0; i<geometry.n_addr_lines; i++) {
pin_init_one(pinout::PIN_ADDR[i]);
}
}
void pin_init_one(int pin) {
pio_gpio_init(pio, pin);
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
}
void pin_deinit_one(int pin) {
pio_gpio_init(pio, pin);
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
}
void blit_thread() {
const uint32_t *databuf = nullptr;
size_t datasize = 0;
int old_buffer_idx = buffer_manager::no_buffer;
int buffer_idx;
while((buffer_idx = manager.get_filled_buffer()) != buffer_manager::exit_request) {
if(buffer_idx != buffer_manager::no_buffer) {
const auto &buffer = buffers[buffer_idx];
databuf = &buffer[0];
datasize = buffer.size() * sizeof(*databuf);
if (old_buffer_idx != buffer_manager::no_buffer) {
manager.put_free_buffer(old_buffer_idx);
}
old_buffer_idx = buffer_idx;
}
if (datasize) {
pio_sm_xfer_data_large(pio, sm, PIO_DIR_TO_SM, datasize, (uint32_t*)databuf);
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
}
PIO pio = NULL;
int sm = -1;
std::span<typename colorspace::data_type const> framebuffer;
buffer_type buffers[3];
buffer_manager manager{};
matrix_geometry geometry;
colorspace converter;
std::thread blitter_thread;
};
}