Initial commit

This commit is contained in:
Mike Bell 2024-08-10 13:04:03 +01:00
commit b0255f2c62
22 changed files with 3038 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build/*

20
CMakeLists.txt Normal file
View file

@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.12)
set(PICO_PLATFORM rp2350)
include(pico_sdk_import.cmake)
include(pimoroni_pico_import.cmake)
# Gooey boilerplate
project(pico_dvhstx_examples C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
# Initialize the SDK
pico_sdk_init()
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
include(pico_dvhstx.cmake)
add_subdirectory(examples/dvhstx)

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Michael Bell and Pimoroni Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

34
README.md Normal file
View file

@ -0,0 +1,34 @@
# DVI for HSTX <!-- omit in toc -->
This repository is home to the Pimoroni PicoGraphics compatible DVI driver for RP2 chips with HSTX (e.g. RP2350).
[![Build Status](https://img.shields.io/github/actions/workflow/status/MichaelBell/dvhstx/micropython.yml?branch=main&label=MicroPython)](https://github.com/MichaelBell/dvhstx/actions/workflows/micropython.yml)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/MichaelBell/dvhstx)](https://github.com/MichaelBell/dvhstx/releases/latest/)
- [Introduction](#introduction)
- [Download MicroPython](#download-micropython)
- [Documentation](#documentation)
- [C/C++ Resources](#cc-resources)
- [C/C++ Community Projects](#cc-community-projects)
## Introduction
DV HSTX will enable you to create big, bold audio visual projects using MicroPython and an HDMI display of your choice.
## Download MicroPython
TODO
* [DV HSTX MicroPython Releases](https://github.com/MichaelBell/dvhstx/releases)
## Documentation
TODO
## C/C++ Resources
* :link: [C++ Boilerplate](https://github.com/MichaelBell/dvhstx-boilerplate/)
## C/C++ Community Projects
TODO

View file

@ -0,0 +1 @@
include(dvhstx.cmake)

View file

@ -0,0 +1,16 @@
set(DRIVER_NAME dvhstx)
# main DV display driver
add_library(${DRIVER_NAME} INTERFACE)
target_sources(${DRIVER_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/dvi.cpp
${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp
${CMAKE_CURRENT_LIST_DIR}/intel_one_mono_2bpp.c
)
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
# Pull in pico libraries that we need
target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib)

874
drivers/dvhstx/dvhstx.cpp Normal file
View file

@ -0,0 +1,874 @@
#include <string.h>
#include <pico/stdlib.h>
extern "C" {
#include <pico/lock_core.h>
}
#include <algorithm>
#include "hardware/dma.h"
#include "hardware/gpio.h"
#include "hardware/irq.h"
#include "hardware/structs/bus_ctrl.h"
#include "hardware/structs/hstx_ctrl.h"
#include "hardware/structs/hstx_fifo.h"
#include "hardware/structs/sio.h"
#include "hardware/structs/ioqspi.h"
#include "hardware/vreg.h"
#include "hardware/structs/qmi.h"
#include "hardware/pll.h"
#include "hardware/clocks.h"
#include "dvi.hpp"
#include "dvhstx.hpp"
using namespace pimoroni;
#include "font.h"
// If changing the font, note this code will not handle glyphs wider than 13 pixels
#define FONT (&intel_one_mono)
static inline __attribute__((always_inline)) uint32_t render_char_line(int c, int y) {
if (c < 0x20 || c > 0x7e) return 0;
const lv_font_fmt_txt_glyph_dsc_t* g = &FONT->dsc->glyph_dsc[c - 0x20 + 1];
const uint8_t *b = FONT->dsc->glyph_bitmap + g->bitmap_index;
const int ey = y - FONT_HEIGHT + FONT->base_line + g->ofs_y + g->box_h;
if (ey < 0 || ey >= g->box_h || g->box_w == 0) {
return 0;
}
else {
int bi = (g->box_w * ey);
uint32_t bits = (b[bi >> 2] << 24) | (b[(bi >> 2) + 1] << 16) | (b[(bi >> 2) + 2] << 8) | b[(bi >> 2) + 3];
bits >>= 6 - ((bi & 3) << 1);
bits &= 0x3ffffff & (0x3ffffff << ((13 - g->box_w) << 1));
bits >>= g->ofs_x << 1;
return bits;
}
}
// ----------------------------------------------------------------------------
// HSTX command lists
// Lists are padded with NOPs to be >= HSTX FIFO size, to avoid DMA rapidly
// pingponging and tripping up the IRQs.
static uint32_t vblank_line_vsync_off[] = {
HSTX_CMD_RAW_REPEAT,
SYNC_V1_H1,
HSTX_CMD_RAW_REPEAT,
SYNC_V1_H0,
HSTX_CMD_RAW_REPEAT,
SYNC_V1_H1
};
static uint32_t vblank_line_vsync_on[] = {
HSTX_CMD_RAW_REPEAT,
SYNC_V0_H1,
HSTX_CMD_RAW_REPEAT,
SYNC_V0_H0,
HSTX_CMD_RAW_REPEAT,
SYNC_V0_H1
};
static uint32_t vactive_line_header[] = {
HSTX_CMD_RAW_REPEAT,
SYNC_V1_H1,
HSTX_CMD_RAW_REPEAT,
SYNC_V1_H0,
HSTX_CMD_RAW_REPEAT,
SYNC_V1_H1,
HSTX_CMD_TMDS
};
static uint32_t vactive_text_line_header[] = {
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
};
#define NUM_FRAME_LINES 2
#define NUM_CHANS 3
static DVHSTX* display = nullptr;
// ----------------------------------------------------------------------------
// DMA logic
void __scratch_x("display") dma_irq_handler() {
display->gfx_dma_handler();
}
void __scratch_x("display") DVHSTX::gfx_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) >> v_repeat_shift;
const int new_line_num = (v_repeat_shift == 0) ? ch_num : (y & (NUM_FRAME_LINES - 1));
const uint line_buf_total_len = ((timing_mode->h_active_pixels * line_bytes_per_pixel) >> 2) + count_of(vactive_line_header);
ch->read_addr = (uintptr_t)&line_buffers[new_line_num * line_buf_total_len];
ch->transfer_count = line_buf_total_len;
// Fill line buffer
if (line_num != new_line_num)
{
line_num = new_line_num;
uint32_t* dst_ptr = &line_buffers[line_num * line_buf_total_len + count_of(vactive_line_header)];
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)];
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 if (line_bytes_per_pixel == 1) {
uint8_t* src_ptr = &frame_buffer_display[y * (timing_mode->h_active_pixels >> h_repeat_shift)];
for (int i = 0; i < timing_mode->h_active_pixels >> 2; ++i) {
uint32_t val = (uint32_t)(*src_ptr++) * 0x01010101;
*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;
}
}
}
}
}
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;
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;
}
}
}
if (++v_scanline == v_total_active_lines) {
v_scanline = 0;
line_num = -1;
if (flip_next) {
flip_next = false;
display->flip_now();
}
__sev();
}
}
// ----------------------------------------------------------------------------
// Experimental clock config
static void __no_inline_not_in_flash_func(set_qmi_timing)() {
// Make sure flash is deselected - QMI doesn't appear to have a busy flag(!)
while ((ioqspi_hw->io[1].status & IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) != IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS)
;
qmi_hw->m[0].timing = 0x40000202;
//qmi_hw->m[0].timing = 0x40000101;
// Force a read through XIP to ensure the timing is applied
volatile uint32_t* ptr = (volatile uint32_t*)0x14000000;
(void) *ptr;
}
void DVHSTX::display_setup_clock() {
// Before messing with clock speeds ensure QSPI clock is nice and slow
hw_write_masked(&qmi_hw->m[0].timing, 6, QMI_M0_TIMING_CLKDIV_BITS);
// We're going to go fast, boost the voltage a little
vreg_set_voltage(VREG_VOLTAGE_1_15);
// Force a read through XIP to ensure the timing is applied before raising the clock rate
volatile uint32_t* ptr = (volatile uint32_t*)0x14000000;
(void) *ptr;
const uint32_t dvi_clock_khz = timing_mode->bit_clk_khz >> 1;
uint vco_freq, post_div1, post_div2;
if (!check_sys_clock_khz(dvi_clock_khz, &vco_freq, &post_div1, &post_div2))
panic("System clock of %u kHz cannot be exactly achieved", dvi_clock_khz);
const uint32_t freq = vco_freq / (post_div1 * post_div2);
// Before we touch PLLs, switch sys and ref cleanly away from their aux sources.
hw_clear_bits(&clocks_hw->clk[clk_sys].ctrl, CLOCKS_CLK_SYS_CTRL_SRC_BITS);
while (clocks_hw->clk[clk_sys].selected != 0x1)
tight_loop_contents();
hw_write_masked(&clocks_hw->clk[clk_ref].ctrl, CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, CLOCKS_CLK_REF_CTRL_SRC_BITS);
while (clocks_hw->clk[clk_ref].selected != 0x4)
tight_loop_contents();
// Stop the other clocks so we don't worry about overspeed
clock_stop(clk_usb);
clock_stop(clk_adc);
clock_stop(clk_peri);
clock_stop(clk_hstx);
// Set the sys PLL to the requested freq, set USB PLL to 528MHz
pll_init(pll_sys, PLL_COMMON_REFDIV, vco_freq, post_div1, post_div2);
pll_init(pll_usb, PLL_COMMON_REFDIV, 1584 * MHZ, 3, 1);
const uint32_t usb_pll_freq = 528 * MHZ;
// CLK SYS = PLL USB 528MHz / 2 = 264MHz
clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
usb_pll_freq, usb_pll_freq / 2);
// CLK PERI = PLL USB 528MHz / 4 = 132MHz
clock_configure(clk_peri,
0, // Only AUX mux on ADC
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
usb_pll_freq, usb_pll_freq / 4);
// CLK USB = PLL USB 528MHz / 11 = 48MHz
clock_configure(clk_usb,
0, // No GLMUX
CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
usb_pll_freq,
USB_CLK_KHZ * KHZ);
// CLK ADC = PLL USB 528MHz / 11 = 48MHz
clock_configure(clk_adc,
0, // No GLMUX
CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
usb_pll_freq,
USB_CLK_KHZ * KHZ);
// CLK HSTX = Requested freq
clock_configure(clk_hstx,
0,
CLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
freq, freq);
// Now we are running fast set fast QSPI clock and read delay
set_qmi_timing();
}
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);
}
bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_)
{
display_width = width;
display_height = height;
frame_width = width;
frame_height = height;
mode = mode_;
timing_mode = nullptr;
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;
}
else if (width == 640 && height == 360) {
h_repeat_shift = 1;
v_repeat_shift = 1;
timing_mode = &dvi_timing_1280x720p_rb_50hz;
}
else if (width == 480 && height == 270) {
h_repeat_shift = 2;
v_repeat_shift = 2;
timing_mode = &dvi_timing_1920x1080p_rb2_30hz;
}
else
{
uint16_t full_width = display_width;
uint16_t full_height = display_height;
h_repeat_shift = 0;
v_repeat_shift = 0;
if (display_width < 640) {
h_repeat_shift = 1;
full_width *= 2;
}
if (display_height < 400) {
v_repeat_shift = 1;
full_height *= 2;
}
if (full_width == 640) {
if (full_height == 480) timing_mode = &dvi_timing_640x480p_60hz;
}
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;
else if (full_height == 576) timing_mode = &dvi_timing_720x576p_50hz;
}
else if (full_width == 800) {
if (full_height == 600) timing_mode = &dvi_timing_800x600p_60hz;
else if (full_height == 480) timing_mode = &dvi_timing_800x480p_60hz;
else if (full_height == 450) timing_mode = &dvi_timing_800x450p_60hz;
}
else if (full_width == 960) {
if (full_height == 540) timing_mode = &dvi_timing_960x540p_60hz;
}
else if (full_width == 1024) {
if (full_height == 768) timing_mode = &dvi_timing_1024x768_rb_60hz;
}
}
if (!timing_mode) {
printf("Unsupported resolution %dx%d", width, height);
return false;
}
display = this;
display_palette = get_palette();
display_setup_clock();
stdio_init_all();
v_inactive_total = timing_mode->v_front_porch + timing_mode->v_sync_width + timing_mode->v_back_porch;
v_total_active_lines = v_inactive_total + timing_mode->v_active_lines;
v_repeat = 1 << v_repeat_shift;
h_repeat = 1 << h_repeat_shift;
vblank_line_vsync_off[0] |= timing_mode->h_front_porch;
vblank_line_vsync_off[2] |= timing_mode->h_sync_width;
vblank_line_vsync_off[4] |= timing_mode->h_back_porch + timing_mode->h_active_pixels;
vblank_line_vsync_on[0] |= timing_mode->h_front_porch;
vblank_line_vsync_on[2] |= timing_mode->h_sync_width;
vblank_line_vsync_on[4] |= timing_mode->h_back_porch + timing_mode->h_active_pixels;
vactive_line_header[0] |= timing_mode->h_front_porch;
vactive_line_header[2] |= timing_mode->h_sync_width;
vactive_line_header[4] |= timing_mode->h_back_porch;
vactive_line_header[6] |= timing_mode->h_active_pixels;
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_RGB565:
frame_bytes_per_pixel = 2;
line_bytes_per_pixel = 2;
break;
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:
printf("Unsupported mode %d", (int)mode);
return false;
}
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);
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);
frame_buffer_display = frame_buffer_display;
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 + (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)
{
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);
}
}
}
switch (mode) {
case MODE_RGB565:
// Configure HSTX's TMDS encoder for RGB565
hstx_ctrl_hw->expand_tmds =
4 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |
8 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB |
5 << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB |
3 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB |
4 << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB |
29 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB;
// Pixels (TMDS) come in 2 16-bit chunks. Control symbols (RAW) are an
// entire 32-bit word.
hstx_ctrl_hw->expand_shift =
2 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB |
16 << 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_PALETTE:
// Configure HSTX's TMDS encoder for RGB888
hstx_ctrl_hw->expand_tmds =
7 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB |
16 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB |
7 << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB |
8 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB |
7 << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB |
0 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB;
// Pixels and control symbols (RAW) are an
// entire 32-bit word.
hstx_ctrl_hw->expand_shift =
1 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB |
0 << 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_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:
printf("Unsupported mode %d", (int)mode);
return false;
}
// Serial output config: clock period of 5 cycles, pop from command
// expander every 5 cycles, shift the output shiftreg by 2 every cycle.
hstx_ctrl_hw->csr = 0;
hstx_ctrl_hw->csr =
HSTX_CTRL_CSR_EXPAND_EN_BITS |
5u << HSTX_CTRL_CSR_CLKDIV_LSB |
5u << HSTX_CTRL_CSR_N_SHIFTS_LSB |
2u << HSTX_CTRL_CSR_SHIFT_LSB |
HSTX_CTRL_CSR_EN_BITS;
// HSTX outputs 0 through 7 appear on GPIO 12 through 19.
// Assign clock pair to two neighbouring pins:
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:
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_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);
gpio_set_drive_strength(i, GPIO_DRIVE_STRENGTH_4MA);
}
// Always use the bottom channels
dma_claim_mask((1 << NUM_CHANS) - 1);
// The channels are set up identically, to transfer a whole scanline and
// then chain to the next channel. Each time a channel finishes, we
// reconfigure the one that just finished, meanwhile the other channel(s)
// are already making progress.
// Using just 2 channels was insufficient to avoid issues with the IRQ.
dma_channel_config c;
c = dma_channel_get_default_config(0);
channel_config_set_chain_to(&c, 1);
channel_config_set_dreq(&c, DREQ_HSTX);
dma_channel_configure(
0,
&c,
&hstx_fifo_hw->fifo,
vblank_line_vsync_off,
count_of(vblank_line_vsync_off),
false
);
c = dma_channel_get_default_config(1);
channel_config_set_chain_to(&c, 2);
channel_config_set_dreq(&c, DREQ_HSTX);
dma_channel_configure(
1,
&c,
&hstx_fifo_hw->fifo,
vblank_line_vsync_off,
count_of(vblank_line_vsync_off),
false
);
for (int i = 2; i < NUM_CHANS; ++i) {
c = dma_channel_get_default_config(i);
channel_config_set_chain_to(&c, (i+1) % NUM_CHANS);
channel_config_set_dreq(&c, DREQ_HSTX);
dma_channel_configure(
i,
&c,
&hstx_fifo_hw->fifo,
vblank_line_vsync_off,
count_of(vblank_line_vsync_off),
false
);
}
dma_hw->ints0 = (1 << NUM_CHANS) - 1;
dma_hw->inte0 = (1 << NUM_CHANS) - 1;
if (is_text_mode) irq_set_exclusive_handler(DMA_IRQ_0, dma_irq_handler_text);
else irq_set_exclusive_handler(DMA_IRQ_0, dma_irq_handler);
irq_set_enabled(DMA_IRQ_0, true);
dma_channel_start(0);
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);
}
return true;
}
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();
}

157
drivers/dvhstx/dvhstx.hpp Normal file
View file

@ -0,0 +1,157 @@
#pragma once
#include <string.h>
#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
namespace pimoroni {
// Digital Video using HSTX
// Valid screen modes are:
// Pixel doubled: 640x480 (60Hz), 720x480 (60Hz), 720x400 (70Hz), 720x576 (50Hz),
// 800x600 (60Hz), 800x480 (60Hz), 800x450 (60Hz), 960x540 (60Hz), 1024x768 (60Hz)
// Pixel doubled or quadrupled: 1280x720 (50Hz)
//
// Giving valid resolutions:
// 320x180, 640x360 (well supported, square pixels on a 16:9 display)
// 480x270, 400x225 (sometimes supported, square pixels on a 16:9 display)
// 320x240, 360x240, 360x200, 360x288, 400x300, 512x384 (well supported, but pixels aren't square)
// 400x240 (sometimes supported, pixels aren't square)
//
// Note that the double buffer is in RAM, so 640x360 uses almost all of the available RAM.
class DVHSTX {
public:
static constexpr int PALETTE_SIZE = 256;
enum Mode {
MODE_PALETTE = 2,
MODE_RGB565 = 1,
MODE_RGB888 = 3,
MODE_TEXT_MONO = 4,
MODE_TEXT_RGB111 = 5,
};
enum TextColour {
TEXT_BLACK = 0,
TEXT_RED = 0b1000000,
TEXT_GREEN = 0b0001000,
TEXT_BLUE = 0b0000001,
TEXT_YELLOW = 0b1001000,
TEXT_MAGENTA = 0b1000001,
TEXT_CYAN = 0b0001001,
TEXT_WHITE = 0b1001001,
};
//--------------------------------------------------
// Variables
//--------------------------------------------------
protected:
friend void vsync_callback();
uint16_t display_width = 320;
uint16_t display_height = 180;
uint16_t frame_width = 320;
uint16_t frame_height = 180;
uint8_t frame_bytes_per_pixel = 2;
uint8_t bank = 0;
uint8_t h_repeat = 4;
uint8_t v_repeat = 4;
Mode mode = MODE_RGB565;
public:
DVHSTX()
{}
//--------------------------------------------------
// Methods
//--------------------------------------------------
public:
// 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);
// 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 text_dma_handler();
private:
RGB888 palette[PALETTE_SIZE];
uint8_t* frame_buffer_display;
uint8_t* frame_buffer_back;
uint32_t* font_cache;
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();
// DMA scanline filling
uint ch_num = 0;
int line_num = -1;
volatile int v_scanline = 2;
volatile bool flip_next;
uint32_t* line_buffers;
const struct dvi_timing* timing_mode;
uint v_inactive_total;
uint v_total_active_lines;
uint h_repeat_shift;
uint v_repeat_shift;
int line_bytes_per_pixel;
uint32_t* display_palette = nullptr;
};
}

265
drivers/dvhstx/dvi.cpp Normal file
View file

@ -0,0 +1,265 @@
#include <pico/stdlib.h>
#include "dvi.hpp"
// VGA -- we do this mode properly, with a pretty comfortable clk_sys (252 MHz)
const struct dvi_timing dvi_timing_640x480p_60hz = {
.h_sync_polarity = false,
.h_front_porch = 16,
.h_sync_width = 96,
.h_back_porch = 48,
.h_active_pixels = 640,
.v_sync_polarity = false,
.v_front_porch = 10,
.v_sync_width = 2,
.v_back_porch = 33,
.v_active_lines = 480,
.bit_clk_khz = 252000
};
// SVGA -- completely by-the-book but requires 400 MHz clk_sys
const struct dvi_timing dvi_timing_800x600p_60hz = {
.h_sync_polarity = false,
.h_front_porch = 44,
.h_sync_width = 128,
.h_back_porch = 88,
.h_active_pixels = 800,
.v_sync_polarity = false,
.v_front_porch = 1,
.v_sync_width = 4,
.v_back_porch = 23,
.v_active_lines = 600,
.bit_clk_khz = 400000
};
// 720x480 - timings from dumping the EDID of my monitor
const struct dvi_timing dvi_timing_720x480p_60hz = {
.h_sync_polarity = false,
.h_front_porch = 16,
.h_sync_width = 62,
.h_back_porch = 60,
.h_active_pixels = 720,
.v_sync_polarity = false,
.v_front_porch = 9,
.v_sync_width = 6,
.v_back_porch = 30,
.v_active_lines = 480,
.bit_clk_khz = 270000
};
// 720x576@50Hz - CEA timing
const struct dvi_timing dvi_timing_720x576p_50hz = {
.h_sync_polarity = false,
.h_front_porch = 12,
.h_sync_width = 64,
.h_back_porch = 68,
.h_active_pixels = 720,
.v_sync_polarity = false,
.v_front_porch = 5,
.v_sync_width = 5,
.v_back_porch = 39,
.v_active_lines = 576,
.bit_clk_khz = 270000
};
// 720x400@70Hz - "IBM" timing
const struct dvi_timing dvi_timing_720x400p_70hz = {
.h_sync_polarity = false,
.h_front_porch = 18,
.h_sync_width = 108,
.h_back_porch = 54,
.h_active_pixels = 720,
.v_sync_polarity = true,
.v_front_porch = 13,
.v_sync_width = 2,
.v_back_porch = 34,
.v_active_lines = 400,
.bit_clk_khz = 283200
};
// 800x480p 60 Hz (note this doesn't seem to be a CEA mode, I just used the
// output of `cvt 800 480 60`), 295 MHz bit clock
const struct dvi_timing dvi_timing_800x480p_60hz = {
.h_sync_polarity = false,
.h_front_porch = 24,
.h_sync_width = 72,
.h_back_porch = 96,
.h_active_pixels = 800,
.v_sync_polarity = true,
.v_front_porch = 3,
.v_sync_width = 10,
.v_back_porch = 7,
.v_active_lines = 480,
.bit_clk_khz = 295200
};
// 800x450p 60 Hz Similarly not a CEA mode, but is 16:9
const struct dvi_timing dvi_timing_800x450p_60hz = {
.h_sync_polarity = false,
.h_front_porch = 24,
.h_sync_width = 72,
.h_back_porch = 96,
.h_active_pixels = 800,
.v_sync_polarity = true,
.v_front_porch = 3,
.v_sync_width = 5,
.v_back_porch = 10,
.v_active_lines = 450,
.bit_clk_khz = 278400
};
// SVGA reduced blanking (355 MHz bit clock) -- valid CVT mode, less common
// than fully-blanked SVGA, but doesn't require such a high system clock
const struct dvi_timing dvi_timing_800x600p_reduced_60hz = {
.h_sync_polarity = true,
.h_front_porch = 48,
.h_sync_width = 32,
.h_back_porch = 80,
.h_active_pixels = 800,
.v_sync_polarity = false,
.v_front_porch = 3,
.v_sync_width = 4,
.v_back_porch = 11,
.v_active_lines = 600,
.bit_clk_khz = 354000
};
// Also known as qHD, bit uncommon, but it's a nice modest-resolution 16:9
// aspect mode. Pixel clock 40.75 MHz for full CVT mode (no reduced blanking)
const struct dvi_timing dvi_timing_960x540p_60hz = {
.h_sync_polarity = false,
.h_front_porch = 32,
.h_sync_width = 96,
.h_back_porch = 128,
.h_active_pixels = 960,
.v_sync_polarity = true,
.v_front_porch = 3,
.v_sync_width = 5,
.v_back_porch = 14,
.v_active_lines = 540,
.bit_clk_khz = 408000
};
// Also known as qHD, bit uncommon, but it's a nice modest-resolution 16:9
// aspect mode. Pixel clock 33.5 MHz for 50Hz CVT mode (no reduced blanking)
const struct dvi_timing dvi_timing_960x540p_50hz = {
.h_sync_polarity = false,
.h_front_porch = 24,
.h_sync_width = 96,
.h_back_porch = 120,
.h_active_pixels = 960,
.v_sync_polarity = true,
.v_front_porch = 3,
.v_sync_width = 5,
.v_back_porch = 11,
.v_active_lines = 540,
.bit_clk_khz = 336000
};
// 1024x768, CVT-RB
const struct dvi_timing dvi_timing_1024x768_rb_60hz = {
.h_sync_polarity = true,
.h_front_porch = 48,
.h_sync_width = 32,
.h_back_porch = 80,
.h_active_pixels = 1024,
.v_sync_polarity = false,
.v_front_porch = 3,
.v_sync_width = 4,
.v_back_porch = 15,
.v_active_lines = 768,
.bit_clk_khz = 560000
};
// 720p50, this is a standard HD mode, the CVT-RB variant
// should be widely supported
const struct dvi_timing dvi_timing_1280x720p_rb_50hz = {
.h_sync_polarity = true,
.h_front_porch = 48,
.h_sync_width = 32,
.h_back_porch = 80,
.h_active_pixels = 1280,
.v_sync_polarity = false,
.v_front_porch = 3,
.v_sync_width = 5,
.v_back_porch = 9,
.v_active_lines = 720,
.bit_clk_khz = 528000
};
// 720p60, this is the CVT-RB variant, again should be widely supported
const struct dvi_timing dvi_timing_1280x720p_rb_60hz = {
.h_sync_polarity = true,
.h_front_porch = 48,
.h_sync_width = 32,
.h_back_porch = 80,
.h_active_pixels = 1280,
.v_sync_polarity = false,
.v_front_porch = 3,
.v_sync_width = 5,
.v_back_porch = 13,
.v_active_lines = 720,
.bit_clk_khz = 640000
};
// 1080p30 - not a normal mode but seems to work on a wide variety of hardware
// Strictly speaking RB2 should have a clock speed matching the target frequency more closely
// but it seems to work!
const struct dvi_timing dvi_timing_1920x1080p_rb2_30hz = {
.h_sync_polarity = true,
.h_front_porch = 8,
.h_sync_width = 32,
.h_back_porch = 40,
.h_active_pixels = 1920,
.v_sync_polarity = false,
.v_front_porch = 7,
.v_sync_width = 8,
.v_back_porch = 6,
.v_active_lines = 1080,
.bit_clk_khz = 660000
};
// 1440p24 YOLO - works on my Dell Ultrasharp, that most forgiving of monitors. May require a little more than 1.3V
const struct dvi_timing dvi_timing_2560x1440p_yolo_24hz = {
.h_sync_polarity = true,
.h_front_porch = 8,
.h_sync_width = 32,
.h_back_porch = 20,
.h_active_pixels = 2560,
.v_sync_polarity = false,
.v_front_porch = 2,
.v_sync_width = 6,
.v_back_porch = 2,
.v_active_lines = 1440,
.bit_clk_khz = 912000
};

62
drivers/dvhstx/dvi.hpp Normal file
View file

@ -0,0 +1,62 @@
#pragma once
// ----------------------------------------------------------------------------
// DVI constants
#define TMDS_CTRL_00 0x354u
#define TMDS_CTRL_01 0x0abu
#define TMDS_CTRL_10 0x154u
#define TMDS_CTRL_11 0x2abu
#define TMDS_BALANCED_LOW 0x307u
#define TMDS_BALANCED_HIGH 0x2f0u
#define TMDS_BLACK_A 0x100u
#define TMDS_BLACK_B 0x1ffu
#define SYNC_V0_H0 (TMDS_CTRL_00 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define SYNC_V0_H1 (TMDS_CTRL_01 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define SYNC_V1_H0 (TMDS_CTRL_10 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define SYNC_V1_H1 (TMDS_CTRL_11 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20))
#define MISSING_PIXEL (TMDS_BALANCED_LOW | (TMDS_BALANCED_LOW << 10) | (TMDS_BALANCED_HIGH << 20))
#define BLACK_PIXEL (TMDS_BALANCED_LOW | (TMDS_BALANCED_LOW << 10) | (TMDS_BALANCED_LOW << 20))
#define BLACK_PIXEL_A (TMDS_BLACK_A | (TMDS_BLACK_A << 10) | (TMDS_BLACK_A << 20))
#define BLACK_PIXEL_B (TMDS_BLACK_B | (TMDS_BLACK_B << 10) | (TMDS_BLACK_B << 20))
#define HSTX_CMD_RAW (0x0u << 12)
#define HSTX_CMD_RAW_REPEAT (0x1u << 12)
#define HSTX_CMD_TMDS (0x2u << 12)
#define HSTX_CMD_TMDS_REPEAT (0x3u << 12)
#define HSTX_CMD_NOP (0xfu << 12)
struct dvi_timing {
bool h_sync_polarity;
uint h_front_porch;
uint h_sync_width;
uint h_back_porch;
uint h_active_pixels;
bool v_sync_polarity;
uint v_front_porch;
uint v_sync_width;
uint v_back_porch;
uint v_active_lines;
uint bit_clk_khz;
};
extern const struct dvi_timing dvi_timing_640x480p_60hz;
extern const struct dvi_timing dvi_timing_720x480p_60hz;
extern const struct dvi_timing dvi_timing_720x576p_50hz;
extern const struct dvi_timing dvi_timing_720x400p_70hz;
extern const struct dvi_timing dvi_timing_800x450p_60hz;
extern const struct dvi_timing dvi_timing_800x480p_60hz;
extern const struct dvi_timing dvi_timing_800x600p_60hz;
extern const struct dvi_timing dvi_timing_960x540p_60hz;
extern const struct dvi_timing dvi_timing_960x540p_50hz;
extern const struct dvi_timing dvi_timing_1024x768_rb_60hz;
extern const struct dvi_timing dvi_timing_1280x720p_rb_50hz;
extern const struct dvi_timing dvi_timing_1280x720p_rb_60hz;
extern const struct dvi_timing dvi_timing_1920x1080p_rb2_30hz;
extern const struct dvi_timing dvi_timing_1920x1080p_yolo_50hz;
extern const struct dvi_timing dvi_timing_1920x1080p_yolo_60hz;

42
drivers/dvhstx/font.h Normal file
View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _FONT_H
#define _FONT_H
#include "pico/types.h"
typedef struct {
uint16_t bitmap_index;
uint16_t adv_w;
int8_t box_w, box_h, ofs_x, ofs_y;
} __attribute__((packed)) lv_font_fmt_txt_glyph_dsc_t;
typedef struct {
uint16_t range_start, range_length, glyph_id_start, list_length;
void *unicode_list, *glyph_id_ofs_list;
enum {
LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
} type;
} lv_font_fmt_txt_cmap_t;
typedef struct {
const uint8_t *glyph_bitmap;
const lv_font_fmt_txt_glyph_dsc_t *glyph_dsc;
const lv_font_fmt_txt_cmap_t *cmaps;
uint8_t cmap_num, bpp, kern_scale, kern_classes;
void *kern_dsc;
} lv_font_fmt_txt_dsc_t;
typedef struct {
lv_font_fmt_txt_dsc_t *dsc;
uint8_t line_height, base_line;
} lv_font_t;
#define FONT_HEIGHT 23
extern const lv_font_t intel_one_mono;
#endif //SOFTWARE_FONT_H

View file

@ -0,0 +1,895 @@
/*******************************************************************************
* Size: 22 px
* Bpp: 2
* Opts: --bpp 2 --size 22 --no-compress --font IntelOneMono-Medium.ttf --range 32-127 --format lvgl -o intel_one_mono.c
******************************************************************************/
// The Intel One Mono font is licensed under the SIL Open Font License, Version 1.1:
// https://github.com/intel/intel-one-mono/blob/main/OFL.txt
// This file is created using https://lvgl.io/tools/fontconverter
#include "font.h"
#ifndef INTEL_ONE_MONO
#define INTEL_ONE_MONO 1
#endif
#if INTEL_ONE_MONO
/*-----------------
* BITMAPS
*----------------*/
/*Store the image of the glyphs*/
static const uint8_t glyph_bitmap[] = {
/* U+0020 " " */
/* U+0021 "!" */
0x7f, 0xc7, 0xfc, 0x3f, 0x83, 0xf8, 0x3f, 0x42,
0xf4, 0x2f, 0x1, 0xf0, 0x1f, 0x0, 0xe0, 0x0,
0x0, 0x0, 0x5, 0x3, 0xf8, 0x7f, 0xc3, 0xf4,
/* U+0022 "\"" */
0xbd, 0xb, 0xd7, 0xd0, 0xbd, 0x7d, 0xb, 0xc7,
0xd0, 0x7c, 0x7d, 0x7, 0xc3, 0xc0, 0x7c, 0x3c,
0x7, 0xc1, 0x40, 0x14,
/* U+0023 "#" */
0x0, 0x3c, 0x3c, 0x0, 0xe, 0xe, 0x0, 0x3,
0x43, 0x80, 0x16, 0xe6, 0xe5, 0x1f, 0xff, 0xff,
0x87, 0xf8, 0x2f, 0xe0, 0xd, 0xb, 0x0, 0x3,
0x43, 0x80, 0x16, 0xc0, 0xe5, 0xf, 0xfa, 0xbf,
0xc3, 0xff, 0xff, 0xf0, 0xf, 0xf, 0x0, 0x3,
0x83, 0x80, 0x0, 0xd0, 0xe0, 0x0, 0x74, 0x74,
0x0,
/* U+0024 "$" */
0x0, 0x3c, 0x0, 0x0, 0xf0, 0x0, 0x3, 0xc0,
0x0, 0xbf, 0xf8, 0xf, 0xff, 0xf4, 0xfe, 0x56,
0xc3, 0xd0, 0x0, 0xf, 0x40, 0x0, 0x3f, 0x40,
0x0, 0x7f, 0xe4, 0x0, 0x7f, 0xfd, 0x0, 0xb,
0xfd, 0x0, 0x1, 0xf8, 0x0, 0x2, 0xf0, 0x0,
0xb, 0xde, 0x40, 0x7e, 0xbf, 0xff, 0xf0, 0x6f,
0xfd, 0x0, 0x3, 0xc0, 0x0, 0xf, 0x0, 0x0,
0x3c, 0x0, 0x0, 0x50, 0x0,
/* U+0025 "%" */
0xb, 0xe0, 0xb, 0xc2, 0xeb, 0xc0, 0xf0, 0x38,
0x1c, 0x2d, 0x3, 0x81, 0xd3, 0x80, 0x38, 0x1d,
0x70, 0x3, 0xc2, 0xcd, 0x0, 0xf, 0xf4, 0x0,
0x0, 0x14, 0x1f, 0xc0, 0x0, 0x57, 0xef, 0x0,
0xc, 0xb0, 0x74, 0x2, 0xcf, 0x3, 0x80, 0x74,
0xf0, 0x38, 0xf, 0xb, 0x7, 0x42, 0xe0, 0x7e,
0xf0, 0x7c, 0x1, 0xfc, 0x0,
/* U+0026 "&" */
0x0, 0xbf, 0xd0, 0x3, 0xff, 0xd0, 0xb, 0xc0,
0x0, 0xb, 0x80, 0x0, 0xb, 0xc0, 0x0, 0x3,
0xd0, 0x10, 0x2, 0xf0, 0x3c, 0xb, 0xfc, 0x78,
0x3f, 0x7d, 0xf0, 0xbc, 0x2f, 0xe0, 0xf4, 0xf,
0xd0, 0xf4, 0x7, 0xe0, 0xbc, 0xf, 0xf0, 0x3f,
0xfd, 0xbc, 0x1f, 0xf4, 0x3e,
/* U+0027 "'" */
0xfd, 0xfd, 0xfd, 0xbc, 0xbc, 0xbc, 0xbc, 0x14,
/* U+0028 "(" */
0x0, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x1f, 0xd0,
0x7, 0xf0, 0x2, 0xf8, 0x0, 0x7e, 0x0, 0xf,
0x80, 0x2, 0xf0, 0x0, 0x3e, 0x0, 0x7, 0xc0,
0x0, 0xbc, 0x0, 0xb, 0xc0, 0x0, 0xbc, 0x0,
0x7, 0xc0, 0x0, 0x3d, 0x0, 0x2, 0xf0, 0x0,
0xf, 0x80, 0x0, 0x7d, 0x0, 0x2, 0xf8, 0x0,
0xb, 0xe0, 0x0, 0x1f, 0xd0, 0x0, 0x2c, 0x0,
0x0, 0x0,
/* U+0029 ")" */
0x0, 0x0, 0x0, 0xb4, 0x0, 0x3, 0xf8, 0x0,
0x1, 0xfc, 0x0, 0x0, 0xfc, 0x0, 0x0, 0xfc,
0x0, 0x0, 0xf8, 0x0, 0x1, 0xf0, 0x0, 0x3,
0xe0, 0x0, 0xb, 0xc0, 0x0, 0x1f, 0x0, 0x0,
0x7c, 0x0, 0x1, 0xf0, 0x0, 0xb, 0xc0, 0x0,
0x3e, 0x0, 0x1, 0xf0, 0x0, 0xf, 0x80, 0x0,
0xfc, 0x0, 0xf, 0xc0, 0x1, 0xfc, 0x0, 0x3f,
0x80, 0x0, 0xb4, 0x0, 0x0, 0x0, 0x0, 0x0,
/* U+002A "*" */
0x0, 0x7c, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x38,
0x0, 0x38, 0x34, 0x78, 0xbf, 0x66, 0xfc, 0x16,
0xbe, 0x50, 0x0, 0xbc, 0x0, 0x3, 0xcb, 0x40,
0xf, 0x83, 0xe0, 0xf, 0x1, 0xe0, 0x1, 0x0,
0x40,
/* U+002B "+" */
0x0, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x7c,
0x0, 0x0, 0x7c, 0x0, 0x0, 0x7c, 0x0, 0xbf,
0xff, 0xfc, 0xbf, 0xff, 0xfc, 0x0, 0x7c, 0x0,
0x0, 0x7c, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x7c,
0x0, 0x0, 0x7c, 0x0,
/* U+002C "," */
0x3, 0xfc, 0x3, 0xf8, 0x3, 0xf4, 0x7, 0xf0,
0xf, 0xd0, 0x2f, 0x40, 0x3d, 0x0,
/* U+002D "-" */
0x15, 0x55, 0x5f, 0xff, 0xf7, 0xff, 0xfc,
/* U+002E "." */
0x0, 0x3, 0xf8, 0x7f, 0xc7, 0xfc, 0x2f, 0x40,
/* U+002F "/" */
0x0, 0x0, 0x6, 0x0, 0x0, 0x3, 0xc0, 0x0,
0x2, 0xe0, 0x0, 0x0, 0xf0, 0x0, 0x0, 0xb8,
0x0, 0x0, 0x3c, 0x0, 0x0, 0x2e, 0x0, 0x0,
0xf, 0x40, 0x0, 0xb, 0xc0, 0x0, 0x3, 0xd0,
0x0, 0x2, 0xf0, 0x0, 0x0, 0xf4, 0x0, 0x0,
0xbc, 0x0, 0x0, 0x3d, 0x0, 0x0, 0x1f, 0x0,
0x0, 0xf, 0x40, 0x0, 0x7, 0xc0, 0x0, 0x3,
0xd0, 0x0, 0x1, 0xf0, 0x0, 0x0, 0xf8, 0x0,
0x0,
/* U+0030 "0" */
0x1, 0xfe, 0x0, 0xb, 0xff, 0xd0, 0x1f, 0x87,
0xf0, 0x3e, 0x0, 0xf4, 0x3c, 0x0, 0xfc, 0x7c,
0x3, 0xfc, 0x7c, 0x1f, 0xbc, 0xbc, 0x7c, 0x7c,
0xbe, 0xe0, 0x7c, 0x7f, 0x80, 0x7c, 0x3d, 0x0,
0xb8, 0x3e, 0x0, 0xf4, 0x2f, 0x87, 0xf0, 0xf,
0xff, 0xd0, 0x2, 0xfe, 0x0,
/* U+0031 "1" */
0x0, 0x74, 0x0, 0x7f, 0x40, 0x7f, 0xf4, 0xb,
0xef, 0x40, 0xa0, 0xf4, 0x0, 0xf, 0x40, 0x0,
0xf4, 0x0, 0xf, 0x40, 0x0, 0xf4, 0x0, 0xf,
0x40, 0x0, 0xf4, 0x0, 0xf, 0x40, 0x0, 0xf4,
0xb, 0xff, 0xfe, 0xbf, 0xff, 0xe0,
/* U+0032 "2" */
0x1, 0xbf, 0x40, 0xb, 0xff, 0xf0, 0x3f, 0x96,
0xf4, 0x7d, 0x0, 0xf8, 0x10, 0x0, 0xf8, 0x0,
0x0, 0xf4, 0x0, 0x3, 0xf0, 0x0, 0xb, 0xc0,
0x0, 0x2f, 0x40, 0x0, 0xbd, 0x0, 0x2, 0xf4,
0x0, 0xb, 0xd0, 0x0, 0x2f, 0x40, 0x0, 0x7f,
0xff, 0xfc, 0x7f, 0xff, 0xfc,
/* U+0033 "3" */
0x7f, 0xff, 0xf5, 0xff, 0xff, 0xc0, 0x0, 0x7e,
0x0, 0x3, 0xe0, 0x0, 0x3e, 0x0, 0x3, 0xf0,
0x0, 0x3f, 0xf8, 0x0, 0xff, 0xfc, 0x0, 0x2,
0xf8, 0x0, 0x2, 0xf0, 0x0, 0xb, 0xc0, 0x0,
0x7e, 0x5, 0x6f, 0xf4, 0xff, 0xff, 0x43, 0xff,
0x90, 0x0,
/* U+0034 "4" */
0x0, 0x1f, 0xc0, 0x0, 0x3f, 0xc0, 0x0, 0xb7,
0xc0, 0x1, 0xf3, 0xc0, 0x3, 0xc3, 0xc0, 0xb,
0x83, 0xc0, 0xf, 0x3, 0xc0, 0x3c, 0x3, 0xc0,
0x78, 0x3, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x0, 0x3, 0xc0, 0x0, 0x3, 0xc0, 0x0,
0x3, 0xc0, 0x0, 0x3, 0xc0,
/* U+0035 "5" */
0x1f, 0xff, 0xf0, 0x7f, 0xff, 0xc2, 0xe0, 0x0,
0xb, 0x80, 0x0, 0x2e, 0x0, 0x0, 0xba, 0xfe,
0x3, 0xff, 0xff, 0xb, 0x90, 0xbe, 0x0, 0x0,
0xfc, 0x0, 0x2, 0xf0, 0x0, 0xf, 0x80, 0x1,
0xfd, 0x1a, 0xff, 0xe0, 0xff, 0xf9, 0x3, 0xa5,
0x0, 0x0,
/* U+0036 "6" */
0x0, 0x1f, 0xc0, 0x0, 0x7f, 0x0, 0x1, 0xfc,
0x0, 0x3, 0xf0, 0x0, 0xf, 0xc0, 0x0, 0x1f,
0xff, 0x80, 0x3f, 0xff, 0xf0, 0x3d, 0x1, 0xf8,
0x7c, 0x0, 0xfc, 0x7c, 0x0, 0xbc, 0x7c, 0x0,
0xbc, 0x3e, 0x0, 0xfc, 0x3f, 0x43, 0xf4, 0xf,
0xff, 0xe0, 0x2, 0xfe, 0x40,
/* U+0037 "7" */
0xbf, 0xff, 0xfe, 0xff, 0xff, 0xf0, 0x0, 0xf,
0x80, 0x0, 0x7c, 0x0, 0x3, 0xe0, 0x0, 0x1f,
0x0, 0x0, 0xfc, 0x0, 0x7, 0xd0, 0x0, 0x3f,
0x0, 0x1, 0xf4, 0x0, 0xf, 0xc0, 0x0, 0x7d,
0x0, 0x3, 0xf0, 0x0, 0x1f, 0x80, 0x0, 0xfc,
0x0, 0x0,
/* U+0038 "8" */
0x2, 0xff, 0x40, 0x3f, 0xff, 0x83, 0xf4, 0x2f,
0x4f, 0x40, 0x3e, 0x3d, 0x0, 0xf4, 0x7c, 0x7,
0xc0, 0x7f, 0xf8, 0x1, 0xff, 0xe0, 0x2f, 0x42,
0xf0, 0xf0, 0x2, 0xe7, 0xc0, 0x7, 0xdf, 0x0,
0x2f, 0x3f, 0x2, 0xf8, 0x7f, 0xff, 0xc0, 0x2f,
0xf4, 0x0,
/* U+0039 "9" */
0x2, 0xff, 0x40, 0xf, 0xff, 0xe0, 0x3f, 0x43,
0xf4, 0x3d, 0x0, 0xf8, 0x7c, 0x0, 0xbc, 0x7c,
0x0, 0xbc, 0x7d, 0x0, 0xbc, 0x3f, 0x0, 0xf8,
0x1f, 0xff, 0xf4, 0x6, 0xff, 0xf0, 0x0, 0x7,
0xd0, 0x0, 0x1f, 0x80, 0x0, 0x7e, 0x0, 0x1,
0xf8, 0x0, 0xb, 0xe0, 0x0,
/* U+003A ":" */
0x2f, 0x47, 0xfc, 0x7f, 0xc2, 0xf4, 0x0, 0x0,
0x0, 0x0, 0x2, 0xf4, 0x7f, 0xc7, 0xfc, 0x2f,
0x40,
/* U+003B ";" */
0x1, 0xf8, 0x3, 0xfc, 0x3, 0xfc, 0x1, 0xf8,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x54,
0x3, 0xf8, 0x3, 0xf4, 0x7, 0xf0, 0xf, 0xe0,
0x1f, 0x80, 0x3e, 0x0, 0x14, 0x0,
/* U+003C "<" */
0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x0, 0x2f,
0xc0, 0xb, 0xfd, 0x7, 0xff, 0x41, 0xff, 0x80,
0xb, 0xd0, 0x0, 0x2f, 0xe0, 0x0, 0x1f, 0xf9,
0x0, 0x2, 0xff, 0x40, 0x0, 0xbf, 0xc0, 0x0,
0x1f, 0x0, 0x0, 0x4,
/* U+003D "=" */
0x3f, 0xff, 0xf8, 0xff, 0xff, 0xe0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff,
0xe3, 0xff, 0xff, 0x80,
/* U+003E ">" */
0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x7f, 0x40,
0x0, 0x2f, 0xf4, 0x0, 0x2, 0xff, 0x80, 0x0,
0x1f, 0xf8, 0x0, 0x0, 0xbc, 0x0, 0x6, 0xfc,
0x0, 0x7f, 0xe0, 0xb, 0xfd, 0x0, 0x7f, 0xd0,
0x0, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0,
/* U+003F "?" */
0xa, 0xff, 0x81, 0xff, 0xff, 0xd7, 0x94, 0x1f,
0xc0, 0x0, 0x2f, 0x0, 0x0, 0xfc, 0x0, 0xf,
0xd0, 0x7, 0xfd, 0x0, 0x7f, 0x80, 0x1, 0xf0,
0x0, 0x7, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x50, 0x0, 0x1f, 0xd0, 0x0, 0xbf,
0x80, 0x0, 0xfc, 0x0,
/* U+0040 "@" */
0x1, 0xbf, 0x80, 0xb, 0x41, 0xb0, 0x1c, 0x0,
0x34, 0x34, 0x0, 0x1c, 0x30, 0x0, 0xc, 0x70,
0x2f, 0xfc, 0xa0, 0xbe, 0xad, 0xa0, 0xf0, 0xd,
0xa1, 0xd0, 0xd, 0xa1, 0xd0, 0xd, 0xa1, 0xe0,
0xc, 0xa0, 0xf0, 0x3c, 0x70, 0x7f, 0xf4, 0x70,
0xa, 0x40, 0x34, 0x0, 0x0, 0x2c, 0x0, 0x0,
0xf, 0x40, 0x0, 0x2, 0xff, 0xe0, 0x0, 0x0,
0x0,
/* U+0041 "A" */
0x0, 0x3f, 0x40, 0x0, 0xf, 0xe0, 0x0, 0xb,
0xfc, 0x0, 0x3, 0xdf, 0x40, 0x0, 0xf2, 0xe0,
0x0, 0x7c, 0x7c, 0x0, 0x2e, 0xf, 0x0, 0xf,
0x3, 0xe0, 0x7, 0xc0, 0xbc, 0x2, 0xff, 0xff,
0x0, 0xff, 0xff, 0xd0, 0x3c, 0x0, 0xf8, 0x1f,
0x0, 0x2f, 0xf, 0x80, 0x7, 0xd3, 0xd0, 0x0,
0xf8,
/* U+0042 "B" */
0x3f, 0xff, 0x80, 0x3f, 0xff, 0xf0, 0x3d, 0x1,
0xf8, 0x3d, 0x0, 0xbc, 0x3d, 0x0, 0xbc, 0x3d,
0x0, 0xf4, 0x3d, 0x2, 0xe0, 0x3f, 0xff, 0x40,
0x3f, 0xff, 0xe0, 0x3d, 0x1, 0xfc, 0x3d, 0x0,
0x3d, 0x3d, 0x0, 0x3d, 0x3d, 0x0, 0xbd, 0x3f,
0xff, 0xfc, 0x3f, 0xff, 0xe0,
/* U+0043 "C" */
0x0, 0xbf, 0x90, 0x7, 0xff, 0xf8, 0x1f, 0xd0,
0xbc, 0x3f, 0x0, 0x4, 0x7d, 0x0, 0x0, 0xbc,
0x0, 0x0, 0xfc, 0x0, 0x0, 0xf8, 0x0, 0x0,
0xfc, 0x0, 0x0, 0xbc, 0x0, 0x0, 0x7c, 0x0,
0x0, 0x3f, 0x0, 0x4, 0x2f, 0xd1, 0xbd, 0xb,
0xff, 0xf8, 0x1, 0xff, 0x80,
/* U+0044 "D" */
0x3f, 0xf8, 0x0, 0x3f, 0xff, 0x80, 0x3d, 0x1b,
0xf0, 0x3d, 0x1, 0xf8, 0x3d, 0x0, 0xbc, 0x3d,
0x0, 0x7c, 0x3d, 0x0, 0x3d, 0x3d, 0x0, 0x3d,
0x3d, 0x0, 0x3d, 0x3d, 0x0, 0x7c, 0x3d, 0x0,
0xfc, 0x3d, 0x2, 0xf4, 0x3d, 0x5f, 0xe0, 0x3f,
0xff, 0x80, 0x3f, 0xe4, 0x0,
/* U+0045 "E" */
0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0, 0xf,
0x80, 0x0, 0xf8, 0x0, 0xf, 0x80, 0x0, 0xf8,
0x0, 0xf, 0xff, 0xfc, 0xff, 0xff, 0xcf, 0x80,
0x0, 0xf8, 0x0, 0xf, 0x80, 0x0, 0xf8, 0x0,
0xf, 0xff, 0xff, 0xff, 0xff, 0xf0,
/* U+0046 "F" */
0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0, 0xf,
0x80, 0x0, 0xf8, 0x0, 0xf, 0x80, 0x0, 0xf8,
0x0, 0xf, 0xff, 0xfc, 0xff, 0xff, 0xcf, 0x80,
0x0, 0xf8, 0x0, 0xf, 0x80, 0x0, 0xf8, 0x0,
0xf, 0x80, 0x0, 0xf8, 0x0, 0x0,
/* U+0047 "G" */
0x0, 0x6f, 0xf0, 0x7, 0xff, 0xf0, 0x1f, 0xe4,
0x0, 0x3f, 0x0, 0x0, 0x7d, 0x0, 0x0, 0xbc,
0x0, 0x0, 0xfc, 0xf, 0xfc, 0xf8, 0xf, 0xfc,
0xfc, 0x0, 0x3c, 0xbc, 0x0, 0x3c, 0x7c, 0x0,
0x3c, 0x3f, 0x0, 0x3c, 0x2f, 0xd0, 0x7c, 0xb,
0xff, 0xfc, 0x1, 0xbf, 0xf8,
/* U+0048 "H" */
0x3d, 0x0, 0xf8, 0xf4, 0x3, 0xe3, 0xd0, 0xf,
0x8f, 0x40, 0x3e, 0x3d, 0x0, 0xf8, 0xf4, 0x3,
0xe3, 0xd0, 0xf, 0x8f, 0xff, 0xfe, 0x3f, 0xff,
0xf8, 0xf4, 0x3, 0xe3, 0xd0, 0xf, 0x8f, 0x40,
0x3e, 0x3d, 0x0, 0xf8, 0xf4, 0x3, 0xe3, 0xd0,
0xf, 0x80,
/* U+0049 "I" */
0x7f, 0xff, 0xca, 0xff, 0x90, 0x1f, 0x0, 0x7,
0xc0, 0x1, 0xf0, 0x0, 0x7c, 0x0, 0x1f, 0x0,
0x7, 0xc0, 0x1, 0xf0, 0x0, 0x7c, 0x0, 0x1f,
0x0, 0x7, 0xc0, 0x1, 0xf0, 0xa, 0xff, 0x97,
0xff, 0xfc,
/* U+004A "J" */
0x0, 0x0, 0x3e, 0x0, 0x0, 0x3e, 0x0, 0x0,
0x3e, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x3e, 0x0,
0x0, 0x3e, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x3e,
0x0, 0x0, 0x3e, 0x0, 0x0, 0x3e, 0xc, 0x0,
0x3e, 0x3f, 0x40, 0x3e, 0x1f, 0xe5, 0xfc, 0x3,
0xff, 0xf8, 0x0, 0x7f, 0xd0,
/* U+004B "K" */
0x3d, 0x0, 0x7e, 0xf, 0x40, 0x3f, 0x3, 0xd0,
0x3f, 0x0, 0xf4, 0x2f, 0x0, 0x3d, 0x1f, 0x40,
0xf, 0x5f, 0x80, 0x3, 0xef, 0xc0, 0x0, 0xff,
0xd0, 0x0, 0x3e, 0xfc, 0x0, 0xf, 0x4f, 0xc0,
0x3, 0xd1, 0xf8, 0x0, 0xf4, 0x2f, 0x40, 0x3d,
0x3, 0xf0, 0xf, 0x40, 0x3f, 0x3, 0xd0, 0x7,
0xe0,
/* U+004C "L" */
0xf8, 0x0, 0x3, 0xe0, 0x0, 0xf, 0x80, 0x0,
0x3e, 0x0, 0x0, 0xf8, 0x0, 0x3, 0xe0, 0x0,
0xf, 0x80, 0x0, 0x3e, 0x0, 0x0, 0xf8, 0x0,
0x3, 0xe0, 0x0, 0xf, 0x80, 0x0, 0x3e, 0x0,
0x0, 0xf8, 0x0, 0x3, 0xff, 0xff, 0xcf, 0xff,
0xff, 0x0,
/* U+004D "M" */
0x3e, 0x0, 0xf8, 0x3f, 0x1, 0xf8, 0x3f, 0x42,
0xf8, 0x7f, 0x83, 0xfc, 0x7e, 0xc7, 0xbc, 0x7d,
0xdf, 0x7c, 0x7c, 0xfd, 0x7c, 0x7c, 0xbc, 0x7c,
0x78, 0x7c, 0x7c, 0x78, 0x34, 0x3c, 0xb8, 0x20,
0x3c, 0xb8, 0x0, 0x3c, 0xb8, 0x0, 0x3c, 0xb8,
0x0, 0x3c, 0xb8, 0x0, 0x3c,
/* U+004E "N" */
0x3f, 0x0, 0xb8, 0xfc, 0x2, 0xe3, 0xf8, 0xb,
0x8f, 0xf0, 0x2e, 0x3f, 0xe0, 0xb8, 0xf7, 0xc2,
0xe3, 0xcf, 0x8b, 0x8f, 0x1f, 0x2e, 0x3c, 0x3d,
0xb8, 0xf0, 0x7e, 0xe3, 0xc0, 0xff, 0x8f, 0x2,
0xfe, 0x3c, 0x3, 0xf8, 0xf0, 0xb, 0xe3, 0xc0,
0xf, 0x80,
/* U+004F "O" */
0x2, 0xfe, 0x40, 0xf, 0xff, 0xd0, 0x2f, 0x47,
0xf0, 0x3d, 0x0, 0xf8, 0x7c, 0x0, 0xbc, 0xbc,
0x0, 0x7c, 0xf8, 0x0, 0x3d, 0xf8, 0x0, 0x3d,
0xf8, 0x0, 0x3d, 0xbc, 0x0, 0x7c, 0xbc, 0x0,
0xbc, 0x3d, 0x0, 0xf8, 0x2f, 0x43, 0xf0, 0xf,
0xff, 0xd0, 0x2, 0xfe, 0x40,
/* U+0050 "P" */
0x3f, 0xff, 0x80, 0x3f, 0xff, 0xf4, 0x3d, 0x1,
0xfc, 0x3d, 0x0, 0x7d, 0x3d, 0x0, 0x3e, 0x3d,
0x0, 0x3e, 0x3d, 0x0, 0x7d, 0x3d, 0x1, 0xfc,
0x3f, 0xff, 0xf0, 0x3f, 0xff, 0x80, 0x3d, 0x0,
0x0, 0x3d, 0x0, 0x0, 0x3d, 0x0, 0x0, 0x3d,
0x0, 0x0, 0x3d, 0x0, 0x0,
/* U+0051 "Q" */
0x2, 0xfe, 0x40, 0x3, 0xff, 0xf4, 0x2, 0xf4,
0x7f, 0x0, 0xf4, 0x3, 0xe0, 0x7c, 0x0, 0xbc,
0x2f, 0x0, 0x1f, 0xf, 0x80, 0x3, 0xd3, 0xe0,
0x0, 0xf4, 0xf8, 0x0, 0x3d, 0x2f, 0x0, 0x1f,
0xb, 0xc0, 0x7, 0xc0, 0xf4, 0x3, 0xe0, 0x2f,
0x42, 0xf0, 0x3, 0xff, 0xf8, 0x0, 0x2f, 0xef,
0x40, 0x0, 0x1, 0xf0, 0x0, 0x0, 0x3f, 0x0,
0x0, 0x7, 0xc0, 0x0, 0x0, 0x40,
/* U+0052 "R" */
0x3f, 0xff, 0x80, 0x3f, 0xff, 0xf0, 0x3d, 0x1,
0xf8, 0x3d, 0x0, 0xbc, 0x3d, 0x0, 0xbc, 0x3d,
0x0, 0xbc, 0x3d, 0x2, 0xf4, 0x3e, 0xbf, 0xd0,
0x3f, 0xf9, 0x0, 0x3e, 0xbd, 0x0, 0x3d, 0x1f,
0x40, 0x3d, 0xb, 0xd0, 0x3d, 0x3, 0xf0, 0x3d,
0x0, 0xfc, 0x3d, 0x0, 0x7e,
/* U+0053 "S" */
0x2, 0xff, 0xe0, 0x3f, 0xff, 0xd2, 0xf4, 0x16,
0xf, 0x80, 0x0, 0x3e, 0x0, 0x0, 0xfd, 0x0,
0x0, 0xff, 0x90, 0x0, 0xbf, 0xf4, 0x0, 0x1f,
0xf4, 0x0, 0x3, 0xf0, 0x0, 0xb, 0xc0, 0x0,
0x2f, 0x39, 0x2, 0xf9, 0xff, 0xff, 0xc1, 0xbf,
0xf4, 0x0,
/* U+0054 "T" */
0x3f, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xe0, 0x2,
0xf0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x1f, 0x0,
0x0, 0x7, 0xc0, 0x0, 0x1, 0xf0, 0x0, 0x0,
0x7c, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x7, 0xc0,
0x0, 0x1, 0xf0, 0x0, 0x0, 0x7c, 0x0, 0x0,
0x1f, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x1, 0xf0,
0x0,
/* U+0055 "U" */
0x7c, 0x0, 0xbd, 0xf0, 0x2, 0xf7, 0xc0, 0xb,
0xdf, 0x0, 0x2f, 0x7c, 0x0, 0xbd, 0xf0, 0x2,
0xf7, 0xc0, 0xb, 0xdf, 0x0, 0x2f, 0x7c, 0x0,
0xbd, 0xf0, 0x2, 0xf7, 0xc0, 0xb, 0xcf, 0x80,
0x3e, 0x3f, 0x97, 0xf4, 0x3f, 0xff, 0x80, 0x2f,
0xf4, 0x0,
/* U+0056 "V" */
0x3d, 0x0, 0xf, 0x8f, 0xc0, 0x3, 0xd1, 0xf0,
0x1, 0xf0, 0x3d, 0x0, 0xb8, 0xf, 0x80, 0x3d,
0x2, 0xf0, 0x1f, 0x0, 0x7c, 0xb, 0xc0, 0xf,
0x43, 0xd0, 0x2, 0xf0, 0xf0, 0x0, 0x7c, 0x7c,
0x0, 0xf, 0x6e, 0x0, 0x3, 0xef, 0x40, 0x0,
0x7f, 0xc0, 0x0, 0xf, 0xe0, 0x0, 0x3, 0xf4,
0x0,
/* U+0057 "W" */
0x3d, 0x0, 0xf, 0x8f, 0x40, 0x3, 0xd3, 0xe0,
0x0, 0xf4, 0xb8, 0x20, 0x3c, 0x2e, 0xd, 0xf,
0x7, 0xc3, 0x87, 0xc1, 0xf1, 0xf1, 0xf0, 0x3c,
0xbc, 0xb8, 0xf, 0x3b, 0x6e, 0x3, 0xdc, 0xeb,
0x40, 0xff, 0x2f, 0xd0, 0x2f, 0xc7, 0xf0, 0xb,
0xe0, 0xfc, 0x1, 0xf4, 0x3f, 0x0, 0x7c, 0xb,
0xc0,
/* U+0058 "X" */
0x2f, 0x0, 0x2f, 0x3, 0xe0, 0xf, 0x40, 0x7c,
0xb, 0xc0, 0xf, 0xc7, 0xd0, 0x1, 0xf7, 0xe0,
0x0, 0x2f, 0xf0, 0x0, 0x3, 0xf8, 0x0, 0x0,
0xfd, 0x0, 0x0, 0x7f, 0xc0, 0x0, 0x3e, 0xf4,
0x0, 0x2f, 0x2f, 0x0, 0xf, 0x43, 0xe0, 0xb,
0xc0, 0x7c, 0x7, 0xd0, 0xf, 0xc3, 0xe0, 0x1,
0xf4,
/* U+0059 "Y" */
0x3e, 0x0, 0xf, 0x8b, 0xd0, 0xb, 0xc0, 0xfc,
0x3, 0xd0, 0x1f, 0x42, 0xf0, 0x3, 0xf1, 0xf4,
0x0, 0x7d, 0xf8, 0x0, 0xb, 0xfc, 0x0, 0x0,
0xfe, 0x0, 0x0, 0x2f, 0x0, 0x0, 0x7, 0xc0,
0x0, 0x1, 0xf0, 0x0, 0x0, 0x7c, 0x0, 0x0,
0x1f, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x1, 0xf0,
0x0,
/* U+005A "Z" */
0x7f, 0xff, 0xfc, 0x7f, 0xff, 0xfc, 0x15, 0x56,
0xf4, 0x0, 0x3, 0xe0, 0x0, 0xf, 0xc0, 0x0,
0x2f, 0x40, 0x0, 0x3e, 0x0, 0x0, 0xfc, 0x0,
0x2, 0xf4, 0x0, 0x3, 0xe0, 0x0, 0xf, 0xc0,
0x0, 0x1f, 0x40, 0x0, 0x3f, 0x0, 0x0, 0xbf,
0xff, 0xfc, 0xbf, 0xff, 0xfc,
/* U+005B "[" */
0x0, 0x0, 0xf, 0xff, 0xe3, 0xff, 0xf8, 0xf4,
0x0, 0x3d, 0x0, 0xf, 0x40, 0x3, 0xd0, 0x0,
0xf4, 0x0, 0x3d, 0x0, 0xf, 0x40, 0x3, 0xd0,
0x0, 0xf4, 0x0, 0x3d, 0x0, 0xf, 0x40, 0x3,
0xd0, 0x0, 0xf4, 0x0, 0x3d, 0x0, 0xf, 0x40,
0x3, 0xd0, 0x0, 0xf4, 0x0, 0x3f, 0xff, 0x8f,
0xff, 0xe0,
/* U+005C "\\" */
0x18, 0x0, 0x0, 0xb, 0xc0, 0x0, 0x0, 0xf0,
0x0, 0x0, 0x2e, 0x0, 0x0, 0x3, 0xc0, 0x0,
0x0, 0xb8, 0x0, 0x0, 0xf, 0x0, 0x0, 0x3,
0xe0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0xf, 0x80,
0x0, 0x1, 0xf0, 0x0, 0x0, 0x3e, 0x0, 0x0,
0x7, 0xc0, 0x0, 0x0, 0xf4, 0x0, 0x0, 0x1f,
0x0, 0x0, 0x3, 0xd0, 0x0, 0x0, 0x7c, 0x0,
0x0, 0xf, 0x40, 0x0, 0x2, 0xf0, 0x0, 0x0,
0x3d,
/* U+005D "]" */
0x0, 0x0, 0xf, 0xff, 0xe3, 0xff, 0xf8, 0x0,
0x3e, 0x0, 0xf, 0x80, 0x3, 0xe0, 0x0, 0xf8,
0x0, 0x3e, 0x0, 0xf, 0x80, 0x3, 0xe0, 0x0,
0xf8, 0x0, 0x3e, 0x0, 0xf, 0x80, 0x3, 0xe0,
0x0, 0xf8, 0x0, 0x3e, 0x0, 0xf, 0x80, 0x3,
0xe0, 0x0, 0xf8, 0x0, 0x3e, 0x3f, 0xff, 0x8f,
0xff, 0xe0,
/* U+005E "^" */
0x0, 0x1a, 0x0, 0x0, 0xf, 0xe0, 0x0, 0xf,
0xbd, 0x0, 0x7, 0xcb, 0xc0, 0x3, 0xe0, 0xf4,
0x1, 0xf0, 0x2f, 0x0, 0xf8, 0x3, 0xd0, 0x7c,
0x0, 0xbc, 0x3e, 0x0, 0xf, 0x40,
/* U+005F "_" */
0x15, 0x55, 0x55, 0xff, 0xff, 0xf7, 0xff, 0xff,
0xc0,
/* U+0060 "`" */
0x1a, 0x40, 0x1f, 0xc0, 0xf, 0xd0, 0x3, 0xf0,
0x2, 0xf4, 0x0, 0xf8, 0x0, 0x7c, 0x0, 0x3e,
/* U+0061 "a" */
0x1f, 0xff, 0x40, 0x2f, 0xff, 0xe0, 0x4, 0x2,
0xf0, 0x0, 0x1, 0xf0, 0x1, 0x6b, 0xf0, 0x2f,
0xff, 0xf0, 0x7e, 0x41, 0xf0, 0xb8, 0x1, 0xf0,
0xbc, 0xb, 0xf0, 0x7f, 0xff, 0xfe, 0x1f, 0xe4,
0xbe,
/* U+0062 "b" */
0x3d, 0x0, 0x0, 0x3d, 0x0, 0x0, 0x3d, 0x0,
0x0, 0x3d, 0x0, 0x0, 0x3d, 0x0, 0x0, 0x3d,
0x7f, 0x80, 0x3f, 0xff, 0xf0, 0x3f, 0x82, 0xf8,
0x3d, 0x0, 0xbc, 0x3d, 0x0, 0x7d, 0x3d, 0x0,
0x3d, 0x3d, 0x0, 0x7c, 0x3d, 0x0, 0xfc, 0x3e,
0x47, 0xf4, 0x3f, 0xff, 0xe0, 0x36, 0xfe, 0x40,
/* U+0063 "c" */
0x1, 0xff, 0x80, 0xb, 0xff, 0xf8, 0x2f, 0x91,
0xb8, 0x3e, 0x0, 0x0, 0xbc, 0x0, 0x0, 0xbc,
0x0, 0x0, 0xbc, 0x0, 0x0, 0x7e, 0x0, 0x0,
0x3f, 0x81, 0xb8, 0xf, 0xff, 0xf8, 0x2, 0xff,
0x80,
/* U+0064 "d" */
0x0, 0x0, 0xf8, 0x0, 0x3, 0xe0, 0x0, 0xf,
0x80, 0x0, 0x3e, 0x0, 0x0, 0xf8, 0xb, 0xf7,
0xe1, 0xff, 0xff, 0x8f, 0xd0, 0xbe, 0x7d, 0x0,
0xfb, 0xf0, 0x3, 0xef, 0x80, 0xf, 0xbf, 0x0,
0x3e, 0xbc, 0x0, 0xf8, 0xfd, 0x1f, 0xe2, 0xff,
0xff, 0x81, 0xfe, 0x2e,
/* U+0065 "e" */
0x1, 0xff, 0x40, 0xf, 0xff, 0xf0, 0x2f, 0x41,
0xf8, 0x3c, 0x0, 0xbc, 0x7f, 0xff, 0xfc, 0xbf,
0xff, 0xfc, 0x7c, 0x0, 0x0, 0x7d, 0x0, 0x0,
0x3f, 0x40, 0x0, 0x1f, 0xff, 0xc0, 0x2, 0xff,
0xc0,
/* U+0066 "f" */
0x0, 0x1b, 0xfc, 0x0, 0x7f, 0xfc, 0x0, 0xfd,
0x0, 0x0, 0xf4, 0x0, 0x0, 0xf4, 0x0, 0x7f,
0xff, 0xf4, 0x7f, 0xff, 0xf4, 0x0, 0xf4, 0x0,
0x0, 0xf4, 0x0, 0x0, 0xf4, 0x0, 0x0, 0xf4,
0x0, 0x0, 0xf4, 0x0, 0x0, 0xf4, 0x0, 0x0,
0xf4, 0x0, 0x0, 0xf4, 0x0, 0x0, 0xf4, 0x0,
/* U+0067 "g" */
0x2, 0xff, 0x68, 0x3f, 0xff, 0xe3, 0xf9, 0x1f,
0x9f, 0x40, 0x3e, 0xbc, 0x0, 0xfb, 0xe0, 0x3,
0xef, 0xc0, 0xf, 0xaf, 0x0, 0x3e, 0x3f, 0x47,
0xf8, 0xbf, 0xff, 0xe0, 0x7f, 0x8f, 0x80, 0x0,
0x3e, 0x0, 0x0, 0xf8, 0x0, 0x1f, 0xd0, 0xff,
0xfe, 0x3, 0xff, 0xd0,
/* U+0068 "h" */
0x3d, 0x0, 0x0, 0xf4, 0x0, 0x3, 0xd0, 0x0,
0xf, 0x40, 0x0, 0x3d, 0x0, 0x0, 0xf4, 0x7f,
0x43, 0xdb, 0xff, 0xf, 0xfd, 0xbe, 0x3f, 0x40,
0xf8, 0xf8, 0x3, 0xe3, 0xd0, 0xf, 0x8f, 0x40,
0x3e, 0x3d, 0x0, 0xf8, 0xf4, 0x3, 0xe3, 0xd0,
0xf, 0x8f, 0x40, 0x3e,
/* U+0069 "i" */
0x0, 0x50, 0x0, 0x3f, 0x80, 0x3, 0xfc, 0x0,
0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f,
0xf4, 0x7, 0xff, 0x40, 0x1, 0xf4, 0x0, 0xf,
0x40, 0x0, 0xf4, 0x0, 0xf, 0x40, 0x0, 0xf4,
0x0, 0xf, 0x40, 0x1, 0xf4, 0xb, 0xff, 0xff,
0xbf, 0xff, 0xf0,
/* U+006A "j" */
0x0, 0x14, 0x0, 0x1f, 0xc0, 0xb, 0xf0, 0x0,
0xa4, 0x0, 0x0, 0x0, 0x0, 0xb, 0xff, 0xe2,
0xff, 0xf8, 0x0, 0x3e, 0x0, 0xf, 0x80, 0x3,
0xe0, 0x0, 0xf8, 0x0, 0x3e, 0x0, 0xf, 0x80,
0x3, 0xe0, 0x0, 0xf8, 0x0, 0x3e, 0x0, 0xf,
0x80, 0x3, 0xe0, 0x2, 0xf4, 0xff, 0xfc, 0x2f,
0xf8, 0x0,
/* U+006B "k" */
0xf8, 0x0, 0x3, 0xe0, 0x0, 0xf, 0x80, 0x0,
0x3e, 0x0, 0x0, 0xf8, 0x0, 0x3, 0xe0, 0xf,
0xcf, 0x80, 0xfc, 0x3e, 0xf, 0xc0, 0xf8, 0xfc,
0x3, 0xef, 0xc0, 0xf, 0xfd, 0x0, 0x3e, 0xbd,
0x0, 0xf8, 0xfd, 0x3, 0xe0, 0xfc, 0xf, 0x80,
0xfc, 0x3e, 0x0, 0xfc,
/* U+006C "l" */
0xbf, 0xf8, 0x2, 0xff, 0xe0, 0x0, 0xf, 0x80,
0x0, 0x3e, 0x0, 0x0, 0xf8, 0x0, 0x3, 0xe0,
0x0, 0xf, 0x80, 0x0, 0x3e, 0x0, 0x0, 0xf8,
0x0, 0x3, 0xe0, 0x0, 0xf, 0x80, 0x0, 0x3e,
0x0, 0x0, 0xf8, 0x0, 0x3, 0xf4, 0x0, 0x7,
0xff, 0xc0, 0xb, 0xfe,
/* U+006D "m" */
0xf0, 0xf4, 0x7c, 0xf3, 0xf8, 0xfd, 0xfb, 0xff,
0xfe, 0xfc, 0x7e, 0x2e, 0xf0, 0x3c, 0x2e, 0xf0,
0x3c, 0x2e, 0xf0, 0x3c, 0x2e, 0xf0, 0x3c, 0x2e,
0xf0, 0x3c, 0x2e, 0xf0, 0x3c, 0x2e, 0xf0, 0x3c,
0x2e,
/* U+006E "n" */
0x3c, 0x1f, 0xd0, 0xf7, 0xff, 0xc3, 0xfe, 0x5f,
0x8f, 0xd0, 0x3e, 0x3d, 0x0, 0xf8, 0xf4, 0x3,
0xe3, 0xd0, 0xf, 0x8f, 0x40, 0x3e, 0x3d, 0x0,
0xf8, 0xf4, 0x3, 0xe3, 0xd0, 0xf, 0x80,
/* U+006F "o" */
0x2, 0xfe, 0x40, 0xf, 0xff, 0xe0, 0x3f, 0x46,
0xf4, 0x7d, 0x0, 0xfc, 0xbc, 0x0, 0x7c, 0xbc,
0x0, 0x7d, 0xbc, 0x0, 0x7c, 0x7d, 0x0, 0xfc,
0x3f, 0x42, 0xf4, 0xf, 0xff, 0xe0, 0x2, 0xfe,
0x40,
/* U+0070 "p" */
0x3c, 0x7f, 0x80, 0x3e, 0xff, 0xf0, 0x3f, 0x41,
0xf8, 0x3d, 0x0, 0xbc, 0x3d, 0x0, 0x7d, 0x3d,
0x0, 0x3d, 0x3d, 0x0, 0x7c, 0x3d, 0x0, 0xfc,
0x3f, 0x42, 0xf8, 0x3f, 0xff, 0xf0, 0x3d, 0xbf,
0x40, 0x3d, 0x0, 0x0, 0x3d, 0x0, 0x0, 0x3d,
0x0, 0x0, 0x3d, 0x0, 0x0, 0x3d, 0x0, 0x0,
/* U+0071 "q" */
0x2, 0xff, 0x68, 0x3f, 0xff, 0xe3, 0xf8, 0x1f,
0x9f, 0x40, 0x3e, 0xbc, 0x0, 0xfb, 0xe0, 0x3,
0xef, 0xc0, 0xf, 0xaf, 0x0, 0x3e, 0x3f, 0x47,
0xf8, 0xbf, 0xff, 0xe0, 0x7f, 0x8f, 0x80, 0x0,
0x3e, 0x0, 0x0, 0xf8, 0x0, 0x3, 0xe0, 0x0,
0xf, 0x80, 0x0, 0x3e,
/* U+0072 "r" */
0x0, 0x0, 0x0, 0x7f, 0xc1, 0xfd, 0x7f, 0xcb,
0xfd, 0x7, 0xdf, 0xe8, 0x7, 0xfc, 0x0, 0x7,
0xf0, 0x0, 0x7, 0xd0, 0x0, 0x7, 0xc0, 0x0,
0x7, 0xc0, 0x0, 0x7, 0xc0, 0x0, 0xff, 0xff,
0x0, 0xff, 0xff, 0x0,
/* U+0073 "s" */
0x2, 0xff, 0xe0, 0x7f, 0xff, 0xc3, 0xf0, 0x1,
0xf, 0xc0, 0x0, 0x2f, 0xf9, 0x0, 0x1f, 0xff,
0x80, 0x1, 0xbf, 0x40, 0x0, 0x3e, 0x14, 0x1,
0xf8, 0xff, 0xff, 0xc2, 0xff, 0xf4, 0x0,
/* U+0074 "t" */
0x1, 0xf0, 0x0, 0x7, 0xc0, 0x0, 0x1f, 0x0,
0x0, 0x7c, 0x0, 0xff, 0xff, 0xf7, 0xff, 0xff,
0xd0, 0x1f, 0x0, 0x0, 0x7c, 0x0, 0x1, 0xf0,
0x0, 0x7, 0xc0, 0x0, 0x1f, 0x0, 0x0, 0x7c,
0x0, 0x1, 0xf8, 0x0, 0x3, 0xff, 0xf0, 0x2,
0xff, 0xc0,
/* U+0075 "u" */
0x3d, 0x0, 0xf8, 0xf4, 0x3, 0xe3, 0xd0, 0xf,
0x8f, 0x40, 0x3e, 0x3d, 0x0, 0xf8, 0xf4, 0x3,
0xe3, 0xd0, 0xf, 0x8f, 0x40, 0xbe, 0x3f, 0x1f,
0xf8, 0xbf, 0xf6, 0xe0, 0xbe, 0xb, 0x80,
/* U+0076 "v" */
0xfc, 0x0, 0x7d, 0x7d, 0x0, 0xbc, 0x3e, 0x0,
0xf8, 0x2f, 0x1, 0xf0, 0xf, 0x42, 0xf0, 0xf,
0x83, 0xd0, 0x7, 0xc7, 0xc0, 0x3, 0xdf, 0x80,
0x2, 0xff, 0x0, 0x1, 0xff, 0x0, 0x0, 0xfd,
0x0,
/* U+0077 "w" */
0x3c, 0x0, 0xb, 0x8f, 0x41, 0x3, 0xd2, 0xe0,
0xd0, 0xf4, 0xb8, 0x38, 0x3c, 0x1f, 0x1f, 0x1f,
0x3, 0xcf, 0xdb, 0x80, 0xf7, 0xba, 0xe0, 0x3e,
0xcf, 0xf4, 0xb, 0xf1, 0xfc, 0x1, 0xf8, 0x3f,
0x0, 0x7c, 0xf, 0xc0,
/* U+0078 "x" */
0x7e, 0x0, 0xf8, 0x2f, 0x43, 0xf0, 0xf, 0xc7,
0xd0, 0x3, 0xef, 0x80, 0x1, 0xff, 0x0, 0x0,
0xfd, 0x0, 0x2, 0xff, 0x0, 0x7, 0xdf, 0xc0,
0xf, 0x83, 0xe0, 0x3f, 0x2, 0xf4, 0xbd, 0x0,
0xfc,
/* U+0079 "y" */
0x3f, 0x0, 0x1f, 0x47, 0xd0, 0xb, 0xc0, 0xf8,
0x3, 0xe0, 0x2f, 0x1, 0xf0, 0x3, 0xd0, 0xbc,
0x0, 0xfc, 0x3d, 0x0, 0x1f, 0x1f, 0x0, 0x3,
0xef, 0x80, 0x0, 0xbf, 0xc0, 0x0, 0xf, 0xf0,
0x0, 0x3, 0xf4, 0x0, 0x0, 0xbc, 0x0, 0x0,
0x3e, 0x0, 0x0, 0x2f, 0x0, 0x3, 0xff, 0x80,
0x0, 0xff, 0x80, 0x0,
/* U+007A "z" */
0x3f, 0xff, 0xf8, 0xff, 0xff, 0xd0, 0x0, 0x7f,
0x0, 0x3, 0xf0, 0x0, 0x3f, 0x0, 0x3, 0xf4,
0x0, 0x2f, 0x40, 0x2, 0xf4, 0x0, 0x2f, 0x80,
0x0, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xc0,
/* U+007B "{" */
0x0, 0x0, 0x0, 0x0, 0x1a, 0xfc, 0x1, 0xff,
0xfd, 0x3, 0xf9, 0x40, 0x3, 0xf0, 0x0, 0x0,
0xf8, 0x0, 0x0, 0x7e, 0x0, 0x0, 0x1f, 0x40,
0x0, 0x1f, 0x80, 0x1, 0xbf, 0x0, 0x7f, 0xe4,
0x0, 0xbd, 0x0, 0x0, 0x7f, 0xe4, 0x0, 0x1,
0xff, 0x0, 0x0, 0x1f, 0x80, 0x0, 0x1f, 0x40,
0x0, 0x7e, 0x0, 0x0, 0xf8, 0x0, 0x3, 0xf0,
0x0, 0x3, 0xf9, 0x40, 0x1, 0xff, 0xfd, 0x0,
0x1a, 0xfc, 0x0, 0x0, 0x0,
/* U+007C "|" */
0x15, 0xf7, 0xdf, 0x7d, 0xf7, 0xdf, 0x7d, 0xf7,
0xdf, 0x7d, 0xf7, 0xdf, 0x7d, 0xf7, 0xdf,
/* U+007D "}" */
0x0, 0x0, 0x0, 0xbf, 0x90, 0x0, 0xff, 0xff,
0x0, 0x5, 0xbf, 0x40, 0x0, 0x1f, 0x40, 0x0,
0x7e, 0x0, 0x0, 0xfc, 0x0, 0x3, 0xf0, 0x0,
0x3, 0xe0, 0x0, 0x2, 0xfe, 0x40, 0x0, 0x1f,
0xf8, 0x0, 0x0, 0xbc, 0x0, 0x1f, 0xf8, 0x2,
0xfe, 0x40, 0x3, 0xf0, 0x0, 0x3, 0xf0, 0x0,
0x0, 0xfc, 0x0, 0x0, 0x7e, 0x0, 0x0, 0x1f,
0x40, 0x1, 0xbf, 0x40, 0xff, 0xff, 0x0, 0xbf,
0x94, 0x0, 0x0, 0x0, 0x0,
/* U+007E "~" */
0x0, 0x40, 0x1, 0x1, 0xfc, 0x2, 0xe0, 0xff,
0xd0, 0xf0, 0xbc, 0xbe, 0xbc, 0x3d, 0x7, 0xfd,
0xf, 0x0, 0x3e, 0x0, 0x0, 0x0, 0x0
};
/*---------------------
* GLYPH DESCRIPTION
*--------------------*/
static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */,
{.bitmap_index = 0, .adv_w = 216, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 0, .adv_w = 216, .box_w = 6, .box_h = 16, .ofs_x = 4, .ofs_y = 0},
{.bitmap_index = 24, .adv_w = 216, .box_w = 10, .box_h = 8, .ofs_x = 2, .ofs_y = 8},
{.bitmap_index = 44, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 93, .adv_w = 216, .box_w = 11, .box_h = 22, .ofs_x = 1, .ofs_y = -4},
{.bitmap_index = 154, .adv_w = 216, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 207, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 252, .adv_w = 216, .box_w = 4, .box_h = 8, .ofs_x = 5, .ofs_y = 8},
{.bitmap_index = 260, .adv_w = 216, .box_w = 10, .box_h = 23, .ofs_x = 2, .ofs_y = -4},
{.bitmap_index = 318, .adv_w = 216, .box_w = 11, .box_h = 23, .ofs_x = 1, .ofs_y = -4},
{.bitmap_index = 382, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 3},
{.bitmap_index = 415, .adv_w = 216, .box_w = 12, .box_h = 12, .ofs_x = 1, .ofs_y = 2},
{.bitmap_index = 451, .adv_w = 216, .box_w = 8, .box_h = 7, .ofs_x = 2, .ofs_y = -4},
{.bitmap_index = 465, .adv_w = 216, .box_w = 9, .box_h = 3, .ofs_x = 2, .ofs_y = 6},
{.bitmap_index = 472, .adv_w = 216, .box_w = 6, .box_h = 5, .ofs_x = 4, .ofs_y = 0},
{.bitmap_index = 480, .adv_w = 216, .box_w = 13, .box_h = 20, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 545, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 590, .adv_w = 216, .box_w = 10, .box_h = 15, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 628, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 673, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 715, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 760, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 802, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 847, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 889, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 931, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 976, .adv_w = 216, .box_w = 6, .box_h = 11, .ofs_x = 4, .ofs_y = 0},
{.bitmap_index = 993, .adv_w = 216, .box_w = 8, .box_h = 15, .ofs_x = 2, .ofs_y = -4},
{.bitmap_index = 1023, .adv_w = 216, .box_w = 11, .box_h = 13, .ofs_x = 1, .ofs_y = 1},
{.bitmap_index = 1059, .adv_w = 216, .box_w = 11, .box_h = 7, .ofs_x = 1, .ofs_y = 4},
{.bitmap_index = 1079, .adv_w = 216, .box_w = 12, .box_h = 13, .ofs_x = 1, .ofs_y = 1},
{.bitmap_index = 1118, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1162, .adv_w = 216, .box_w = 12, .box_h = 19, .ofs_x = 1, .ofs_y = -3},
{.bitmap_index = 1219, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 1268, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1313, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1358, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1403, .adv_w = 216, .box_w = 10, .box_h = 15, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 1441, .adv_w = 216, .box_w = 10, .box_h = 15, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 1479, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1524, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1566, .adv_w = 216, .box_w = 9, .box_h = 15, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 1600, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 1645, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1694, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 1736, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1781, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1823, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1868, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 1913, .adv_w = 216, .box_w = 13, .box_h = 19, .ofs_x = 1, .ofs_y = -4},
{.bitmap_index = 1975, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2020, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2062, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 2111, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2153, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 2202, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 2251, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 2300, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 2349, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2394, .adv_w = 216, .box_w = 9, .box_h = 22, .ofs_x = 3, .ofs_y = -3},
{.bitmap_index = 2444, .adv_w = 216, .box_w = 13, .box_h = 20, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 2509, .adv_w = 216, .box_w = 9, .box_h = 22, .ofs_x = 1, .ofs_y = -3},
{.bitmap_index = 2559, .adv_w = 216, .box_w = 13, .box_h = 9, .ofs_x = 0, .ofs_y = 6},
{.bitmap_index = 2589, .adv_w = 216, .box_w = 11, .box_h = 3, .ofs_x = 1, .ofs_y = -3},
{.bitmap_index = 2598, .adv_w = 216, .box_w = 8, .box_h = 8, .ofs_x = 2, .ofs_y = 9},
{.bitmap_index = 2614, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2647, .adv_w = 216, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2695, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2728, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2772, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2805, .adv_w = 216, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2853, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 1, .ofs_y = -5},
{.bitmap_index = 2897, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 2941, .adv_w = 216, .box_w = 10, .box_h = 17, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 2984, .adv_w = 216, .box_w = 9, .box_h = 22, .ofs_x = 2, .ofs_y = -5},
{.bitmap_index = 3034, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 2, .ofs_y = 0},
{.bitmap_index = 3078, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 3122, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 3155, .adv_w = 216, .box_w = 11, .box_h = 11, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 3186, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 3219, .adv_w = 216, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = -5},
{.bitmap_index = 3267, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 1, .ofs_y = -5},
{.bitmap_index = 3311, .adv_w = 216, .box_w = 12, .box_h = 12, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 3347, .adv_w = 216, .box_w = 11, .box_h = 11, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 3378, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 3420, .adv_w = 216, .box_w = 11, .box_h = 11, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 3451, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 3484, .adv_w = 216, .box_w = 13, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 3520, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 3553, .adv_w = 216, .box_w = 13, .box_h = 16, .ofs_x = 0, .ofs_y = -5},
{.bitmap_index = 3605, .adv_w = 216, .box_w = 11, .box_h = 11, .ofs_x = 1, .ofs_y = 0},
{.bitmap_index = 3636, .adv_w = 216, .box_w = 12, .box_h = 23, .ofs_x = 1, .ofs_y = -4},
{.bitmap_index = 3705, .adv_w = 216, .box_w = 3, .box_h = 20, .ofs_x = 5, .ofs_y = -2},
{.bitmap_index = 3720, .adv_w = 216, .box_w = 12, .box_h = 23, .ofs_x = 1, .ofs_y = -4},
{.bitmap_index = 3789, .adv_w = 216, .box_w = 13, .box_h = 7, .ofs_x = 0, .ofs_y = 4}
};
/*---------------------
* CHARACTER MAPPING
*--------------------*/
/*Collect the unicode lists and glyph_id offsets*/
static const lv_font_fmt_txt_cmap_t cmaps[] =
{
{
.range_start = 32, .range_length = 95, .glyph_id_start = 1,
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
}
};
/*--------------------
* ALL CUSTOM DATA
*--------------------*/
static lv_font_fmt_txt_dsc_t font_dsc = {
.glyph_bitmap = glyph_bitmap,
.glyph_dsc = glyph_dsc,
.cmaps = cmaps,
.kern_dsc = NULL,
.kern_scale = 0,
.cmap_num = 1,
.bpp = 2,
.kern_classes = 0,
};
/*-----------------
* PUBLIC FONT
*----------------*/
/*Initialize a public general font descriptor*/
const lv_font_t intel_one_mono = {
.dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */
//.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/
//.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/
.line_height = 24, /*The maximum line height required by the font*/
.base_line = 5, /*Baseline measured from the bottom of the line*/
};
#endif /*#if INTEL_ONE_MONO*/

View file

@ -0,0 +1,12 @@
add_executable(
mandelbrot
mandelbrot.cpp
mandelf.c
)
# Pull in pico libraries that we need
target_link_libraries(mandelbrot pico_stdlib pico_multicore pico_dvhstx)
pico_enable_stdio_usb(mandelbrot 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(mandelbrot)

View file

@ -0,0 +1,128 @@
#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 360
//#define FRAME_WIDTH 400
//#define FRAME_HEIGHT 300
//#define FRAME_WIDTH 320
//#define FRAME_HEIGHT 180
static DVHSTX display;
static PicoGraphics_PenDVHSTX_P8 graphics(FRAME_WIDTH, FRAME_HEIGHT, display);
static FractalBuffer fractal;
static void init_palette() {
graphics.create_pen(0, 0, 0);
for (int i = 0; i < 64; ++i) {
graphics.create_pen_hsv(i * (1.f / 63.f), 1.0f, 0.5f + (i & 7) * (0.5f / 7.f));
}
}
static void init_mandel() {
fractal.rows = FRAME_HEIGHT / 2;
fractal.cols = FRAME_WIDTH;
fractal.max_iter = 63;
fractal.iter_offset = 0;
fractal.minx = -2.25f;
fractal.maxx = 0.75f;
fractal.miny = -1.6f;
fractal.maxy = 0.f - (1.6f / FRAME_HEIGHT); // Half a row
fractal.use_cycle_check = true;
init_fractal(&fractal);
}
#define NUM_ZOOMS 100
static uint32_t zoom_count = 0;
static void zoom_mandel() {
if (++zoom_count == NUM_ZOOMS)
{
init_mandel();
zoom_count = 0;
sleep_ms(2000);
return;
}
float zoomx = -.75f - .7f * ((float)zoom_count / (float)NUM_ZOOMS);
float sizex = fractal.maxx - fractal.minx;
float sizey = fractal.miny * -2.f;
float zoomr = 0.974f * 0.5f;
fractal.minx = zoomx - zoomr * sizex;
fractal.maxx = zoomx + zoomr * sizex;
fractal.miny = -zoomr * sizey;
fractal.maxy = 0.f + fractal.miny / FRAME_HEIGHT;
init_fractal(&fractal);
}
static void display_row(int y, uint8_t* buf) {
display.write_palette_pixel_span({0, y}, FRAME_WIDTH, buf);
display.write_palette_pixel_span({0, FRAME_HEIGHT - 1 - y}, FRAME_WIDTH, buf);
}
static uint8_t row_buf_core1[FRAME_WIDTH];
void core1_main() {
while (true) {
int y = multicore_fifo_pop_blocking();
generate_one_line(&fractal, row_buf_core1, y);
multicore_fifo_push_blocking(y);
}
}
static uint8_t row_buf[FRAME_WIDTH];
static void draw_two_rows(int y) {
multicore_fifo_push_blocking(y+1);
generate_one_line(&fractal, row_buf, y);
display_row(y, row_buf);
multicore_fifo_pop_blocking();
display_row(y+1, row_buf_core1);
}
void draw_mandel() {
//display.wait_for_flip();
for (int y = 0; y < FRAME_HEIGHT / 2; y += 2)
{
draw_two_rows(y);
}
//display.flip_async();
display.flip_now();
}
int main() {
stdio_init_all();
display.init(FRAME_WIDTH, FRAME_HEIGHT, DVHSTX::MODE_PALETTE);
init_palette();
graphics.set_pen(0);
graphics.clear();
display.flip_now();
graphics.clear();
multicore_launch_core1(core1_main);
init_mandel();
draw_mandel();
while(true) {
absolute_time_t start_time = get_absolute_time();
zoom_mandel();
draw_mandel();
printf("Drawing zoom %ld took %.2fms\n", zoom_count, absolute_time_diff_us(start_time, get_absolute_time()) * 0.001f);
}
}

63
examples/dvhstx/mandelf.c Normal file
View file

@ -0,0 +1,63 @@
// Copyright (C) Michael Bell 2021
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "pico/stdlib.h"
#include "mandelf.h"
// Cycle checking parameters
#define MAX_CYCLE_LEN 8 // Must be power of 2
#define MIN_CYCLE_CHECK_ITER 24 // Must be multiple of max cycle len
#define CYCLE_TOLERANCE 4e-3
#define ESCAPE_SQUARE 4.f
void init_fractal(FractalBuffer* f)
{
f->done = false;
f->min_iter = f->max_iter - 1;
f->incx = (f->maxx - f->minx) / (f->cols - 1);
f->incy = (f->maxy - f->miny) / (f->rows - 1);
f->count_inside = 0;
}
static inline void generate_one(FractalBuffer* f, float x0, float y0, uint8_t* buffptr)
{
float x = x0;
float y = y0;
uint16_t k = 1;
for (; k < f->max_iter; ++k) {
float x_square = x*x;
float y_square = y*y;
if (x_square + y_square > ESCAPE_SQUARE) break;
float nextx = x_square - y_square + x0;
y = x*y*2.f + y0;
x = nextx;
}
if (k == f->max_iter) {
*buffptr = 0;
f->count_inside++;
} else {
if (k > f->iter_offset) k -= f->iter_offset;
else k = 1;
*buffptr = k;
if (f->min_iter > k) f->min_iter = k;
}
}
void generate_one_line(FractalBuffer* f, uint8_t* buf, uint16_t ipos)
{
if (f->done) return;
float y0 = f->miny + ipos * f->incy;
float x0 = f->minx;
for (int16_t j = 0; j < f->cols; ++j) {
generate_one(f, x0, y0, buf++);
x0 += f->incx;
}
}

24
examples/dvhstx/mandelf.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
typedef struct {
// Configuration
int16_t rows;
int16_t cols;
uint16_t max_iter;
uint16_t iter_offset;
float minx, miny, maxx, maxy;
bool use_cycle_check;
// State
volatile bool done;
volatile uint16_t min_iter;
float incx, incy;
volatile uint32_t count_inside;
} FractalBuffer;
// Generate a section of the fractal into buff
// Result written to buff is 0 for inside Mandelbrot set
// Otherwise iteration of escape minus min_iter (clamped to 1)
void init_fractal(FractalBuffer* fractal);
void generate_one_line(FractalBuffer* f, uint8_t* buf, uint16_t row);

View file

@ -0,0 +1,88 @@
#include "pico_graphics.hpp"
#include "dvhstx.hpp"
namespace pimoroni {
enum BlendMode {
TARGET = 0,
FIXED = 1,
};
class PicoGraphicsDVHSTX : public PicoGraphics {
public:
DVHSTX &driver;
BlendMode blend_mode = BlendMode::TARGET;
void set_blend_mode(BlendMode mode) {
blend_mode = mode;
}
virtual void set_depth(uint8_t new_depth) {}
virtual void set_bg(uint c) {};
PicoGraphicsDVHSTX(uint16_t width, uint16_t height, DVHSTX &dv_display)
: PicoGraphics(width, height, nullptr),
driver(dv_display)
{
this->pen_type = PEN_DV_RGB555;
}
};
class PicoGraphics_PenDVHSTX_RGB565 : public PicoGraphicsDVHSTX {
public:
RGB565 color;
RGB565 background;
uint16_t depth = 0;
PicoGraphics_PenDVHSTX_RGB565(uint16_t width, uint16_t height, DVHSTX &dv_display);
void set_pen(uint c) override;
void set_bg(uint c) override;
void set_depth(uint8_t new_depth) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
int create_pen_hsv(float h, float s, float v) override;
void set_pixel(const Point &p) override;
void set_pixel_span(const Point &p, uint l) override;
void set_pixel_alpha(const Point &p, const uint8_t a) override;
bool supports_alpha_blend() override {return true;}
bool render_pico_vector_tile(const Rect &bounds, uint8_t* alpha_data, uint32_t stride, uint8_t alpha_type) override;
static size_t buffer_size(uint w, uint h) {
return w * h * sizeof(RGB565);
}
};
class PicoGraphics_PenDVHSTX_P8 : public PicoGraphicsDVHSTX {
public:
static const uint16_t palette_size = 256;
uint8_t color;
uint8_t depth = 0;
bool used[palette_size];
std::array<std::array<uint8_t, 16>, 512> candidate_cache;
std::array<uint8_t, 16> candidates;
bool cache_built = false;
PicoGraphics_PenDVHSTX_P8(uint16_t width, uint16_t height, DVHSTX &dv_display);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
void set_depth(uint8_t new_depth) override;
int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override;
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
int create_pen_hsv(float h, float s, float v) override;
int reset_pen(uint8_t i) override;
int get_palette_size() override {return 0;};
RGB* get_palette() override {return nullptr;};
void set_pixel(const Point &p) override;
void set_pixel_span(const Point &p, uint l) override;
void get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array<uint8_t, 16> &candidates);
void set_pixel_dither(const Point &p, const RGB &c) override;
static size_t buffer_size(uint w, uint h) {
return w * h;
}
};
}

View file

@ -0,0 +1,112 @@
#include "pico_graphics_dvhstx.hpp"
namespace pimoroni {
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;
}
PicoGraphics_PenDVHSTX_P8::PicoGraphics_PenDVHSTX_P8(uint16_t width, uint16_t height, DVHSTX &dv_display)
: PicoGraphicsDVHSTX(width, height, dv_display)
{
this->pen_type = PEN_DV_P5;
for(auto i = 0u; i < palette_size; i++) {
driver.set_palette_colour(i, RGB_to_RGB888(i, i, i) << 3);
used[i] = false;
}
cache_built = false;
}
void PicoGraphics_PenDVHSTX_P8::set_pen(uint c) {
color = c;
}
void PicoGraphics_PenDVHSTX_P8::set_depth(uint8_t new_depth) {
depth = new_depth > 0 ? 1 : 0;
}
void PicoGraphics_PenDVHSTX_P8::set_pen(uint8_t r, uint8_t g, uint8_t b) {
RGB888 *driver_palette = driver.get_palette();
RGB palette[palette_size];
for(auto i = 0u; i < palette_size; i++) {
palette[i] = RGB((uint)driver_palette[i]);
}
int pen = RGB(r, g, b).closest(palette, palette_size);
if(pen != -1) color = pen;
}
int PicoGraphics_PenDVHSTX_P8::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) {
used[i] = true;
cache_built = false;
driver.set_palette_colour(i, RGB_to_RGB888(r, g, b));
return i;
}
int PicoGraphics_PenDVHSTX_P8::create_pen(uint8_t r, uint8_t g, uint8_t b) {
// Create a colour and place it in the palette if there's space
for(auto i = 0u; i < palette_size; i++) {
if(!used[i]) {
used[i] = true;
cache_built = false;
driver.set_palette_colour(i, RGB_to_RGB888(r, g, b));
return i;
}
}
return -1;
}
int PicoGraphics_PenDVHSTX_P8::create_pen_hsv(float h, float s, float v) {
RGB p = RGB::from_hsv(h, s, v);
return create_pen(p.r, p.g, p.b);
}
int PicoGraphics_PenDVHSTX_P8::reset_pen(uint8_t i) {
driver.set_palette_colour(i, 0);
used[i] = false;
cache_built = false;
return i;
}
void PicoGraphics_PenDVHSTX_P8::set_pixel(const Point &p) {
driver.write_palette_pixel(p, color);
}
void PicoGraphics_PenDVHSTX_P8::set_pixel_span(const Point &p, uint l) {
driver.write_palette_pixel_span(p, l, color);
}
void PicoGraphics_PenDVHSTX_P8::get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array<uint8_t, 16> &candidates) {
RGB error;
for(size_t i = 0; i < candidates.size(); i++) {
candidates[i] = (col + error).closest(palette, len);
error += (col - palette[candidates[i]]);
}
// sort by a rough approximation of luminance, this ensures that neighbouring
// pixels in the dither matrix are at extreme opposites of luminence
// giving a more balanced output
std::sort(candidates.begin(), candidates.end(), [palette](int a, int b) {
return palette[a].luminance() > palette[b].luminance();
});
}
void PicoGraphics_PenDVHSTX_P8::set_pixel_dither(const Point &p, const RGB &c) {
if(!bounds.contains(p)) return;
if(!cache_built) {
RGB888 *driver_palette = driver.get_palette();
RGB palette[palette_size];
for(auto i = 0u; i < palette_size; i++) {
palette[i] = RGB((uint)driver_palette[i]);
}
for(uint i = 0; i < 512; i++) {
RGB cache_col((i & 0x1C0) >> 1, (i & 0x38) << 2, (i & 0x7) << 5);
get_dither_candidates(cache_col, palette, palette_size, candidate_cache[i]);
}
cache_built = true;
}
uint cache_key = ((c.r & 0xE0) << 1) | ((c.g & 0xE0) >> 2) | ((c.b & 0xE0) >> 5);
// find the pattern coordinate offset
uint pattern_index = (p.x & 0b11) | ((p.y & 0b11) << 2);
// set the pixel
color = candidate_cache[cache_key][dither16_pattern[pattern_index]];
set_pixel(p);
}
}

View file

@ -0,0 +1,72 @@
#include "pico_graphics_dvhstx.hpp"
#ifndef MICROPY_BUILD_TYPE
#define mp_printf(_, ...) printf(__VA_ARGS__);
#else
extern "C" {
#include "py/runtime.h"
}
#endif
namespace pimoroni {
RGB565 to_rgb565_noswap(const RGB& color) {
uint16_t p = ((color.r & 0b11111000) << 8) |
((color.g & 0b11111100) << 3) |
((color.b & 0b11111000) >> 3);
return p;
}
PicoGraphics_PenDVHSTX_RGB565::PicoGraphics_PenDVHSTX_RGB565(uint16_t width, uint16_t height, DVHSTX &dv_display)
: PicoGraphicsDVHSTX(width, height, dv_display)
{
this->pen_type = PEN_DV_RGB555;
}
void PicoGraphics_PenDVHSTX_RGB565::set_pen(uint c) {
color = c;
}
void PicoGraphics_PenDVHSTX_RGB565::set_bg(uint c) {
background = c;
}
void PicoGraphics_PenDVHSTX_RGB565::set_depth(uint8_t new_depth) {
depth = new_depth > 0 ? 0x8000 : 0;
}
void PicoGraphics_PenDVHSTX_RGB565::set_pen(uint8_t r, uint8_t g, uint8_t b) {
RGB src_color{r, g, b};
color = to_rgb565_noswap(src_color);
}
int PicoGraphics_PenDVHSTX_RGB565::create_pen(uint8_t r, uint8_t g, uint8_t b) {
return to_rgb565_noswap(RGB(r, g, b));
}
int PicoGraphics_PenDVHSTX_RGB565::create_pen_hsv(float h, float s, float v) {
return to_rgb565_noswap(RGB::from_hsv(h, s, v));
}
void PicoGraphics_PenDVHSTX_RGB565::set_pixel(const Point &p) {
driver.write_pixel(p, color);
}
void PicoGraphics_PenDVHSTX_RGB565::set_pixel_span(const Point &p, uint l) {
driver.write_pixel_span(p, l, color);
}
void PicoGraphics_PenDVHSTX_RGB565::set_pixel_alpha(const Point &p, const uint8_t a) {
uint16_t src = background;
if (blend_mode == BlendMode::TARGET) {
driver.read_pixel_span(p, 1, &src);
}
uint8_t src_r = (src >> 8) & 0b11111000;
uint8_t src_g = (src >> 3) & 0b11111100;
uint8_t src_b = (src << 3) & 0b11111000;
uint8_t dst_r = (color >> 8) & 0b11111000;
uint8_t dst_g = (color >> 3) & 0b11111100;
uint8_t dst_b = (color << 3) & 0b11111000;
RGB565 blended = to_rgb565_noswap(RGB(src_r, src_g, src_b).blend(RGB(dst_r, dst_g, dst_b), a));
driver.write_pixel(p, blended);
}
bool PicoGraphics_PenDVHSTX_RGB565::render_pico_vector_tile(const Rect &src_bounds, uint8_t* alpha_data, uint32_t stride, uint8_t alpha_type) {
return false;
}
}

22
pico_dvhstx.cmake Normal file
View file

@ -0,0 +1,22 @@
add_subdirectory(drivers/dvhstx)
set(LIB_NAME pico_dvhstx)
add_library(${LIB_NAME} INTERFACE)
target_sources(${LIB_NAME} INTERFACE
${PIMORONI_PICO_PATH}/libraries/pico_graphics/pico_graphics.cpp
# ${CMAKE_CURRENT_LIST_DIR}/libraries/pico_graphics/pico_graphics_pen_dvhstx_rgb888.cpp
${CMAKE_CURRENT_LIST_DIR}/libraries/pico_graphics/pico_graphics_pen_dvhstx_rgb565.cpp
${CMAKE_CURRENT_LIST_DIR}/libraries/pico_graphics/pico_graphics_pen_dvhstx_p8.cpp
${PIMORONI_PICO_PATH}/libraries/pico_graphics/types.cpp
)
target_include_directories(${LIB_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/libraries/pico_graphics # for pico_graphics_dv.hpp
${PIMORONI_PICO_PATH}/libraries/pico_graphics # for pico_graphics.hpp
${PIMORONI_PICO_PATH}/libraries/pngdec
)
target_link_libraries(${LIB_NAME} INTERFACE dvhstx pico_stdlib hardware_i2c hardware_dma)

73
pico_sdk_import.cmake Normal file
View file

@ -0,0 +1,73 @@
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of Pico SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download Pico SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
# GIT_SUBMODULES_RECURSE was added in 3.17
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
GIT_SUBMODULES_RECURSE FALSE
)
else ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
)
endif ()
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
FetchContent_Populate(pico_sdk)
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"Pico SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
include(${PICO_SDK_INIT_CMAKE_FILE})

View file

@ -0,0 +1,56 @@
# This file can be dropped into a project to help locate the Pimoroni Pico libraries
# It will also set up the required include and module search paths.
if (DEFINED ENV{PIMORONI_PICO_FETCH_FROM_GIT} AND (NOT PIMORONI_PICO_FETCH_FROM_GIT))
set(PIMORONI_PICO_FETCH_FROM_GIT $ENV{PIMORONI_PICO_FETCH_FROM_GIT})
message("Using PIMORONI_PICO_FETCH_FROM_GIT from environment ('${PIMORONI_PICO_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PIMORONI_PICO_FETCH_FROM_GIT_PATH} AND (NOT PIMORONI_PICO_FETCH_FROM_GIT_PATH))
set(PIMORONI_PICO_FETCH_FROM_GIT_PATH $ENV{PIMORONI_PICO_FETCH_FROM_GIT_PATH})
message("Using PIMORONI_PICO_FETCH_FROM_GIT_PATH from environment ('${PIMORONI_PICO_FETCH_FROM_GIT_PATH}')")
endif ()
if (NOT PIMORONI_PICO_PATH)
if (PIMORONI_PICO_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PIMORONI_PICO_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PIMORONI_PICO_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
FetchContent_Declare(
pimoroni_pico
GIT_REPOSITORY https://github.com/pimoroni/pimoroni-pico
GIT_TAG main
)
if (NOT pimoroni_pico)
message("Downloading PIMORONI_PICO SDK")
FetchContent_Populate(pimoroni_pico)
set(PIMORONI_PICO_PATH ${pimoroni_pico_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
elseif(PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pimoroni-pico")
set(PIMORONI_PICO_PATH ${PICO_SDK_PATH}/../pimoroni-pico)
message("Defaulting PIMORONI_PICO_PATH as sibling of PICO_SDK_PATH: ${PIMORONI_PICO_PATH}")
elseif(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/../../pimoroni-pico/")
set(PIMORONI_PICO_PATH ${CMAKE_CURRENT_BINARY_DIR}/../../pimoroni-pico/)
else()
message(FATAL_ERROR "Pimoroni Pico location was not specified. Please set PIMORONI_PICO_PATH.")
endif()
endif()
get_filename_component(PIMORONI_PICO_PATH "${PIMORONI_PICO_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PIMORONI_PICO_PATH})
message(FATAL_ERROR "Directory '${PIMORONI_PICO_PATH}' not found")
endif ()
if (NOT EXISTS ${PIMORONI_PICO_PATH}/pimoroni_pico_import.cmake)
message(FATAL_ERROR "Directory '${PIMORONI_PICO_PATH}' does not appear to contain the Pimoroni Pico libraries")
endif ()
message("PIMORONI_PICO_PATH is ${PIMORONI_PICO_PATH}")
set(PIMORONI_PICO_PATH ${PIMORONI_PICO_PATH} CACHE PATH "Path to the Pimoroni Pico libraries" FORCE)
include_directories(${PIMORONI_PICO_PATH})
list(APPEND CMAKE_MODULE_PATH ${PIMORONI_PICO_PATH})