kopia lustrzana https://github.com/Wren6991/PicoDVI
Add double-buffered 8-bit framebuf (requires GFX 1.11.4)
rodzic
e12f1a3d91
commit
c192ac459f
|
|
@ -0,0 +1,51 @@
|
|||
// Double-buffered 8-bit Adafruit_GFX-compatible framebuffer for PicoDVI.
|
||||
// Allows animation without redraw flicker. Requires Adafruit_GFX 1.11.4
|
||||
|
||||
#include <PicoDVI.h>
|
||||
|
||||
//DVIGFX8x2 display(320, 240, dvi_timing_640x480p_60hz, VREG_VOLTAGE_1_20, pimoroni_demo_hdmi_cfg);
|
||||
DVIGFX8x2 display(400, 240, dvi_timing_800x480p_60hz, VREG_VOLTAGE_1_30, pimoroni_demo_hdmi_cfg);
|
||||
|
||||
#define N_BALLS 100 // 1-254 (not 255)
|
||||
struct {
|
||||
int16_t pos[2];
|
||||
int8_t vel[2];
|
||||
} ball[N_BALLS];
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
//while(!Serial);
|
||||
if (!display.begin()) { // Blink LED if insufficient RAM
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
for (;;) digitalWrite(LED_BUILTIN, (millis() / 500) & 1);
|
||||
}
|
||||
|
||||
// Randomize initial ball positions, velocities and colors
|
||||
for (int i=0; i<N_BALLS; i++) {
|
||||
display.setColor(i+1, 64 + random(192), 64 + random(192), 64 + random(192));
|
||||
ball[i].pos[0] = 10 + random(display.width() - 20);
|
||||
ball[i].pos[1] = 10 + random(display.height() - 20);
|
||||
do {
|
||||
ball[i].vel[0] = 2 - random(5);
|
||||
ball[i].vel[1] = 2 - random(5);
|
||||
} while ((ball[i].vel[0] == 0) && (ball[i].vel[1] == 0));
|
||||
}
|
||||
display.setColor(255, 0xFFFF); // Last palette entry = white
|
||||
display.swap(false, true); // Duplicate same palette into front & back buffers
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Clear back framebuffer and draw balls (circles) there.
|
||||
display.fillScreen(0);
|
||||
for (int i=0; i<N_BALLS; i++) {
|
||||
display.fillCircle(ball[i].pos[0] - 10, ball[i].pos[1] - 10, 20, i + 1);
|
||||
// After drawing each one, update positions, bounce off edges.
|
||||
ball[i].pos[0] += ball[i].vel[0];
|
||||
if ((ball[i].pos[0] <= 0) || (ball[i].pos[0] >= display.width())) ball[i].vel[0] *= -1;
|
||||
ball[i].pos[1] += ball[i].vel[1];
|
||||
if ((ball[i].pos[1] <= 0) || (ball[i].pos[1] >= display.height())) ball[i].vel[1] *= -1;
|
||||
}
|
||||
// Swap front/back buffers, do not duplicate current screen state to next frame,
|
||||
// we'll draw it new from scratch each time.
|
||||
display.swap();
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// 8-bit Adafruit_GFX-compatible framebuffer for PicoDVI.
|
||||
|
||||
#include <PicoDVI.h>
|
||||
|
||||
DVIGFX8 display(320, 240, dvi_timing_640x480p_60hz, VREG_VOLTAGE_1_20, pimoroni_demo_hdmi_cfg);
|
||||
//DVIGFX8 display(400, 240, dvi_timing_800x480p_60hz, VREG_VOLTAGE_1_30, pimoroni_demo_hdmi_cfg);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
//while(!Serial);
|
||||
if (!display.begin()) { // Blink LED if insufficient RAM
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
for (;;) digitalWrite(LED_BUILTIN, (millis() / 500) & 1);
|
||||
}
|
||||
|
||||
// Randomize color palette. First entry is left black, last is set white.
|
||||
for (int i=1; i<255; i++) display.setColor(i, random(65536));
|
||||
display.setColor(255, 0xFFFF);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Draw random lines
|
||||
display.drawLine(random(display.width()), random(display.height()), random(display.width()), random(display.height()), random(256));
|
||||
}
|
||||
|
|
@ -136,10 +136,8 @@ bool DVIGFX8::begin(void) {
|
|||
mainloop = dvi_scanbuf_main_16bpp; // in libdvi
|
||||
dvi0.scanline_callback = scanline_callback_GFX8;
|
||||
PicoDVI::begin();
|
||||
for (int i = 0; i < WIDTH; i++) {
|
||||
row565[0][i] = palette[bufptr[i]];
|
||||
row565[1][i] = palette[bufptr[i + WIDTH]];
|
||||
}
|
||||
// No need to initialize the row565 buffer contents as that memory is
|
||||
// cleared on canvas alloc, and the initial palette state is also all 0.
|
||||
uint16_t *b16 = row565[0];
|
||||
queue_add_blocking_u32(&dvi0.q_colour_valid, &b16);
|
||||
b16 = row565[1];
|
||||
|
|
@ -149,3 +147,93 @@ bool DVIGFX8::begin(void) {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// DVIGFX8x2 (8-bit, color-indexed, double-buffered for animation)
|
||||
// requires latest Adafruit_GFX as it plays games with the canvas pointer,
|
||||
// wasn't possible until that was made protected (vs private). This is very
|
||||
// similar to DVIGFX8 but effectively has two canvases and palettes ("front"
|
||||
// and "back"). Drawing and palette-setting operations ONLY apply to the
|
||||
// "back" state. Call swap() to switch the front/back buffers at the next
|
||||
// vertical sync, for flicker-free and tear-free animation.
|
||||
|
||||
DVIGFX8x2::DVIGFX8x2(const uint16_t w, const uint16_t h,
|
||||
const struct dvi_timing &t, vreg_voltage v,
|
||||
const struct dvi_serialiser_cfg &c)
|
||||
: PicoDVI(t, v, c), GFXcanvas8(w, h * 2 + 4) {
|
||||
HEIGHT = _height = h;
|
||||
buffer_save = buffer;
|
||||
}
|
||||
|
||||
DVIGFX8x2::~DVIGFX8x2(void) {
|
||||
buffer = buffer_save; // Restore pointer so canvas destructor works
|
||||
gfxptr = NULL;
|
||||
}
|
||||
|
||||
static void scanline_callback_GFX8x2(void) {
|
||||
((DVIGFX8x2 *)gfxptr)->_scanline_callback();
|
||||
}
|
||||
|
||||
void __not_in_flash_func(DVIGFX8x2::_scanline_callback)(void) {
|
||||
uint16_t *b16;
|
||||
while (queue_try_remove_u32(&dvi0.q_colour_free, &b16))
|
||||
; // Discard returned pointer(s)
|
||||
b16 = row565[rowidx]; // Next row to send
|
||||
queue_add_blocking_u32(&dvi0.q_colour_valid, &b16); // Send it
|
||||
|
||||
if (++scanline >= HEIGHT) { // Next scanline...end of screen reached?
|
||||
if (swap_wait) { // Swap buffers?
|
||||
back_index = 1 - back_index; // Yes plz
|
||||
buffer = buffer_save + WIDTH * HEIGHT * back_index;
|
||||
swap_wait = 0;
|
||||
}
|
||||
scanline = 0;
|
||||
}
|
||||
// Refresh from front buffer
|
||||
uint8_t *b8 = buffer_save + WIDTH * HEIGHT * (1 - back_index) +
|
||||
WIDTH * scanline; // New src
|
||||
rowidx = (rowidx + 1) & 1; // Swap row565[] bufs
|
||||
b16 = row565[rowidx]; // New dest
|
||||
for (int i = 0; i < WIDTH; i++)
|
||||
b16[i] = palette[1 - back_index][b8[i]];
|
||||
}
|
||||
|
||||
bool DVIGFX8x2::begin(void) {
|
||||
uint8_t *bufptr = getBuffer();
|
||||
if ((bufptr)) {
|
||||
gfxptr = this;
|
||||
row565[0] = (uint16_t *)&bufptr[WIDTH * HEIGHT * 2];
|
||||
row565[1] = row565[0] + WIDTH;
|
||||
memset(palette, 0, sizeof palette);
|
||||
// mainloop = mainloop8;
|
||||
mainloop = dvi_scanbuf_main_16bpp; // in libdvi
|
||||
dvi0.scanline_callback = scanline_callback_GFX8x2;
|
||||
PicoDVI::begin();
|
||||
bufptr += WIDTH * HEIGHT; // Initial front buffer is index 1
|
||||
// No need to initialize the row565 buffer contents as that memory is
|
||||
// cleared on canvas alloc, and the initial palette state is also all 0.
|
||||
uint16_t *b16 = row565[0];
|
||||
queue_add_blocking_u32(&dvi0.q_colour_valid, &b16);
|
||||
b16 = row565[1];
|
||||
queue_add_blocking_u32(&dvi0.q_colour_valid, &b16);
|
||||
wait_begin = false; // Set core 1 in motion
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DVIGFX8x2::swap(bool copy_framebuffer, bool copy_palette) {
|
||||
|
||||
// Request buffer swap at next frame end, wait for it to happen.
|
||||
for (swap_wait = 1; swap_wait;)
|
||||
;
|
||||
|
||||
if ((copy_framebuffer)) {
|
||||
uint32_t bufsize = WIDTH * HEIGHT;
|
||||
memcpy(buffer_save + bufsize * back_index,
|
||||
buffer_save + bufsize * (1 - back_index), bufsize);
|
||||
}
|
||||
|
||||
if ((copy_palette)) {
|
||||
memcpy(palette[back_index], palette[1 - back_index], sizeof(palette[0]));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,3 +61,34 @@ protected:
|
|||
uint16_t scanline = 2; // First 2 scanlines are set up before DVI start
|
||||
uint8_t rowidx = 1; // Alternate 0/1 for which row565[] is active
|
||||
};
|
||||
|
||||
class DVIGFX8x2 : public PicoDVI, public GFXcanvas8 {
|
||||
public:
|
||||
DVIGFX8x2(const uint16_t w = 320, const uint16_t h = 240,
|
||||
const struct dvi_timing &t = dvi_timing_640x480p_60hz,
|
||||
vreg_voltage v = VREG_VOLTAGE_1_10,
|
||||
const struct dvi_serialiser_cfg &c = pimoroni_demo_hdmi_cfg);
|
||||
~DVIGFX8x2(void);
|
||||
bool begin(void);
|
||||
uint16_t *getPalette(void) { return palette[back_index]; }
|
||||
void setColor(uint8_t idx, uint16_t color) {
|
||||
palette[back_index][idx] = color;
|
||||
}
|
||||
void setColor(uint8_t idx, uint8_t red, uint8_t green, uint8_t blue) {
|
||||
palette[back_index][idx] =
|
||||
((red & 0xF8) << 8) | ((green & 0xFC) << 3) | (blue >> 3);
|
||||
}
|
||||
uint16_t getColor(uint8_t idx) { return palette[back_index][idx]; }
|
||||
void swap(bool copy_framebuffer = false, bool copy_palette = false);
|
||||
|
||||
void _scanline_callback(void);
|
||||
|
||||
protected:
|
||||
uint16_t palette[2][256]; // Double-buffered palette
|
||||
uint16_t *row565[2]; // 2 scanlines of 16-bit RGB565 data
|
||||
uint16_t scanline = 2; // First 2 scanlines are set up before DVI start
|
||||
uint8_t rowidx = 1; // Alternate 0/1 for which row565[] is active
|
||||
uint8_t *buffer_save; // Original canvas buffer pointer
|
||||
uint8_t back_index = 0; // Which of 2 buffers receives draw ops
|
||||
volatile bool swap_wait = 0; // For syncronizing front/back buffer swap
|
||||
};
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue