├── .github └── workflows │ ├── cmake.yml │ └── micropython.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Mandelbrot.jpg ├── README.md ├── ci └── micropython.sh ├── drivers └── dvhstx │ ├── CMakeLists.txt │ ├── dvhstx.cmake │ ├── dvhstx.cpp │ ├── dvhstx.hpp │ ├── dvi.cpp │ ├── dvi.hpp │ ├── font.h │ └── intel_one_mono_2bpp.c ├── examples └── dvhstx │ ├── CMakeLists.txt │ ├── mandelbrot.cpp │ ├── mandelf.c │ ├── mandelf.h │ └── textmode.cpp ├── libraries └── pico_graphics │ ├── pico_graphics_dvhstx.hpp │ ├── pico_graphics_pen_dvhstx_p8.cpp │ └── pico_graphics_pen_dvhstx_rgb565.cpp ├── micropython ├── board │ ├── PIMORONI_PICO_PLUS2 │ │ ├── board.json │ │ ├── manifest.py │ │ ├── mpconfigboard.cmake │ │ ├── mpconfigboard.h │ │ ├── mpconfigvariant.cmake │ │ ├── mpconfigvariant_PSRAM.cmake │ │ └── pins.csv │ └── manifest_pico2.py └── examples │ └── vector_clock.py ├── modules ├── dvhstx.cmake └── picographics │ ├── README.md │ ├── micropython.cmake │ ├── picographics.c │ ├── picographics.cpp │ ├── picographics.h │ └── spritesheet-to-rgb332.py ├── pico_dvhstx.cmake ├── pico_sdk_import.cmake └── pimoroni_pico_import.cmake /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | pull_request: 6 | release: 7 | types: [created] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | name: ${{matrix.name}} 16 | strategy: 17 | matrix: 18 | include: 19 | - os: ubuntu-22.04 20 | name: Linux 21 | cache-key: linux 22 | cmake-args: '-DPIMORONI_PICO_PATH=$GITHUB_WORKSPACE/pimoroni-pico -DPICO_SDK_PATH=$GITHUB_WORKSPACE/pico-sdk -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/install' 23 | apt-packages: clang-tidy gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib 24 | 25 | runs-on: ${{matrix.os}} 26 | 27 | env: 28 | PICO_SDK_PATH: $GITHUB_WORKSPACE/pico-sdk 29 | PIMORONI_PICO_LIBS: $GITHUB_WORKSPACE/pimoroni-pico 30 | RELEASE_FILE: ${{github.event.repository.name}}-${{github.event.release.tag_name}} 31 | 32 | steps: 33 | - name: Checkout Code 34 | uses: actions/checkout@v3 35 | with: 36 | path: project 37 | 38 | # Checkout the Pimoroni Pico Libraries 39 | - name: Checkout Pimoroni Pico Libraries 40 | uses: actions/checkout@v3 41 | with: 42 | repository: pimoroni/pimoroni-pico 43 | path: pimoroni-pico 44 | 45 | # Checkout the Pico SDK 46 | - name: Checkout Pico SDK 47 | uses: actions/checkout@v3 48 | with: 49 | repository: raspberrypi/pico-sdk 50 | path: pico-sdk 51 | submodules: true 52 | 53 | # Linux deps 54 | - name: Install deps 55 | if: runner.os == 'Linux' 56 | run: | 57 | sudo apt update && sudo apt install ${{matrix.apt-packages}} 58 | mkdir toolchain 59 | cd toolchain 60 | wget https://buildbot.embecosm.com/job/riscv32-gcc-ubuntu2204-release/10/artifact/riscv32-embecosm-ubuntu2204-gcc13.2.0.tar.gz 61 | tar xzf riscv32-embecosm-ubuntu2204-gcc13.2.0.tar.gz 62 | 63 | - name: Create Build Environment 64 | run: | 65 | cmake -E make_directory ${{runner.workspace}}/build 66 | cmake -E make_directory ${{runner.workspace}}/build-riscv 67 | 68 | - name: Configure CMake 69 | shell: bash 70 | working-directory: ${{runner.workspace}}/build 71 | run: cmake $GITHUB_WORKSPACE/project -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCPACK_PACKAGE_FILE_NAME=${{env.RELEASE_FILE}} ${{matrix.cmake-args}} 72 | 73 | - name: Build 74 | working-directory: ${{runner.workspace}}/build 75 | shell: bash 76 | run: | 77 | cmake --build . --config $BUILD_TYPE -j 2 78 | 79 | - name: Configure CMake RiscV 80 | shell: bash 81 | working-directory: ${{runner.workspace}}/build-riscv 82 | run: cmake $GITHUB_WORKSPACE/project -DPICO_PLATFORM=rp2350-riscv -DPICO_TOOLCHAIN_PATH=$GITHUB_WORKSPACE/toolchain/riscv32-embecosm-ubuntu2204-gcc13.2.0 -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCPACK_PACKAGE_FILE_NAME=${{env.RELEASE_FILE}} ${{matrix.cmake-args}} 83 | 84 | - name: Build RiscV 85 | working-directory: ${{runner.workspace}}/build-riscv 86 | shell: bash 87 | run: | 88 | cmake --build . --config $BUILD_TYPE -j 2 89 | 90 | - name: Build Release Packages 91 | if: github.event_name == 'release' 92 | working-directory: ${{runner.workspace}}/build 93 | shell: bash 94 | run: | 95 | cmake --build . --config $BUILD_TYPE --target package -j 2 96 | 97 | - name: Upload .zip 98 | if: github.event_name == 'release' 99 | uses: actions/upload-release-asset@v1 100 | env: 101 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 102 | with: 103 | asset_path: ${{runner.workspace}}/build/${{env.RELEASE_FILE}}.zip 104 | upload_url: ${{github.event.release.upload_url}} 105 | asset_name: ${{env.RELEASE_FILE}}.zip 106 | asset_content_type: application/zip 107 | 108 | - name: Upload .tar.gz 109 | if: github.event_name == 'release' 110 | uses: actions/upload-release-asset@v1 111 | env: 112 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 113 | with: 114 | asset_path: ${{runner.workspace}}/build/${{env.RELEASE_FILE}}.tar.gz 115 | upload_url: ${{github.event.release.upload_url}} 116 | asset_name: ${{env.RELEASE_FILE}}.tar.gz 117 | asset_content_type: application/octet-stream 118 | -------------------------------------------------------------------------------- /.github/workflows/micropython.yml: -------------------------------------------------------------------------------- 1 | name: MicroPython 2 | 3 | on: 4 | push: 5 | pull_request: 6 | release: 7 | types: [created] 8 | 9 | env: 10 | MICROPYTHON_VERSION: feature/psram 11 | MICROPYTHON_FLAVOUR: MichaelBell 12 | PIMORONI_PICO_VERSION: feature/sdk-2.0.0 13 | 14 | jobs: 15 | build: 16 | name: ${{ matrix.name }} (${{ matrix.board }} ${{ matrix.variant }}) 17 | runs-on: ubuntu-20.04 18 | continue-on-error: true 19 | strategy: 20 | matrix: 21 | include: 22 | - name: pico_plus2_rp2350_psram 23 | board: PIMORONI_PICO_PLUS2 24 | variant: PSRAM 25 | 26 | env: 27 | # MicroPython version will be contained in github.event.release.tag_name for releases 28 | RELEASE_FILE: ${{ matrix.name }}-${{ github.event.release.tag_name || github.sha }}-micropython 29 | PIMORONI_PICO_DIR: "${{ github.workspace }}/pimoroni-pico" 30 | USER_C_MODULES: "${{ github.workspace }}/src-${{ github.sha }}/modules/dvhstx.cmake" 31 | TAG_OR_SHA: ${{ github.event.release.tag_name || github.sha }} 32 | MICROPY_BOARD: ${{ matrix.board }} 33 | MICROPY_BOARD_VARIANT: ${{ matrix.variant }} 34 | MICROPY_BOARD_DIR: "${{ github.workspace }}/src-${{ github.sha }}/micropython/board/${{ matrix.BOARD }}" 35 | BOARD_NAME: ${{ matrix.name }} 36 | BUILD_TOOLS: src-${{ github.sha }}/ci/micropython.sh 37 | 38 | steps: 39 | - name: "CCache: Restore saved cache" 40 | uses: actions/cache@v4 41 | with: 42 | path: /home/runner/.ccache 43 | key: ccache-micropython-${{ matrix.name }}-${{ github.ref }}-${{ github.sha }} 44 | restore-keys: | 45 | ccache-micropython-${{ matrix.name }}-${{ github.ref }} 46 | ccache-micropython-${{ matrix.name }}- 47 | 48 | - name: "Src: Checkout" 49 | uses: actions/checkout@v4 50 | with: 51 | submodules: true 52 | path: src-${{ github.sha }} 53 | 54 | - name: "Pimoroni Pico: Checkout" 55 | uses: actions/checkout@v4 56 | with: 57 | repository: pimoroni/pimoroni-pico 58 | ref: ${{env.PIMORONI_PICO_VERSION}} 59 | submodules: true 60 | path: pimoroni-pico 61 | 62 | - name: Install Arm GNU Toolchain (arm-none-eabi-gcc) 63 | uses: carlosperate/arm-none-eabi-gcc-action@v1 64 | with: 65 | release: '13.3.Rel1' 66 | 67 | - name: "CCache: Install" 68 | run: | 69 | source $BUILD_TOOLS 70 | apt_install_build_deps 71 | 72 | - name: "MicroPython: Checkout" 73 | run: | 74 | source $BUILD_TOOLS 75 | micropython_clone 76 | 77 | - name: "Py_Decl: Checkout" 78 | uses: actions/checkout@v4 79 | with: 80 | repository: gadgetoid/py_decl 81 | ref: v0.0.2 82 | path: py_decl 83 | 84 | - name: "dir2uf2: Checkout" 85 | uses: actions/checkout@v4 86 | with: 87 | repository: gadgetoid/dir2uf2 88 | ref: v0.0.7 89 | path: dir2uf2 90 | 91 | - name: "MicroPython: Build MPY Cross" 92 | run: | 93 | source $BUILD_TOOLS 94 | micropython_build_mpy_cross 95 | 96 | - name: "MicroPython: Configure" 97 | shell: bash 98 | run: | 99 | source $BUILD_TOOLS 100 | micropython_version 101 | cmake_configure 102 | 103 | - name: "MicroPython: Build" 104 | shell: bash 105 | run: | 106 | source $BUILD_TOOLS 107 | cmake_build 108 | 109 | - name: "Py_Decl: Verify .uf2" 110 | shell: bash 111 | run: | 112 | python3 py_decl/py_decl.py --to-json --verify build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2 113 | 114 | - name: "Artifacts: Upload .uf2" 115 | uses: actions/upload-artifact@v4 116 | with: 117 | name: ${{ env.RELEASE_FILE }}.uf2 118 | path: build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2 119 | 120 | - name: "Release: Upload .uf2" 121 | if: github.event_name == 'release' 122 | uses: actions/upload-release-asset@v1 123 | env: 124 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 125 | with: 126 | asset_path: build-${{ matrix.name }}/${{ env.RELEASE_FILE }}.uf2 127 | upload_url: ${{ github.event.release.upload_url }} 128 | asset_name: ${{ env.RELEASE_FILE }}.uf2 129 | asset_content_type: application/octet-stream 130 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | if (NOT PICO_PLATFORM) 4 | set(PICO_PLATFORM rp2350) 5 | endif() 6 | 7 | include(pico_sdk_import.cmake) 8 | include(pimoroni_pico_import.cmake) 9 | 10 | # Gooey boilerplate 11 | project(pico_dvhstx_examples C CXX ASM) 12 | set(CMAKE_C_STANDARD 11) 13 | set(CMAKE_CXX_STANDARD 17) 14 | 15 | # Initialize the SDK 16 | pico_sdk_init() 17 | 18 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") 19 | 20 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) 21 | include(pico_dvhstx.cmake) 22 | 23 | add_subdirectory(examples/dvhstx) 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Michael Bell and Pimoroni Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Mandelbrot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichaelBell/dvhstx/7eca7c14871dbb407a6ba6636b810c30e9cb0e3f/Mandelbrot.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DVI for HSTX 2 | 3 | This repository is home to the Pimoroni PicoGraphics compatible DVI driver for RP2 chips with HSTX (e.g. RP2350). 4 | 5 | [![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) 6 | [![GitHub release (latest by date)](https://img.shields.io/github/v/release/MichaelBell/dvhstx)](https://github.com/MichaelBell/dvhstx/releases/latest/) 7 | 8 | - [Introduction](#introduction) 9 | - [Download MicroPython](#download-micropython) 10 | - [Documentation](#documentation) 11 | - [C/C++ Resources](#cc-resources) 12 | - [C/C++ Community Projects](#cc-community-projects) 13 | 14 | ## Introduction 15 | 16 | DV HSTX will enable you to create big, bold audio visual projects using MicroPython and an HDMI display of your choice. 17 | 18 | ![Mandelbrot fractal displayed on a monitor](Mandelbrot.jpg) 19 | 20 | ## Download MicroPython 21 | 22 | TODO 23 | 24 | * [DV HSTX MicroPython Releases](https://github.com/MichaelBell/dvhstx/releases) 25 | 26 | ## Documentation 27 | 28 | Wire up your DVI breakout as follows: 29 | 30 | GPIO 12 - CK- 31 | GPIO 13 - CK+ 32 | GPIO 14 - D0- 33 | GPIO 15 - D0+ 34 | GPIO 16 - D1- 35 | GPIO 17 - D1+ 36 | GPIO 18 - D2- 37 | GPIO 19 - D2+ 38 | 39 | If using jumper jerky, twist the - and + wires for each signal together to help with signal integrity. 40 | 41 | Other pinouts can be used by passing a `pinout` parameter to the `init` 42 | function. 43 | This pinout consists of 4 numbers giving the *positive* pin in each differential pair, in the order CK, D0, D1, D2, D3, using GPIO numbering. 44 | The default pinout is written `{13, 15, 17, 19}`. 45 | Only pin numbers from 12 to 19 are valid, as other pins are not connected to the HSTX peripheral. 46 | Using invalid pin numbers is an undignosed error. 47 | 48 | TODO 49 | 50 | ## C/C++ Resources 51 | 52 | * :link: [C++ Boilerplate](https://github.com/MichaelBell/dvhstx-boilerplate/) 53 | 54 | ## C/C++ Community Projects 55 | 56 | TODO 57 | -------------------------------------------------------------------------------- /ci/micropython.sh: -------------------------------------------------------------------------------- 1 | export TERM=${TERM:="xterm-256color"} 2 | 3 | function log_success { 4 | echo -e "$(tput setaf 2)$1$(tput sgr0)" 5 | } 6 | 7 | function log_inform { 8 | echo -e "$(tput setaf 6)$1$(tput sgr0)" 9 | } 10 | 11 | function log_warning { 12 | echo -e "$(tput setaf 1)$1$(tput sgr0)" 13 | } 14 | 15 | function micropython_clone { 16 | log_inform "Using MicroPython $MICROPYTHON_VERSION" 17 | git clone https://github.com/$MICROPYTHON_FLAVOUR/micropython 18 | cd micropython 19 | git checkout $MICROPYTHON_VERSION 20 | git submodule update --init lib/pico-sdk 21 | git submodule update --init lib/cyw43-driver 22 | git submodule update --init lib/lwip 23 | git submodule update --init lib/mbedtls 24 | git submodule update --init lib/micropython-lib 25 | git submodule update --init lib/tinyusb 26 | git submodule update --init lib/btstack 27 | cd ../ 28 | } 29 | 30 | function micropython_build_mpy_cross { 31 | cd micropython/mpy-cross 32 | ccache --zero-stats || true 33 | CROSS_COMPILE="ccache " make 34 | ccache --show-stats || true 35 | cd ../../ 36 | } 37 | 38 | function apt_install_build_deps { 39 | sudo apt update && sudo apt install ccache 40 | } 41 | 42 | function micropython_version { 43 | echo "MICROPY_GIT_TAG=$MICROPYTHON_VERSION, $BOARD_NAME $TAG_OR_SHA" >> $GITHUB_ENV 44 | echo "MICROPY_GIT_HASH=$MICROPYTHON_VERSION-$TAG_OR_SHA" >> $GITHUB_ENV 45 | } 46 | 47 | function cmake_configure { 48 | cmake -S micropython/ports/rp2 -B build-$BOARD_NAME \ 49 | -DPICO_BUILD_DOCS=0 \ 50 | -DPICO_NO_COPRO_DIS=1 \ 51 | -DUSER_C_MODULES=$USER_C_MODULES \ 52 | -DMICROPY_BOARD_DIR=$MICROPY_BOARD_DIR \ 53 | -DMICROPY_BOARD=$MICROPY_BOARD \ 54 | -DMICROPY_BOARD_VARIANT=$MICROPY_BOARD_VARIANT \ 55 | -DCMAKE_C_COMPILER_LAUNCHER=ccache \ 56 | -DCMAKE_CXX_COMPILER_LAUNCHER=ccache 57 | } 58 | 59 | function cmake_build { 60 | ccache --zero-stats || true 61 | cmake --build build-$BOARD_NAME -j 2 62 | ccache --show-stats || true 63 | cd build-$BOARD_NAME 64 | cp firmware.uf2 $RELEASE_FILE.uf2 65 | } -------------------------------------------------------------------------------- /drivers/dvhstx/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(dvhstx.cmake) -------------------------------------------------------------------------------- /drivers/dvhstx/dvhstx.cmake: -------------------------------------------------------------------------------- 1 | set(DRIVER_NAME dvhstx) 2 | 3 | # main DV display driver 4 | add_library(${DRIVER_NAME} INTERFACE) 5 | 6 | target_sources(${DRIVER_NAME} INTERFACE 7 | ${CMAKE_CURRENT_LIST_DIR}/dvi.cpp 8 | ${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp 9 | 10 | ${CMAKE_CURRENT_LIST_DIR}/intel_one_mono_2bpp.c 11 | ) 12 | 13 | # Enforce consistent compile options. 14 | # For the moment, don't use -O3 options that increase code size significantly 15 | target_compile_options(${DRIVER_NAME} INTERFACE -Wall -Werror -O2 -fgcse-after-reload -floop-interchange -fpeel-loops -fpredictive-commoning -fsplit-paths -ftree-loop-distribute-patterns -ftree-loop-distribution -ftree-vectorize -ftree-partial-pre -funswitch-loops) 16 | 17 | target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 18 | 19 | # Pull in pico libraries that we need 20 | target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib) 21 | -------------------------------------------------------------------------------- /drivers/dvhstx/dvhstx.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" { 5 | #include 6 | } 7 | 8 | #include 9 | #include "hardware/dma.h" 10 | #include "hardware/gpio.h" 11 | #include "hardware/irq.h" 12 | #include "hardware/structs/bus_ctrl.h" 13 | #include "hardware/structs/hstx_ctrl.h" 14 | #include "hardware/structs/hstx_fifo.h" 15 | #include "hardware/structs/sio.h" 16 | 17 | #include "hardware/structs/ioqspi.h" 18 | #include "hardware/vreg.h" 19 | #include "hardware/structs/qmi.h" 20 | #include "hardware/pll.h" 21 | #include "hardware/clocks.h" 22 | 23 | #include "dvi.hpp" 24 | #include "dvhstx.hpp" 25 | 26 | using namespace pimoroni; 27 | 28 | #ifdef MICROPY_BUILD_TYPE 29 | #define FRAME_BUFFER_SIZE (640*360) 30 | __attribute__((section(".uninitialized_data"))) static uint8_t frame_buffer_a[FRAME_BUFFER_SIZE]; 31 | __attribute__((section(".uninitialized_data"))) static uint8_t frame_buffer_b[FRAME_BUFFER_SIZE]; 32 | #endif 33 | 34 | #include "font.h" 35 | 36 | // If changing the font, note this code will not handle glyphs wider than 13 pixels 37 | #define FONT (&intel_one_mono) 38 | 39 | // The first displayable character in text mode is FIRST_GLYPH, and the total 40 | // number of glyphs is GLYPH_COUNT. This makes the last displayable character 41 | // (FIRST_GLYPH + GLYPH_COUNT - 1) or 126, ASCII tilde. 42 | // 43 | // Because all glyphs not present in the cache are displayed as blank, there's 44 | // no need to include space among the cached glyphs. 45 | constexpr int FIRST_GLYPH=33, GLYPH_COUNT=95; 46 | 47 | #ifdef MICROPY_BUILD_TYPE 48 | extern "C" { 49 | void dvhstx_debug(const char *fmt, ...); 50 | } 51 | #else 52 | #define dvhstx_debug printf 53 | #endif 54 | 55 | static inline __attribute__((always_inline)) uint32_t render_char_line(int c, int y) { 56 | if (c < 0x20 || c > 0x7e) return 0; 57 | const lv_font_fmt_txt_glyph_dsc_t* g = &FONT->dsc->glyph_dsc[c - 0x20 + 1]; 58 | const uint8_t *b = FONT->dsc->glyph_bitmap + g->bitmap_index; 59 | const int ey = y - FONT_HEIGHT + FONT->base_line + g->ofs_y + g->box_h; 60 | if (ey < 0 || ey >= g->box_h || g->box_w == 0) { 61 | return 0; 62 | } 63 | else { 64 | int bi = (g->box_w * ey); 65 | 66 | uint32_t bits = (b[bi >> 2] << 24) | (b[(bi >> 2) + 1] << 16) | (b[(bi >> 2) + 2] << 8) | b[(bi >> 2) + 3]; 67 | bits >>= 6 - ((bi & 3) << 1); 68 | bits &= 0x3ffffff & (0x3ffffff << ((13 - g->box_w) << 1)); 69 | bits >>= g->ofs_x << 1; 70 | 71 | return bits; 72 | } 73 | } 74 | 75 | // ---------------------------------------------------------------------------- 76 | // HSTX command lists 77 | 78 | // Lists are padded with NOPs to be >= HSTX FIFO size, to avoid DMA rapidly 79 | // pingponging and tripping up the IRQs. 80 | 81 | static const uint32_t vblank_line_vsync_off_src[] = { 82 | HSTX_CMD_RAW_REPEAT, 83 | SYNC_V1_H1, 84 | HSTX_CMD_RAW_REPEAT, 85 | SYNC_V1_H0, 86 | HSTX_CMD_RAW_REPEAT, 87 | SYNC_V1_H1 88 | }; 89 | static uint32_t vblank_line_vsync_off[count_of(vblank_line_vsync_off_src)]; 90 | 91 | static const uint32_t vblank_line_vsync_on_src[] = { 92 | HSTX_CMD_RAW_REPEAT, 93 | SYNC_V0_H1, 94 | HSTX_CMD_RAW_REPEAT, 95 | SYNC_V0_H0, 96 | HSTX_CMD_RAW_REPEAT, 97 | SYNC_V0_H1 98 | }; 99 | static uint32_t vblank_line_vsync_on[count_of(vblank_line_vsync_on_src)]; 100 | 101 | static const uint32_t vactive_line_header_src[] = { 102 | HSTX_CMD_RAW_REPEAT, 103 | SYNC_V1_H1, 104 | HSTX_CMD_RAW_REPEAT, 105 | SYNC_V1_H0, 106 | HSTX_CMD_RAW_REPEAT, 107 | SYNC_V1_H1, 108 | HSTX_CMD_TMDS 109 | }; 110 | static uint32_t vactive_line_header[count_of(vactive_line_header_src)]; 111 | 112 | static const uint32_t vactive_text_line_header_src[] = { 113 | HSTX_CMD_RAW_REPEAT, 114 | SYNC_V1_H1, 115 | HSTX_CMD_RAW_REPEAT, 116 | SYNC_V1_H0, 117 | HSTX_CMD_RAW_REPEAT, 118 | SYNC_V1_H1, 119 | HSTX_CMD_RAW | 6, 120 | BLACK_PIXEL_A, 121 | BLACK_PIXEL_B, 122 | BLACK_PIXEL_A, 123 | BLACK_PIXEL_B, 124 | BLACK_PIXEL_A, 125 | BLACK_PIXEL_B, 126 | HSTX_CMD_TMDS 127 | }; 128 | static uint32_t vactive_text_line_header[count_of(vactive_text_line_header_src)]; 129 | 130 | #define NUM_FRAME_LINES 2 131 | #define NUM_CHANS 3 132 | 133 | static DVHSTX* display = nullptr; 134 | 135 | // ---------------------------------------------------------------------------- 136 | // DMA logic 137 | 138 | void __scratch_x("display") dma_irq_handler() { 139 | display->gfx_dma_handler(); 140 | } 141 | 142 | void __scratch_x("display") DVHSTX::gfx_dma_handler() { 143 | // ch_num indicates the channel that just finished, which is the one 144 | // we're about to reload. 145 | dma_channel_hw_t *ch = &dma_hw->ch[ch_num]; 146 | dma_hw->intr = 1u << ch_num; 147 | if (++ch_num == NUM_CHANS) ch_num = 0; 148 | 149 | if (v_scanline >= timing_mode->v_front_porch && v_scanline < (timing_mode->v_front_porch + timing_mode->v_sync_width)) { 150 | ch->read_addr = (uintptr_t)vblank_line_vsync_on; 151 | ch->transfer_count = count_of(vblank_line_vsync_on); 152 | } else if (v_scanline < v_inactive_total) { 153 | ch->read_addr = (uintptr_t)vblank_line_vsync_off; 154 | ch->transfer_count = count_of(vblank_line_vsync_off); 155 | } else { 156 | const int y = (v_scanline - v_inactive_total) >> v_repeat_shift; 157 | const int new_line_num = (v_repeat_shift == 0) ? ch_num : (y & (NUM_FRAME_LINES - 1)); 158 | const uint line_buf_total_len = ((timing_mode->h_active_pixels * line_bytes_per_pixel) >> 2) + count_of(vactive_line_header); 159 | 160 | ch->read_addr = (uintptr_t)&line_buffers[new_line_num * line_buf_total_len]; 161 | ch->transfer_count = line_buf_total_len; 162 | 163 | // Fill line buffer 164 | if (line_num != new_line_num) 165 | { 166 | line_num = new_line_num; 167 | uint32_t* dst_ptr = &line_buffers[line_num * line_buf_total_len + count_of(vactive_line_header)]; 168 | 169 | if (line_bytes_per_pixel == 2) { 170 | uint16_t* src_ptr = (uint16_t*)&frame_buffer_display[y * 2 * (timing_mode->h_active_pixels >> h_repeat_shift)]; 171 | if (h_repeat_shift == 2) { 172 | for (int i = 0; i < timing_mode->h_active_pixels >> 1; i += 2) { 173 | uint32_t val = (uint32_t)(*src_ptr++) * 0x10001; 174 | *dst_ptr++ = val; 175 | *dst_ptr++ = val; 176 | } 177 | } 178 | else { 179 | for (int i = 0; i < timing_mode->h_active_pixels >> 1; ++i) { 180 | uint32_t val = (uint32_t)(*src_ptr++) * 0x10001; 181 | *dst_ptr++ = val; 182 | } 183 | } 184 | } 185 | else if (line_bytes_per_pixel == 1) { 186 | uint8_t* src_ptr = &frame_buffer_display[y * (timing_mode->h_active_pixels >> h_repeat_shift)]; 187 | if (h_repeat_shift == 2) { 188 | for (int i = 0; i < timing_mode->h_active_pixels >> 2; ++i) { 189 | uint32_t val = (uint32_t)(*src_ptr++) * 0x01010101; 190 | *dst_ptr++ = val; 191 | } 192 | } 193 | else { 194 | for (int i = 0; i < timing_mode->h_active_pixels >> 2; ++i) { 195 | uint32_t val = ((uint32_t)(*src_ptr++) * 0x0101); 196 | val |= ((uint32_t)(*src_ptr++) * 0x01010000); 197 | *dst_ptr++ = val; 198 | } 199 | } 200 | } 201 | else if (line_bytes_per_pixel == 4) { 202 | uint8_t* src_ptr = &frame_buffer_display[y * (timing_mode->h_active_pixels >> h_repeat_shift)]; 203 | if (h_repeat_shift == 2) { 204 | for (int i = 0; i < timing_mode->h_active_pixels; i += 4) { 205 | uint32_t val = display_palette[*src_ptr++]; 206 | *dst_ptr++ = val; 207 | *dst_ptr++ = val; 208 | *dst_ptr++ = val; 209 | *dst_ptr++ = val; 210 | } 211 | } 212 | else { 213 | for (int i = 0; i < timing_mode->h_active_pixels; i += 2) { 214 | uint32_t val = display_palette[*src_ptr++]; 215 | *dst_ptr++ = val; 216 | *dst_ptr++ = val; 217 | } 218 | } 219 | } 220 | } 221 | } 222 | 223 | if (++v_scanline == v_total_active_lines) { 224 | v_scanline = 0; 225 | line_num = -1; 226 | if (flip_next) { 227 | flip_next = false; 228 | display->flip_now(); 229 | } 230 | __sev(); 231 | } 232 | } 233 | 234 | void __scratch_x("display") dma_irq_handler_text() { 235 | display->text_dma_handler(); 236 | } 237 | 238 | void __scratch_x("display") DVHSTX::text_dma_handler() { 239 | // ch_num indicates the channel that just finished, which is the one 240 | // we're about to reload. 241 | dma_channel_hw_t *ch = &dma_hw->ch[ch_num]; 242 | dma_hw->intr = 1u << ch_num; 243 | if (++ch_num == NUM_CHANS) ch_num = 0; 244 | 245 | if (v_scanline >= timing_mode->v_front_porch && v_scanline < (timing_mode->v_front_porch + timing_mode->v_sync_width)) { 246 | ch->read_addr = (uintptr_t)vblank_line_vsync_on; 247 | ch->transfer_count = count_of(vblank_line_vsync_on); 248 | } else if (v_scanline < v_inactive_total) { 249 | ch->read_addr = (uintptr_t)vblank_line_vsync_off; 250 | ch->transfer_count = count_of(vblank_line_vsync_off); 251 | } else { 252 | const int y = (v_scanline - v_inactive_total); 253 | const uint line_buf_total_len = (frame_width * line_bytes_per_pixel + 3) / 4 + count_of(vactive_text_line_header); 254 | 255 | ch->read_addr = (uintptr_t)&line_buffers[ch_num * line_buf_total_len]; 256 | ch->transfer_count = line_buf_total_len; 257 | 258 | // Fill line buffer 259 | int char_y = y % 24; 260 | if (line_bytes_per_pixel == 4) { 261 | uint32_t* dst_ptr = &line_buffers[ch_num * line_buf_total_len + count_of(vactive_text_line_header)]; 262 | uint8_t* src_ptr = &frame_buffer_display[(y / 24) * frame_width]; 263 | for (int i = 0; i < frame_width; ++i) { 264 | *dst_ptr++ = render_char_line(*src_ptr++, char_y); 265 | } 266 | } 267 | else { 268 | uint8_t* dst_ptr = (uint8_t*)&line_buffers[ch_num * line_buf_total_len + count_of(vactive_text_line_header)]; 269 | uint8_t* src_ptr = &frame_buffer_display[(y / 24) * frame_width]; 270 | uint8_t* colour_ptr = src_ptr + frame_width * frame_height; 271 | #ifdef __riscv 272 | for (int i = 0; i < frame_width; ++i) { 273 | const uint8_t c = (*src_ptr++ - FIRST_GLYPH); 274 | uint32_t bits = (c < GLYPH_COUNT) ? font_cache[c * 24 + char_y] : 0; 275 | const uint8_t colour = *colour_ptr++; 276 | 277 | *dst_ptr++ = colour * ((bits >> 24) & 3); 278 | *dst_ptr++ = colour * ((bits >> 22) & 3); 279 | *dst_ptr++ = colour * ((bits >> 20) & 3); 280 | *dst_ptr++ = colour * ((bits >> 18) & 3); 281 | *dst_ptr++ = colour * ((bits >> 16) & 3); 282 | *dst_ptr++ = colour * ((bits >> 14) & 3); 283 | *dst_ptr++ = colour * ((bits >> 12) & 3); 284 | *dst_ptr++ = colour * ((bits >> 10) & 3); 285 | *dst_ptr++ = colour * ((bits >> 8) & 3); 286 | *dst_ptr++ = colour * ((bits >> 6) & 3); 287 | *dst_ptr++ = colour * ((bits >> 4) & 3); 288 | *dst_ptr++ = colour * ((bits >> 2) & 3); 289 | *dst_ptr++ = colour * (bits & 3); 290 | *dst_ptr++ = 0; 291 | } 292 | #else 293 | int i = 0; 294 | for (; i < frame_width-1; i += 2) { 295 | uint8_t c = (*src_ptr++ - FIRST_GLYPH); 296 | uint32_t bits = (c < GLYPH_COUNT) ? font_cache[c * 24 + char_y] : 0; 297 | uint8_t colour = *colour_ptr++; 298 | c = (*src_ptr++ - FIRST_GLYPH); 299 | uint32_t bits2 = (c < GLYPH_COUNT) ? font_cache[c * 24 + char_y] : 0; 300 | uint8_t colour2 = *colour_ptr++; 301 | 302 | // This ASM works around a compiler bug where the optimizer decides 303 | // to unroll so hard it spills to the stack. 304 | uint32_t tmp, tmp2; 305 | asm volatile ( 306 | "ubfx %[tmp], %[cbits], #24, #2\n\t" 307 | "ubfx %[tmp2], %[cbits], #22, #2\n\t" 308 | "bfi %[tmp], %[tmp2], #8, #8\n\t" 309 | "ubfx %[tmp2], %[cbits], #20, #2\n\t" 310 | "bfi %[tmp], %[tmp2], #16, #8\n\t" 311 | "ubfx %[tmp2], %[cbits], #18, #2\n\t" 312 | "bfi %[tmp], %[tmp2], #24, #8\n\t" 313 | "muls %[tmp], %[colour], %[tmp]\n\t" 314 | "str %[tmp], [%[dst_ptr]]\n\t" 315 | 316 | "ubfx %[tmp], %[cbits], #16, #2\n\t" 317 | "ubfx %[tmp2], %[cbits], #14, #2\n\t" 318 | "bfi %[tmp], %[tmp2], #8, #8\n\t" 319 | "ubfx %[tmp2], %[cbits], #12, #2\n\t" 320 | "bfi %[tmp], %[tmp2], #16, #8\n\t" 321 | "ubfx %[tmp2], %[cbits], #10, #2\n\t" 322 | "bfi %[tmp], %[tmp2], #24, #8\n\t" 323 | "muls %[tmp], %[colour], %[tmp]\n\t" 324 | "str %[tmp], [%[dst_ptr], #4]\n\t" 325 | 326 | "ubfx %[tmp], %[cbits], #8, #2\n\t" 327 | "ubfx %[tmp2], %[cbits], #6, #2\n\t" 328 | "bfi %[tmp], %[tmp2], #8, #8\n\t" 329 | "ubfx %[tmp2], %[cbits], #4, #2\n\t" 330 | "bfi %[tmp], %[tmp2], #16, #8\n\t" 331 | "ubfx %[tmp2], %[cbits], #2, #2\n\t" 332 | "bfi %[tmp], %[tmp2], #24, #8\n\t" 333 | "muls %[tmp], %[colour], %[tmp]\n\t" 334 | "str %[tmp], [%[dst_ptr], #8]\n\t" 335 | 336 | "ubfx %[tmp], %[cbits2], #24, #2\n\t" 337 | "ubfx %[tmp2], %[cbits2], #22, #2\n\t" 338 | "bfi %[tmp], %[tmp2], #8, #8\n\t" 339 | "muls %[tmp], %[colour2], %[tmp]\n\t" 340 | "and %[tmp2], %[cbits], #3\n\t" 341 | "muls %[tmp2], %[colour], %[tmp2]\n\t" 342 | "bfi %[tmp2], %[tmp], #16, #16\n\t" 343 | "str %[tmp2], [%[dst_ptr], #12]\n\t" 344 | 345 | "ubfx %[tmp], %[cbits2], #20, #2\n\t" 346 | "ubfx %[tmp2], %[cbits2], #18, #2\n\t" 347 | "bfi %[tmp], %[tmp2], #8, #8\n\t" 348 | "ubfx %[tmp2], %[cbits2], #16, #2\n\t" 349 | "bfi %[tmp], %[tmp2], #16, #8\n\t" 350 | "ubfx %[tmp2], %[cbits2], #14, #2\n\t" 351 | "bfi %[tmp], %[tmp2], #24, #8\n\t" 352 | "muls %[tmp], %[colour2], %[tmp]\n\t" 353 | "str %[tmp], [%[dst_ptr], #16]\n\t" 354 | 355 | "ubfx %[tmp], %[cbits2], #12, #2\n\t" 356 | "ubfx %[tmp2], %[cbits2], #10, #2\n\t" 357 | "bfi %[tmp], %[tmp2], #8, #8\n\t" 358 | "ubfx %[tmp2], %[cbits2], #8, #2\n\t" 359 | "bfi %[tmp], %[tmp2], #16, #8\n\t" 360 | "ubfx %[tmp2], %[cbits2], #6, #2\n\t" 361 | "bfi %[tmp], %[tmp2], #24, #8\n\t" 362 | "muls %[tmp], %[colour2], %[tmp]\n\t" 363 | "str %[tmp], [%[dst_ptr], #20]\n\t" 364 | 365 | "ubfx %[tmp], %[cbits2], #4, #2\n\t" 366 | "ubfx %[tmp2], %[cbits2], #2, #2\n\t" 367 | "bfi %[tmp], %[tmp2], #8, #8\n\t" 368 | "bfi %[tmp], %[cbits2], #16, #2\n\t" 369 | "muls %[tmp], %[colour2], %[tmp]\n\t" 370 | "str %[tmp], [%[dst_ptr], #24]\n\t" 371 | : [tmp] "=&l" (tmp), 372 | [tmp2] "=&l" (tmp2) 373 | : [cbits] "r" (bits), 374 | [colour] "l" (colour), 375 | [cbits2] "r" (bits2), 376 | [colour2] "l" (colour2), 377 | [dst_ptr] "r" (dst_ptr) 378 | : "cc", "memory" ); 379 | dst_ptr += 14 * 2; 380 | } 381 | if (i != frame_width) { 382 | const uint8_t c = (*src_ptr++ - FIRST_GLYPH); 383 | uint32_t bits = (c < GLYPH_COUNT) ? font_cache[c * 24 + char_y] : 0; 384 | const uint8_t colour = *colour_ptr++; 385 | 386 | *dst_ptr++ = colour * ((bits >> 24) & 3); 387 | *dst_ptr++ = colour * ((bits >> 22) & 3); 388 | *dst_ptr++ = colour * ((bits >> 20) & 3); 389 | *dst_ptr++ = colour * ((bits >> 18) & 3); 390 | *dst_ptr++ = colour * ((bits >> 16) & 3); 391 | *dst_ptr++ = colour * ((bits >> 14) & 3); 392 | *dst_ptr++ = colour * ((bits >> 12) & 3); 393 | *dst_ptr++ = colour * ((bits >> 10) & 3); 394 | *dst_ptr++ = colour * ((bits >> 8) & 3); 395 | *dst_ptr++ = colour * ((bits >> 6) & 3); 396 | *dst_ptr++ = colour * ((bits >> 4) & 3); 397 | *dst_ptr++ = colour * ((bits >> 2) & 3); 398 | *dst_ptr++ = colour * (bits & 3); 399 | *dst_ptr++ = 0; 400 | } 401 | #endif 402 | } 403 | } 404 | 405 | if (++v_scanline == v_total_active_lines) { 406 | v_scanline = 0; 407 | line_num = -1; 408 | if (flip_next) { 409 | flip_next = false; 410 | display->flip_now(); 411 | } 412 | __sev(); 413 | } 414 | } 415 | 416 | // ---------------------------------------------------------------------------- 417 | // Experimental clock config 418 | 419 | #ifndef MICROPY_BUILD_TYPE 420 | static void __no_inline_not_in_flash_func(set_qmi_timing)() { 421 | // Make sure flash is deselected - QMI doesn't appear to have a busy flag(!) 422 | while ((ioqspi_hw->io[1].status & IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) != IO_QSPI_GPIO_QSPI_SS_STATUS_OUTTOPAD_BITS) 423 | ; 424 | 425 | qmi_hw->m[0].timing = 0x40000202; 426 | //qmi_hw->m[0].timing = 0x40000101; 427 | // Force a read through XIP to ensure the timing is applied 428 | volatile uint32_t* ptr = (volatile uint32_t*)0x14000000; 429 | (void) *ptr; 430 | } 431 | #endif 432 | 433 | extern "C" void __no_inline_not_in_flash_func(display_setup_clock_preinit)() { 434 | uint32_t intr_stash = save_and_disable_interrupts(); 435 | 436 | // Before messing with clock speeds ensure QSPI clock is nice and slow 437 | hw_write_masked(&qmi_hw->m[0].timing, 6, QMI_M0_TIMING_CLKDIV_BITS); 438 | 439 | // We're going to go fast, boost the voltage a little 440 | vreg_set_voltage(VREG_VOLTAGE_1_15); 441 | 442 | // Force a read through XIP to ensure the timing is applied before raising the clock rate 443 | volatile uint32_t* ptr = (volatile uint32_t*)0x14000000; 444 | (void) *ptr; 445 | 446 | // Before we touch PLLs, switch sys and ref cleanly away from their aux sources. 447 | hw_clear_bits(&clocks_hw->clk[clk_sys].ctrl, CLOCKS_CLK_SYS_CTRL_SRC_BITS); 448 | while (clocks_hw->clk[clk_sys].selected != 0x1) 449 | tight_loop_contents(); 450 | hw_write_masked(&clocks_hw->clk[clk_ref].ctrl, CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, CLOCKS_CLK_REF_CTRL_SRC_BITS); 451 | while (clocks_hw->clk[clk_ref].selected != 0x4) 452 | tight_loop_contents(); 453 | 454 | // Stop the other clocks so we don't worry about overspeed 455 | clock_stop(clk_usb); 456 | clock_stop(clk_adc); 457 | clock_stop(clk_peri); 458 | clock_stop(clk_hstx); 459 | 460 | // Set USB PLL to 528MHz 461 | pll_init(pll_usb, PLL_COMMON_REFDIV, 1584 * MHZ, 3, 1); 462 | 463 | const uint32_t usb_pll_freq = 528 * MHZ; 464 | 465 | // CLK SYS = PLL USB 528MHz / 2 = 264MHz 466 | clock_configure(clk_sys, 467 | CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, 468 | CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, 469 | usb_pll_freq, usb_pll_freq / 2); 470 | 471 | // CLK PERI = PLL USB 528MHz / 4 = 132MHz 472 | clock_configure(clk_peri, 473 | 0, // Only AUX mux on ADC 474 | CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, 475 | usb_pll_freq, usb_pll_freq / 4); 476 | 477 | // CLK USB = PLL USB 528MHz / 11 = 48MHz 478 | clock_configure(clk_usb, 479 | 0, // No GLMUX 480 | CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, 481 | usb_pll_freq, 482 | USB_CLK_KHZ * KHZ); 483 | 484 | // CLK ADC = PLL USB 528MHz / 11 = 48MHz 485 | clock_configure(clk_adc, 486 | 0, // No GLMUX 487 | CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, 488 | usb_pll_freq, 489 | USB_CLK_KHZ * KHZ); 490 | 491 | // Now we are running fast set fast QSPI clock and read delay 492 | // On MicroPython this is setup by main. 493 | #ifndef MICROPY_BUILD_TYPE 494 | set_qmi_timing(); 495 | #endif 496 | 497 | restore_interrupts(intr_stash); 498 | } 499 | 500 | #ifndef MICROPY_BUILD_TYPE 501 | // Trigger clock setup early - on MicroPython this is done by a hook in main. 502 | namespace { 503 | class DV_preinit { 504 | public: 505 | DV_preinit() { 506 | display_setup_clock_preinit(); 507 | } 508 | }; 509 | DV_preinit dv_preinit __attribute__ ((init_priority (101))) ; 510 | } 511 | #endif 512 | 513 | void DVHSTX::display_setup_clock() { 514 | const uint32_t dvi_clock_khz = timing_mode->bit_clk_khz >> 1; 515 | uint vco_freq, post_div1, post_div2; 516 | if (!check_sys_clock_khz(dvi_clock_khz, &vco_freq, &post_div1, &post_div2)) 517 | panic("System clock of %u kHz cannot be exactly achieved", dvi_clock_khz); 518 | const uint32_t freq = vco_freq / (post_div1 * post_div2); 519 | 520 | if (timing_mode->bit_clk_khz > 600000) { 521 | vreg_set_voltage(VREG_VOLTAGE_1_25); 522 | } else if (timing_mode->bit_clk_khz > 800000) { 523 | // YOLO mode 524 | hw_set_bits(&powman_hw->vreg_ctrl, POWMAN_PASSWORD_BITS | POWMAN_VREG_CTRL_DISABLE_VOLTAGE_LIMIT_BITS); 525 | vreg_set_voltage(VREG_VOLTAGE_1_40); 526 | if (timing_mode->bit_clk_khz > 900000) { 527 | vreg_set_voltage(VREG_VOLTAGE_1_50); 528 | } 529 | sleep_ms(1); 530 | } 531 | 532 | // Set the sys PLL to the requested freq 533 | pll_init(pll_sys, PLL_COMMON_REFDIV, vco_freq, post_div1, post_div2); 534 | 535 | // CLK HSTX = Requested freq 536 | clock_configure(clk_hstx, 537 | 0, 538 | CLOCKS_CLK_HSTX_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, 539 | freq, freq); 540 | } 541 | 542 | void DVHSTX::write_pixel(const Point &p, uint16_t colour) 543 | { 544 | *point_to_ptr16(p) = colour; 545 | } 546 | 547 | void DVHSTX::write_pixel_span(const Point &p, uint l, uint16_t colour) 548 | { 549 | uint16_t* ptr = point_to_ptr16(p); 550 | for (uint i = 0; i < l; ++i) ptr[i] = colour; 551 | } 552 | 553 | void DVHSTX::write_pixel_span(const Point &p, uint l, uint16_t *data) 554 | { 555 | uint16_t* ptr = point_to_ptr16(p); 556 | for (uint i = 0; i < l; ++i) ptr[i] = data[i]; 557 | } 558 | 559 | void DVHSTX::read_pixel_span(const Point &p, uint l, uint16_t *data) 560 | { 561 | const uint16_t* ptr = point_to_ptr16(p); 562 | for (uint i = 0; i < l; ++i) data[i] = ptr[i]; 563 | } 564 | 565 | void DVHSTX::set_palette(RGB888 new_palette[PALETTE_SIZE]) 566 | { 567 | memcpy(palette, new_palette, PALETTE_SIZE * sizeof(RGB888)); 568 | } 569 | 570 | void DVHSTX::set_palette_colour(uint8_t entry, RGB888 colour) 571 | { 572 | palette[entry] = colour; 573 | } 574 | 575 | RGB888* DVHSTX::get_palette() 576 | { 577 | return palette; 578 | } 579 | 580 | void DVHSTX::write_palette_pixel(const Point &p, uint8_t colour) 581 | { 582 | *point_to_ptr_palette(p) = colour; 583 | } 584 | 585 | void DVHSTX::write_palette_pixel_span(const Point &p, uint l, uint8_t colour) 586 | { 587 | uint8_t* ptr = point_to_ptr_palette(p); 588 | memset(ptr, colour, l); 589 | } 590 | 591 | void DVHSTX::write_palette_pixel_span(const Point &p, uint l, uint8_t* data) 592 | { 593 | uint8_t* ptr = point_to_ptr_palette(p); 594 | memcpy(ptr, data, l); 595 | } 596 | 597 | void DVHSTX::read_palette_pixel_span(const Point &p, uint l, uint8_t *data) 598 | { 599 | const uint8_t* ptr = point_to_ptr_palette(p); 600 | memcpy(data, ptr, l); 601 | } 602 | 603 | void DVHSTX::write_text(const Point &p, const char* text, TextColour colour, bool immediate) 604 | { 605 | char* ptr = (char*)point_to_ptr_text(p, immediate); 606 | int len = std::min((int)(frame_width - p.x), (int)strlen(text)); 607 | memcpy(ptr, text, len); 608 | if (mode == MODE_TEXT_RGB111) memset(ptr + frame_width * frame_height, (uint8_t)colour, len); 609 | } 610 | 611 | void DVHSTX::clear() 612 | { 613 | memset(frame_buffer_back, 0, frame_width * frame_height * frame_bytes_per_pixel); 614 | } 615 | 616 | DVHSTX::DVHSTX() 617 | { 618 | // Always use the bottom channels 619 | dma_claim_mask((1 << NUM_CHANS) - 1); 620 | } 621 | 622 | bool DVHSTX::init(uint16_t width, uint16_t height, Mode mode_, Pinout pinout) 623 | { 624 | if (inited) reset(); 625 | 626 | ch_num = 0; 627 | line_num = -1; 628 | v_scanline = 2; 629 | flip_next = false; 630 | 631 | display_width = width; 632 | display_height = height; 633 | frame_width = width; 634 | frame_height = height; 635 | mode = mode_; 636 | 637 | timing_mode = nullptr; 638 | if (mode == MODE_TEXT_MONO || mode == MODE_TEXT_RGB111) { 639 | width = 1280; 640 | height = 720; 641 | display_width = 91; 642 | frame_width = 91; 643 | display_height = 30; 644 | frame_height = 30; 645 | h_repeat_shift = 0; 646 | v_repeat_shift = 0; 647 | timing_mode = &dvi_timing_1280x720p_rb_50hz; 648 | } 649 | else if (width == 320 && height == 180) { 650 | h_repeat_shift = 2; 651 | v_repeat_shift = 2; 652 | timing_mode = &dvi_timing_1280x720p_rb_50hz; 653 | } 654 | else if (width == 640 && height == 360) { 655 | h_repeat_shift = 1; 656 | v_repeat_shift = 1; 657 | timing_mode = &dvi_timing_1280x720p_rb_50hz; 658 | } 659 | else if (width == 480 && height == 270) { 660 | h_repeat_shift = 2; 661 | v_repeat_shift = 2; 662 | timing_mode = &dvi_timing_1920x1080p_rb2_30hz; 663 | } 664 | else 665 | { 666 | uint16_t full_width = display_width; 667 | uint16_t full_height = display_height; 668 | h_repeat_shift = 0; 669 | v_repeat_shift = 0; 670 | 671 | if (display_width < 640) { 672 | h_repeat_shift = 1; 673 | full_width *= 2; 674 | } 675 | 676 | if (display_height < 400) { 677 | v_repeat_shift = 1; 678 | full_height *= 2; 679 | } 680 | 681 | if (full_width == 640) { 682 | if (full_height == 480) timing_mode = &dvi_timing_640x480p_60hz; 683 | } 684 | else if (full_width == 720) { 685 | if (full_height == 480) timing_mode = &dvi_timing_720x480p_60hz; 686 | else if (full_height == 400) timing_mode = &dvi_timing_720x400p_70hz; 687 | else if (full_height == 576) timing_mode = &dvi_timing_720x576p_50hz; 688 | } 689 | else if (full_width == 800) { 690 | if (full_height == 600) timing_mode = &dvi_timing_800x600p_60hz; 691 | else if (full_height == 480) timing_mode = &dvi_timing_800x480p_60hz; 692 | else if (full_height == 450) timing_mode = &dvi_timing_800x450p_60hz; 693 | } 694 | else if (full_width == 960) { 695 | if (full_height == 540) timing_mode = &dvi_timing_960x540p_60hz; 696 | } 697 | else if (full_width == 1024) { 698 | if (full_height == 768) timing_mode = &dvi_timing_1024x768_rb_60hz; 699 | } 700 | } 701 | 702 | if (!timing_mode) { 703 | dvhstx_debug("Unsupported resolution %dx%d", width, height); 704 | return false; 705 | } 706 | 707 | display = this; 708 | display_palette = get_palette(); 709 | 710 | dvhstx_debug("Setup clock\n"); 711 | display_setup_clock(); 712 | 713 | #ifndef MICROPY_BUILD_TYPE 714 | stdio_init_all(); 715 | #endif 716 | dvhstx_debug("Clock setup done\n"); 717 | 718 | v_inactive_total = timing_mode->v_front_porch + timing_mode->v_sync_width + timing_mode->v_back_porch; 719 | v_total_active_lines = v_inactive_total + timing_mode->v_active_lines; 720 | v_repeat = 1 << v_repeat_shift; 721 | h_repeat = 1 << h_repeat_shift; 722 | 723 | memcpy(vblank_line_vsync_off, vblank_line_vsync_off_src, sizeof(vblank_line_vsync_off_src)); 724 | vblank_line_vsync_off[0] |= timing_mode->h_front_porch; 725 | vblank_line_vsync_off[2] |= timing_mode->h_sync_width; 726 | vblank_line_vsync_off[4] |= timing_mode->h_back_porch + timing_mode->h_active_pixels; 727 | 728 | memcpy(vblank_line_vsync_on, vblank_line_vsync_on_src, sizeof(vblank_line_vsync_on_src)); 729 | vblank_line_vsync_on[0] |= timing_mode->h_front_porch; 730 | vblank_line_vsync_on[2] |= timing_mode->h_sync_width; 731 | vblank_line_vsync_on[4] |= timing_mode->h_back_porch + timing_mode->h_active_pixels; 732 | 733 | memcpy(vactive_line_header, vactive_line_header_src, sizeof(vactive_line_header_src)); 734 | vactive_line_header[0] |= timing_mode->h_front_porch; 735 | vactive_line_header[2] |= timing_mode->h_sync_width; 736 | vactive_line_header[4] |= timing_mode->h_back_porch; 737 | vactive_line_header[6] |= timing_mode->h_active_pixels; 738 | 739 | memcpy(vactive_text_line_header, vactive_text_line_header_src, sizeof(vactive_text_line_header_src)); 740 | vactive_text_line_header[0] |= timing_mode->h_front_porch; 741 | vactive_text_line_header[2] |= timing_mode->h_sync_width; 742 | vactive_text_line_header[4] |= timing_mode->h_back_porch; 743 | vactive_text_line_header[7+6] |= timing_mode->h_active_pixels - 6; 744 | 745 | switch (mode) { 746 | case MODE_RGB565: 747 | frame_bytes_per_pixel = 2; 748 | line_bytes_per_pixel = 2; 749 | break; 750 | case MODE_PALETTE: 751 | frame_bytes_per_pixel = 1; 752 | line_bytes_per_pixel = 4; 753 | break; 754 | case MODE_RGB888: 755 | frame_bytes_per_pixel = 4; 756 | line_bytes_per_pixel = 4; 757 | break; 758 | case MODE_TEXT_MONO: 759 | frame_bytes_per_pixel = 1; 760 | line_bytes_per_pixel = 4; 761 | break; 762 | case MODE_TEXT_RGB111: 763 | frame_bytes_per_pixel = 2; 764 | line_bytes_per_pixel = 14; 765 | break; 766 | default: 767 | dvhstx_debug("Unsupported mode %d", (int)mode); 768 | return false; 769 | } 770 | 771 | #ifdef MICROPY_BUILD_TYPE 772 | if (frame_width * frame_height * frame_bytes_per_pixel > sizeof(frame_buffer_a)) { 773 | panic("Frame buffer too large"); 774 | } 775 | 776 | frame_buffer_display = frame_buffer_a; 777 | frame_buffer_back = frame_buffer_b; 778 | #else 779 | frame_buffer_display = (uint8_t*)malloc(frame_width * frame_height * frame_bytes_per_pixel); 780 | frame_buffer_back = (uint8_t*)malloc(frame_width * frame_height * frame_bytes_per_pixel); 781 | #endif 782 | memset(frame_buffer_display, 0, frame_width * frame_height * frame_bytes_per_pixel); 783 | memset(frame_buffer_back, 0, frame_width * frame_height * frame_bytes_per_pixel); 784 | 785 | memset(palette, 0, PALETTE_SIZE * sizeof(palette[0])); 786 | 787 | frame_buffer_display = frame_buffer_display; 788 | dvhstx_debug("Frame buffers inited\n"); 789 | 790 | const bool is_text_mode = (mode == MODE_TEXT_MONO || mode == MODE_TEXT_RGB111); 791 | const int frame_pixel_words = (frame_width * h_repeat * line_bytes_per_pixel + 3) >> 2; 792 | const int frame_line_words = frame_pixel_words + (is_text_mode ? count_of(vactive_text_line_header) : count_of(vactive_line_header)); 793 | const int frame_lines = (v_repeat == 1) ? NUM_CHANS : NUM_FRAME_LINES; 794 | line_buffers = (uint32_t*)malloc(frame_line_words * 4 * frame_lines); 795 | 796 | for (int i = 0; i < frame_lines; ++i) 797 | { 798 | if (is_text_mode) memcpy(&line_buffers[i * frame_line_words], vactive_text_line_header, count_of(vactive_text_line_header) * sizeof(uint32_t)); 799 | else memcpy(&line_buffers[i * frame_line_words], vactive_line_header, count_of(vactive_line_header) * sizeof(uint32_t)); 800 | } 801 | 802 | if (mode == MODE_TEXT_RGB111) { 803 | // Need to pre-render the font to RAM to be fast enough. 804 | font_cache = (uint32_t*)malloc(4 * FONT->line_height * GLYPH_COUNT); 805 | uint32_t* font_cache_ptr = font_cache; 806 | for (int c = 0; c < GLYPH_COUNT; ++c) { 807 | for (int y = 0; y < FONT->line_height; ++y) { 808 | *font_cache_ptr++ = render_char_line(c + FIRST_GLYPH, y); 809 | } 810 | } 811 | } 812 | 813 | // Ensure HSTX FIFO is clear 814 | reset_block_num(RESET_HSTX); 815 | sleep_us(10); 816 | unreset_block_num_wait_blocking(RESET_HSTX); 817 | sleep_us(10); 818 | 819 | switch (mode) { 820 | case MODE_RGB565: 821 | // Configure HSTX's TMDS encoder for RGB565 822 | hstx_ctrl_hw->expand_tmds = 823 | 4 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB | 824 | 8 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB | 825 | 5 << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB | 826 | 3 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB | 827 | 4 << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB | 828 | 29 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB; 829 | 830 | // Pixels (TMDS) come in 2 16-bit chunks. Control symbols (RAW) are an 831 | // entire 32-bit word. 832 | hstx_ctrl_hw->expand_shift = 833 | 2 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB | 834 | 16 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB | 835 | 1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB | 836 | 0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB; 837 | break; 838 | 839 | case MODE_PALETTE: 840 | // Configure HSTX's TMDS encoder for RGB888 841 | hstx_ctrl_hw->expand_tmds = 842 | 7 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB | 843 | 16 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB | 844 | 7 << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB | 845 | 8 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB | 846 | 7 << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB | 847 | 0 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB; 848 | 849 | // Pixels and control symbols (RAW) are an 850 | // entire 32-bit word. 851 | hstx_ctrl_hw->expand_shift = 852 | 1 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB | 853 | 0 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB | 854 | 1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB | 855 | 0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB; 856 | break; 857 | 858 | case MODE_TEXT_MONO: 859 | // Configure HSTX's TMDS encoder for 2bpp 860 | hstx_ctrl_hw->expand_tmds = 861 | 1 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB | 862 | 18 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB | 863 | 1 << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB | 864 | 18 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB | 865 | 1 << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB | 866 | 18 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB; 867 | 868 | // Pixels and control symbols (RAW) are an 869 | // entire 32-bit word. 870 | hstx_ctrl_hw->expand_shift = 871 | 14 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB | 872 | 30 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB | 873 | 1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB | 874 | 0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB; 875 | break; 876 | 877 | case MODE_TEXT_RGB111: 878 | // Configure HSTX's TMDS encoder for RGB222 879 | hstx_ctrl_hw->expand_tmds = 880 | 1 << HSTX_CTRL_EXPAND_TMDS_L2_NBITS_LSB | 881 | 0 << HSTX_CTRL_EXPAND_TMDS_L2_ROT_LSB | 882 | 1 << HSTX_CTRL_EXPAND_TMDS_L1_NBITS_LSB | 883 | 29 << HSTX_CTRL_EXPAND_TMDS_L1_ROT_LSB | 884 | 1 << HSTX_CTRL_EXPAND_TMDS_L0_NBITS_LSB | 885 | 26 << HSTX_CTRL_EXPAND_TMDS_L0_ROT_LSB; 886 | 887 | // Pixels (TMDS) come in 4 8-bit chunks. Control symbols (RAW) are an 888 | // entire 32-bit word. 889 | hstx_ctrl_hw->expand_shift = 890 | 4 << HSTX_CTRL_EXPAND_SHIFT_ENC_N_SHIFTS_LSB | 891 | 8 << HSTX_CTRL_EXPAND_SHIFT_ENC_SHIFT_LSB | 892 | 1 << HSTX_CTRL_EXPAND_SHIFT_RAW_N_SHIFTS_LSB | 893 | 0 << HSTX_CTRL_EXPAND_SHIFT_RAW_SHIFT_LSB; 894 | break; 895 | 896 | default: 897 | dvhstx_debug("Unsupported mode %d", (int)mode); 898 | return false; 899 | } 900 | 901 | // Serial output config: clock period of 5 cycles, pop from command 902 | // expander every 5 cycles, shift the output shiftreg by 2 every cycle. 903 | hstx_ctrl_hw->csr = 0; 904 | hstx_ctrl_hw->csr = 905 | HSTX_CTRL_CSR_EXPAND_EN_BITS | 906 | 5u << HSTX_CTRL_CSR_CLKDIV_LSB | 907 | 5u << HSTX_CTRL_CSR_N_SHIFTS_LSB | 908 | 2u << HSTX_CTRL_CSR_SHIFT_LSB | 909 | HSTX_CTRL_CSR_EN_BITS; 910 | 911 | // HSTX outputs 0 through 7 appear on GPIO 12 through 19. 912 | constexpr int HSTX_FIRST_PIN = 12; 913 | 914 | // Assign clock pair to two neighbouring pins: 915 | { 916 | int bit = pinout.clk_p - HSTX_FIRST_PIN; 917 | hstx_ctrl_hw->bit[bit ] = HSTX_CTRL_BIT0_CLK_BITS; 918 | hstx_ctrl_hw->bit[bit ^ 1] = HSTX_CTRL_BIT0_CLK_BITS | HSTX_CTRL_BIT0_INV_BITS; 919 | } 920 | 921 | for (uint lane = 0; lane < 3; ++lane) { 922 | // For each TMDS lane, assign it to the correct GPIO pair based on the 923 | // desired pinout: 924 | int bit = pinout.rgb_p[lane] - HSTX_FIRST_PIN; 925 | // Output even bits during first half of each HSTX cycle, and odd bits 926 | // during second half. The shifter advances by two bits each cycle. 927 | uint32_t lane_data_sel_bits = 928 | (lane * 10 ) << HSTX_CTRL_BIT0_SEL_P_LSB | 929 | (lane * 10 + 1) << HSTX_CTRL_BIT0_SEL_N_LSB; 930 | // The two halves of each pair get identical data, but one pin is inverted. 931 | hstx_ctrl_hw->bit[bit ] = lane_data_sel_bits; 932 | hstx_ctrl_hw->bit[bit ^ 1] = lane_data_sel_bits | HSTX_CTRL_BIT0_INV_BITS; 933 | } 934 | 935 | for (int i = 12; i <= 19; ++i) { 936 | gpio_set_function(i, GPIO_FUNC_HSTX); 937 | gpio_set_drive_strength(i, GPIO_DRIVE_STRENGTH_4MA); 938 | if (timing_mode->bit_clk_khz > 900000) { 939 | gpio_set_slew_rate(i, GPIO_SLEW_RATE_FAST); 940 | } 941 | } 942 | 943 | dvhstx_debug("GPIO configured\n"); 944 | 945 | // The channels are set up identically, to transfer a whole scanline and 946 | // then chain to the next channel. Each time a channel finishes, we 947 | // reconfigure the one that just finished, meanwhile the other channel(s) 948 | // are already making progress. 949 | // Using just 2 channels was insufficient to avoid issues with the IRQ. 950 | dma_channel_config c; 951 | c = dma_channel_get_default_config(0); 952 | channel_config_set_chain_to(&c, 1); 953 | channel_config_set_dreq(&c, DREQ_HSTX); 954 | dma_channel_configure( 955 | 0, 956 | &c, 957 | &hstx_fifo_hw->fifo, 958 | vblank_line_vsync_off, 959 | count_of(vblank_line_vsync_off), 960 | false 961 | ); 962 | c = dma_channel_get_default_config(1); 963 | channel_config_set_chain_to(&c, 2); 964 | channel_config_set_dreq(&c, DREQ_HSTX); 965 | dma_channel_configure( 966 | 1, 967 | &c, 968 | &hstx_fifo_hw->fifo, 969 | vblank_line_vsync_off, 970 | count_of(vblank_line_vsync_off), 971 | false 972 | ); 973 | for (int i = 2; i < NUM_CHANS; ++i) { 974 | c = dma_channel_get_default_config(i); 975 | channel_config_set_chain_to(&c, (i+1) % NUM_CHANS); 976 | channel_config_set_dreq(&c, DREQ_HSTX); 977 | dma_channel_configure( 978 | i, 979 | &c, 980 | &hstx_fifo_hw->fifo, 981 | vblank_line_vsync_off, 982 | count_of(vblank_line_vsync_off), 983 | false 984 | ); 985 | } 986 | 987 | dvhstx_debug("DMA channels claimed\n"); 988 | 989 | dma_hw->intr = (1 << NUM_CHANS) - 1; 990 | dma_hw->ints2 = (1 << NUM_CHANS) - 1; 991 | dma_hw->inte2 = (1 << NUM_CHANS) - 1; 992 | if (is_text_mode) irq_set_exclusive_handler(DMA_IRQ_2, dma_irq_handler_text); 993 | else irq_set_exclusive_handler(DMA_IRQ_2, dma_irq_handler); 994 | irq_set_enabled(DMA_IRQ_2, true); 995 | 996 | dma_channel_start(0); 997 | 998 | dvhstx_debug("DVHSTX started\n"); 999 | 1000 | for (int i = 0; i < frame_height; ++i) { 1001 | memset(&frame_buffer_display[i * frame_width * frame_bytes_per_pixel], i, frame_width * frame_bytes_per_pixel); 1002 | } 1003 | 1004 | dvhstx_debug("Frame buffer filled\n"); 1005 | 1006 | inited = true; 1007 | return true; 1008 | } 1009 | 1010 | void DVHSTX::reset() { 1011 | if (!inited) return; 1012 | inited = false; 1013 | 1014 | hstx_ctrl_hw->csr = 0; 1015 | 1016 | irq_set_enabled(DMA_IRQ_2, false); 1017 | irq_remove_handler(DMA_IRQ_2, irq_get_exclusive_handler(DMA_IRQ_2)); 1018 | 1019 | for (int i = 0; i < NUM_CHANS; ++i) 1020 | dma_channel_abort(i); 1021 | 1022 | if (font_cache) { 1023 | free(font_cache); 1024 | font_cache = nullptr; 1025 | } 1026 | free(line_buffers); 1027 | 1028 | #ifndef MICROPY_BUILD_TYPE 1029 | free(frame_buffer_display); 1030 | free(frame_buffer_back); 1031 | #endif 1032 | } 1033 | 1034 | void DVHSTX::flip_blocking() { 1035 | wait_for_vsync(); 1036 | flip_now(); 1037 | } 1038 | 1039 | void DVHSTX::flip_now() { 1040 | std::swap(frame_buffer_display, frame_buffer_back); 1041 | } 1042 | 1043 | void DVHSTX::wait_for_vsync() { 1044 | while (v_scanline >= timing_mode->v_front_porch) __wfe(); 1045 | } 1046 | 1047 | void DVHSTX::flip_async() { 1048 | flip_next = true; 1049 | } 1050 | 1051 | void DVHSTX::wait_for_flip() { 1052 | while (flip_next) __wfe(); 1053 | } -------------------------------------------------------------------------------- /drivers/dvhstx/dvhstx.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "pico/stdlib.h" 6 | #include "hardware/gpio.h" 7 | #include "common/pimoroni_common.hpp" 8 | #include "common/pimoroni_i2c.hpp" 9 | #include "libraries/pico_graphics/pico_graphics.hpp" 10 | 11 | // DVI HSTX driver for use with Pimoroni PicoGraphics 12 | 13 | namespace pimoroni { 14 | 15 | // Digital Video using HSTX 16 | // Valid screen modes are: 17 | // Pixel doubled: 640x480 (60Hz), 720x480 (60Hz), 720x400 (70Hz), 720x576 (50Hz), 18 | // 800x600 (60Hz), 800x480 (60Hz), 800x450 (60Hz), 960x540 (60Hz), 1024x768 (60Hz) 19 | // Pixel doubled or quadrupled: 1280x720 (50Hz) 20 | // 21 | // Giving valid resolutions: 22 | // 320x180, 640x360 (well supported, square pixels on a 16:9 display) 23 | // 480x270, 400x225 (sometimes supported, square pixels on a 16:9 display) 24 | // 320x240, 360x240, 360x200, 360x288, 400x300, 512x384 (well supported, but pixels aren't square) 25 | // 400x240 (sometimes supported, pixels aren't square) 26 | // 27 | // Note that the double buffer is in RAM, so 640x360 uses almost all of the available RAM. 28 | class DVHSTX { 29 | public: 30 | static constexpr int PALETTE_SIZE = 256; 31 | 32 | struct Pinout { 33 | uint8_t clk_p, rgb_p[3]; 34 | }; 35 | 36 | enum Mode { 37 | MODE_PALETTE = 2, 38 | MODE_RGB565 = 1, 39 | MODE_RGB888 = 3, 40 | MODE_TEXT_MONO = 4, 41 | MODE_TEXT_RGB111 = 5, 42 | }; 43 | 44 | enum TextColour { 45 | TEXT_BLACK = 0, 46 | TEXT_RED = 0b1000000, 47 | TEXT_GREEN = 0b0001000, 48 | TEXT_BLUE = 0b0000001, 49 | TEXT_YELLOW = 0b1001000, 50 | TEXT_MAGENTA = 0b1000001, 51 | TEXT_CYAN = 0b0001001, 52 | TEXT_WHITE = 0b1001001, 53 | }; 54 | 55 | //-------------------------------------------------- 56 | // Variables 57 | //-------------------------------------------------- 58 | protected: 59 | friend void vsync_callback(); 60 | 61 | uint16_t display_width = 320; 62 | uint16_t display_height = 180; 63 | uint16_t frame_width = 320; 64 | uint16_t frame_height = 180; 65 | uint8_t frame_bytes_per_pixel = 2; 66 | uint8_t h_repeat = 4; 67 | uint8_t v_repeat = 4; 68 | Mode mode = MODE_RGB565; 69 | 70 | public: 71 | DVHSTX(); 72 | 73 | //-------------------------------------------------- 74 | // Methods 75 | //-------------------------------------------------- 76 | public: 77 | // 16bpp interface 78 | void write_pixel(const Point &p, uint16_t colour); 79 | void write_pixel_span(const Point &p, uint l, uint16_t colour); 80 | void write_pixel_span(const Point &p, uint l, uint16_t *data); 81 | void read_pixel_span(const Point &p, uint l, uint16_t *data); 82 | 83 | // 256 colour palette mode. 84 | void set_palette(RGB888 new_palette[PALETTE_SIZE]); 85 | void set_palette_colour(uint8_t entry, RGB888 colour); 86 | RGB888* get_palette(); 87 | 88 | void write_palette_pixel(const Point &p, uint8_t colour); 89 | void write_palette_pixel_span(const Point &p, uint l, uint8_t colour); 90 | void write_palette_pixel_span(const Point &p, uint l, uint8_t* data); 91 | void read_palette_pixel_span(const Point &p, uint l, uint8_t *data); 92 | 93 | // Text mode (91 x 30) 94 | // Immediate writes to the active buffer instead of the back buffer 95 | void write_text(const Point &p, const char* text, TextColour colour = TEXT_WHITE, bool immediate = false); 96 | 97 | void clear(); 98 | 99 | bool init(uint16_t width, uint16_t height, Mode mode = MODE_RGB565, Pinout pinout = {13, 15, 17, 19}); 100 | void reset(); 101 | 102 | // Wait for vsync and then flip the buffers 103 | void flip_blocking(); 104 | 105 | // Flip immediately without waiting for vsync 106 | void flip_now(); 107 | 108 | void wait_for_vsync(); 109 | 110 | // flip_async queues a flip to happen next vsync but returns without blocking. 111 | // You should call wait_for_flip before doing any more reads or writes, defining sprites, etc. 112 | void flip_async(); 113 | void wait_for_flip(); 114 | 115 | // DMA handlers, should not be called externally 116 | void gfx_dma_handler(); 117 | void text_dma_handler(); 118 | 119 | private: 120 | RGB888 palette[PALETTE_SIZE]; 121 | 122 | uint8_t* frame_buffer_display; 123 | uint8_t* frame_buffer_back; 124 | uint32_t* font_cache = nullptr; 125 | 126 | uint16_t* point_to_ptr16(const Point &p) const { 127 | return ((uint16_t*)frame_buffer_back) + (p.y * (uint32_t)frame_width) + p.x; 128 | } 129 | 130 | uint8_t* point_to_ptr_palette(const Point &p) const { 131 | return frame_buffer_back + (p.y * (uint32_t)frame_width) + p.x; 132 | } 133 | 134 | uint8_t* point_to_ptr_text(const Point &p, bool immediate) const { 135 | const uint32_t offset = (p.y * (uint32_t)frame_width) + p.x; 136 | if (immediate) return frame_buffer_display + offset; 137 | return frame_buffer_back + offset; 138 | } 139 | 140 | void display_setup_clock(); 141 | 142 | // DMA scanline filling 143 | uint ch_num = 0; 144 | int line_num = -1; 145 | 146 | volatile int v_scanline = 2; 147 | volatile bool flip_next; 148 | 149 | bool inited = false; 150 | 151 | uint32_t* line_buffers; 152 | const struct dvi_timing* timing_mode; 153 | int v_inactive_total; 154 | int v_total_active_lines; 155 | 156 | uint h_repeat_shift; 157 | uint v_repeat_shift; 158 | int line_bytes_per_pixel; 159 | 160 | uint32_t* display_palette = nullptr; 161 | }; 162 | } 163 | -------------------------------------------------------------------------------- /drivers/dvhstx/dvi.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "dvi.hpp" 4 | 5 | // VGA -- we do this mode properly, with a pretty comfortable clk_sys (252 MHz) 6 | const struct dvi_timing dvi_timing_640x480p_60hz = { 7 | .h_sync_polarity = false, 8 | .h_front_porch = 16, 9 | .h_sync_width = 96, 10 | .h_back_porch = 48, 11 | .h_active_pixels = 640, 12 | 13 | .v_sync_polarity = false, 14 | .v_front_porch = 10, 15 | .v_sync_width = 2, 16 | .v_back_porch = 33, 17 | .v_active_lines = 480, 18 | 19 | .bit_clk_khz = 252000 20 | }; 21 | 22 | // SVGA -- completely by-the-book but requires 400 MHz clk_sys 23 | const struct dvi_timing dvi_timing_800x600p_60hz = { 24 | .h_sync_polarity = false, 25 | .h_front_porch = 44, 26 | .h_sync_width = 128, 27 | .h_back_porch = 88, 28 | .h_active_pixels = 800, 29 | 30 | .v_sync_polarity = false, 31 | .v_front_porch = 1, 32 | .v_sync_width = 4, 33 | .v_back_porch = 23, 34 | .v_active_lines = 600, 35 | 36 | .bit_clk_khz = 400000 37 | }; 38 | 39 | // 720x480 - timings from dumping the EDID of my monitor 40 | const struct dvi_timing dvi_timing_720x480p_60hz = { 41 | .h_sync_polarity = false, 42 | .h_front_porch = 16, 43 | .h_sync_width = 62, 44 | .h_back_porch = 60, 45 | .h_active_pixels = 720, 46 | 47 | .v_sync_polarity = false, 48 | .v_front_porch = 9, 49 | .v_sync_width = 6, 50 | .v_back_porch = 30, 51 | .v_active_lines = 480, 52 | 53 | .bit_clk_khz = 270000 54 | }; 55 | 56 | // 720x576@50Hz - CEA timing 57 | const struct dvi_timing dvi_timing_720x576p_50hz = { 58 | .h_sync_polarity = false, 59 | .h_front_porch = 12, 60 | .h_sync_width = 64, 61 | .h_back_porch = 68, 62 | .h_active_pixels = 720, 63 | 64 | .v_sync_polarity = false, 65 | .v_front_porch = 5, 66 | .v_sync_width = 5, 67 | .v_back_porch = 39, 68 | .v_active_lines = 576, 69 | 70 | .bit_clk_khz = 270000 71 | }; 72 | 73 | // 720x400@70Hz - "IBM" timing 74 | const struct dvi_timing dvi_timing_720x400p_70hz = { 75 | .h_sync_polarity = false, 76 | .h_front_porch = 18, 77 | .h_sync_width = 108, 78 | .h_back_porch = 54, 79 | .h_active_pixels = 720, 80 | 81 | .v_sync_polarity = true, 82 | .v_front_porch = 13, 83 | .v_sync_width = 2, 84 | .v_back_porch = 34, 85 | .v_active_lines = 400, 86 | 87 | .bit_clk_khz = 283200 88 | }; 89 | 90 | // 800x480p 60 Hz (note this doesn't seem to be a CEA mode, I just used the 91 | // output of `cvt 800 480 60`), 295 MHz bit clock 92 | const struct dvi_timing dvi_timing_800x480p_60hz = { 93 | .h_sync_polarity = false, 94 | .h_front_porch = 24, 95 | .h_sync_width = 72, 96 | .h_back_porch = 96, 97 | .h_active_pixels = 800, 98 | 99 | .v_sync_polarity = true, 100 | .v_front_porch = 3, 101 | .v_sync_width = 10, 102 | .v_back_porch = 7, 103 | .v_active_lines = 480, 104 | 105 | .bit_clk_khz = 295200 106 | }; 107 | 108 | // 800x450p 60 Hz Similarly not a CEA mode, but is 16:9 109 | const struct dvi_timing dvi_timing_800x450p_60hz = { 110 | .h_sync_polarity = false, 111 | .h_front_porch = 24, 112 | .h_sync_width = 72, 113 | .h_back_porch = 96, 114 | .h_active_pixels = 800, 115 | 116 | .v_sync_polarity = true, 117 | .v_front_porch = 3, 118 | .v_sync_width = 5, 119 | .v_back_porch = 10, 120 | .v_active_lines = 450, 121 | 122 | .bit_clk_khz = 278400 123 | }; 124 | 125 | // SVGA reduced blanking (355 MHz bit clock) -- valid CVT mode, less common 126 | // than fully-blanked SVGA, but doesn't require such a high system clock 127 | const struct dvi_timing dvi_timing_800x600p_reduced_60hz = { 128 | .h_sync_polarity = true, 129 | .h_front_porch = 48, 130 | .h_sync_width = 32, 131 | .h_back_porch = 80, 132 | .h_active_pixels = 800, 133 | 134 | .v_sync_polarity = false, 135 | .v_front_porch = 3, 136 | .v_sync_width = 4, 137 | .v_back_porch = 11, 138 | .v_active_lines = 600, 139 | 140 | .bit_clk_khz = 354000 141 | }; 142 | 143 | // Also known as qHD, bit uncommon, but it's a nice modest-resolution 16:9 144 | // aspect mode. Pixel clock 40.75 MHz for full CVT mode (no reduced blanking) 145 | const struct dvi_timing dvi_timing_960x540p_60hz = { 146 | .h_sync_polarity = false, 147 | .h_front_porch = 32, 148 | .h_sync_width = 96, 149 | .h_back_porch = 128, 150 | .h_active_pixels = 960, 151 | 152 | .v_sync_polarity = true, 153 | .v_front_porch = 3, 154 | .v_sync_width = 5, 155 | .v_back_porch = 14, 156 | .v_active_lines = 540, 157 | 158 | .bit_clk_khz = 408000 159 | }; 160 | 161 | // Also known as qHD, bit uncommon, but it's a nice modest-resolution 16:9 162 | // aspect mode. Pixel clock 33.5 MHz for 50Hz CVT mode (no reduced blanking) 163 | const struct dvi_timing dvi_timing_960x540p_50hz = { 164 | .h_sync_polarity = false, 165 | .h_front_porch = 24, 166 | .h_sync_width = 96, 167 | .h_back_porch = 120, 168 | .h_active_pixels = 960, 169 | 170 | .v_sync_polarity = true, 171 | .v_front_porch = 3, 172 | .v_sync_width = 5, 173 | .v_back_porch = 11, 174 | .v_active_lines = 540, 175 | 176 | .bit_clk_khz = 336000 177 | }; 178 | 179 | // 1024x768, CVT-RB 180 | const struct dvi_timing dvi_timing_1024x768_rb_60hz = { 181 | .h_sync_polarity = true, 182 | .h_front_porch = 48, 183 | .h_sync_width = 32, 184 | .h_back_porch = 80, 185 | .h_active_pixels = 1024, 186 | 187 | .v_sync_polarity = false, 188 | .v_front_porch = 3, 189 | .v_sync_width = 4, 190 | .v_back_porch = 15, 191 | .v_active_lines = 768, 192 | 193 | .bit_clk_khz = 560000 194 | }; 195 | 196 | // 720p50, this is a standard HD mode, the CVT-RB variant 197 | // should be widely supported 198 | const struct dvi_timing dvi_timing_1280x720p_rb_50hz = { 199 | .h_sync_polarity = true, 200 | .h_front_porch = 48, 201 | .h_sync_width = 32, 202 | .h_back_porch = 80, 203 | .h_active_pixels = 1280, 204 | 205 | .v_sync_polarity = false, 206 | .v_front_porch = 3, 207 | .v_sync_width = 5, 208 | .v_back_porch = 9, 209 | .v_active_lines = 720, 210 | 211 | .bit_clk_khz = 528000 212 | }; 213 | 214 | // 720p60, this is the CVT-RB variant, again should be widely supported 215 | const struct dvi_timing dvi_timing_1280x720p_rb_60hz = { 216 | .h_sync_polarity = true, 217 | .h_front_porch = 48, 218 | .h_sync_width = 32, 219 | .h_back_porch = 80, 220 | .h_active_pixels = 1280, 221 | 222 | .v_sync_polarity = false, 223 | .v_front_porch = 3, 224 | .v_sync_width = 5, 225 | .v_back_porch = 13, 226 | .v_active_lines = 720, 227 | 228 | .bit_clk_khz = 640000 229 | }; 230 | 231 | // 1080p30 - not a normal mode but seems to work on a wide variety of hardware 232 | // Strictly speaking RB2 should have a clock speed matching the target frequency more closely 233 | // but it seems to work! 234 | const struct dvi_timing dvi_timing_1920x1080p_rb2_30hz = { 235 | .h_sync_polarity = true, 236 | .h_front_porch = 8, 237 | .h_sync_width = 32, 238 | .h_back_porch = 40, 239 | .h_active_pixels = 1920, 240 | 241 | .v_sync_polarity = false, 242 | .v_front_porch = 7, 243 | .v_sync_width = 8, 244 | .v_back_porch = 6, 245 | .v_active_lines = 1080, 246 | 247 | .bit_clk_khz = 660000 248 | }; 249 | 250 | // 1080p48 YOLO - may require more than 1.3V, and sorting through your 251 | // devboards to find one that gives a clean signal. 252 | const struct dvi_timing dvi_timing_1920x1080p_yolo_48hz = { 253 | .h_sync_polarity = true, 254 | .h_front_porch = 8, 255 | .h_sync_width = 16, 256 | .h_back_porch = 24, 257 | .h_active_pixels = 1920, 258 | 259 | .v_sync_polarity = false, 260 | .v_front_porch = 4, 261 | .v_sync_width = 2, 262 | .v_back_porch = 6, 263 | .v_active_lines = 1080, 264 | 265 | .bit_clk_khz = 1032000 266 | }; 267 | 268 | // 1440p24 YOLO - works on my Dell Ultrasharp, that most forgiving of monitors. May require a little more than 1.3V 269 | const struct dvi_timing dvi_timing_2560x1440p_yolo_24hz = { 270 | .h_sync_polarity = true, 271 | .h_front_porch = 8, 272 | .h_sync_width = 32, 273 | .h_back_porch = 20, 274 | .h_active_pixels = 2560, 275 | 276 | .v_sync_polarity = false, 277 | .v_front_porch = 2, 278 | .v_sync_width = 6, 279 | .v_back_porch = 2, 280 | .v_active_lines = 1440, 281 | 282 | .bit_clk_khz = 912000 283 | }; 284 | -------------------------------------------------------------------------------- /drivers/dvhstx/dvi.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // ---------------------------------------------------------------------------- 4 | // DVI constants 5 | 6 | #define TMDS_CTRL_00 0x354u 7 | #define TMDS_CTRL_01 0x0abu 8 | #define TMDS_CTRL_10 0x154u 9 | #define TMDS_CTRL_11 0x2abu 10 | 11 | #define TMDS_BALANCED_LOW 0x307u 12 | #define TMDS_BALANCED_HIGH 0x2f0u 13 | 14 | #define TMDS_BLACK_A 0x100u 15 | #define TMDS_BLACK_B 0x1ffu 16 | 17 | #define SYNC_V0_H0 (TMDS_CTRL_00 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20)) 18 | #define SYNC_V0_H1 (TMDS_CTRL_01 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20)) 19 | #define SYNC_V1_H0 (TMDS_CTRL_10 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20)) 20 | #define SYNC_V1_H1 (TMDS_CTRL_11 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20)) 21 | #define MISSING_PIXEL (TMDS_BALANCED_LOW | (TMDS_BALANCED_LOW << 10) | (TMDS_BALANCED_HIGH << 20)) 22 | #define BLACK_PIXEL (TMDS_BALANCED_LOW | (TMDS_BALANCED_LOW << 10) | (TMDS_BALANCED_LOW << 20)) 23 | #define BLACK_PIXEL_A (TMDS_BLACK_A | (TMDS_BLACK_A << 10) | (TMDS_BLACK_A << 20)) 24 | #define BLACK_PIXEL_B (TMDS_BLACK_B | (TMDS_BLACK_B << 10) | (TMDS_BLACK_B << 20)) 25 | 26 | #define HSTX_CMD_RAW (0x0u << 12) 27 | #define HSTX_CMD_RAW_REPEAT (0x1u << 12) 28 | #define HSTX_CMD_TMDS (0x2u << 12) 29 | #define HSTX_CMD_TMDS_REPEAT (0x3u << 12) 30 | #define HSTX_CMD_NOP (0xfu << 12) 31 | 32 | struct dvi_timing { 33 | bool h_sync_polarity; 34 | int h_front_porch; 35 | int h_sync_width; 36 | int h_back_porch; 37 | int h_active_pixels; 38 | 39 | bool v_sync_polarity; 40 | int v_front_porch; 41 | int v_sync_width; 42 | int v_back_porch; 43 | int v_active_lines; 44 | 45 | uint bit_clk_khz; 46 | }; 47 | 48 | extern const struct dvi_timing dvi_timing_640x480p_60hz; 49 | extern const struct dvi_timing dvi_timing_720x480p_60hz; 50 | extern const struct dvi_timing dvi_timing_720x576p_50hz; 51 | extern const struct dvi_timing dvi_timing_720x400p_70hz; 52 | extern const struct dvi_timing dvi_timing_800x450p_60hz; 53 | extern const struct dvi_timing dvi_timing_800x480p_60hz; 54 | extern const struct dvi_timing dvi_timing_800x600p_60hz; 55 | extern const struct dvi_timing dvi_timing_960x540p_60hz; 56 | extern const struct dvi_timing dvi_timing_960x540p_50hz; 57 | extern const struct dvi_timing dvi_timing_1024x768_rb_60hz; 58 | extern const struct dvi_timing dvi_timing_1280x720p_rb_50hz; 59 | extern const struct dvi_timing dvi_timing_1280x720p_rb_60hz; 60 | extern const struct dvi_timing dvi_timing_1920x1080p_rb2_30hz; 61 | extern const struct dvi_timing dvi_timing_1920x1080p_yolo_48hz; 62 | extern const struct dvi_timing dvi_timing_2560x1440p_yolo_24hz; 63 | -------------------------------------------------------------------------------- /drivers/dvhstx/font.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _FONT_H 8 | #define _FONT_H 9 | 10 | #include "pico/types.h" 11 | 12 | typedef struct { 13 | uint16_t bitmap_index; 14 | uint16_t adv_w; 15 | int8_t box_w, box_h, ofs_x, ofs_y; 16 | } __attribute__((packed)) lv_font_fmt_txt_glyph_dsc_t; 17 | 18 | typedef struct { 19 | uint16_t range_start, range_length, glyph_id_start, list_length; 20 | void *unicode_list, *glyph_id_ofs_list; 21 | enum { 22 | LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY 23 | } type; 24 | } lv_font_fmt_txt_cmap_t; 25 | 26 | typedef struct { 27 | const uint8_t *glyph_bitmap; 28 | const lv_font_fmt_txt_glyph_dsc_t *glyph_dsc; 29 | const lv_font_fmt_txt_cmap_t *cmaps; 30 | uint8_t cmap_num, bpp, kern_scale, kern_classes; 31 | void *kern_dsc; 32 | } lv_font_fmt_txt_dsc_t; 33 | 34 | typedef struct { 35 | lv_font_fmt_txt_dsc_t *dsc; 36 | uint8_t line_height, base_line; 37 | } lv_font_t; 38 | 39 | #define FONT_HEIGHT 23 40 | extern const lv_font_t intel_one_mono; 41 | 42 | #endif //SOFTWARE_FONT_H -------------------------------------------------------------------------------- /drivers/dvhstx/intel_one_mono_2bpp.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Size: 22 px 3 | * Bpp: 2 4 | * Opts: --bpp 2 --size 22 --no-compress --font IntelOneMono-Medium.ttf --range 32-127 --format lvgl -o intel_one_mono.c 5 | ******************************************************************************/ 6 | 7 | // The Intel One Mono font is licensed under the SIL Open Font License, Version 1.1: 8 | // https://github.com/intel/intel-one-mono/blob/main/OFL.txt 9 | 10 | // This file is created using https://lvgl.io/tools/fontconverter 11 | 12 | #include "font.h" 13 | 14 | #ifndef INTEL_ONE_MONO 15 | #define INTEL_ONE_MONO 1 16 | #endif 17 | 18 | #if INTEL_ONE_MONO 19 | 20 | /*----------------- 21 | * BITMAPS 22 | *----------------*/ 23 | 24 | /*Store the image of the glyphs*/ 25 | static const uint8_t glyph_bitmap[] = { 26 | /* U+0020 " " */ 27 | 28 | /* U+0021 "!" */ 29 | 0x7f, 0xc7, 0xfc, 0x3f, 0x83, 0xf8, 0x3f, 0x42, 30 | 0xf4, 0x2f, 0x1, 0xf0, 0x1f, 0x0, 0xe0, 0x0, 31 | 0x0, 0x0, 0x5, 0x3, 0xf8, 0x7f, 0xc3, 0xf4, 32 | 33 | /* U+0022 "\"" */ 34 | 0xbd, 0xb, 0xd7, 0xd0, 0xbd, 0x7d, 0xb, 0xc7, 35 | 0xd0, 0x7c, 0x7d, 0x7, 0xc3, 0xc0, 0x7c, 0x3c, 36 | 0x7, 0xc1, 0x40, 0x14, 37 | 38 | /* U+0023 "#" */ 39 | 0x0, 0x3c, 0x3c, 0x0, 0xe, 0xe, 0x0, 0x3, 40 | 0x43, 0x80, 0x16, 0xe6, 0xe5, 0x1f, 0xff, 0xff, 41 | 0x87, 0xf8, 0x2f, 0xe0, 0xd, 0xb, 0x0, 0x3, 42 | 0x43, 0x80, 0x16, 0xc0, 0xe5, 0xf, 0xfa, 0xbf, 43 | 0xc3, 0xff, 0xff, 0xf0, 0xf, 0xf, 0x0, 0x3, 44 | 0x83, 0x80, 0x0, 0xd0, 0xe0, 0x0, 0x74, 0x74, 45 | 0x0, 46 | 47 | /* U+0024 "$" */ 48 | 0x0, 0x3c, 0x0, 0x0, 0xf0, 0x0, 0x3, 0xc0, 49 | 0x0, 0xbf, 0xf8, 0xf, 0xff, 0xf4, 0xfe, 0x56, 50 | 0xc3, 0xd0, 0x0, 0xf, 0x40, 0x0, 0x3f, 0x40, 51 | 0x0, 0x7f, 0xe4, 0x0, 0x7f, 0xfd, 0x0, 0xb, 52 | 0xfd, 0x0, 0x1, 0xf8, 0x0, 0x2, 0xf0, 0x0, 53 | 0xb, 0xde, 0x40, 0x7e, 0xbf, 0xff, 0xf0, 0x6f, 54 | 0xfd, 0x0, 0x3, 0xc0, 0x0, 0xf, 0x0, 0x0, 55 | 0x3c, 0x0, 0x0, 0x50, 0x0, 56 | 57 | /* U+0025 "%" */ 58 | 0xb, 0xe0, 0xb, 0xc2, 0xeb, 0xc0, 0xf0, 0x38, 59 | 0x1c, 0x2d, 0x3, 0x81, 0xd3, 0x80, 0x38, 0x1d, 60 | 0x70, 0x3, 0xc2, 0xcd, 0x0, 0xf, 0xf4, 0x0, 61 | 0x0, 0x14, 0x1f, 0xc0, 0x0, 0x57, 0xef, 0x0, 62 | 0xc, 0xb0, 0x74, 0x2, 0xcf, 0x3, 0x80, 0x74, 63 | 0xf0, 0x38, 0xf, 0xb, 0x7, 0x42, 0xe0, 0x7e, 64 | 0xf0, 0x7c, 0x1, 0xfc, 0x0, 65 | 66 | /* U+0026 "&" */ 67 | 0x0, 0xbf, 0xd0, 0x3, 0xff, 0xd0, 0xb, 0xc0, 68 | 0x0, 0xb, 0x80, 0x0, 0xb, 0xc0, 0x0, 0x3, 69 | 0xd0, 0x10, 0x2, 0xf0, 0x3c, 0xb, 0xfc, 0x78, 70 | 0x3f, 0x7d, 0xf0, 0xbc, 0x2f, 0xe0, 0xf4, 0xf, 71 | 0xd0, 0xf4, 0x7, 0xe0, 0xbc, 0xf, 0xf0, 0x3f, 72 | 0xfd, 0xbc, 0x1f, 0xf4, 0x3e, 73 | 74 | /* U+0027 "'" */ 75 | 0xfd, 0xfd, 0xfd, 0xbc, 0xbc, 0xbc, 0xbc, 0x14, 76 | 77 | /* U+0028 "(" */ 78 | 0x0, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x1f, 0xd0, 79 | 0x7, 0xf0, 0x2, 0xf8, 0x0, 0x7e, 0x0, 0xf, 80 | 0x80, 0x2, 0xf0, 0x0, 0x3e, 0x0, 0x7, 0xc0, 81 | 0x0, 0xbc, 0x0, 0xb, 0xc0, 0x0, 0xbc, 0x0, 82 | 0x7, 0xc0, 0x0, 0x3d, 0x0, 0x2, 0xf0, 0x0, 83 | 0xf, 0x80, 0x0, 0x7d, 0x0, 0x2, 0xf8, 0x0, 84 | 0xb, 0xe0, 0x0, 0x1f, 0xd0, 0x0, 0x2c, 0x0, 85 | 0x0, 0x0, 86 | 87 | /* U+0029 ")" */ 88 | 0x0, 0x0, 0x0, 0xb4, 0x0, 0x3, 0xf8, 0x0, 89 | 0x1, 0xfc, 0x0, 0x0, 0xfc, 0x0, 0x0, 0xfc, 90 | 0x0, 0x0, 0xf8, 0x0, 0x1, 0xf0, 0x0, 0x3, 91 | 0xe0, 0x0, 0xb, 0xc0, 0x0, 0x1f, 0x0, 0x0, 92 | 0x7c, 0x0, 0x1, 0xf0, 0x0, 0xb, 0xc0, 0x0, 93 | 0x3e, 0x0, 0x1, 0xf0, 0x0, 0xf, 0x80, 0x0, 94 | 0xfc, 0x0, 0xf, 0xc0, 0x1, 0xfc, 0x0, 0x3f, 95 | 0x80, 0x0, 0xb4, 0x0, 0x0, 0x0, 0x0, 0x0, 96 | 97 | /* U+002A "*" */ 98 | 0x0, 0x7c, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x38, 99 | 0x0, 0x38, 0x34, 0x78, 0xbf, 0x66, 0xfc, 0x16, 100 | 0xbe, 0x50, 0x0, 0xbc, 0x0, 0x3, 0xcb, 0x40, 101 | 0xf, 0x83, 0xe0, 0xf, 0x1, 0xe0, 0x1, 0x0, 102 | 0x40, 103 | 104 | /* U+002B "+" */ 105 | 0x0, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x7c, 106 | 0x0, 0x0, 0x7c, 0x0, 0x0, 0x7c, 0x0, 0xbf, 107 | 0xff, 0xfc, 0xbf, 0xff, 0xfc, 0x0, 0x7c, 0x0, 108 | 0x0, 0x7c, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x7c, 109 | 0x0, 0x0, 0x7c, 0x0, 110 | 111 | /* U+002C "," */ 112 | 0x3, 0xfc, 0x3, 0xf8, 0x3, 0xf4, 0x7, 0xf0, 113 | 0xf, 0xd0, 0x2f, 0x40, 0x3d, 0x0, 114 | 115 | /* U+002D "-" */ 116 | 0x15, 0x55, 0x5f, 0xff, 0xf7, 0xff, 0xfc, 117 | 118 | /* U+002E "." */ 119 | 0x0, 0x3, 0xf8, 0x7f, 0xc7, 0xfc, 0x2f, 0x40, 120 | 121 | /* U+002F "/" */ 122 | 0x0, 0x0, 0x6, 0x0, 0x0, 0x3, 0xc0, 0x0, 123 | 0x2, 0xe0, 0x0, 0x0, 0xf0, 0x0, 0x0, 0xb8, 124 | 0x0, 0x0, 0x3c, 0x0, 0x0, 0x2e, 0x0, 0x0, 125 | 0xf, 0x40, 0x0, 0xb, 0xc0, 0x0, 0x3, 0xd0, 126 | 0x0, 0x2, 0xf0, 0x0, 0x0, 0xf4, 0x0, 0x0, 127 | 0xbc, 0x0, 0x0, 0x3d, 0x0, 0x0, 0x1f, 0x0, 128 | 0x0, 0xf, 0x40, 0x0, 0x7, 0xc0, 0x0, 0x3, 129 | 0xd0, 0x0, 0x1, 0xf0, 0x0, 0x0, 0xf8, 0x0, 130 | 0x0, 131 | 132 | /* U+0030 "0" */ 133 | 0x1, 0xfe, 0x0, 0xb, 0xff, 0xd0, 0x1f, 0x87, 134 | 0xf0, 0x3e, 0x0, 0xf4, 0x3c, 0x0, 0xfc, 0x7c, 135 | 0x3, 0xfc, 0x7c, 0x1f, 0xbc, 0xbc, 0x7c, 0x7c, 136 | 0xbe, 0xe0, 0x7c, 0x7f, 0x80, 0x7c, 0x3d, 0x0, 137 | 0xb8, 0x3e, 0x0, 0xf4, 0x2f, 0x87, 0xf0, 0xf, 138 | 0xff, 0xd0, 0x2, 0xfe, 0x0, 139 | 140 | /* U+0031 "1" */ 141 | 0x0, 0x74, 0x0, 0x7f, 0x40, 0x7f, 0xf4, 0xb, 142 | 0xef, 0x40, 0xa0, 0xf4, 0x0, 0xf, 0x40, 0x0, 143 | 0xf4, 0x0, 0xf, 0x40, 0x0, 0xf4, 0x0, 0xf, 144 | 0x40, 0x0, 0xf4, 0x0, 0xf, 0x40, 0x0, 0xf4, 145 | 0xb, 0xff, 0xfe, 0xbf, 0xff, 0xe0, 146 | 147 | /* U+0032 "2" */ 148 | 0x1, 0xbf, 0x40, 0xb, 0xff, 0xf0, 0x3f, 0x96, 149 | 0xf4, 0x7d, 0x0, 0xf8, 0x10, 0x0, 0xf8, 0x0, 150 | 0x0, 0xf4, 0x0, 0x3, 0xf0, 0x0, 0xb, 0xc0, 151 | 0x0, 0x2f, 0x40, 0x0, 0xbd, 0x0, 0x2, 0xf4, 152 | 0x0, 0xb, 0xd0, 0x0, 0x2f, 0x40, 0x0, 0x7f, 153 | 0xff, 0xfc, 0x7f, 0xff, 0xfc, 154 | 155 | /* U+0033 "3" */ 156 | 0x7f, 0xff, 0xf5, 0xff, 0xff, 0xc0, 0x0, 0x7e, 157 | 0x0, 0x3, 0xe0, 0x0, 0x3e, 0x0, 0x3, 0xf0, 158 | 0x0, 0x3f, 0xf8, 0x0, 0xff, 0xfc, 0x0, 0x2, 159 | 0xf8, 0x0, 0x2, 0xf0, 0x0, 0xb, 0xc0, 0x0, 160 | 0x7e, 0x5, 0x6f, 0xf4, 0xff, 0xff, 0x43, 0xff, 161 | 0x90, 0x0, 162 | 163 | /* U+0034 "4" */ 164 | 0x0, 0x1f, 0xc0, 0x0, 0x3f, 0xc0, 0x0, 0xb7, 165 | 0xc0, 0x1, 0xf3, 0xc0, 0x3, 0xc3, 0xc0, 0xb, 166 | 0x83, 0xc0, 0xf, 0x3, 0xc0, 0x3c, 0x3, 0xc0, 167 | 0x78, 0x3, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 168 | 0xff, 0x0, 0x3, 0xc0, 0x0, 0x3, 0xc0, 0x0, 169 | 0x3, 0xc0, 0x0, 0x3, 0xc0, 170 | 171 | /* U+0035 "5" */ 172 | 0x1f, 0xff, 0xf0, 0x7f, 0xff, 0xc2, 0xe0, 0x0, 173 | 0xb, 0x80, 0x0, 0x2e, 0x0, 0x0, 0xba, 0xfe, 174 | 0x3, 0xff, 0xff, 0xb, 0x90, 0xbe, 0x0, 0x0, 175 | 0xfc, 0x0, 0x2, 0xf0, 0x0, 0xf, 0x80, 0x1, 176 | 0xfd, 0x1a, 0xff, 0xe0, 0xff, 0xf9, 0x3, 0xa5, 177 | 0x0, 0x0, 178 | 179 | /* U+0036 "6" */ 180 | 0x0, 0x1f, 0xc0, 0x0, 0x7f, 0x0, 0x1, 0xfc, 181 | 0x0, 0x3, 0xf0, 0x0, 0xf, 0xc0, 0x0, 0x1f, 182 | 0xff, 0x80, 0x3f, 0xff, 0xf0, 0x3d, 0x1, 0xf8, 183 | 0x7c, 0x0, 0xfc, 0x7c, 0x0, 0xbc, 0x7c, 0x0, 184 | 0xbc, 0x3e, 0x0, 0xfc, 0x3f, 0x43, 0xf4, 0xf, 185 | 0xff, 0xe0, 0x2, 0xfe, 0x40, 186 | 187 | /* U+0037 "7" */ 188 | 0xbf, 0xff, 0xfe, 0xff, 0xff, 0xf0, 0x0, 0xf, 189 | 0x80, 0x0, 0x7c, 0x0, 0x3, 0xe0, 0x0, 0x1f, 190 | 0x0, 0x0, 0xfc, 0x0, 0x7, 0xd0, 0x0, 0x3f, 191 | 0x0, 0x1, 0xf4, 0x0, 0xf, 0xc0, 0x0, 0x7d, 192 | 0x0, 0x3, 0xf0, 0x0, 0x1f, 0x80, 0x0, 0xfc, 193 | 0x0, 0x0, 194 | 195 | /* U+0038 "8" */ 196 | 0x2, 0xff, 0x40, 0x3f, 0xff, 0x83, 0xf4, 0x2f, 197 | 0x4f, 0x40, 0x3e, 0x3d, 0x0, 0xf4, 0x7c, 0x7, 198 | 0xc0, 0x7f, 0xf8, 0x1, 0xff, 0xe0, 0x2f, 0x42, 199 | 0xf0, 0xf0, 0x2, 0xe7, 0xc0, 0x7, 0xdf, 0x0, 200 | 0x2f, 0x3f, 0x2, 0xf8, 0x7f, 0xff, 0xc0, 0x2f, 201 | 0xf4, 0x0, 202 | 203 | /* U+0039 "9" */ 204 | 0x2, 0xff, 0x40, 0xf, 0xff, 0xe0, 0x3f, 0x43, 205 | 0xf4, 0x3d, 0x0, 0xf8, 0x7c, 0x0, 0xbc, 0x7c, 206 | 0x0, 0xbc, 0x7d, 0x0, 0xbc, 0x3f, 0x0, 0xf8, 207 | 0x1f, 0xff, 0xf4, 0x6, 0xff, 0xf0, 0x0, 0x7, 208 | 0xd0, 0x0, 0x1f, 0x80, 0x0, 0x7e, 0x0, 0x1, 209 | 0xf8, 0x0, 0xb, 0xe0, 0x0, 210 | 211 | /* U+003A ":" */ 212 | 0x2f, 0x47, 0xfc, 0x7f, 0xc2, 0xf4, 0x0, 0x0, 213 | 0x0, 0x0, 0x2, 0xf4, 0x7f, 0xc7, 0xfc, 0x2f, 214 | 0x40, 215 | 216 | /* U+003B ";" */ 217 | 0x1, 0xf8, 0x3, 0xfc, 0x3, 0xfc, 0x1, 0xf8, 218 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x54, 219 | 0x3, 0xf8, 0x3, 0xf4, 0x7, 0xf0, 0xf, 0xe0, 220 | 0x1f, 0x80, 0x3e, 0x0, 0x14, 0x0, 221 | 222 | /* U+003C "<" */ 223 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x0, 0x2f, 224 | 0xc0, 0xb, 0xfd, 0x7, 0xff, 0x41, 0xff, 0x80, 225 | 0xb, 0xd0, 0x0, 0x2f, 0xe0, 0x0, 0x1f, 0xf9, 226 | 0x0, 0x2, 0xff, 0x40, 0x0, 0xbf, 0xc0, 0x0, 227 | 0x1f, 0x0, 0x0, 0x4, 228 | 229 | /* U+003D "=" */ 230 | 0x3f, 0xff, 0xf8, 0xff, 0xff, 0xe0, 0x0, 0x0, 231 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 232 | 0xe3, 0xff, 0xff, 0x80, 233 | 234 | /* U+003E ">" */ 235 | 0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x7f, 0x40, 236 | 0x0, 0x2f, 0xf4, 0x0, 0x2, 0xff, 0x80, 0x0, 237 | 0x1f, 0xf8, 0x0, 0x0, 0xbc, 0x0, 0x6, 0xfc, 238 | 0x0, 0x7f, 0xe0, 0xb, 0xfd, 0x0, 0x7f, 0xd0, 239 | 0x0, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 240 | 241 | /* U+003F "?" */ 242 | 0xa, 0xff, 0x81, 0xff, 0xff, 0xd7, 0x94, 0x1f, 243 | 0xc0, 0x0, 0x2f, 0x0, 0x0, 0xfc, 0x0, 0xf, 244 | 0xd0, 0x7, 0xfd, 0x0, 0x7f, 0x80, 0x1, 0xf0, 245 | 0x0, 0x7, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 246 | 0x0, 0x0, 0x50, 0x0, 0x1f, 0xd0, 0x0, 0xbf, 247 | 0x80, 0x0, 0xfc, 0x0, 248 | 249 | /* U+0040 "@" */ 250 | 0x1, 0xbf, 0x80, 0xb, 0x41, 0xb0, 0x1c, 0x0, 251 | 0x34, 0x34, 0x0, 0x1c, 0x30, 0x0, 0xc, 0x70, 252 | 0x2f, 0xfc, 0xa0, 0xbe, 0xad, 0xa0, 0xf0, 0xd, 253 | 0xa1, 0xd0, 0xd, 0xa1, 0xd0, 0xd, 0xa1, 0xe0, 254 | 0xc, 0xa0, 0xf0, 0x3c, 0x70, 0x7f, 0xf4, 0x70, 255 | 0xa, 0x40, 0x34, 0x0, 0x0, 0x2c, 0x0, 0x0, 256 | 0xf, 0x40, 0x0, 0x2, 0xff, 0xe0, 0x0, 0x0, 257 | 0x0, 258 | 259 | /* U+0041 "A" */ 260 | 0x0, 0x3f, 0x40, 0x0, 0xf, 0xe0, 0x0, 0xb, 261 | 0xfc, 0x0, 0x3, 0xdf, 0x40, 0x0, 0xf2, 0xe0, 262 | 0x0, 0x7c, 0x7c, 0x0, 0x2e, 0xf, 0x0, 0xf, 263 | 0x3, 0xe0, 0x7, 0xc0, 0xbc, 0x2, 0xff, 0xff, 264 | 0x0, 0xff, 0xff, 0xd0, 0x3c, 0x0, 0xf8, 0x1f, 265 | 0x0, 0x2f, 0xf, 0x80, 0x7, 0xd3, 0xd0, 0x0, 266 | 0xf8, 267 | 268 | /* U+0042 "B" */ 269 | 0x3f, 0xff, 0x80, 0x3f, 0xff, 0xf0, 0x3d, 0x1, 270 | 0xf8, 0x3d, 0x0, 0xbc, 0x3d, 0x0, 0xbc, 0x3d, 271 | 0x0, 0xf4, 0x3d, 0x2, 0xe0, 0x3f, 0xff, 0x40, 272 | 0x3f, 0xff, 0xe0, 0x3d, 0x1, 0xfc, 0x3d, 0x0, 273 | 0x3d, 0x3d, 0x0, 0x3d, 0x3d, 0x0, 0xbd, 0x3f, 274 | 0xff, 0xfc, 0x3f, 0xff, 0xe0, 275 | 276 | /* U+0043 "C" */ 277 | 0x0, 0xbf, 0x90, 0x7, 0xff, 0xf8, 0x1f, 0xd0, 278 | 0xbc, 0x3f, 0x0, 0x4, 0x7d, 0x0, 0x0, 0xbc, 279 | 0x0, 0x0, 0xfc, 0x0, 0x0, 0xf8, 0x0, 0x0, 280 | 0xfc, 0x0, 0x0, 0xbc, 0x0, 0x0, 0x7c, 0x0, 281 | 0x0, 0x3f, 0x0, 0x4, 0x2f, 0xd1, 0xbd, 0xb, 282 | 0xff, 0xf8, 0x1, 0xff, 0x80, 283 | 284 | /* U+0044 "D" */ 285 | 0x3f, 0xf8, 0x0, 0x3f, 0xff, 0x80, 0x3d, 0x1b, 286 | 0xf0, 0x3d, 0x1, 0xf8, 0x3d, 0x0, 0xbc, 0x3d, 287 | 0x0, 0x7c, 0x3d, 0x0, 0x3d, 0x3d, 0x0, 0x3d, 288 | 0x3d, 0x0, 0x3d, 0x3d, 0x0, 0x7c, 0x3d, 0x0, 289 | 0xfc, 0x3d, 0x2, 0xf4, 0x3d, 0x5f, 0xe0, 0x3f, 290 | 0xff, 0x80, 0x3f, 0xe4, 0x0, 291 | 292 | /* U+0045 "E" */ 293 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0, 0xf, 294 | 0x80, 0x0, 0xf8, 0x0, 0xf, 0x80, 0x0, 0xf8, 295 | 0x0, 0xf, 0xff, 0xfc, 0xff, 0xff, 0xcf, 0x80, 296 | 0x0, 0xf8, 0x0, 0xf, 0x80, 0x0, 0xf8, 0x0, 297 | 0xf, 0xff, 0xff, 0xff, 0xff, 0xf0, 298 | 299 | /* U+0046 "F" */ 300 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0, 0xf, 301 | 0x80, 0x0, 0xf8, 0x0, 0xf, 0x80, 0x0, 0xf8, 302 | 0x0, 0xf, 0xff, 0xfc, 0xff, 0xff, 0xcf, 0x80, 303 | 0x0, 0xf8, 0x0, 0xf, 0x80, 0x0, 0xf8, 0x0, 304 | 0xf, 0x80, 0x0, 0xf8, 0x0, 0x0, 305 | 306 | /* U+0047 "G" */ 307 | 0x0, 0x6f, 0xf0, 0x7, 0xff, 0xf0, 0x1f, 0xe4, 308 | 0x0, 0x3f, 0x0, 0x0, 0x7d, 0x0, 0x0, 0xbc, 309 | 0x0, 0x0, 0xfc, 0xf, 0xfc, 0xf8, 0xf, 0xfc, 310 | 0xfc, 0x0, 0x3c, 0xbc, 0x0, 0x3c, 0x7c, 0x0, 311 | 0x3c, 0x3f, 0x0, 0x3c, 0x2f, 0xd0, 0x7c, 0xb, 312 | 0xff, 0xfc, 0x1, 0xbf, 0xf8, 313 | 314 | /* U+0048 "H" */ 315 | 0x3d, 0x0, 0xf8, 0xf4, 0x3, 0xe3, 0xd0, 0xf, 316 | 0x8f, 0x40, 0x3e, 0x3d, 0x0, 0xf8, 0xf4, 0x3, 317 | 0xe3, 0xd0, 0xf, 0x8f, 0xff, 0xfe, 0x3f, 0xff, 318 | 0xf8, 0xf4, 0x3, 0xe3, 0xd0, 0xf, 0x8f, 0x40, 319 | 0x3e, 0x3d, 0x0, 0xf8, 0xf4, 0x3, 0xe3, 0xd0, 320 | 0xf, 0x80, 321 | 322 | /* U+0049 "I" */ 323 | 0x7f, 0xff, 0xca, 0xff, 0x90, 0x1f, 0x0, 0x7, 324 | 0xc0, 0x1, 0xf0, 0x0, 0x7c, 0x0, 0x1f, 0x0, 325 | 0x7, 0xc0, 0x1, 0xf0, 0x0, 0x7c, 0x0, 0x1f, 326 | 0x0, 0x7, 0xc0, 0x1, 0xf0, 0xa, 0xff, 0x97, 327 | 0xff, 0xfc, 328 | 329 | /* U+004A "J" */ 330 | 0x0, 0x0, 0x3e, 0x0, 0x0, 0x3e, 0x0, 0x0, 331 | 0x3e, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x3e, 0x0, 332 | 0x0, 0x3e, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x3e, 333 | 0x0, 0x0, 0x3e, 0x0, 0x0, 0x3e, 0xc, 0x0, 334 | 0x3e, 0x3f, 0x40, 0x3e, 0x1f, 0xe5, 0xfc, 0x3, 335 | 0xff, 0xf8, 0x0, 0x7f, 0xd0, 336 | 337 | /* U+004B "K" */ 338 | 0x3d, 0x0, 0x7e, 0xf, 0x40, 0x3f, 0x3, 0xd0, 339 | 0x3f, 0x0, 0xf4, 0x2f, 0x0, 0x3d, 0x1f, 0x40, 340 | 0xf, 0x5f, 0x80, 0x3, 0xef, 0xc0, 0x0, 0xff, 341 | 0xd0, 0x0, 0x3e, 0xfc, 0x0, 0xf, 0x4f, 0xc0, 342 | 0x3, 0xd1, 0xf8, 0x0, 0xf4, 0x2f, 0x40, 0x3d, 343 | 0x3, 0xf0, 0xf, 0x40, 0x3f, 0x3, 0xd0, 0x7, 344 | 0xe0, 345 | 346 | /* U+004C "L" */ 347 | 0xf8, 0x0, 0x3, 0xe0, 0x0, 0xf, 0x80, 0x0, 348 | 0x3e, 0x0, 0x0, 0xf8, 0x0, 0x3, 0xe0, 0x0, 349 | 0xf, 0x80, 0x0, 0x3e, 0x0, 0x0, 0xf8, 0x0, 350 | 0x3, 0xe0, 0x0, 0xf, 0x80, 0x0, 0x3e, 0x0, 351 | 0x0, 0xf8, 0x0, 0x3, 0xff, 0xff, 0xcf, 0xff, 352 | 0xff, 0x0, 353 | 354 | /* U+004D "M" */ 355 | 0x3e, 0x0, 0xf8, 0x3f, 0x1, 0xf8, 0x3f, 0x42, 356 | 0xf8, 0x7f, 0x83, 0xfc, 0x7e, 0xc7, 0xbc, 0x7d, 357 | 0xdf, 0x7c, 0x7c, 0xfd, 0x7c, 0x7c, 0xbc, 0x7c, 358 | 0x78, 0x7c, 0x7c, 0x78, 0x34, 0x3c, 0xb8, 0x20, 359 | 0x3c, 0xb8, 0x0, 0x3c, 0xb8, 0x0, 0x3c, 0xb8, 360 | 0x0, 0x3c, 0xb8, 0x0, 0x3c, 361 | 362 | /* U+004E "N" */ 363 | 0x3f, 0x0, 0xb8, 0xfc, 0x2, 0xe3, 0xf8, 0xb, 364 | 0x8f, 0xf0, 0x2e, 0x3f, 0xe0, 0xb8, 0xf7, 0xc2, 365 | 0xe3, 0xcf, 0x8b, 0x8f, 0x1f, 0x2e, 0x3c, 0x3d, 366 | 0xb8, 0xf0, 0x7e, 0xe3, 0xc0, 0xff, 0x8f, 0x2, 367 | 0xfe, 0x3c, 0x3, 0xf8, 0xf0, 0xb, 0xe3, 0xc0, 368 | 0xf, 0x80, 369 | 370 | /* U+004F "O" */ 371 | 0x2, 0xfe, 0x40, 0xf, 0xff, 0xd0, 0x2f, 0x47, 372 | 0xf0, 0x3d, 0x0, 0xf8, 0x7c, 0x0, 0xbc, 0xbc, 373 | 0x0, 0x7c, 0xf8, 0x0, 0x3d, 0xf8, 0x0, 0x3d, 374 | 0xf8, 0x0, 0x3d, 0xbc, 0x0, 0x7c, 0xbc, 0x0, 375 | 0xbc, 0x3d, 0x0, 0xf8, 0x2f, 0x43, 0xf0, 0xf, 376 | 0xff, 0xd0, 0x2, 0xfe, 0x40, 377 | 378 | /* U+0050 "P" */ 379 | 0x3f, 0xff, 0x80, 0x3f, 0xff, 0xf4, 0x3d, 0x1, 380 | 0xfc, 0x3d, 0x0, 0x7d, 0x3d, 0x0, 0x3e, 0x3d, 381 | 0x0, 0x3e, 0x3d, 0x0, 0x7d, 0x3d, 0x1, 0xfc, 382 | 0x3f, 0xff, 0xf0, 0x3f, 0xff, 0x80, 0x3d, 0x0, 383 | 0x0, 0x3d, 0x0, 0x0, 0x3d, 0x0, 0x0, 0x3d, 384 | 0x0, 0x0, 0x3d, 0x0, 0x0, 385 | 386 | /* U+0051 "Q" */ 387 | 0x2, 0xfe, 0x40, 0x3, 0xff, 0xf4, 0x2, 0xf4, 388 | 0x7f, 0x0, 0xf4, 0x3, 0xe0, 0x7c, 0x0, 0xbc, 389 | 0x2f, 0x0, 0x1f, 0xf, 0x80, 0x3, 0xd3, 0xe0, 390 | 0x0, 0xf4, 0xf8, 0x0, 0x3d, 0x2f, 0x0, 0x1f, 391 | 0xb, 0xc0, 0x7, 0xc0, 0xf4, 0x3, 0xe0, 0x2f, 392 | 0x42, 0xf0, 0x3, 0xff, 0xf8, 0x0, 0x2f, 0xef, 393 | 0x40, 0x0, 0x1, 0xf0, 0x0, 0x0, 0x3f, 0x0, 394 | 0x0, 0x7, 0xc0, 0x0, 0x0, 0x40, 395 | 396 | /* U+0052 "R" */ 397 | 0x3f, 0xff, 0x80, 0x3f, 0xff, 0xf0, 0x3d, 0x1, 398 | 0xf8, 0x3d, 0x0, 0xbc, 0x3d, 0x0, 0xbc, 0x3d, 399 | 0x0, 0xbc, 0x3d, 0x2, 0xf4, 0x3e, 0xbf, 0xd0, 400 | 0x3f, 0xf9, 0x0, 0x3e, 0xbd, 0x0, 0x3d, 0x1f, 401 | 0x40, 0x3d, 0xb, 0xd0, 0x3d, 0x3, 0xf0, 0x3d, 402 | 0x0, 0xfc, 0x3d, 0x0, 0x7e, 403 | 404 | /* U+0053 "S" */ 405 | 0x2, 0xff, 0xe0, 0x3f, 0xff, 0xd2, 0xf4, 0x16, 406 | 0xf, 0x80, 0x0, 0x3e, 0x0, 0x0, 0xfd, 0x0, 407 | 0x0, 0xff, 0x90, 0x0, 0xbf, 0xf4, 0x0, 0x1f, 408 | 0xf4, 0x0, 0x3, 0xf0, 0x0, 0xb, 0xc0, 0x0, 409 | 0x2f, 0x39, 0x2, 0xf9, 0xff, 0xff, 0xc1, 0xbf, 410 | 0xf4, 0x0, 411 | 412 | /* U+0054 "T" */ 413 | 0x3f, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xe0, 0x2, 414 | 0xf0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x1f, 0x0, 415 | 0x0, 0x7, 0xc0, 0x0, 0x1, 0xf0, 0x0, 0x0, 416 | 0x7c, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x7, 0xc0, 417 | 0x0, 0x1, 0xf0, 0x0, 0x0, 0x7c, 0x0, 0x0, 418 | 0x1f, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x1, 0xf0, 419 | 0x0, 420 | 421 | /* U+0055 "U" */ 422 | 0x7c, 0x0, 0xbd, 0xf0, 0x2, 0xf7, 0xc0, 0xb, 423 | 0xdf, 0x0, 0x2f, 0x7c, 0x0, 0xbd, 0xf0, 0x2, 424 | 0xf7, 0xc0, 0xb, 0xdf, 0x0, 0x2f, 0x7c, 0x0, 425 | 0xbd, 0xf0, 0x2, 0xf7, 0xc0, 0xb, 0xcf, 0x80, 426 | 0x3e, 0x3f, 0x97, 0xf4, 0x3f, 0xff, 0x80, 0x2f, 427 | 0xf4, 0x0, 428 | 429 | /* U+0056 "V" */ 430 | 0x3d, 0x0, 0xf, 0x8f, 0xc0, 0x3, 0xd1, 0xf0, 431 | 0x1, 0xf0, 0x3d, 0x0, 0xb8, 0xf, 0x80, 0x3d, 432 | 0x2, 0xf0, 0x1f, 0x0, 0x7c, 0xb, 0xc0, 0xf, 433 | 0x43, 0xd0, 0x2, 0xf0, 0xf0, 0x0, 0x7c, 0x7c, 434 | 0x0, 0xf, 0x6e, 0x0, 0x3, 0xef, 0x40, 0x0, 435 | 0x7f, 0xc0, 0x0, 0xf, 0xe0, 0x0, 0x3, 0xf4, 436 | 0x0, 437 | 438 | /* U+0057 "W" */ 439 | 0x3d, 0x0, 0xf, 0x8f, 0x40, 0x3, 0xd3, 0xe0, 440 | 0x0, 0xf4, 0xb8, 0x20, 0x3c, 0x2e, 0xd, 0xf, 441 | 0x7, 0xc3, 0x87, 0xc1, 0xf1, 0xf1, 0xf0, 0x3c, 442 | 0xbc, 0xb8, 0xf, 0x3b, 0x6e, 0x3, 0xdc, 0xeb, 443 | 0x40, 0xff, 0x2f, 0xd0, 0x2f, 0xc7, 0xf0, 0xb, 444 | 0xe0, 0xfc, 0x1, 0xf4, 0x3f, 0x0, 0x7c, 0xb, 445 | 0xc0, 446 | 447 | /* U+0058 "X" */ 448 | 0x2f, 0x0, 0x2f, 0x3, 0xe0, 0xf, 0x40, 0x7c, 449 | 0xb, 0xc0, 0xf, 0xc7, 0xd0, 0x1, 0xf7, 0xe0, 450 | 0x0, 0x2f, 0xf0, 0x0, 0x3, 0xf8, 0x0, 0x0, 451 | 0xfd, 0x0, 0x0, 0x7f, 0xc0, 0x0, 0x3e, 0xf4, 452 | 0x0, 0x2f, 0x2f, 0x0, 0xf, 0x43, 0xe0, 0xb, 453 | 0xc0, 0x7c, 0x7, 0xd0, 0xf, 0xc3, 0xe0, 0x1, 454 | 0xf4, 455 | 456 | /* U+0059 "Y" */ 457 | 0x3e, 0x0, 0xf, 0x8b, 0xd0, 0xb, 0xc0, 0xfc, 458 | 0x3, 0xd0, 0x1f, 0x42, 0xf0, 0x3, 0xf1, 0xf4, 459 | 0x0, 0x7d, 0xf8, 0x0, 0xb, 0xfc, 0x0, 0x0, 460 | 0xfe, 0x0, 0x0, 0x2f, 0x0, 0x0, 0x7, 0xc0, 461 | 0x0, 0x1, 0xf0, 0x0, 0x0, 0x7c, 0x0, 0x0, 462 | 0x1f, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x1, 0xf0, 463 | 0x0, 464 | 465 | /* U+005A "Z" */ 466 | 0x7f, 0xff, 0xfc, 0x7f, 0xff, 0xfc, 0x15, 0x56, 467 | 0xf4, 0x0, 0x3, 0xe0, 0x0, 0xf, 0xc0, 0x0, 468 | 0x2f, 0x40, 0x0, 0x3e, 0x0, 0x0, 0xfc, 0x0, 469 | 0x2, 0xf4, 0x0, 0x3, 0xe0, 0x0, 0xf, 0xc0, 470 | 0x0, 0x1f, 0x40, 0x0, 0x3f, 0x0, 0x0, 0xbf, 471 | 0xff, 0xfc, 0xbf, 0xff, 0xfc, 472 | 473 | /* U+005B "[" */ 474 | 0x0, 0x0, 0xf, 0xff, 0xe3, 0xff, 0xf8, 0xf4, 475 | 0x0, 0x3d, 0x0, 0xf, 0x40, 0x3, 0xd0, 0x0, 476 | 0xf4, 0x0, 0x3d, 0x0, 0xf, 0x40, 0x3, 0xd0, 477 | 0x0, 0xf4, 0x0, 0x3d, 0x0, 0xf, 0x40, 0x3, 478 | 0xd0, 0x0, 0xf4, 0x0, 0x3d, 0x0, 0xf, 0x40, 479 | 0x3, 0xd0, 0x0, 0xf4, 0x0, 0x3f, 0xff, 0x8f, 480 | 0xff, 0xe0, 481 | 482 | /* U+005C "\\" */ 483 | 0x18, 0x0, 0x0, 0xb, 0xc0, 0x0, 0x0, 0xf0, 484 | 0x0, 0x0, 0x2e, 0x0, 0x0, 0x3, 0xc0, 0x0, 485 | 0x0, 0xb8, 0x0, 0x0, 0xf, 0x0, 0x0, 0x3, 486 | 0xe0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0xf, 0x80, 487 | 0x0, 0x1, 0xf0, 0x0, 0x0, 0x3e, 0x0, 0x0, 488 | 0x7, 0xc0, 0x0, 0x0, 0xf4, 0x0, 0x0, 0x1f, 489 | 0x0, 0x0, 0x3, 0xd0, 0x0, 0x0, 0x7c, 0x0, 490 | 0x0, 0xf, 0x40, 0x0, 0x2, 0xf0, 0x0, 0x0, 491 | 0x3d, 492 | 493 | /* U+005D "]" */ 494 | 0x0, 0x0, 0xf, 0xff, 0xe3, 0xff, 0xf8, 0x0, 495 | 0x3e, 0x0, 0xf, 0x80, 0x3, 0xe0, 0x0, 0xf8, 496 | 0x0, 0x3e, 0x0, 0xf, 0x80, 0x3, 0xe0, 0x0, 497 | 0xf8, 0x0, 0x3e, 0x0, 0xf, 0x80, 0x3, 0xe0, 498 | 0x0, 0xf8, 0x0, 0x3e, 0x0, 0xf, 0x80, 0x3, 499 | 0xe0, 0x0, 0xf8, 0x0, 0x3e, 0x3f, 0xff, 0x8f, 500 | 0xff, 0xe0, 501 | 502 | /* U+005E "^" */ 503 | 0x0, 0x1a, 0x0, 0x0, 0xf, 0xe0, 0x0, 0xf, 504 | 0xbd, 0x0, 0x7, 0xcb, 0xc0, 0x3, 0xe0, 0xf4, 505 | 0x1, 0xf0, 0x2f, 0x0, 0xf8, 0x3, 0xd0, 0x7c, 506 | 0x0, 0xbc, 0x3e, 0x0, 0xf, 0x40, 507 | 508 | /* U+005F "_" */ 509 | 0x15, 0x55, 0x55, 0xff, 0xff, 0xf7, 0xff, 0xff, 510 | 0xc0, 511 | 512 | /* U+0060 "`" */ 513 | 0x1a, 0x40, 0x1f, 0xc0, 0xf, 0xd0, 0x3, 0xf0, 514 | 0x2, 0xf4, 0x0, 0xf8, 0x0, 0x7c, 0x0, 0x3e, 515 | 516 | /* U+0061 "a" */ 517 | 0x1f, 0xff, 0x40, 0x2f, 0xff, 0xe0, 0x4, 0x2, 518 | 0xf0, 0x0, 0x1, 0xf0, 0x1, 0x6b, 0xf0, 0x2f, 519 | 0xff, 0xf0, 0x7e, 0x41, 0xf0, 0xb8, 0x1, 0xf0, 520 | 0xbc, 0xb, 0xf0, 0x7f, 0xff, 0xfe, 0x1f, 0xe4, 521 | 0xbe, 522 | 523 | /* U+0062 "b" */ 524 | 0x3d, 0x0, 0x0, 0x3d, 0x0, 0x0, 0x3d, 0x0, 525 | 0x0, 0x3d, 0x0, 0x0, 0x3d, 0x0, 0x0, 0x3d, 526 | 0x7f, 0x80, 0x3f, 0xff, 0xf0, 0x3f, 0x82, 0xf8, 527 | 0x3d, 0x0, 0xbc, 0x3d, 0x0, 0x7d, 0x3d, 0x0, 528 | 0x3d, 0x3d, 0x0, 0x7c, 0x3d, 0x0, 0xfc, 0x3e, 529 | 0x47, 0xf4, 0x3f, 0xff, 0xe0, 0x36, 0xfe, 0x40, 530 | 531 | /* U+0063 "c" */ 532 | 0x1, 0xff, 0x80, 0xb, 0xff, 0xf8, 0x2f, 0x91, 533 | 0xb8, 0x3e, 0x0, 0x0, 0xbc, 0x0, 0x0, 0xbc, 534 | 0x0, 0x0, 0xbc, 0x0, 0x0, 0x7e, 0x0, 0x0, 535 | 0x3f, 0x81, 0xb8, 0xf, 0xff, 0xf8, 0x2, 0xff, 536 | 0x80, 537 | 538 | /* U+0064 "d" */ 539 | 0x0, 0x0, 0xf8, 0x0, 0x3, 0xe0, 0x0, 0xf, 540 | 0x80, 0x0, 0x3e, 0x0, 0x0, 0xf8, 0xb, 0xf7, 541 | 0xe1, 0xff, 0xff, 0x8f, 0xd0, 0xbe, 0x7d, 0x0, 542 | 0xfb, 0xf0, 0x3, 0xef, 0x80, 0xf, 0xbf, 0x0, 543 | 0x3e, 0xbc, 0x0, 0xf8, 0xfd, 0x1f, 0xe2, 0xff, 544 | 0xff, 0x81, 0xfe, 0x2e, 545 | 546 | /* U+0065 "e" */ 547 | 0x1, 0xff, 0x40, 0xf, 0xff, 0xf0, 0x2f, 0x41, 548 | 0xf8, 0x3c, 0x0, 0xbc, 0x7f, 0xff, 0xfc, 0xbf, 549 | 0xff, 0xfc, 0x7c, 0x0, 0x0, 0x7d, 0x0, 0x0, 550 | 0x3f, 0x40, 0x0, 0x1f, 0xff, 0xc0, 0x2, 0xff, 551 | 0xc0, 552 | 553 | /* U+0066 "f" */ 554 | 0x0, 0x1b, 0xfc, 0x0, 0x7f, 0xfc, 0x0, 0xfd, 555 | 0x0, 0x0, 0xf4, 0x0, 0x0, 0xf4, 0x0, 0x7f, 556 | 0xff, 0xf4, 0x7f, 0xff, 0xf4, 0x0, 0xf4, 0x0, 557 | 0x0, 0xf4, 0x0, 0x0, 0xf4, 0x0, 0x0, 0xf4, 558 | 0x0, 0x0, 0xf4, 0x0, 0x0, 0xf4, 0x0, 0x0, 559 | 0xf4, 0x0, 0x0, 0xf4, 0x0, 0x0, 0xf4, 0x0, 560 | 561 | /* U+0067 "g" */ 562 | 0x2, 0xff, 0x68, 0x3f, 0xff, 0xe3, 0xf9, 0x1f, 563 | 0x9f, 0x40, 0x3e, 0xbc, 0x0, 0xfb, 0xe0, 0x3, 564 | 0xef, 0xc0, 0xf, 0xaf, 0x0, 0x3e, 0x3f, 0x47, 565 | 0xf8, 0xbf, 0xff, 0xe0, 0x7f, 0x8f, 0x80, 0x0, 566 | 0x3e, 0x0, 0x0, 0xf8, 0x0, 0x1f, 0xd0, 0xff, 567 | 0xfe, 0x3, 0xff, 0xd0, 568 | 569 | /* U+0068 "h" */ 570 | 0x3d, 0x0, 0x0, 0xf4, 0x0, 0x3, 0xd0, 0x0, 571 | 0xf, 0x40, 0x0, 0x3d, 0x0, 0x0, 0xf4, 0x7f, 572 | 0x43, 0xdb, 0xff, 0xf, 0xfd, 0xbe, 0x3f, 0x40, 573 | 0xf8, 0xf8, 0x3, 0xe3, 0xd0, 0xf, 0x8f, 0x40, 574 | 0x3e, 0x3d, 0x0, 0xf8, 0xf4, 0x3, 0xe3, 0xd0, 575 | 0xf, 0x8f, 0x40, 0x3e, 576 | 577 | /* U+0069 "i" */ 578 | 0x0, 0x50, 0x0, 0x3f, 0x80, 0x3, 0xfc, 0x0, 579 | 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f, 580 | 0xf4, 0x7, 0xff, 0x40, 0x1, 0xf4, 0x0, 0xf, 581 | 0x40, 0x0, 0xf4, 0x0, 0xf, 0x40, 0x0, 0xf4, 582 | 0x0, 0xf, 0x40, 0x1, 0xf4, 0xb, 0xff, 0xff, 583 | 0xbf, 0xff, 0xf0, 584 | 585 | /* U+006A "j" */ 586 | 0x0, 0x14, 0x0, 0x1f, 0xc0, 0xb, 0xf0, 0x0, 587 | 0xa4, 0x0, 0x0, 0x0, 0x0, 0xb, 0xff, 0xe2, 588 | 0xff, 0xf8, 0x0, 0x3e, 0x0, 0xf, 0x80, 0x3, 589 | 0xe0, 0x0, 0xf8, 0x0, 0x3e, 0x0, 0xf, 0x80, 590 | 0x3, 0xe0, 0x0, 0xf8, 0x0, 0x3e, 0x0, 0xf, 591 | 0x80, 0x3, 0xe0, 0x2, 0xf4, 0xff, 0xfc, 0x2f, 592 | 0xf8, 0x0, 593 | 594 | /* U+006B "k" */ 595 | 0xf8, 0x0, 0x3, 0xe0, 0x0, 0xf, 0x80, 0x0, 596 | 0x3e, 0x0, 0x0, 0xf8, 0x0, 0x3, 0xe0, 0xf, 597 | 0xcf, 0x80, 0xfc, 0x3e, 0xf, 0xc0, 0xf8, 0xfc, 598 | 0x3, 0xef, 0xc0, 0xf, 0xfd, 0x0, 0x3e, 0xbd, 599 | 0x0, 0xf8, 0xfd, 0x3, 0xe0, 0xfc, 0xf, 0x80, 600 | 0xfc, 0x3e, 0x0, 0xfc, 601 | 602 | /* U+006C "l" */ 603 | 0xbf, 0xf8, 0x2, 0xff, 0xe0, 0x0, 0xf, 0x80, 604 | 0x0, 0x3e, 0x0, 0x0, 0xf8, 0x0, 0x3, 0xe0, 605 | 0x0, 0xf, 0x80, 0x0, 0x3e, 0x0, 0x0, 0xf8, 606 | 0x0, 0x3, 0xe0, 0x0, 0xf, 0x80, 0x0, 0x3e, 607 | 0x0, 0x0, 0xf8, 0x0, 0x3, 0xf4, 0x0, 0x7, 608 | 0xff, 0xc0, 0xb, 0xfe, 609 | 610 | /* U+006D "m" */ 611 | 0xf0, 0xf4, 0x7c, 0xf3, 0xf8, 0xfd, 0xfb, 0xff, 612 | 0xfe, 0xfc, 0x7e, 0x2e, 0xf0, 0x3c, 0x2e, 0xf0, 613 | 0x3c, 0x2e, 0xf0, 0x3c, 0x2e, 0xf0, 0x3c, 0x2e, 614 | 0xf0, 0x3c, 0x2e, 0xf0, 0x3c, 0x2e, 0xf0, 0x3c, 615 | 0x2e, 616 | 617 | /* U+006E "n" */ 618 | 0x3c, 0x1f, 0xd0, 0xf7, 0xff, 0xc3, 0xfe, 0x5f, 619 | 0x8f, 0xd0, 0x3e, 0x3d, 0x0, 0xf8, 0xf4, 0x3, 620 | 0xe3, 0xd0, 0xf, 0x8f, 0x40, 0x3e, 0x3d, 0x0, 621 | 0xf8, 0xf4, 0x3, 0xe3, 0xd0, 0xf, 0x80, 622 | 623 | /* U+006F "o" */ 624 | 0x2, 0xfe, 0x40, 0xf, 0xff, 0xe0, 0x3f, 0x46, 625 | 0xf4, 0x7d, 0x0, 0xfc, 0xbc, 0x0, 0x7c, 0xbc, 626 | 0x0, 0x7d, 0xbc, 0x0, 0x7c, 0x7d, 0x0, 0xfc, 627 | 0x3f, 0x42, 0xf4, 0xf, 0xff, 0xe0, 0x2, 0xfe, 628 | 0x40, 629 | 630 | /* U+0070 "p" */ 631 | 0x3c, 0x7f, 0x80, 0x3e, 0xff, 0xf0, 0x3f, 0x41, 632 | 0xf8, 0x3d, 0x0, 0xbc, 0x3d, 0x0, 0x7d, 0x3d, 633 | 0x0, 0x3d, 0x3d, 0x0, 0x7c, 0x3d, 0x0, 0xfc, 634 | 0x3f, 0x42, 0xf8, 0x3f, 0xff, 0xf0, 0x3d, 0xbf, 635 | 0x40, 0x3d, 0x0, 0x0, 0x3d, 0x0, 0x0, 0x3d, 636 | 0x0, 0x0, 0x3d, 0x0, 0x0, 0x3d, 0x0, 0x0, 637 | 638 | /* U+0071 "q" */ 639 | 0x2, 0xff, 0x68, 0x3f, 0xff, 0xe3, 0xf8, 0x1f, 640 | 0x9f, 0x40, 0x3e, 0xbc, 0x0, 0xfb, 0xe0, 0x3, 641 | 0xef, 0xc0, 0xf, 0xaf, 0x0, 0x3e, 0x3f, 0x47, 642 | 0xf8, 0xbf, 0xff, 0xe0, 0x7f, 0x8f, 0x80, 0x0, 643 | 0x3e, 0x0, 0x0, 0xf8, 0x0, 0x3, 0xe0, 0x0, 644 | 0xf, 0x80, 0x0, 0x3e, 645 | 646 | /* U+0072 "r" */ 647 | 0x0, 0x0, 0x0, 0x7f, 0xc1, 0xfd, 0x7f, 0xcb, 648 | 0xfd, 0x7, 0xdf, 0xe8, 0x7, 0xfc, 0x0, 0x7, 649 | 0xf0, 0x0, 0x7, 0xd0, 0x0, 0x7, 0xc0, 0x0, 650 | 0x7, 0xc0, 0x0, 0x7, 0xc0, 0x0, 0xff, 0xff, 651 | 0x0, 0xff, 0xff, 0x0, 652 | 653 | /* U+0073 "s" */ 654 | 0x2, 0xff, 0xe0, 0x7f, 0xff, 0xc3, 0xf0, 0x1, 655 | 0xf, 0xc0, 0x0, 0x2f, 0xf9, 0x0, 0x1f, 0xff, 656 | 0x80, 0x1, 0xbf, 0x40, 0x0, 0x3e, 0x14, 0x1, 657 | 0xf8, 0xff, 0xff, 0xc2, 0xff, 0xf4, 0x0, 658 | 659 | /* U+0074 "t" */ 660 | 0x1, 0xf0, 0x0, 0x7, 0xc0, 0x0, 0x1f, 0x0, 661 | 0x0, 0x7c, 0x0, 0xff, 0xff, 0xf7, 0xff, 0xff, 662 | 0xd0, 0x1f, 0x0, 0x0, 0x7c, 0x0, 0x1, 0xf0, 663 | 0x0, 0x7, 0xc0, 0x0, 0x1f, 0x0, 0x0, 0x7c, 664 | 0x0, 0x1, 0xf8, 0x0, 0x3, 0xff, 0xf0, 0x2, 665 | 0xff, 0xc0, 666 | 667 | /* U+0075 "u" */ 668 | 0x3d, 0x0, 0xf8, 0xf4, 0x3, 0xe3, 0xd0, 0xf, 669 | 0x8f, 0x40, 0x3e, 0x3d, 0x0, 0xf8, 0xf4, 0x3, 670 | 0xe3, 0xd0, 0xf, 0x8f, 0x40, 0xbe, 0x3f, 0x1f, 671 | 0xf8, 0xbf, 0xf6, 0xe0, 0xbe, 0xb, 0x80, 672 | 673 | /* U+0076 "v" */ 674 | 0xfc, 0x0, 0x7d, 0x7d, 0x0, 0xbc, 0x3e, 0x0, 675 | 0xf8, 0x2f, 0x1, 0xf0, 0xf, 0x42, 0xf0, 0xf, 676 | 0x83, 0xd0, 0x7, 0xc7, 0xc0, 0x3, 0xdf, 0x80, 677 | 0x2, 0xff, 0x0, 0x1, 0xff, 0x0, 0x0, 0xfd, 678 | 0x0, 679 | 680 | /* U+0077 "w" */ 681 | 0x3c, 0x0, 0xb, 0x8f, 0x41, 0x3, 0xd2, 0xe0, 682 | 0xd0, 0xf4, 0xb8, 0x38, 0x3c, 0x1f, 0x1f, 0x1f, 683 | 0x3, 0xcf, 0xdb, 0x80, 0xf7, 0xba, 0xe0, 0x3e, 684 | 0xcf, 0xf4, 0xb, 0xf1, 0xfc, 0x1, 0xf8, 0x3f, 685 | 0x0, 0x7c, 0xf, 0xc0, 686 | 687 | /* U+0078 "x" */ 688 | 0x7e, 0x0, 0xf8, 0x2f, 0x43, 0xf0, 0xf, 0xc7, 689 | 0xd0, 0x3, 0xef, 0x80, 0x1, 0xff, 0x0, 0x0, 690 | 0xfd, 0x0, 0x2, 0xff, 0x0, 0x7, 0xdf, 0xc0, 691 | 0xf, 0x83, 0xe0, 0x3f, 0x2, 0xf4, 0xbd, 0x0, 692 | 0xfc, 693 | 694 | /* U+0079 "y" */ 695 | 0x3f, 0x0, 0x1f, 0x47, 0xd0, 0xb, 0xc0, 0xf8, 696 | 0x3, 0xe0, 0x2f, 0x1, 0xf0, 0x3, 0xd0, 0xbc, 697 | 0x0, 0xfc, 0x3d, 0x0, 0x1f, 0x1f, 0x0, 0x3, 698 | 0xef, 0x80, 0x0, 0xbf, 0xc0, 0x0, 0xf, 0xf0, 699 | 0x0, 0x3, 0xf4, 0x0, 0x0, 0xbc, 0x0, 0x0, 700 | 0x3e, 0x0, 0x0, 0x2f, 0x0, 0x3, 0xff, 0x80, 701 | 0x0, 0xff, 0x80, 0x0, 702 | 703 | /* U+007A "z" */ 704 | 0x3f, 0xff, 0xf8, 0xff, 0xff, 0xd0, 0x0, 0x7f, 705 | 0x0, 0x3, 0xf0, 0x0, 0x3f, 0x0, 0x3, 0xf4, 706 | 0x0, 0x2f, 0x40, 0x2, 0xf4, 0x0, 0x2f, 0x80, 707 | 0x0, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xc0, 708 | 709 | /* U+007B "{" */ 710 | 0x0, 0x0, 0x0, 0x0, 0x1a, 0xfc, 0x1, 0xff, 711 | 0xfd, 0x3, 0xf9, 0x40, 0x3, 0xf0, 0x0, 0x0, 712 | 0xf8, 0x0, 0x0, 0x7e, 0x0, 0x0, 0x1f, 0x40, 713 | 0x0, 0x1f, 0x80, 0x1, 0xbf, 0x0, 0x7f, 0xe4, 714 | 0x0, 0xbd, 0x0, 0x0, 0x7f, 0xe4, 0x0, 0x1, 715 | 0xff, 0x0, 0x0, 0x1f, 0x80, 0x0, 0x1f, 0x40, 716 | 0x0, 0x7e, 0x0, 0x0, 0xf8, 0x0, 0x3, 0xf0, 717 | 0x0, 0x3, 0xf9, 0x40, 0x1, 0xff, 0xfd, 0x0, 718 | 0x1a, 0xfc, 0x0, 0x0, 0x0, 719 | 720 | /* U+007C "|" */ 721 | 0x15, 0xf7, 0xdf, 0x7d, 0xf7, 0xdf, 0x7d, 0xf7, 722 | 0xdf, 0x7d, 0xf7, 0xdf, 0x7d, 0xf7, 0xdf, 723 | 724 | /* U+007D "}" */ 725 | 0x0, 0x0, 0x0, 0xbf, 0x90, 0x0, 0xff, 0xff, 726 | 0x0, 0x5, 0xbf, 0x40, 0x0, 0x1f, 0x40, 0x0, 727 | 0x7e, 0x0, 0x0, 0xfc, 0x0, 0x3, 0xf0, 0x0, 728 | 0x3, 0xe0, 0x0, 0x2, 0xfe, 0x40, 0x0, 0x1f, 729 | 0xf8, 0x0, 0x0, 0xbc, 0x0, 0x1f, 0xf8, 0x2, 730 | 0xfe, 0x40, 0x3, 0xf0, 0x0, 0x3, 0xf0, 0x0, 731 | 0x0, 0xfc, 0x0, 0x0, 0x7e, 0x0, 0x0, 0x1f, 732 | 0x40, 0x1, 0xbf, 0x40, 0xff, 0xff, 0x0, 0xbf, 733 | 0x94, 0x0, 0x0, 0x0, 0x0, 734 | 735 | /* U+007E "~" */ 736 | 0x0, 0x40, 0x1, 0x1, 0xfc, 0x2, 0xe0, 0xff, 737 | 0xd0, 0xf0, 0xbc, 0xbe, 0xbc, 0x3d, 0x7, 0xfd, 738 | 0xf, 0x0, 0x3e, 0x0, 0x0, 0x0, 0x0 739 | }; 740 | 741 | 742 | /*--------------------- 743 | * GLYPH DESCRIPTION 744 | *--------------------*/ 745 | 746 | static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { 747 | {.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */, 748 | {.bitmap_index = 0, .adv_w = 216, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0}, 749 | {.bitmap_index = 0, .adv_w = 216, .box_w = 6, .box_h = 16, .ofs_x = 4, .ofs_y = 0}, 750 | {.bitmap_index = 24, .adv_w = 216, .box_w = 10, .box_h = 8, .ofs_x = 2, .ofs_y = 8}, 751 | {.bitmap_index = 44, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, 752 | {.bitmap_index = 93, .adv_w = 216, .box_w = 11, .box_h = 22, .ofs_x = 1, .ofs_y = -4}, 753 | {.bitmap_index = 154, .adv_w = 216, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, 754 | {.bitmap_index = 207, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 755 | {.bitmap_index = 252, .adv_w = 216, .box_w = 4, .box_h = 8, .ofs_x = 5, .ofs_y = 8}, 756 | {.bitmap_index = 260, .adv_w = 216, .box_w = 10, .box_h = 23, .ofs_x = 2, .ofs_y = -4}, 757 | {.bitmap_index = 318, .adv_w = 216, .box_w = 11, .box_h = 23, .ofs_x = 1, .ofs_y = -4}, 758 | {.bitmap_index = 382, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 3}, 759 | {.bitmap_index = 415, .adv_w = 216, .box_w = 12, .box_h = 12, .ofs_x = 1, .ofs_y = 2}, 760 | {.bitmap_index = 451, .adv_w = 216, .box_w = 8, .box_h = 7, .ofs_x = 2, .ofs_y = -4}, 761 | {.bitmap_index = 465, .adv_w = 216, .box_w = 9, .box_h = 3, .ofs_x = 2, .ofs_y = 6}, 762 | {.bitmap_index = 472, .adv_w = 216, .box_w = 6, .box_h = 5, .ofs_x = 4, .ofs_y = 0}, 763 | {.bitmap_index = 480, .adv_w = 216, .box_w = 13, .box_h = 20, .ofs_x = 0, .ofs_y = -2}, 764 | {.bitmap_index = 545, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 765 | {.bitmap_index = 590, .adv_w = 216, .box_w = 10, .box_h = 15, .ofs_x = 2, .ofs_y = 0}, 766 | {.bitmap_index = 628, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 767 | {.bitmap_index = 673, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 768 | {.bitmap_index = 715, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 769 | {.bitmap_index = 760, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 770 | {.bitmap_index = 802, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 771 | {.bitmap_index = 847, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 772 | {.bitmap_index = 889, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 773 | {.bitmap_index = 931, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 774 | {.bitmap_index = 976, .adv_w = 216, .box_w = 6, .box_h = 11, .ofs_x = 4, .ofs_y = 0}, 775 | {.bitmap_index = 993, .adv_w = 216, .box_w = 8, .box_h = 15, .ofs_x = 2, .ofs_y = -4}, 776 | {.bitmap_index = 1023, .adv_w = 216, .box_w = 11, .box_h = 13, .ofs_x = 1, .ofs_y = 1}, 777 | {.bitmap_index = 1059, .adv_w = 216, .box_w = 11, .box_h = 7, .ofs_x = 1, .ofs_y = 4}, 778 | {.bitmap_index = 1079, .adv_w = 216, .box_w = 12, .box_h = 13, .ofs_x = 1, .ofs_y = 1}, 779 | {.bitmap_index = 1118, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 1, .ofs_y = 0}, 780 | {.bitmap_index = 1162, .adv_w = 216, .box_w = 12, .box_h = 19, .ofs_x = 1, .ofs_y = -3}, 781 | {.bitmap_index = 1219, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, 782 | {.bitmap_index = 1268, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 783 | {.bitmap_index = 1313, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 784 | {.bitmap_index = 1358, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 785 | {.bitmap_index = 1403, .adv_w = 216, .box_w = 10, .box_h = 15, .ofs_x = 2, .ofs_y = 0}, 786 | {.bitmap_index = 1441, .adv_w = 216, .box_w = 10, .box_h = 15, .ofs_x = 2, .ofs_y = 0}, 787 | {.bitmap_index = 1479, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 788 | {.bitmap_index = 1524, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 789 | {.bitmap_index = 1566, .adv_w = 216, .box_w = 9, .box_h = 15, .ofs_x = 2, .ofs_y = 0}, 790 | {.bitmap_index = 1600, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, 791 | {.bitmap_index = 1645, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 792 | {.bitmap_index = 1694, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 2, .ofs_y = 0}, 793 | {.bitmap_index = 1736, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 794 | {.bitmap_index = 1781, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 795 | {.bitmap_index = 1823, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 796 | {.bitmap_index = 1868, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 797 | {.bitmap_index = 1913, .adv_w = 216, .box_w = 13, .box_h = 19, .ofs_x = 1, .ofs_y = -4}, 798 | {.bitmap_index = 1975, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 799 | {.bitmap_index = 2020, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 800 | {.bitmap_index = 2062, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, 801 | {.bitmap_index = 2111, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 802 | {.bitmap_index = 2153, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, 803 | {.bitmap_index = 2202, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, 804 | {.bitmap_index = 2251, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, 805 | {.bitmap_index = 2300, .adv_w = 216, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, 806 | {.bitmap_index = 2349, .adv_w = 216, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 807 | {.bitmap_index = 2394, .adv_w = 216, .box_w = 9, .box_h = 22, .ofs_x = 3, .ofs_y = -3}, 808 | {.bitmap_index = 2444, .adv_w = 216, .box_w = 13, .box_h = 20, .ofs_x = 0, .ofs_y = -2}, 809 | {.bitmap_index = 2509, .adv_w = 216, .box_w = 9, .box_h = 22, .ofs_x = 1, .ofs_y = -3}, 810 | {.bitmap_index = 2559, .adv_w = 216, .box_w = 13, .box_h = 9, .ofs_x = 0, .ofs_y = 6}, 811 | {.bitmap_index = 2589, .adv_w = 216, .box_w = 11, .box_h = 3, .ofs_x = 1, .ofs_y = -3}, 812 | {.bitmap_index = 2598, .adv_w = 216, .box_w = 8, .box_h = 8, .ofs_x = 2, .ofs_y = 9}, 813 | {.bitmap_index = 2614, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, 814 | {.bitmap_index = 2647, .adv_w = 216, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = 0}, 815 | {.bitmap_index = 2695, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, 816 | {.bitmap_index = 2728, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 1, .ofs_y = 0}, 817 | {.bitmap_index = 2772, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, 818 | {.bitmap_index = 2805, .adv_w = 216, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = 0}, 819 | {.bitmap_index = 2853, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 1, .ofs_y = -5}, 820 | {.bitmap_index = 2897, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 1, .ofs_y = 0}, 821 | {.bitmap_index = 2941, .adv_w = 216, .box_w = 10, .box_h = 17, .ofs_x = 2, .ofs_y = 0}, 822 | {.bitmap_index = 2984, .adv_w = 216, .box_w = 9, .box_h = 22, .ofs_x = 2, .ofs_y = -5}, 823 | {.bitmap_index = 3034, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 2, .ofs_y = 0}, 824 | {.bitmap_index = 3078, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 1, .ofs_y = 0}, 825 | {.bitmap_index = 3122, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, 826 | {.bitmap_index = 3155, .adv_w = 216, .box_w = 11, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, 827 | {.bitmap_index = 3186, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, 828 | {.bitmap_index = 3219, .adv_w = 216, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = -5}, 829 | {.bitmap_index = 3267, .adv_w = 216, .box_w = 11, .box_h = 16, .ofs_x = 1, .ofs_y = -5}, 830 | {.bitmap_index = 3311, .adv_w = 216, .box_w = 12, .box_h = 12, .ofs_x = 1, .ofs_y = 0}, 831 | {.bitmap_index = 3347, .adv_w = 216, .box_w = 11, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, 832 | {.bitmap_index = 3378, .adv_w = 216, .box_w = 11, .box_h = 15, .ofs_x = 1, .ofs_y = 0}, 833 | {.bitmap_index = 3420, .adv_w = 216, .box_w = 11, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, 834 | {.bitmap_index = 3451, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, 835 | {.bitmap_index = 3484, .adv_w = 216, .box_w = 13, .box_h = 11, .ofs_x = 0, .ofs_y = 0}, 836 | {.bitmap_index = 3520, .adv_w = 216, .box_w = 12, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, 837 | {.bitmap_index = 3553, .adv_w = 216, .box_w = 13, .box_h = 16, .ofs_x = 0, .ofs_y = -5}, 838 | {.bitmap_index = 3605, .adv_w = 216, .box_w = 11, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, 839 | {.bitmap_index = 3636, .adv_w = 216, .box_w = 12, .box_h = 23, .ofs_x = 1, .ofs_y = -4}, 840 | {.bitmap_index = 3705, .adv_w = 216, .box_w = 3, .box_h = 20, .ofs_x = 5, .ofs_y = -2}, 841 | {.bitmap_index = 3720, .adv_w = 216, .box_w = 12, .box_h = 23, .ofs_x = 1, .ofs_y = -4}, 842 | {.bitmap_index = 3789, .adv_w = 216, .box_w = 13, .box_h = 7, .ofs_x = 0, .ofs_y = 4} 843 | }; 844 | 845 | /*--------------------- 846 | * CHARACTER MAPPING 847 | *--------------------*/ 848 | 849 | 850 | 851 | /*Collect the unicode lists and glyph_id offsets*/ 852 | static const lv_font_fmt_txt_cmap_t cmaps[] = 853 | { 854 | { 855 | .range_start = 32, .range_length = 95, .glyph_id_start = 1, 856 | .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY 857 | } 858 | }; 859 | 860 | 861 | 862 | /*-------------------- 863 | * ALL CUSTOM DATA 864 | *--------------------*/ 865 | 866 | static lv_font_fmt_txt_dsc_t font_dsc = { 867 | .glyph_bitmap = glyph_bitmap, 868 | .glyph_dsc = glyph_dsc, 869 | .cmaps = cmaps, 870 | .kern_dsc = NULL, 871 | .kern_scale = 0, 872 | .cmap_num = 1, 873 | .bpp = 2, 874 | .kern_classes = 0, 875 | }; 876 | 877 | 878 | 879 | /*----------------- 880 | * PUBLIC FONT 881 | *----------------*/ 882 | 883 | /*Initialize a public general font descriptor*/ 884 | const lv_font_t intel_one_mono = { 885 | .dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */ 886 | //.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/ 887 | //.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/ 888 | .line_height = 24, /*The maximum line height required by the font*/ 889 | .base_line = 5, /*Baseline measured from the bottom of the line*/ 890 | }; 891 | 892 | 893 | 894 | #endif /*#if INTEL_ONE_MONO*/ 895 | 896 | -------------------------------------------------------------------------------- /examples/dvhstx/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable( 2 | mandelbrot 3 | mandelbrot.cpp 4 | mandelf.c 5 | ) 6 | 7 | # Pull in pico libraries that we need 8 | target_link_libraries(mandelbrot pico_stdlib pico_multicore pico_dvhstx hardware_adc) 9 | pico_enable_stdio_usb(mandelbrot 1) 10 | 11 | # create map/bin/hex file etc. 12 | pico_add_extra_outputs(mandelbrot) 13 | 14 | add_executable( 15 | textmode 16 | textmode.cpp 17 | ) 18 | 19 | # Pull in pico libraries that we need 20 | target_link_libraries(textmode pico_stdlib pico_dvhstx) 21 | pico_enable_stdio_usb(textmode 1) 22 | 23 | # create map/bin/hex file etc. 24 | pico_add_extra_outputs(textmode) 25 | -------------------------------------------------------------------------------- /examples/dvhstx/mandelbrot.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hardware/uart.h" 3 | #include "hardware/adc.h" 4 | #include "pico/multicore.h" 5 | #include "drivers/dvhstx/dvhstx.hpp" 6 | #include "libraries/pico_graphics/pico_graphics_dvhstx.hpp" 7 | 8 | extern "C" { 9 | #include "mandelf.h" 10 | } 11 | 12 | using namespace pimoroni; 13 | 14 | #ifdef __riscv 15 | #define FRAME_WIDTH 400 16 | #define FRAME_HEIGHT 300 17 | #else 18 | #define FRAME_WIDTH 640 19 | #define FRAME_HEIGHT 360 20 | #endif 21 | 22 | //#define FRAME_WIDTH 320 23 | //#define FRAME_HEIGHT 180 24 | 25 | static DVHSTX display; 26 | static PicoGraphics_PenDVHSTX_P8 graphics(FRAME_WIDTH, FRAME_HEIGHT, display); 27 | 28 | static FractalBuffer fractal; 29 | 30 | static void init_palette() { 31 | graphics.create_pen(0, 0, 0); 32 | for (int i = 0; i < 64; ++i) { 33 | graphics.create_pen_hsv(i * (1.f / 63.f), 1.0f, 0.5f + (i & 7) * (0.5f / 7.f)); 34 | } 35 | } 36 | 37 | static void init_mandel() { 38 | fractal.rows = FRAME_HEIGHT / 2; 39 | fractal.cols = FRAME_WIDTH; 40 | fractal.max_iter = 63; 41 | fractal.iter_offset = 0; 42 | fractal.minx = -2.25f; 43 | fractal.maxx = 0.75f; 44 | fractal.miny = -1.6f; 45 | fractal.maxy = 0.f - (1.6f / FRAME_HEIGHT); // Half a row 46 | fractal.use_cycle_check = true; 47 | init_fractal(&fractal); 48 | } 49 | 50 | #define NUM_ZOOMS 100 51 | static uint32_t zoom_count = 0; 52 | 53 | static void zoom_mandel() { 54 | if (++zoom_count == NUM_ZOOMS) 55 | { 56 | init_mandel(); 57 | zoom_count = 0; 58 | sleep_ms(2000); 59 | return; 60 | } 61 | 62 | float zoomx = -.75f - .7f * ((float)zoom_count / (float)NUM_ZOOMS); 63 | float sizex = fractal.maxx - fractal.minx; 64 | float sizey = fractal.miny * -2.f; 65 | float zoomr = 0.974f * 0.5f; 66 | fractal.minx = zoomx - zoomr * sizex; 67 | fractal.maxx = zoomx + zoomr * sizex; 68 | fractal.miny = -zoomr * sizey; 69 | fractal.maxy = 0.f + fractal.miny / FRAME_HEIGHT; 70 | init_fractal(&fractal); 71 | } 72 | 73 | static void display_row(int y, uint8_t* buf) { 74 | display.write_palette_pixel_span({0, y}, FRAME_WIDTH, buf); 75 | display.write_palette_pixel_span({0, FRAME_HEIGHT - 1 - y}, FRAME_WIDTH, buf); 76 | } 77 | 78 | static uint8_t row_buf_core1[FRAME_WIDTH]; 79 | void core1_main() { 80 | while (true) { 81 | int y = multicore_fifo_pop_blocking(); 82 | generate_one_line(&fractal, row_buf_core1, y); 83 | multicore_fifo_push_blocking(y); 84 | } 85 | } 86 | 87 | static uint8_t row_buf[FRAME_WIDTH]; 88 | static void draw_two_rows(int y) { 89 | multicore_fifo_push_blocking(y+1); 90 | generate_one_line(&fractal, row_buf, y); 91 | 92 | display_row(y, row_buf); 93 | 94 | multicore_fifo_pop_blocking(); 95 | display_row(y+1, row_buf_core1); 96 | } 97 | 98 | void draw_mandel() { 99 | //display.wait_for_flip(); 100 | for (int y = 0; y < FRAME_HEIGHT / 2; y += 2) 101 | { 102 | draw_two_rows(y); 103 | } 104 | //display.flip_async(); 105 | display.flip_now(); 106 | } 107 | 108 | /* References for this implementation: 109 | * raspberry-pi-pico-c-sdk.pdf, Section '4.1.1. hardware_adc' 110 | * pico-examples/adc/adc_console/adc_console.c */ 111 | float read_onboard_temperature() { 112 | 113 | /* 12-bit conversion, assume max value == ADC_VREF == 3.3 V */ 114 | const float conversionFactor = 3.3f / (1 << 12); 115 | 116 | float adc = (float)adc_read() * conversionFactor; 117 | float tempC = 27.0f - (adc - 0.706f) / 0.001721f; 118 | 119 | return tempC; 120 | } 121 | 122 | int main() { 123 | display.init(FRAME_WIDTH, FRAME_HEIGHT, DVHSTX::MODE_PALETTE); 124 | 125 | stdio_init_all(); 126 | //while (!stdio_usb_connected()); 127 | 128 | /* Initialize hardware AD converter, enable onboard temperature sensor and 129 | * select its channel (do this once for efficiency, but beware that this 130 | * is a global operation). */ 131 | adc_init(); 132 | adc_set_temp_sensor_enabled(true); 133 | adc_select_input(4); 134 | 135 | init_palette(); 136 | 137 | graphics.set_pen(0); 138 | graphics.clear(); 139 | display.flip_now(); 140 | graphics.clear(); 141 | 142 | multicore_launch_core1(core1_main); 143 | 144 | init_mandel(); 145 | draw_mandel(); 146 | 147 | while(true) { 148 | absolute_time_t start_time = get_absolute_time(); 149 | zoom_mandel(); 150 | draw_mandel(); 151 | printf("Drawing zoom %ld took %.2fms\t", zoom_count, absolute_time_diff_us(start_time, get_absolute_time()) * 0.001f); 152 | 153 | const float temperature = read_onboard_temperature(); 154 | printf("Temp = %.02fC\n", temperature); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /examples/dvhstx/mandelf.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) Michael Bell 2021 2 | 3 | #include 4 | #include 5 | #include 6 | #include "pico/stdlib.h" 7 | 8 | #include "mandelf.h" 9 | 10 | // Cycle checking parameters 11 | #define MAX_CYCLE_LEN 8 // Must be power of 2 12 | #define MIN_CYCLE_CHECK_ITER 24 // Must be multiple of max cycle len 13 | #define CYCLE_TOLERANCE 4e-3 14 | 15 | #define ESCAPE_SQUARE 4.f 16 | 17 | void init_fractal(FractalBuffer* f) 18 | { 19 | f->done = false; 20 | f->min_iter = f->max_iter - 1; 21 | f->incx = (f->maxx - f->minx) / (f->cols - 1); 22 | f->incy = (f->maxy - f->miny) / (f->rows - 1); 23 | f->count_inside = 0; 24 | } 25 | 26 | static inline void generate_one(FractalBuffer* f, float x0, float y0, uint8_t* buffptr) 27 | { 28 | float x = x0; 29 | float y = y0; 30 | 31 | uint16_t k = 1; 32 | for (; k < f->max_iter; ++k) { 33 | float x_square = x*x; 34 | float y_square = y*y; 35 | if (x_square + y_square > ESCAPE_SQUARE) break; 36 | 37 | float nextx = x_square - y_square + x0; 38 | y = x*y*2.f + y0; 39 | x = nextx; 40 | } 41 | if (k == f->max_iter) { 42 | *buffptr = 0; 43 | f->count_inside++; 44 | } else { 45 | if (k > f->iter_offset) k -= f->iter_offset; 46 | else k = 1; 47 | *buffptr = k; 48 | if (f->min_iter > k) f->min_iter = k; 49 | } 50 | } 51 | 52 | void generate_one_line(FractalBuffer* f, uint8_t* buf, uint16_t ipos) 53 | { 54 | if (f->done) return; 55 | 56 | float y0 = f->miny + ipos * f->incy; 57 | float x0 = f->minx; 58 | 59 | for (int16_t j = 0; j < f->cols; ++j) { 60 | generate_one(f, x0, y0, buf++); 61 | x0 += f->incx; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /examples/dvhstx/mandelf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct { 4 | // Configuration 5 | int16_t rows; 6 | int16_t cols; 7 | 8 | uint16_t max_iter; 9 | uint16_t iter_offset; 10 | float minx, miny, maxx, maxy; 11 | bool use_cycle_check; 12 | 13 | // State 14 | volatile bool done; 15 | volatile uint16_t min_iter; 16 | float incx, incy; 17 | volatile uint32_t count_inside; 18 | } FractalBuffer; 19 | 20 | // Generate a section of the fractal into buff 21 | // Result written to buff is 0 for inside Mandelbrot set 22 | // Otherwise iteration of escape minus min_iter (clamped to 1) 23 | void init_fractal(FractalBuffer* fractal); 24 | void generate_one_line(FractalBuffer* f, uint8_t* buf, uint16_t row); 25 | -------------------------------------------------------------------------------- /examples/dvhstx/textmode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hardware/uart.h" 3 | #include "drivers/dvhstx/dvhstx.hpp" 4 | 5 | using namespace pimoroni; 6 | 7 | #define FRAME_WIDTH 91 8 | #define FRAME_HEIGHT 30 9 | 10 | static DVHSTX display; 11 | 12 | using namespace pimoroni; 13 | 14 | void fill_text(int frame) 15 | { 16 | char buf[128]; 17 | sprintf(buf, "Hello World! Frame: %d", frame); 18 | display.write_text({0,0}, buf); 19 | 20 | for (int i = 0; i < 0x7f; ++i) { 21 | buf[i] = i; 22 | } 23 | buf[0x7f] = 0; 24 | 25 | for (int i = 1; i < FRAME_HEIGHT; ++i) { 26 | display.write_text({0, i}, &buf[0x20 + i], (i & 1) ? (i & 2) ? DVHSTX::TEXT_WHITE : DVHSTX::TEXT_BLUE : (i & 2) ? DVHSTX::TEXT_YELLOW : DVHSTX::TEXT_RED); 27 | } 28 | } 29 | 30 | int main() 31 | { 32 | stdio_init_all(); 33 | 34 | printf("Main\n"); 35 | 36 | bool rv = display.init(FRAME_WIDTH, FRAME_HEIGHT, DVHSTX::MODE_TEXT_RGB111); 37 | 38 | printf("Init complete: %s", rv ? "True" : "False"); 39 | 40 | fill_text(0); 41 | display.flip_now(); 42 | 43 | int frame = 1; 44 | while(1) { 45 | fill_text(frame++); 46 | display.flip_blocking(); 47 | printf(".\n"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /libraries/pico_graphics/pico_graphics_dvhstx.hpp: -------------------------------------------------------------------------------- 1 | #include "pico_graphics.hpp" 2 | #include "drivers/dvhstx/dvhstx.hpp" 3 | 4 | namespace pimoroni { 5 | enum BlendMode { 6 | TARGET = 0, 7 | FIXED = 1, 8 | }; 9 | 10 | class PicoGraphicsDVHSTX : public PicoGraphics { 11 | public: 12 | DVHSTX &driver; 13 | BlendMode blend_mode = BlendMode::TARGET; 14 | 15 | void set_blend_mode(BlendMode mode) { 16 | blend_mode = mode; 17 | } 18 | 19 | virtual void set_depth(uint8_t new_depth) {} 20 | virtual void set_bg(uint c) {}; 21 | 22 | PicoGraphicsDVHSTX(uint16_t width, uint16_t height, DVHSTX &dv_display) 23 | : PicoGraphics(width, height, nullptr), 24 | driver(dv_display) 25 | { 26 | this->pen_type = PEN_DV_RGB555; 27 | } 28 | }; 29 | 30 | class PicoGraphics_PenDVHSTX_RGB565 : public PicoGraphicsDVHSTX { 31 | public: 32 | RGB565 color; 33 | RGB565 background; 34 | uint16_t depth = 0; 35 | 36 | PicoGraphics_PenDVHSTX_RGB565(uint16_t width, uint16_t height, DVHSTX &dv_display); 37 | void set_pen(uint c) override; 38 | void set_bg(uint c) override; 39 | void set_depth(uint8_t new_depth) override; 40 | void set_pen(uint8_t r, uint8_t g, uint8_t b) override; 41 | int create_pen(uint8_t r, uint8_t g, uint8_t b) override; 42 | int create_pen_hsv(float h, float s, float v) override; 43 | void set_pixel(const Point &p) override; 44 | void set_pixel_span(const Point &p, uint l) override; 45 | void set_pixel_alpha(const Point &p, const uint8_t a) override; 46 | 47 | bool supports_alpha_blend() override {return true;} 48 | 49 | bool render_tile(const Tile *tile) override; 50 | 51 | static size_t buffer_size(uint w, uint h) { 52 | return w * h * sizeof(RGB565); 53 | } 54 | }; 55 | 56 | class PicoGraphics_PenDVHSTX_P8 : public PicoGraphicsDVHSTX { 57 | public: 58 | static const uint16_t palette_size = 256; 59 | uint8_t color; 60 | uint8_t depth = 0; 61 | bool used[palette_size]; 62 | 63 | std::array, 512> candidate_cache; 64 | std::array candidates; 65 | bool cache_built = false; 66 | 67 | PicoGraphics_PenDVHSTX_P8(uint16_t width, uint16_t height, DVHSTX &dv_display); 68 | void set_pen(uint c) override; 69 | void set_pen(uint8_t r, uint8_t g, uint8_t b) override; 70 | void set_depth(uint8_t new_depth) override; 71 | int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override; 72 | int create_pen(uint8_t r, uint8_t g, uint8_t b) override; 73 | int create_pen_hsv(float h, float s, float v) override; 74 | int reset_pen(uint8_t i) override; 75 | 76 | int get_palette_size() override {return 0;}; 77 | RGB* get_palette() override {return nullptr;}; 78 | 79 | void set_pixel(const Point &p) override; 80 | void set_pixel_span(const Point &p, uint l) override; 81 | void get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array &candidates); 82 | void set_pixel_dither(const Point &p, const RGB &c) override; 83 | 84 | static size_t buffer_size(uint w, uint h) { 85 | return w * h; 86 | } 87 | }; 88 | } -------------------------------------------------------------------------------- /libraries/pico_graphics/pico_graphics_pen_dvhstx_p8.cpp: -------------------------------------------------------------------------------- 1 | #include "pico_graphics_dvhstx.hpp" 2 | 3 | namespace pimoroni { 4 | 5 | inline constexpr uint32_t RGB_to_RGB888(const uint8_t r, const uint8_t g, const uint8_t b) { 6 | return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; 7 | } 8 | 9 | PicoGraphics_PenDVHSTX_P8::PicoGraphics_PenDVHSTX_P8(uint16_t width, uint16_t height, DVHSTX &dv_display) 10 | : PicoGraphicsDVHSTX(width, height, dv_display) 11 | { 12 | this->pen_type = PEN_DV_P5; 13 | for(auto i = 0u; i < palette_size; i++) { 14 | driver.set_palette_colour(i, RGB_to_RGB888(i, i, i) << 3); 15 | used[i] = false; 16 | } 17 | cache_built = false; 18 | } 19 | void PicoGraphics_PenDVHSTX_P8::set_pen(uint c) { 20 | color = c; 21 | } 22 | void PicoGraphics_PenDVHSTX_P8::set_depth(uint8_t new_depth) { 23 | depth = new_depth > 0 ? 1 : 0; 24 | } 25 | void PicoGraphics_PenDVHSTX_P8::set_pen(uint8_t r, uint8_t g, uint8_t b) { 26 | RGB888 *driver_palette = driver.get_palette(); 27 | RGB palette[palette_size]; 28 | for(auto i = 0u; i < palette_size; i++) { 29 | palette[i] = RGB((uint)driver_palette[i]); 30 | } 31 | 32 | int pen = RGB(r, g, b).closest(palette, palette_size); 33 | if(pen != -1) color = pen; 34 | } 35 | int PicoGraphics_PenDVHSTX_P8::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) { 36 | used[i] = true; 37 | cache_built = false; 38 | driver.set_palette_colour(i, RGB_to_RGB888(r, g, b)); 39 | return i; 40 | } 41 | int PicoGraphics_PenDVHSTX_P8::create_pen(uint8_t r, uint8_t g, uint8_t b) { 42 | // Create a colour and place it in the palette if there's space 43 | for(auto i = 0u; i < palette_size; i++) { 44 | if(!used[i]) { 45 | used[i] = true; 46 | cache_built = false; 47 | driver.set_palette_colour(i, RGB_to_RGB888(r, g, b)); 48 | return i; 49 | } 50 | } 51 | return -1; 52 | } 53 | int PicoGraphics_PenDVHSTX_P8::create_pen_hsv(float h, float s, float v) { 54 | RGB p = RGB::from_hsv(h, s, v); 55 | return create_pen(p.r, p.g, p.b); 56 | } 57 | int PicoGraphics_PenDVHSTX_P8::reset_pen(uint8_t i) { 58 | driver.set_palette_colour(i, 0); 59 | used[i] = false; 60 | cache_built = false; 61 | return i; 62 | } 63 | void PicoGraphics_PenDVHSTX_P8::set_pixel(const Point &p) { 64 | driver.write_palette_pixel(p, color); 65 | } 66 | 67 | void PicoGraphics_PenDVHSTX_P8::set_pixel_span(const Point &p, uint l) { 68 | driver.write_palette_pixel_span(p, l, color); 69 | } 70 | 71 | void PicoGraphics_PenDVHSTX_P8::get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array &candidates) { 72 | RGB error; 73 | for(size_t i = 0; i < candidates.size(); i++) { 74 | candidates[i] = (col + error).closest(palette, len); 75 | error += (col - palette[candidates[i]]); 76 | } 77 | 78 | // sort by a rough approximation of luminance, this ensures that neighbouring 79 | // pixels in the dither matrix are at extreme opposites of luminence 80 | // giving a more balanced output 81 | std::sort(candidates.begin(), candidates.end(), [palette](int a, int b) { 82 | return palette[a].luminance() > palette[b].luminance(); 83 | }); 84 | } 85 | 86 | void PicoGraphics_PenDVHSTX_P8::set_pixel_dither(const Point &p, const RGB &c) { 87 | if(!bounds.contains(p)) return; 88 | 89 | if(!cache_built) { 90 | RGB888 *driver_palette = driver.get_palette(); 91 | RGB palette[palette_size]; 92 | for(auto i = 0u; i < palette_size; i++) { 93 | palette[i] = RGB((uint)driver_palette[i]); 94 | } 95 | 96 | for(uint i = 0; i < 512; i++) { 97 | RGB cache_col((i & 0x1C0) >> 1, (i & 0x38) << 2, (i & 0x7) << 5); 98 | get_dither_candidates(cache_col, palette, palette_size, candidate_cache[i]); 99 | } 100 | cache_built = true; 101 | } 102 | 103 | uint cache_key = ((c.r & 0xE0) << 1) | ((c.g & 0xE0) >> 2) | ((c.b & 0xE0) >> 5); 104 | 105 | // find the pattern coordinate offset 106 | uint pattern_index = (p.x & 0b11) | ((p.y & 0b11) << 2); 107 | 108 | // set the pixel 109 | color = candidate_cache[cache_key][dither16_pattern[pattern_index]]; 110 | set_pixel(p); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /libraries/pico_graphics/pico_graphics_pen_dvhstx_rgb565.cpp: -------------------------------------------------------------------------------- 1 | #include "pico_graphics_dvhstx.hpp" 2 | 3 | #ifndef MICROPY_BUILD_TYPE 4 | #define mp_printf(_, ...) printf(__VA_ARGS__); 5 | #else 6 | extern "C" { 7 | #include "py/runtime.h" 8 | } 9 | #endif 10 | 11 | namespace pimoroni { 12 | RGB565 to_rgb565_noswap(const RGB& color) { 13 | uint16_t p = ((color.r & 0b11111000) << 8) | 14 | ((color.g & 0b11111100) << 3) | 15 | ((color.b & 0b11111000) >> 3); 16 | 17 | return p; 18 | } 19 | 20 | PicoGraphics_PenDVHSTX_RGB565::PicoGraphics_PenDVHSTX_RGB565(uint16_t width, uint16_t height, DVHSTX &dv_display) 21 | : PicoGraphicsDVHSTX(width, height, dv_display) 22 | { 23 | this->pen_type = PEN_DV_RGB555; 24 | } 25 | void PicoGraphics_PenDVHSTX_RGB565::set_pen(uint c) { 26 | color = c; 27 | } 28 | void PicoGraphics_PenDVHSTX_RGB565::set_bg(uint c) { 29 | background = c; 30 | } 31 | void PicoGraphics_PenDVHSTX_RGB565::set_depth(uint8_t new_depth) { 32 | depth = new_depth > 0 ? 0x8000 : 0; 33 | } 34 | void PicoGraphics_PenDVHSTX_RGB565::set_pen(uint8_t r, uint8_t g, uint8_t b) { 35 | RGB src_color{r, g, b}; 36 | color = to_rgb565_noswap(src_color); 37 | } 38 | int PicoGraphics_PenDVHSTX_RGB565::create_pen(uint8_t r, uint8_t g, uint8_t b) { 39 | return to_rgb565_noswap(RGB(r, g, b)); 40 | } 41 | int PicoGraphics_PenDVHSTX_RGB565::create_pen_hsv(float h, float s, float v) { 42 | return to_rgb565_noswap(RGB::from_hsv(h, s, v)); 43 | } 44 | void PicoGraphics_PenDVHSTX_RGB565::set_pixel(const Point &p) { 45 | driver.write_pixel(p, color); 46 | } 47 | void PicoGraphics_PenDVHSTX_RGB565::set_pixel_span(const Point &p, uint l) { 48 | driver.write_pixel_span(p, l, color); 49 | } 50 | void PicoGraphics_PenDVHSTX_RGB565::set_pixel_alpha(const Point &p, const uint8_t a) { 51 | uint16_t src = background; 52 | if (blend_mode == BlendMode::TARGET) { 53 | driver.read_pixel_span(p, 1, &src); 54 | } 55 | 56 | uint8_t src_r = (src >> 8) & 0b11111000; 57 | uint8_t src_g = (src >> 3) & 0b11111100; 58 | uint8_t src_b = (src << 3) & 0b11111000; 59 | 60 | uint8_t dst_r = (color >> 8) & 0b11111000; 61 | uint8_t dst_g = (color >> 3) & 0b11111100; 62 | uint8_t dst_b = (color << 3) & 0b11111000; 63 | 64 | RGB565 blended = to_rgb565_noswap(RGB(src_r, src_g, src_b).blend(RGB(dst_r, dst_g, dst_b), a)); 65 | 66 | driver.write_pixel(p, blended); 67 | } 68 | 69 | bool PicoGraphics_PenDVHSTX_RGB565::render_tile(const Tile* tile) { 70 | return false; 71 | } 72 | } -------------------------------------------------------------------------------- /micropython/board/PIMORONI_PICO_PLUS2/board.json: -------------------------------------------------------------------------------- 1 | { 2 | "deploy": [ 3 | "../deploy.md" 4 | ], 5 | "docs": "", 6 | "features": [ 7 | "Dual-core", 8 | "External Flash", 9 | "USB" 10 | ], 11 | "images": [ 12 | "rp2-picos.jpg" 13 | ], 14 | "mcu": "rp2350", 15 | "product": "Pico2", 16 | "thumbnail": "", 17 | "url": "https://shop.pimoroni.com/products/raspberry-pi-pico-plus2/", 18 | "vendor": "Raspberry Pi" 19 | } 20 | -------------------------------------------------------------------------------- /micropython/board/PIMORONI_PICO_PLUS2/manifest.py: -------------------------------------------------------------------------------- 1 | include("$(PORT_DIR)/boards/manifest.py") 2 | 3 | include("../manifest_pico2.py") -------------------------------------------------------------------------------- /micropython/board/PIMORONI_PICO_PLUS2/mpconfigboard.cmake: -------------------------------------------------------------------------------- 1 | # cmake file for Raspberry Pi Pico 2 | set(PICO_BOARD "pimoroni_pico_plus2_rp2350") 3 | set(PICO_PLATFORM "rp2350") 4 | set(PICO_NUM_GPIOS 48) 5 | 6 | # Board specific version of the frozen manifest 7 | set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) 8 | 9 | set(MICROPY_C_HEAP_SIZE 32768) 10 | -------------------------------------------------------------------------------- /micropython/board/PIMORONI_PICO_PLUS2/mpconfigboard.h: -------------------------------------------------------------------------------- 1 | // Board and hardware specific configuration 2 | #ifndef MICROPY_HW_BOARD_NAME 3 | // Might be defined by mpconfigvariant.cmake 4 | #define MICROPY_HW_BOARD_NAME "Pimoroni Pico Plus 2" 5 | #endif 6 | #define MICROPY_HW_FLASH_STORAGE_BYTES (PICO_FLASH_SIZE_BYTES - (2 * 1024 * 1024)) 7 | 8 | #define MICROPY_HW_PSRAM_CS_PIN PIMORONI_PICO_PLUS2_PSRAM_CS_PIN 9 | 10 | extern void display_setup_clock_preinit(); 11 | #define MICROPY_BOARD_STARTUP() display_setup_clock_preinit() -------------------------------------------------------------------------------- /micropython/board/PIMORONI_PICO_PLUS2/mpconfigvariant.cmake: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichaelBell/dvhstx/7eca7c14871dbb407a6ba6636b810c30e9cb0e3f/micropython/board/PIMORONI_PICO_PLUS2/mpconfigvariant.cmake -------------------------------------------------------------------------------- /micropython/board/PIMORONI_PICO_PLUS2/mpconfigvariant_PSRAM.cmake: -------------------------------------------------------------------------------- 1 | # Override the MicroPython board name 2 | list(APPEND MICROPY_DEF_BOARD 3 | "MICROPY_HW_ENABLE_PSRAM=1" 4 | "MICROPY_GC_SPLIT_HEAP=0" 5 | "MICROPY_HW_BOARD_NAME=\"Pimoroni Pico Plus 2 (PSRAM)\"" 6 | ) 7 | -------------------------------------------------------------------------------- /micropython/board/PIMORONI_PICO_PLUS2/pins.csv: -------------------------------------------------------------------------------- 1 | GP0,GPIO0 2 | GP1,GPIO1 3 | GP2,GPIO2 4 | GP3,GPIO3 5 | GP4,GPIO4 6 | GP5,GPIO5 7 | GP6,GPIO6 8 | GP7,GPIO7 9 | GP8,GPIO8 10 | GP9,GPIO9 11 | GP10,GPIO10 12 | GP11,GPIO11 13 | GP12,GPIO12 14 | GP13,GPIO13 15 | GP14,GPIO14 16 | GP15,GPIO15 17 | GP16,GPIO16 18 | GP17,GPIO17 19 | GP18,GPIO18 20 | GP19,GPIO19 21 | GP20,GPIO20 22 | GP21,GPIO21 23 | GP22,GPIO22 24 | GP25,GPIO25 25 | GP26,GPIO26 26 | GP27,GPIO27 27 | GP28,GPIO28 28 | GP29,GPIO29 29 | GP30,GPIO30 30 | GP31,GPIO31 31 | GP32,GPIO32 32 | GP33,GPIO33 33 | GP34,GPIO34 34 | GP35,GPIO35 35 | GP36,GPIO36 36 | GP37,GPIO37 37 | GP38,GPIO38 38 | GP39,GPIO39 39 | GP40,GPIO40 40 | GP41,GPIO41 41 | GP42,GPIO42 42 | GP43,GPIO43 43 | GP44,GPIO44 44 | GP45,GPIO45 45 | GP46,GPIO46 46 | GP47,GPIO47 47 | LED,GPIO25 -------------------------------------------------------------------------------- /micropython/board/manifest_pico2.py: -------------------------------------------------------------------------------- 1 | MODULES_PY = "../../../pimoroni-pico/micropython/modules_py" 2 | 3 | freeze(MODULES_PY, "gfx_pack.py") 4 | 5 | freeze(MODULES_PY, "pimoroni.py") 6 | freeze(MODULES_PY, "boot.py") 7 | -------------------------------------------------------------------------------- /micropython/examples/vector_clock.py: -------------------------------------------------------------------------------- 1 | 2 | import time 3 | import gc 4 | 5 | from dvhstx import DVHSTX, PEN_P8, PEN_RGB565 6 | from picovector import PicoVector, Polygon, RegularPolygon, Rectangle, ANTIALIAS_X4, ANTIALIAS_X16 7 | 8 | display = DVHSTX(PEN_P8, 640, 360) 9 | 10 | vector = PicoVector(display) 11 | vector.set_antialiasing(ANTIALIAS_X4) 12 | 13 | BG = display.create_pen(20, 20, 50) 14 | RED = display.create_pen(200, 0, 0) 15 | BLACK = display.create_pen(0, 0, 0) 16 | GREY = display.create_pen(200, 200, 200) 17 | WHITE = display.create_pen(255, 255, 255) 18 | 19 | """ 20 | # Redefine colours for a Blue clock 21 | RED = display.create_pen(200, 0, 0) 22 | BLACK = display.create_pen(135, 159, 169) 23 | GREY = display.create_pen(10, 40, 50) 24 | WHITE = display.create_pen(14, 60, 76) 25 | """ 26 | 27 | WIDTH, HEIGHT = display.get_bounds() 28 | 29 | hub = RegularPolygon(int(WIDTH / 2), int(HEIGHT / 2), 24, 5) 30 | 31 | face = RegularPolygon(int(WIDTH / 2), int(HEIGHT / 2), 48, int(HEIGHT / 2)) 32 | 33 | print(time.localtime()) 34 | 35 | last_second = None 36 | 37 | while True: 38 | t_start = time.ticks_ms() 39 | year, month, day, hour, minute, second, _, _ = time.localtime() 40 | 41 | if last_second == second: 42 | continue 43 | 44 | last_second = second 45 | 46 | display.set_pen(0) 47 | display.clear() 48 | 49 | display.set_pen(BLACK) 50 | display.circle(int(WIDTH / 2), int(HEIGHT / 2), int(HEIGHT / 2)) 51 | display.set_pen(WHITE) 52 | display.circle(int(WIDTH / 2), int(HEIGHT / 2), int(HEIGHT / 2) - 4) 53 | 54 | display.set_pen(GREY) 55 | 56 | for a in range(60): 57 | tick_mark = Rectangle(int(WIDTH / 2) - 3, 10, 6, int(HEIGHT / 48)) 58 | vector.rotate(tick_mark, 360 / 60.0 * a, int(WIDTH / 2), int(HEIGHT / 2)) 59 | vector.translate(tick_mark, 0, 2) 60 | vector.draw(tick_mark) 61 | 62 | for a in range(12): 63 | hour_mark = Rectangle(int(WIDTH / 2) - 5, 10, 10, int(HEIGHT / 10)) 64 | vector.rotate(hour_mark, 360 / 12.0 * a, int(WIDTH / 2), int(HEIGHT / 2)) 65 | vector.translate(hour_mark, 0, 2) 66 | vector.draw(hour_mark) 67 | 68 | angle_second = second * 6 69 | second_hand_length = int(HEIGHT / 2) - int(HEIGHT / 8) 70 | second_hand = Polygon((-2, -second_hand_length), (-2, int(HEIGHT / 8)), (2, int(HEIGHT / 8)), (2, -second_hand_length)) 71 | vector.rotate(second_hand, angle_second, 0, 0) 72 | vector.translate(second_hand, int(WIDTH / 2), int(HEIGHT / 2) + 5) 73 | 74 | angle_minute = minute * 6 75 | angle_minute += second / 10.0 76 | minute_hand_length = int(HEIGHT / 2) - int(HEIGHT / 24) 77 | minute_hand = Polygon((-5, -minute_hand_length), (-10, int(HEIGHT / 16)), (10, int(HEIGHT / 16)), (5, -minute_hand_length)) 78 | vector.rotate(minute_hand, angle_minute, 0, 0) 79 | vector.translate(minute_hand, int(WIDTH / 2), int(HEIGHT / 2) + 5) 80 | 81 | angle_hour = (hour % 12) * 30 82 | angle_hour += minute / 2 83 | hour_hand_length = int(HEIGHT / 2) - int(HEIGHT / 8) 84 | hour_hand = Polygon((-5, -hour_hand_length), (-10, int(HEIGHT / 16)), (10, int(HEIGHT / 16)), (5, -hour_hand_length)) 85 | vector.rotate(hour_hand, angle_hour, 0, 0) 86 | vector.translate(hour_hand, int(WIDTH / 2), int(HEIGHT / 2) + 5) 87 | 88 | display.set_pen(GREY) 89 | 90 | vector.draw(minute_hand) 91 | vector.draw(hour_hand) 92 | vector.draw(second_hand) 93 | 94 | display.set_pen(BLACK) 95 | 96 | for a in range(60): 97 | tick_mark = Rectangle(int(WIDTH / 2) - 3, 10, 6, int(HEIGHT / 48)) 98 | vector.rotate(tick_mark, 360 / 60.0 * a, int(WIDTH / 2), int(HEIGHT / 2)) 99 | vector.draw(tick_mark) 100 | 101 | for a in range(12): 102 | hour_mark = Rectangle(int(WIDTH / 2) - 5, 10, 10, int(HEIGHT / 10)) 103 | vector.rotate(hour_mark, 360 / 12.0 * a, int(WIDTH / 2), int(HEIGHT / 2)) 104 | vector.draw(hour_mark) 105 | 106 | vector.translate(minute_hand, 0, -5) 107 | vector.translate(hour_hand, 0, -5) 108 | vector.draw(minute_hand) 109 | vector.draw(hour_hand) 110 | 111 | display.set_pen(RED) 112 | vector.translate(second_hand, 0, -5) 113 | vector.draw(second_hand) 114 | vector.draw(hub) 115 | 116 | display.update() 117 | gc.collect() 118 | 119 | t_end = time.ticks_ms() 120 | print(f"Took {t_end - t_start}ms") -------------------------------------------------------------------------------- /modules/dvhstx.cmake: -------------------------------------------------------------------------------- 1 | if(NOT DEFINED PIMORONI_PICO_PATH) 2 | set(PIMORONI_PICO_PATH ../pimoroni-pico) 3 | endif() 4 | include(${CMAKE_CURRENT_LIST_DIR}/../pimoroni_pico_import.cmake) 5 | 6 | include_directories(${PIMORONI_PICO_PATH}/micropython) 7 | 8 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../") 9 | list(APPEND CMAKE_MODULE_PATH "${PIMORONI_PICO_PATH}/micropython") 10 | list(APPEND CMAKE_MODULE_PATH "${PIMORONI_PICO_PATH}/micropython/modules") 11 | 12 | # Allows us to find /pga/modules/c//micropython 13 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") 14 | 15 | set(CMAKE_C_STANDARD 11) 16 | set(CMAKE_CXX_STANDARD 17) 17 | 18 | # Essential 19 | include(pimoroni_i2c/micropython) 20 | include(pimoroni_bus/micropython) 21 | 22 | # Pico Graphics Essential 23 | include(hershey_fonts/micropython) 24 | include(bitmap_fonts/micropython) 25 | include(picovector/micropython) 26 | include(modules/picographics/micropython) 27 | 28 | # Pico Graphics Extra 29 | include(jpegdec/micropython) 30 | include(pngdec/micropython) 31 | include(qrcode/micropython/micropython) 32 | 33 | # Sensors & Breakouts 34 | include(micropython-common-breakouts) 35 | 36 | # Utility 37 | include(adcfft/micropython) 38 | 39 | # Note: cppmem is *required* for C++ code to function on MicroPython 40 | # it redirects `malloc` and `free` calls to MicroPython's heap 41 | include(cppmem/micropython) 42 | 43 | # version.py, pimoroni.py and boot.py 44 | include(modules_py/modules_py) 45 | -------------------------------------------------------------------------------- /modules/picographics/README.md: -------------------------------------------------------------------------------- 1 | # Pico Graphics 2 | 3 | Pico Graphics is our unified graphics and display library for driving displays from your Pico in MicroPython. 4 | 5 | Pico Graphics replaces the individual drivers for displays- if you're been using breakout_colorlcd, ST7789 then you'll need to update your code! 6 | 7 | - [Setting up Pico Graphics](#setting-up-pico-graphics) 8 | - [Supported Displays](#supported-displays) 9 | - [Interstate75 and Interstate75W Display modes](#interstate75-and-interstate75w-display-modes) 10 | - [Supported Graphics Modes (Pen Type)](#supported-graphics-modes-pen-type) 11 | - [Supported Rotations](#supported-rotations) 12 | - [Custom Pins](#custom-pins) 13 | - [SPI / Parallel](#spi--parallel) 14 | - [I2C](#i2c) 15 | - [Function Reference](#function-reference) 16 | - [General](#general) 17 | - [Creating and Setting Pens](#creating-and-setting-pens) 18 | - [RGB888, RGB565, RGB332, P8 and P4 modes](#rgb888-rgb565-rgb332-p8-and-p4-modes) 19 | - [Monochrome Modes](#monochrome-modes) 20 | - [Inky Frame](#inky-frame) 21 | - [Controlling the Backlight](#controlling-the-backlight) 22 | - [Clipping](#clipping) 23 | - [Clear](#clear) 24 | - [Update](#update) 25 | - [Get Bounds](#get-bounds) 26 | - [Text](#text) 27 | - [Changing The Font](#changing-the-font) 28 | - [Changing The Thickness](#changing-the-thickness) 29 | - [Drawing Text](#drawing-text) 30 | - [Basic Shapes](#basic-shapes) 31 | - [Line](#line) 32 | - [Circle](#circle) 33 | - [Rectangle](#rectangle) 34 | - [Triangle](#triangle) 35 | - [Polygon](#polygon) 36 | - [Pixels](#pixels) 37 | - [Palette Management](#palette-management) 38 | - [Utility Functions](#utility-functions) 39 | - [Sprites](#sprites) 40 | - [Loading Sprites](#loading-sprites) 41 | - [Drawing Sprites](#drawing-sprites) 42 | - [JPEG Files](#jpeg-files) 43 | 44 | ## Setting up Pico Graphics 45 | 46 | You must construct an instance of PicoGraphics with your desired display: 47 | 48 | ```python 49 | from picographics import PicoGraphics, DISPLAY_LCD_160X80 50 | 51 | display = PicoGraphics(display=DISPLAY_LCD_160X80) 52 | ``` 53 | 54 | Bear in mind that MicroPython has only 192K of RAM available- a 320x240 pixel display in RGB565 mode uses 150K! 55 | 56 | ### Supported Displays 57 | 58 | * Pico Display - 240x135 SPI LCD - `DISPLAY_PICO_DISPLAY` 59 | * Pico Display 2 - 320x240 SPI LCD - `DISPLAY_PICO_DISPLAY_2` 60 | * Tufty 2040 - 320x240 Parallel LCD - `DISPLAY_TUFTY_2040` 61 | * Pico Explorer - 240x240 SPI LCD - `DISPLAY_PICO_EXPLORER` 62 | * Enviro Plus - 240x240 SPI LCD - `DISPLAY_ENVIRO_PLUS` 63 | * 240x240 Round SPI LCD Breakout - `DISPLAY_ROUND_LCD_240X240` 64 | * 240x240 Square SPI LCD Breakout - `DISPLAY_LCD_240X240` 65 | * 160x80 SPI LCD Breakout - `DISPLAY_LCD_160X80` 66 | * 128x128 I2C OLED - `DISPLAY_I2C_OLED_128X128` 67 | * Pico Inky Pack / Badger 2040 / Badger 2040 W - 296x128 mono E ink - `DISPLAY_INKY_PACK` 68 | * Inky Frame 5.7" - 600x448 7-colour E ink - `DISPLAY_INKY_FRAME` 69 | * Inky Frame 4.0" - 640x400 7-colour E ink - `DISPLAY_INKY_FRAME_4` 70 | * Inky Frame 7.3" - 800x480 7-colour E ink - `DISPLAY_INKY_FRAME_7` 71 | * Pico GFX Pack - 128x64 mono LCD Matrix - `DISPLAY_GFX_PACK` 72 | * Galactic Unicorn - 53x11 LED Matrix - `DISPLAY_GALACTIC_UNICORN` 73 | * Interstate75 and 75W - HUB75 Matrix driver - `DISPLAY_INTERSTATE75_SIZEOFMATRIX` please read below! 74 | * Cosmic Unicorn - 32x32 LED Matrix - `DISPLAY_COSMIC_UNICORN` 75 | 76 | #### Interstate75 and Interstate75W Display modes 77 | 78 | Both the Interstate75 and Interstate75W support lots of different sizes of HUB75 matrix displays. 79 | 80 | The available display settings are listed here: 81 | 82 | * 32 x 32 Matrix - `DISPLAY_INTERSTATE75_32X32` 83 | * 64 x 32 Matrix - `DISPLAY_INTERSTATE75_64X32` 84 | * 96 x 32 Matrix - `DISPLAY_INTERSTATE75_96X32` 85 | * 128 x 32 Matrix - `DISPLAY_INTERSTATE75_128X32` 86 | * 64 x 64 Matrix - `DISPLAY_INTERSTATE75_64X64` 87 | * 128 x 64 Matrix - `DISPLAY_INTERSTATE75_128X64` 88 | * 192 x 64 Matrix - `DISPLAY_INTERSTATE75_192X64` 89 | * 256 x 64 Matrix - `DISPLAY_INTERSTATE75_256X64` 90 | 91 | ### Supported Graphics Modes (Pen Type) 92 | 93 | * 1-bit - `PEN_1BIT` - mono, used for Pico Inky Pack and i2c OLED 94 | * 3-bit - `PEN_3BIT` - 8-colour, used for Inky Frame 95 | * 4-bit - `PEN_P4` - 16-colour palette of your choice 96 | * 8-bit - `PEN_P8` - 256-colour palette of your choice 97 | * 8-bit RGB332 - `PEN_RGB332` - 256 fixed colours (3 bits red, 3 bits green, 2 bits blue) 98 | * 16-bit RGB565 - `PEN_RGB565` - 64K colours at the cost of RAM. (5 bits red, 6 bits green, 5 bits blue) 99 | * 24-bit RGB888 - `PEN_RGB888` - 16M colours at the cost of lots of RAM. (8 bits red, 8 bits green, 8 bits blue) 100 | 101 | These offer a tradeoff between RAM usage and available colours. In most cases you would probably use `RGB332` since it offers the easiest tradeoff. It's also the default for colour LCDs. 102 | 103 | Eg: 104 | 105 | ```python 106 | display = PicoGraphics(display=PICO_DISPLAY) 107 | ``` 108 | 109 | Is equivalent to: 110 | 111 | ```python 112 | display = PicoGraphics(display=PICO_DISPLAY, pen_type=PEN_RGB332) 113 | ``` 114 | 115 | ### Supported Rotations 116 | 117 | All SPI LCDs support 0, 90, 180 and 270 degree rotations. 118 | 119 | Eg: 120 | 121 | ```python 122 | display = PicoGraphics(display=PICO_DISPLAY, rotate=90) 123 | ``` 124 | 125 | ### Custom Pins 126 | 127 | #### SPI / Parallel 128 | 129 | The `pimoroni_bus` library includes `SPIBus` for SPI LCDs and `ParallelBus` for Parallel LCDs (like Tufty 2040). 130 | 131 | In most cases you'll never need to use these, but they come in useful if you're wiring breakouts to your Pico or using multiple LCDs. 132 | 133 | A custom SPI bus: 134 | 135 | ```python 136 | from pimoroni_bus import SPIBus 137 | from picographics import PicoGraphics, DISPLAY_PICO_EXPLORER, PEN_RGB332 138 | 139 | spibus = SPIBus(cs=17, dc=16, sck=18, mosi=19) 140 | 141 | display = PicoGraphics(display=DISPLAY_PICO_EXPLORER, bus=spibus, pen_type=PEN_RGB332) 142 | ``` 143 | 144 | #### I2C 145 | 146 | The `pimoroni_i2c` library includes `PimoroniI2C` which can be used to change the pins used by the mono OLED: 147 | 148 | ```python 149 | from pimoroni_i2c import PimoroniI2C 150 | from picographics import PicoGraphics, DISPLAY_I2C_OLED_128X128 151 | 152 | i2cbus = PimoroniI2C(4, 5) 153 | 154 | display = PicoGraphics(display=DISPLAY_I2C_OLED_128X128, bus=i2cbus) 155 | ``` 156 | 157 | ## Function Reference 158 | 159 | ### General 160 | 161 | #### Creating and Setting Pens 162 | 163 | ##### RGB888, RGB565, RGB332, P8 and P4 modes 164 | 165 | Create a pen colour for drawing into a screen: 166 | 167 | ```python 168 | my_pen = display.create_pen(r, g, b) 169 | ``` 170 | 171 | In RGB565 and RGB332 modes this packs the given RGB into an integer representing a colour in these formats and returns the result. 172 | 173 | In P4 and P8 modes this will consume one palette entry, or return an error if your palette is full. Palette colours are stored as RGB and converted when they are displayed on screen. 174 | 175 | You can also now specify an HSV pen, which allows a pen to be created from HSV (Hue, Saturation, Value) values between 0.0 and 1.0, avoiding the need to calculate the RGB result in Python. 176 | 177 | ```python 178 | display.create_pen_hsv(h, s, v) 179 | ``` 180 | 181 | To tell PicoGraphics which pen to use: 182 | 183 | ```python 184 | display.set_pen(my_pen) 185 | ``` 186 | 187 | This will be either an RGB332, RGB565 or RGB888 colour, or a palette index. 188 | 189 | ##### Monochrome Modes 190 | 191 | For 1BIT mode - such as for Inky Pack and the Mono OLED - pens are handled a little differently. 192 | 193 | There's no need to create one, since mapping an RGB colour to black/white is meaningless. 194 | 195 | Instead you can pick from 16 shades of grey which are automatically dithered into the PicoGraphics buffer, where: 196 | 197 | * `0` is Black, 198 | * `1 - 14` are shades of grey, 199 | * `15` is white. 200 | 201 | And just call `set_pen` with your desired shade: 202 | 203 | ```python 204 | display.set_pen(0) # Black 205 | display.set_pen(15) # White 206 | ``` 207 | 208 | Because shades 1 through 14 are created with ordered dither you should avoid using them for text, small details or lines. 209 | 210 | Dithering works by mixing black and white pixels in various patterns and quantities to fake grey shades. 211 | 212 | If you were to try and draw a single "grey" pixel it will end up either black or white depending on where it's drawn and which shade of grey you pick. 213 | 214 | ##### Inky Frame 215 | 216 | Inky Frame is a special case- the display itself supports only 7 (8 if you include its cleaning "clear" colour, which we call Taupe) colours. 217 | 218 | These are: 219 | 220 | * `BLACK` = 0 221 | * `WHITE` = 1 222 | * `GREEN` = 2 223 | * `BLUE` = 3 224 | * `RED` = 4 225 | * `YELLOW` = 5 226 | * `ORANGE` = 6 227 | * `TAUPE` = 7 228 | 229 | #### Controlling the Backlight 230 | 231 | You can set the display backlight brightness between `0.0` and `1.0`: 232 | 233 | ```python 234 | display.set_backlight(0.5) 235 | ``` 236 | 237 | #### Clipping 238 | 239 | Set the clipping bounds for drawing: 240 | 241 | ```python 242 | display.set_clip(x, y, w, h) 243 | ``` 244 | 245 | Remove the clipping bounds: 246 | 247 | ```python 248 | display.remove_clip() 249 | ``` 250 | 251 | #### Clear 252 | 253 | Clear the display to the current pen colour: 254 | 255 | ```python 256 | display.clear() 257 | ``` 258 | 259 | This is equivalent to: 260 | 261 | ```python 262 | w, h = display.get_bounds() 263 | display.rectangle(0, 0, w, h) 264 | ``` 265 | 266 | You can clear portions of the screen with rectangles to save time redrawing things like JPEGs or complex graphics. 267 | 268 | #### Update 269 | 270 | Send the contents of your Pico Graphics buffer to your screen: 271 | 272 | ```python 273 | display.update() 274 | ``` 275 | 276 | If you are using a Galactic Unicorn, then the process for updating the display is different. Instead of the above, do: 277 | 278 | ```python 279 | galactic_unicorn.update(display) 280 | ``` 281 | 282 | #### Get Bounds 283 | 284 | You can use `get_bounds()` to get the width and height of the display - useful for writing code that's portable across different displays. 285 | 286 | ```python 287 | WIDTH, HEIGHT = display.get_bounds() 288 | ``` 289 | 290 | ### Text 291 | 292 | #### Changing The Font 293 | 294 | Change the font: 295 | 296 | ```python 297 | display.set_font(font) 298 | ``` 299 | 300 | Bitmap fonts. 301 | These are aligned from their top-left corner. 302 | 303 | * `bitmap6` 304 | * `bitmap8` 305 | * `bitmap14_outline` 306 | 307 | Vector (Hershey) fonts. 308 | These are aligned horizontally (x) to their left edge, but vertically (y) to their midline excluding descenders [i.e., aligned at top edge of lower case letter m]. At `scale=1`, the top edge of upper case letters is 10 pixels above the specified `y`, text baseline is 10 pixels below the specified `y`, and descenders go down to 20 pixels below the specified `y`. 309 | 310 | * `sans` 311 | * `gothic` 312 | * `cursive` 313 | * `serif_italic` 314 | * `serif` 315 | 316 | #### Changing The Thickness 317 | 318 | Vector (Hershey) fonts are drawn with individual lines. By default these are 1px thick, making for very thin and typically illegible text. 319 | 320 | To change the thickness of lines used for Vector fonts, use the `set_thickness` method: 321 | 322 | ```python 323 | display.set_thickness(n) 324 | ``` 325 | 326 | Drawing thick text involves setting a lot more pixels and may slow your drawing down considerably. Be careful how and where you use this. 327 | 328 | #### Drawing Text 329 | 330 | Write some text: 331 | 332 | ```python 333 | display.text(text, x, y, wordwrap, scale, angle, spacing) 334 | ``` 335 | 336 | * `text` - the text string to draw 337 | * `x` - the destination X coordinate 338 | * `y` - the destination Y coordinate 339 | * `wordwrap` - number of pixels width before trying to break text into multiple lines 340 | * `scale` - size 341 | * `angle` - rotation angle (Vector only!) 342 | * `spacing` - letter spacing 343 | 344 | Text scale can be a whole number (integer) for Bitmap fonts, or a decimal (float) for Vector (Hershey) fonts. 345 | 346 | For example: 347 | 348 | ```python 349 | display.set_font("bitmap8") 350 | display.text("Hello World", 0, 0, scale=2) 351 | ``` 352 | Draws "Hello World" in a 16px tall, 2x scaled version of the `bitmap8` font. 353 | 354 | Sometimes you might want to measure a text string for centering or alignment on screen, you can do this with: 355 | 356 | ```python 357 | width = display.measure_text(text, scale, spacing) 358 | ``` 359 | 360 | The height of each Bitmap font is explicit in its name. 361 | 362 | Write a single character: 363 | 364 | ```python 365 | display.character(char, x, y, scale) 366 | ``` 367 | 368 | Specify `char` using a [decimal ASCII code](https://www.ascii-code.com/). Note not all characters are supported. 369 | 370 | For example: 371 | ```python 372 | display.set_font("bitmap8") 373 | display.character(38, 0, 0, scale=2) 374 | ``` 375 | Draws an ampersand in a 16px tall, 2x scaled version of the 'bitmap8' font. 376 | 377 | ### Basic Shapes 378 | 379 | #### Line 380 | 381 | To draw a straight line at any angle between two specified points: 382 | 383 | ```python 384 | display.line(x1, y1, x2, y2) 385 | ``` 386 | 387 | The X1/Y1 and X2/Y2 coordinates describe the start and end of the line respectively. 388 | 389 | If you need a thicker line, for an outline or UI elements you can supply a fifth parameter - thickness - like so: 390 | 391 | ```python 392 | display.line(x1, y1, x2, y2, thickness) 393 | ``` 394 | 395 | #### Circle 396 | 397 | To draw a circle: 398 | 399 | ```python 400 | display.circle(x, y, r) 401 | ``` 402 | 403 | * `x` - the destination X coordinate 404 | * `y` - the destination Y coordinate 405 | * `r` - the radius 406 | 407 | The X/Y coordinates describe the center of your circle. 408 | 409 | #### Rectangle 410 | 411 | ```python 412 | display.rectangle(x, y, w, h) 413 | ``` 414 | 415 | * `x` - the destination X coordinate 416 | * `y` - the destination Y coordinate 417 | * `w` - the width 418 | * `h` - the height 419 | 420 | #### Triangle 421 | 422 | ```python 423 | display.triangle(x1, y1, x2, y2, x3, y3) 424 | ``` 425 | 426 | The three pairs of X/Y coordinates describe each point of the triangle. 427 | 428 | #### Polygon 429 | 430 | To draw other shapes, you can provide a list of points to `polygon`: 431 | 432 | ```python 433 | display.polygon([ 434 | (0, 10), 435 | (20, 10), 436 | (20, 0), 437 | (30, 20), 438 | (20, 30), 439 | (20, 20), 440 | (0, 20), 441 | ]) 442 | ``` 443 | 444 | ### Pixels 445 | 446 | Setting individual pixels is slow, but you can do it with: 447 | 448 | ```python 449 | display.pixel(x, y) 450 | ``` 451 | 452 | You can draw a horizontal span of pixels a little faster with: 453 | 454 | ```python 455 | display.pixel_span(x, y, length) 456 | ``` 457 | 458 | (use `display.line()` instead if you want to draw a straight line at any angle) 459 | 460 | ### Palette Management 461 | 462 | Intended for P4 and P8 modes. 463 | 464 | You have a 16-color and 256-color palette respectively. 465 | 466 | Set n elements in the palette from a list of RGB tuples: 467 | 468 | ```python 469 | display.set_palette([ 470 | (r, g, b), 471 | (r, g, b), 472 | (r, g, b) 473 | ]) 474 | ``` 475 | 476 | Update an entry in the P4 or P8 palette with the given colour. 477 | 478 | ```python 479 | display.update_pen(index, r, g, b) 480 | ``` 481 | 482 | This is stored internally as RGB and converted to whatever format your screen requires when displayed. 483 | 484 | Reset a pen back to its default value (black, marked unused): 485 | 486 | ```python 487 | display.reset_pen(index) 488 | ``` 489 | 490 | #### Utility Functions 491 | 492 | Sometimes it can be useful to convert between colour formats: 493 | 494 | * `RGB332_to_RGB` 495 | * `RGB_to_RGB332` 496 | * `RGB565_to_RGB` 497 | * `RGB_to_RGB565` 498 | 499 | 500 | ### Sprites 501 | 502 | Pico Display has very limited support for sprites in RGB332 mode. 503 | 504 | Sprites must be 8x8 pixels arranged in a 128x128 pixel spritesheet. 1-bit transparency is handled by electing a single colour to skip over. 505 | 506 | We've prepared some RGB332-compatible sprite assets for you, but you can use `spritesheet-to-rgb332.py ` to convert your own. 507 | 508 | #### Loading Sprites 509 | 510 | You'll need to include the [pen_type](#supported-graphics-modes-pen-type) in the import statement, and define the pen_type before using loading the spritesheet: 511 | 512 | ``` python 513 | from picographics import PicoGraphics, PEN_RGB565, PEN_RGB332 514 | 515 | display = PicoGraphics(display=PICO_DISPLAY, pen_type=PEN_RGB332) 516 | ``` 517 | Use Thonny to upload your `spritesheet.rgb332` file onto your Pico. Then load it into Pico Graphics: 518 | 519 | ```python 520 | display.load_spritesheet("s4m_ur4i-dingbads.rgb332") 521 | ``` 522 | 523 | and then update the display, to show the sprite: 524 | 525 | ```python 526 | display.update() 527 | ``` 528 | 529 | #### Drawing Sprites 530 | 531 | And finally display a sprite: 532 | 533 | ```python 534 | display.sprite(0, 0, 0, 0) 535 | ``` 536 | 537 | These arguments for `sprite` are as follows: 538 | 539 | 1. Sprite X position (from 0-15) - this selects the horizontal location of an 8x8 sprite from your 128x128 pixel spritesheet. 540 | 2. Sprite Y position (from 0-15) 541 | 3. Destination X - where to draw on your screen horizontally 542 | 4. Destination Y = where to draw on your screen vertically 543 | 5. Scale (optional) - an integer scale value, 1 = 8x8, 2 = 16x16 etc. 544 | 6. Transparent (optional) - specify a colour to treat as transparent 545 | 546 | ### JPEG Files 547 | 548 | We've included BitBank's JPEGDEC - https://github.com/bitbank2/JPEGDEC - so you can display JPEG files on your LCDs. 549 | 550 | Eg: 551 | 552 | ```python 553 | import picographics 554 | import jpegdec 555 | 556 | display = picographics.PicoGraphics(display=picographics.DISPLAY_PICO_EXPLORER) 557 | 558 | # Create a new JPEG decoder for our PicoGraphics 559 | j = jpegdec.JPEG(display) 560 | 561 | # Open the JPEG file 562 | j.open_file("filename.jpeg") 563 | 564 | # Decode the JPEG 565 | j.decode(0, 0, jpegdec.JPEG_SCALE_FULL, dither=True) 566 | 567 | # Display the result 568 | display.update() 569 | ``` 570 | 571 | JPEG files must be small enough to load into RAM for decoding, and must *not* be progressive. 572 | 573 | JPEG files will be automatically dithered in RGB332 mode. 574 | 575 | In P4 and P8 modes JPEGs are dithered to your custom colour palette. Their appearance of an image will vary based on the colours you choose. 576 | 577 | The arguments for `decode` are as follows: 578 | 579 | 1. Decode X - where to place the decoded JPEG on screen 580 | 2. Decode Y 581 | 3. Flags - one of `JPEG_SCALE_FULL`, `JPEG_SCALE_HALF`, `JPEG_SCALE_QUARTER` or `JPEG_SCALE_EIGHTH` 582 | 4. If you want to turn off dither altogether, try `dither=False`. This is useful if you want to [pre-dither your images](https://ditherit.com/) or for artsy posterization effects. 583 | -------------------------------------------------------------------------------- /modules/picographics/micropython.cmake: -------------------------------------------------------------------------------- 1 | set(MOD_NAME picographics) 2 | string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) 3 | add_library(usermod_${MOD_NAME} INTERFACE) 4 | 5 | get_filename_component(PICOVISION_PATH ${CMAKE_CURRENT_LIST_DIR}/../.. ABSOLUTE) 6 | 7 | target_sources(usermod_${MOD_NAME} INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c 9 | ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp 10 | ${PICOVISION_PATH}/drivers/dvhstx/dvhstx.cpp 11 | ${PICOVISION_PATH}/drivers/dvhstx/dvi.cpp 12 | ${PICOVISION_PATH}/drivers/dvhstx/intel_one_mono_2bpp.c 13 | ${PIMORONI_PICO_PATH}/libraries/pico_graphics/pico_graphics.cpp 14 | ${PICOVISION_PATH}/libraries/pico_graphics/pico_graphics_pen_dvhstx_rgb565.cpp 15 | ${PICOVISION_PATH}/libraries/pico_graphics/pico_graphics_pen_dvhstx_p8.cpp 16 | ${PIMORONI_PICO_PATH}/libraries/pico_graphics/types.cpp 17 | ) 18 | 19 | # MicroPython compiles with -Os by default, these functions are critical path enough that -O2 is worth it (note -O3 is slower in this case) 20 | set_source_files_properties(${PICOVISION_PATH}/drivers/dvhstx/dvhstx.cpp PROPERTIES COMPILE_OPTIONS "-O2") 21 | 22 | 23 | target_include_directories(usermod_${MOD_NAME} INTERFACE 24 | ${CMAKE_CURRENT_LIST_DIR} 25 | ${PICOVISION_PATH} 26 | ${PICOVISION_PATH}/drivers/dvhstx 27 | ${PICOVISION_PATH}/libraries/pico_graphics # for pico_graphics_dv.hpp 28 | ${PIMORONI_PICO_PATH}/libraries/pico_graphics # for pico_graphics.hpp 29 | # ${PIMORONI_PICO_PATH}/libraries/pngdec 30 | ) 31 | 32 | target_compile_definitions(usermod_${MOD_NAME} INTERFACE 33 | -DMODULE_${MOD_NAME_UPPER}_ENABLED=1 34 | ) 35 | 36 | if (SUPPORT_WIDE_MODES) 37 | target_compile_definitions(usermod_${MOD_NAME} INTERFACE 38 | -DSUPPORT_WIDE_MODES=1 39 | ) 40 | endif() 41 | 42 | target_link_libraries(usermod INTERFACE usermod_${MOD_NAME} hardware_vreg) 43 | 44 | set_source_files_properties( 45 | ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c 46 | PROPERTIES COMPILE_FLAGS 47 | "-Wno-discarded-qualifiers" 48 | ) 49 | -------------------------------------------------------------------------------- /modules/picographics/picographics.c: -------------------------------------------------------------------------------- 1 | #include "picographics.h" 2 | 3 | // Class Methods 4 | MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_update_obj, ModPicoGraphics_update); 5 | 6 | // Palette management 7 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_update_pen_obj, 5, 5, ModPicoGraphics_update_pen); 8 | MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_reset_pen_obj, ModPicoGraphics_reset_pen); 9 | MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_set_palette_obj, 2, ModPicoGraphics_set_palette); 10 | 11 | // Pen 12 | MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_pen_obj, ModPicoGraphics_set_pen); 13 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_create_pen_obj, 4, 4, ModPicoGraphics_create_pen); 14 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_create_pen_hsv_obj, 4, 4, ModPicoGraphics_create_pen_hsv); 15 | MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_thickness_obj, ModPicoGraphics_set_thickness); 16 | 17 | // Alpha Blending 18 | MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_bg_obj, ModPicoGraphics_set_bg); 19 | MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_blend_mode_obj, ModPicoGraphics_set_blend_mode); 20 | 21 | // Depth 22 | MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_depth_obj, ModPicoGraphics_set_depth); 23 | 24 | // Primitives 25 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_set_clip_obj, 5, 5, ModPicoGraphics_set_clip); 26 | MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_remove_clip_obj, ModPicoGraphics_remove_clip); 27 | MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_clear_obj, ModPicoGraphics_clear); 28 | MP_DEFINE_CONST_FUN_OBJ_3(ModPicoGraphics_pixel_obj, ModPicoGraphics_pixel); 29 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_pixel_span_obj, 4, 4, ModPicoGraphics_pixel_span); 30 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_rectangle_obj, 5, 5, ModPicoGraphics_rectangle); 31 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_circle_obj, 4, 4, ModPicoGraphics_circle); 32 | MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_character_obj, 1, ModPicoGraphics_character); 33 | MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_text_obj, 1, ModPicoGraphics_text); 34 | MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_measure_text_obj, 1, ModPicoGraphics_measure_text); 35 | MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_polygon_obj, 2, ModPicoGraphics_polygon); 36 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_triangle_obj, 7, 7, ModPicoGraphics_triangle); 37 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_line_obj, 5, 6, ModPicoGraphics_line); 38 | 39 | // Sprites 40 | MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_load_sprite_obj, 2, ModPicoGraphics_load_sprite); 41 | MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_display_sprite_obj, 5, ModPicoGraphics_display_sprite); 42 | MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_clear_sprite_obj, ModPicoGraphics_clear_sprite); 43 | 44 | // Utility 45 | MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_get_bounds_obj, ModPicoGraphics_get_bounds); 46 | MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_font_obj, ModPicoGraphics_set_font); 47 | 48 | MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics__del__obj, ModPicoGraphics__del__); 49 | 50 | // Loop 51 | MP_DEFINE_CONST_FUN_OBJ_3(ModPicoGraphics_loop_obj, ModPicoGraphics_loop); 52 | 53 | 54 | static const mp_rom_map_elem_t ModPicoGraphics_locals_dict_table[] = { 55 | { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&ModPicoGraphics_pixel_obj) }, 56 | { MP_ROM_QSTR(MP_QSTR_set_pen), MP_ROM_PTR(&ModPicoGraphics_set_pen_obj) }, 57 | { MP_ROM_QSTR(MP_QSTR_set_thickness), MP_ROM_PTR(&ModPicoGraphics_set_thickness_obj) }, 58 | { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&ModPicoGraphics_clear_obj) }, 59 | 60 | { MP_ROM_QSTR(MP_QSTR_set_bg), MP_ROM_PTR(&ModPicoGraphics_set_bg_obj) }, 61 | { MP_ROM_QSTR(MP_QSTR_set_blend_mode), MP_ROM_PTR(&ModPicoGraphics_set_blend_mode_obj) }, 62 | 63 | { MP_ROM_QSTR(MP_QSTR_set_depth), MP_ROM_PTR(&ModPicoGraphics_set_depth_obj) }, 64 | 65 | { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&ModPicoGraphics_update_obj) }, 66 | { MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&ModPicoGraphics_set_clip_obj) }, 67 | { MP_ROM_QSTR(MP_QSTR_remove_clip), MP_ROM_PTR(&ModPicoGraphics_remove_clip_obj) }, 68 | { MP_ROM_QSTR(MP_QSTR_pixel_span), MP_ROM_PTR(&ModPicoGraphics_pixel_span_obj) }, 69 | { MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&ModPicoGraphics_rectangle_obj) }, 70 | { MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&ModPicoGraphics_circle_obj) }, 71 | { MP_ROM_QSTR(MP_QSTR_character), MP_ROM_PTR(&ModPicoGraphics_character_obj) }, 72 | { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&ModPicoGraphics_text_obj) }, 73 | { MP_ROM_QSTR(MP_QSTR_measure_text), MP_ROM_PTR(&ModPicoGraphics_measure_text_obj) }, 74 | { MP_ROM_QSTR(MP_QSTR_polygon), MP_ROM_PTR(&ModPicoGraphics_polygon_obj) }, 75 | { MP_ROM_QSTR(MP_QSTR_triangle), MP_ROM_PTR(&ModPicoGraphics_triangle_obj) }, 76 | { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&ModPicoGraphics_line_obj) }, 77 | 78 | { MP_ROM_QSTR(MP_QSTR_create_pen), MP_ROM_PTR(&ModPicoGraphics_create_pen_obj) }, 79 | { MP_ROM_QSTR(MP_QSTR_create_pen_hsv), MP_ROM_PTR(&ModPicoGraphics_create_pen_hsv_obj) }, 80 | { MP_ROM_QSTR(MP_QSTR_update_pen), MP_ROM_PTR(&ModPicoGraphics_update_pen_obj) }, 81 | { MP_ROM_QSTR(MP_QSTR_reset_pen), MP_ROM_PTR(&ModPicoGraphics_reset_pen_obj) }, 82 | { MP_ROM_QSTR(MP_QSTR_set_palette), MP_ROM_PTR(&ModPicoGraphics_set_palette_obj) }, 83 | 84 | { MP_ROM_QSTR(MP_QSTR_get_bounds), MP_ROM_PTR(&ModPicoGraphics_get_bounds_obj) }, 85 | { MP_ROM_QSTR(MP_QSTR_set_font), MP_ROM_PTR(&ModPicoGraphics_set_font_obj) }, 86 | 87 | // { MP_ROM_QSTR(MP_QSTR_loop), MP_ROM_PTR(&ModPicoGraphics_loop_obj) }, 88 | 89 | { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ModPicoGraphics__del__obj) }, 90 | }; 91 | static MP_DEFINE_CONST_DICT(ModPicoGraphics_locals_dict, ModPicoGraphics_locals_dict_table); 92 | 93 | /***** Class Definition *****/ 94 | #ifdef MP_DEFINE_CONST_OBJ_TYPE 95 | MP_DEFINE_CONST_OBJ_TYPE( 96 | ModPicoGraphics_type, 97 | MP_QSTR_dvhstx, 98 | MP_TYPE_FLAG_NONE, 99 | make_new, ModPicoGraphics_make_new, 100 | locals_dict, (mp_obj_dict_t*)&ModPicoGraphics_locals_dict 101 | ); 102 | #else 103 | const mp_obj_type_t ModPicoGraphics_type = { 104 | { &mp_type_type }, 105 | .name = MP_QSTR_dvhstx, 106 | .make_new = ModPicoGraphics_make_new, 107 | .locals_dict = (mp_obj_dict_t*)&ModPicoGraphics_locals_dict, 108 | }; 109 | #endif 110 | 111 | /***** Module Globals *****/ 112 | static const mp_map_elem_t picographics_globals_table[] = { 113 | { MP_ROM_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_dvhstx) }, 114 | { MP_ROM_QSTR(MP_QSTR_DVHSTX), (mp_obj_t)&ModPicoGraphics_type }, 115 | 116 | { MP_ROM_QSTR(MP_QSTR_PEN_RGB888), MP_ROM_INT(PEN_RGB888) }, 117 | { MP_ROM_QSTR(MP_QSTR_PEN_RGB565), MP_ROM_INT(PEN_RGB565) }, 118 | { MP_ROM_QSTR(MP_QSTR_PEN_P8), MP_ROM_INT(PEN_P8) }, 119 | 120 | { MP_ROM_QSTR(MP_QSTR_BLEND_TARGET), MP_ROM_INT(0) }, 121 | { MP_ROM_QSTR(MP_QSTR_BLEND_FIXED), MP_ROM_INT(1) }, 122 | 123 | { MP_ROM_QSTR(MP_QSTR_SPRITE_OVERWRITE), MP_ROM_INT(0) }, 124 | { MP_ROM_QSTR(MP_QSTR_SPRITE_UNDER), MP_ROM_INT(1) }, 125 | { MP_ROM_QSTR(MP_QSTR_SPRITE_OVER), MP_ROM_INT(2) }, 126 | { MP_ROM_QSTR(MP_QSTR_SPRITE_BLEND_UNDER), MP_ROM_INT(3) }, 127 | { MP_ROM_QSTR(MP_QSTR_SPRITE_BLEND_OVER), MP_ROM_INT(4) }, 128 | 129 | #if SUPPORT_WIDE_MODES 130 | { MP_ROM_QSTR(MP_QSTR_WIDESCREEN), MP_ROM_TRUE }, 131 | #else 132 | { MP_ROM_QSTR(MP_QSTR_WIDESCREEN), MP_ROM_FALSE }, 133 | #endif 134 | }; 135 | static MP_DEFINE_CONST_DICT(mp_module_picographics_globals, picographics_globals_table); 136 | 137 | /***** Module Definition *****/ 138 | const mp_obj_module_t picographics_user_cmodule = { 139 | .base = { &mp_type_module }, 140 | .globals = (mp_obj_dict_t*)&mp_module_picographics_globals, 141 | }; 142 | 143 | #if MICROPY_VERSION <= 70144 144 | MP_REGISTER_MODULE(MP_QSTR_dvhstx, picographics_user_cmodule, MODULE_PICOGRAPHICS_ENABLED); 145 | #else 146 | MP_REGISTER_MODULE(MP_QSTR_dvhstx, picographics_user_cmodule); 147 | #endif 148 | -------------------------------------------------------------------------------- /modules/picographics/picographics.cpp: -------------------------------------------------------------------------------- 1 | #include "drivers/dvhstx/dvhstx.hpp" 2 | #include "libraries/pico_graphics/pico_graphics_dvhstx.hpp" 3 | #include "common/pimoroni_common.hpp" 4 | 5 | #include "micropython/modules/util.hpp" 6 | 7 | using namespace pimoroni; 8 | 9 | extern "C" { 10 | #include "picographics.h" 11 | #include "pimoroni_i2c.h" 12 | #include "py/stream.h" 13 | #include "py/reader.h" 14 | #include "py/objarray.h" 15 | #include "extmod/vfs.h" 16 | 17 | const std::string_view mp_obj_to_string_r(const mp_obj_t &obj) { 18 | if(mp_obj_is_str_or_bytes(obj)) { 19 | GET_STR_DATA_LEN(obj, str, str_len); 20 | return std::string_view((const char*)str, str_len); 21 | } 22 | mp_raise_TypeError("can't convert object to str implicitly"); 23 | } 24 | 25 | void __printf_debug_flush() { 26 | for(auto i = 0u; i < 10; i++) { 27 | sleep_ms(2); 28 | mp_event_handle_nowait(); 29 | } 30 | } 31 | 32 | int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args); 33 | 34 | void dvhstx_debug(const char *fmt, ...) { 35 | va_list ap; 36 | va_start(ap, fmt); 37 | int ret = mp_vprintf(&mp_plat_print, fmt, ap); 38 | va_end(ap); 39 | __printf_debug_flush(); 40 | (void)ret; 41 | } 42 | 43 | static DVHSTX dv_display; 44 | 45 | typedef struct _ModPicoGraphics_obj_t { 46 | mp_obj_base_t base; 47 | PicoGraphicsDVHSTX *graphics; 48 | DVHSTX *display; 49 | } ModPicoGraphics_obj_t; 50 | 51 | size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint height) { 52 | switch(pen_type) { 53 | //case PEN_RGB888: 54 | //return PicoGraphics_PenDVHSTX_RGB888::buffer_size(width, height); 55 | case PEN_RGB565: 56 | return PicoGraphics_PenDVHSTX_RGB565::buffer_size(width, height); 57 | case PEN_P8: 58 | return PicoGraphics_PenDVHSTX_P8::buffer_size(width, height); 59 | default: 60 | return 0; 61 | } 62 | } 63 | 64 | mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { 65 | ModPicoGraphics_obj_t *self = nullptr; 66 | 67 | enum { ARG_pen_type, ARG_width, ARG_height }; 68 | static const mp_arg_t allowed_args[] = { 69 | { MP_QSTR_pen_type, MP_ARG_INT, { .u_int = PEN_P8 } }, 70 | { MP_QSTR_width, MP_ARG_INT, { .u_int = 320 } }, 71 | { MP_QSTR_height, MP_ARG_INT, { .u_int = 240 } } 72 | }; 73 | 74 | // Parse args. 75 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 76 | mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); 77 | 78 | self = mp_obj_malloc_with_finaliser(ModPicoGraphics_obj_t, &ModPicoGraphics_type); 79 | self->base.type = &ModPicoGraphics_type; 80 | 81 | bool status = false; 82 | int width = args[ARG_width].u_int; 83 | int height = args[ARG_height].u_int; 84 | int pen_type = args[ARG_pen_type].u_int; 85 | 86 | dvhstx_debug("DVHSTX create display\n"); 87 | 88 | // Create an instance of the graphics library and DV display driver 89 | switch((PicoGraphicsPenType)pen_type) { 90 | case PEN_RGB888: 91 | //self->graphics = m_new_class(PicoGraphics_PenDVHSTX_RGB888, width, height, dv_display); 92 | //status = dv_display.init(width, height, DVHSTX::MODE_RGB888); 93 | break; 94 | case PEN_RGB565: 95 | self->graphics = m_new_class(PicoGraphics_PenDVHSTX_RGB565, width, height, dv_display); 96 | status = dv_display.init(width, height, DVHSTX::MODE_RGB565); 97 | break; 98 | case PEN_P8: 99 | self->graphics = m_new_class(PicoGraphics_PenDVHSTX_P8, width, height, dv_display); 100 | status = dv_display.init(width, height, DVHSTX::MODE_PALETTE); 101 | break; 102 | default: 103 | break; 104 | } 105 | 106 | if (!status) { 107 | mp_raise_msg(&mp_type_RuntimeError, "PicoVision: Unsupported Mode!"); 108 | } 109 | 110 | dvhstx_debug("DVHSTX created\n"); 111 | 112 | self->display = &dv_display; 113 | 114 | // Clear each buffer 115 | for(auto x = 0u; x < 2u; x++){ 116 | self->graphics->set_pen(0); 117 | self->graphics->clear(); 118 | dv_display.flip_now(); 119 | } 120 | 121 | return MP_OBJ_FROM_PTR(self); 122 | 123 | } 124 | 125 | mp_obj_t ModPicoGraphics__del__(mp_obj_t self_in) { 126 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 127 | 128 | dv_display.reset(); 129 | if (self->graphics) { 130 | m_del_class(PicoGraphicsDVHSTX, self->graphics); 131 | self->graphics = nullptr; 132 | } 133 | return mp_const_none; 134 | } 135 | 136 | mp_obj_t ModPicoGraphics_set_font(mp_obj_t self_in, mp_obj_t font) { 137 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 138 | self->graphics->set_font(mp_obj_to_string_r(font)); 139 | return mp_const_none; 140 | } 141 | 142 | mp_obj_t ModPicoGraphics_get_bounds(mp_obj_t self_in) { 143 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 144 | mp_obj_t tuple[2] = { 145 | mp_obj_new_int(self->graphics->bounds.w), 146 | mp_obj_new_int(self->graphics->bounds.h) 147 | }; 148 | return mp_obj_new_tuple(2, tuple); 149 | } 150 | 151 | mp_obj_t ModPicoGraphics_update(mp_obj_t self_in) { 152 | (void)self_in; 153 | dv_display.flip_blocking(); 154 | return mp_const_none; 155 | } 156 | 157 | mp_obj_t ModPicoGraphics_module_RGB332_to_RGB(mp_obj_t rgb332) { 158 | RGB c((RGB332)mp_obj_get_int(rgb332)); 159 | mp_obj_t t[] = { 160 | mp_obj_new_int(c.r), 161 | mp_obj_new_int(c.g), 162 | mp_obj_new_int(c.b), 163 | }; 164 | return mp_obj_new_tuple(3, t); 165 | } 166 | 167 | mp_obj_t ModPicoGraphics_module_RGB565_to_RGB(mp_obj_t rgb565) { 168 | RGB c((RGB565)mp_obj_get_int(rgb565)); 169 | mp_obj_t t[] = { 170 | mp_obj_new_int(c.r), 171 | mp_obj_new_int(c.g), 172 | mp_obj_new_int(c.b), 173 | }; 174 | return mp_obj_new_tuple(3, t); 175 | } 176 | 177 | mp_obj_t ModPicoGraphics_module_RGB_to_RGB332(mp_obj_t r, mp_obj_t g, mp_obj_t b) { 178 | return mp_obj_new_int(RGB( 179 | mp_obj_get_int(r), 180 | mp_obj_get_int(g), 181 | mp_obj_get_int(b) 182 | ).to_rgb332()); 183 | } 184 | 185 | mp_obj_t ModPicoGraphics_module_RGB_to_RGB565(mp_obj_t r, mp_obj_t g, mp_obj_t b) { 186 | return mp_obj_new_int(RGB( 187 | mp_obj_get_int(r), 188 | mp_obj_get_int(g), 189 | mp_obj_get_int(b) 190 | ).to_rgb565()); 191 | } 192 | 193 | mp_obj_t ModPicoGraphics_set_pen(mp_obj_t self_in, mp_obj_t pen) { 194 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 195 | 196 | self->graphics->set_pen(mp_obj_get_int(pen)); 197 | 198 | return mp_const_none; 199 | } 200 | 201 | mp_obj_t ModPicoGraphics_set_bg(mp_obj_t self_in, mp_obj_t pen) { 202 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 203 | 204 | self->graphics->set_bg(mp_obj_get_int(pen)); 205 | 206 | return mp_const_none; 207 | } 208 | 209 | mp_obj_t ModPicoGraphics_set_blend_mode(mp_obj_t self_in, mp_obj_t pen) { 210 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 211 | 212 | self->graphics->set_blend_mode((BlendMode)mp_obj_get_int(pen)); 213 | 214 | return mp_const_none; 215 | } 216 | 217 | mp_obj_t ModPicoGraphics_set_depth(mp_obj_t self_in, mp_obj_t depth) { 218 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 219 | 220 | self->graphics->set_depth(mp_obj_get_int(depth)); 221 | 222 | return mp_const_none; 223 | } 224 | 225 | mp_obj_t ModPicoGraphics_reset_pen(mp_obj_t self_in, mp_obj_t pen) { 226 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 227 | 228 | self->graphics->reset_pen(mp_obj_get_int(pen)); 229 | 230 | return mp_const_none; 231 | } 232 | 233 | mp_obj_t ModPicoGraphics_update_pen(size_t n_args, const mp_obj_t *args) { 234 | enum { ARG_self, ARG_i, ARG_r, ARG_g, ARG_b }; 235 | 236 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); 237 | 238 | self->graphics->update_pen( 239 | mp_obj_get_int(args[ARG_i]) & 0xff, 240 | mp_obj_get_int(args[ARG_r]) & 0xff, 241 | mp_obj_get_int(args[ARG_g]) & 0xff, 242 | mp_obj_get_int(args[ARG_b]) & 0xff 243 | ); 244 | 245 | return mp_const_none; 246 | } 247 | 248 | mp_obj_t ModPicoGraphics_create_pen(size_t n_args, const mp_obj_t *args) { 249 | enum { ARG_self, ARG_r, ARG_g, ARG_b }; 250 | 251 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); 252 | 253 | int result = self->graphics->create_pen( 254 | mp_obj_get_int(args[ARG_r]) & 0xff, 255 | mp_obj_get_int(args[ARG_g]) & 0xff, 256 | mp_obj_get_int(args[ARG_b]) & 0xff 257 | ); 258 | 259 | if (result == -1) mp_raise_ValueError("create_pen failed. No matching colour or space in palette!"); 260 | 261 | return mp_obj_new_int(result); 262 | } 263 | 264 | mp_obj_t ModPicoGraphics_create_pen_hsv(size_t n_args, const mp_obj_t *args) { 265 | enum { ARG_self, ARG_h, ARG_s, ARG_v }; 266 | 267 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); 268 | int result = self->graphics->create_pen_hsv( 269 | mp_obj_get_float(args[ARG_h]), 270 | mp_obj_get_float(args[ARG_s]), 271 | mp_obj_get_float(args[ARG_v]) 272 | ); 273 | 274 | if (result == -1) mp_raise_ValueError("create_pen failed. No matching colour or space in palette!"); 275 | 276 | return mp_obj_new_int(result); 277 | } 278 | 279 | mp_obj_t ModPicoGraphics_set_thickness(mp_obj_t self_in, mp_obj_t pen) { 280 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 281 | 282 | self->graphics->set_thickness(mp_obj_get_int(pen)); 283 | 284 | return mp_const_none; 285 | } 286 | 287 | mp_obj_t ModPicoGraphics_set_palette(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { 288 | size_t num_tuples = n_args - 1; 289 | const mp_obj_t *tuples = pos_args + 1; 290 | 291 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(pos_args[0], ModPicoGraphics_obj_t); 292 | 293 | // Check if there is only one argument, which might be a list 294 | if(n_args == 2) { 295 | if(mp_obj_is_type(pos_args[1], &mp_type_list)) { 296 | mp_obj_list_t *points = MP_OBJ_TO_PTR2(pos_args[1], mp_obj_list_t); 297 | 298 | if(points->len <= 0) mp_raise_ValueError("set_palette(): cannot provide an empty list"); 299 | 300 | num_tuples = points->len; 301 | tuples = points->items; 302 | } 303 | else { 304 | mp_raise_TypeError("set_palette(): can't convert object to list"); 305 | } 306 | } 307 | 308 | for(size_t i = 0; i < num_tuples; i++) { 309 | mp_obj_t obj = tuples[i]; 310 | if(!mp_obj_is_type(obj, &mp_type_tuple)) mp_raise_ValueError("set_palette(): can't convert object to tuple"); 311 | 312 | mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(obj, mp_obj_tuple_t); 313 | 314 | if(tuple->len != 3) mp_raise_ValueError("set_palette(): tuple must contain R, G, B values"); 315 | 316 | self->graphics->update_pen( 317 | i, 318 | mp_obj_get_int(tuple->items[0]), 319 | mp_obj_get_int(tuple->items[1]), 320 | mp_obj_get_int(tuple->items[2]) 321 | ); 322 | } 323 | 324 | return mp_const_none; 325 | } 326 | 327 | mp_obj_t ModPicoGraphics_set_clip(size_t n_args, const mp_obj_t *args) { 328 | enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; 329 | 330 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); 331 | 332 | self->graphics->set_clip({ 333 | mp_obj_get_int(args[ARG_x]), 334 | mp_obj_get_int(args[ARG_y]), 335 | mp_obj_get_int(args[ARG_w]), 336 | mp_obj_get_int(args[ARG_h]) 337 | }); 338 | 339 | return mp_const_none; 340 | } 341 | 342 | mp_obj_t ModPicoGraphics_remove_clip(mp_obj_t self_in) { 343 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 344 | 345 | self->graphics->remove_clip(); 346 | 347 | return mp_const_none; 348 | } 349 | 350 | mp_obj_t ModPicoGraphics_clear(mp_obj_t self_in) { 351 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 352 | 353 | self->graphics->clear(); 354 | 355 | return mp_const_none; 356 | } 357 | 358 | mp_obj_t ModPicoGraphics_pixel(mp_obj_t self_in, mp_obj_t x, mp_obj_t y) { 359 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 360 | 361 | self->graphics->pixel({ 362 | mp_obj_get_int(x), 363 | mp_obj_get_int(y) 364 | }); 365 | 366 | return mp_const_none; 367 | } 368 | 369 | mp_obj_t ModPicoGraphics_pixel_span(size_t n_args, const mp_obj_t *args) { 370 | enum { ARG_self, ARG_x, ARG_y, ARG_l }; 371 | 372 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); 373 | 374 | self->graphics->pixel_span({ 375 | mp_obj_get_int(args[ARG_x]), 376 | mp_obj_get_int(args[ARG_y]) 377 | }, mp_obj_get_int(args[ARG_l])); 378 | 379 | return mp_const_none; 380 | } 381 | 382 | mp_obj_t ModPicoGraphics_rectangle(size_t n_args, const mp_obj_t *args) { 383 | enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h }; 384 | 385 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); 386 | 387 | self->graphics->rectangle({ 388 | mp_obj_get_int(args[ARG_x]), 389 | mp_obj_get_int(args[ARG_y]), 390 | mp_obj_get_int(args[ARG_w]), 391 | mp_obj_get_int(args[ARG_h]) 392 | }); 393 | 394 | return mp_const_none; 395 | } 396 | 397 | mp_obj_t ModPicoGraphics_circle(size_t n_args, const mp_obj_t *args) { 398 | enum { ARG_self, ARG_x, ARG_y, ARG_r }; 399 | 400 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); 401 | 402 | self->graphics->circle({ 403 | mp_obj_get_int(args[ARG_x]), 404 | mp_obj_get_int(args[ARG_y]) 405 | }, mp_obj_get_int(args[ARG_r])); 406 | 407 | return mp_const_none; 408 | } 409 | 410 | mp_obj_t ModPicoGraphics_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { 411 | enum { ARG_self, ARG_char, ARG_x, ARG_y, ARG_scale }; 412 | static const mp_arg_t allowed_args[] = { 413 | { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, 414 | { MP_QSTR_char, MP_ARG_REQUIRED | MP_ARG_OBJ }, 415 | { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, 416 | { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, 417 | { MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} }, 418 | }; 419 | 420 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 421 | mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); 422 | 423 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, ModPicoGraphics_obj_t); 424 | 425 | int c = mp_obj_get_int(args[ARG_char].u_obj); 426 | int x = args[ARG_x].u_int; 427 | int y = args[ARG_y].u_int; 428 | int scale = args[ARG_scale].u_int; 429 | 430 | self->graphics->character((char)c, Point(x, y), scale); 431 | 432 | return mp_const_none; 433 | } 434 | 435 | mp_obj_t ModPicoGraphics_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { 436 | enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_wrap, ARG_scale, ARG_angle, ARG_spacing, ARG_fixed_width }; 437 | static const mp_arg_t allowed_args[] = { 438 | { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, 439 | { MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ }, 440 | { MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT }, 441 | { MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT }, 442 | { MP_QSTR_wordwrap, MP_ARG_INT, {.u_int = __INT32_MAX__} }, // Sort-of a fudge, but wide-enough to avoid wrapping on any display size 443 | { MP_QSTR_scale, MP_ARG_OBJ, {.u_obj = mp_const_none} }, 444 | { MP_QSTR_angle, MP_ARG_INT, {.u_int = 0} }, 445 | { MP_QSTR_spacing, MP_ARG_INT, {.u_int = 1} }, 446 | { MP_QSTR_fixed_width, MP_ARG_OBJ, {.u_obj = mp_const_false} }, 447 | }; 448 | 449 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 450 | mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); 451 | 452 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, ModPicoGraphics_obj_t); 453 | 454 | mp_obj_t text_obj = args[ARG_text].u_obj; 455 | 456 | if(!mp_obj_is_str_or_bytes(text_obj)) mp_raise_TypeError("text: string required"); 457 | 458 | GET_STR_DATA_LEN(text_obj, str, str_len); 459 | 460 | const std::string_view t((const char*)str, str_len); 461 | 462 | int x = args[ARG_x].u_int; 463 | int y = args[ARG_y].u_int; 464 | int wrap = args[ARG_wrap].u_int; 465 | float scale = args[ARG_scale].u_obj == mp_const_none ? 2.0f : mp_obj_get_float(args[ARG_scale].u_obj); 466 | int angle = args[ARG_angle].u_int; 467 | int letter_spacing = args[ARG_spacing].u_int; 468 | bool fixed_width = args[ARG_fixed_width].u_obj == mp_const_true; 469 | 470 | self->graphics->text(t, Point(x, y), wrap, scale, angle, letter_spacing, fixed_width); 471 | 472 | return mp_const_none; 473 | } 474 | 475 | mp_obj_t ModPicoGraphics_measure_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { 476 | enum { ARG_self, ARG_text, ARG_scale, ARG_spacing, ARG_fixed_width }; 477 | static const mp_arg_t allowed_args[] = { 478 | { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, 479 | { MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ }, 480 | { MP_QSTR_scale, MP_ARG_OBJ, {.u_obj = mp_const_none} }, 481 | { MP_QSTR_spacing, MP_ARG_INT, {.u_int = 1} }, 482 | { MP_QSTR_fixed_width, MP_ARG_OBJ, {.u_obj = mp_const_false} }, 483 | }; 484 | 485 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; 486 | mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); 487 | 488 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, ModPicoGraphics_obj_t); 489 | 490 | mp_obj_t text_obj = args[ARG_text].u_obj; 491 | 492 | if(!mp_obj_is_str_or_bytes(text_obj)) mp_raise_TypeError("text: string required"); 493 | 494 | GET_STR_DATA_LEN(text_obj, str, str_len); 495 | 496 | const std::string_view t((const char*)str, str_len); 497 | 498 | float scale = args[ARG_scale].u_obj == mp_const_none ? 2.0f : mp_obj_get_float(args[ARG_scale].u_obj); 499 | int letter_spacing = args[ARG_spacing].u_int; 500 | bool fixed_width = args[ARG_fixed_width].u_obj == mp_const_true; 501 | 502 | int width = self->graphics->measure_text(t, scale, letter_spacing, fixed_width); 503 | 504 | return mp_obj_new_int(width); 505 | } 506 | 507 | mp_obj_t ModPicoGraphics_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { 508 | size_t num_tuples = n_args - 1; 509 | const mp_obj_t *tuples = pos_args + 1; 510 | 511 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(pos_args[0], ModPicoGraphics_obj_t); 512 | 513 | // Check if there is only one argument, which might be a list 514 | if(n_args == 2) { 515 | if(mp_obj_is_type(pos_args[1], &mp_type_list)) { 516 | mp_obj_list_t *points = MP_OBJ_TO_PTR2(pos_args[1], mp_obj_list_t); 517 | 518 | if(points->len <= 0) mp_raise_ValueError("poly(): cannot provide an empty list"); 519 | 520 | num_tuples = points->len; 521 | tuples = points->items; 522 | } 523 | else { 524 | mp_raise_TypeError("poly(): can't convert object to list"); 525 | } 526 | } 527 | 528 | if(num_tuples > 0) { 529 | std::vector points; 530 | for(size_t i = 0; i < num_tuples; i++) { 531 | mp_obj_t obj = tuples[i]; 532 | if(!mp_obj_is_type(obj, &mp_type_tuple)) mp_raise_ValueError("poly(): can't convert object to tuple"); 533 | 534 | mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(obj, mp_obj_tuple_t); 535 | 536 | if(tuple->len != 2) mp_raise_ValueError("poly(): tuple must only contain two numbers"); 537 | 538 | points.push_back({ 539 | mp_obj_get_int(tuple->items[0]), 540 | mp_obj_get_int(tuple->items[1]) 541 | }); 542 | } 543 | self->graphics->polygon(points); 544 | } 545 | 546 | return mp_const_none; 547 | } 548 | 549 | mp_obj_t ModPicoGraphics_triangle(size_t n_args, const mp_obj_t *args) { 550 | enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_x3, ARG_y3 }; 551 | 552 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); 553 | 554 | self->graphics->triangle( 555 | {mp_obj_get_int(args[ARG_x1]), 556 | mp_obj_get_int(args[ARG_y1])}, 557 | {mp_obj_get_int(args[ARG_x2]), 558 | mp_obj_get_int(args[ARG_y2])}, 559 | {mp_obj_get_int(args[ARG_x3]), 560 | mp_obj_get_int(args[ARG_y3])} 561 | ); 562 | 563 | return mp_const_none; 564 | } 565 | 566 | mp_obj_t ModPicoGraphics_line(size_t n_args, const mp_obj_t *args) { 567 | enum { ARG_self, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_thickness }; 568 | 569 | ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t); 570 | 571 | if(n_args == 5) { 572 | self->graphics->line( 573 | {mp_obj_get_int(args[ARG_x1]), 574 | mp_obj_get_int(args[ARG_y1])}, 575 | {mp_obj_get_int(args[ARG_x2]), 576 | mp_obj_get_int(args[ARG_y2])} 577 | ); 578 | } 579 | else if(n_args == 6) { 580 | self->graphics->thick_line( 581 | {mp_obj_get_int(args[ARG_x1]), 582 | mp_obj_get_int(args[ARG_y1])}, 583 | {mp_obj_get_int(args[ARG_x2]), 584 | mp_obj_get_int(args[ARG_y2])}, 585 | mp_obj_get_int(args[ARG_thickness]) 586 | ); 587 | } 588 | 589 | return mp_const_none; 590 | } 591 | 592 | mp_obj_t ModPicoGraphics_loop(mp_obj_t self_in, mp_obj_t update, mp_obj_t render) { 593 | (void)self_in; 594 | /* 595 | TODO: Uh how do we typecheck a function? 596 | if(!mp_obj_is_type(update, &mp_type_function)) { 597 | mp_raise_TypeError("update(ticks_ms) must be a function."); 598 | } 599 | if(!mp_obj_is_type(render, &mp_type_function)) { 600 | mp_raise_TypeError("render(ticks_ms) must be a function."); 601 | }*/ 602 | //ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); 603 | absolute_time_t t_start = get_absolute_time(); 604 | mp_obj_t result; 605 | while(true) { 606 | uint32_t tick = absolute_time_diff_us(t_start, get_absolute_time()) / 1000; 607 | result = mp_call_function_1(update, mp_obj_new_int(tick)); 608 | if (result == mp_const_false) break; 609 | dv_display.wait_for_flip(); 610 | result = mp_call_function_1(render, mp_obj_new_int(tick)); 611 | if (result == mp_const_false) break; 612 | dv_display.flip_async(); 613 | #ifdef mp_event_handle_nowait 614 | mp_event_handle_nowait(); 615 | #endif 616 | } 617 | return mp_const_none; 618 | } 619 | 620 | } 621 | -------------------------------------------------------------------------------- /modules/picographics/picographics.h: -------------------------------------------------------------------------------- 1 | #include "py/runtime.h" 2 | #include "py/objstr.h" 3 | 4 | enum PicoGraphicsPenType { 5 | PEN_1BIT = 0, 6 | PEN_3BIT, 7 | PEN_P2, 8 | PEN_P4, 9 | PEN_P8, 10 | PEN_RGB332, 11 | PEN_RGB565, 12 | PEN_RGB888, 13 | PEN_INKY7 14 | }; 15 | 16 | enum PicoGraphicsBusType { 17 | BUS_I2C, 18 | BUS_SPI, 19 | BUS_PARALLEL, 20 | BUS_PIO 21 | }; 22 | 23 | // Type 24 | extern const mp_obj_type_t ModPicoGraphics_type; 25 | 26 | // Module functions 27 | extern mp_obj_t ModPicoGraphics_get_required_buffer_size(mp_obj_t display_in, mp_obj_t pen_type_in); 28 | 29 | // DV Display specific functions 30 | extern mp_obj_t ModPicoGraphics_set_scroll_group_offset(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); 31 | extern mp_obj_t ModPicoGraphics_set_scroll_group_for_lines(size_t n_args, const mp_obj_t *args); 32 | extern mp_obj_t ModPicoGraphics_tilemap(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); 33 | extern mp_obj_t ModPicoGraphics_load_animation(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); 34 | 35 | // Class methods 36 | extern mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); 37 | 38 | extern mp_obj_t ModPicoGraphics_update(mp_obj_t self_in); 39 | 40 | // Palette management 41 | extern mp_obj_t ModPicoGraphics_update_pen(size_t n_args, const mp_obj_t *args); 42 | extern mp_obj_t ModPicoGraphics_reset_pen(mp_obj_t self_in, mp_obj_t pen); 43 | extern mp_obj_t ModPicoGraphics_set_palette(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); 44 | extern mp_obj_t ModPicoGraphics_hsv_to_rgb(size_t n_args, const mp_obj_t *args); 45 | 46 | // Pen 47 | extern mp_obj_t ModPicoGraphics_set_pen(mp_obj_t self_in, mp_obj_t pen); 48 | extern mp_obj_t ModPicoGraphics_create_pen(size_t n_args, const mp_obj_t *args); 49 | extern mp_obj_t ModPicoGraphics_create_pen_hsv(size_t n_args, const mp_obj_t *args); 50 | extern mp_obj_t ModPicoGraphics_set_thickness(mp_obj_t self_in, mp_obj_t thickness); 51 | 52 | // Alpha Blending 53 | extern mp_obj_t ModPicoGraphics_set_bg(mp_obj_t self_in, mp_obj_t pen); 54 | extern mp_obj_t ModPicoGraphics_set_blend_mode(mp_obj_t self_in, mp_obj_t pen); 55 | 56 | // Depth 57 | extern mp_obj_t ModPicoGraphics_set_depth(mp_obj_t self_in, mp_obj_t depth); 58 | 59 | // Primitives 60 | extern mp_obj_t ModPicoGraphics_set_clip(size_t n_args, const mp_obj_t *args); 61 | extern mp_obj_t ModPicoGraphics_remove_clip(mp_obj_t self_in); 62 | extern mp_obj_t ModPicoGraphics_clear(mp_obj_t self_in); 63 | extern mp_obj_t ModPicoGraphics_pixel(mp_obj_t self_in, mp_obj_t x, mp_obj_t y); 64 | extern mp_obj_t ModPicoGraphics_pixel_span(size_t n_args, const mp_obj_t *args); 65 | extern mp_obj_t ModPicoGraphics_rectangle(size_t n_args, const mp_obj_t *args); 66 | extern mp_obj_t ModPicoGraphics_circle(size_t n_args, const mp_obj_t *args); 67 | extern mp_obj_t ModPicoGraphics_character(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); 68 | extern mp_obj_t ModPicoGraphics_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); 69 | extern mp_obj_t ModPicoGraphics_measure_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); 70 | extern mp_obj_t ModPicoGraphics_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); 71 | extern mp_obj_t ModPicoGraphics_triangle(size_t n_args, const mp_obj_t *args); 72 | extern mp_obj_t ModPicoGraphics_line(size_t n_args, const mp_obj_t *args); 73 | 74 | // Sprites 75 | extern mp_obj_t ModPicoGraphics_load_sprite(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); 76 | extern mp_obj_t ModPicoGraphics_display_sprite(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); 77 | extern mp_obj_t ModPicoGraphics_clear_sprite(mp_obj_t self_in, mp_obj_t slot); 78 | 79 | // Utility 80 | extern mp_obj_t ModPicoGraphics_set_font(mp_obj_t self_in, mp_obj_t font); 81 | extern mp_obj_t ModPicoGraphics_get_bounds(mp_obj_t self_in); 82 | 83 | extern mp_obj_t ModPicoGraphics_get_i2c(mp_obj_t self_in); 84 | 85 | extern mp_obj_t ModPicoGraphics__del__(mp_obj_t self_in); 86 | 87 | // IO IO 88 | extern mp_obj_t ModPicoGraphics_is_button_x_pressed(mp_obj_t self_in); 89 | extern mp_obj_t ModPicoGraphics_is_button_a_pressed(mp_obj_t self_in); 90 | 91 | extern mp_obj_t ModPicoGraphics_get_gpu_io_value(mp_obj_t self_in, mp_obj_t pin); 92 | extern mp_obj_t ModPicoGraphics_set_gpu_io_value(mp_obj_t self_in, mp_obj_t pin, mp_obj_t value); 93 | extern mp_obj_t ModPicoGraphics_set_gpu_io_output_enable(mp_obj_t self_in, mp_obj_t pin, mp_obj_t enable); 94 | extern mp_obj_t ModPicoGraphics_set_gpu_io_pull_up(mp_obj_t self_in, mp_obj_t pin, mp_obj_t enable); 95 | extern mp_obj_t ModPicoGraphics_set_gpu_io_pull_down(mp_obj_t self_in, mp_obj_t pin, mp_obj_t enable); 96 | 97 | extern mp_obj_t ModPicoGraphics_set_gpu_io_adc_enable(mp_obj_t self_in, mp_obj_t pin, mp_obj_t enable); 98 | extern mp_obj_t ModPicoGraphics_get_gpu_io_adc_voltage(mp_obj_t self_in, mp_obj_t pin); 99 | 100 | extern mp_obj_t ModPicoGraphics_get_gpu_temp(mp_obj_t self_in); 101 | 102 | // Loop 103 | extern mp_obj_t ModPicoGraphics_loop(mp_obj_t self_in, mp_obj_t update, mp_obj_t render); 104 | -------------------------------------------------------------------------------- /modules/picographics/spritesheet-to-rgb332.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | from PIL import Image 3 | import numpy 4 | import sys 5 | import pathlib 6 | 7 | # Run with `./filename.py source-image.jpg` 8 | IMAGE_PATH = pathlib.Path(sys.argv[1]) 9 | OUTPUT_PATH = IMAGE_PATH.with_suffix(".rgb332") 10 | 11 | 12 | def image_to_data(image): 13 | """Generator function to convert a PIL image to 16-bit 565 RGB bytes.""" 14 | # NumPy is much faster at doing this. NumPy code provided by: 15 | # Keith (https://www.blogger.com/profile/02555547344016007163) 16 | pb = numpy.array(image.convert('RGBA')).astype('uint16') 17 | 18 | r = (pb[:, :, 0] & 0b11100000) >> 0 19 | g = (pb[:, :, 1] & 0b11100000) >> 3 20 | b = (pb[:, :, 2] & 0b11000000) >> 6 21 | a = pb[:, :, 3] # Discard 22 | 23 | # AAAA RRRR GGGG BBBB 24 | color = r | g | b 25 | return color.astype("uint8").flatten().tobytes() 26 | 27 | 28 | img = Image.open(IMAGE_PATH) 29 | w, h = img.size 30 | data = image_to_data(img) 31 | 32 | print(f"Converted: {w}x{h} {len(data)} bytes") 33 | 34 | with open(OUTPUT_PATH, "wb") as f: 35 | f.write(data) 36 | 37 | print(f"Written to: {OUTPUT_PATH}") -------------------------------------------------------------------------------- /pico_dvhstx.cmake: -------------------------------------------------------------------------------- 1 | 2 | include(drivers/dvhstx/dvhstx) 3 | 4 | set(LIB_NAME pico_dvhstx) 5 | add_library(${LIB_NAME} INTERFACE) 6 | 7 | target_sources(${LIB_NAME} INTERFACE 8 | ${PIMORONI_PICO_PATH}/libraries/pico_graphics/pico_graphics.cpp 9 | # ${CMAKE_CURRENT_LIST_DIR}/libraries/pico_graphics/pico_graphics_pen_dvhstx_rgb888.cpp 10 | ${CMAKE_CURRENT_LIST_DIR}/libraries/pico_graphics/pico_graphics_pen_dvhstx_rgb565.cpp 11 | ${CMAKE_CURRENT_LIST_DIR}/libraries/pico_graphics/pico_graphics_pen_dvhstx_p8.cpp 12 | ${PIMORONI_PICO_PATH}/libraries/pico_graphics/types.cpp 13 | ) 14 | 15 | target_include_directories(${LIB_NAME} INTERFACE 16 | ${CMAKE_CURRENT_LIST_DIR} 17 | ${CMAKE_CURRENT_LIST_DIR}/libraries/pico_graphics # for pico_graphics_dv.hpp 18 | ${PIMORONI_PICO_PATH}/libraries/pico_graphics # for pico_graphics.hpp 19 | ${PIMORONI_PICO_PATH}/libraries/pngdec 20 | ) 21 | 22 | target_link_libraries(${LIB_NAME} INTERFACE dvhstx pico_stdlib hardware_i2c hardware_dma) 23 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | 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") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download Pico SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | # GIT_SUBMODULES_RECURSE was added in 3.17 33 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") 34 | FetchContent_Declare( 35 | pico_sdk 36 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 37 | GIT_TAG master 38 | GIT_SUBMODULES_RECURSE FALSE 39 | ) 40 | else () 41 | FetchContent_Declare( 42 | pico_sdk 43 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 44 | GIT_TAG master 45 | ) 46 | endif () 47 | 48 | if (NOT pico_sdk) 49 | message("Downloading Raspberry Pi Pico SDK") 50 | FetchContent_Populate(pico_sdk) 51 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 52 | endif () 53 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 54 | else () 55 | message(FATAL_ERROR 56 | "Pico SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 57 | ) 58 | endif () 59 | endif () 60 | 61 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 62 | if (NOT EXISTS ${PICO_SDK_PATH}) 63 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 64 | endif () 65 | 66 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 67 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 68 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 69 | endif () 70 | 71 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 72 | 73 | include(${PICO_SDK_INIT_CMAKE_FILE}) -------------------------------------------------------------------------------- /pimoroni_pico_import.cmake: -------------------------------------------------------------------------------- 1 | # This file can be dropped into a project to help locate the Pimoroni Pico libraries 2 | # It will also set up the required include and module search paths. 3 | 4 | if (DEFINED ENV{PIMORONI_PICO_FETCH_FROM_GIT} AND (NOT PIMORONI_PICO_FETCH_FROM_GIT)) 5 | set(PIMORONI_PICO_FETCH_FROM_GIT $ENV{PIMORONI_PICO_FETCH_FROM_GIT}) 6 | message("Using PIMORONI_PICO_FETCH_FROM_GIT from environment ('${PIMORONI_PICO_FETCH_FROM_GIT}')") 7 | endif () 8 | 9 | if (DEFINED ENV{PIMORONI_PICO_FETCH_FROM_GIT_PATH} AND (NOT PIMORONI_PICO_FETCH_FROM_GIT_PATH)) 10 | set(PIMORONI_PICO_FETCH_FROM_GIT_PATH $ENV{PIMORONI_PICO_FETCH_FROM_GIT_PATH}) 11 | message("Using PIMORONI_PICO_FETCH_FROM_GIT_PATH from environment ('${PIMORONI_PICO_FETCH_FROM_GIT_PATH}')") 12 | endif () 13 | 14 | if (NOT PIMORONI_PICO_PATH) 15 | if (PIMORONI_PICO_FETCH_FROM_GIT) 16 | include(FetchContent) 17 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 18 | if (PIMORONI_PICO_FETCH_FROM_GIT_PATH) 19 | get_filename_component(FETCHCONTENT_BASE_DIR "${PIMORONI_PICO_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 20 | endif () 21 | FetchContent_Declare( 22 | pimoroni_pico 23 | GIT_REPOSITORY https://github.com/pimoroni/pimoroni-pico 24 | GIT_TAG main 25 | ) 26 | if (NOT pimoroni_pico) 27 | message("Downloading PIMORONI_PICO SDK") 28 | FetchContent_Populate(pimoroni_pico) 29 | set(PIMORONI_PICO_PATH ${pimoroni_pico_SOURCE_DIR}) 30 | endif () 31 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 32 | elseif(PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pimoroni-pico") 33 | set(PIMORONI_PICO_PATH ${PICO_SDK_PATH}/../pimoroni-pico) 34 | message("Defaulting PIMORONI_PICO_PATH as sibling of PICO_SDK_PATH: ${PIMORONI_PICO_PATH}") 35 | elseif(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/../../pimoroni-pico/") 36 | set(PIMORONI_PICO_PATH ${CMAKE_CURRENT_BINARY_DIR}/../../pimoroni-pico/) 37 | else() 38 | message(FATAL_ERROR "Pimoroni Pico location was not specified. Please set PIMORONI_PICO_PATH.") 39 | endif() 40 | endif() 41 | 42 | get_filename_component(PIMORONI_PICO_PATH "${PIMORONI_PICO_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 43 | if (NOT EXISTS ${PIMORONI_PICO_PATH}) 44 | message(FATAL_ERROR "Directory '${PIMORONI_PICO_PATH}' not found") 45 | endif () 46 | 47 | if (NOT EXISTS ${PIMORONI_PICO_PATH}/pimoroni_pico_import.cmake) 48 | message(FATAL_ERROR "Directory '${PIMORONI_PICO_PATH}' does not appear to contain the Pimoroni Pico libraries") 49 | endif () 50 | 51 | message("PIMORONI_PICO_PATH is ${PIMORONI_PICO_PATH}") 52 | 53 | set(PIMORONI_PICO_PATH ${PIMORONI_PICO_PATH} CACHE PATH "Path to the Pimoroni Pico libraries" FORCE) 54 | 55 | include_directories(${PIMORONI_PICO_PATH}) 56 | list(APPEND CMAKE_MODULE_PATH ${PIMORONI_PICO_PATH}) 57 | --------------------------------------------------------------------------------