From fa0734fec04cf767e0acb61faef20cf579b778e1 Mon Sep 17 00:00:00 2001 From: giomba Date: Fri, 2 Dec 2022 22:49:07 +0100 Subject: [PATCH] Refactoring in multiple independent modules. --- CMakeLists.txt | 2 + src/draw_demo.c | 170 ++++++++++++++++++++++++++++++++++++++++ src/draw_demo.h | 30 +++++++ src/framebuffer.c | 8 ++ src/framebuffer.h | 16 ++++ src/main.c | 195 +--------------------------------------------- 6 files changed, 228 insertions(+), 193 deletions(-) create mode 100644 src/draw_demo.c create mode 100644 src/draw_demo.h create mode 100644 src/framebuffer.c create mode 100644 src/framebuffer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 842a853..226d790 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,8 @@ add_compile_options(-Wall add_executable(ceda2vga src/main.c + src/draw_demo.c + src/framebuffer.c ) pico_generate_pio_header(ceda2vga diff --git a/src/draw_demo.c b/src/draw_demo.c new file mode 100644 index 0000000..3d61525 --- /dev/null +++ b/src/draw_demo.c @@ -0,0 +1,170 @@ +#include "draw_demo.h" + +#include "framebuffer.h" +#include "pico/platform.h" + +#include + +static bool valid_coord(uint16_t x, uint16_t y) +{ + if (x < 0 || x >= HPIXEL) + return false; + if (y < 0 || y >= VPIXEL) + return false; + + return true; +} + +// just because i did not need all the letters +// and didn't want to fill a proper array :-) +const Letter letters[] = { + {'A', 6, (Point[]){{0, 7}, {0, 0}, {7, 0}, {7, 7}, {7, 3}, {0, 3}}}, + {'G', 6, (Point[]){{7, 0}, {0, 0}, {0, 7}, {7, 7}, {7, 3}, {3, 3}}}, + {'L', 3, (Point[]){{0, 0}, {0, 7}, {7, 7}}}, + {'M', 5, (Point[]){{0, 7}, {0, 0}, {3, 3}, {7, 0}, {7, 7}}}, + {'O', 5, (Point[]){{0, 0}, {0, 7}, {7, 7}, {7, 0}, {0, 0}}}, + {'P', 5, (Point[]){{0, 7}, {0, 0}, {7, 0}, {7, 3}, {0, 3}}}, + {'R', 6, (Point[]){{0, 7}, {0, 0}, {7, 0}, {7, 3}, {0, 3}, {7, 7}}}, + {'S', 6, (Point[]){{0, 7}, {7, 7}, {7, 3}, {0, 3}, {0, 0}, {7, 0}}}, +}; + +void draw_path(uint16_t x, uint16_t y, Point points[], size_t len) +{ + Point *old_point = &points[0]; + + for (int i = 1; i < len; ++i) + { + Point *current_point = &points[i]; + draw_line(x + old_point->x, y + old_point->y, x + current_point->x, y + current_point->y, + true); + old_point = current_point; + } +} + +void draw_letter(uint16_t x, uint16_t y, char c) +{ + for (int i = 0; i < count_of(letters); ++i) + { + if (letters[i].letter == c) + { + draw_path(x, y, letters[i].points, letters[i].len); + break; + } + } +} + +void draw_string(uint16_t x, uint16_t y, const char *s) +{ + while (*s != 0) + { + draw_letter(x, y, *s); + s++; + x += 9; + } +} + +void draw_demo(void) +{ + // draw a little demo + + // clear frame buffer + memset(&frame[0], 0, sizeof(frame)); + + // horizontal lines + draw_line(0, 0, 640 - 1, 0, true); + draw_line(0, 240 - 1, 640 - 1, 240 - 1, true); + draw_line(0, 480 - 1, 640 - 1, 480 - 1, true); + + // vertical lines + draw_line(0, 0, 0, 480 - 1, true); + draw_line(320 - 1, 0, 320 - 1, 480 - 1, true); + draw_line(638, 0, 638, 480 - 1, true); // last pixel of a line can't be set, see set_pixel() + + // cat eye + for (uint16_t x = 0; x < 640; x += 20) + { + draw_line(x, 0, 0, (480 - x * 480 / 640), true); + draw_line(640, x * 480 / 640, 640 - x, 480, true); + } + + // string + draw_string(200, 200, "GLG PROGRAMS"); +} + +void draw_simple_line(uint16_t x, uint16_t y, uint16_t len, bool horizontal) +{ + for (uint16_t i = 0; i < len; ++i) + { + if (horizontal) + set_pixel(x + i, y); + else + set_pixel(x, y + i); + } +} + +void draw_line(uint16_t _x0, uint16_t _y0, uint16_t _x1, uint16_t _y1, bool fill) +{ + const float x0 = _x0; + const float y0 = _y0; + const float x1 = _x1; + const float y1 = _y1; + + if (x0 != x1) + { + const uint16_t x_begin = MIN(x0, x1); + const uint16_t x_end = MAX(x0, x1); + + for (uint16_t x = x_begin; x < x_end; ++x) + { + const uint16_t y = (y1 - y0) * (x - x0) / (x1 - x0) + y0; + if (fill) + set_pixel(x, y); + else + clear_pixel(x, y); + } + } + else + { + const uint16_t y_begin = MIN(y0, y1); + const uint16_t y_end = MAX(y0, y1); + + for (uint16_t y = y_begin; y < y_end; ++y) + { + if (fill) + set_pixel(x0, y); + else + clear_pixel(x0, y); + } + } +} + +void set_pixel(uint16_t x, uint16_t y) +{ + // ignore out of frame pixels + if (!valid_coord(x, y)) + return; + + // Since there is not much space in PIO program memory, + // it is not possible to set the last pixel of a line, + // because there is not enough program space to unset it + // in the hardware, and, if left set, will mess with + // the sync signals. + if (x == 639) + return; + + // skip wasted blanking lines + // (see frame struct declaration) + y += VBBLANK + VSPULSE - 1; + + // set bit + frame[y * 80 + x / 8] |= (1 << x % 8); +} + +void clear_pixel(uint16_t x, uint16_t y) +{ + if (!valid_coord(x, y)) + return; + + y += VBBLANK + VSPULSE - 1; + frame[y * 80 + x / 8] &= ~(1 << x % 8); +} \ No newline at end of file diff --git a/src/draw_demo.h b/src/draw_demo.h new file mode 100644 index 0000000..cc2a33d --- /dev/null +++ b/src/draw_demo.h @@ -0,0 +1,30 @@ +#ifndef DRAW_DEMO_H +#define DRAW_DEMO_H + +#include +#include +#include + +typedef struct Point +{ + uint16_t x; + uint16_t y; +} Point; + +typedef struct Letter +{ + char letter; + size_t len; + Point *points; +} Letter; + +void draw_path(uint16_t x, uint16_t y, Point points[], size_t len); +void draw_letter(uint16_t x, uint16_t y, char c); +void draw_string(uint16_t x, uint16_t y, const char *s); +void draw_demo(void); +void draw_simple_line(uint16_t x, uint16_t y, uint16_t len, bool horizontal); +void draw_line(uint16_t _x0, uint16_t _y0, uint16_t _x1, uint16_t _y1, bool fill); +void set_pixel(uint16_t x, uint16_t y); +void clear_pixel(uint16_t x, uint16_t y); + +#endif // DRAW_DEMO_H diff --git a/src/framebuffer.c b/src/framebuffer.c new file mode 100644 index 0000000..5fc1dac --- /dev/null +++ b/src/framebuffer.c @@ -0,0 +1,8 @@ +#include "framebuffer.h" + +// Frame buffer. +// Due to limitations in program space of PIO interface, +// additional blank lines are needed and sent to the display, +// to fill the vertical blanking interval at the top of the display. +// (at 1BPP, this wastes about 1k) +uint8_t frame[HPIXEL * (VSPULSE + VBBLANK + VPIXEL) * BPP / 8] = {0}; diff --git a/src/framebuffer.h b/src/framebuffer.h new file mode 100644 index 0000000..d3541b2 --- /dev/null +++ b/src/framebuffer.h @@ -0,0 +1,16 @@ +#ifndef FRAMEBUFFER_H +#define FRAMEBUFFER_H + +#include + +#define HPIXEL 640 // number of pixels displayed horizontally +#define VPIXEL 480 // number of pixels displayed vertically +#define BPP 1 // bits per pixel + +#define VSPULSE 3 // number of lines during the vertical sync pulse +#define VBBLANK 16 // number of lines of the initial back porch blanking period +#define VFBLANK 1 // number of lines of the final front porch blanking period + +extern uint8_t frame[HPIXEL * (VSPULSE + VBBLANK + VPIXEL) * BPP / 8]; + +#endif diff --git a/src/main.c b/src/main.c index 6f8f397..ec8a16a 100644 --- a/src/main.c +++ b/src/main.c @@ -16,27 +16,13 @@ #include #include -#include #include #include #include "vga.pio.h" -#define HPIXEL 640 // number of pixels displayed horizontally -#define VPIXEL 480 // number of pixels displayed vertically -#define BPP 1 // bits per pixel - -#define VSPULSE 3 // number of lines during the vertical sync pulse -#define VBBLANK 16 // number of lines of the initial back porch blanking period -#define VFBLANK 1 // number of lines of the final front porch blanking period - -// Frame buffer. -// Due to limitations in program space of PIO interface, -// additional blank lines are needed and sent to the display, -// to fill the vertical blanking interval at top and bottom -// of the display -// (at 1BPP, this wastes about 1k) -uint8_t frame[HPIXEL * (VSPULSE + VBBLANK + VPIXEL) * BPP / 8] = {0}; +#include "draw_demo.h" +#include "framebuffer.h" static void vga_pixel_program_init(PIO pio, uint sm, uint offset) { @@ -140,94 +126,6 @@ void dma_handler(void) dma_hw->ints0 = 1 << dma_channel; } -bool valid_coord(uint16_t x, uint16_t y) -{ - if (x < 0 || x >= HPIXEL) - return false; - if (y < 0 || y >= VPIXEL) - return false; - - return true; -} - -void set_pixel(uint16_t x, uint16_t y) -{ - // ignore out of frame pixels - if (!valid_coord(x, y)) - return; - - // Since there is not much space in PIO program memory, - // it is not possible to set the last pixel of a line, - // because there is not enough program space to unset it - // in the hardware, and, if left set, will mess with - // the sync signals. - if (x == 639) - return; - - // skip wasted blanking lines - // (see frame struct declaration) - y += VBBLANK + VSPULSE - 1; - - // set bit - frame[y * 80 + x / 8] |= (1 << x % 8); -} - -void clear_pixel(uint16_t x, uint16_t y) -{ - if (!valid_coord(x, y)) - return; - - y += VBBLANK + VSPULSE - 1; - frame[y * 80 + x / 8] &= ~(1 << x % 8); -} - -void draw_simple_line(uint16_t x, uint16_t y, uint16_t len, bool horizontal) -{ - for (uint16_t i = 0; i < len; ++i) - { - if (horizontal) - set_pixel(x + i, y); - else - set_pixel(x, y + i); - } -} - -void draw_line(uint16_t _x0, uint16_t _y0, uint16_t _x1, uint16_t _y1, bool fill) -{ - const float x0 = _x0; - const float y0 = _y0; - const float x1 = _x1; - const float y1 = _y1; - - if (x0 != x1) - { - const uint16_t x_begin = MIN(x0, x1); - const uint16_t x_end = MAX(x0, x1); - - for (uint16_t x = x_begin; x < x_end; ++x) - { - const uint16_t y = (y1 - y0) * (x - x0) / (x1 - x0) + y0; - if (fill) - set_pixel(x, y); - else - clear_pixel(x, y); - } - } - else - { - const uint16_t y_begin = MIN(y0, y1); - const uint16_t y_end = MAX(y0, y1); - - for (uint16_t y = y_begin; y < y_end; ++y) - { - if (fill) - set_pixel(x0, y); - else - clear_pixel(x0, y); - } - } -} - void new_frame_handler(void) { if (vga_pixel_ptr && dma_ready) @@ -253,95 +151,6 @@ void new_frame_handler(void) pio_interrupt_clear(pio0_hw, 1); } -typedef struct Point -{ - uint16_t x; - uint16_t y; -} Point; - -typedef struct Letter -{ - char letter; - size_t len; - Point *points; -} Letter; - -// just because i did not need all the letters -// and didn't want to fill a proper array :-) -const Letter letters[] = { - {'A', 6, (Point[]){{0, 7}, {0, 0}, {7, 0}, {7, 7}, {7, 3}, {0, 3}}}, - {'G', 6, (Point[]){{7, 0}, {0, 0}, {0, 7}, {7, 7}, {7, 3}, {3, 3}}}, - {'L', 3, (Point[]){{0, 0}, {0, 7}, {7, 7}}}, - {'M', 5, (Point[]){{0, 7}, {0, 0}, {3, 3}, {7, 0}, {7, 7}}}, - {'O', 5, (Point[]){{0, 0}, {0, 7}, {7, 7}, {7, 0}, {0, 0}}}, - {'P', 5, (Point[]){{0, 7}, {0, 0}, {7, 0}, {7, 3}, {0, 3}}}, - {'R', 6, (Point[]){{0, 7}, {0, 0}, {7, 0}, {7, 3}, {0, 3}, {7, 7}}}, - {'S', 6, (Point[]){{0, 7}, {7, 7}, {7, 3}, {0, 3}, {0, 0}, {7, 0}}}, -}; - -void draw_path(uint16_t x, uint16_t y, Point points[], size_t len) -{ - Point *old_point = &points[0]; - - for (int i = 1; i < len; ++i) - { - Point *current_point = &points[i]; - draw_line(x + old_point->x, y + old_point->y, x + current_point->x, y + current_point->y, - true); - old_point = current_point; - } -} - -void draw_letter(uint16_t x, uint16_t y, char c) -{ - for (int i = 0; i < count_of(letters); ++i) - { - if (letters[i].letter == c) - { - draw_path(x, y, letters[i].points, letters[i].len); - break; - } - } -} - -void draw_string(uint16_t x, uint16_t y, const char *s) -{ - while (*s != 0) - { - draw_letter(x, y, *s); - s++; - x += 9; - } -} - -void draw_demo(void) -{ - // draw a little demo - - // clear frame buffer - memset(&frame[0], 0, sizeof(frame)); - - // horizontal lines - draw_line(0, 0, 640 - 1, 0, true); - draw_line(0, 240 - 1, 640 - 1, 240 - 1, true); - draw_line(0, 480 - 1, 640 - 1, 480 - 1, true); - - // vertical lines - draw_line(0, 0, 0, 480 - 1, true); - draw_line(320 - 1, 0, 320 - 1, 480 - 1, true); - draw_line(638, 0, 638, 480 - 1, true); // last pixel of a line can't be set, see set_pixel() - - // cat eye - for (uint16_t x = 0; x < 640; x += 20) - { - draw_line(x, 0, 0, (480 - x * 480 / 640), true); - draw_line(640, x * 480 / 640, 640 - x, 480, true); - } - - // string - draw_string(200, 200, "GLG PROGRAMS"); -} - int main() { setup_clocks();