Compare commits
No commits in common. "for-rp2-doom" and "main" have entirely different histories.
for-rp2-do
...
main
8 changed files with 551 additions and 127 deletions
|
|
@ -15,7 +15,7 @@ set(CMAKE_CXX_STANDARD 17)
|
|||
# Initialize the SDK
|
||||
pico_sdk_init()
|
||||
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
|
||||
include(pico_dvhstx.cmake)
|
||||
|
|
|
|||
|
|
@ -38,13 +38,6 @@ Wire up your DVI breakout as follows:
|
|||
|
||||
If using jumper jerky, twist the - and + wires for each signal together to help with signal integrity.
|
||||
|
||||
Other pinouts can be used by passing a `pinout` parameter to the `init`
|
||||
function.
|
||||
This pinout consists of 4 numbers giving the *positive* pin in each differential pair, in the order CK, D0, D1, D2, D3, using GPIO numbering.
|
||||
The default pinout is written `{13, 15, 17, 19}`.
|
||||
Only pin numbers from 12 to 20 are valid, as other pins are not connected to the HSTX peripheral.
|
||||
Using invalid pin numbers is an undignosed error.
|
||||
|
||||
TODO
|
||||
|
||||
## C/C++ Resources
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ target_sources(${DRIVER_NAME} INTERFACE
|
|||
|
||||
# Enforce consistent compile options.
|
||||
# For the moment, don't use -O3 options that increase code size significantly
|
||||
target_compile_options(${DRIVER_NAME} INTERFACE -O2 -fgcse-after-reload -floop-interchange -fpeel-loops -fpredictive-commoning -fsplit-paths -ftree-loop-distribute-patterns -ftree-loop-distribution -ftree-vectorize -ftree-partial-pre -funswitch-loops)
|
||||
target_compile_options(${DRIVER_NAME} INTERFACE -Wall -Werror -O2 -fgcse-after-reload -floop-interchange -fpeel-loops -fpredictive-commoning -fsplit-paths -ftree-loop-distribute-patterns -ftree-loop-distribution -ftree-vectorize -ftree-partial-pre -funswitch-loops)
|
||||
|
||||
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ using namespace pimoroni;
|
|||
#define FRAME_BUFFER_SIZE (640*360)
|
||||
__attribute__((section(".uninitialized_data"))) static uint8_t frame_buffer_a[FRAME_BUFFER_SIZE];
|
||||
__attribute__((section(".uninitialized_data"))) static uint8_t frame_buffer_b[FRAME_BUFFER_SIZE];
|
||||
__attribute__((section(".uninitialized_data"))) RGB888 *global_palette[PALETTE_SIZE];
|
||||
#endif
|
||||
|
||||
#include "font.h"
|
||||
|
|
@ -42,7 +41,6 @@ extern "C" {
|
|||
void dvhstx_debug(const char *fmt, ...);
|
||||
}
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#define dvhstx_debug printf
|
||||
#endif
|
||||
|
||||
|
|
@ -103,6 +101,24 @@ static const uint32_t vactive_line_header_src[] = {
|
|||
};
|
||||
static uint32_t vactive_line_header[count_of(vactive_line_header_src)];
|
||||
|
||||
static const uint32_t vactive_text_line_header_src[] = {
|
||||
HSTX_CMD_RAW_REPEAT,
|
||||
SYNC_V1_H1,
|
||||
HSTX_CMD_RAW_REPEAT,
|
||||
SYNC_V1_H0,
|
||||
HSTX_CMD_RAW_REPEAT,
|
||||
SYNC_V1_H1,
|
||||
HSTX_CMD_RAW | 6,
|
||||
BLACK_PIXEL_A,
|
||||
BLACK_PIXEL_B,
|
||||
BLACK_PIXEL_A,
|
||||
BLACK_PIXEL_B,
|
||||
BLACK_PIXEL_A,
|
||||
BLACK_PIXEL_B,
|
||||
HSTX_CMD_TMDS
|
||||
};
|
||||
static uint32_t vactive_text_line_header[count_of(vactive_text_line_header_src)];
|
||||
|
||||
#define NUM_FRAME_LINES 2
|
||||
#define NUM_CHANS 3
|
||||
|
||||
|
|
@ -142,8 +158,56 @@ void __scratch_x("display") DVHSTX::gfx_dma_handler() {
|
|||
line_num = new_line_num;
|
||||
uint32_t* dst_ptr = &line_buffers[line_num * line_buf_total_len + count_of(vactive_line_header)];
|
||||
|
||||
if (callback) {
|
||||
callback(cb_data, y, dst_ptr);
|
||||
if (line_bytes_per_pixel == 2) {
|
||||
uint16_t* src_ptr = (uint16_t*)&frame_buffer_display[y * 2 * (timing_mode->h_active_pixels >> h_repeat_shift)];
|
||||
if (h_repeat_shift == 2) {
|
||||
for (int i = 0; i < timing_mode->h_active_pixels >> 1; i += 2) {
|
||||
uint32_t val = (uint32_t)(*src_ptr++) * 0x10001;
|
||||
*dst_ptr++ = val;
|
||||
*dst_ptr++ = val;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < timing_mode->h_active_pixels >> 1; ++i) {
|
||||
uint32_t val = (uint32_t)(*src_ptr++) * 0x10001;
|
||||
*dst_ptr++ = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (line_bytes_per_pixel == 1) {
|
||||
uint8_t* src_ptr = &frame_buffer_display[y * (timing_mode->h_active_pixels >> h_repeat_shift)];
|
||||
if (h_repeat_shift == 2) {
|
||||
for (int i = 0; i < timing_mode->h_active_pixels >> 2; ++i) {
|
||||
uint32_t val = (uint32_t)(*src_ptr++) * 0x01010101;
|
||||
*dst_ptr++ = val;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < timing_mode->h_active_pixels >> 2; ++i) {
|
||||
uint32_t val = ((uint32_t)(*src_ptr++) * 0x0101);
|
||||
val |= ((uint32_t)(*src_ptr++) * 0x01010000);
|
||||
*dst_ptr++ = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (line_bytes_per_pixel == 4) {
|
||||
uint8_t* src_ptr = &frame_buffer_display[y * (timing_mode->h_active_pixels >> h_repeat_shift)];
|
||||
if (h_repeat_shift == 2) {
|
||||
for (int i = 0; i < timing_mode->h_active_pixels; i += 4) {
|
||||
uint32_t val = display_palette[*src_ptr++];
|
||||
*dst_ptr++ = val;
|
||||
*dst_ptr++ = val;
|
||||
*dst_ptr++ = val;
|
||||
*dst_ptr++ = val;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < timing_mode->h_active_pixels; i += 2) {
|
||||
uint32_t val = display_palette[*src_ptr++];
|
||||
*dst_ptr++ = val;
|
||||
*dst_ptr++ = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -151,6 +215,192 @@ void __scratch_x("display") DVHSTX::gfx_dma_handler() {
|
|||
if (++v_scanline == v_total_active_lines) {
|
||||
v_scanline = 0;
|
||||
line_num = -1;
|
||||
if (flip_next) {
|
||||
flip_next = false;
|
||||
display->flip_now();
|
||||
}
|
||||
__sev();
|
||||
}
|
||||
}
|
||||
|
||||
void __scratch_x("display") dma_irq_handler_text() {
|
||||
display->text_dma_handler();
|
||||
}
|
||||
|
||||
void __scratch_x("display") DVHSTX::text_dma_handler() {
|
||||
// ch_num indicates the channel that just finished, which is the one
|
||||
// we're about to reload.
|
||||
dma_channel_hw_t *ch = &dma_hw->ch[ch_num];
|
||||
dma_hw->intr = 1u << ch_num;
|
||||
if (++ch_num == NUM_CHANS) ch_num = 0;
|
||||
|
||||
if (v_scanline >= timing_mode->v_front_porch && v_scanline < (timing_mode->v_front_porch + timing_mode->v_sync_width)) {
|
||||
ch->read_addr = (uintptr_t)vblank_line_vsync_on;
|
||||
ch->transfer_count = count_of(vblank_line_vsync_on);
|
||||
} else if (v_scanline < v_inactive_total) {
|
||||
ch->read_addr = (uintptr_t)vblank_line_vsync_off;
|
||||
ch->transfer_count = count_of(vblank_line_vsync_off);
|
||||
} else {
|
||||
const int y = (v_scanline - v_inactive_total);
|
||||
const uint line_buf_total_len = (frame_width * line_bytes_per_pixel + 3) / 4 + count_of(vactive_text_line_header);
|
||||
|
||||
ch->read_addr = (uintptr_t)&line_buffers[ch_num * line_buf_total_len];
|
||||
ch->transfer_count = line_buf_total_len;
|
||||
|
||||
// Fill line buffer
|
||||
int char_y = y % 24;
|
||||
if (line_bytes_per_pixel == 4) {
|
||||
uint32_t* dst_ptr = &line_buffers[ch_num * line_buf_total_len + count_of(vactive_text_line_header)];
|
||||
uint8_t* src_ptr = &frame_buffer_display[(y / 24) * frame_width];
|
||||
for (int i = 0; i < frame_width; ++i) {
|
||||
*dst_ptr++ = render_char_line(*src_ptr++, char_y);
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint8_t* dst_ptr = (uint8_t*)&line_buffers[ch_num * line_buf_total_len + count_of(vactive_text_line_header)];
|
||||
uint8_t* src_ptr = &frame_buffer_display[(y / 24) * frame_width];
|
||||
uint8_t* colour_ptr = src_ptr + frame_width * frame_height;
|
||||
#ifdef __riscv
|
||||
for (int i = 0; i < frame_width; ++i) {
|
||||
const uint8_t c = (*src_ptr++ - 0x20);
|
||||
uint32_t bits = (c < 95) ? font_cache[c * 24 + char_y] : 0;
|
||||
const uint8_t colour = *colour_ptr++;
|
||||
|
||||
*dst_ptr++ = colour * ((bits >> 24) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 22) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 20) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 18) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 16) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 14) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 12) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 10) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 8) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 6) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 4) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 2) & 3);
|
||||
*dst_ptr++ = colour * (bits & 3);
|
||||
*dst_ptr++ = 0;
|
||||
}
|
||||
#else
|
||||
int i = 0;
|
||||
for (; i < frame_width-1; i += 2) {
|
||||
uint8_t c = (*src_ptr++ - 0x20);
|
||||
uint32_t bits = (c < 95) ? font_cache[c * 24 + char_y] : 0;
|
||||
uint8_t colour = *colour_ptr++;
|
||||
c = (*src_ptr++ - 0x20);
|
||||
uint32_t bits2 = (c < 95) ? font_cache[c * 24 + char_y] : 0;
|
||||
uint8_t colour2 = *colour_ptr++;
|
||||
|
||||
// This ASM works around a compiler bug where the optimizer decides
|
||||
// to unroll so hard it spills to the stack.
|
||||
uint32_t tmp, tmp2;
|
||||
asm volatile (
|
||||
"ubfx %[tmp], %[cbits], #24, #2\n\t"
|
||||
"ubfx %[tmp2], %[cbits], #22, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #8, #8\n\t"
|
||||
"ubfx %[tmp2], %[cbits], #20, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #16, #8\n\t"
|
||||
"ubfx %[tmp2], %[cbits], #18, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #24, #8\n\t"
|
||||
"muls %[tmp], %[colour], %[tmp]\n\t"
|
||||
"str %[tmp], [%[dst_ptr]]\n\t"
|
||||
|
||||
"ubfx %[tmp], %[cbits], #16, #2\n\t"
|
||||
"ubfx %[tmp2], %[cbits], #14, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #8, #8\n\t"
|
||||
"ubfx %[tmp2], %[cbits], #12, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #16, #8\n\t"
|
||||
"ubfx %[tmp2], %[cbits], #10, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #24, #8\n\t"
|
||||
"muls %[tmp], %[colour], %[tmp]\n\t"
|
||||
"str %[tmp], [%[dst_ptr], #4]\n\t"
|
||||
|
||||
"ubfx %[tmp], %[cbits], #8, #2\n\t"
|
||||
"ubfx %[tmp2], %[cbits], #6, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #8, #8\n\t"
|
||||
"ubfx %[tmp2], %[cbits], #4, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #16, #8\n\t"
|
||||
"ubfx %[tmp2], %[cbits], #2, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #24, #8\n\t"
|
||||
"muls %[tmp], %[colour], %[tmp]\n\t"
|
||||
"str %[tmp], [%[dst_ptr], #8]\n\t"
|
||||
|
||||
"ubfx %[tmp], %[cbits2], #24, #2\n\t"
|
||||
"ubfx %[tmp2], %[cbits2], #22, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #8, #8\n\t"
|
||||
"muls %[tmp], %[colour2], %[tmp]\n\t"
|
||||
"and %[tmp2], %[cbits], #3\n\t"
|
||||
"muls %[tmp2], %[colour], %[tmp2]\n\t"
|
||||
"bfi %[tmp2], %[tmp], #16, #16\n\t"
|
||||
"str %[tmp2], [%[dst_ptr], #12]\n\t"
|
||||
|
||||
"ubfx %[tmp], %[cbits2], #20, #2\n\t"
|
||||
"ubfx %[tmp2], %[cbits2], #18, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #8, #8\n\t"
|
||||
"ubfx %[tmp2], %[cbits2], #16, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #16, #8\n\t"
|
||||
"ubfx %[tmp2], %[cbits2], #14, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #24, #8\n\t"
|
||||
"muls %[tmp], %[colour2], %[tmp]\n\t"
|
||||
"str %[tmp], [%[dst_ptr], #16]\n\t"
|
||||
|
||||
"ubfx %[tmp], %[cbits2], #12, #2\n\t"
|
||||
"ubfx %[tmp2], %[cbits2], #10, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #8, #8\n\t"
|
||||
"ubfx %[tmp2], %[cbits2], #8, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #16, #8\n\t"
|
||||
"ubfx %[tmp2], %[cbits2], #6, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #24, #8\n\t"
|
||||
"muls %[tmp], %[colour2], %[tmp]\n\t"
|
||||
"str %[tmp], [%[dst_ptr], #20]\n\t"
|
||||
|
||||
"ubfx %[tmp], %[cbits2], #4, #2\n\t"
|
||||
"ubfx %[tmp2], %[cbits2], #2, #2\n\t"
|
||||
"bfi %[tmp], %[tmp2], #8, #8\n\t"
|
||||
"bfi %[tmp], %[cbits2], #16, #2\n\t"
|
||||
"muls %[tmp], %[colour2], %[tmp]\n\t"
|
||||
"str %[tmp], [%[dst_ptr], #24]\n\t"
|
||||
: [tmp] "=&l" (tmp),
|
||||
[tmp2] "=&l" (tmp2)
|
||||
: [cbits] "r" (bits),
|
||||
[colour] "l" (colour),
|
||||
[cbits2] "r" (bits2),
|
||||
[colour2] "l" (colour2),
|
||||
[dst_ptr] "r" (dst_ptr)
|
||||
: "cc", "memory" );
|
||||
dst_ptr += 14 * 2;
|
||||
}
|
||||
if (i != frame_width) {
|
||||
const uint8_t c = (*src_ptr++ - 0x20);
|
||||
uint32_t bits = (c < 95) ? font_cache[c * 24 + char_y] : 0;
|
||||
const uint8_t colour = *colour_ptr++;
|
||||
|
||||
*dst_ptr++ = colour * ((bits >> 24) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 22) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 20) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 18) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 16) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 14) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 12) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 10) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 8) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 6) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 4) & 3);
|
||||
*dst_ptr++ = colour * ((bits >> 2) & 3);
|
||||
*dst_ptr++ = colour * (bits & 3);
|
||||
*dst_ptr++ = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (++v_scanline == v_total_active_lines) {
|
||||
v_scanline = 0;
|
||||
line_num = -1;
|
||||
if (flip_next) {
|
||||
flip_next = false;
|
||||
display->flip_now();
|
||||
}
|
||||
__sev();
|
||||
}
|
||||
}
|
||||
|
|
@ -269,19 +519,94 @@ void DVHSTX::display_setup_clock() {
|
|||
freq, freq);
|
||||
}
|
||||
|
||||
void DVHSTX::write_pixel(const Point &p, uint16_t colour)
|
||||
{
|
||||
*point_to_ptr16(p) = colour;
|
||||
}
|
||||
|
||||
void DVHSTX::write_pixel_span(const Point &p, uint l, uint16_t colour)
|
||||
{
|
||||
uint16_t* ptr = point_to_ptr16(p);
|
||||
for (uint i = 0; i < l; ++i) ptr[i] = colour;
|
||||
}
|
||||
|
||||
void DVHSTX::write_pixel_span(const Point &p, uint l, uint16_t *data)
|
||||
{
|
||||
uint16_t* ptr = point_to_ptr16(p);
|
||||
for (uint i = 0; i < l; ++i) ptr[i] = data[i];
|
||||
}
|
||||
|
||||
void DVHSTX::read_pixel_span(const Point &p, uint l, uint16_t *data)
|
||||
{
|
||||
const uint16_t* ptr = point_to_ptr16(p);
|
||||
for (uint i = 0; i < l; ++i) data[i] = ptr[i];
|
||||
}
|
||||
|
||||
void DVHSTX::set_palette(RGB888 new_palette[PALETTE_SIZE])
|
||||
{
|
||||
memcpy(palette, new_palette, PALETTE_SIZE * sizeof(RGB888));
|
||||
}
|
||||
|
||||
void DVHSTX::set_palette_colour(uint8_t entry, RGB888 colour)
|
||||
{
|
||||
palette[entry] = colour;
|
||||
}
|
||||
|
||||
RGB888* DVHSTX::get_palette()
|
||||
{
|
||||
return palette;
|
||||
}
|
||||
|
||||
void DVHSTX::write_palette_pixel(const Point &p, uint8_t colour)
|
||||
{
|
||||
*point_to_ptr_palette(p) = colour;
|
||||
}
|
||||
|
||||
void DVHSTX::write_palette_pixel_span(const Point &p, uint l, uint8_t colour)
|
||||
{
|
||||
uint8_t* ptr = point_to_ptr_palette(p);
|
||||
memset(ptr, colour, l);
|
||||
}
|
||||
|
||||
void DVHSTX::write_palette_pixel_span(const Point &p, uint l, uint8_t* data)
|
||||
{
|
||||
uint8_t* ptr = point_to_ptr_palette(p);
|
||||
memcpy(ptr, data, l);
|
||||
}
|
||||
|
||||
void DVHSTX::read_palette_pixel_span(const Point &p, uint l, uint8_t *data)
|
||||
{
|
||||
const uint8_t* ptr = point_to_ptr_palette(p);
|
||||
memcpy(data, ptr, l);
|
||||
}
|
||||
|
||||
void DVHSTX::write_text(const Point &p, const char* text, TextColour colour, bool immediate)
|
||||
{
|
||||
char* ptr = (char*)point_to_ptr_text(p, immediate);
|
||||
int len = std::min((int)(frame_width - p.x), (int)strlen(text));
|
||||
memcpy(ptr, text, len);
|
||||
if (mode == MODE_TEXT_RGB111) memset(ptr + frame_width * frame_height, (uint8_t)colour, len);
|
||||
}
|
||||
|
||||
void DVHSTX::clear()
|
||||
{
|
||||
memset(frame_buffer_back, 0, frame_width * frame_height * frame_bytes_per_pixel);
|
||||
}
|
||||
|
||||
DVHSTX::DVHSTX()
|
||||
{
|
||||
// Always use the bottom channels
|
||||
dma_claim_mask((1 << NUM_CHANS) - 1);
|
||||
}
|
||||
|
||||
bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, Pinout pinout)
|
||||
bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_)
|
||||
{
|
||||
if (inited) reset();
|
||||
|
||||
ch_num = 0;
|
||||
line_num = -1;
|
||||
v_scanline = 2;
|
||||
flip_next = false;
|
||||
|
||||
display_width = width;
|
||||
display_height = height;
|
||||
|
|
@ -290,7 +615,18 @@ bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, Pinout pinout)
|
|||
mode = mode_;
|
||||
|
||||
timing_mode = nullptr;
|
||||
if (width == 320 && height == 180) {
|
||||
if (mode == MODE_TEXT_MONO || mode == MODE_TEXT_RGB111) {
|
||||
width = 1280;
|
||||
height = 720;
|
||||
display_width = 91;
|
||||
frame_width = 91;
|
||||
display_height = 30;
|
||||
frame_height = 30;
|
||||
h_repeat_shift = 0;
|
||||
v_repeat_shift = 0;
|
||||
timing_mode = &dvi_timing_1280x720p_rb_50hz;
|
||||
}
|
||||
else if (width == 320 && height == 180) {
|
||||
h_repeat_shift = 2;
|
||||
v_repeat_shift = 2;
|
||||
timing_mode = &dvi_timing_1280x720p_rb_50hz;
|
||||
|
|
@ -307,8 +643,8 @@ bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, Pinout pinout)
|
|||
}
|
||||
else
|
||||
{
|
||||
volatile uint16_t full_width = display_width;
|
||||
volatile uint16_t full_height = display_height;
|
||||
uint16_t full_width = display_width;
|
||||
uint16_t full_height = display_height;
|
||||
h_repeat_shift = 0;
|
||||
v_repeat_shift = 0;
|
||||
|
||||
|
|
@ -325,9 +661,6 @@ bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, Pinout pinout)
|
|||
if (full_width == 640) {
|
||||
if (full_height == 480) timing_mode = &dvi_timing_640x480p_60hz;
|
||||
}
|
||||
else if (full_width == 1280 && full_height == 720) {
|
||||
timing_mode = &dvi_timing_1280x720p_rb_50hz;
|
||||
}
|
||||
else if (full_width == 720) {
|
||||
if (full_height == 480) timing_mode = &dvi_timing_720x480p_60hz;
|
||||
else if (full_height == 400) timing_mode = &dvi_timing_720x400p_70hz;
|
||||
|
|
@ -348,11 +681,11 @@ bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, Pinout pinout)
|
|||
|
||||
if (!timing_mode) {
|
||||
dvhstx_debug("Unsupported resolution %dx%d", width, height);
|
||||
__builtin_trap();
|
||||
return false;
|
||||
}
|
||||
|
||||
display = this;
|
||||
display_palette = get_palette();
|
||||
|
||||
dvhstx_debug("Setup clock\n");
|
||||
display_setup_clock();
|
||||
|
|
@ -383,28 +716,78 @@ bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, Pinout pinout)
|
|||
vactive_line_header[4] |= timing_mode->h_back_porch;
|
||||
vactive_line_header[6] |= timing_mode->h_active_pixels;
|
||||
|
||||
memcpy(vactive_text_line_header, vactive_text_line_header_src, sizeof(vactive_text_line_header_src));
|
||||
vactive_text_line_header[0] |= timing_mode->h_front_porch;
|
||||
vactive_text_line_header[2] |= timing_mode->h_sync_width;
|
||||
vactive_text_line_header[4] |= timing_mode->h_back_porch;
|
||||
vactive_text_line_header[7+6] |= timing_mode->h_active_pixels - 6;
|
||||
|
||||
switch (mode) {
|
||||
case MODE_LINE_CALLBACK_RGB565:
|
||||
case MODE_RGB565:
|
||||
frame_bytes_per_pixel = 2;
|
||||
line_bytes_per_pixel = 2;
|
||||
break;
|
||||
case MODE_LINE_CALLBACK_RGB888:
|
||||
case MODE_PALETTE:
|
||||
frame_bytes_per_pixel = 1;
|
||||
line_bytes_per_pixel = 4;
|
||||
break;
|
||||
case MODE_RGB888:
|
||||
frame_bytes_per_pixel = 4;
|
||||
line_bytes_per_pixel = 4;
|
||||
break;
|
||||
case MODE_TEXT_MONO:
|
||||
frame_bytes_per_pixel = 1;
|
||||
line_bytes_per_pixel = 4;
|
||||
break;
|
||||
case MODE_TEXT_RGB111:
|
||||
frame_bytes_per_pixel = 2;
|
||||
line_bytes_per_pixel = 14;
|
||||
break;
|
||||
default:
|
||||
dvhstx_debug("Unsupported mode %d", (int)mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef MICROPY_BUILD_TYPE
|
||||
if (frame_width * frame_height * frame_bytes_per_pixel > sizeof(frame_buffer_a)) {
|
||||
panic("Frame buffer too large");
|
||||
}
|
||||
|
||||
frame_buffer_display = frame_buffer_a;
|
||||
frame_buffer_back = frame_buffer_b;
|
||||
#else
|
||||
frame_buffer_display = (uint8_t*)malloc(frame_width * frame_height * frame_bytes_per_pixel);
|
||||
frame_buffer_back = (uint8_t*)malloc(frame_width * frame_height * frame_bytes_per_pixel);
|
||||
#endif
|
||||
memset(frame_buffer_display, 0, frame_width * frame_height * frame_bytes_per_pixel);
|
||||
memset(frame_buffer_back, 0, frame_width * frame_height * frame_bytes_per_pixel);
|
||||
|
||||
memset(palette, 0, PALETTE_SIZE * sizeof(palette[0]));
|
||||
|
||||
frame_buffer_display = frame_buffer_display;
|
||||
dvhstx_debug("Frame buffers inited\n");
|
||||
|
||||
const bool is_text_mode = (mode == MODE_TEXT_MONO || mode == MODE_TEXT_RGB111);
|
||||
const int frame_pixel_words = (frame_width * h_repeat * line_bytes_per_pixel + 3) >> 2;
|
||||
const int frame_line_words = frame_pixel_words + count_of(vactive_line_header);
|
||||
const int frame_line_words = frame_pixel_words + (is_text_mode ? count_of(vactive_text_line_header) : count_of(vactive_line_header));
|
||||
const int frame_lines = (v_repeat == 1) ? NUM_CHANS : NUM_FRAME_LINES;
|
||||
line_buffers = (uint32_t*)malloc(frame_line_words * 4 * frame_lines);
|
||||
|
||||
for (int i = 0; i < frame_lines; ++i)
|
||||
{
|
||||
memcpy(&line_buffers[i * frame_line_words], vactive_line_header, count_of(vactive_line_header) * sizeof(uint32_t));
|
||||
if (is_text_mode) memcpy(&line_buffers[i * frame_line_words], vactive_text_line_header, count_of(vactive_text_line_header) * sizeof(uint32_t));
|
||||
else memcpy(&line_buffers[i * frame_line_words], vactive_line_header, count_of(vactive_line_header) * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
if (mode == MODE_TEXT_RGB111) {
|
||||
// Need to pre-render the font to RAM to be fast enough.
|
||||
font_cache = (uint32_t*)malloc(4 * FONT->line_height * 95);
|
||||
uint32_t* font_cache_ptr = font_cache;
|
||||
for (int c = 0x20; c < 128; ++c) {
|
||||
for (int y = 0; y < FONT->line_height; ++y) {
|
||||
*font_cache_ptr++ = render_char_line(c, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure HSTX FIFO is clear
|
||||
|
|
@ -414,7 +797,7 @@ bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, Pinout pinout)
|
|||
sleep_us(10);
|
||||
|
||||
switch (mode) {
|
||||
case MODE_LINE_CALLBACK_RGB565:
|
||||
case MODE_RGB565:
|
||||
// Configure HSTX's TMDS encoder for RGB565
|
||||
hstx_ctrl_hw->expand_tmds =
|
||||
4 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |
|
||||
|
|
@ -433,7 +816,7 @@ bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, Pinout pinout)
|
|||
0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB;
|
||||
break;
|
||||
|
||||
case MODE_LINE_CALLBACK_RGB888:
|
||||
case MODE_PALETTE:
|
||||
// Configure HSTX's TMDS encoder for RGB888
|
||||
hstx_ctrl_hw->expand_tmds =
|
||||
7 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |
|
||||
|
|
@ -452,6 +835,44 @@ bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, Pinout pinout)
|
|||
0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB;
|
||||
break;
|
||||
|
||||
case MODE_TEXT_MONO:
|
||||
// Configure HSTX's TMDS encoder for 2bpp
|
||||
hstx_ctrl_hw->expand_tmds =
|
||||
1 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |
|
||||
18 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB |
|
||||
1 << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB |
|
||||
18 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB |
|
||||
1 << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB |
|
||||
18 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB;
|
||||
|
||||
// Pixels and control symbols (RAW) are an
|
||||
// entire 32-bit word.
|
||||
hstx_ctrl_hw->expand_shift =
|
||||
14 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB |
|
||||
30 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB |
|
||||
1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB |
|
||||
0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB;
|
||||
break;
|
||||
|
||||
case MODE_TEXT_RGB111:
|
||||
// Configure HSTX's TMDS encoder for RGB222
|
||||
hstx_ctrl_hw->expand_tmds =
|
||||
1 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |
|
||||
0 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB |
|
||||
1 << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB |
|
||||
29 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB |
|
||||
1 << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB |
|
||||
26 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB;
|
||||
|
||||
// Pixels (TMDS) come in 4 8-bit chunks. Control symbols (RAW) are an
|
||||
// entire 32-bit word.
|
||||
hstx_ctrl_hw->expand_shift =
|
||||
4 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB |
|
||||
8 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB |
|
||||
1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB |
|
||||
0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB;
|
||||
break;
|
||||
|
||||
default:
|
||||
dvhstx_debug("Unsupported mode %d", (int)mode);
|
||||
return false;
|
||||
|
|
@ -468,28 +889,24 @@ bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, Pinout pinout)
|
|||
HSTX_CTRL_CSR_EN_BITS;
|
||||
|
||||
// HSTX outputs 0 through 7 appear on GPIO 12 through 19.
|
||||
constexpr int HSTX_FIRST_PIN = 12;
|
||||
|
||||
// Assign clock pair to two neighbouring pins:
|
||||
{
|
||||
int bit = pinout.clk_p - HSTX_FIRST_PIN;
|
||||
hstx_ctrl_hw->bit[bit ] = HSTX_CTRL_BIT0_CLK_BITS;
|
||||
hstx_ctrl_hw->bit[bit ^ 1] = HSTX_CTRL_BIT0_CLK_BITS | HSTX_CTRL_BIT0_INV_BITS;
|
||||
}
|
||||
|
||||
hstx_ctrl_hw->bit[1] = HSTX_CTRL_BIT0_CLK_BITS;
|
||||
hstx_ctrl_hw->bit[0] = HSTX_CTRL_BIT0_CLK_BITS | HSTX_CTRL_BIT0_INV_BITS;
|
||||
for (uint lane = 0; lane < 3; ++lane) {
|
||||
// For each TMDS lane, assign it to the correct GPIO pair based on the
|
||||
// desired pinout:
|
||||
int bit = pinout.rgb_p[lane] - HSTX_FIRST_PIN;
|
||||
static const int lane_to_output_bit[3] = {2, 4, 6};
|
||||
int bit = lane_to_output_bit[lane];
|
||||
// Output even bits during first half of each HSTX cycle, and odd bits
|
||||
// during second half. The shifter advances by two bits each cycle.
|
||||
uint32_t lane_data_sel_bits =
|
||||
(lane * 10 ) << HSTX_CTRL_BIT0_SEL_P_LSB |
|
||||
(lane * 10 + 1) << HSTX_CTRL_BIT0_SEL_N_LSB;
|
||||
// The two halves of each pair get identical data, but one pin is inverted.
|
||||
hstx_ctrl_hw->bit[bit ] = lane_data_sel_bits;
|
||||
hstx_ctrl_hw->bit[bit ^ 1] = lane_data_sel_bits | HSTX_CTRL_BIT0_INV_BITS;
|
||||
}
|
||||
hstx_ctrl_hw->bit[bit ] = lane_data_sel_bits | HSTX_CTRL_BIT0_INV_BITS;
|
||||
hstx_ctrl_hw->bit[bit + 1] = lane_data_sel_bits;
|
||||
}
|
||||
|
||||
for (int i = 12; i <= 19; ++i) {
|
||||
gpio_set_function(i, GPIO_FUNC_HSTX);
|
||||
|
|
@ -545,13 +962,20 @@ bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, Pinout pinout)
|
|||
dma_hw->intr = (1 << NUM_CHANS) - 1;
|
||||
dma_hw->ints2 = (1 << NUM_CHANS) - 1;
|
||||
dma_hw->inte2 = (1 << NUM_CHANS) - 1;
|
||||
irq_set_exclusive_handler(DMA_IRQ_2, dma_irq_handler);
|
||||
if (is_text_mode) irq_set_exclusive_handler(DMA_IRQ_2, dma_irq_handler_text);
|
||||
else irq_set_exclusive_handler(DMA_IRQ_2, dma_irq_handler);
|
||||
irq_set_enabled(DMA_IRQ_2, true);
|
||||
|
||||
dma_channel_start(0);
|
||||
|
||||
dvhstx_debug("DVHSTX started\n");
|
||||
|
||||
for (int i = 0; i < frame_height; ++i) {
|
||||
memset(&frame_buffer_display[i * frame_width * frame_bytes_per_pixel], i, frame_width * frame_bytes_per_pixel);
|
||||
}
|
||||
|
||||
dvhstx_debug("Frame buffer filled\n");
|
||||
|
||||
inited = true;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -568,9 +992,35 @@ void DVHSTX::reset() {
|
|||
for (int i = 0; i < NUM_CHANS; ++i)
|
||||
dma_channel_abort(i);
|
||||
|
||||
if (font_cache) {
|
||||
free(font_cache);
|
||||
font_cache = nullptr;
|
||||
}
|
||||
free(line_buffers);
|
||||
|
||||
#ifndef MICROPY_BUILD_TYPE
|
||||
free(frame_buffer_display);
|
||||
free(frame_buffer_back);
|
||||
#endif
|
||||
}
|
||||
|
||||
int DVHSTX::get_h_active_pixels() const {
|
||||
return timing_mode->h_active_pixels;
|
||||
void DVHSTX::flip_blocking() {
|
||||
wait_for_vsync();
|
||||
flip_now();
|
||||
}
|
||||
|
||||
void DVHSTX::flip_now() {
|
||||
std::swap(frame_buffer_display, frame_buffer_back);
|
||||
}
|
||||
|
||||
void DVHSTX::wait_for_vsync() {
|
||||
while (v_scanline >= timing_mode->v_front_porch) __wfe();
|
||||
}
|
||||
|
||||
void DVHSTX::flip_async() {
|
||||
flip_next = true;
|
||||
}
|
||||
|
||||
void DVHSTX::wait_for_flip() {
|
||||
while (flip_next) __wfe();
|
||||
}
|
||||
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "common/pimoroni_common.hpp"
|
||||
#include "common/pimoroni_i2c.hpp"
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
|
||||
// DVI HSTX driver for use with Pimoroni PicoGraphics
|
||||
|
||||
|
|
@ -26,13 +29,12 @@ namespace pimoroni {
|
|||
public:
|
||||
static constexpr int PALETTE_SIZE = 256;
|
||||
|
||||
struct Pinout {
|
||||
uint8_t clk_p, rgb_p[3];
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
MODE_LINE_CALLBACK_RGB565,
|
||||
MODE_LINE_CALLBACK_RGB888,
|
||||
MODE_PALETTE = 2,
|
||||
MODE_RGB565 = 1,
|
||||
MODE_RGB888 = 3,
|
||||
MODE_TEXT_MONO = 4,
|
||||
MODE_TEXT_RGB111 = 5,
|
||||
};
|
||||
|
||||
enum TextColour {
|
||||
|
|
@ -59,7 +61,7 @@ namespace pimoroni {
|
|||
uint8_t frame_bytes_per_pixel = 2;
|
||||
uint8_t h_repeat = 4;
|
||||
uint8_t v_repeat = 4;
|
||||
Mode mode = MODE_LINE_CALLBACK_RGB565;
|
||||
Mode mode = MODE_RGB565;
|
||||
|
||||
public:
|
||||
DVHSTX();
|
||||
|
|
@ -68,21 +70,68 @@ namespace pimoroni {
|
|||
// Methods
|
||||
//--------------------------------------------------
|
||||
public:
|
||||
typedef void(*line_fun_t)(void *cb_data, int line_num, uint32_t *data);
|
||||
void set_callback(line_fun_t cb, void *data) {
|
||||
callback = cb;
|
||||
cb_data = data;
|
||||
}
|
||||
bool init(uint16_t width, uint16_t height, Mode mode, Pinout pinout);
|
||||
// 16bpp interface
|
||||
void write_pixel(const Point &p, uint16_t colour);
|
||||
void write_pixel_span(const Point &p, uint l, uint16_t colour);
|
||||
void write_pixel_span(const Point &p, uint l, uint16_t *data);
|
||||
void read_pixel_span(const Point &p, uint l, uint16_t *data);
|
||||
|
||||
// 256 colour palette mode.
|
||||
void set_palette(RGB888 new_palette[PALETTE_SIZE]);
|
||||
void set_palette_colour(uint8_t entry, RGB888 colour);
|
||||
RGB888* get_palette();
|
||||
|
||||
void write_palette_pixel(const Point &p, uint8_t colour);
|
||||
void write_palette_pixel_span(const Point &p, uint l, uint8_t colour);
|
||||
void write_palette_pixel_span(const Point &p, uint l, uint8_t* data);
|
||||
void read_palette_pixel_span(const Point &p, uint l, uint8_t *data);
|
||||
|
||||
// Text mode (91 x 30)
|
||||
// Immediate writes to the active buffer instead of the back buffer
|
||||
void write_text(const Point &p, const char* text, TextColour colour = TEXT_WHITE, bool immediate = false);
|
||||
|
||||
void clear();
|
||||
|
||||
bool init(uint16_t width, uint16_t height, Mode mode = MODE_RGB565);
|
||||
void reset();
|
||||
|
||||
int get_h_repeat_shift() const { return h_repeat_shift; }
|
||||
int get_h_active_pixels() const;
|
||||
// Wait for vsync and then flip the buffers
|
||||
void flip_blocking();
|
||||
|
||||
// Flip immediately without waiting for vsync
|
||||
void flip_now();
|
||||
|
||||
void wait_for_vsync();
|
||||
|
||||
// flip_async queues a flip to happen next vsync but returns without blocking.
|
||||
// You should call wait_for_flip before doing any more reads or writes, defining sprites, etc.
|
||||
void flip_async();
|
||||
void wait_for_flip();
|
||||
|
||||
// DMA handlers, should not be called externally
|
||||
void gfx_dma_handler();
|
||||
void gfx_dma_handler();
|
||||
void text_dma_handler();
|
||||
|
||||
private:
|
||||
RGB888 palette[PALETTE_SIZE];
|
||||
|
||||
uint8_t* frame_buffer_display;
|
||||
uint8_t* frame_buffer_back;
|
||||
uint32_t* font_cache = nullptr;
|
||||
|
||||
uint16_t* point_to_ptr16(const Point &p) const {
|
||||
return ((uint16_t*)frame_buffer_back) + (p.y * (uint32_t)frame_width) + p.x;
|
||||
}
|
||||
|
||||
uint8_t* point_to_ptr_palette(const Point &p) const {
|
||||
return frame_buffer_back + (p.y * (uint32_t)frame_width) + p.x;
|
||||
}
|
||||
|
||||
uint8_t* point_to_ptr_text(const Point &p, bool immediate) const {
|
||||
const uint32_t offset = (p.y * (uint32_t)frame_width) + p.x;
|
||||
if (immediate) return frame_buffer_display + offset;
|
||||
return frame_buffer_back + offset;
|
||||
}
|
||||
|
||||
void display_setup_clock();
|
||||
|
||||
|
|
@ -91,6 +140,7 @@ namespace pimoroni {
|
|||
int line_num = -1;
|
||||
|
||||
volatile int v_scanline = 2;
|
||||
volatile bool flip_next;
|
||||
|
||||
bool inited = false;
|
||||
|
||||
|
|
@ -104,8 +154,5 @@ namespace pimoroni {
|
|||
int line_bytes_per_pixel;
|
||||
|
||||
uint32_t* display_palette = nullptr;
|
||||
|
||||
line_fun_t callback = nullptr;
|
||||
void *cb_data = nullptr;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,3 @@
|
|||
add_executable(
|
||||
otf
|
||||
otf.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(otf pico_stdlib pico_multicore pico_dvhstx)
|
||||
pico_enable_stdio_usb(otf 1)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(otf)
|
||||
|
||||
add_executable(
|
||||
mandelbrot
|
||||
mandelbrot.cpp
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ void draw_mandel() {
|
|||
}
|
||||
|
||||
int main() {
|
||||
display.init(FRAME_WIDTH, FRAME_HEIGHT, DVHSTX::MODE_PALETTE, {13, 15, 17, 19});
|
||||
display.init(FRAME_WIDTH, FRAME_HEIGHT, DVHSTX::MODE_PALETTE);
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include "hardware/uart.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "drivers/dvhstx/dvhstx.hpp"
|
||||
#include "libraries/pico_graphics/pico_graphics_dvhstx.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "mandelf.h"
|
||||
}
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
#define FRAME_WIDTH 640
|
||||
#define FRAME_HEIGHT 240
|
||||
|
||||
static DVHSTX display;
|
||||
static PicoGraphics_PenDVHSTX_P8 graphics(FRAME_WIDTH, FRAME_HEIGHT, display);
|
||||
|
||||
inline constexpr uint32_t RGB_to_RGB888(const uint8_t r, const uint8_t g, const uint8_t b) {
|
||||
return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
||||
}
|
||||
|
||||
static uint32_t palette[256];
|
||||
static void init_palette() {
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
#if 0
|
||||
int h = i * (1.f / 255.f), s = 1.0f, v = 0.5f + (i & 7) * (0.5f / 7.f);
|
||||
RGB p = RGB::from_hsv(h, s, v);
|
||||
palette[i] = RGB_to_RGB888(p.r, p.g, p.b);
|
||||
#endif
|
||||
palette[i] = RGB_to_RGB888(i, i, i);
|
||||
}
|
||||
}
|
||||
|
||||
void gen_line(void *cb_data, int line_num, uint32_t *dest) {
|
||||
int y1 = line_num - FRAME_HEIGHT / 2;
|
||||
int ysq = y1*y1 * 4;
|
||||
for(int h=0; h<FRAME_WIDTH; h++) {
|
||||
int x = h - FRAME_WIDTH / 2;
|
||||
int r2 = x*x + ysq;
|
||||
#define LIM (320*320)
|
||||
*dest++ = palette[r2 / 256 % 256];
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
display.set_callbacks(gen_line, &display);
|
||||
display.init(FRAME_WIDTH, FRAME_HEIGHT, MODE_LINE_CALLBACK_RGB565, {13, 15, 17, 19});
|
||||
init_palette();
|
||||
|
||||
while(true) {
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue