Refactoring in multiple independent modules.
This commit is contained in:
parent
8af09d4a6e
commit
fa0734fec0
@ -18,6 +18,8 @@ add_compile_options(-Wall
|
|||||||
|
|
||||||
add_executable(ceda2vga
|
add_executable(ceda2vga
|
||||||
src/main.c
|
src/main.c
|
||||||
|
src/draw_demo.c
|
||||||
|
src/framebuffer.c
|
||||||
)
|
)
|
||||||
|
|
||||||
pico_generate_pio_header(ceda2vga
|
pico_generate_pio_header(ceda2vga
|
||||||
|
170
src/draw_demo.c
Normal file
170
src/draw_demo.c
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#include "draw_demo.h"
|
||||||
|
|
||||||
|
#include "framebuffer.h"
|
||||||
|
#include "pico/platform.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
30
src/draw_demo.h
Normal file
30
src/draw_demo.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef DRAW_DEMO_H
|
||||||
|
#define DRAW_DEMO_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
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
|
8
src/framebuffer.c
Normal file
8
src/framebuffer.c
Normal file
@ -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};
|
16
src/framebuffer.h
Normal file
16
src/framebuffer.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef FRAMEBUFFER_H
|
||||||
|
#define FRAMEBUFFER_H
|
||||||
|
|
||||||
|
#include <stdint.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
|
||||||
|
|
||||||
|
extern uint8_t frame[HPIXEL * (VSPULSE + VBBLANK + VPIXEL) * BPP / 8];
|
||||||
|
|
||||||
|
#endif
|
195
src/main.c
195
src/main.c
@ -16,27 +16,13 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "vga.pio.h"
|
#include "vga.pio.h"
|
||||||
|
|
||||||
#define HPIXEL 640 // number of pixels displayed horizontally
|
#include "draw_demo.h"
|
||||||
#define VPIXEL 480 // number of pixels displayed vertically
|
#include "framebuffer.h"
|
||||||
#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};
|
|
||||||
|
|
||||||
static void vga_pixel_program_init(PIO pio, uint sm, uint offset)
|
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;
|
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)
|
void new_frame_handler(void)
|
||||||
{
|
{
|
||||||
if (vga_pixel_ptr && dma_ready)
|
if (vga_pixel_ptr && dma_ready)
|
||||||
@ -253,95 +151,6 @@ void new_frame_handler(void)
|
|||||||
pio_interrupt_clear(pio0_hw, 1);
|
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()
|
int main()
|
||||||
{
|
{
|
||||||
setup_clocks();
|
setup_clocks();
|
||||||
|
Loading…
Reference in New Issue
Block a user