├── .gitignore ├── CMakeLists.txt ├── LICENSE.TXT ├── README.md ├── apps ├── CMakeLists.txt ├── popcorn │ ├── CMakeLists.txt │ ├── README.md │ ├── atlantis.c │ ├── converter │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ └── src │ │ │ └── convert.cpp │ ├── font.h │ ├── lcd12.c │ ├── lcd18.c │ └── popcorn.c └── usb_sound_card │ ├── CMakeLists.txt │ ├── lufa │ ├── AudioClassCommon.h │ ├── LICENSE.TXT │ └── StdDescriptors.h │ └── usb_sound_card.c ├── audio ├── CMakeLists.txt └── sine_wave │ ├── CMakeLists.txt │ └── sine_wave.c ├── pico_extras_import.cmake ├── pico_sdk_import.cmake ├── reset ├── CMakeLists.txt └── hello_reset │ ├── CMakeLists.txt │ └── hello_reset.c ├── scanvideo ├── CMakeLists.txt ├── Raspberry Pi Pico to VGA Connection Schematic.png ├── demo1 │ ├── CMakeLists.txt │ ├── data.c │ ├── data.h │ ├── demo1.c │ └── screenshot.jpg ├── demo2 │ ├── CMakeLists.txt │ ├── data2.c │ ├── data2.h │ ├── demo2.c │ └── screenshot.jpg ├── flash_stream │ ├── CMakeLists.txt │ ├── flash_stream.c │ ├── img │ │ ├── .gitignore │ │ ├── LICENSE.txt │ │ ├── Lighthouse_at_sunrise_by_Frenchie_Smalls.png │ │ ├── Stone_Mountain_by_Brad_Huchteman.png │ │ ├── Voss_by_fortuneblues.png │ │ ├── pack.sh │ │ └── packtiles │ └── screenshot.jpg ├── hscroll_dma_tiles │ ├── CMakeLists.txt │ ├── data.c │ ├── data.h │ ├── hscroll_dma_tiles.c │ ├── level0.h │ └── screenshot.jpg ├── mandelbrot │ ├── CMakeLists.txt │ ├── mandelbrot.c │ └── screenshot.jpg ├── mario_tiles │ ├── CMakeLists.txt │ ├── data.c │ ├── data.h │ ├── level0.h │ ├── mario_tiles.c │ └── screenshot.jpg ├── render │ ├── CMakeLists.txt │ ├── README.md │ ├── image.c │ ├── image.h │ ├── spans.c │ └── spans.h ├── scanvideo_minimal │ ├── CMakeLists.txt │ ├── scanvideo_minimal.c │ └── screenshot.jpg ├── sprite │ ├── CMakeLists.txt │ ├── affine_transform.h │ ├── sprite.S │ ├── sprite.c │ └── sprite.h ├── sprite_demo │ ├── CMakeLists.txt │ ├── raspberry_128x128_bgar5515.h │ ├── raspberry_128x128_bgar5515_flip.h │ ├── screenshot.jpg │ └── sprite_demo.c ├── test_pattern │ ├── CMakeLists.txt │ ├── screenshot.jpg │ └── test_pattern.c └── textmode │ ├── CMakeLists.txt │ ├── FONT_LICENSE.TXT │ ├── font.h │ ├── font10.c │ ├── font6.c │ ├── font8.c │ ├── lcd.c │ ├── screenshot.jpg │ └── textmode.c ├── sleep ├── CMakeLists.txt ├── hello_dormant │ ├── CMakeLists.txt │ ├── hello_dormant_aon.c │ └── hello_dormant_gpio.c └── hello_sleep │ ├── CMakeLists.txt │ ├── hello_sleep_alarm.c │ └── hello_sleep_aon.c ├── standalone ├── README.md └── static_sdk │ ├── CMakeLists.txt │ ├── dummy.c │ ├── hacky_cmake_helper.cmake │ └── pico_sdk_import.cmake └── stdio ├── CMakeLists.txt └── pio ├── CMakeLists.txt ├── stdio_pio.c └── uart_tx.pio /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | cmake-* 4 | build 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | # Pull in PICO SDK (must be before project) 4 | include(pico_sdk_import.cmake) 5 | 6 | # We also need PICO EXTRAS 7 | include(pico_extras_import.cmake) 8 | 9 | project(pico_playground C CXX) 10 | set(CMAKE_C_STANDARD 11) 11 | set(CMAKE_CXX_STANDARD 17) 12 | 13 | # Initialize the Pico SDK 14 | pico_sdk_init() 15 | 16 | if (PICO_SDK_VERSION_STRING VERSION_LESS "2.0.0") 17 | message(FATAL_ERROR "Require at least Raspberry Pi Pico SDK version 2.0.0") 18 | endif() 19 | 20 | function(add_subdirectory_exclude_platforms NAME) 21 | if (ARGN) 22 | if (PICO_PLATFORM IN_LIST ARGN) 23 | message("Skipping ${NAME} example which is unsupported on this platform") 24 | return() 25 | endif() 26 | foreach(PATTERN IN LISTS ARGN) 27 | string(REGEX MATCH "${PATTERN}" MATCHED "${PICO_PLATFORM}") 28 | if (MATCHED) 29 | message("Skipping ${NAME} example which is unsupported on this platform") 30 | return() 31 | endif() 32 | endforeach() 33 | endif() 34 | add_subdirectory(${NAME}) 35 | endfunction() 36 | 37 | add_subdirectory(audio) 38 | add_subdirectory(apps) 39 | add_subdirectory(reset) 40 | add_subdirectory(scanvideo) 41 | add_subdirectory(sleep) 42 | add_subdirectory(stdio) 43 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 4 | following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following 7 | disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 10 | disclaimer in the documentation and/or other materials provided with the distribution. 11 | 12 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 20 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 21 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repo is similar to [pico-examples](https://github.com/raspberrypi/pico-examples) but utilizing additional libraries 2 | from [pico-extras](https://github.com/raspberrypi/pico-extras) 3 | 4 | Note that most of these examples are neither fleshed out nor well documented. They mostly serve 5 | the purpose of playing with/testing particular areas of functionality (mostly audio/video related) 6 | 7 | Finally, you may wonder why many of these demos set the system clock to 48Mhz. The reason is that until we had physical 8 | chips, we were running at a fixed 48Mhz system clock using an FPGA. Most of these examples were written before the 9 | RP2040 design was final, so they were all developed with that fixed 48MHz system clock. As a result some of the examples do things in a way 10 | that you wouldn't necessarily need to if you had more clock speed available (which you do), but on the plus side, 11 | you have that much more time to do even more things! 12 | 13 | ## Full Applications 14 | 15 | Name|Description 16 | ---|--- 17 | [popcorn](apps/popcorn)| This is a movie player for 30fps 320x240 movies with 44100 stereo sound, read in a custom format from SD card... it can even play backwards :-) Sample movie file linked from [here](apps/popcorn/README.md). 18 | [usb_sound_card](apps/usb_sound_card)| A no frills but functional USB sound card... hooked up via our old (pre TinyUSB) USB device stack. Keeping it around as it works nicely! 19 | 20 | ## Audio 21 | 22 | Name|Description 23 | ---|--- 24 | [sine_wave_i2s](audio/sine_wave)| A simple sine wave audio output using I2S. 25 | [sine_wave_pwm](audio/sine_wave)| A simple sine wave audio output using PWM. 26 | [sine_wave_spdif](audio/sine_wave)| A simple sine wave audio output using S/PDIF. 27 | 28 | ## Scanout Video 29 | 30 | In _scanout_ video, every pixel is driven by the PIO every frame, and a framebuffer is not (necessarily) used (which 31 | is useful when you only have 264K of RAM). 32 | 33 | For a fuller description of scanout video see [here](https://github.com/raspberrypi/pico-extras/blob/master/src/common/pico_scanvideo/README.adoc) 34 | 35 | Name| Screenshot |Description 36 | ---|-------------------------------------------------|--- 37 | [demo1](scanvideo/demo1)| ![](scanvideo/demo1/screenshot.jpg) | So named because it was the first demo program written that used video.. it is a bit dated now and hails from a time where there was _much_ less RAM on the RP2040 38 | [demo2](scanvideo/demo2)| ![](scanvideo/demo2/screenshot.jpg) | A little variation on `demo1` for RP2350. Displays a RISC-V logo also; if built for RISC-V the RISC-V logo is in front; if built for Arm, it is behind 39 | [flash_stream](scanvideo/flash_stream)| ![](scanvideo/flash_stream/screenshot.jpg) | Streams video data out of flash fast enough to drive 640x480x60fps bitmap display 40 | [hscroll_dma_tiles](scanvideo/hscroll_dma_tiles)| ![](scanvideo/hscroll_dma_tiles/screenshot.jpg) | A horizontal scrolling test app which uses a second video plane (PIO) to overlay sprites 41 | [mandelbrot](scanvideo/mandelbrot)| ![](scanvideo/mandelbrot/screenshot.jpg) | A mandelbrot generator using a 320x240x16 framebuffer 42 | [mario_tiles](scanvideo/mario_tiles)| ![](scanvideo/mario_tiles/screenshot.jpg) | Confusingly named as a reference to Super Mario Kart's tiled psuedo-3D rendering. This is similar to [hscroll_dma_tiles](scanvideo/hscroll_dma_tiles) except the whole tiled scrolling area is now rotated and zoomed. 43 | [scanvideo_minimal](scanvideo/scanvideo_minimal)| ![](scanvideo/scanvideo_minimal/screenshot.jpg) | A very basic video output generator which generates a test pattern 44 | [render](scanvideo/render)| | A very dated rendering library used by [demo1](scanvideo/demo1) - avoid! 45 | [sprite](scanvideo/sprite)| | A small sprite library used by [sprite_demo](scanvideo/scanvideo_minimal) 46 | [sprite_demo](scanvideo/sprite_demo)| ![](scanvideo/sprite_demo/screenshot.jpg) | Some bouncing Eben heads 47 | [test_pattern](scanvideo/test_pattern)| ![](scanvideo/test_pattern/screenshot.jpg) | Display color bars 48 | [textmode](scanvideo/textmode)| ![](scanvideo/textmode/screenshot.jpg) | Shows off chained DMA to generate scanlines out of glyph fragments via DMA/PIO 49 | 50 | The above are intended to be used with the VGA demo board as described in [Hardware Design with RP2040](https://rptl.io/rp2040-design) however it is possible to wire your own VGA output according to the following schematic: 51 | ![](scanvideo/Raspberry%20Pi%20Pico%20to%20VGA%20Connection%20Schematic.png) 52 | 53 | ## Sleep 54 | 55 | Examples of using low power mode; these use `pico_sleep` from pico_extras which is not yet stable. 56 | 57 | Name|Description 58 | ---|--- 59 | [hello_dormant](sleep/hello_dormant)| Demonstrates dormant mode 60 | [hello_sleep](sleep/hello_sleep)| Demonstrates low power sleep and waking from RTC 61 | 62 | 63 | ## Stdio 64 | 65 | Examples of adding additional stdio support. Note the interface for stdio drivers is not yet considered stable 66 | even though it is in the Pico SDK 67 | 68 | Name|Description 69 | ---|--- 70 | [stdio_pio](stdio/pio)| Demonstrates adding a custom STDIO driver using a PIO UART 71 | -------------------------------------------------------------------------------- /apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory_exclude_platforms(popcorn "rp2350-riscv") 2 | add_subdirectory_exclude_platforms(usb_sound_card) -------------------------------------------------------------------------------- /apps/popcorn/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (PICO_ON_DEVICE) 2 | if (TARGET pico_scanvideo_dpi AND TARGET pico_sd_card) 3 | add_executable(popcorn 4 | popcorn.c 5 | atlantis.c 6 | lcd12.c 7 | lcd18.c 8 | ) 9 | 10 | target_compile_definitions(popcorn PRIVATE 11 | PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS=164 12 | # seems fine without 16 (maybe need for overlay) 13 | PICO_SCANVIDEO_SCANLINE_BUFFER_COUNT=16 14 | #PICO_DEBUG_MALLOC 15 | PICO_AUDIO_I2S_DMA_IRQ=1 16 | PICO_AUDIO_I2S_PIO=0 17 | PICO_STACK_SIZE=0x400 18 | __HEAP_SIZE=0x500 19 | PICO_USE_STACK_GUARDS=1 20 | PICO_SCANVIDEO_ADJUST_BUS_PRIORITY=1 21 | PICO_SCANVIDEO_ENABLE_VIDEO_CLOCK_DOWN=1 22 | PLATYPUS_565 23 | VIDEO_565 24 | VIDEO_DBI 25 | 26 | PICO_SCANVIDEO_LINKED_SCANLINE_BUFFERS=1 # we do two rows at a time 27 | 28 | #PICO_SCANVIDEO_48MHZ # still uses this for now 29 | ) 30 | 31 | if (PICO_RP2350) 32 | target_compile_definitions(popcorn PRIVATE 33 | #PICO_SCANVIDEO_48MHZ=1 34 | PLATYPUS_TABLES_MAIN_RAM=1 #todo not enough space in scratch (we can fixup tables tho later) 35 | ) 36 | endif() 37 | target_link_libraries(popcorn 38 | pico_multicore 39 | pico_stdlib 40 | platypus 41 | pico_scanvideo_dpi 42 | pico_sd_card 43 | pico_audio_i2s) 44 | pico_add_extra_outputs(popcorn) 45 | endif() 46 | else() 47 | add_subdirectory(converter) 48 | endif() -------------------------------------------------------------------------------- /apps/popcorn/README.md: -------------------------------------------------------------------------------- 1 | This is a 320x240x30 movie player that runs at a 48Mhz system clock (it was developed on FPGA which ran at that frequency). 2 | 3 | The compression format is not surprisingly not crazily advanced, so movie files are large! 4 | 5 | ### Sample Movie 6 | 7 | Here is "Big Buck Bunny": https://drive.google.com/file/d/1q3szTVccPZ08v_TMDxy9ZgqeOOXXwHCX/view?usp=sharing which is 1.6GB 8 | 9 | ### Writing a Movie to SD Card 10 | 11 | These are raw disk images without a filesystem. These instructions assume a certain level of knowledge Please feel free to submit PRs 12 | to improve them! 13 | 14 | #### Single Movie 15 | 16 | A single movie can just be burned as the entirety of the SD card (via `dd` on unix). Note this will overwrite everything on the card. 17 | 18 | #### One or More Movies 19 | 20 | You can format the card with a GPT and then image movies onto the partitions (the partitions must obviously be big enough). The partition name from the GPT is used as the title for the movie. 21 | 22 | ### Playback controls 23 | 24 | These are quite limited and use the 3 buttons on the VGA board, and use single button presses with function determined by how long the button is pressed before it is released. 25 | 26 | * Short (<.25s) press while playing: `Slower : Pause : Faster` 27 | * Short (<.25s) press while paused: `Step Backward : Play : Step Forward` 28 | * Medium (<1s) press: `Previous File : Toggle Menu Display : Next File` 29 | * Long (>1s) press: `Lower Volume : Toggle Play Direction : Higher Volume` - note you can hold the button down to continuosuly lower or raise volume. 30 | 31 | Pause and then unpause to reset playback speed to 1x 32 | 33 | 34 | ### Converting 35 | 36 | see [here](converter/README.md) -------------------------------------------------------------------------------- /apps/popcorn/converter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | project(converter) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | include_directories(../libs/render src) 7 | add_executable(converter 8 | src/convert.cpp 9 | ) 10 | -------------------------------------------------------------------------------- /apps/popcorn/converter/README.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | Note these instructions apply to Unix; they have not been tested on other platforms 4 | 5 | To build the converter, either do a `PICO_PLATFORM=host` build of pico-playground (which will include it), 6 | or from this directory just do 7 | 8 | ```bash 9 | mkdir build 10 | cd build 11 | cmake -DCMAKE_BUILD_TYPE=Release .. 12 | make 13 | ``` 14 | 15 | Converter takes two inputs (and you're going to need to read the `ffmpeg` docs!) 16 | 17 | # .rgb file 18 | 19 | This must be 320x240 30fps 24 bit raw RGB 20 | 21 | e.g. 22 | 23 | ``` 24 | ffmpeg -i input.mkv -vf "crop=1088:816:416:132, scale=(iw*sar)*max(320/(iw*sar)\,240/ih):ih*max(320/(iw*sar)\,240/ih)" -c:v rawvideo -pix_fmt rgb24 -r 30 movie.rgb 25 | ``` 26 | 27 | `-c:v rawvideo -pix_fmt rgb24 -r 30 movie.rgb` are key here (and the 320x240 output size) 28 | 29 | # .pcm file 30 | 31 | This must be stereo 44100Hz signed 16 bit little-endian raw data 32 | 33 | e.g. 34 | 35 | ``` 36 | ffmpeg -i input.mkv -ac 2 -f s16le -c:a pcm_s16le -ar 44100 movie.pcm 37 | ``` 38 | 39 | # Converting 40 | 41 | You can then do: 42 | 43 | ```c 44 | converter movie.rgb movie.pcm movie.pl2 45 | ``` 46 | 47 | If the inputs are not as specified, then the converter will likely crash! 48 | -------------------------------------------------------------------------------- /apps/popcorn/font.h: -------------------------------------------------------------------------------- 1 | #ifndef SOFTWARE_FONT_H 2 | #define SOFTWARE_FONT_H 3 | 4 | #include "pico.h" 5 | 6 | typedef struct { 7 | uint16_t bitmap_index; 8 | uint16_t adv_w; 9 | int8_t box_w, box_h, ofs_x, ofs_y; 10 | } __attribute__((packed)) lv_font_fmt_txt_glyph_dsc_t; 11 | 12 | typedef struct { 13 | uint16_t range_start, range_length, glyph_id_start;//, list_length; 14 | // void *unicode_list, *glyph_id_ofs_list; 15 | enum { 16 | LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY, 17 | LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL 18 | } type; 19 | } lv_font_fmt_txt_cmap_t; 20 | 21 | typedef struct { 22 | const uint8_t *glyph_bitmap; 23 | const lv_font_fmt_txt_glyph_dsc_t *glyph_dsc; 24 | const lv_font_fmt_txt_cmap_t *cmaps; 25 | uint8_t cmap_num, bpp, kern_scale, kern_classes; 26 | void *kern_dsc; 27 | } lv_font_fmt_txt_dsc_t; 28 | 29 | typedef struct { 30 | lv_font_fmt_txt_dsc_t *dsc; 31 | uint8_t line_height, base_line; 32 | } lv_font_t; 33 | 34 | extern const lv_font_t lcd12; 35 | extern const lv_font_t lcd18; 36 | 37 | #define LV_ATTRIBUTE_LARGE_CONST 38 | #endif //SOFTWARE_FONT_H 39 | -------------------------------------------------------------------------------- /apps/popcorn/lcd12.c: -------------------------------------------------------------------------------- 1 | #include "font.h" 2 | 3 | /******************************************************************************* 4 | * FrozenCrystal 5 | * Size: 12 px 6 | * Bpp: 4 7 | * Opts: 8 | ******************************************************************************/ 9 | 10 | #ifndef LCD12 11 | #define LCD12 1 12 | #endif 13 | 14 | #if LCD12 15 | 16 | /*----------------- 17 | * BITMAPS 18 | *----------------*/ 19 | 20 | /*Store the image of the glyphs*/ 21 | static LV_ATTRIBUTE_LARGE_CONST const uint8_t gylph_bitmap[] = { 22 | /* U+2E "." */ 23 | 0x93, 24 | 25 | /* U+30 "0" */ 26 | 0xc, 0xbb, 0x93, 0x47, 0x0, 0x84, 0x58, 0x0, 27 | 0xc4, 0x75, 0x31, 0x90, 0x51, 0x73, 0x90, 0xc4, 28 | 0x0, 0xe0, 0xc0, 0x1, 0xc0, 0xab, 0xbb, 0x70, 29 | 30 | /* U+31 "1" */ 31 | 0xc9, 0x40, 0x88, 0x8, 0x40, 0x74, 0x6, 0x0, 32 | 0xd0, 0xf, 0x0, 0xa0, 33 | 34 | /* U+32 "2" */ 35 | 0xc, 0xbb, 0x93, 0x0, 0x0, 0x84, 0x0, 0x0, 36 | 0xc3, 0x3, 0x33, 0xa0, 0x59, 0x88, 0x30, 0xc4, 37 | 0x0, 0x0, 0xc0, 0x0, 0x0, 0xab, 0xbb, 0x50, 38 | 39 | /* U+33 "3" */ 40 | 0xc, 0xbb, 0x93, 0x0, 0x0, 0x84, 0x0, 0x0, 41 | 0xc3, 0x3, 0x33, 0xa0, 0x8, 0x88, 0xc0, 0x0, 42 | 0x0, 0xd0, 0x0, 0x1, 0xc0, 0x9b, 0xba, 0xa0, 43 | 44 | /* U+34 "4" */ 45 | 0x26, 0x0, 0x43, 0x49, 0x0, 0x84, 0x58, 0x0, 46 | 0xc4, 0x77, 0x33, 0xb0, 0x7, 0x88, 0xc0, 0x0, 47 | 0x0, 0xd0, 0x0, 0x1, 0xc0, 0x0, 0x1, 0xa0, 48 | 49 | /* U+35 "5" */ 50 | 0x29, 0xbb, 0x54, 0x90, 0x0, 0x58, 0x0, 0x7, 51 | 0x73, 0x32, 0x7, 0x88, 0xc0, 0x0, 0xd, 0x0, 52 | 0x1, 0xc9, 0xbb, 0xaa, 53 | 54 | /* U+36 "6" */ 55 | 0xc, 0xbb, 0xb3, 0x47, 0x0, 0x0, 0x58, 0x0, 56 | 0x0, 0x78, 0x33, 0x20, 0x58, 0x88, 0xc0, 0xc4, 57 | 0x0, 0xe0, 0xc0, 0x1, 0xc0, 0xab, 0xbb, 0x70, 58 | 59 | /* U+37 "7" */ 60 | 0xcb, 0xb9, 0x30, 0x0, 0x84, 0x0, 0xc, 0x30, 61 | 0x0, 0x90, 0x0, 0x9, 0x0, 0x0, 0xd0, 0x0, 62 | 0x1c, 0x0, 0x1, 0x90, 63 | 64 | /* U+38 "8" */ 65 | 0xc, 0xbb, 0x93, 0x47, 0x0, 0x84, 0x58, 0x0, 66 | 0xc4, 0x78, 0x33, 0xb0, 0x58, 0x88, 0xc0, 0xc4, 67 | 0x0, 0xe0, 0xc0, 0x1, 0xc0, 0xab, 0xbb, 0x70, 68 | 69 | /* U+39 "9" */ 70 | 0xb, 0xbb, 0xa3, 0x48, 0x0, 0x84, 0x48, 0x0, 71 | 0xa4, 0x88, 0x33, 0xb1, 0x7, 0x88, 0xa0, 0x0, 72 | 0x0, 0xf0, 0x0, 0x0, 0xc0, 0x9b, 0xbb, 0x70, 73 | 74 | /* U+3A ":" */ 75 | 0x12, 0x35, 0x0, 0x0, 0x93 76 | }; 77 | 78 | 79 | /*--------------------- 80 | * GLYPH DESCRIPTION 81 | *--------------------*/ 82 | 83 | static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { 84 | {.bitmap_index = 0, .adv_w = 0, .box_h = 0, .box_w = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */, 85 | {.bitmap_index = 0, .adv_w = 55, .box_h = 1, .box_w = 2, .ofs_x = 1, .ofs_y = 0}, 86 | {.bitmap_index = 1, .adv_w = 107, .box_h = 8, .box_w = 6, .ofs_x = 1, .ofs_y = 0}, 87 | {.bitmap_index = 25, .adv_w = 77, .box_h = 8, .box_w = 3, .ofs_x = 2, .ofs_y = 0}, 88 | {.bitmap_index = 37, .adv_w = 107, .box_h = 8, .box_w = 6, .ofs_x = 1, .ofs_y = 0}, 89 | {.bitmap_index = 61, .adv_w = 107, .box_h = 8, .box_w = 6, .ofs_x = 1, .ofs_y = 0}, 90 | {.bitmap_index = 85, .adv_w = 107, .box_h = 8, .box_w = 6, .ofs_x = 1, .ofs_y = 0}, 91 | {.bitmap_index = 109, .adv_w = 107, .box_h = 8, .box_w = 5, .ofs_x = 1, .ofs_y = 0}, 92 | {.bitmap_index = 129, .adv_w = 107, .box_h = 8, .box_w = 6, .ofs_x = 1, .ofs_y = 0}, 93 | {.bitmap_index = 153, .adv_w = 107, .box_h = 8, .box_w = 5, .ofs_x = 2, .ofs_y = 0}, 94 | {.bitmap_index = 173, .adv_w = 107, .box_h = 8, .box_w = 6, .ofs_x = 1, .ofs_y = 0}, 95 | {.bitmap_index = 197, .adv_w = 107, .box_h = 8, .box_w = 6, .ofs_x = 1, .ofs_y = 0}, 96 | {.bitmap_index = 221, .adv_w = 55, .box_h = 5, .box_w = 2, .ofs_x = 1, .ofs_y = 0} 97 | }; 98 | 99 | /*--------------------- 100 | * CHARACTER MAPPING 101 | *--------------------*/ 102 | 103 | //static const uint8_t glyph_id_ofs_list_0[] = { 104 | // 0, 0, 1, 2, 3, 4, 5, 6, 105 | // 7, 8, 9, 10, 11 106 | //}; 107 | // 108 | /*Collect the unicode lists and glyph_id offsets*/ 109 | static const lv_font_fmt_txt_cmap_t cmaps[] = 110 | { 111 | { 112 | .range_start = 46, .range_length = 13, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL, 113 | .glyph_id_start = 1, 114 | } 115 | }; 116 | 117 | /*----------------- 118 | * KERNING 119 | *----------------*/ 120 | 121 | 122 | ///*Pair left and right glyphs for kerning*/ 123 | //static const uint8_t kern_pair_glyph_ids[] = 124 | // { 125 | // 1, 3, 126 | // 1, 6, 127 | // 12, 3 128 | // }; 129 | // 130 | ///* Kerning between the respective left and right glyphs 131 | // * 4.4 format which needs to scaled with `kern_scale`*/ 132 | //static const int8_t kern_pair_values[] = 133 | // { 134 | // -21, -21, -21 135 | // }; 136 | 137 | ///*Collect the kern pair's data in one place*/ 138 | //static const lv_font_fmt_txt_kern_pair_t kern_pairs = 139 | // { 140 | // .glyph_ids = kern_pair_glyph_ids, 141 | // .values = kern_pair_values, 142 | // .pair_cnt = 3, 143 | // .glyph_ids_size = 0 144 | // }; 145 | 146 | /*-------------------- 147 | * ALL CUSTOM DATA 148 | *--------------------*/ 149 | 150 | /*Store all the custom data of the font*/ 151 | static lv_font_fmt_txt_dsc_t font_dsc = { 152 | .glyph_bitmap = gylph_bitmap, 153 | .glyph_dsc = glyph_dsc, 154 | .cmaps = cmaps, 155 | .cmap_num = 1, 156 | .bpp = 4, 157 | 158 | // .kern_scale = 16, 159 | // .kern_dsc = &kern_pairs, 160 | // .kern_classes = 0 161 | }; 162 | 163 | 164 | /*----------------- 165 | * PUBLIC FONT 166 | *----------------*/ 167 | 168 | /*Initialize a public general font descriptor*/ 169 | const lv_font_t lcd12 = { 170 | .dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */ 171 | // .get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/ 172 | // .get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/ 173 | .line_height = 9, /*The maximum line height required by the font*/ 174 | .base_line = 1, /*Baseline measured from the bottom of the line*/ 175 | }; 176 | 177 | #endif /*#if LCD12*/ 178 | -------------------------------------------------------------------------------- /apps/popcorn/lcd18.c: -------------------------------------------------------------------------------- 1 | #include "font.h" 2 | 3 | /******************************************************************************* 4 | * Size: 18 px 5 | * Bpp: 4 6 | * Opts: 7 | ******************************************************************************/ 8 | 9 | #ifndef LCD18 10 | #define LCD18 1 11 | #endif 12 | 13 | #if LCD18 14 | 15 | /*----------------- 16 | * BITMAPS 17 | *----------------*/ 18 | 19 | /*Store the image of the glyphs*/ 20 | static LV_ATTRIBUTE_LARGE_CONST const uint8_t gylph_bitmap[] = { 21 | /* U+2E "." */ 22 | 0x3, 0x11, 0xf7, 23 | 24 | /* U+30 "0" */ 25 | 0x1, 0x9b, 0xbb, 0xb8, 0x30, 0x3a, 0xcc, 0xcb, 26 | 0xb8, 0x7, 0xe0, 0x0, 0xe, 0x70, 0x8c, 0x0, 27 | 0x0, 0xf4, 0xa, 0xc0, 0x0, 0xf, 0x40, 0xa3, 28 | 0x7, 0x20, 0xb1, 0x5, 0x2, 0xc5, 0x8, 0x0, 29 | 0xf4, 0x0, 0x7, 0xe0, 0xf, 0x40, 0x0, 0x8c, 30 | 0x2, 0xf3, 0x0, 0xa, 0xc0, 0x4f, 0x33, 0x33, 31 | 0x96, 0x3, 0xbe, 0xff, 0xff, 0x40, 32 | 33 | /* U+31 "1" */ 34 | 0x19, 0xb8, 0x51, 0x8c, 0x9b, 0x0, 0xc, 0xa0, 35 | 0x0, 0xc8, 0x0, 0xe, 0x70, 0x0, 0x93, 0x0, 36 | 0x6, 0x20, 0x3, 0xf1, 0x0, 0x4f, 0x0, 0x7, 37 | 0xe0, 0x0, 0x8c, 0x0, 0x5, 0x90, 38 | 39 | /* U+32 "2" */ 40 | 0x1, 0x9b, 0xbb, 0xb8, 0x30, 0x18, 0xcc, 0xcb, 41 | 0xb8, 0x0, 0x0, 0x0, 0xf, 0x60, 0x0, 0x0, 42 | 0x0, 0xf4, 0x0, 0x0, 0x0, 0x1f, 0x40, 0x5, 43 | 0x77, 0x76, 0xc0, 0x5, 0xbc, 0xcc, 0xb1, 0x0, 44 | 0xf4, 0x0, 0x0, 0x0, 0xf, 0x40, 0x0, 0x0, 45 | 0x2, 0xf3, 0x0, 0x0, 0x0, 0x4f, 0x33, 0x33, 46 | 0x30, 0x3, 0xbe, 0xff, 0xff, 0x40, 47 | 48 | /* U+33 "3" */ 49 | 0x1, 0x9b, 0xbb, 0xb8, 0x30, 0x18, 0xcc, 0xcb, 50 | 0xb8, 0x0, 0x0, 0x0, 0xf, 0x60, 0x0, 0x0, 51 | 0x0, 0xf4, 0x0, 0x0, 0x0, 0x1f, 0x40, 0x5, 52 | 0x77, 0x76, 0xc0, 0x0, 0xbc, 0xcc, 0xb8, 0x0, 53 | 0x0, 0x0, 0x7, 0xd0, 0x0, 0x0, 0x0, 0x8c, 54 | 0x0, 0x0, 0x0, 0xa, 0xb0, 0x2, 0x33, 0x33, 55 | 0xc8, 0x3, 0xff, 0xff, 0xfb, 0x50, 56 | 57 | /* U+34 "4" */ 58 | 0x17, 0x0, 0x0, 0x43, 0x4e, 0x0, 0x0, 0xb8, 59 | 0x7e, 0x0, 0x0, 0xe7, 0x8c, 0x0, 0x0, 0xf4, 60 | 0xac, 0x0, 0x0, 0xf4, 0xa8, 0x77, 0x77, 0xc1, 61 | 0xb, 0xcc, 0xcc, 0xa0, 0x0, 0x0, 0x7, 0xe0, 62 | 0x0, 0x0, 0x8, 0xc0, 0x0, 0x0, 0xa, 0xc0, 63 | 0x0, 0x0, 0xc, 0x80, 0x0, 0x0, 0x8, 0x50, 64 | 65 | /* U+35 "5" */ 66 | 0x1, 0x7c, 0xbb, 0xb4, 0x4, 0xe9, 0xcc, 0xb0, 67 | 0x7, 0xe0, 0x0, 0x0, 0x8, 0xc0, 0x0, 0x0, 68 | 0xa, 0xc0, 0x0, 0x0, 0xa, 0x87, 0x77, 0x71, 69 | 0x0, 0xbc, 0xcc, 0xc9, 0x0, 0x0, 0x0, 0x7d, 70 | 0x0, 0x0, 0x0, 0x8c, 0x0, 0x0, 0x0, 0xab, 71 | 0x2, 0x33, 0x33, 0xc8, 0x3f, 0xff, 0xff, 0xb5, 72 | 73 | /* U+36 "6" */ 74 | 0x1, 0x9b, 0xbb, 0xbb, 0x40, 0x3a, 0xcc, 0xcc, 75 | 0x92, 0x7, 0xe0, 0x0, 0x0, 0x0, 0x8c, 0x0, 76 | 0x0, 0x0, 0xa, 0xc0, 0x0, 0x0, 0x0, 0xa8, 77 | 0x77, 0x77, 0x10, 0x5, 0xbc, 0xcc, 0xca, 0x0, 78 | 0xf4, 0x0, 0x7, 0xe0, 0xf, 0x40, 0x0, 0x8c, 79 | 0x2, 0xf3, 0x0, 0xa, 0xc0, 0x4f, 0x33, 0x33, 80 | 0x96, 0x3, 0xbe, 0xff, 0xff, 0x40, 81 | 82 | /* U+37 "7" */ 83 | 0x19, 0xbb, 0xbb, 0x83, 0x18, 0xcc, 0xcb, 0xb8, 84 | 0x0, 0x0, 0x0, 0xf6, 0x0, 0x0, 0x0, 0xf4, 85 | 0x0, 0x0, 0x1, 0xf4, 0x0, 0x0, 0x0, 0xc0, 86 | 0x0, 0x0, 0x0, 0x70, 0x0, 0x0, 0x7, 0xd0, 87 | 0x0, 0x0, 0x8, 0xc0, 0x0, 0x0, 0xa, 0xb0, 88 | 0x0, 0x0, 0xc, 0x80, 0x0, 0x0, 0x9, 0x50, 89 | 90 | /* U+38 "8" */ 91 | 0x1, 0x9b, 0xbb, 0xb8, 0x30, 0x3a, 0xcc, 0xcb, 92 | 0xb8, 0x7, 0xe0, 0x0, 0xe, 0x70, 0x8c, 0x0, 93 | 0x0, 0xf4, 0xa, 0xc0, 0x0, 0xf, 0x40, 0xa8, 94 | 0x77, 0x77, 0xc1, 0x5, 0xbc, 0xcc, 0xca, 0x0, 95 | 0xf4, 0x0, 0x7, 0xe0, 0xf, 0x40, 0x0, 0x8c, 96 | 0x2, 0xf3, 0x0, 0xa, 0xc0, 0x4f, 0x33, 0x33, 97 | 0x96, 0x3, 0xbe, 0xff, 0xff, 0x40, 98 | 99 | /* U+39 "9" */ 100 | 0x0, 0x9b, 0xbb, 0xb7, 0x40, 0x3a, 0xcc, 0xcc, 101 | 0xb8, 0x5, 0xf0, 0x0, 0xc, 0x80, 0x8c, 0x0, 102 | 0x0, 0xf5, 0x8, 0xc0, 0x0, 0xf, 0x40, 0x98, 103 | 0x77, 0x77, 0xb3, 0x0, 0xbc, 0xcc, 0xc7, 0x0, 104 | 0x0, 0x0, 0x5, 0xf0, 0x0, 0x0, 0x0, 0x8c, 105 | 0x0, 0x0, 0x0, 0x8, 0xc0, 0x2, 0x33, 0x33, 106 | 0x98, 0x3, 0xff, 0xff, 0xff, 0x40, 107 | 108 | /* U+3A ":" */ 109 | 0x3, 0x60, 0x7, 0xb1, 0x0, 0x0, 0x0, 0x0, 110 | 0x0, 0x0, 0x3, 0x10, 0x1f, 0x70 111 | }; 112 | 113 | 114 | /*--------------------- 115 | * GLYPH DESCRIPTION 116 | *--------------------*/ 117 | 118 | static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { 119 | {.bitmap_index = 0, .adv_w = 0, .box_h = 0, .box_w = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */, 120 | {.bitmap_index = 0, .adv_w = 83, .box_h = 2, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 121 | {.bitmap_index = 3, .adv_w = 160, .box_h = 12, .box_w = 9, .ofs_x = 1, .ofs_y = 0}, 122 | {.bitmap_index = 57, .adv_w = 115, .box_h = 12, .box_w = 5, .ofs_x = 2, .ofs_y = 0}, 123 | {.bitmap_index = 87, .adv_w = 160, .box_h = 12, .box_w = 9, .ofs_x = 1, .ofs_y = 0}, 124 | {.bitmap_index = 141, .adv_w = 160, .box_h = 12, .box_w = 9, .ofs_x = 1, .ofs_y = 0}, 125 | {.bitmap_index = 195, .adv_w = 160, .box_h = 12, .box_w = 8, .ofs_x = 2, .ofs_y = 0}, 126 | {.bitmap_index = 243, .adv_w = 160, .box_h = 12, .box_w = 8, .ofs_x = 1, .ofs_y = 0}, 127 | {.bitmap_index = 291, .adv_w = 161, .box_h = 12, .box_w = 9, .ofs_x = 1, .ofs_y = 0}, 128 | {.bitmap_index = 345, .adv_w = 160, .box_h = 12, .box_w = 8, .ofs_x = 2, .ofs_y = 0}, 129 | {.bitmap_index = 393, .adv_w = 160, .box_h = 12, .box_w = 9, .ofs_x = 1, .ofs_y = 0}, 130 | {.bitmap_index = 447, .adv_w = 161, .box_h = 12, .box_w = 9, .ofs_x = 1, .ofs_y = 0}, 131 | {.bitmap_index = 501, .adv_w = 83, .box_h = 7, .box_w = 4, .ofs_x = 1, .ofs_y = 0} 132 | }; 133 | 134 | /*--------------------- 135 | * CHARACTER MAPPING 136 | *--------------------*/ 137 | 138 | //static const uint8_t glyph_id_ofs_list_0[] = { 139 | // 0, 0, 1, 2, 3, 4, 5, 6, 140 | // 7, 8, 9, 10, 11 141 | //}; 142 | 143 | /*Collect the unicode lists and glyph_id offsets*/ 144 | static const lv_font_fmt_txt_cmap_t cmaps[] = 145 | { 146 | { 147 | .range_start = 46, .range_length = 13, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL, 148 | .glyph_id_start = 1, 149 | } 150 | }; 151 | 152 | /*----------------- 153 | * KERNING 154 | *----------------*/ 155 | 156 | 157 | /*Pair left and right glyphs for kerning*/ 158 | //static const uint8_t kern_pair_glyph_ids[] = 159 | // { 160 | // 1, 3, 161 | // 1, 6, 162 | // 12, 3 163 | // }; 164 | 165 | ///* Kerning between the respective left and right glyphs 166 | // * 4.4 format which needs to scaled with `kern_scale`*/ 167 | //static const int8_t kern_pair_values[] = 168 | // { 169 | // -32, -32, -32 170 | // }; 171 | // 172 | ///*Collect the kern pair's data in one place*/ 173 | //static const lv_font_fmt_txt_kern_pair_t kern_pairs = 174 | // { 175 | // .glyph_ids = kern_pair_glyph_ids, 176 | // .values = kern_pair_values, 177 | // .pair_cnt = 3, 178 | // .glyph_ids_size = 0 179 | // }; 180 | 181 | /*-------------------- 182 | * ALL CUSTOM DATA 183 | *--------------------*/ 184 | 185 | /*Store all the custom data of the font*/ 186 | static lv_font_fmt_txt_dsc_t font_dsc = { 187 | .glyph_bitmap = gylph_bitmap, 188 | .glyph_dsc = glyph_dsc, 189 | .cmaps = cmaps, 190 | .cmap_num = 1, 191 | .bpp = 4, 192 | 193 | // .kern_scale = 16, 194 | // .kern_dsc = &kern_pairs, 195 | // .kern_classes = 0 196 | }; 197 | 198 | 199 | /*----------------- 200 | * PUBLIC FONT 201 | *----------------*/ 202 | 203 | /*Initialize a public general font descriptor*/ 204 | const lv_font_t lcd18 = { 205 | .dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */ 206 | // .get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/ 207 | // .get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/ 208 | .line_height = 13, /*The maximum line height required by the font*/ 209 | .base_line = 1, /*Baseline measured from the bottom of the line*/ 210 | }; 211 | 212 | #endif /*#if LCD18*/ 213 | -------------------------------------------------------------------------------- /apps/usb_sound_card/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (TARGET usb_device) 2 | PROJECT(usb_sound_card) 3 | 4 | add_executable(usb_sound_card 5 | usb_sound_card.c 6 | ) 7 | 8 | target_compile_definitions(usb_sound_card PRIVATE 9 | AUDIO_FREQ_MAX=48000 10 | 11 | # ours are zero based, so say so 12 | PICO_USBDEV_USE_ZERO_BASED_INTERFACES=1 13 | 14 | # need large descriptor 15 | PICO_USBDEV_MAX_DESCRIPTOR_SIZE=256 16 | 17 | 18 | PICO_USBDEV_ISOCHRONOUS_BUFFER_STRIDE_TYPE=1 19 | PICO_USBDEV_ENABLE_DEBUG_TRAgCE 20 | 21 | PICO_AUDIO_I2S_MONO_OUTPUT=0 22 | PICO_AUDIO_I2S_MONO_INPUT=0 23 | ) 24 | 25 | target_link_libraries(usb_sound_card pico_stdlib usb_device pico_audio_i2s pico_multicore) 26 | pico_add_extra_outputs(usb_sound_card) 27 | endif() 28 | -------------------------------------------------------------------------------- /apps/usb_sound_card/lufa/LICENSE.TXT: -------------------------------------------------------------------------------- 1 | LUFA Library 2 | Copyright (C) Dean Camera, 2020. 3 | 4 | dean [at] fourwalledcubicle [dot] com 5 | www.lufa-lib.org 6 | 7 | 8 | Permission to use, copy, modify, and distribute this software 9 | and its documentation for any purpose is hereby granted without 10 | fee, provided that the above copyright notice appear in all 11 | copies and that both that the copyright notice and this 12 | permission notice and warranty disclaimer appear in supporting 13 | documentation, and that the name of the author not be used in 14 | advertising or publicity pertaining to distribution of the 15 | software without specific, written prior permission. 16 | 17 | The author disclaims all warranties with regard to this 18 | software, including all implied warranties of merchantability 19 | and fitness. In no event shall the author be liable for any 20 | special, indirect or consequential damages or any damages 21 | whatsoever resulting from loss of use, data or profits, whether 22 | in an action of contract, negligence or other tortious action, 23 | arising out of or in connection with the use or performance of 24 | this software. -------------------------------------------------------------------------------- /audio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(sine_wave) -------------------------------------------------------------------------------- /audio/sine_wave/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # only build I2S example if library is available 2 | if (TARGET pico_audio_i2s) 3 | add_executable(sine_wave_i2s 4 | sine_wave.c 5 | ) 6 | 7 | target_link_libraries(sine_wave_i2s PRIVATE 8 | pico_stdlib 9 | pico_audio_i2s 10 | ) 11 | 12 | target_compile_definitions(sine_wave_i2s PRIVATE 13 | # compile time configuration of I2S 14 | PICO_AUDIO_I2S_MONO_INPUT=1 15 | #define for our example code 16 | USE_AUDIO_I2S=1 17 | # PICO_AUDIO_I2S_DATA_PIN=22 18 | # PICO_AUDIO_I2S_CLOCK_PIN_BASE=23 19 | # PICO_DEFAULT_UART=0 20 | # PICO_DEFAULT_UART_TX_PIN=28 21 | # PICO_DEFAULT_UART_RX_PIN=29 22 | ) 23 | # create map/bin/hex file etc. 24 | pico_add_extra_outputs(sine_wave_i2s) 25 | endif () 26 | 27 | # only build PWM example if library is available 28 | if (TARGET pico_audio_pwm) 29 | add_executable(sine_wave_pwm 30 | sine_wave.c 31 | ) 32 | 33 | target_link_libraries(sine_wave_pwm PRIVATE 34 | pico_stdlib 35 | pico_audio_pwm 36 | ) 37 | 38 | target_compile_definitions(sine_wave_pwm PRIVATE 39 | #define for our example code 40 | USE_AUDIO_PWM=1 41 | ) 42 | # create map/bin/hex file etc. 43 | pico_add_extra_outputs(sine_wave_pwm) 44 | endif () 45 | 46 | # only build S/PDIF example if library is available 47 | if (TARGET pico_audio_spdif) 48 | add_executable(sine_wave_spdif 49 | sine_wave.c 50 | ) 51 | 52 | target_link_libraries(sine_wave_spdif PRIVATE 53 | pico_stdlib 54 | pico_audio_spdif 55 | ) 56 | 57 | target_compile_definitions(sine_wave_spdif PRIVATE 58 | # compile time configuration of S/PDIF 59 | PICO_AUDIO_SPDIF_MONO_INPUT=1 60 | #define for our example code 61 | USE_AUDIO_SPDIF=1 62 | ) 63 | # create map/bin/hex file etc. 64 | pico_add_extra_outputs(sine_wave_spdif) 65 | endif () 66 | -------------------------------------------------------------------------------- /audio/sine_wave/sine_wave.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #if PICO_ON_DEVICE 11 | 12 | #include "hardware/clocks.h" 13 | #include "hardware/structs/clocks.h" 14 | 15 | #endif 16 | 17 | #include "pico/stdlib.h" 18 | 19 | #if USE_AUDIO_I2S 20 | 21 | #include "pico/audio_i2s.h" 22 | 23 | #if PICO_ON_DEVICE 24 | #include "pico/binary_info.h" 25 | bi_decl(bi_3pins_with_names(PICO_AUDIO_I2S_DATA_PIN, "I2S DIN", PICO_AUDIO_I2S_CLOCK_PIN_BASE, "I2S BCK", PICO_AUDIO_I2S_CLOCK_PIN_BASE+1, "I2S LRCK")); 26 | #endif 27 | 28 | #elif USE_AUDIO_PWM 29 | #include "pico/audio_pwm.h" 30 | #elif USE_AUDIO_SPDIF 31 | #include "pico/audio_spdif.h" 32 | #endif 33 | 34 | #define SINE_WAVE_TABLE_LEN 2048 35 | #define SAMPLES_PER_BUFFER 256 36 | 37 | static int16_t sine_wave_table[SINE_WAVE_TABLE_LEN]; 38 | 39 | struct audio_buffer_pool *init_audio() { 40 | 41 | static audio_format_t audio_format = { 42 | .format = AUDIO_BUFFER_FORMAT_PCM_S16, 43 | #if USE_AUDIO_SPDIF 44 | .sample_freq = 44100, 45 | #else 46 | .sample_freq = 24000, 47 | #endif 48 | .channel_count = 1, 49 | }; 50 | 51 | static struct audio_buffer_format producer_format = { 52 | .format = &audio_format, 53 | .sample_stride = 2 54 | }; 55 | 56 | struct audio_buffer_pool *producer_pool = audio_new_producer_pool(&producer_format, 3, 57 | SAMPLES_PER_BUFFER); // todo correct size 58 | bool __unused ok; 59 | const struct audio_format *output_format; 60 | #if USE_AUDIO_I2S 61 | struct audio_i2s_config config = { 62 | .data_pin = PICO_AUDIO_I2S_DATA_PIN, 63 | .clock_pin_base = PICO_AUDIO_I2S_CLOCK_PIN_BASE, 64 | .dma_channel = 0, 65 | .pio_sm = 0, 66 | }; 67 | 68 | output_format = audio_i2s_setup(&audio_format, &config); 69 | if (!output_format) { 70 | panic("PicoAudio: Unable to open audio device.\n"); 71 | } 72 | 73 | ok = audio_i2s_connect(producer_pool); 74 | assert(ok); 75 | audio_i2s_set_enabled(true); 76 | #elif USE_AUDIO_PWM 77 | output_format = audio_pwm_setup(&audio_format, -1, &default_mono_channel_config); 78 | if (!output_format) { 79 | panic("PicoAudio: Unable to open audio device.\n"); 80 | } 81 | ok = audio_pwm_default_connect(producer_pool, false); 82 | assert(ok); 83 | audio_pwm_set_enabled(true); 84 | #elif USE_AUDIO_SPDIF 85 | output_format = audio_spdif_setup(&audio_format, &audio_spdif_default_config); 86 | if (!output_format) { 87 | panic("PicoAudio: Unable to open audio device.\n"); 88 | } 89 | //ok = audio_spdif_connect(producer_pool); 90 | ok = audio_spdif_connect(producer_pool); 91 | assert(ok); 92 | audio_spdif_set_enabled(true); 93 | #endif 94 | return producer_pool; 95 | } 96 | 97 | int main() { 98 | #if PICO_ON_DEVICE 99 | #if USE_AUDIO_PWM 100 | set_sys_clock_48mhz(); 101 | #endif 102 | #endif 103 | 104 | stdio_init_all(); 105 | 106 | for (int i = 0; i < SINE_WAVE_TABLE_LEN; i++) { 107 | sine_wave_table[i] = 32767 * cosf(i * 2 * (float) (M_PI / SINE_WAVE_TABLE_LEN)); 108 | } 109 | 110 | struct audio_buffer_pool *ap = init_audio(); 111 | uint32_t step = 0x200000; 112 | uint32_t pos = 0; 113 | uint32_t pos_max = 0x10000 * SINE_WAVE_TABLE_LEN; 114 | uint vol = 128; 115 | while (true) { 116 | #if USE_AUDIO_PWM 117 | enum audio_correction_mode m = audio_pwm_get_correction_mode(); 118 | #endif 119 | int c = getchar_timeout_us(0); 120 | if (c >= 0) { 121 | if (c == '-' && vol) vol -= 4; 122 | if ((c == '=' || c == '+') && vol < 255) vol += 4; 123 | if (c == '[' && step > 0x10000) step -= 0x10000; 124 | if (c == ']' && step < (SINE_WAVE_TABLE_LEN / 16) * 0x20000) step += 0x10000; 125 | if (c == 'q') break; 126 | #if USE_AUDIO_PWM 127 | if (c == 'c') { 128 | bool done = false; 129 | while (!done) { 130 | if (m == none) m = fixed_dither; 131 | else if (m == fixed_dither) m = dither; 132 | else if (m == dither) m = noise_shaped_dither; 133 | else if (m == noise_shaped_dither) m = none; 134 | done = audio_pwm_set_correction_mode(m); 135 | } 136 | } 137 | printf("vol = %d, step = %d mode = %d \r", vol, step >>16, m); 138 | #else 139 | printf("vol = %d, step = %d \r", vol, step >> 16); 140 | #endif 141 | } 142 | struct audio_buffer *buffer = take_audio_buffer(ap, true); 143 | int16_t *samples = (int16_t *) buffer->buffer->bytes; 144 | for (uint i = 0; i < buffer->max_sample_count; i++) { 145 | samples[i] = (vol * sine_wave_table[pos >> 16u]) >> 8u; 146 | pos += step; 147 | if (pos >= pos_max) pos -= pos_max; 148 | } 149 | buffer->sample_count = buffer->max_sample_count; 150 | give_audio_buffer(ap, buffer); 151 | } 152 | puts("\n"); 153 | return 0; 154 | } 155 | -------------------------------------------------------------------------------- /pico_extras_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_extras_import.cmake 2 | 3 | # This can be dropped into an external project to help locate pico-extras 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_EXTRAS_PATH} AND (NOT PICO_EXTRAS_PATH)) 7 | set(PICO_EXTRAS_PATH $ENV{PICO_EXTRAS_PATH}) 8 | message("Using PICO_EXTRAS_PATH from environment ('${PICO_EXTRAS_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT)) 12 | set(PICO_EXTRAS_FETCH_FROM_GIT $ENV{PICO_EXTRAS_FETCH_FROM_GIT}) 13 | message("Using PICO_EXTRAS_FETCH_FROM_GIT from environment ('${PICO_EXTRAS_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT_PATH)) 17 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH $ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_EXTRAS_FETCH_FROM_GIT_PATH from environment ('${PICO_EXTRAS_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | if (NOT PICO_EXTRAS_PATH) 22 | if (PICO_EXTRAS_FETCH_FROM_GIT) 23 | include(FetchContent) 24 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 25 | if (PICO_EXTRAS_FETCH_FROM_GIT_PATH) 26 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 27 | endif () 28 | FetchContent_Declare( 29 | pico_extras 30 | GIT_REPOSITORY https://github.com/raspberrypi/pico-extras 31 | GIT_TAG master 32 | ) 33 | if (NOT pico_extras) 34 | message("Downloading PICO EXTRAS") 35 | FetchContent_Populate(pico_extras) 36 | set(PICO_EXTRAS_PATH ${pico_extras_SOURCE_DIR}) 37 | endif () 38 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 39 | else () 40 | if (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pico-extras") 41 | set(PICO_EXTRAS_PATH ${PICO_SDK_PATH}/../pico-extras) 42 | message("Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: ${PICO_EXTRAS_PATH}") 43 | else() 44 | message(FATAL_ERROR 45 | "PICO EXTRAS location was not specified. Please set PICO_EXTRAS_PATH or set PICO_EXTRAS_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif() 48 | endif () 49 | endif () 50 | 51 | set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to the PICO EXTRAS") 52 | set(PICO_EXTRAS_FETCH_FROM_GIT "${PICO_EXTRAS_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO EXTRAS from git if not otherwise locatable") 53 | set(PICO_EXTRAS_FETCH_FROM_GIT_PATH "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download EXTRAS") 54 | 55 | get_filename_component(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 56 | if (NOT EXISTS ${PICO_EXTRAS_PATH}) 57 | message(FATAL_ERROR "Directory '${PICO_EXTRAS_PATH}' not found") 58 | endif () 59 | 60 | set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH} CACHE PATH "Path to the PICO EXTRAS" FORCE) 61 | 62 | add_subdirectory(${PICO_EXTRAS_PATH} pico_extras) 63 | -------------------------------------------------------------------------------- /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 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 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 | "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}) 74 | -------------------------------------------------------------------------------- /reset/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory_exclude_platforms(hello_reset) -------------------------------------------------------------------------------- /reset/hello_reset/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (TARGET hardware_reset) 2 | add_executable(hello_reset 3 | hello_reset.c 4 | ) 5 | 6 | # Pull in our pico_stdlib which pulls in commonly used features 7 | target_link_libraries(hello_reset pico_stdlib) 8 | 9 | # create map/bin/hex file etc. 10 | pico_add_extra_outputs(hello_reset) 11 | 12 | # add url via pico_set_program_url 13 | example_auto_set_url(hello_reset) 14 | endif () -------------------------------------------------------------------------------- /reset/hello_reset/hello_reset.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include "pico/stdlib.h" 9 | #include "hardware/resets.h" 10 | 11 | // tag::hello_reset[] 12 | int main() { 13 | stdio_init_all(); 14 | 15 | printf("Hello, reset!\n"); 16 | 17 | // Put the PWM block into reset 18 | reset_block(RESETS_RESET_PWM_RST_N_BITS); 19 | 20 | // And bring it out 21 | unreset_block_wait(RESETS_RESET_PWM_RST_N_BITS); 22 | 23 | // Put the PWM and RTC block into reset 24 | reset_block(RESETS_RESET_PWM_RST_N_BITS | RESETS_RESET_RTC_RST_N_BITS); 25 | 26 | // Wait for both to come out of reset 27 | unreset_block_wait(RESETS_RESET_PWM_RST_N_BITS | RESETS_RESET_RTC_RST_N_BITS); 28 | 29 | return 0; 30 | } 31 | // end::hello_reset[] 32 | -------------------------------------------------------------------------------- /scanvideo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (TARGET pico_scanvideo) # not all build types support it 2 | # Libs 3 | add_subdirectory_exclude_platforms(render) 4 | add_subdirectory_exclude_platforms(sprite) 5 | # Apps 6 | add_subdirectory_exclude_platforms(demo1) 7 | add_subdirectory_exclude_platforms(demo2 "rp2040") 8 | add_subdirectory_exclude_platforms(flash_stream "rp2350.*") 9 | add_subdirectory_exclude_platforms(hscroll_dma_tiles) 10 | add_subdirectory_exclude_platforms(mandelbrot) 11 | add_subdirectory_exclude_platforms(mario_tiles) 12 | add_subdirectory_exclude_platforms(scanvideo_minimal) 13 | add_subdirectory_exclude_platforms(sprite_demo "rp2350-riscv") 14 | add_subdirectory_exclude_platforms(test_pattern) 15 | add_subdirectory_exclude_platforms(textmode) 16 | endif() -------------------------------------------------------------------------------- /scanvideo/Raspberry Pi Pico to VGA Connection Schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspberrypi/pico-playground/49a701a955ada550f8e7221aadef354eb9ff7b62/scanvideo/Raspberry Pi Pico to VGA Connection Schematic.png -------------------------------------------------------------------------------- /scanvideo/demo1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (TARGET pico_scanvideo_dpi) 2 | add_executable(demo1 3 | demo1.c 4 | data.c 5 | data.h 6 | ) 7 | 8 | target_compile_definitions(demo1 9 | PRIVATE 10 | #PICO_SCANVIDEO_48MHZ=1 11 | # commented out as video overlay is distracting 12 | # PICO_SCANVIDEO_PLANE_COUNT=3 13 | ) 14 | 15 | if (PICO_RP2350) 16 | target_compile_definitions(demo1 PRIVATE 17 | PICO_USE_SW_SPIN_LOCKS=0 18 | ) 19 | endif() 20 | target_link_libraries(demo1 PRIVATE pico_stdlib pico_scanvideo_dpi render pico_multicore) 21 | pico_add_extra_outputs(demo1) 22 | endif() -------------------------------------------------------------------------------- /scanvideo/demo1/data.h: -------------------------------------------------------------------------------- 1 | #ifndef SOFTWARE_DATA_H 2 | #define SOFTWARE_DATA_H 3 | #include "image.h" 4 | 5 | extern const struct palette32 welcome_palette; 6 | extern const struct palette32 pi_palette; 7 | extern const struct image_data welcome_image_data; 8 | extern const struct image_data pi400_image_data; 9 | 10 | #endif //SOFTWARE_DATA_H 11 | -------------------------------------------------------------------------------- /scanvideo/demo1/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspberrypi/pico-playground/49a701a955ada550f8e7221aadef354eb9ff7b62/scanvideo/demo1/screenshot.jpg -------------------------------------------------------------------------------- /scanvideo/demo2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (TARGET pico_scanvideo_dpi) 2 | add_executable(demo2 demo2.c data2.c) 3 | 4 | if (PICO_RP2350) 5 | target_compile_definitions(demo2 PRIVATE 6 | #PICO_SCANVIDEO_48MHZ=1 7 | PICO_USE_SW_SPIN_LOCKS=0 8 | PICO_SCANVIDEO_SCANLINE_BUFFER_COUNT=16 # a few extra for amortization 9 | ) 10 | endif() 11 | target_link_libraries(demo2 PRIVATE pico_stdlib pico_scanvideo_dpi pico_multicore render) 12 | pico_add_extra_outputs(demo2) 13 | endif() -------------------------------------------------------------------------------- /scanvideo/demo2/data2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "pico.h" 3 | #include "image.h" 4 | 5 | typedef struct { 6 | size_t size; 7 | const uint8_t *bytes; 8 | } blob; 9 | 10 | extern const struct palette32 pi_palette; 11 | extern const struct palette32 riscv_palette; 12 | extern const struct image_data pi400_image_data; 13 | extern const struct image_data riscv_image_data; 14 | -------------------------------------------------------------------------------- /scanvideo/demo2/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspberrypi/pico-playground/49a701a955ada550f8e7221aadef354eb9ff7b62/scanvideo/demo2/screenshot.jpg -------------------------------------------------------------------------------- /scanvideo/flash_stream/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (PICO_ON_DEVICE AND TARGET pico_scanvideo_dpi) 2 | add_executable(flash_stream 3 | flash_stream.c 4 | ) 5 | target_link_libraries(flash_stream PRIVATE 6 | pico_stdlib 7 | pico_scanvideo_dpi 8 | hardware_dma 9 | ) 10 | target_compile_definitions(flash_stream PRIVATE 11 | PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS=500 12 | ) 13 | pico_add_extra_outputs(flash_stream) 14 | endif () 15 | -------------------------------------------------------------------------------- /scanvideo/flash_stream/flash_stream.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | 9 | #include "pico.h" 10 | #include "pico/scanvideo.h" 11 | #include "pico/scanvideo/composable_scanline.h" 12 | #include "pico/sync.h" 13 | #include "pico/stdlib.h" 14 | 15 | #include "hardware/clocks.h" 16 | #include "hardware/structs/dma.h" 17 | #include "hardware/structs/ssi.h" 18 | 19 | // This app must be built with PICO_COPY_TO_RAM=1 20 | 21 | #define FLASH_IMAGE_BASE 0x1003c000 22 | #define FLASH_IMAGE_SCANLINE_SIZE (640 * sizeof(uint16_t)) 23 | #define FLASH_IMAGE_SIZE (FLASH_IMAGE_SCANLINE_SIZE * 480) 24 | #define FLASH_N_IMAGES 3 25 | #define FRAMES_PER_IMAGE 300 26 | 27 | #define VGA_MODE vga_mode_640x480_60 28 | extern const struct scanvideo_pio_program video_24mhz_composable; 29 | 30 | static void frame_update_logic(); 31 | static void render_scanline(struct scanvideo_scanline_buffer *dest); 32 | 33 | int __time_critical_func(render_loop)() { 34 | static uint32_t last_frame_num = 0; 35 | while (true) { 36 | struct scanvideo_scanline_buffer *scanline_buffer = scanvideo_begin_scanline_generation(true); 37 | uint32_t frame_num = scanvideo_frame_number(scanline_buffer->scanline_id); 38 | if (frame_num != last_frame_num) { 39 | last_frame_num = frame_num; 40 | frame_update_logic(); 41 | } 42 | render_scanline(scanline_buffer); 43 | scanvideo_end_scanline_generation(scanline_buffer); 44 | } 45 | } 46 | 47 | int vga_main(void) { 48 | scanvideo_setup(&VGA_MODE); 49 | scanvideo_timing_enable(true); 50 | render_loop(); 51 | return 0; 52 | } 53 | 54 | const uint16_t *img_base = (const uint16_t *) FLASH_IMAGE_BASE; 55 | 56 | void __time_critical_func(frame_update_logic)() { 57 | static uint slideshow_ctr = 0; 58 | static uint image_index = 0; 59 | if (++slideshow_ctr >= FRAMES_PER_IMAGE) { 60 | slideshow_ctr = 0; 61 | image_index = (image_index + 1) % FLASH_N_IMAGES; 62 | img_base = (const uint16_t *) (FLASH_IMAGE_BASE + FLASH_IMAGE_SIZE * image_index); 63 | } 64 | } 65 | 66 | static inline uint16_t *raw_scanline_prepare(struct scanvideo_scanline_buffer *dest, uint width) { 67 | assert(width >= 3); 68 | assert(width % 2 == 0); 69 | // +1 for the black pixel at the end, -3 because the program outputs n+3 pixels. 70 | dest->data[0] = COMPOSABLE_RAW_RUN | (width + 1 - 3 << 16); 71 | // After user pixels, 1 black pixel then discard remaining FIFO data 72 | dest->data[width / 2 + 2] = 0x0000u | (COMPOSABLE_EOL_ALIGN << 16); 73 | dest->data_used = width / 2 + 2; 74 | assert(dest->data_used <= dest->data_max); 75 | return (uint16_t *) &dest->data[1]; 76 | } 77 | 78 | static inline void raw_scanline_finish(struct scanvideo_scanline_buffer *dest) { 79 | // Need to pivot the first pixel with the count so that PIO can keep up 80 | // with its 1 pixel per 2 clocks 81 | uint32_t first = dest->data[0]; 82 | uint32_t second = dest->data[1]; 83 | dest->data[0] = (first & 0x0000ffffu) | ((second & 0x0000ffffu) << 16); 84 | dest->data[1] = (second & 0xffff0000u) | ((first & 0xffff0000u) >> 16); 85 | dest->status = SCANLINE_OK; 86 | } 87 | 88 | // Use direct SSI DMA for maximum transfer speed (but cannot execute from 89 | // flash at the same time) 90 | void __no_inline_not_in_flash_func(flash_bulk_read)(uint32_t *rxbuf, uint32_t flash_offs, size_t len, 91 | uint dma_chan) { 92 | ssi_hw->ssienr = 0; 93 | ssi_hw->ctrlr1 = len - 1; // NDF, number of data frames (32b each) 94 | ssi_hw->dmacr = SSI_DMACR_TDMAE_BITS | SSI_DMACR_RDMAE_BITS; 95 | ssi_hw->ssienr = 1; 96 | 97 | dma_hw->ch[dma_chan].read_addr = (uint32_t) &ssi_hw->dr0; 98 | dma_hw->ch[dma_chan].write_addr = (uint32_t) rxbuf; 99 | dma_hw->ch[dma_chan].transfer_count = len; 100 | // Must enable DMA byteswap because non-XIP 32-bit flash transfers are 101 | // big-endian on SSI (we added a hardware tweak to make XIP sensible) 102 | dma_hw->ch[dma_chan].ctrl_trig = 103 | DMA_CH0_CTRL_TRIG_BSWAP_BITS | 104 | DREQ_XIP_SSIRX << DMA_CH0_CTRL_TRIG_TREQ_SEL_LSB | 105 | dma_chan << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB | 106 | DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS | 107 | DMA_CH0_CTRL_TRIG_DATA_SIZE_VALUE_SIZE_WORD << DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB | 108 | DMA_CH0_CTRL_TRIG_EN_BITS; 109 | 110 | // Now DMA is waiting, kick off the SSI transfer (mode continuation bits in LSBs) 111 | ssi_hw->dr0 = (flash_offs << 8) | 0xa0; 112 | 113 | while (dma_hw->ch[dma_chan].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS) 114 | tight_loop_contents(); 115 | 116 | ssi_hw->ssienr = 0; 117 | ssi_hw->ctrlr1 = 0; 118 | ssi_hw->dmacr = 0; 119 | ssi_hw->ssienr = 1; 120 | } 121 | 122 | void __time_critical_func(render_scanline)(struct scanvideo_scanline_buffer *dest) { 123 | int l = scanvideo_scanline_number(dest->scanline_id); 124 | uint16_t *colour_buf = raw_scanline_prepare(dest, VGA_MODE.width); 125 | // Just use a random DMA channel which hopefully nobody minds us borrowing 126 | // "It's easier to seek forgiveness than permission, unless you hardfault" 127 | flash_bulk_read( 128 | (uint32_t *) colour_buf, 129 | (uint32_t) img_base + l * FLASH_IMAGE_SCANLINE_SIZE, 130 | FLASH_IMAGE_SCANLINE_SIZE / sizeof(uint32_t), 131 | 11 132 | ); 133 | raw_scanline_finish(dest); 134 | } 135 | 136 | int main(void) { 137 | set_sys_clock_khz(200000, true); 138 | setup_default_uart(); 139 | 140 | #ifdef PICO_SMPS_MODE_PIN 141 | gpio_init(PICO_SMPS_MODE_PIN); 142 | gpio_set_dir(PICO_SMPS_MODE_PIN, GPIO_OUT); 143 | gpio_put(PICO_SMPS_MODE_PIN, 1); 144 | #endif 145 | 146 | return vga_main(); 147 | } 148 | -------------------------------------------------------------------------------- /scanvideo/flash_stream/img/.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | *.uf2 3 | -------------------------------------------------------------------------------- /scanvideo/flash_stream/img/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Upstream-Name: ubuntu-wallpapers 2 | Source: https://launchpad.net/ubuntu-wallpapers 3 | 4 | Files: *.png 5 | Copyright: 2018-2020 Ubuntu community contributors 6 | License: CC-BY-SA 3.0 7 | 8 | The wallpapers used are: 9 | 10 | Lighthouse at sunrise, by Frenchie Smalls 11 | Stone Mountain, by Brad Huchteman 12 | Voss, by fortuneblues 13 | -------------------------------------------------------------------------------- /scanvideo/flash_stream/img/Lighthouse_at_sunrise_by_Frenchie_Smalls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspberrypi/pico-playground/49a701a955ada550f8e7221aadef354eb9ff7b62/scanvideo/flash_stream/img/Lighthouse_at_sunrise_by_Frenchie_Smalls.png -------------------------------------------------------------------------------- /scanvideo/flash_stream/img/Stone_Mountain_by_Brad_Huchteman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspberrypi/pico-playground/49a701a955ada550f8e7221aadef354eb9ff7b62/scanvideo/flash_stream/img/Stone_Mountain_by_Brad_Huchteman.png -------------------------------------------------------------------------------- /scanvideo/flash_stream/img/Voss_by_fortuneblues.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspberrypi/pico-playground/49a701a955ada550f8e7221aadef354eb9ff7b62/scanvideo/flash_stream/img/Voss_by_fortuneblues.png -------------------------------------------------------------------------------- /scanvideo/flash_stream/img/pack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | rm -f *.bin pack.uf2 5 | ./packtiles -sdf bgar5515 Lighthouse_at_sunrise_by_Frenchie_Smalls.png lighthouse.bin 6 | ./packtiles -sdf bgar5515 Stone_Mountain_by_Brad_Huchteman.png stone_mountain.bin 7 | ./packtiles -sdf bgar5515 Voss_by_fortuneblues.png voss.bin 8 | cat *.bin > pack.bin 9 | uf2conv -f pico -b 0x1003c000 pack.bin -o pack.uf2 -------------------------------------------------------------------------------- /scanvideo/flash_stream/img/packtiles: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Public Domain software (c) Luke Wren 3 | 4 | from PIL import Image 5 | import argparse 6 | import struct 7 | import sys 8 | 9 | FORMATS = ["argb1555", "rgab5515", "bgar5515", "rgb565", "argb1232", "ragb2132", "rgb332", "p8", "p4", "p2", "p1"] 10 | 11 | def bytes_from_bitstream_le(bitstream): 12 | accum = 0 13 | accum_size = 0 14 | while True: 15 | while accum_size < 8: 16 | try: 17 | nbits, newdata = next(bitstream) 18 | except StopIteration: 19 | return 20 | accum = accum | (newdata << accum_size) 21 | accum_size += nbits 22 | while accum_size >= 8: 23 | yield accum & 0xff 24 | accum = accum >> 8 25 | accum_size -= 8 26 | 27 | class BinHeader: 28 | def __init__(self, filename, arrayname=None): 29 | if arrayname is None: 30 | arrayname = filename.split(".")[0] 31 | self.f = open(filename, "w") 32 | self.out_count = 0 33 | self.f.write("static char __attribute__((aligned(4))) {}[] = {{\n\t".format(arrayname)) 34 | 35 | def write(self, bs): 36 | for b in bs: 37 | self.f.write("0x{:02x}".format(b) + (",\n\t" if self.out_count % 16 == 15 else ", ")) 38 | self.out_count += 1 39 | 40 | def close(self): 41 | self.f.write("\n};\n") 42 | self.f.close() 43 | 44 | # Fixed dither -- note every number 0...15 appears once (thanks Graham) 45 | dither_pattern_4x4 = [ 46 | [0 , 8 , 2 , 10], 47 | [12 , 4 , 14 , 6 ], 48 | [3 , 11 , 1 , 9 ], 49 | [15 , 7 , 13 , 5 ], 50 | ] 51 | 52 | def format_channel(data, msb, lsb, dither=False, dithercoord=None): 53 | # Assume data to be 8 bits 54 | out_width = msb - lsb + 1 55 | assert(out_width <= 8) 56 | if dither: 57 | ditherval = dither_pattern_4x4[dithercoord[1] % 4][dithercoord[0] % 4] 58 | shamt = (8 - out_width) - 4 59 | if shamt >= 0: 60 | data += ditherval << shamt 61 | else: 62 | data += ditherval >> -shamt 63 | data = min(data, 0xff) 64 | return (data >> (8 - out_width)) << lsb 65 | 66 | def format_rgb_pixel(pix, fmt, dither=False, dithercoord=None): 67 | accum = 0 68 | for p, f in zip(pix, fmt): 69 | accum |= format_channel(p, f[0], f[1], dither, dithercoord) 70 | if len(pix) == len(fmt) - 1: 71 | accum |= format_channel(0xff, fmt[-1][0], fmt[-1][1]) 72 | return accum 73 | 74 | # TODO would be kind of nice to generate these based on format string but I don't need that yet 75 | rgb_formats = { 76 | "argb1555": (16, ((14, 10), (9, 5), (4, 0), (15, 15))), 77 | "rgab5515": (16, ((15, 11), (10, 6), (4, 0), (5, 5))), 78 | "bgar5515": (16, ((4, 0), (10, 6), (15, 11), (5, 5))), 79 | "rgb565" : (16, ((15, 11), (10, 5), (4, 0))), 80 | "argb1232": (8, ((6, 5), (4, 2), (1, 0), (7, 7))), 81 | "ragb2132": (8, ((7, 6), (4, 2), (1, 0), (5, 5))), 82 | "rgb332" : (8, ((7, 5), (4, 2), (1, 0))), 83 | } 84 | 85 | def format_pixel(format, src_has_transparency, pixel, dither=False, dithercoord=None): 86 | assert(format in FORMATS) 87 | if format in rgb_formats: 88 | return (rgb_formats[format][0], format_rgb_pixel(pixel, rgb_formats[format][1], dither, dithercoord)) 89 | elif format in ["p8", "p4", "p2", "p1"]: 90 | size = int(format[1:]) 91 | return (size, (pixel + src_has_transparency) & ((1 << size) - 1)) 92 | else: 93 | raise Exception() 94 | 95 | if __name__ == "__main__": 96 | parser = argparse.ArgumentParser() 97 | parser.add_argument("input", help="Input file name") 98 | parser.add_argument("output", help="Output file name") 99 | parser.add_argument("--tilesize", "-t", help="Tile size (pixels), default 8", 100 | default="8", choices=[str(2 ** i) for i in range(3, 11)]) 101 | parser.add_argument("--single", "-s", action="store_true", 102 | help="The input consists of a single image of arbitrary width/height, rather than a tileset") 103 | parser.add_argument("--format", "-f", help="Output pixel format, default argb1555", 104 | default="argb1555", choices=FORMATS) 105 | parser.add_argument("--dither", "-d", action="store_true", 106 | help="Apply a simple fixed dither pattern when packing RGB files") 107 | parser.add_argument("--metadata", "-m", action="store_true", 108 | help="Write out opacity metadata at end of file for faster alpha blit (must be used with --single)") 109 | args = parser.parse_args() 110 | img = Image.open(args.input) 111 | if args.single: 112 | tsize_x = img.width 113 | tsize_y = img.height 114 | else: 115 | tsize_x = int(args.tilesize) 116 | tsize_y = tsize_x 117 | if args.metadata and not args.single: 118 | sys.exit("--metadata must be used with --single") 119 | 120 | format_is_paletted = args.format.startswith("p") 121 | image_is_transparent = img.mode == "RGBA" and img.getextrema()[3][0] < 255 122 | if args.metadata and not image_is_transparent: 123 | sys.exit("Can't write opacity metadata for a non-transparent image") 124 | 125 | if format_is_paletted: 126 | ncolours_max = 1 << int(args.format[1:]) 127 | ncolours_actual = min(ncolours_max, len(img.getcolors())) 128 | pimg = img.quantize(ncolours_max) 129 | palette = pimg.getpalette() 130 | # TODO haven't found a sane way to make PIL map transparency to palette 131 | if image_is_transparent: 132 | for x in range(img.width): 133 | for y in range(img.height): 134 | if not (img.getpixel((x, y))[3] & 0x80): 135 | pimg.putpixel((x, y), 255) 136 | 137 | with open(args.output + ".pal", "wb") as pfile: 138 | if image_is_transparent: 139 | pfile.write(bytes(2)) 140 | pfile.write(bytes(bytes_from_bitstream_le( 141 | format_pixel("argb1555", False, palette[i:i+3]) for i in range(0, ncolours_actual * 3, 3) 142 | ))) 143 | if ncolours_actual < ncolours_max: 144 | pfile.write(bytes(2 * (ncolours_max - ncolours_actual))) 145 | img = pimg 146 | 147 | if args.output.endswith(".h"): 148 | ofile = BinHeader(args.output, arrayname = args.input.split(".")[0]) 149 | else: 150 | ofile = open(args.output, "wb") 151 | 152 | for y in range(0, img.height - (tsize_y - 1), tsize_y): 153 | for x in range(0, img.width - (tsize_x - 1), tsize_x): 154 | tile = img.crop((x, y, x + tsize_x, y + tsize_y)) 155 | ofile.write(bytes(bytes_from_bitstream_le( 156 | format_pixel(args.format, image_is_transparent, tile.getpixel((i, j)), args.dither, dithercoord=(i, j)) for j in range(tsize_y) for i in range(tsize_x) 157 | ))) 158 | if args.metadata: 159 | assert(tsize_x * tsize_y % 4 == 0) 160 | for y in range(0, tsize_y): 161 | opacity = list(img.getpixel((x, y))[3] >= 128 for x in range(tsize_x)) 162 | try: 163 | first_transparent = opacity.index(True) 164 | last_transparent = tsize_x - 1 - list(reversed(opacity)).index(True) 165 | continuous_span = all(opacity[first_transparent:last_transparent + 1]) 166 | ofile.write(struct.pack(" 8 | #include 9 | #include "pico.h" 10 | #include "pico/scanvideo.h" 11 | #include "pico/sync.h" 12 | #include "pico/scanvideo/composable_scanline.h" 13 | #include "hardware/gpio.h" 14 | #include "pico/stdlib.h" 15 | #include "pico/multicore.h" 16 | 17 | #if PICO_ON_DEVICE 18 | 19 | #include "hardware/clocks.h" 20 | #include "hardware/vreg.h" 21 | 22 | #endif 23 | 24 | CU_REGISTER_DEBUG_PINS(generation) 25 | 26 | #define vga_mode vga_mode_320x240_60 27 | 28 | #if PICO_RISCV 29 | // seems to be an issue with IRQ timing so moving onto core 1 for now 30 | #define ALARM_POOL_ON_CORE1 1 31 | #endif 32 | 33 | #ifndef USE_FLOAT 34 | #if PICO_RP2350 && !PICO_RISCV 35 | #define USE_FLOAT 1 // fastest 36 | #endif 37 | #endif 38 | //#define USE_FLOAT 1 39 | 40 | #if USE_FLOAT 41 | typedef float fixed; 42 | //typedef double fixed; 43 | static inline fixed float_to_fixed(float x) { 44 | return x; 45 | } 46 | static inline fixed fixed_mult(fixed a, fixed b) { 47 | return a*b; 48 | } 49 | static inline fixed double_to_fixed(double x) { 50 | return (fixed)x; 51 | } 52 | #else 53 | #define FRAC_BITS 25u 54 | typedef int32_t fixed; 55 | 56 | static inline fixed float_to_fixed(float x) { 57 | return (fixed) (x * (float) (1u << FRAC_BITS)); 58 | } 59 | 60 | static inline fixed double_to_fixed(double x) { 61 | return (fixed) (x * (double) (1u << FRAC_BITS)); 62 | } 63 | 64 | #if !PICO_ON_DEVICE || (FRAC_BITS != 25) || defined(__riscv) 65 | static inline fixed fixed_mult(fixed a, fixed b) { 66 | int64_t r = ((int64_t) a) * b; 67 | return (int32_t) (r >> FRAC_BITS); 68 | } 69 | #else 70 | // Since we're trying to go fast, do a better multiply of 32x32 preserving the bits we want 71 | static inline fixed fixed_mult(fixed a, fixed b) { 72 | #if __ARM_ARCH_6M__ 73 | uint32_t tmp1, tmp2, tmp3; 74 | __asm__ volatile ( 75 | ".syntax unified\n" 76 | "asrs %[r_tmp1], %[r_b], #16 \n" // r_tmp1 = BH 77 | "uxth %[r_tmp2], %[r_a] \n" // r_tmp2 = AL 78 | "muls %[r_tmp2], %[r_tmp1] \n" // r_tmp2 = BH * AL 79 | "asrs %[r_tmp3], %[r_a], #16 \n" // r_tmp3 = AH 80 | "muls %[r_tmp1], %[r_tmp3] \n" // r_tmp1 = BH * AH 81 | "uxth %[r_b], %[r_b] \n" // r_b = BL 82 | "uxth %[r_a], %[r_a] \n" // r_a = AL 83 | "muls %[r_a], %[r_b] \n" // r_a = AL * BL 84 | "muls %[r_b], %[r_tmp3] \n" // r_b = BL * AH 85 | "add %[r_b], %[r_tmp2] \n" // r_b = BL * AH + BH * AL 86 | "lsls %[r_tmp1], #32 - 25 \n" // r_tmp1 = (BH * AH) >> (32 - FRAC_BITS) 87 | "lsrs %[r_a], #16 \n" // r_a = (AL & BL) H 88 | "add %[r_a], %[r_b] \n" 89 | "asrs %[r_a], #25- 16 \n" // r_a = (BL * AH + BH * AL) H | (AL & BL) H >> (32 - FRAC_BITS) 90 | "add %[r_a], %[r_tmp1] \n" 91 | : [r_a] "+l" (a), [r_b] "+l" (b), [r_tmp1] "=&l" (tmp1), [r_tmp2] "=&l" (tmp2), [r_tmp3] "=&l" (tmp3) 92 | : 93 | ); 94 | return a; 95 | #else 96 | return (fixed) ((((uint64_t)a) * b) >> FRAC_BITS); 97 | #endif 98 | } 99 | #endif 100 | 101 | #endif 102 | 103 | #define max_iters 127//255 104 | 105 | struct mutex frame_logic_mutex; 106 | static void frame_update_logic(); 107 | 108 | void fill_scanline_buffer(struct scanvideo_scanline_buffer *buffer); 109 | static uint y; 110 | static fixed x0, y0; 111 | static fixed dx0_dx, dy0_dy; 112 | static fixed max; 113 | static bool params_ready; 114 | 115 | static uint16_t framebuffer[320 * 240]; 116 | //static uint16_t *framebuffer; 117 | 118 | static uint16_t colors[16] = { 119 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(66, 30, 15), 120 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(25, 7, 26), 121 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(9, 1, 47), 122 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(4, 4, 73), 123 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(0, 7, 100), 124 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(12, 44, 138), 125 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(24, 82, 177), 126 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(57, 125, 209), 127 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(134, 181, 229), 128 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(211, 236, 248), 129 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(241, 233, 191), 130 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(248, 201, 95), 131 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(255, 170, 0), 132 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(204, 128, 0), 133 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(153, 87, 0), 134 | PICO_SCANVIDEO_PIXEL_FROM_RGB8(106, 52, 3), 135 | }; 136 | 137 | static void scanline(uint16_t *line_buffer, uint length, fixed mx, fixed my, fixed dmx_dx) { 138 | for (int x = 0; x < length; ++x) { 139 | int iters; 140 | fixed cr = mx; 141 | fixed ci = my; 142 | fixed zr = cr; 143 | fixed zi = ci; 144 | fixed xold = 0; 145 | fixed yold = 0; 146 | int period = 0; 147 | for (iters = 0; iters < max_iters; ++iters) { 148 | fixed zr2 = fixed_mult(zr, zr); 149 | fixed zi2 = fixed_mult(zi, zi); 150 | if (zr2 + zi2 > max) { 151 | break; 152 | } 153 | fixed zrtemp = zr2 - zi2 + cr; 154 | zi = 2 * fixed_mult(zr, zi) + ci; 155 | zr = zrtemp; 156 | if (zr == xold && zi == yold) { 157 | iters = max_iters + 1; 158 | break; 159 | } 160 | if (++period > 20) { 161 | period = 0; 162 | xold = zr; 163 | yold = zi; 164 | } 165 | } 166 | if (iters == max_iters + 1) { 167 | line_buffer[x] = 0;//x1f; 168 | } else { 169 | line_buffer[x] = iters == max_iters ? 0 : colors[iters & 15u]; 170 | } 171 | mx += dmx_dx; 172 | } 173 | } 174 | 175 | // "Worker thread" for each core 176 | void __time_critical_func(render_loop)() { 177 | static uint32_t last_frame_num = 0; 178 | int core_num = get_core_num(); 179 | printf("Rendering on core %d\n", core_num); 180 | while (true) { 181 | mutex_enter_blocking(&frame_logic_mutex); 182 | if (y == vga_mode.height) { 183 | params_ready = false; 184 | frame_update_logic(); 185 | y = 0; 186 | } 187 | uint _y = y++; 188 | fixed _x0 = x0, _y0 = y0; 189 | fixed _dx0_dx = dx0_dx, _dy0_dy = dy0_dy; 190 | mutex_exit(&frame_logic_mutex); 191 | 192 | scanline(framebuffer + _y * 320, 320, _x0, _y0 + _dy0_dy * _y, _dx0_dx); 193 | #if !PICO_ON_DEVICE 194 | struct scanvideo_scanline_buffer *buffer = scanvideo_begin_scanline_generation(true); 195 | fill_scanline_buffer(buffer); 196 | scanvideo_end_scanline_generation(buffer); 197 | #endif 198 | } 199 | } 200 | 201 | int64_t timer_callback(alarm_id_t alarm_id, void *user_data) { 202 | struct scanvideo_scanline_buffer *buffer = scanvideo_begin_scanline_generation(false); 203 | while (buffer) { 204 | fill_scanline_buffer(buffer); 205 | scanvideo_end_scanline_generation(buffer); 206 | buffer = scanvideo_begin_scanline_generation(false); 207 | } 208 | return 100; 209 | } 210 | 211 | void fill_scanline_buffer(struct scanvideo_scanline_buffer *buffer) { 212 | static uint32_t postamble[] = { 213 | 0x0000u | (COMPOSABLE_EOL_ALIGN << 16) 214 | }; 215 | 216 | buffer->data[0] = 4; 217 | buffer->data[1] = host_safe_hw_ptr(buffer->data + 8); 218 | buffer->data[2] = 158; // first four pixels are handled separately 219 | uint16_t *pixels = framebuffer + scanvideo_scanline_number(buffer->scanline_id) * 320; 220 | buffer->data[3] = host_safe_hw_ptr(pixels + 4); 221 | buffer->data[4] = count_of(postamble); 222 | buffer->data[5] = host_safe_hw_ptr(postamble); 223 | buffer->data[6] = 0; 224 | buffer->data[7] = 0; 225 | buffer->data_used = 8; 226 | 227 | // 3 pixel run followed by main run, consuming the first 4 pixels 228 | buffer->data[8] = (pixels[0] << 16u) | COMPOSABLE_RAW_RUN; 229 | buffer->data[9] = (pixels[1] << 16u) | 0; 230 | buffer->data[10] = (COMPOSABLE_RAW_RUN << 16u) | pixels[2]; 231 | buffer->data[11] = ((317 + 1 - 3) << 16u) | pixels[3]; // note we add one for the black pixel at the end 232 | } 233 | 234 | struct semaphore video_setup_complete; 235 | 236 | void core1_func() { 237 | sem_acquire_blocking(&video_setup_complete); 238 | printf("CORE 1 go\n"); 239 | #if ALARM_POOL_ON_CORE1 240 | #if PICO_ON_DEVICE 241 | 242 | alarm_pool_add_alarm_in_us(alarm_pool_create(0, 3), 100, timer_callback, NULL, true); 243 | #endif 244 | #endif 245 | 246 | render_loop(); 247 | } 248 | 249 | int vga_main(void) { 250 | // framebuffer = calloc(320*240, sizeof(uint16_t)); 251 | mutex_init(&frame_logic_mutex); 252 | sem_init(&video_setup_complete, 0, 1); 253 | 254 | // Core 1 will wait for us to finish video setup, and then start rendering 255 | multicore_launch_core1(core1_func); 256 | 257 | hard_assert(vga_mode.width + 4 <= PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS * 2); 258 | scanvideo_setup(&vga_mode); 259 | scanvideo_timing_enable(true); 260 | 261 | frame_update_logic(); 262 | sem_release(&video_setup_complete); 263 | 264 | #if !ALARM_POOL_ON_CORE1 265 | #if PICO_ON_DEVICE 266 | add_alarm_in_us(100, timer_callback, NULL, true); 267 | #endif 268 | #endif 269 | render_loop(); 270 | return 0; 271 | } 272 | 273 | static inline void raw_scanline_finish(struct scanvideo_scanline_buffer *dest) { 274 | // Need to pivot the first pixel with the count so that PIO can keep up 275 | // with its 1 pixel per 2 clocks 276 | uint32_t first = dest->data[0]; 277 | uint32_t second = dest->data[1]; 278 | dest->data[0] = (first & 0x0000ffffu) | ((second & 0x0000ffffu) << 16); 279 | dest->data[1] = (second & 0xffff0000u) | ((first & 0xffff0000u) >> 16); 280 | dest->status = SCANLINE_OK; 281 | } 282 | 283 | void __time_critical_func(frame_update_logic)() { 284 | if (!params_ready) { 285 | double scale = vga_mode.height / 2; 286 | static int foo; 287 | double offx = (MIN(foo, 196.7)) / 500.0; 288 | double offy = -(MIN(foo, 229)) / 250.0; 289 | scale *= (10000 + (foo++) * (double) foo) / 10000.0; 290 | x0 = double_to_fixed(offx + (-vga_mode.width / 2) / scale - 0.5); 291 | y0 = double_to_fixed(offy + (-vga_mode.height / 2) / scale); 292 | dx0_dx = double_to_fixed(1.0f / scale); 293 | dy0_dy = double_to_fixed(1.0f / scale); 294 | max = double_to_fixed(4.f); 295 | params_ready = true; 296 | } 297 | } 298 | 299 | int main(void) { 300 | uint base_freq; 301 | #if PICO_SCANVIDEO_48MHZ 302 | base_freq = 48000; 303 | #else 304 | base_freq = 50000; 305 | #endif 306 | #if PICO_ON_DEVICE 307 | #if TURBO_BOOST 308 | vreg_set_voltage(VREG_VOLTAGE_1_30); 309 | sleep_ms(10); 310 | set_sys_clock_khz(base_freq*6, true); 311 | #else 312 | set_sys_clock_khz(base_freq * 3, true); 313 | #endif 314 | #endif 315 | // Re init uart now that clk_peri has changed 316 | setup_default_uart(); 317 | // gpio_debug_pins_init(); 318 | 319 | return vga_main(); 320 | } 321 | -------------------------------------------------------------------------------- /scanvideo/mandelbrot/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspberrypi/pico-playground/49a701a955ada550f8e7221aadef354eb9ff7b62/scanvideo/mandelbrot/screenshot.jpg -------------------------------------------------------------------------------- /scanvideo/mario_tiles/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (PICO_ON_DEVICE AND TARGET pico_scanvideo_dpi) 2 | add_executable(mario_tiles 3 | mario_tiles.c 4 | data.c 5 | data.h 6 | ) 7 | 8 | add_compile_definitions(mario_tiles PRIVATE 9 | # DISABLE_HPIXELS 10 | PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS=200 11 | PICO_SCANVIDEO_PLANE_COUNT=2 12 | PICO_SCANVIDEO_PLANE2_VARIABLE_FRAGMENT_DMA=1 13 | PICO_SCANVIDEO_MAX_SCANLINE_BUFFER2_WORDS=200 14 | PICO_SCANVIDEO_SCANLINE_BUFFER_COUNT=5 15 | PICO_SCANVIDEO_ENABLE_VIDEO_CLOCK_DOWN=1 16 | ) 17 | 18 | target_link_libraries(mario_tiles PRIVATE 19 | pico_stdlib 20 | pico_multicore 21 | pico_scanvideo_dpi 22 | render) 23 | 24 | if (PICO_ON_DEVICE) 25 | target_link_libraries(mario_tiles PRIVATE 26 | hardware_interp) 27 | endif() 28 | pico_add_extra_outputs(mario_tiles) 29 | endif() 30 | -------------------------------------------------------------------------------- /scanvideo/mario_tiles/data.h: -------------------------------------------------------------------------------- 1 | #ifndef SOFTWARE_DATA_H 2 | #define SOFTWARE_DATA_H 3 | #include "image.h" 4 | 5 | extern const struct tile_data16 tiles_tile_data; 6 | 7 | extern int level0_map_width; 8 | extern int level0_map_height; 9 | extern uint16_t level0_map[]; 10 | 11 | extern const struct tile_data16 galaga_tile_data; 12 | 13 | #endif //SOFTWARE_DATA_H 14 | -------------------------------------------------------------------------------- /scanvideo/mario_tiles/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspberrypi/pico-playground/49a701a955ada550f8e7221aadef354eb9ff7b62/scanvideo/mario_tiles/screenshot.jpg -------------------------------------------------------------------------------- /scanvideo/render/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(render INTERFACE) 2 | 3 | cmake_policy(SET CMP0076 NEW) 4 | target_sources(render INTERFACE 5 | image.c 6 | image.h 7 | spans.c 8 | spans.h 9 | ) 10 | 11 | target_include_directories(render INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 12 | target_link_libraries(render INTERFACE pico_base_headers) 13 | -------------------------------------------------------------------------------- /scanvideo/render/README.md: -------------------------------------------------------------------------------- 1 | This `render` library is entirely legacy - it just supports the example `demo1` -------------------------------------------------------------------------------- /scanvideo/render/image.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include "image.h" 10 | #include "pico/scanvideo.h" 11 | 12 | struct palette16 *blend_palette(const struct palette32 *source, uint32_t back_color) { 13 | struct palette16 *dest = (struct palette16 *) malloc(sizeof(struct palette16) + source->size * sizeof(uint16_t)); 14 | dest->flags = 15 | CF_PALETTE_COMPOSITED | (source->flags & ~(CF_HAS_SEMI_TRANSPARENT | CF_HAS_TRANSPARENT)) | CF_HAS_OPAQUE; 16 | dest->composited_on_color = back_color; 17 | dest->size = source->size; 18 | uint32_t __unused ba = (back_color >> 24) & 0xff; 19 | uint32_t bb = (back_color >> 16) & 0xff; 20 | uint32_t bg = (back_color >> 8) & 0xff; 21 | uint32_t br = (back_color >> 0) & 0xff; 22 | assert(ba == 255); // expect to be on an opaque color 23 | for (int i = 0; i < source->size; i++) { 24 | uint32_t fore_color = source->entries[i]; 25 | uint32_t fa = (fore_color >> 24) & 0xff; 26 | uint32_t fb = (fore_color >> 16) & 0xff; 27 | uint32_t fg = (fore_color >> 8) & 0xff; 28 | uint32_t fr = (fore_color >> 0) & 0xff; 29 | if (!i && !fa) { 30 | // even though we don't record alpha in the blended palette, we may care to use a color key (of 0) 31 | dest->flags |= CF_PALETTE_INDEX_0_TRANSPARENT; 32 | } 33 | if (fa == 255) fa = 256; 34 | fb = (fa * fb + (256 - fa) * bb) >> 11; 35 | fg = (fa * fg + (256 - fa) * bg) >> 11; 36 | fr = (fa * fr + (256 - fa) * br) >> 11; 37 | dest->entries[i] = PICO_SCANVIDEO_PIXEL_FROM_RGB5(fr, fg, fb); 38 | } 39 | return dest; 40 | } 41 | -------------------------------------------------------------------------------- /scanvideo/render/image.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _RENDER_IMAGE_H 8 | #define _RENDER_IMAGE_H 9 | 10 | #include "pico.h" 11 | 12 | typedef uint16_t short_flags; 13 | 14 | // common flags 15 | #define CF_HAS_OPAQUE ((short_flags)1) 16 | #define CF_HAS_SEMI_TRANSPARENT ((short_flags)2) 17 | #define CF_HAS_TRANSPARENT ((short_flags)4) 18 | #define CF_PALETTE_INDEX_0_TRANSPARENT ((short_flags)8) 19 | #define CF_PALETTE_COMPOSITED ((short_flags)16) 20 | 21 | #define CF_OPACITY_MASK (CF_HAS_OPAQUE | CF_HAS_SEMI_TRANSPARENT | CF_HAS_TRANSPARENT) 22 | 23 | struct palette32 { 24 | uint16_t size; 25 | short_flags flags; 26 | uint32_t entries[]; 27 | }; 28 | 29 | struct palette16 { 30 | uint16_t size; 31 | short_flags flags; 32 | uint32_t composited_on_color; // if flags & CF_PALETTE_COMPOSITED 33 | uint16_t entries[]; 34 | }; 35 | 36 | enum image_format { 37 | IMG_FMT_4BIT_VOGON = 1, 38 | IMG_FMT_8_BIT_RAW, 39 | IMG_FMT_16_BIT_RAW 40 | }; 41 | 42 | struct blob { 43 | size_t size; 44 | const uint8_t *bytes; 45 | }; 46 | 47 | struct image_data { 48 | int format; 49 | int width; 50 | int height; 51 | struct blob blob; 52 | const uint16_t *row_offsets; // optional and possibly initted on demand 53 | }; 54 | 55 | struct tile_data { 56 | uint8_t depth; 57 | uint16_t count; 58 | uint16_t width; 59 | uint16_t height; 60 | struct blob blob; 61 | const uint16_t *span_numbers; 62 | }; 63 | 64 | struct tile_data16 { 65 | uint16_t count; 66 | uint16_t width; 67 | uint16_t height; 68 | struct blob blob; 69 | const uint16_t *span_offsets; 70 | }; 71 | 72 | extern struct palette16 *blend_palette(const struct palette32 *source, uint32_t back_color); 73 | 74 | #endif //SOFTWARE_IMAGE_H 75 | -------------------------------------------------------------------------------- /scanvideo/render/spans.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _RENDER_SPANS_H 8 | #define _RENDER_SPANS_H 9 | 10 | #include "image.h" 11 | 12 | // ---------------------------------------------------------------------------- 13 | // 4bit1 encoding (vogon) - data is paletted and as such may contain alpha 14 | // 15 | // note changing this affects decoder since it is subtracted from length 16 | #define MIN_COLOR_SPAN_4BIT 5 17 | #define MIN_RAW_SPAN_4BIT 4 18 | enum vogon_commands { 19 | END_OF_LINE = 0, 20 | RAW_PIXELS_SHORT = 0x40, 21 | COLOR_PIXELS_SHORT = 0x80, 22 | SINGLE_PIXEL = 0xc0, 23 | RAW_PIXELS_LONG = 0xd0, 24 | COLOR_PIXELS_LONG = 0xd1 25 | }; 26 | 27 | enum { 28 | SPAN_SOLID, 29 | SPAN_4BIT_VOGON_OPAQUE, // vogon data but using a solid color palette 30 | SPAN_4BIT_RAW, 31 | SPAN_8BIT_RAW 32 | }; 33 | 34 | struct span { 35 | struct span *next; 36 | short_flags flags; 37 | uint16_t width; // count of displayed pixels 38 | uint8_t type; 39 | union { 40 | struct { 41 | uint16_t color16; 42 | } solid; 43 | struct { 44 | uint16_t clip_left; // > 0 to clip pixels off the left 45 | uint16_t content_width; // pixel width of the original content 46 | struct palette16 *palette; 47 | const uint8_t *data; 48 | uint16_t data_length; 49 | } vogon, raw_4bit, raw_8bit; 50 | }; 51 | }; 52 | 53 | extern int32_t render_spans(uint32_t *render_spans_buffer, size_t max_words, struct span *head, int width); 54 | extern int32_t single_color_scanline(uint32_t *buf, size_t buf_length, int width, uint32_t color16); 55 | extern void init_solid_color_span(struct span *span, uint16_t width, uint16_t color16, struct span *prev); 56 | extern void init_vogon_4bit_span(struct span *span, uint16_t width, const uint8_t *encoding, uint16_t encoded_size, 57 | struct palette16 *palette, struct span *prev); 58 | extern void set_solid_color_span_color(struct span *span, uint16_t color16); 59 | extern void set_vogon_4bit_span_encoding(struct span *span, const uint8_t *data, uint16_t data_length); 60 | extern void set_vogon_4bit_clipping(struct span *span, int clip_left, int display_width); 61 | 62 | #endif //CONVERT_SPANS_H 63 | -------------------------------------------------------------------------------- /scanvideo/scanvideo_minimal/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (TARGET pico_scanvideo_dpi) 2 | add_executable(scanvideo_minimal 3 | scanvideo_minimal.c 4 | ) 5 | 6 | 7 | target_link_libraries(scanvideo_minimal PRIVATE 8 | pico_multicore 9 | pico_stdlib 10 | pico_scanvideo_dpi) 11 | pico_add_extra_outputs(scanvideo_minimal) 12 | endif () 13 | -------------------------------------------------------------------------------- /scanvideo/scanvideo_minimal/scanvideo_minimal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | 9 | #include "pico.h" 10 | #include "pico/scanvideo.h" 11 | #include "pico/scanvideo/composable_scanline.h" 12 | #include "pico/multicore.h" 13 | #include "pico/sync.h" 14 | #include "pico/stdlib.h" 15 | #if PICO_ON_DEVICE 16 | #include "hardware/clocks.h" 17 | #endif 18 | 19 | #define DUAL_CORE_RENDER 20 | #define VGA_MODE vga_mode_320x240_60 21 | extern const struct scanvideo_pio_program video_24mhz_composable; 22 | 23 | // to make sure only one core updates the state when the frame number changes 24 | // todo note we should actually make sure here that the other core isn't still rendering (i.e. all must arrive before either can proceed - a la barrier) 25 | static struct mutex frame_logic_mutex; 26 | 27 | static void frame_update_logic(); 28 | static void render_scanline(struct scanvideo_scanline_buffer *dest, int core); 29 | 30 | // "Worker thread" for each core 31 | void render_loop() { 32 | static uint32_t last_frame_num = 0; 33 | int core_num = get_core_num(); 34 | printf("Rendering on core %d\n", core_num); 35 | while (true) { 36 | struct scanvideo_scanline_buffer *scanline_buffer = scanvideo_begin_scanline_generation(true); 37 | mutex_enter_blocking(&frame_logic_mutex); 38 | uint32_t frame_num = scanvideo_frame_number(scanline_buffer->scanline_id); 39 | // Note that with multiple cores we may have got here not for the first 40 | // scanline, however one of the cores will do this logic first before either 41 | // does the actual generation 42 | if (frame_num != last_frame_num) { 43 | last_frame_num = frame_num; 44 | frame_update_logic(); 45 | } 46 | mutex_exit(&frame_logic_mutex); 47 | 48 | render_scanline(scanline_buffer, core_num); 49 | 50 | // Release the rendered buffer into the wild 51 | scanvideo_end_scanline_generation(scanline_buffer); 52 | } 53 | } 54 | 55 | struct semaphore video_setup_complete; 56 | 57 | void core1_func() { 58 | sem_acquire_blocking(&video_setup_complete); 59 | render_loop(); 60 | } 61 | 62 | int vga_main(void) { 63 | mutex_init(&frame_logic_mutex); 64 | sem_init(&video_setup_complete, 0, 1); 65 | 66 | // Core 1 will wait for us to finish video setup, and then start rendering 67 | #ifdef DUAL_CORE_RENDER 68 | multicore_launch_core1(core1_func); 69 | #endif 70 | 71 | scanvideo_setup(&VGA_MODE); 72 | scanvideo_timing_enable(true); 73 | 74 | sem_release(&video_setup_complete); 75 | render_loop(); 76 | return 0; 77 | } 78 | 79 | void frame_update_logic() { 80 | 81 | } 82 | 83 | #define MIN_COLOR_RUN 3 84 | 85 | int32_t single_color_scanline(uint32_t *buf, size_t buf_length, int width, uint32_t color16) { 86 | assert(buf_length >= 2); 87 | 88 | assert(width >= MIN_COLOR_RUN); 89 | // | jmp color_run | color | count-3 | buf[0] = 90 | buf[0] = COMPOSABLE_COLOR_RUN | (color16 << 16); 91 | buf[1] = (width - MIN_COLOR_RUN) | (COMPOSABLE_RAW_1P << 16); 92 | // note we must end with a black pixel 93 | buf[2] = 0 | (COMPOSABLE_EOL_ALIGN << 16); 94 | 95 | return 3; 96 | } 97 | 98 | void render_scanline(struct scanvideo_scanline_buffer *dest, int core) { 99 | uint32_t *buf = dest->data; 100 | size_t buf_length = dest->data_max; 101 | 102 | int l = scanvideo_scanline_number(dest->scanline_id); 103 | uint16_t bgcolour = (uint16_t) l << 2; 104 | dest->data_used = single_color_scanline(buf, buf_length, VGA_MODE.width, bgcolour); 105 | dest->status = SCANLINE_OK; 106 | } 107 | 108 | 109 | int main(void) { 110 | #if PICO_SCANVIDEO_48MHZ 111 | set_sys_clock_48mhz(); 112 | #endif 113 | // Re init uart now that clk_peri has changed 114 | setup_default_uart(); 115 | 116 | return vga_main(); 117 | } 118 | -------------------------------------------------------------------------------- /scanvideo/scanvideo_minimal/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspberrypi/pico-playground/49a701a955ada550f8e7221aadef354eb9ff7b62/scanvideo/scanvideo_minimal/screenshot.jpg -------------------------------------------------------------------------------- /scanvideo/sprite/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(sprite INTERFACE) 2 | 3 | target_sources(sprite INTERFACE 4 | $<$:${CMAKE_CURRENT_LIST_DIR}/sprite.S> 5 | ${CMAKE_CURRENT_LIST_DIR}/sprite.c 6 | ${CMAKE_CURRENT_LIST_DIR}/sprite.h 7 | ) 8 | 9 | target_include_directories(sprite INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 10 | target_link_libraries(sprite INTERFACE pico_base_headers hardware_interp) 11 | -------------------------------------------------------------------------------- /scanvideo/sprite/affine_transform.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | #ifndef _AFFINE_TRANSFORM_H_ 7 | #define _AFFINE_TRANSFORM_H_ 8 | 9 | // Stolen from RISCBoy 10 | 11 | #include 12 | #include "pico/platform.h" 13 | 14 | // Store unpacked affine transforms as signed 16.16 fixed point in the following order: 15 | // a00, a01, b0, a10, a11, b1 16 | // i.e. the top two rows of the matrix 17 | // [ a00 a01 b0 ] 18 | // [ a01 a11 b1 ] 19 | // [ 0 0 1 ] 20 | // Then pack integers appropriately 21 | 22 | typedef int32_t affine_transform_t[6]; 23 | static const int32_t AF_ONE = 1 << 16; 24 | 25 | static inline __attribute__((always_inline)) int32_t mul_fp1616(int32_t x, int32_t y) { 26 | // TODO this results in an aeabi call?! 27 | int64_t result = (int64_t) x * y; 28 | return result >> 16; 29 | } 30 | 31 | // result can not be == left or right 32 | static inline void affine_mul(affine_transform_t result, const affine_transform_t left, 33 | const affine_transform_t right) { 34 | result[0] = mul_fp1616(left[0], right[0]) + mul_fp1616(left[1], right[3]); 35 | result[1] = mul_fp1616(left[0], right[1]) + mul_fp1616(left[1], right[4]); 36 | result[2] = mul_fp1616(left[0], right[2]) + mul_fp1616(left[1], right[5]) + left[2]; 37 | result[3] = mul_fp1616(left[3], right[0]) + mul_fp1616(left[4], right[3]); 38 | result[4] = mul_fp1616(left[3], right[1]) + mul_fp1616(left[4], right[4]); 39 | result[5] = mul_fp1616(left[3], right[2]) + mul_fp1616(left[4], right[5]) + left[5]; 40 | } 41 | 42 | static inline void affine_copy(affine_transform_t dst, const affine_transform_t src) { 43 | for (int i = 0; i < 6; ++i) 44 | dst[i] = src[i]; 45 | } 46 | 47 | // User is describing a sequence of transformations from texture space to 48 | // screen space, which are applied by premultiplying a column vector. However, 49 | // hardware transforms *from* screenspace *to* texture space, so we want the 50 | // inverse of the transform the user is building. Therefore our functions each 51 | // produce the inverse of the requested transform, and we apply transforms by 52 | // *post*-multiplication. 53 | static inline void affine_identity(affine_transform_t current_trans) { 54 | int32_t tmp[6] = { 55 | AF_ONE, 0, 0, 56 | 0, AF_ONE, 0 57 | }; 58 | affine_copy(current_trans, tmp); 59 | } 60 | 61 | static inline void affine_translate(affine_transform_t current_trans, int32_t x, int32_t y) { 62 | int32_t tmp[6]; 63 | int32_t transform[6] = { 64 | AF_ONE, 0, -AF_ONE * x, 65 | 0, AF_ONE, -AF_ONE * y 66 | }; 67 | affine_mul(tmp, current_trans, transform); 68 | affine_copy(current_trans, tmp); 69 | } 70 | 71 | // TODO this is shit 72 | static const int32_t __not_in_flash("atrans") sin_lookup_fp1616[256] = { 73 | 0x0, 0x648, 0xc8f, 0x12d5, 0x1917, 0x1f56, 0x2590, 0x2bc4, 0x31f1, 0x3817, 74 | 0x3e33, 0x4447, 0x4a50, 0x504d, 0x563e, 0x5c22, 0x61f7, 0x67bd, 0x6d74, 75 | 0x7319, 0x78ad, 0x7e2e, 0x839c, 0x88f5, 0x8e39, 0x9368, 0x987f, 0x9d7f, 76 | 0xa267, 0xa736, 0xabeb, 0xb085, 0xb504, 0xb968, 0xbdae, 0xc1d8, 0xc5e4, 77 | 0xc9d1, 0xcd9f, 0xd14d, 0xd4db, 0xd848, 0xdb94, 0xdebe, 0xe1c5, 0xe4aa, 78 | 0xe76b, 0xea09, 0xec83, 0xeed8, 0xf109, 0xf314, 0xf4fa, 0xf6ba, 0xf853, 79 | 0xf9c7, 0xfb14, 0xfc3b, 0xfd3a, 0xfe13, 0xfec4, 0xff4e, 0xffb1, 0xffec, 80 | 0x10000, 0xffec, 0xffb1, 0xff4e, 0xfec4, 0xfe13, 0xfd3a, 0xfc3b, 0xfb14, 81 | 0xf9c7, 0xf853, 0xf6ba, 0xf4fa, 0xf314, 0xf109, 0xeed8, 0xec83, 0xea09, 82 | 0xe76b, 0xe4aa, 0xe1c5, 0xdebe, 0xdb94, 0xd848, 0xd4db, 0xd14d, 0xcd9f, 83 | 0xc9d1, 0xc5e4, 0xc1d8, 0xbdae, 0xb968, 0xb504, 0xb085, 0xabeb, 0xa736, 84 | 0xa267, 0x9d7f, 0x987f, 0x9368, 0x8e39, 0x88f5, 0x839c, 0x7e2e, 0x78ad, 85 | 0x7319, 0x6d74, 0x67bd, 0x61f7, 0x5c22, 0x563e, 0x504d, 0x4a50, 0x4447, 86 | 0x3e33, 0x3817, 0x31f1, 0x2bc4, 0x2590, 0x1f56, 0x1917, 0x12d5, 0xc8f, 0x648, 87 | 0x0, 0xfffff9b8, 0xfffff371, 0xffffed2b, 0xffffe6e9, 0xffffe0aa, 0xffffda70, 88 | 0xffffd43c, 0xffffce0f, 0xffffc7e9, 0xffffc1cd, 0xffffbbb9, 0xffffb5b0, 89 | 0xffffafb3, 0xffffa9c2, 0xffffa3de, 0xffff9e09, 0xffff9843, 0xffff928c, 90 | 0xffff8ce7, 0xffff8753, 0xffff81d2, 0xffff7c64, 0xffff770b, 0xffff71c7, 91 | 0xffff6c98, 0xffff6781, 0xffff6281, 0xffff5d99, 0xffff58ca, 0xffff5415, 92 | 0xffff4f7b, 0xffff4afc, 0xffff4698, 0xffff4252, 0xffff3e28, 0xffff3a1c, 93 | 0xffff362f, 0xffff3261, 0xffff2eb3, 0xffff2b25, 0xffff27b8, 0xffff246c, 94 | 0xffff2142, 0xffff1e3b, 0xffff1b56, 0xffff1895, 0xffff15f7, 0xffff137d, 95 | 0xffff1128, 0xffff0ef7, 0xffff0cec, 0xffff0b06, 0xffff0946, 0xffff07ad, 96 | 0xffff0639, 0xffff04ec, 0xffff03c5, 0xffff02c6, 0xffff01ed, 0xffff013c, 97 | 0xffff00b2, 0xffff004f, 0xffff0014, 0xffff0000, 0xffff0014, 0xffff004f, 98 | 0xffff00b2, 0xffff013c, 0xffff01ed, 0xffff02c6, 0xffff03c5, 0xffff04ec, 99 | 0xffff0639, 0xffff07ad, 0xffff0946, 0xffff0b06, 0xffff0cec, 0xffff0ef7, 100 | 0xffff1128, 0xffff137d, 0xffff15f7, 0xffff1895, 0xffff1b56, 0xffff1e3b, 101 | 0xffff2142, 0xffff246c, 0xffff27b8, 0xffff2b25, 0xffff2eb3, 0xffff3261, 102 | 0xffff362f, 0xffff3a1c, 0xffff3e28, 0xffff4252, 0xffff4698, 0xffff4afc, 103 | 0xffff4f7b, 0xffff5415, 0xffff58ca, 0xffff5d99, 0xffff6281, 0xffff6781, 104 | 0xffff6c98, 0xffff71c7, 0xffff770b, 0xffff7c64, 0xffff81d2, 0xffff8753, 105 | 0xffff8ce7, 0xffff928c, 0xffff9843, 0xffff9e09, 0xffffa3de, 0xffffa9c2, 106 | 0xffffafb3, 0xffffb5b0, 0xffffbbb9, 0xffffc1cd, 0xffffc7e9, 0xffffce0f, 107 | 0xffffd43c, 0xffffda70, 0xffffe0aa, 0xffffe6e9, 0xffffed2b, 0xfffff371, 108 | 0xfffff9b8 109 | }; 110 | 111 | static inline int32_t sin_fp1616(uint8_t theta) { 112 | return sin_lookup_fp1616[theta]; 113 | } 114 | 115 | static inline int32_t cos_fp1616(uint8_t theta) { 116 | return sin_lookup_fp1616[(theta + 64) & 0xff]; 117 | } 118 | 119 | // Appears as a counterclockwise rotation (when viewed from texture space to screen space) 120 | // Units of angle are 256 = one turn 121 | static inline void affine_rotate(affine_transform_t current_trans, uint8_t theta) { 122 | int32_t tmp[6]; 123 | int32_t transform[6] = { 124 | cos_fp1616(theta), -sin_fp1616(theta), 0, 125 | sin_fp1616(theta), cos_fp1616(theta), 0 126 | }; 127 | affine_mul(tmp, current_trans, transform); 128 | affine_copy(current_trans, tmp); 129 | } 130 | 131 | static inline void affine_scale(affine_transform_t current_trans, int32_t sx, int32_t sy) { 132 | int32_t sx_inv = ((int64_t) AF_ONE * AF_ONE) / sx; 133 | int32_t sy_inv = ((int64_t) AF_ONE * AF_ONE) / sy; 134 | int32_t tmp[6]; 135 | int32_t transform[6] = { 136 | sx_inv, 0, 0, 137 | 0, sy_inv, 0 138 | }; 139 | affine_mul(tmp, current_trans, transform); 140 | affine_copy(current_trans, tmp); 141 | } 142 | 143 | #endif // _AFFINE_TRANSFORM_H_ 144 | -------------------------------------------------------------------------------- /scanvideo/sprite/sprite.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | // Functions for doing simple 2D graphics operations on a RGB scanline buffer. 8 | 9 | #include "hardware/regs/addressmap.h" 10 | #include "hardware/regs/sio.h" 11 | 12 | #define POP2_OFFS (SIO_INTERP0_POP_FULL_OFFSET - SIO_INTERP0_ACCUM0_OFFSET) 13 | #define CTRL0_OFFS (SIO_INTERP0_CTRL_LANE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET) 14 | #define INTERP1 (SIO_INTERP1_ACCUM0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET) 15 | 16 | .syntax unified 17 | .cpu cortex-m0plus 18 | .thumb 19 | 20 | // Put every function in its own ELF section, to permit linker GC 21 | .macro decl_func name 22 | .section .time_critical.\name, "ax" 23 | .global \name 24 | .type \name,%function 25 | .thumb_func 26 | \name: 27 | .endm 28 | 29 | // ---------------------------------------------------------------------------- 30 | // Colour fill 31 | 32 | // r0: dst 33 | // r1: value 34 | // r2: count 35 | 36 | decl_func sprite_fill8 37 | // Slide for short fills 38 | cmp r2, #18 39 | bhi 2f 40 | adr r3, 1f 41 | lsls r2, #1 42 | subs r3, r2 43 | adds r3, #1 // thumb bit 44 | bx r3 45 | .align 2 46 | strb r1, [r0, #17] 47 | strb r1, [r0, #16] 48 | strb r1, [r0, #15] 49 | strb r1, [r0, #14] 50 | strb r1, [r0, #13] 51 | strb r1, [r0, #12] 52 | strb r1, [r0, #11] 53 | strb r1, [r0, #10] 54 | strb r1, [r0, #9] 55 | strb r1, [r0, #8] 56 | strb r1, [r0, #7] 57 | strb r1, [r0, #6] 58 | strb r1, [r0, #5] 59 | strb r1, [r0, #4] 60 | strb r1, [r0, #3] 61 | strb r1, [r0, #2] 62 | strb r1, [r0, #1] 63 | strb r1, [r0, #0] 64 | 1: 65 | bx lr 66 | 2: 67 | lsls r3, r1, #8 68 | orrs r1, r3 69 | lsls r3, r1, #16 70 | orrs r1, r3 71 | // Get r0 word-aligned: 72 | lsrs r3, r0, #1 73 | bcc 1f 74 | strb r1, [r0] 75 | adds r0, #1 76 | subs r2, #1 77 | 1: 78 | lsrs r3, r0, #2 79 | bcc 1f 80 | strh r1, [r0] 81 | adds r0, #2 82 | subs r2, #2 83 | 1: 84 | // Set up for main loop. Limit pointer at end - (loop body size - 1) 85 | push {r4} 86 | adds r2, r0 87 | subs r2, #15 88 | mov ip, r2 89 | mov r2, r1 90 | mov r3, r1 91 | mov r4, r1 92 | 93 | // Fall straight into loop, because cases less than (loop body + max misalignment) are handled by slide 94 | 1: 95 | stmia r0!, {r1, r2, r3, r4} 96 | cmp r0, ip 97 | blo 1b 98 | 99 | // Main loop done, now tidy up the odds and ends 100 | mov r4, ip 101 | subs r4, r0 102 | adds r4, #15 103 | // No more than 15 bytes remaining -- first test bit 3 104 | lsls r4, #29 105 | bcc 1f 106 | stmia r0!, {r1, r2} 107 | 1: 108 | lsls r4, #1 109 | bcc 1f 110 | stmia r0!, {r1} 111 | 1: 112 | lsls r4, #1 113 | bcc 1f 114 | strh r1, [r0] 115 | adds r0, #2 116 | 1: 117 | lsls r4, #1 118 | bcc 1f 119 | strb r1, [r0] 120 | 1: 121 | pop {r4} 122 | bx lr 123 | 124 | 125 | decl_func sprite_fill16 126 | // Slide for short fills 127 | cmp r2, #15 128 | bhi 2f 129 | adr r3, 1f 130 | lsls r2, #1 131 | subs r3, r2 132 | adds r3, #1 133 | bx r3 134 | .align 2 135 | strh r1, [r0, #30] 136 | strh r1, [r0, #28] 137 | strh r1, [r0, #26] 138 | strh r1, [r0, #24] 139 | strh r1, [r0, #22] 140 | strh r1, [r0, #20] 141 | strh r1, [r0, #18] 142 | strh r1, [r0, #16] 143 | strh r1, [r0, #14] 144 | strh r1, [r0, #12] 145 | strh r1, [r0, #10] 146 | strh r1, [r0, #8] 147 | strh r1, [r0, #6] 148 | strh r1, [r0, #4] 149 | strh r1, [r0, #2] 150 | strh r1, [r0, #0] 151 | 1: 152 | bx lr 153 | 2: 154 | push {r4, r5, r6, r7, lr} 155 | // Get word-aligned before main fill loop 156 | lsrs r3, r2, #2 157 | bcc 1f 158 | strh r1, [r0] 159 | adds r0, #2 160 | subs r2, #1 161 | 1: 162 | // Set limit pointer at end - (loop body size - 1) 163 | lsls r2, #1 164 | adds r2, r0 165 | subs r2, #26 166 | mov ip, r2 167 | 168 | lsls r2, r1, #16 169 | orrs r1, r2 170 | mov r2, r1 171 | mov r3, r1 172 | mov r4, r1 173 | mov r5, r1 174 | mov r6, r1 175 | mov r7, r1 176 | // We can fall through because cases < 1 loop are handled by slide 177 | 1: 178 | stmia r0!, {r1, r2, r3, r4, r5, r6, r7} // wheeeeeeeeeee 179 | cmp r0, ip 180 | blo 1b 181 | 182 | // Most of the work done, we have a few more to tidy up 183 | movs r2, #26 184 | add r2, ip 185 | subs r2, r0 186 | 187 | lsls r2, #28 188 | bcc 1f 189 | stmia r0!, {r4, r5, r6, r7} 190 | 1: 191 | lsls r2, #1 192 | bcc 1f 193 | stmia r0!, {r4, r5} 194 | 1: 195 | lsls r2, #1 196 | bcc 1f 197 | stmia r0!, {r4} 198 | 1: 199 | lsls r2, #1 200 | bcc 1f 201 | strh r4, [r0] 202 | 1: 203 | pop {r4, r5, r6, r7, pc} 204 | 205 | // ---------------------------------------------------------------------------- 206 | // Non-AT sprite 207 | 208 | // r0: dst 209 | // r1: src 210 | // r2: pixel count 211 | // 212 | 213 | // Unrolled loop body with an initial computed branch. Note we can go much 214 | // faster if r0 and r1 are co-aligned, but it's not all that helpful to have a 215 | // 1 in 4 chance of being really fast when minimising worst-case scanline time 216 | 217 | decl_func sprite_blit8 218 | mov ip, r0 219 | lsrs r3, r2, #3 220 | lsls r3, #3 221 | eors r2, r3 // r2 = pixels % 8, r3 = pixels = pixels % 8 222 | 223 | add r0, r3 224 | add r1, r3 225 | 226 | adr r3, 2f 227 | lsls r2, #2 228 | subs r3, r2 229 | adds r3, #1 // thumb bit >:( 230 | bx r3 231 | 232 | .align 2 233 | 1: 234 | subs r0, #8 235 | subs r1, #8 236 | ldrb r3, [r1, #7] 237 | strb r3, [r0, #7] 238 | ldrb r3, [r1, #6] 239 | strb r3, [r0, #6] 240 | ldrb r3, [r1, #5] 241 | strb r3, [r0, #5] 242 | ldrb r3, [r1, #4] 243 | strb r3, [r0, #4] 244 | ldrb r3, [r1, #3] 245 | strb r3, [r0, #3] 246 | ldrb r3, [r1, #2] 247 | strb r3, [r0, #2] 248 | ldrb r3, [r1, #1] 249 | strb r3, [r0, #1] 250 | ldrb r3, [r1, #0] 251 | strb r3, [r0, #0] 252 | 2: 253 | cmp r0, ip 254 | bhi 1b 255 | bx lr 256 | 257 | // Assume RAGB2132 (so alpha is bit 5) 258 | 259 | #define ALPHA_SHIFT_8BPP 6 260 | 261 | .macro sprite_blit8_alpha_body n 262 | ldrb r3, [r1, #\n] 263 | lsrs r2, r3, #ALPHA_SHIFT_8BPP 264 | bcc 2f 265 | strb r3, [r0, #\n] 266 | 2: 267 | .endm 268 | 269 | decl_func sprite_blit8_alpha 270 | mov ip, r0 271 | lsrs r3, r2, #3 272 | lsls r3, #3 273 | eors r2, r3 274 | 275 | add r0, r3 276 | add r1, r3 277 | 278 | adr r3, 3f 279 | lsls r2, #3 280 | subs r3, r2 281 | adds r3, #1 282 | bx r3 283 | 284 | .align 2 285 | 1: 286 | subs r0, #8 287 | subs r1, #8 288 | sprite_blit8_alpha_body 7 289 | sprite_blit8_alpha_body 6 290 | sprite_blit8_alpha_body 5 291 | sprite_blit8_alpha_body 4 292 | sprite_blit8_alpha_body 3 293 | sprite_blit8_alpha_body 2 294 | sprite_blit8_alpha_body 1 295 | sprite_blit8_alpha_body 0 296 | 3: 297 | cmp r0, ip 298 | bhi 1b 299 | bx lr 300 | 301 | 302 | decl_func sprite_blit16 303 | mov ip, r0 304 | lsrs r3, r2, #3 305 | lsls r3, #3 306 | eors r2, r3 // r2 = pixels % 8, r3 = pixels = pixels % 8 307 | 308 | lsls r3, #1 309 | add r0, r3 310 | add r1, r3 311 | 312 | adr r3, 2f 313 | lsls r2, #2 314 | subs r3, r2 315 | adds r3, #1 // thumb bit >:( 316 | bx r3 317 | 318 | .align 2 319 | 1: 320 | subs r0, #16 321 | subs r1, #16 322 | ldrh r3, [r1, #14] 323 | strh r3, [r0, #14] 324 | ldrh r3, [r1, #12] 325 | strh r3, [r0, #12] 326 | ldrh r3, [r1, #10] 327 | strh r3, [r0, #10] 328 | ldrh r3, [r1, #8] 329 | strh r3, [r0, #8] 330 | ldrh r3, [r1, #6] 331 | strh r3, [r0, #6] 332 | ldrh r3, [r1, #4] 333 | strh r3, [r0, #4] 334 | ldrh r3, [r1, #2] 335 | strh r3, [r0, #2] 336 | ldrh r3, [r1, #0] 337 | strh r3, [r0, #0] 338 | 2: 339 | cmp r0, ip 340 | bhi 1b 341 | bx lr 342 | 343 | // Assume RGAB5515 (so alpha is bit 5) 344 | // Note the alpha bit being in the same position as RAGB2132 is a coincidence. 345 | // We are just stealing an LSB such that we can treat our alpha pixels in the 346 | // same way as non-alpha pixels when encoding (and the co-opted channel LSB 347 | // always ends up being set on alpha pixels, which is pretty harmless) 348 | 349 | #define ALPHA_SHIFT_16BPP 6 350 | 351 | .macro sprite_blit16_alpha_body n 352 | ldrh r3, [r1, #2*\n] 353 | lsrs r2, r3, #ALPHA_SHIFT_16BPP 354 | bcc 2f 355 | strh r3, [r0, #2*\n] 356 | 2: 357 | .endm 358 | 359 | decl_func sprite_blit16_alpha 360 | mov ip, r0 361 | lsrs r3, r2, #3 362 | lsls r3, #3 363 | eors r2, r3 364 | 365 | lsls r3, #1 366 | add r0, r3 367 | add r1, r3 368 | 369 | adr r3, 3f 370 | lsls r2, #3 371 | subs r3, r2 372 | adds r3, #1 373 | bx r3 374 | 375 | .align 2 376 | 1: 377 | subs r0, #16 378 | subs r1, #16 379 | sprite_blit16_alpha_body 7 380 | sprite_blit16_alpha_body 6 381 | sprite_blit16_alpha_body 5 382 | sprite_blit16_alpha_body 4 383 | sprite_blit16_alpha_body 3 384 | sprite_blit16_alpha_body 2 385 | sprite_blit16_alpha_body 1 386 | sprite_blit16_alpha_body 0 387 | 3: 388 | cmp r0, ip 389 | bhi 1b 390 | bx lr 391 | 392 | 393 | // ---------------------------------------------------------------------------- 394 | // Affine-transformed sprite (note these are just the inner loops -- INTERP0 395 | // must be configured by the caller, which is presumably not written in asm) 396 | 397 | // r0: raster start pointer 398 | // r1: raster span size (pixels) 399 | 400 | .macro sprite_ablit8_loop_body n 401 | ldr r1, [r3, #CTRL0_OFFS] 402 | ldr r2, [r3, #POP2_OFFS] 403 | lsrs r1, #SIO_INTERP0_CTRL_LANE0_OVERF_LSB + 1 404 | bcs 2f 405 | ldrb r2, [r2] 406 | strb r2, [r0, #\n] 407 | 2: 408 | .endm 409 | 410 | decl_func sprite_ablit8_loop 411 | mov ip, r0 412 | 413 | lsrs r2, r1, #3 414 | lsls r2, #3 415 | eors r1, r2 416 | add r0, r2 417 | 418 | adr r2, 3f 419 | movs r3, #12 // Each (non-unrolled) loop body is 12 bytes 420 | muls r1, r3 421 | subs r2, r1 422 | adds r2, #1 423 | 424 | ldr r3, =(SIO_BASE + SIO_INTERP0_ACCUM0_OFFSET) 425 | bx r2 426 | 427 | .align 2 428 | nop 429 | 1: 430 | subs r0, #8 431 | sprite_ablit8_loop_body 7 432 | sprite_ablit8_loop_body 6 433 | sprite_ablit8_loop_body 5 434 | sprite_ablit8_loop_body 4 435 | sprite_ablit8_loop_body 3 436 | sprite_ablit8_loop_body 2 437 | sprite_ablit8_loop_body 1 438 | sprite_ablit8_loop_body 0 439 | 3: 440 | cmp r0, ip 441 | bne 1b 442 | bx lr 443 | 444 | 445 | 446 | // As above but bit 5 is assumed to be an alpha bit (RAGB2132) 447 | 448 | .macro sprite_ablit8_alpha_loop_body n 449 | ldr r1, [r3, #CTRL0_OFFS] 450 | ldr r2, [r3, #POP2_OFFS] 451 | lsrs r1, #SIO_INTERP0_CTRL_LANE0_OVERF_LSB + 1 452 | bcs 2f 453 | ldrb r2, [r2] 454 | lsrs r1, r2, #ALPHA_SHIFT_8BPP 455 | bcc 2f 456 | strb r2, [r0, #\n] 457 | 2: 458 | .endm 459 | 460 | decl_func sprite_ablit8_alpha_loop 461 | mov ip, r0 462 | ldr r3, =(SIO_BASE + SIO_INTERP0_ACCUM0_OFFSET) 463 | 464 | lsrs r2, r1, #3 465 | lsls r2, #3 466 | eors r1, r2 467 | add r0, r2 468 | 469 | adr r2, 3f 470 | lsls r1, #4 // Each (non-unrolled) loop body is 16 bytes 471 | subs r2, r1 472 | adds r2, #1 473 | bx r2 474 | 475 | .align 2 476 | nop 477 | 1: 478 | subs r0, #8 479 | sprite_ablit8_alpha_loop_body 7 480 | sprite_ablit8_alpha_loop_body 6 481 | sprite_ablit8_alpha_loop_body 5 482 | sprite_ablit8_alpha_loop_body 4 483 | sprite_ablit8_alpha_loop_body 3 484 | sprite_ablit8_alpha_loop_body 2 485 | sprite_ablit8_alpha_loop_body 1 486 | sprite_ablit8_alpha_loop_body 0 487 | 3: 488 | cmp r0, ip 489 | bhi 1b 490 | bx lr 491 | 492 | 493 | 494 | .macro sprite_ablit16_loop_body n 495 | ldr r1, [r3, #CTRL0_OFFS] 496 | ldr r2, [r3, #POP2_OFFS] 497 | lsrs r1, #SIO_INTERP0_CTRL_LANE0_OVERF_LSB + 1 498 | bcs 2f 499 | ldrh r2, [r2] 500 | strh r2, [r0, #2*\n] 501 | 2: 502 | .endm 503 | 504 | decl_func sprite_ablit16_loop 505 | mov ip, r0 506 | 507 | lsrs r2, r1, #3 508 | lsls r2, #3 509 | eors r1, r2 510 | lsls r2, #1 // Each pixel is 2 bytes 511 | add r0, r2 512 | 513 | adr r2, 3f 514 | movs r3, #12 // Each (non-unrolled) loop body is 12 bytes 515 | muls r1, r3 516 | subs r2, r1 517 | adds r2, #1 518 | 519 | ldr r3, =(SIO_BASE + SIO_INTERP0_ACCUM0_OFFSET) 520 | bx r2 521 | 522 | .align 2 523 | nop 524 | 1: 525 | subs r0, #16 526 | sprite_ablit16_loop_body 7 527 | sprite_ablit16_loop_body 6 528 | sprite_ablit16_loop_body 5 529 | sprite_ablit16_loop_body 4 530 | sprite_ablit16_loop_body 3 531 | sprite_ablit16_loop_body 2 532 | sprite_ablit16_loop_body 1 533 | sprite_ablit16_loop_body 0 534 | 3: 535 | cmp r0, ip 536 | bne 1b 537 | bx lr 538 | 539 | 540 | 541 | .macro sprite_ablit16_alpha_loop_body n 542 | ldr r1, [r3, #CTRL0_OFFS] 543 | ldr r2, [r3, #POP2_OFFS] 544 | lsrs r1, #SIO_INTERP0_CTRL_LANE0_OVERF_LSB + 1 545 | bcs 2f 546 | ldrh r2, [r2] 547 | lsrs r1, r2, #ALPHA_SHIFT_16BPP 548 | bcc 2f 549 | strh r2, [r0, #2*\n] 550 | 2: 551 | .endm 552 | 553 | decl_func sprite_ablit16_alpha_loop 554 | mov ip, r0 555 | ldr r3, =(SIO_BASE + SIO_INTERP0_ACCUM0_OFFSET) 556 | 557 | lsrs r2, r1, #3 558 | lsls r2, #3 559 | eors r1, r2 560 | lsls r2, #1 // Each pixel is 2 bytes 561 | add r0, r2 562 | 563 | adr r2, 3f 564 | lsls r1, #4 // Each (non-unrolled) loop body is 16 bytes 565 | subs r2, r1 566 | adds r2, #1 567 | bx r2 568 | 569 | .align 2 570 | nop 571 | 1: 572 | subs r0, #16 573 | sprite_ablit16_alpha_loop_body 7 574 | sprite_ablit16_alpha_loop_body 6 575 | sprite_ablit16_alpha_loop_body 5 576 | sprite_ablit16_alpha_loop_body 4 577 | sprite_ablit16_alpha_loop_body 3 578 | sprite_ablit16_alpha_loop_body 2 579 | sprite_ablit16_alpha_loop_body 1 580 | sprite_ablit16_alpha_loop_body 0 581 | 3: 582 | cmp r0, ip 583 | bhi 1b 584 | bx lr 585 | -------------------------------------------------------------------------------- /scanvideo/sprite/sprite.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include "sprite.h" 8 | #include "affine_transform.h" 9 | 10 | #include "pico/platform.h" // for __not_in_flash 11 | #include "hardware/interp.h" 12 | 13 | // Note some of the sprite routines are quite large (unrolled), so trying to 14 | // keep everything in separate sections so the linker can garbage collect 15 | // unused sprite code. In particular we usually need 8bpp xor 16bpp functions! 16 | #define __ram_func(foo) __not_in_flash_func(foo) 17 | 18 | typedef struct { 19 | int tex_offs_x; 20 | int tex_offs_y; 21 | int size_x; 22 | } intersect_t; 23 | 24 | // Always-inline else the compiler does rash things like passing structs in memory 25 | static inline intersect_t _get_sprite_intersect(const sprite_t *sp, uint raster_y, uint raster_w) { 26 | intersect_t isct = {0}; 27 | isct.tex_offs_y = (int) raster_y - sp->y; 28 | int size = 1u << sp->log_size; 29 | uint upper_mask = -size; 30 | if ((uint) isct.tex_offs_y & upper_mask) 31 | return isct; 32 | int x_start_clipped = MAX(0, sp->x); 33 | isct.tex_offs_x = x_start_clipped - sp->x; 34 | isct.size_x = MIN(sp->x + size, (int) raster_w) - x_start_clipped; 35 | return isct; 36 | } 37 | 38 | // Sprites may have an array of metadata on the end. One word per line, encodes first opaque pixel, last opaque pixel, and whether the span in between is solid. This allows fewer 39 | static inline intersect_t _intersect_with_metadata(intersect_t isct, uint32_t meta) { 40 | int span_end = meta & 0xffff; 41 | int span_start = (meta >> 16) & 0x7fff; 42 | int isct_new_start = MAX(isct.tex_offs_x, span_start); 43 | int isct_new_end = MIN(isct.tex_offs_x + isct.size_x, span_end); 44 | isct.tex_offs_x = isct_new_start; 45 | isct.size_x = isct_new_end - isct_new_start; 46 | return isct; 47 | } 48 | 49 | void __ram_func(sprite_sprite8)(uint8_t *scanbuf, const sprite_t *sp, uint raster_y, uint raster_w) { 50 | int size = 1u << sp->log_size; 51 | intersect_t isct = _get_sprite_intersect(sp, raster_y, raster_w); 52 | if (isct.size_x <= 0) 53 | return; 54 | const uint8_t *img = sp->img; 55 | if (sp->has_opacity_metadata) { 56 | // Metadata is one word per row, concatenated to end of pixel data 57 | uint32_t meta = ((uint32_t *) (sp->img + size * size * sizeof(uint8_t)))[isct.tex_offs_y]; 58 | isct = _intersect_with_metadata(isct, meta); 59 | if (isct.size_x <= 0) 60 | return; 61 | bool span_continuous = !!(meta & (1u << 31)); 62 | if (span_continuous) { 63 | // Non-alpha blit is ~50% faster 64 | sprite_blit8(scanbuf + sp->x + isct.tex_offs_x, img + isct.tex_offs_x + isct.tex_offs_y * size, 65 | isct.size_x); 66 | } else { 67 | sprite_blit8_alpha(scanbuf + sp->x + isct.tex_offs_x, img + isct.tex_offs_x + isct.tex_offs_y * size, 68 | isct.size_x); 69 | } 70 | } else { 71 | sprite_blit8_alpha(scanbuf + sp->x + isct.tex_offs_x, img + isct.tex_offs_x + isct.tex_offs_y * size, 72 | isct.size_x); 73 | } 74 | } 75 | 76 | void __ram_func(sprite_sprite16)(uint16_t *scanbuf, const sprite_t *sp, uint raster_y, uint raster_w) { 77 | int size = 1u << sp->log_size; 78 | intersect_t isct = _get_sprite_intersect(sp, raster_y, raster_w); 79 | if (isct.size_x <= 0) 80 | return; 81 | const uint16_t *img = sp->img; 82 | if (sp->has_opacity_metadata) { 83 | uint32_t meta = ((uint32_t *) (sp->img + size * size * sizeof(uint16_t)))[isct.tex_offs_y]; 84 | isct = _intersect_with_metadata(isct, meta); 85 | if (isct.size_x <= 0) 86 | return; 87 | bool span_continuous = !!(meta & (1u << 31)); 88 | if (span_continuous) 89 | sprite_blit16(scanbuf + sp->x + isct.tex_offs_x, img + isct.tex_offs_x + isct.tex_offs_y * size, 90 | isct.size_x); 91 | else 92 | sprite_blit16_alpha(scanbuf + sp->x + isct.tex_offs_x, img + isct.tex_offs_x + isct.tex_offs_y * size, 93 | isct.size_x); 94 | } else { 95 | sprite_blit16_alpha(scanbuf + MAX(0, sp->x), img + isct.tex_offs_x + isct.tex_offs_y * size, isct.size_x); 96 | } 97 | } 98 | 99 | // We're defining the affine transform as: 100 | // 101 | // [u] [ a00 a01 b0 ] [x] [a00 * x + a01 * y + b0] 102 | // [v] = [ a10 a11 b1 ] * [y] = [a10 * x + a11 * y + b1] 103 | // [1] [ 0 0 1 ] [1] [ 1 ] 104 | // 105 | // We represent this in memory as {a00, a01, b0, a10, a11, b1} (all int32_t) 106 | // i.e. the non-constant parts in row-major order 107 | 108 | // Set up an interpolator to follow a straight line through u,v space 109 | static inline __attribute__((always_inline)) void _setup_interp_affine(interp_hw_t *interp, intersect_t isct, 110 | const affine_transform_t atrans) { 111 | // Calculate the u,v coord of the first sample. Note that we are iterating 112 | // *backward* along the raster span because this is faster (yes) 113 | int32_t x0 = 114 | mul_fp1616(atrans[0], (isct.tex_offs_x + isct.size_x) * AF_ONE) + 115 | mul_fp1616(atrans[1], isct.tex_offs_y * AF_ONE) + 116 | atrans[2]; 117 | int32_t y0 = 118 | mul_fp1616(atrans[3], (isct.tex_offs_x + isct.size_x) * AF_ONE) + 119 | mul_fp1616(atrans[4], isct.tex_offs_y * AF_ONE) + 120 | atrans[5]; 121 | interp->accum[0] = x0; 122 | interp->accum[1] = y0; 123 | interp->base[0] = -atrans[0]; // -a00, since x decrements by 1 with each coord 124 | interp->base[1] = -atrans[3]; // -a10 125 | } 126 | 127 | // Set up an interpolator to generate pixel lookup addresses from fp1616 128 | // numbers in accum1, accum0 based on the parameters of sprite sp and the size 129 | // of the individual pixels 130 | static inline __attribute__((always_inline)) void _setup_interp_pix_coordgen(interp_hw_t *interp, 131 | const sprite_t *sp, uint pixel_shift) { 132 | // Concatenate from accum0[31:16] and accum1[31:16] as many LSBs as required 133 | // to index the sprite texture in both directions. Reading from POP_FULL will 134 | // yields these bits, added to sp->img, and this will also trigger BASE0 and 135 | // BASE1 to be directly added (thanks to CTRL_ADD_RAW) to the accumulators, 136 | // which generates the u,v coordinate for the *next* read. 137 | assert(sp->log_size + pixel_shift <= 16); 138 | 139 | interp_config c0 = interp_default_config(); 140 | interp_config_set_add_raw(&c0, true); 141 | interp_config_set_shift(&c0, 16 - pixel_shift); 142 | interp_config_set_mask(&c0, pixel_shift, pixel_shift + sp->log_size - 1); 143 | interp_set_config(interp, 0, &c0); 144 | 145 | interp_config c1 = interp_default_config(); 146 | interp_config_set_add_raw(&c1, true); 147 | interp_config_set_shift(&c1, 16 - sp->log_size - pixel_shift); 148 | interp_config_set_mask(&c1, pixel_shift + sp->log_size, pixel_shift + 2 * sp->log_size - 1); 149 | interp_set_config(interp, 1, &c1); 150 | 151 | interp->base[2] = (uint32_t) sp->img; 152 | } 153 | 154 | // Note we do NOT save/restore the interpolator! 155 | void __ram_func(sprite_asprite8)(uint8_t *scanbuf, const sprite_t *sp, const affine_transform_t atrans, uint raster_y, 156 | uint raster_w) { 157 | intersect_t isct = _get_sprite_intersect(sp, raster_y, raster_w); 158 | if (isct.size_x <= 0) 159 | return; 160 | interp_hw_t *interp = interp0; 161 | _setup_interp_affine(interp, isct, atrans); 162 | _setup_interp_pix_coordgen(interp, sp, 0); 163 | // Now every read from POP_FULL will give us a new MODE7 lookup pointer for sp->img :) 164 | sprite_ablit8_alpha_loop(scanbuf + MAX(0, sp->x), isct.size_x); 165 | } 166 | 167 | void __ram_func(sprite_asprite16)(uint16_t *scanbuf, const sprite_t *sp, const affine_transform_t atrans, uint raster_y, 168 | uint raster_w) { 169 | intersect_t isct = _get_sprite_intersect(sp, raster_y, raster_w); 170 | if (isct.size_x <= 0) 171 | return; 172 | interp_hw_t *interp = interp0; 173 | _setup_interp_affine(interp, isct, atrans); 174 | _setup_interp_pix_coordgen(interp, sp, 1); 175 | sprite_ablit16_alpha_loop(scanbuf + MAX(0, sp->x), isct.size_x); 176 | } 177 | -------------------------------------------------------------------------------- /scanvideo/sprite/sprite.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #ifndef _SPRITE_H 8 | #define _SPRITE_H 9 | 10 | #include "pico.h" 11 | #include "affine_transform.h" 12 | 13 | typedef struct sprite { 14 | int16_t x; 15 | int16_t y; 16 | const void *img; 17 | uint8_t log_size; // always square 18 | bool has_opacity_metadata; 19 | } sprite_t; 20 | 21 | // ---------------------------------------------------------------------------- 22 | // Functions from sprite.S 23 | 24 | // Constant-colour span 25 | void sprite_fill8(uint8_t *dst, uint8_t colour, uint len); 26 | void sprite_fill16(uint16_t *dst, uint16_t colour, uint len); 27 | 28 | // Block image transfers 29 | void sprite_blit8(uint8_t *dst, const uint8_t *src, uint len); 30 | void sprite_blit8_alpha(uint8_t *dst, const uint8_t *src, uint len); 31 | void sprite_blit16(uint16_t *dst, const uint16_t *src, uint len); 32 | void sprite_blit16_alpha(uint16_t *dst, const uint16_t *src, uint len); 33 | 34 | // These are just inner loops, and require INTERP0 to be configured before calling: 35 | void sprite_ablit8_loop(uint8_t *dst, uint len); 36 | void sprite_ablit8_alpha_loop(uint8_t *dst, uint len); 37 | void sprite_ablit16_loop(uint16_t *dst, uint len); 38 | void sprite_ablit16_alpha_loop(uint16_t *dst, uint len); 39 | 40 | // ---------------------------------------------------------------------------- 41 | // Functions from sprite.c 42 | 43 | // Render the intersection of a sprite with the current scanline: 44 | void sprite_sprite8(uint8_t *scanbuf, const sprite_t *sp, uint raster_y, uint raster_w); 45 | void sprite_sprite16(uint16_t *scanbuf, const sprite_t *sp, uint raster_y, uint raster_w); 46 | 47 | // As above, but apply an affine transform on sprite texture lookups (SLOW, even with interpolator) 48 | void sprite_asprite8(uint8_t *scanbuf, const sprite_t *sp, const affine_transform_t atrans, uint raster_y, 49 | uint raster_w); 50 | void sprite_asprite16(uint16_t *scanbuf, const sprite_t *sp, const affine_transform_t atrans, uint raster_y, 51 | uint raster_w); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /scanvideo/sprite_demo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (PICO_ON_DEVICE AND TARGET pico_scanvideo_dpi) 2 | add_executable(sprite_demo 3 | sprite_demo.c 4 | ) 5 | 6 | target_compile_definitions(sprite_demo PRIVATE 7 | PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS=500 8 | ) 9 | target_link_libraries(sprite_demo PRIVATE 10 | pico_multicore 11 | pico_stdlib 12 | pico_scanvideo_dpi 13 | sprite) 14 | pico_add_extra_outputs(sprite_demo) 15 | endif () 16 | -------------------------------------------------------------------------------- /scanvideo/sprite_demo/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspberrypi/pico-playground/49a701a955ada550f8e7221aadef354eb9ff7b62/scanvideo/sprite_demo/screenshot.jpg -------------------------------------------------------------------------------- /scanvideo/sprite_demo/sprite_demo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include "pico.h" 11 | #include "hardware/uart.h" 12 | #include "hardware/gpio.h" 13 | #include "pico/scanvideo.h" 14 | #include "pico/scanvideo/composable_scanline.h" 15 | #include "pico/multicore.h" 16 | #include "pico/sync.h" 17 | #include "pico/stdlib.h" 18 | #include "hardware/clocks.h" 19 | 20 | #include "sprite.h" 21 | #include "raspberry_128x128_bgar5515.h" 22 | #include "raspberry_128x128_bgar5515_flip.h" 23 | 24 | #include "hardware/vreg.h" 25 | 26 | //#define VGA_MODE vga_mode_320x240_60 27 | #define VGA_MODE vga_mode_640x480_60 28 | #define DUAL_CORE_RENDER 29 | // #define TURBO_BOOST 1 30 | #define N_BERRIES 45 31 | 32 | CU_REGISTER_DEBUG_PINS(generation) 33 | CU_SELECT_DEBUG_PINS(generation) 34 | 35 | extern const struct scanvideo_pio_program video_24mhz_composable; 36 | 37 | // to make sure only one core updates the state when the frame number changes 38 | // todo note we should actually make sure here that the other core isn't still rendering (i.e. all must arrive before either can proceed - a la barrier) 39 | static struct mutex frame_logic_mutex; 40 | 41 | static void frame_update_logic(); 42 | static void render_scanline(struct scanvideo_scanline_buffer *dest, int core); 43 | 44 | // "Worker thread" for each core 45 | void __time_critical_func(render_loop)() { 46 | static uint32_t last_frame_num = 0; 47 | int core_num = get_core_num(); 48 | printf("Rendering on core %d\n", core_num); 49 | while (true) { 50 | struct scanvideo_scanline_buffer *scanline_buffer = scanvideo_begin_scanline_generation(true); 51 | mutex_enter_blocking(&frame_logic_mutex); 52 | uint32_t frame_num = scanvideo_frame_number(scanline_buffer->scanline_id); 53 | // Note that with multiple cores we may have got here not for the first 54 | // scanline, however one of the cores will do this logic first before either 55 | // does the actual generation 56 | if (frame_num != last_frame_num) { 57 | last_frame_num = frame_num; 58 | frame_update_logic(); 59 | } 60 | mutex_exit(&frame_logic_mutex); 61 | 62 | render_scanline(scanline_buffer, core_num); 63 | 64 | // Release the rendered buffer into the wild 65 | scanvideo_end_scanline_generation(scanline_buffer); 66 | } 67 | } 68 | 69 | struct semaphore video_setup_complete; 70 | 71 | void core1_func() { 72 | sem_acquire_blocking(&video_setup_complete); 73 | render_loop(); 74 | } 75 | 76 | int vga_main(void) { 77 | mutex_init(&frame_logic_mutex); 78 | sem_init(&video_setup_complete, 0, 1); 79 | 80 | // Core 1 will wait for us to finish video setup, and then start rendering 81 | #ifdef DUAL_CORE_RENDER 82 | multicore_launch_core1(core1_func); 83 | #endif 84 | 85 | hard_assert(VGA_MODE.width + 4 <= PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS * 2); 86 | scanvideo_setup(&VGA_MODE); 87 | scanvideo_timing_enable(true); 88 | 89 | sem_release(&video_setup_complete); 90 | render_loop(); 91 | 92 | } 93 | 94 | // Helper functions to: 95 | // - Get a scanbuf into a state where a region of it can be directly rendered to, 96 | // and return a pointer to this region 97 | // - After rendering, manipulate this scanbuffer into a form where PIO can 98 | // yeet it out on VGA 99 | 100 | static inline uint16_t *raw_scanline_prepare(struct scanvideo_scanline_buffer *dest, uint width) { 101 | assert(width >= 3); 102 | assert(width % 2 == 0); 103 | // +1 for the black pixel at the end, -3 because the program outputs n+3 pixels. 104 | dest->data[0] = COMPOSABLE_RAW_RUN | (width + 1 - 3 << 16); 105 | // After user pixels, 1 black pixel then discard remaining FIFO data 106 | dest->data[width / 2 + 2] = 0x0000u | (COMPOSABLE_EOL_ALIGN << 16); 107 | dest->data_used = width / 2 + 2; 108 | assert(dest->data_used <= dest->data_max); 109 | return (uint16_t *) &dest->data[1]; 110 | } 111 | 112 | static inline void raw_scanline_finish(struct scanvideo_scanline_buffer *dest) { 113 | // Need to pivot the first pixel with the count so that PIO can keep up 114 | // with its 1 pixel per 2 clocks 115 | uint32_t first = dest->data[0]; 116 | uint32_t second = dest->data[1]; 117 | dest->data[0] = (first & 0x0000ffffu) | ((second & 0x0000ffffu) << 16); 118 | dest->data[1] = (second & 0xffff0000u) | ((first & 0xffff0000u) >> 16); 119 | dest->status = SCANLINE_OK; 120 | } 121 | 122 | sprite_t berry[N_BERRIES]; 123 | int vx[N_BERRIES]; 124 | int vy[N_BERRIES]; 125 | 126 | void __time_critical_func(render_scanline)(struct scanvideo_scanline_buffer *dest, int core) { 127 | int l = scanvideo_scanline_number(dest->scanline_id); 128 | uint16_t *colour_buf = raw_scanline_prepare(dest, VGA_MODE.width); 129 | 130 | DEBUG_PINS_SET(generation, (core + 1)); 131 | DEBUG_PINS_SET(generation, 4); 132 | const uint16_t bgcol = PICO_SCANVIDEO_PIXEL_FROM_RGB8(0x40, 0xc0, 0xff); 133 | sprite_fill16(colour_buf, bgcol, VGA_MODE.width); 134 | DEBUG_PINS_CLR(generation, 4); 135 | 136 | for (int i = 0; i < N_BERRIES; ++i) 137 | sprite_sprite16(colour_buf, &berry[i], l, VGA_MODE.width); 138 | 139 | DEBUG_PINS_CLR(generation, (core + 1)); 140 | raw_scanline_finish(dest); 141 | } 142 | 143 | static inline int random_velocity() { 144 | // Never return 0 -- it makes the demo much less interesting 145 | return (rand() % 5 + 1) * (rand() & 0x8000 ? 1 : -1); 146 | } 147 | 148 | static inline int clip(int x, int min, int max) { 149 | return x < min ? min : x > max ? max : x; 150 | } 151 | 152 | static int xmin; 153 | static int xmax; 154 | static int ymin; 155 | static int ymax; 156 | 157 | void __time_critical_func(frame_update_logic)() { 158 | for (int i = 0; i < N_BERRIES; ++i) { 159 | berry[i].x += vx[i]; 160 | berry[i].y += vy[i]; 161 | int xclip = clip(berry[i].x, xmin, xmax); 162 | int yclip = clip(berry[i].y, ymin, ymax); 163 | if (berry[i].x != xclip || berry[i].y != yclip) { 164 | berry[i].x = xclip; 165 | berry[i].y = yclip; 166 | vx[i] = random_velocity(); 167 | vy[i] = random_velocity(); 168 | berry[i].img = vx[i] < 0 ? raspberry_128x128_flip : raspberry_128x128; 169 | } 170 | } 171 | } 172 | 173 | int main(void) { 174 | #if TURBO_BOOST 175 | vreg_set_voltage(VREG_VOLTAGE_MAX); 176 | sleep_ms(10); 177 | set_sys_clock_khz(400000, true); 178 | #else 179 | #if PICO_SCANVIDEO_48MHZ 180 | set_sys_clock_khz(192000, true); 181 | #else 182 | set_sys_clock_khz(200000, true); 183 | #endif 184 | #endif 185 | // Re init uart now that clk_peri has changed 186 | setup_default_uart(); 187 | 188 | #ifdef PICO_SMPS_MODE_PIN 189 | gpio_init(PICO_SMPS_MODE_PIN); 190 | gpio_set_dir(PICO_SMPS_MODE_PIN, GPIO_OUT); 191 | gpio_put(PICO_SMPS_MODE_PIN, 1); 192 | #endif 193 | 194 | xmin = -100; 195 | xmax = VGA_MODE.width - 30; 196 | ymin = -100; 197 | ymax = VGA_MODE.height - 30; 198 | 199 | for (int i = 0; i < N_BERRIES; ++i) { 200 | berry[i].x = rand() % (xmax - xmin + 1) - xmin; 201 | berry[i].y = rand() % (ymax - ymin + 1) - ymin; 202 | berry[i].img = raspberry_128x128_flip; 203 | berry[i].log_size = 7; 204 | berry[i].has_opacity_metadata = true; 205 | vx[i] = random_velocity(); 206 | vy[i] = random_velocity(); 207 | } 208 | 209 | return vga_main(); 210 | } 211 | -------------------------------------------------------------------------------- /scanvideo/test_pattern/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (TARGET pico_scanvideo_dpi) 2 | add_executable(test_pattern 3 | test_pattern.c 4 | ) 5 | 6 | target_link_libraries(test_pattern PRIVATE 7 | pico_multicore 8 | pico_stdlib 9 | pico_scanvideo_dpi) 10 | 11 | pico_add_extra_outputs(test_pattern) 12 | 13 | pico_enable_stdio_uart(test_pattern 1) 14 | pico_enable_stdio_usb(test_pattern 1) 15 | endif () 16 | -------------------------------------------------------------------------------- /scanvideo/test_pattern/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspberrypi/pico-playground/49a701a955ada550f8e7221aadef354eb9ff7b62/scanvideo/test_pattern/screenshot.jpg -------------------------------------------------------------------------------- /scanvideo/test_pattern/test_pattern.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | 9 | #include "pico.h" 10 | #include "pico/stdlib.h" 11 | #include "pico/multicore.h" 12 | #include "pico/scanvideo.h" 13 | #include "pico/scanvideo/composable_scanline.h" 14 | #include "pico/sync.h" 15 | 16 | #define vga_mode vga_mode_320x240_60 17 | 18 | void core1_func(); 19 | 20 | // Simple color bar program, which draws 7 colored bars: red, green, yellow, blow, magenta, cyan, white 21 | // Can be used to check resister DAC correctness. 22 | // 23 | // Note this program also demonstrates running video on core 1, leaving core 0 free. It supports 24 | // user input over USB or UART stdin, although all it does with it is invert the colors when you press SPACE 25 | 26 | static semaphore_t video_initted; 27 | static bool invert; 28 | 29 | int main(void) { 30 | stdio_init_all(); 31 | 32 | // create a semaphore to be posted when video init is complete 33 | sem_init(&video_initted, 0, 1); 34 | 35 | // launch all the video on core 1, so it isn't affected by USB handling on core 0 36 | multicore_launch_core1(core1_func); 37 | 38 | // wait for initialization of video to be complete 39 | sem_acquire_blocking(&video_initted); 40 | 41 | puts("Color bars ready, press SPACE to invert..."); 42 | 43 | while (true) { 44 | // prevent tearing when we invert - if you're astute you'll notice this actually causes 45 | // a fixed tear a number of scanlines from the top. this is caused by pre-buffering of scanlines 46 | // and is too detailed a topic to fix here. 47 | scanvideo_wait_for_vblank(); 48 | int c = getchar_timeout_us(0); 49 | switch (c) { 50 | case ' ': 51 | invert = !invert; 52 | printf("Inverted: %d\n", invert); 53 | break; 54 | } 55 | } 56 | } 57 | 58 | void draw_color_bar(scanvideo_scanline_buffer_t *buffer) { 59 | // figure out 1/32 of the color value 60 | uint line_num = scanvideo_scanline_number(buffer->scanline_id); 61 | uint32_t primary_color = 1u + (line_num * 7 / vga_mode.height); 62 | uint32_t color_mask = PICO_SCANVIDEO_PIXEL_FROM_RGB5(0x1f * (primary_color & 1u), 0x1f * ((primary_color >> 1u) & 1u), 0x1f * ((primary_color >> 2u) & 1u)); 63 | uint bar_width = vga_mode.width / 32; 64 | 65 | uint16_t *p = (uint16_t *) buffer->data; 66 | 67 | uint32_t invert_bits = invert ? PICO_SCANVIDEO_PIXEL_FROM_RGB5(0x1f,0x1f,0x1f) : 0; 68 | for (uint bar = 0; bar < 32; bar++) { 69 | *p++ = COMPOSABLE_COLOR_RUN; 70 | uint32_t color = PICO_SCANVIDEO_PIXEL_FROM_RGB5(bar, bar, bar); 71 | *p++ = (color & color_mask) ^ invert_bits; 72 | *p++ = bar_width - 3; 73 | } 74 | 75 | // 32 * 3, so we should be word aligned 76 | assert(!(3u & (uintptr_t) p)); 77 | 78 | // black pixel to end line 79 | *p++ = COMPOSABLE_RAW_1P; 80 | *p++ = 0; 81 | // end of line with alignment padding 82 | *p++ = COMPOSABLE_EOL_SKIP_ALIGN; 83 | *p++ = 0; 84 | 85 | buffer->data_used = ((uint32_t *) p) - buffer->data; 86 | assert(buffer->data_used < buffer->data_max); 87 | 88 | buffer->status = SCANLINE_OK; 89 | } 90 | 91 | void core1_func() { 92 | // initialize video and interrupts on core 1 93 | scanvideo_setup(&vga_mode); 94 | scanvideo_timing_enable(true); 95 | sem_release(&video_initted); 96 | 97 | while (true) { 98 | scanvideo_scanline_buffer_t *scanline_buffer = scanvideo_begin_scanline_generation(true); 99 | draw_color_bar(scanline_buffer); 100 | scanvideo_end_scanline_generation(scanline_buffer); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /scanvideo/textmode/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT(textmode) 2 | 3 | if (TARGET pico_scanvideo_dpi) 4 | add_executable(textmode 5 | textmode.c 6 | font6.c 7 | font8.c 8 | font10.c 9 | lcd.c 10 | ) 11 | 12 | target_compile_definitions(textmode PRIVATE 13 | PICO_SCANVIDEO_SCANLINE_BUFFER_COUNT=4 14 | PICO_SCANVIDEO_PLANE1_FIXED_FRAGMENT_DMA=true 15 | PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS=250 # room for more characters 16 | ) 17 | 18 | target_link_libraries(textmode PRIVATE 19 | pico_multicore 20 | pico_stdlib 21 | pico_scanvideo_dpi 22 | render) 23 | pico_add_extra_outputs(textmode) 24 | 25 | if (PICO_RP2350) 26 | target_compile_definitions(textmode PRIVATE 27 | PICO_USE_SW_SPIN_LOCKS=0 28 | ) 29 | endif() 30 | endif() -------------------------------------------------------------------------------- /scanvideo/textmode/FONT_LICENSE.TXT: -------------------------------------------------------------------------------- 1 | ------------------------------- 2 | UBUNTU FONT LICENCE Version 1.0 3 | ------------------------------- 4 | 5 | PREAMBLE 6 | This licence allows the licensed fonts to be used, studied, modified and 7 | redistributed freely. The fonts, including any derivative works, can be 8 | bundled, embedded, and redistributed provided the terms of this licence 9 | are met. The fonts and derivatives, however, cannot be released under 10 | any other licence. The requirement for fonts to remain under this 11 | licence does not require any document created using the fonts or their 12 | derivatives to be published under this licence, as long as the primary 13 | purpose of the document is not to be a vehicle for the distribution of 14 | the fonts. 15 | 16 | DEFINITIONS 17 | "Font Software" refers to the set of files released by the Copyright 18 | Holder(s) under this licence and clearly marked as such. This may 19 | include source files, build scripts and documentation. 20 | 21 | "Original Version" refers to the collection of Font Software components 22 | as received under this licence. 23 | 24 | "Modified Version" refers to any derivative made by adding to, deleting, 25 | or substituting -- in part or in whole -- any of the components of the 26 | Original Version, by changing formats or by porting the Font Software to 27 | a new environment. 28 | 29 | "Copyright Holder(s)" refers to all individuals and companies who have a 30 | copyright ownership of the Font Software. 31 | 32 | "Substantially Changed" refers to Modified Versions which can be easily 33 | identified as dissimilar to the Font Software by users of the Font 34 | Software comparing the Original Version with the Modified Version. 35 | 36 | To "Propagate" a work means to do anything with it that, without 37 | permission, would make you directly or secondarily liable for 38 | infringement under applicable copyright law, except executing it on a 39 | computer or modifying a private copy. Propagation includes copying, 40 | distribution (with or without modification and with or without charging 41 | a redistribution fee), making available to the public, and in some 42 | countries other activities as well. 43 | 44 | PERMISSION & CONDITIONS 45 | This licence does not grant any rights under trademark law and all such 46 | rights are reserved. 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a 49 | copy of the Font Software, to propagate the Font Software, subject to 50 | the below conditions: 51 | 52 | 1) Each copy of the Font Software must contain the above copyright 53 | notice and this licence. These can be included either as stand-alone 54 | text files, human-readable headers or in the appropriate machine- 55 | readable metadata fields within text or binary files as long as those 56 | fields can be easily viewed by the user. 57 | 58 | 2) The font name complies with the following: 59 | (a) The Original Version must retain its name, unmodified. 60 | (b) Modified Versions which are Substantially Changed must be renamed to 61 | avoid use of the name of the Original Version or similar names entirely. 62 | (c) Modified Versions which are not Substantially Changed must be 63 | renamed to both (i) retain the name of the Original Version and (ii) add 64 | additional naming elements to distinguish the Modified Version from the 65 | Original Version. The name of such Modified Versions must be the name of 66 | the Original Version, with "derivative X" where X represents the name of 67 | the new work, appended to that name. 68 | 69 | 3) The name(s) of the Copyright Holder(s) and any contributor to the 70 | Font Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except (i) as required by this licence, (ii) to 72 | acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with 73 | their explicit written permission. 74 | 75 | 4) The Font Software, modified or unmodified, in part or in whole, must 76 | be distributed entirely under this licence, and must not be distributed 77 | under any other licence. The requirement for fonts to remain under this 78 | licence does not affect any document created using the Font Software, 79 | except any version of the Font Software extracted from a document 80 | created using the Font Software may only be distributed under this 81 | licence. 82 | 83 | TERMINATION 84 | This licence becomes null and void if any of the above conditions are 85 | not met. 86 | 87 | DISCLAIMER 88 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF 91 | COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 92 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 93 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 94 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 95 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER 96 | DEALINGS IN THE FONT SOFTWARE. 97 | -------------------------------------------------------------------------------- /scanvideo/textmode/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 | extern const lv_font_t ubuntu_mono6; 40 | extern const lv_font_t ubuntu_mono8; 41 | extern const lv_font_t ubuntu_mono10; 42 | extern const lv_font_t lcd; 43 | #endif //SOFTWARE_FONT_H 44 | -------------------------------------------------------------------------------- /scanvideo/textmode/font6.c: -------------------------------------------------------------------------------- 1 | #include "font.h" 2 | 3 | /******************************************************************************* 4 | * Converted via https://lvgl.io/tools/fontconverter 5 | * Size: 10 px 6 | * Bpp: 4 7 | * Opts: 8 | ******************************************************************************/ 9 | 10 | #ifndef UBUNTU_MONO 11 | #define UBUNTU_MONO 1 12 | #endif 13 | 14 | #if UBUNTU_MONO 15 | 16 | /*----------------- 17 | * BITMAPS 18 | *----------------*/ 19 | 20 | /*Store the image of the glyphs*/ 21 | static const uint8_t gylph_bitmap[] = { 22 | /* U+20 " " */ 23 | 24 | /* U+21 "!" */ 25 | 0xff, 0xfc, 0xc, 26 | 27 | /* U+22 "\"" */ 28 | 0xf0, 0xff, 0xf, 0xb0, 0xa0, 29 | 30 | /* U+23 "#" */ 31 | 0x1f, 0x1f, 0x4c, 0x4c, 0xff, 0xff, 0x97, 0x97, 32 | 0xff, 0xff, 0xf1, 0xf1, 33 | 34 | /* U+24 "$" */ 35 | 0xf, 0x9, 0xfe, 0xf1, 0xe, 0xb2, 0x2b, 0xd0, 36 | 0x1f, 0xff, 0x90, 0xf0, 37 | 38 | /* U+25 "%" */ 39 | 0xaf, 0xcc, 0xf4, 0xf4, 0xaf, 0xc0, 0xc, 0xf9, 40 | 0x4f, 0x4f, 0xcd, 0xfa, 41 | 42 | /* U+26 "&" */ 43 | 0x9f, 0xa0, 0xf2, 0xf0, 0xca, 0x60, 0xcc, 0x7e, 44 | 0xf2, 0xf6, 0xaf, 0x8c, 45 | 46 | /* U+27 "'" */ 47 | 0xff, 0xb0, 48 | 49 | /* U+28 "(" */ 50 | 0x3, 0xb3, 0xe1, 0xa6, 0xf, 0x0, 0xf0, 0xa, 51 | 0x60, 0x3e, 0x10, 0x3b, 52 | 53 | /* U+29 ")" */ 54 | 0xb3, 0x2, 0xe3, 0x6, 0xa0, 0xf, 0x0, 0xf0, 55 | 0x6b, 0x1e, 0x3c, 0x30, 56 | 57 | /* U+2A "*" */ 58 | 0x0, 0xf0, 0xc, 0x9d, 0x9b, 0x1a, 0xea, 0x10, 59 | 0xb2, 0xb0, 60 | 61 | /* U+2B "+" */ 62 | 0x0, 0xf0, 0x0, 0xf, 0x0, 0xff, 0xff, 0xf0, 63 | 0xf, 0x0, 0x0, 0xf0, 0x0, 64 | 65 | /* U+2C "," */ 66 | 0xd, 0xf7, 67 | 68 | /* U+2D "-" */ 69 | 0xff, 70 | 71 | /* U+2E "." */ 72 | 0xc0, 73 | 74 | /* U+2F "/" */ 75 | 0x2, 0xe0, 0x6b, 0x9, 0x70, 0xd4, 0xf, 0x4, 76 | 0xd0, 0x79, 0xb, 0x60, 0xe2, 0x0, 77 | 78 | /* U+30 "0" */ 79 | 0x3d, 0xd3, 0xc4, 0x5b, 0xfd, 0xef, 0xf0, 0xf, 80 | 0xc5, 0x5c, 0x3e, 0xe3, 81 | 82 | /* U+31 "1" */ 83 | 0x5e, 0x9, 0xf0, 0xf, 0x0, 0xf0, 0xf, 0xf, 84 | 0xff, 85 | 86 | /* U+32 "2" */ 87 | 0x8f, 0x98, 0x1f, 0x2, 0xc1, 0xb1, 0x94, 0xf, 88 | 0xff, 89 | 90 | /* U+33 "3" */ 91 | 0xcf, 0x90, 0x2f, 0xf, 0x50, 0x3f, 0x3, 0xfe, 92 | 0xf7, 93 | 94 | /* U+34 "4" */ 95 | 0x9, 0xf0, 0x4e, 0xf0, 0xca, 0xf0, 0xff, 0xff, 96 | 0x0, 0xf0, 0x0, 0xf0, 97 | 98 | /* U+35 "5" */ 99 | 0xcf, 0xff, 0xd0, 0x0, 0xff, 0xa3, 0x1, 0x7e, 100 | 0x0, 0x4f, 0xdf, 0xe6, 101 | 102 | /* U+36 "6" */ 103 | 0x6, 0xcf, 0x7b, 0x30, 0xee, 0xf7, 0xf1, 0x3f, 104 | 0xe4, 0x3f, 0x4e, 0xf6, 105 | 106 | /* U+37 "7" */ 107 | 0xff, 0xf0, 0x3d, 0x8, 0x80, 0xb4, 0xe, 0x20, 108 | 0xf0, 109 | 110 | /* U+38 "8" */ 111 | 0x8f, 0x9f, 0x2f, 0xda, 0xac, 0xbc, 0xf2, 0xfa, 112 | 0xf9, 113 | 114 | /* U+39 "9" */ 115 | 0x7f, 0x6f, 0x3e, 0xf2, 0xf9, 0xfe, 0x17, 0xaf, 116 | 0xa1, 117 | 118 | /* U+3A ":" */ 119 | 0xc0, 0xc, 120 | 121 | /* U+3B ";" */ 122 | 0xc, 0x0, 0x0, 0xd, 0xf7, 123 | 124 | /* U+3C "<" */ 125 | 0x0, 0x4b, 0x2b, 0xc4, 0xfa, 0x31, 0x5, 0xad, 126 | 127 | /* U+3D "=" */ 128 | 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 129 | 130 | /* U+3E ">" */ 131 | 0xc4, 0x0, 0x4c, 0xb2, 0x13, 0xaf, 0xda, 0x50, 132 | 133 | /* U+3F "?" */ 134 | 0xcf, 0xf8, 0x0, 0x2f, 0x1, 0xa5, 0xd, 0x30, 135 | 0x0, 0x0, 0xc, 0x0, 136 | 137 | /* U+40 "@" */ 138 | 0x3d, 0xf6, 0xb7, 0x2f, 0xf1, 0xcf, 0xf0, 0xff, 139 | 0xe2, 0xbf, 0x9c, 0x20, 0x9, 0xff, 140 | 141 | /* U+41 "A" */ 142 | 0x1f, 0xf0, 0x4e, 0xd3, 0x7b, 0xa6, 0x98, 0x89, 143 | 0xcf, 0xfc, 0xf2, 0x2f, 144 | 145 | /* U+42 "B" */ 146 | 0xff, 0xe6, 0xf0, 0x3f, 0xff, 0xf5, 0xf0, 0x3f, 147 | 0xf0, 0x3f, 0xff, 0xe6, 148 | 149 | /* U+43 "C" */ 150 | 0x1b, 0xfd, 0xb9, 0x0, 0xf1, 0x0, 0xf0, 0x0, 151 | 0xb7, 0x0, 0x3c, 0xfd, 152 | 153 | /* U+44 "D" */ 154 | 0xff, 0xb2, 0xf0, 0x8b, 0xf0, 0xf, 0xf0, 0xf, 155 | 0xf0, 0x8b, 0xff, 0xc2, 156 | 157 | /* U+45 "E" */ 158 | 0xff, 0xff, 0xf0, 0x0, 0xff, 0xf0, 0xf0, 0x0, 159 | 0xf0, 0x0, 0xff, 0xff, 160 | 161 | /* U+46 "F" */ 162 | 0xff, 0xff, 0xf0, 0x0, 0xf0, 0x0, 0xff, 0xff, 163 | 0xf0, 0x0, 0xf0, 0x0, 164 | 165 | /* U+47 "G" */ 166 | 0x1b, 0xfd, 0xba, 0x0, 0xf1, 0x0, 0xf0, 0xf, 167 | 0xb7, 0xf, 0x3c, 0xfe, 168 | 169 | /* U+48 "H" */ 170 | 0xf0, 0xf, 0xf0, 0xf, 0xff, 0xff, 0xf0, 0xf, 171 | 0xf0, 0xf, 0xf0, 0xf, 172 | 173 | /* U+49 "I" */ 174 | 0xff, 0xf0, 0xf0, 0xf, 0x0, 0xf0, 0xf, 0xf, 175 | 0xff, 176 | 177 | /* U+4A "J" */ 178 | 0xf, 0xf0, 0xf, 0x0, 0xf0, 0xf, 0x3, 0xfd, 179 | 0xf8, 180 | 181 | /* U+4B "K" */ 182 | 0xf0, 0xcc, 0xf7, 0xf2, 0xff, 0x70, 0xf7, 0x90, 183 | 0xf0, 0xc5, 0xf0, 0x5c, 184 | 185 | /* U+4C "L" */ 186 | 0xf0, 0x0, 0xf0, 0x0, 0xf0, 0x0, 0xf0, 0x0, 187 | 0xf0, 0x0, 0xff, 0xff, 188 | 189 | /* U+4D "M" */ 190 | 0x9c, 0xf9, 0xcc, 0xfc, 0xcc, 0xfc, 0xdc, 0xfc, 191 | 0xf0, 0xf, 0xf0, 0xf, 192 | 193 | /* U+4E "N" */ 194 | 0xf6, 0xf, 0xfb, 0xf, 0xf8, 0x4f, 0xf1, 0xaf, 195 | 0xf0, 0xaf, 0xf0, 0x6f, 196 | 197 | /* U+4F "O" */ 198 | 0x3e, 0xe3, 0xd5, 0x5c, 0xf0, 0xf, 0xf0, 0xf, 199 | 0xd5, 0x5d, 0x4f, 0xf3, 200 | 201 | /* U+50 "P" */ 202 | 0xff, 0xe6, 0xf0, 0x5f, 0xf0, 0x3f, 0xff, 0xf6, 203 | 0xf0, 0x0, 0xf0, 0x0, 204 | 205 | /* U+51 "Q" */ 206 | 0x4e, 0xe3, 0xd5, 0x5c, 0xf0, 0xf, 0xf0, 0xf, 207 | 0xe5, 0x5c, 0x8f, 0xe3, 0xd, 0x61, 0x2, 0xad, 208 | 209 | /* U+52 "R" */ 210 | 0xff, 0xd5, 0xf0, 0x3f, 0xf0, 0x3f, 0xff, 0xf6, 211 | 0xf0, 0xc6, 0xf0, 0x4d, 212 | 213 | /* U+53 "S" */ 214 | 0x6e, 0xfc, 0xf2, 0x0, 0xcc, 0x61, 0x6, 0xcb, 215 | 0x0, 0x2f, 0xcf, 0xf6, 216 | 217 | /* U+54 "T" */ 218 | 0xff, 0xff, 0xf0, 0xf, 0x0, 0x0, 0xf0, 0x0, 219 | 0xf, 0x0, 0x0, 0xf0, 0x0, 0xf, 0x0, 220 | 221 | /* U+55 "U" */ 222 | 0xf0, 0xf, 0xf0, 0xf, 0xf0, 0xf, 0xf0, 0xf, 223 | 0xf3, 0x3f, 0x6f, 0xf6, 224 | 225 | /* U+56 "V" */ 226 | 0xe3, 0x1e, 0xb7, 0x4b, 0x7c, 0x77, 0x2f, 0xc2, 227 | 0xe, 0xe0, 0x9, 0x90, 228 | 229 | /* U+57 "W" */ 230 | 0xc4, 0x4, 0xcc, 0x40, 0x4c, 0xca, 0xfa, 0xcc, 231 | 0xcc, 0xcc, 0xcf, 0x8f, 0xcc, 0xf0, 0xfc, 232 | 233 | /* U+58 "X" */ 234 | 0xc6, 0x5b, 0x2b, 0x92, 0x8, 0x80, 0xa, 0xb0, 235 | 0x49, 0xa5, 0xc4, 0x6c, 236 | 237 | /* U+59 "Y" */ 238 | 0xc6, 0x6, 0xc4, 0xe0, 0xe4, 0xc, 0xdc, 0x0, 239 | 0x3f, 0x20, 0x0, 0xf0, 0x0, 0xf, 0x0, 240 | 241 | /* U+5A "Z" */ 242 | 0xff, 0xf0, 0x8c, 0xe, 0x46, 0xe0, 0xc7, 0xf, 243 | 0xff, 244 | 245 | /* U+5B "[" */ 246 | 0xff, 0xff, 0x0, 0xf0, 0xf, 0x0, 0xf0, 0xf, 247 | 0x0, 0xf0, 0xf, 0xff, 248 | 249 | /* U+5C "\\" */ 250 | 0xe2, 0xb, 0x60, 0x79, 0x4, 0xc0, 0xf, 0x0, 251 | 0xd3, 0x9, 0x70, 0x6a, 0x2, 0xe0, 252 | 253 | /* U+5D "]" */ 254 | 0xff, 0xf0, 0xf, 0x0, 0xf0, 0xf, 0x0, 0xf0, 255 | 0xf, 0x0, 0xff, 0xff, 256 | 257 | /* U+5E "^" */ 258 | 0x5, 0xf5, 0x3, 0xe4, 0xe2, 0xb5, 0x5, 0xb0, 259 | 260 | /* U+5F "_" */ 261 | 0xff, 0xff, 0xf0, 262 | 263 | /* U+60 "`" */ 264 | 0xa1, 0x19, 265 | 266 | /* U+61 "a" */ 267 | 0xff, 0x80, 0x1f, 0xaf, 0xff, 0x2f, 0xaf, 0xf0, 268 | 269 | /* U+62 "b" */ 270 | 0xf0, 0xf, 0x0, 0xfe, 0x8f, 0x3f, 0xf0, 0xff, 271 | 0x2e, 0xff, 0x60, 272 | 273 | /* U+63 "c" */ 274 | 0x5e, 0xee, 0x40, 0xf0, 0xe, 0x40, 0x6f, 0xf0, 275 | 276 | /* U+64 "d" */ 277 | 0x0, 0xf0, 0xf, 0x8e, 0xff, 0x3f, 0xf0, 0xfe, 278 | 0x2f, 0x6f, 0xf0, 279 | 280 | /* U+65 "e" */ 281 | 0x6f, 0x7e, 0x3e, 0xff, 0xfe, 0x30, 0x5f, 0xf0, 282 | 283 | /* U+66 "f" */ 284 | 0x8, 0xfd, 0xf, 0x10, 0xff, 0xf0, 0xf, 0x0, 285 | 0xf, 0x0, 0xf, 0x0, 0xf, 0x0, 286 | 287 | /* U+67 "g" */ 288 | 0x3d, 0xfd, 0xe5, 0xf, 0xf0, 0xf, 0xe4, 0x1f, 289 | 0x6f, 0xff, 0x0, 0x4e, 0xdf, 0xe4, 290 | 291 | /* U+68 "h" */ 292 | 0xf0, 0xf, 0x0, 0xff, 0x9f, 0x1f, 0xf0, 0xff, 293 | 0xf, 0xf0, 0xf0, 294 | 295 | /* U+69 "i" */ 296 | 0xc, 0x0, 0x0, 0xff, 0x0, 0xf0, 0xf, 0x0, 297 | 0xf1, 0xc, 0xf0, 298 | 299 | /* U+6A "j" */ 300 | 0xc, 0x0, 0x0, 0xff, 0xf0, 0xf, 0x0, 0xf0, 301 | 0xf, 0x0, 0xf1, 0x2f, 0xef, 0x80, 302 | 303 | /* U+6B "k" */ 304 | 0xf0, 0xf, 0x0, 0xf0, 0x3f, 0x66, 0xff, 0x2f, 305 | 0x38, 0xf0, 0x40, 306 | 307 | /* U+6C "l" */ 308 | 0xff, 0x0, 0xf0, 0xf, 0x0, 0xf0, 0xf, 0x0, 309 | 0xf1, 0xc, 0xf0, 310 | 311 | /* U+6D "m" */ 312 | 0xef, 0xdb, 0xf1, 0xff, 0xf0, 0xff, 0xf0, 0xf, 313 | 0xf0, 0xf, 314 | 315 | /* U+6E "n" */ 316 | 0xef, 0x8f, 0x1f, 0xf0, 0xff, 0xf, 0xf0, 0xf0, 317 | 318 | /* U+6F "o" */ 319 | 0x6f, 0x6e, 0x4e, 0xf0, 0xfe, 0x4e, 0x6f, 0x60, 320 | 321 | /* U+70 "p" */ 322 | 0xef, 0x6f, 0x2e, 0xf0, 0xff, 0x3f, 0xff, 0x8f, 323 | 0x0, 0xf0, 0x0, 324 | 325 | /* U+71 "q" */ 326 | 0x6f, 0xde, 0x2f, 0xf0, 0xff, 0x3f, 0x8f, 0xf0, 327 | 0xf, 0x0, 0xf0, 328 | 329 | /* U+72 "r" */ 330 | 0xdf, 0xff, 0x10, 0xf0, 0xf, 0x0, 0xf0, 0x0, 331 | 332 | /* U+73 "s" */ 333 | 0x9f, 0xef, 0x30, 0x8f, 0x80, 0x3f, 0xef, 0xa0, 334 | 335 | /* U+74 "t" */ 336 | 0xf, 0xf, 0xff, 0xf, 0x0, 0xf0, 0xf, 0x10, 337 | 0xcf, 338 | 339 | /* U+75 "u" */ 340 | 0xf0, 0xff, 0xf, 0xf0, 0xff, 0x2f, 0x8f, 0xe0, 341 | 342 | /* U+76 "v" */ 343 | 0xf3, 0x2f, 0xc6, 0x5b, 0x8a, 0x98, 0x4d, 0xe3, 344 | 0xf, 0xf0, 345 | 346 | /* U+77 "w" */ 347 | 0xf0, 0x0, 0xfd, 0x6f, 0x6d, 0xcb, 0xfa, 0xc8, 348 | 0xd9, 0xe8, 0x6f, 0x8f, 0x50, 349 | 350 | /* U+78 "x" */ 351 | 0xb8, 0x6b, 0x1d, 0xd1, 0x8, 0x90, 0x2e, 0xc4, 352 | 0xc6, 0x7b, 353 | 354 | /* U+79 "y" */ 355 | 0xe4, 0x3, 0xea, 0x80, 0x9a, 0x5c, 0xe, 0x50, 356 | 0xf6, 0xf0, 0xa, 0xf9, 0x0, 0x8f, 0x20, 0xff, 357 | 0x60, 0x0, 358 | 359 | /* U+7A "z" */ 360 | 0xff, 0xf0, 0x9b, 0x3f, 0x2c, 0xa0, 0xff, 0xf0, 361 | 362 | /* U+7B "{" */ 363 | 0x9, 0xff, 0xf, 0x10, 0xf, 0x0, 0x1f, 0x0, 364 | 0xf9, 0x0, 0x1f, 0x0, 0xf, 0x0, 0xf, 0x10, 365 | 0x9, 0xff, 366 | 367 | /* U+7C "|" */ 368 | 0xff, 0xff, 0xff, 0xff, 0xf0, 369 | 370 | /* U+7D "}" */ 371 | 0xff, 0x80, 0x1, 0xf0, 0x0, 0xf0, 0x0, 0xf1, 372 | 0x0, 0x9f, 0x0, 0xf1, 0x0, 0xf0, 0x1, 0xf0, 373 | 0xff, 0x90, 374 | 375 | /* U+7E "~" */ 376 | 0x9b, 0x3d, 0xe3, 0xd9 377 | }; 378 | 379 | 380 | /*--------------------- 381 | * GLYPH DESCRIPTION 382 | *--------------------*/ 383 | 384 | static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { 385 | {.bitmap_index = 0, .adv_w = 0, .box_h = 0, .box_w = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */, 386 | {.bitmap_index = 0, .adv_w = 80, .box_h = 0, .box_w = 0, .ofs_x = 0, .ofs_y = 0}, 387 | {.bitmap_index = 0, .adv_w = 80, .box_h = 6, .box_w = 1, .ofs_x = 2, .ofs_y = 0}, 388 | {.bitmap_index = 3, .adv_w = 80, .box_h = 3, .box_w = 3, .ofs_x = 1, .ofs_y = 4}, 389 | {.bitmap_index = 8, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 390 | {.bitmap_index = 20, .adv_w = 80, .box_h = 8, .box_w = 3, .ofs_x = 1, .ofs_y = -1}, 391 | {.bitmap_index = 32, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 392 | {.bitmap_index = 44, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 393 | {.bitmap_index = 56, .adv_w = 80, .box_h = 3, .box_w = 1, .ofs_x = 2, .ofs_y = 4}, 394 | {.bitmap_index = 58, .adv_w = 80, .box_h = 8, .box_w = 3, .ofs_x = 1, .ofs_y = -2}, 395 | {.bitmap_index = 70, .adv_w = 80, .box_h = 8, .box_w = 3, .ofs_x = 1, .ofs_y = -2}, 396 | {.bitmap_index = 82, .adv_w = 80, .box_h = 4, .box_w = 5, .ofs_x = 0, .ofs_y = 2}, 397 | {.bitmap_index = 92, .adv_w = 80, .box_h = 5, .box_w = 5, .ofs_x = 0, .ofs_y = 0}, 398 | {.bitmap_index = 105, .adv_w = 80, .box_h = 2, .box_w = 2, .ofs_x = 1, .ofs_y = -1}, 399 | {.bitmap_index = 107, .adv_w = 80, .box_h = 1, .box_w = 2, .ofs_x = 1, .ofs_y = 2}, 400 | {.bitmap_index = 108, .adv_w = 80, .box_h = 1, .box_w = 1, .ofs_x = 2, .ofs_y = 0}, 401 | {.bitmap_index = 109, .adv_w = 80, .box_h = 9, .box_w = 3, .ofs_x = 1, .ofs_y = -2}, 402 | {.bitmap_index = 123, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 403 | {.bitmap_index = 135, .adv_w = 80, .box_h = 6, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 404 | {.bitmap_index = 144, .adv_w = 80, .box_h = 6, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 405 | {.bitmap_index = 153, .adv_w = 80, .box_h = 6, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 406 | {.bitmap_index = 162, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 407 | {.bitmap_index = 174, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 408 | {.bitmap_index = 186, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 409 | {.bitmap_index = 198, .adv_w = 80, .box_h = 6, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 410 | {.bitmap_index = 207, .adv_w = 80, .box_h = 6, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 411 | {.bitmap_index = 216, .adv_w = 80, .box_h = 6, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 412 | {.bitmap_index = 225, .adv_w = 80, .box_h = 4, .box_w = 1, .ofs_x = 2, .ofs_y = 0}, 413 | {.bitmap_index = 227, .adv_w = 80, .box_h = 5, .box_w = 2, .ofs_x = 1, .ofs_y = -1}, 414 | {.bitmap_index = 232, .adv_w = 80, .box_h = 4, .box_w = 4, .ofs_x = 0, .ofs_y = 1}, 415 | {.bitmap_index = 240, .adv_w = 80, .box_h = 3, .box_w = 4, .ofs_x = 0, .ofs_y = 1}, 416 | {.bitmap_index = 246, .adv_w = 80, .box_h = 4, .box_w = 4, .ofs_x = 0, .ofs_y = 1}, 417 | {.bitmap_index = 254, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 1, .ofs_y = 0}, 418 | {.bitmap_index = 266, .adv_w = 80, .box_h = 7, .box_w = 4, .ofs_x = 0, .ofs_y = -1}, 419 | {.bitmap_index = 280, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 420 | {.bitmap_index = 292, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 421 | {.bitmap_index = 304, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 422 | {.bitmap_index = 316, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 423 | {.bitmap_index = 328, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 424 | {.bitmap_index = 340, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 425 | {.bitmap_index = 352, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 426 | {.bitmap_index = 364, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 427 | {.bitmap_index = 376, .adv_w = 80, .box_h = 6, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 428 | {.bitmap_index = 385, .adv_w = 80, .box_h = 6, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 429 | {.bitmap_index = 394, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 430 | {.bitmap_index = 406, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 431 | {.bitmap_index = 418, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 432 | {.bitmap_index = 430, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 433 | {.bitmap_index = 442, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 434 | {.bitmap_index = 454, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 435 | {.bitmap_index = 466, .adv_w = 80, .box_h = 8, .box_w = 4, .ofs_x = 0, .ofs_y = -2}, 436 | {.bitmap_index = 482, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 437 | {.bitmap_index = 494, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 438 | {.bitmap_index = 506, .adv_w = 80, .box_h = 6, .box_w = 5, .ofs_x = 0, .ofs_y = 0}, 439 | {.bitmap_index = 521, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 440 | {.bitmap_index = 533, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 441 | {.bitmap_index = 545, .adv_w = 80, .box_h = 6, .box_w = 5, .ofs_x = 0, .ofs_y = 0}, 442 | {.bitmap_index = 560, .adv_w = 80, .box_h = 6, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 443 | {.bitmap_index = 572, .adv_w = 80, .box_h = 6, .box_w = 5, .ofs_x = 0, .ofs_y = 0}, 444 | {.bitmap_index = 587, .adv_w = 80, .box_h = 6, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 445 | {.bitmap_index = 596, .adv_w = 80, .box_h = 8, .box_w = 3, .ofs_x = 1, .ofs_y = -2}, 446 | {.bitmap_index = 608, .adv_w = 80, .box_h = 9, .box_w = 3, .ofs_x = 1, .ofs_y = -2}, 447 | {.bitmap_index = 622, .adv_w = 80, .box_h = 8, .box_w = 3, .ofs_x = 1, .ofs_y = -2}, 448 | {.bitmap_index = 634, .adv_w = 80, .box_h = 3, .box_w = 5, .ofs_x = 0, .ofs_y = 3}, 449 | {.bitmap_index = 642, .adv_w = 80, .box_h = 1, .box_w = 5, .ofs_x = 0, .ofs_y = -2}, 450 | {.bitmap_index = 645, .adv_w = 80, .box_h = 2, .box_w = 2, .ofs_x = 1, .ofs_y = 5}, 451 | {.bitmap_index = 647, .adv_w = 80, .box_h = 5, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 452 | {.bitmap_index = 655, .adv_w = 80, .box_h = 7, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 453 | {.bitmap_index = 666, .adv_w = 80, .box_h = 5, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 454 | {.bitmap_index = 674, .adv_w = 80, .box_h = 7, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 455 | {.bitmap_index = 685, .adv_w = 80, .box_h = 5, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 456 | {.bitmap_index = 693, .adv_w = 80, .box_h = 7, .box_w = 4, .ofs_x = 1, .ofs_y = 0}, 457 | {.bitmap_index = 707, .adv_w = 80, .box_h = 7, .box_w = 4, .ofs_x = 0, .ofs_y = -2}, 458 | {.bitmap_index = 721, .adv_w = 80, .box_h = 7, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 459 | {.bitmap_index = 732, .adv_w = 80, .box_h = 7, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 460 | {.bitmap_index = 743, .adv_w = 80, .box_h = 9, .box_w = 3, .ofs_x = 1, .ofs_y = -2}, 461 | {.bitmap_index = 757, .adv_w = 80, .box_h = 7, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 462 | {.bitmap_index = 768, .adv_w = 80, .box_h = 7, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 463 | {.bitmap_index = 779, .adv_w = 80, .box_h = 5, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 464 | {.bitmap_index = 789, .adv_w = 80, .box_h = 5, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 465 | {.bitmap_index = 797, .adv_w = 80, .box_h = 5, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 466 | {.bitmap_index = 805, .adv_w = 80, .box_h = 7, .box_w = 3, .ofs_x = 1, .ofs_y = -2}, 467 | {.bitmap_index = 816, .adv_w = 80, .box_h = 7, .box_w = 3, .ofs_x = 1, .ofs_y = -2}, 468 | {.bitmap_index = 827, .adv_w = 80, .box_h = 5, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 469 | {.bitmap_index = 835, .adv_w = 80, .box_h = 5, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 470 | {.bitmap_index = 843, .adv_w = 80, .box_h = 6, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 471 | {.bitmap_index = 852, .adv_w = 80, .box_h = 5, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 472 | {.bitmap_index = 860, .adv_w = 80, .box_h = 5, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 473 | {.bitmap_index = 870, .adv_w = 80, .box_h = 5, .box_w = 5, .ofs_x = 0, .ofs_y = 0}, 474 | {.bitmap_index = 883, .adv_w = 80, .box_h = 5, .box_w = 4, .ofs_x = 0, .ofs_y = 0}, 475 | {.bitmap_index = 893, .adv_w = 80, .box_h = 7, .box_w = 5, .ofs_x = 0, .ofs_y = -2}, 476 | {.bitmap_index = 911, .adv_w = 80, .box_h = 5, .box_w = 3, .ofs_x = 1, .ofs_y = 0}, 477 | {.bitmap_index = 919, .adv_w = 80, .box_h = 9, .box_w = 4, .ofs_x = 1, .ofs_y = -2}, 478 | {.bitmap_index = 937, .adv_w = 80, .box_h = 9, .box_w = 1, .ofs_x = 2, .ofs_y = -2}, 479 | {.bitmap_index = 942, .adv_w = 80, .box_h = 9, .box_w = 4, .ofs_x = 0, .ofs_y = -2}, 480 | {.bitmap_index = 960, .adv_w = 80, .box_h = 2, .box_w = 4, .ofs_x = 0, .ofs_y = 2} 481 | }; 482 | 483 | /*--------------------- 484 | * CHARACTER MAPPING 485 | *--------------------*/ 486 | 487 | 488 | 489 | /*Collect the unicode lists and glyph_id offsets*/ 490 | static const lv_font_fmt_txt_cmap_t cmaps[] = 491 | { 492 | { 493 | .range_start = 32, .range_length = 95, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY, 494 | .glyph_id_start = 1, .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0 495 | } 496 | }; 497 | 498 | 499 | 500 | /*-------------------- 501 | * ALL CUSTOM DATA 502 | *--------------------*/ 503 | 504 | /*Store all the custom data of the font*/ 505 | static lv_font_fmt_txt_dsc_t font_dsc = { 506 | .glyph_bitmap = gylph_bitmap, 507 | .glyph_dsc = glyph_dsc, 508 | .cmaps = cmaps, 509 | .cmap_num = 1, 510 | .bpp = 4, 511 | 512 | .kern_scale = 0, 513 | .kern_dsc = NULL, 514 | .kern_classes = 0, 515 | }; 516 | 517 | 518 | /*----------------- 519 | * PUBLIC FONT 520 | *----------------*/ 521 | 522 | /*Initialize a public general font descriptor*/ 523 | const lv_font_t ubuntu_mono6 = { 524 | .dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */ 525 | // .get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/ 526 | // .get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/ 527 | .line_height = 10, /*The maximum line height required by the font*/ 528 | .base_line = 2, /*Baseline measured from the bottom of the line*/ 529 | }; 530 | 531 | #endif /*#if UBUNTU_MONO*/ 532 | -------------------------------------------------------------------------------- /scanvideo/textmode/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raspberrypi/pico-playground/49a701a955ada550f8e7221aadef354eb9ff7b62/scanvideo/textmode/screenshot.jpg -------------------------------------------------------------------------------- /scanvideo/textmode/textmode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include "pico.h" 10 | #include "pico/stdlib.h" 11 | #include "pico/scanvideo.h" 12 | #include "pico/scanvideo/composable_scanline.h" 13 | #include "pico/multicore.h" 14 | #include "pico/sync.h" 15 | #include "font.h" 16 | 17 | // set this to 3, 4 or 5 for smallest to biggest font 18 | #define FRAGMENT_WORDS 4 19 | 20 | #if PICO_ON_DEVICE 21 | #include "hardware/clocks.h" 22 | #include "hardware/structs/bus_ctrl.h" 23 | #endif 24 | 25 | CU_REGISTER_DEBUG_PINS(frame_gen) 26 | 27 | //CU_SELECT_DEBUG_PINS(frame_gen) 28 | 29 | typedef bool (*render_scanline_func)(struct scanvideo_scanline_buffer *dest, int core); 30 | bool render_scanline_test_pattern(struct scanvideo_scanline_buffer *dest, int core); 31 | bool render_scanline_bg(struct scanvideo_scanline_buffer *dest, int core); 32 | 33 | #define vga_mode vga_mode_640x480_60 34 | //#define vga_mode vga_mode_320x240_60 35 | //#define vga_mode vga_mode_213x160_60 36 | //#define vga_mode vga_mode_160x120_60 37 | ////#define vga_mode vga_mode_tft_800x480_50 38 | //#define vga_mode vga_mode_tft_400x240_50 39 | 40 | #define COUNT ((vga_mode.width/8)-1) 41 | 42 | // for now we want to see second counter on native and don't need both cores 43 | #if PICO_ON_DEVICE 44 | // todo there is a bug in multithreaded rendering atm 45 | #define RENDER_ON_CORE0 46 | #endif 47 | #define RENDER_ON_CORE1 48 | 49 | //#define IRQS_ON_CORE1 50 | 51 | render_scanline_func render_scanline = render_scanline_bg; 52 | 53 | #define COORD_SHIFT 3 54 | int vspeed = 1 * 1; 55 | int hspeed = 1 << COORD_SHIFT; 56 | int hpos; 57 | int vpos; 58 | 59 | static const int input_pin0 = 22; 60 | 61 | // to make sure only one core updates the state when the frame number changes 62 | // todo note we should actually make sure here that the other core isn't still rendering (i.e. all must arrive before either can proceed - a la barrier) 63 | //auto_init_mutex(frame_logic_mutex); 64 | struct mutex frame_logic_mutex; 65 | 66 | static int left = 0; 67 | static int top = 0; 68 | static int x_sprites = 1; 69 | 70 | void go_core1(void (*execute)()); 71 | void init_render_state(int core); 72 | 73 | // ok this is going to be the beginning of retained mode 74 | // 75 | 76 | 77 | void render_loop() { 78 | static uint8_t last_input = 0; 79 | static uint32_t last_frame_num = 0; 80 | int core_num = get_core_num(); 81 | assert(core_num >= 0 && core_num < 2); 82 | printf("Rendering on core %d\n", core_num); 83 | #if DEBUG_PINS_ENABLED(frame_gen) 84 | if (core_num == 1) { 85 | gpio_init(PICO_DEBUG_PIN_BASE + 1); 86 | gpio_set_dir_out_masked(2 << PICO_DEBUG_PIN_BASE); // steal debug pin 2 for this core 87 | } 88 | #endif 89 | while (true) { 90 | struct scanvideo_scanline_buffer *scanline_buffer = scanvideo_begin_scanline_generation(true); 91 | // if (scanline_buffer->data_used) { 92 | // // validate the previous scanline to make sure noone corrupted it 93 | // validate_scanline(scanline_buffer->data, scanline_buffer->data_used, vga_mode.width, vga_mode.width); 94 | // } 95 | // do any frame related logic 96 | // todo probably a race condition here ... thread dealing with last line of a frame may end 97 | // todo up waiting on the next frame... 98 | mutex_enter_blocking(&frame_logic_mutex); 99 | uint32_t frame_num = scanvideo_frame_number(scanline_buffer->scanline_id); 100 | // note that with multiple cores we may have got here not for the first scanline, however one of the cores will do this logic first before either does the actual generation 101 | if (frame_num != last_frame_num) { 102 | // this could should be during vblank as we try to create the next line 103 | // todo should we ignore if we aren't attempting the next line 104 | last_frame_num = frame_num; 105 | hpos += hspeed; 106 | // if (hpos < 0) { 107 | // hpos = 0; 108 | // hspeed = -hspeed; 109 | // } else if (hpos >= (level0_map_width*8 - vga_mode.width) << COORD_SHIFT) { 110 | // hpos = (level0_map_width*8 - vga_mode.width) << COORD_SHIFT; 111 | // hspeed = -hspeed; 112 | // } 113 | uint8_t new_input = gpio_get(input_pin0); 114 | if ((last_input && !new_input) || getchar_timeout_us(0) == ' ') { 115 | static int foo = 1; 116 | foo++; 117 | #if PICO_ON_DEVICE 118 | bus_ctrl_hw->priority = (foo & 1u) << BUSCTRL_BUS_PRIORITY_DMA_R_LSB; 119 | #endif 120 | hpos++; 121 | } 122 | last_input = new_input; 123 | static int bar = 1; 124 | #if PICO_ON_DEVICE 125 | // if (bar >= 800 && bar <= 802) 126 | // bus_ctrl_hw->priority = (bar&1u) << BUSCTRL_BUS_PRIORITY_DMA_R_LSB; 127 | // bar++; 128 | #endif 129 | } 130 | mutex_exit(&frame_logic_mutex); 131 | DEBUG_PINS_SET(frame_gen, core_num ? 2 : 4); 132 | render_scanline(scanline_buffer, core_num); 133 | DEBUG_PINS_CLR(frame_gen, core_num ? 2 : 4); 134 | #if PICO_SCANVIDEO_PLANE_COUNT > 2 135 | assert(false); 136 | #endif 137 | // release the scanline into the wild 138 | scanvideo_end_scanline_generation(scanline_buffer); 139 | // do this outside mutex and scanline generation 140 | } 141 | } 142 | 143 | struct semaphore video_setup_complete; 144 | 145 | void setup_video() { 146 | scanvideo_setup(&vga_mode); 147 | scanvideo_timing_enable(true); 148 | sem_release(&video_setup_complete); 149 | } 150 | 151 | void core1_func() { 152 | #ifdef IRQS_ON_CORE1 153 | setup_video(); 154 | #endif 155 | #ifdef RENDER_ON_CORE1 156 | render_loop(); 157 | #endif 158 | } 159 | 160 | #define TEST_WAIT_FOR_SCANLINE 161 | 162 | #ifdef TEST_WAIT_FOR_SCANLINE 163 | volatile uint32_t scanline_color = 0; 164 | #endif 165 | 166 | uint8_t pad[65536]; 167 | 168 | #if PICO_ON_DEVICE 169 | uint32_t *font_raw_pixels; 170 | #else 171 | uint32_t font_raw_pixels[16384]; 172 | #endif 173 | #define FONT_WIDTH_WORDS FRAGMENT_WORDS 174 | #if FRAGMENT_WORDS == 5 175 | const lv_font_t *font = &ubuntu_mono10; 176 | //const lv_font_t *font = &lcd; 177 | #elif FRAGMENT_WORDS == 4 178 | const lv_font_t *font = &ubuntu_mono8; 179 | #else 180 | const lv_font_t *font = &ubuntu_mono6; 181 | #endif 182 | #define FONT_HEIGHT (font->line_height) 183 | #define FONT_SIZE_WORDS (FONT_HEIGHT * FONT_WIDTH_WORDS) 184 | 185 | void build_font() { 186 | uint16_t colors[16]; 187 | for (int i = 0; i < count_of(colors); i++) { 188 | colors[i] = PICO_SCANVIDEO_PIXEL_FROM_RGB5(1, 1, 1) * ((i * 3) / 2); 189 | if (i) i != 0x8000; 190 | } 191 | #if PICO_ON_DEVICE 192 | font_raw_pixels = (uint32_t *) calloc(4, font->dsc->cmaps->range_length * FONT_SIZE_WORDS); 193 | #endif 194 | uint32_t *p = font_raw_pixels; 195 | assert(font->line_height == FONT_HEIGHT); 196 | for (int c = 0; c < font->dsc->cmaps->range_length; c++) { 197 | // inefficient but simple 198 | const lv_font_fmt_txt_glyph_dsc_t *g = &font->dsc->glyph_dsc[c + 1]; 199 | const uint8_t *b = font->dsc->glyph_bitmap + g->bitmap_index; 200 | int bi = 0; 201 | for (int y = 0; y < FONT_HEIGHT; y++) { 202 | int ey = y - FONT_HEIGHT + font->base_line + g->ofs_y + g->box_h; 203 | for (int x = 0; x < FONT_WIDTH_WORDS * 2; x++) { 204 | uint32_t pixel; 205 | int ex = x - g->ofs_x; 206 | if (ex >= 0 && ex < g->box_w && ey >= 0 && ey < g->box_h) { 207 | pixel = bi & 1 ? colors[b[bi >> 1] & 0xf] : colors[b[bi >> 1] >> 4]; 208 | bi++; 209 | } else { 210 | pixel = 0; 211 | } 212 | // printf("%d", !!pixel); 213 | // uint q = 7 - (c%7); 214 | // pixel = (q&4)*0x0f00 + (q&2) * 0x0f0 + (q&1)*0x0f; 215 | // if ((c%16) == 1 || (c%16) == 2) pixel = 0xffff; 216 | if (!(x & 1)) { 217 | *p = pixel; 218 | } else { 219 | *p++ |= pixel << 16; 220 | } 221 | } 222 | if (ey >= 0 && ey < g->box_h) { 223 | for (int x = FONT_WIDTH_WORDS * 2 - g->ofs_x; x < g->box_w; x++) { 224 | bi++; 225 | } 226 | } 227 | 228 | // printf("\n"); 229 | } 230 | // printf("\n"); 231 | } 232 | printf("%p %p\n", p, font_raw_pixels + font->dsc->cmaps->range_length * FONT_SIZE_WORDS); 233 | } 234 | 235 | int video_main(void) { 236 | 237 | mutex_init(&frame_logic_mutex); 238 | // set 18-22 to RIO for debugging 239 | for (int i = PICO_DEBUG_PIN_BASE; i < 22; ++i) 240 | gpio_init(i); 241 | 242 | // gpio_set_function(i, 8); 243 | gpio_init(24); 244 | gpio_init(22); 245 | // just from this core 246 | gpio_set_dir_out_masked(0x01380000); 247 | gpio_set_dir_in_masked(0x00400000); 248 | 249 | //gpio_set_function(22, 0); 250 | 251 | // debug pin 252 | gpio_put(24, 0); 253 | 254 | printf("%d\n", pad[0]); 255 | #if 0 256 | printf("Press button to start\n"); 257 | // todo NOTE THAT ON STARTUP RIGHT NOW WITH RESET ISSUES ON FPGA, THIS CURRENTLY DOES NOT STOP!!! if you make last_input static, then it never releases instead :-( 258 | uint8_t last_input = 0; 259 | while (true) { 260 | uint8_t new_input = gpio_get(input_pin0); 261 | if (last_input && !new_input) { 262 | break; 263 | } 264 | last_input = new_input; 265 | yield(); 266 | } 267 | #endif 268 | 269 | // go for launch (debug pin) 270 | gpio_put(24, 1); 271 | 272 | build_font(); 273 | sem_init(&video_setup_complete, 0, 1); 274 | #ifndef IRQS_ON_CORE1 275 | setup_video(); 276 | #endif 277 | 278 | #if PICO_ON_DEVICE 279 | // bus_ctrl_hw->priority = 1u << BUSCTRL_BUS_PRIORITY_DMA_R_LSB; 280 | #endif 281 | 282 | init_render_state(0); 283 | 284 | #ifdef RENDER_ON_CORE1 285 | init_render_state(1); 286 | #endif 287 | #if defined(RENDER_ON_CORE1) || defined(IRQS_ON_CORE1) 288 | go_core1(core1_func); 289 | #endif 290 | #ifdef RENDER_ON_CORE0 291 | render_loop(); 292 | #else 293 | 294 | sem_acquire_blocking(&video_setup_complete); 295 | while (true) { 296 | #ifndef TEST_WAIT_FOR_SCANLINE 297 | // Just use vblank to print out a value every second 298 | static int i=0, s=0; 299 | video_wait_for_vblank(); 300 | if (++i == 60) { 301 | printf("%d\n", s++); 302 | i = 0; 303 | } 304 | #else 305 | static uint32_t sl = 0; 306 | sl = scanvideo_wait_for_scanline_complete(sl); 307 | scanline_color = (scanline_color + 0x10u) & 0xffu; 308 | #endif 309 | } 310 | #endif 311 | __builtin_unreachable(); 312 | } 313 | 314 | //struct palette16 *opaque_pi_palette = NULL; 315 | 316 | // must not be called concurrently 317 | void init_render_state(int core) { 318 | 319 | // todo we should of course have a wide solid color span that overlaps 320 | // todo we can of course also reuse these 321 | } 322 | 323 | #if PICO_SCANVIDEO_PLANE1_FRAGMENT_DMA 324 | static __not_in_flash("x") uint16_t beginning_of_line[] = { 325 | // todo we need to be able to shift scanline to absorb these extra pixels 326 | #if FRAGMENT_WORDS == 5 327 | COMPOSABLE_RAW_1P, 0, 328 | #endif 329 | #if FRAGMENT_WORDS >= 4 330 | COMPOSABLE_RAW_1P, 0, 331 | #endif 332 | COMPOSABLE_RAW_1P, 0, 333 | // main run, 2 more black pixels 334 | COMPOSABLE_RAW_RUN, 0, 335 | 0/*COUNT * 2 * FRAGMENT_WORDS -3 + 2*/, 0 336 | }; 337 | static __not_in_flash("y") uint16_t end_of_line[] = { 338 | #if FRAGMENT_WORDS == 5 || FRAGMENT_WORDS == 3 339 | COMPOSABLE_RAW_1P, 0, 340 | #endif 341 | #if FRAGMENT_WORDS == 3 342 | COMPOSABLE_RAW_1P, 0, 343 | #endif 344 | #if FRAGMENT_WORDS >= 4 345 | COMPOSABLE_RAW_2P, 0, 346 | 0, COMPOSABLE_RAW_1P_SKIP_ALIGN, 347 | 0, 0, 348 | #endif 349 | COMPOSABLE_EOL_SKIP_ALIGN, 0xffff // eye catcher 350 | }; 351 | #endif 352 | 353 | bool render_scanline_bg(struct scanvideo_scanline_buffer *dest, int core) { 354 | // 1 + line_num red, then white 355 | uint32_t *buf = dest->data; 356 | size_t buf_length = dest->data_max; 357 | int y = scanvideo_scanline_number(dest->scanline_id) + vpos; 358 | int x = hpos; 359 | //y = (y + frame_number(dest->scanline_id)) % (level0_map_height * 8); 360 | #if !PICO_SCANVIDEO_PLANE1_FRAGMENT_DMA 361 | uint16_t *output = (uint16_t*)buf; 362 | // raw run has an inline first pixel, so we need to handle that here 363 | // todo shift the video mode over by a pixel to account for this 364 | *output++ = COMPOSABLE_RAW_RUN; 365 | *output++ = 0; 366 | #undef COUNT 367 | #define COUNT 10 368 | //(320/FRAGMENT_WORDS) 369 | *output++ = 2 + COUNT * 2 * FRAGMENT_WORDS - 3; 370 | *output++ = 0; 371 | uint32_t *dbase = font_raw_pixels + FONT_WIDTH_WORDS * (y % FONT_HEIGHT); 372 | for(int i=0;idata_max)); 398 | dest->data_used = (uint16_t)(((uint32_t*)output) - buf); 399 | #else 400 | // we handle both ends separately 401 | // static const uint32_t end_of_line[] = { 402 | // COMPOSABLE_RAW_1P | (0u<<16), 403 | // COMPOSABLE_EOL_SKIP_ALIGN | (0xffff << 16) // eye catcher ffff 404 | // }; 405 | #undef COUNT 406 | // todo for SOME REASON, 80 is the max we can do without starting to really get bus delays (even with priority)... not sure how this could be 407 | // todo actually it seems it can work, it just mostly starts incorrectly synced!? 408 | #if PICO_RP2040 409 | #define COUNT MIN(vga_mode.width/(FRAGMENT_WORDS*2)-1, 80)//MAX_SCANLINE_BUFFER_WORDS / 2 - 2) 410 | #else 411 | #define COUNT MIN(vga_mode.width/(FRAGMENT_WORDS*2)-1, (PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS / 2 - 2)) 412 | #endif 413 | //#undef COUNT 414 | //#define COUNT 79 415 | // we need to take up 5 words, since we have fixed width 416 | #if PICO_SCANVIDEO_PLANE1_FIXED_FRAGMENT_DMA 417 | dest->fragment_words = FRAGMENT_WORDS; 418 | #endif 419 | beginning_of_line[FRAGMENT_WORDS * 2 - 2] = COUNT * 2 * FRAGMENT_WORDS - 3 + 2; 420 | assert(FRAGMENT_WORDS * 2 == count_of(beginning_of_line)); 421 | assert(FRAGMENT_WORDS * 2 == count_of(end_of_line)); 422 | 423 | uint32_t *output32 = buf; 424 | #if PICO_SCANVIDEO_PLANE1_VARIABLE_FRAGMENT_DMA 425 | *output32++ = FRAGMENT_WORDS; 426 | #endif 427 | *output32++ = host_safe_hw_ptr(beginning_of_line); 428 | uint32_t *dbase = font_raw_pixels + FONT_WIDTH_WORDS * (y % FONT_HEIGHT); 429 | int cmax = font->dsc->cmaps[0].range_length; 430 | int ch = 0; 431 | // __breakpoint(); 432 | for (int i = 0; i < COUNT; i++) { 433 | ch++; 434 | if (ch == cmax) ch = 1; 435 | #if PICO_SCANVIDEO_PLANE1_VARIABLE_FRAGMENT_DMA 436 | *output32++ = FRAGMENT_WORDS; 437 | #endif 438 | *output32++ = host_safe_hw_ptr(dbase + ch * FONT_HEIGHT * FONT_WIDTH_WORDS); 439 | } 440 | #if PICO_SCANVIDEO_PLANE1_VARIABLE_FRAGMENT_DMA 441 | *output32++ = FRAGMENT_WORDS; 442 | #endif 443 | *output32++ = host_safe_hw_ptr(end_of_line); 444 | *output32++ = 0; // end of chain 445 | #if PICO_SCANVIDEO_PLANE1_VARIABLE_FRAGMENT_DMA 446 | *output32++ = 0; // end of chain 447 | #endif 448 | assert(0 == (3u & (intptr_t) output32)); 449 | assert((uint32_t *) output32 <= (buf + dest->data_max)); 450 | 451 | dest->data_used = (uint16_t) (output32 - 452 | buf); // todo we don't want to include the off the end data in the "size" for the dma 453 | #endif 454 | // why was this here, it is buf anyway! 455 | // dest->data = buf; 456 | 457 | #if PICO_SCANVIDEO_PLANE_COUNT > 1 458 | #if !PICO_SCANVIDEO_PLANE2_VARIABLE_FRAGMENT_DMA 459 | assert(false); 460 | #endif 461 | buf = dest->data2; 462 | output32 = buf; 463 | 464 | uint32_t *inline_data = output32 + PICO_SCANVIDEO_MAX_SCANLINE_BUFFER2_WORDS / 2; 465 | output = (uint16_t *)inline_data; 466 | 467 | uint32_t *base = (uint32_t *)output; 468 | 469 | #define MAKE_SEGMENT \ 470 | assert(0 == (3u & (intptr_t)output)); \ 471 | *output32++ = (uint32_t*)output - base; \ 472 | *output32++ = host_safe_hw_ptr(base); \ 473 | base = (uint32_t *)output; 474 | 475 | int wibble = (frame_number(dest->scanline_id)>>2)%7; 476 | for(int q = 0; q < x_sprites; q++) { 477 | // nice if we can do two black pixel before 478 | *output++ = COMPOSABLE_RAW_RUN; 479 | *output++ = 0; 480 | *output++ = galaga_tile_data.width + 2 - 3; 481 | *output++ = 0; 482 | MAKE_SEGMENT; 483 | 484 | span_offsets = galaga_tile_data.span_offsets + (q+wibble) * galaga_tile_data.height + (y - vpos);//(y%galaga_tile_data.count 7u); 485 | off = span_offsets[0]; 486 | data = (uint16_t *) (galaga_tile_data.blob.bytes + off); 487 | 488 | *output32++ = galaga_tile_data.width / 2; 489 | *output32++ = host_safe_hw_ptr(data); 490 | } 491 | *output++ = COMPOSABLE_RAW_1P; 492 | *output++ = 0; 493 | *output++ = COMPOSABLE_EOL_SKIP_ALIGN; 494 | *output++ = 0xffff; 495 | MAKE_SEGMENT; 496 | 497 | // end of dma chain 498 | *output32++ = 0; 499 | *output32++ = 0; 500 | 501 | assert(output32 < inline_data); 502 | assert((uint32_t*)output <= (buf + dest->data2_max)); 503 | dest->data2_used = (uint16_t)(output32 - buf); // todo we don't want to include the inline data in the "size" for the dma 504 | #endif 505 | dest->status = SCANLINE_OK; 506 | return true; 507 | } 508 | 509 | void go_core1(void (*execute)()) { 510 | multicore_launch_core1(execute); 511 | } 512 | 513 | int main(void) { 514 | #if PICO_SCANVIDEO_48MHZ 515 | set_sys_clock_48mhz(); 516 | #endif 517 | gpio_put(27, 0); 518 | 519 | setup_default_uart(); 520 | 521 | #if !PICO_ON_DEVICE 522 | //#include 523 | // for(int i = 0; i<64;i++) { 524 | // printf("%d, ", (int)(0x7f*cos(i*M_PI/32))); 525 | // } 526 | // printf("\n"); 527 | #endif 528 | 529 | return video_main(); 530 | } -------------------------------------------------------------------------------- /sleep/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT PICO_NO_HARDWARE) 2 | add_subdirectory(hello_dormant) 3 | add_subdirectory(hello_sleep) 4 | endif () 5 | -------------------------------------------------------------------------------- /sleep/hello_dormant/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(hello_dormant_gpio 2 | hello_dormant_gpio.c 3 | ) 4 | target_link_libraries(hello_dormant_gpio 5 | pico_stdlib 6 | hardware_sleep) 7 | pico_add_extra_outputs(hello_dormant_gpio) 8 | 9 | add_executable(hello_dormant_aon 10 | hello_dormant_aon.c 11 | ) 12 | target_link_libraries(hello_dormant_aon 13 | pico_stdlib 14 | hardware_sleep 15 | ) 16 | pico_add_extra_outputs(hello_dormant_aon) 17 | -------------------------------------------------------------------------------- /sleep/hello_dormant/hello_dormant_aon.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include "pico/stdlib.h" 9 | #include "pico/sleep.h" 10 | 11 | // For clock_configure_gpin 12 | #ifdef PICO_RP2040 13 | #include "hardware/clocks.h" 14 | #endif 15 | 16 | // For RP2040 this example needs an external clock fed into the GP20 17 | // Note: Only GP20 and GP22 can be used for clock input, See the GPIO function table in the datasheet. 18 | // You can use another Pico to generate this. See the clocks/hello_gpout example for more details. 19 | // rp2040: clock_gpio_init(21, CLOCKS_CLK_GPOUT3_CTRL_AUXSRC_VALUE_CLK_RTC, 1); // 46875Hz can only export a clock on gpios 21,23,24,25 and only 21 is exposed by Pico 20 | // RP2350 has an LPOSC it can use, so doesn't need this 21 | #define EXTERNAL_CLOCK_INPUT_PIN 20 22 | #define RTC_FREQ_HZ 46875 23 | 24 | static void sleep_callback(void) { 25 | printf("AON timer woke us up\n"); 26 | } 27 | 28 | static void aon_sleep(void) { 29 | 30 | // Get the time from the aon timer and set our alarm time 31 | struct timespec ts; 32 | aon_timer_get_time(&ts); 33 | ts.tv_sec += 10; 34 | 35 | printf("Sleeping for 10 seconds\n"); 36 | uart_default_tx_wait_blocking(); 37 | 38 | #if PICO_RP2040 39 | // The RTC must be run from an external source, since the dormant source will be inactive 40 | clock_configure_gpin(clk_rtc, EXTERNAL_CLOCK_INPUT_PIN, RTC_FREQ_HZ, 46875); 41 | #endif 42 | 43 | // Go to sleep for 10 seconds, with RTC running off GP20 44 | // The external clock is the RTC of another pico being fed to GP20 45 | sleep_goto_dormant_until(&ts, &sleep_callback); 46 | } 47 | 48 | int main() { 49 | 50 | stdio_init_all(); 51 | printf("Hello Dormant AON Timer!\n"); 52 | 53 | struct timespec ts = { .tv_sec = 1723124088, .tv_nsec = 0 }; 54 | aon_timer_start(&ts); 55 | 56 | while(true) { 57 | printf("Awake for 10s\n"); 58 | sleep_ms(10000); 59 | 60 | uart_default_tx_wait_blocking(); 61 | 62 | // Set the crystal oscillator as the dormant clock source, UART will be reconfigured from here 63 | // This is necessary before sending the pico dormant 64 | #if PICO_RP2040 65 | printf("Switching to XOSC\n"); 66 | sleep_run_from_xosc(); 67 | #else 68 | printf("Switching to LPSC\n"); 69 | sleep_run_from_lposc(); 70 | #endif 71 | 72 | uart_default_tx_wait_blocking(); 73 | 74 | printf("Going dormant\n"); 75 | uart_default_tx_wait_blocking(); 76 | 77 | // Go to sleep until the RTC interrupt is generated after 10 seconds 78 | aon_sleep(); 79 | 80 | // Re-enabling clock sources and generators. 81 | sleep_power_up(); 82 | } 83 | return 0; 84 | } 85 | -------------------------------------------------------------------------------- /sleep/hello_dormant/hello_dormant_gpio.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include "pico/stdlib.h" 9 | #include "pico/sleep.h" 10 | 11 | #define WAKE_GPIO 10 12 | 13 | int main() { 14 | stdio_init_all(); 15 | printf("Hello Dormant GPIO!\n"); 16 | 17 | printf("Test starting in 10s\n"); 18 | sleep_ms(10000); 19 | 20 | while(true) { 21 | printf("Switching to XOSC\n"); 22 | uart_default_tx_wait_blocking(); 23 | 24 | // Set the crystal oscillator as the dormant clock source, UART will be reconfigured from here 25 | // This is necessary before sending the pico into dormancy 26 | sleep_run_from_xosc(); 27 | 28 | printf("Going dormant until GPIO %d goes edge high\n", WAKE_GPIO); 29 | uart_default_tx_wait_blocking(); 30 | 31 | // Go to sleep until we see a high edge on GPIO 10 32 | sleep_goto_dormant_until_edge_high(WAKE_GPIO); 33 | 34 | // Re-enabling clock sources and generators. 35 | sleep_power_up(); 36 | printf("Now awake for 10s\n"); 37 | sleep_ms(1000 * 10); 38 | } 39 | 40 | return 0; 41 | } -------------------------------------------------------------------------------- /sleep/hello_sleep/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(hello_sleep_alarm 2 | hello_sleep_alarm.c 3 | ) 4 | target_link_libraries(hello_sleep_alarm 5 | pico_stdlib 6 | hardware_sleep 7 | ) 8 | pico_add_extra_outputs(hello_sleep_alarm) 9 | 10 | add_executable(hello_sleep_aon 11 | hello_sleep_aon.c 12 | ) 13 | target_link_libraries(hello_sleep_aon 14 | pico_stdlib 15 | hardware_sleep 16 | pico_aon_timer 17 | ) 18 | pico_add_extra_outputs(hello_sleep_aon) 19 | -------------------------------------------------------------------------------- /sleep/hello_sleep/hello_sleep_alarm.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include "pico/stdlib.h" 9 | #include "pico/sleep.h" 10 | 11 | static bool awake; 12 | 13 | static void alarm_sleep_callback(uint alarm_id) { 14 | printf("alarm woke us up\n"); 15 | uart_default_tx_wait_blocking(); 16 | awake = true; 17 | hardware_alarm_set_callback(alarm_id, NULL); 18 | hardware_alarm_unclaim(alarm_id); 19 | } 20 | 21 | int main() { 22 | 23 | stdio_init_all(); 24 | printf("Hello Alarm Sleep!\n"); 25 | 26 | do { 27 | printf("Awake for 10 seconds\n"); 28 | sleep_ms(1000 * 10); 29 | 30 | printf("Switching to XOSC\n"); 31 | 32 | // Wait for the fifo to be drained so we get reliable output 33 | uart_default_tx_wait_blocking(); 34 | 35 | // Set the crystal oscillator as the dormant clock source, UART will be reconfigured from here 36 | // This is only really necessary before sending the pico dormant but running from xosc while asleep saves power 37 | sleep_run_from_xosc(); 38 | awake = false; 39 | 40 | // Go to sleep until the alarm interrupt is generated after 10 seconds 41 | printf("Sleeping for 10 seconds\n"); 42 | uart_default_tx_wait_blocking(); 43 | 44 | if (sleep_goto_sleep_for(10000, &alarm_sleep_callback)) { 45 | // Make sure we don't wake 46 | while (!awake) { 47 | printf("Should be sleeping\n"); 48 | } 49 | } 50 | 51 | // Re-enabling clock sources and generators. 52 | sleep_power_up(); 53 | } while(true); 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /sleep/hello_sleep/hello_sleep_aon.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include "pico/stdlib.h" 9 | #include "pico/sleep.h" 10 | #include "pico/aon_timer.h" 11 | 12 | static bool awake; 13 | 14 | static void sleep_callback(void) { 15 | printf("AON Timer woke us up\n"); 16 | uart_default_tx_wait_blocking(); 17 | awake = true; 18 | } 19 | 20 | static void aon_sleep(void) { 21 | 22 | // Get the time from the aon timer and set our alarm time 23 | struct timespec ts; 24 | aon_timer_get_time(&ts); 25 | ts.tv_sec += 10; 26 | 27 | printf("Sleeping for 10 seconds\n"); 28 | uart_default_tx_wait_blocking(); 29 | 30 | // Go to sleep 31 | sleep_goto_sleep_until(&ts, &sleep_callback); 32 | } 33 | 34 | int main() { 35 | 36 | stdio_init_all(); 37 | 38 | printf("Hello AON timer Sleep!\n"); 39 | 40 | struct timespec ts = { .tv_sec = 1723124088, .tv_nsec = 0 }; 41 | aon_timer_start(&ts); 42 | 43 | do { 44 | printf("Awake for 10 seconds\n"); 45 | sleep_ms(1000 * 10); 46 | 47 | printf("Switching to XOSC\n"); 48 | 49 | // Wait for the fifo to be drained so we get reliable output 50 | uart_default_tx_wait_blocking(); 51 | 52 | // Set the crystal oscillator as the dormant clock source, UART will be reconfigured from here 53 | // This is only really necessary before sending the pico into dormancy but running from xosc while asleep saves power 54 | sleep_run_from_xosc(); 55 | 56 | // Go to sleep until an interrupt is generated after 10 seconds 57 | awake = false; 58 | aon_sleep(); 59 | 60 | // Make sure we don't wake 61 | while (!awake) { 62 | printf("Should be sleeping\n"); 63 | } 64 | 65 | // Re-enabling clock sources and generators. 66 | sleep_power_up(); 67 | } while(true); 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /standalone/README.md: -------------------------------------------------------------------------------- 1 | Directory of standalone projects; i.e. they are not included in the main build. 2 | You should do a cmake build from within them -------------------------------------------------------------------------------- /standalone/static_sdk/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # THIS IS A SLIGHTLY HACKY EXAMPLE PROJECT DESIGNED TO HELP YOU TOWARDS INCLUDING THE PICO SDK 2 | # CODE OUTSIDE OF THE CMAKE BUILD 3 | # 4 | # Note that the Pico SDK is designed to make it easy for users to build applications rather to build 5 | # other runtimes for the Pico. 6 | # 7 | # There is no included "static library" of the Pico SDK because all of the Pico SDK Libraries are configurable 8 | # on a per binary basis, and are always compiled from scratch per binary within the normal Pico SDK CMake build 9 | # 10 | # Note you would want to customize this to add or remove the dependencies you want (e.g. you mau not want 11 | # to depend on pico_stdlib, but rather pick and mix some of its constituents to give you a finer grain of control 12 | # 13 | # Note that this example produces a build_info.txt in the output with some approximate (we're not trying to 14 | # rewrite CMake here!) compile and link flags based on the setup of your static_sdk library 15 | # 16 | # Finally note that even copying the build and link flags from build_info.txt and linking against the generated 17 | # library does not get you all the way, as you still need a second stage boot at the start of the binary (0x000-0x100) 18 | 19 | cmake_minimum_required(VERSION 3.12) 20 | 21 | # Pull in PICO SDK (must be before project) 22 | include(pico_sdk_import.cmake) 23 | 24 | project(static_sdk C CXX) 25 | set(CMAKE_C_STANDARD 11) 26 | set(CMAKE_CXX_STANDARD 17) 27 | 28 | pico_sdk_init() 29 | 30 | if (PICO_ON_DEVICE) 31 | include(hacky_cmake_helper.cmake) 32 | 33 | add_library(static_sdk 34 | dummy.c) 35 | 36 | target_compile_definitions(static_sdk PUBLIC 37 | PICO_NO_BUILD_TYPE_FEATURE=1) 38 | 39 | target_link_libraries(static_sdk PUBLIC pico_stdlib) 40 | 41 | # our hacky_cmake_helper does not deal with generator expressions for libraries, so pick some (assuming we want the pico versions) 42 | # you might choose not to have these libraries (which are mostly redirecting AEABI functions to our faster (possible ROM) equivalents 43 | # especially if you are skipping SDK initialization (which is required for some of them) 44 | target_link_libraries(static_sdk PUBLIC 45 | pico_printf_pico 46 | pico_bit_ops_pico 47 | pico_divider_hardware 48 | pico_float_pico 49 | pico_double_pico 50 | pico_mem_ops_pico 51 | pico_int64_ops_pico 52 | 53 | pico_stdio_uart 54 | #pico_stdio_usb 55 | ) 56 | 57 | # Compile definitions 58 | gather_vars(COMPILE_DEFINITIONS INTERFACE_COMPILE_DEFINITIONS _touched_cd static_sdk) 59 | list(REMOVE_DUPLICATES COMPILE_DEFINITIONS) 60 | List(PREPEND COMPILE_DEFINITIONS "") # need a -D at the beginning 61 | string(REPLACE ";" " -D" COMPILE_DEFINITIONS "${COMPILE_DEFINITIONS}") 62 | 63 | # Compile options 64 | gather_vars(COMPILE_OPTIONS INTERFACE_COMPILE_OPTIONS _touched_cf static_sdk) 65 | list(REMOVE_DUPLICATES COMPILE_OPTIONS) 66 | string(REPLACE ";" " " COMPILE_OPTIONS "${COMPILE_OPTIONS}") 67 | # Note that the Pico SDK has some options specific to the language being compiled. All 68 | # SDK has already been compiled, so we'll just strip these and you'll have to deal with these yourselves 69 | string(REGEX REPLACE "\\$<\\$:.*>" "" COMPILE_OPTIONS ${COMPILE_OPTIONS}) 70 | 71 | # Include dirs 72 | gather_vars(INCLUDE_DIRECTORIES INTERFACE_INCLUDE_DIRECTORIES _touched_id static_sdk) 73 | list(REMOVE_DUPLICATES INCLUDE_DIRECTORIES) 74 | string(REPLACE ";" " " INCLUDE_DIRECTORIES "${INCLUDE_DIRECTORIES}") 75 | 76 | # Link options 77 | gather_vars(LINK_FLAGS INTERFACE_LINK_OPTIONS _touched_l static_sdk) 78 | string(REPLACE ";" " " LINK_FLAGS "${LINK_FLAGS}") 79 | string(REPLACE "LINKER:" "-Wl," LINK_FLAGS "${LINK_FLAGS}") 80 | 81 | # Target properties were scoped to the target being built, lets scope them to static_sdk 82 | string(REPLACE "$/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 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 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 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /stdio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (NOT PICO_NO_HARDWARE) 2 | add_subdirectory_exclude_platforms(pio) 3 | endif () 4 | -------------------------------------------------------------------------------- /stdio/pio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(stdio_pio) 2 | 3 | pico_generate_pio_header(stdio_pio ${CMAKE_CURRENT_LIST_DIR}/uart_tx.pio) 4 | 5 | target_sources(stdio_pio PRIVATE stdio_pio.c) 6 | 7 | target_link_libraries(stdio_pio PRIVATE pico_stdlib hardware_pio) 8 | pico_add_extra_outputs(stdio_pio) 9 | -------------------------------------------------------------------------------- /stdio/pio/stdio_pio.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | // Example of how to attach a custom interface to the stdout plumbing, so that 8 | // printf(), puts() etc will have their output directed there. This example 9 | // uses PIO to add an extra UART output on GPIO 2, which is not normally 10 | // usable for UART TX. 11 | 12 | #include 13 | 14 | #include "pico/stdlib.h" 15 | #include "pico/stdio/driver.h" 16 | #include "hardware/pio.h" 17 | #include "uart_tx.pio.h" 18 | 19 | #define SERIAL_TX_PIN 2 20 | #define SERIAL_BAUD 115200 21 | 22 | // Shim function for directing characters to a fixed SM. 23 | PIO print_pio; 24 | uint print_sm; 25 | void pio_out_chars(const char *buf, int len) { 26 | for (int i = 0; i < len; ++i) { 27 | uart_tx_program_putc(print_pio, print_sm, buf[i]); 28 | } 29 | } 30 | 31 | // Data structure for registering this function with the stdio plumbing (we 32 | // only provide output here, but stdin can be hooked up in the same way.) 33 | stdio_driver_t stdio_pio = { 34 | .out_chars = pio_out_chars, 35 | #ifdef PICO_STDIO_ENABLE_CRLF_SUPPORT 36 | .crlf_enabled = PICO_STDIO_DEFAULT_CRLF 37 | #endif 38 | }; 39 | 40 | int main() { 41 | 42 | stdio_init_all(); 43 | // This text will go to all the regular stdout outputs enabled on this 44 | // build (any/all of UART, USB and semihosting) 45 | printf("PIO stdio example! PIO isn't set up yet.\n"); 46 | 47 | // Get the state machine ready to print characters 48 | print_pio = pio0; 49 | print_sm = pio_claim_unused_sm(print_pio, true); 50 | uint offset = pio_add_program(print_pio, &uart_tx_program); 51 | uart_tx_program_init(print_pio, print_sm, offset, SERIAL_TX_PIN, SERIAL_BAUD); 52 | 53 | while (true) { 54 | // Register the print function with stdio 55 | stdio_set_driver_enabled(&stdio_pio, true); 56 | printf("\n\n1. PIO driver enabled -- this text should go to all outputs.\n"); 57 | 58 | // Direct stdout *only* to PIO 59 | stdio_filter_driver(&stdio_pio); 60 | printf("2. PIO driver filtered -- this text should go *only* to PIO on GPIO %d\n", SERIAL_TX_PIN); 61 | 62 | // Remove filter to send text to all outputs once more 63 | stdio_filter_driver(NULL); 64 | printf("3. Filter removed -- this text should go to all outputs.\n"); 65 | 66 | stdio_set_driver_enabled(&stdio_pio, false); 67 | printf("4. PIO driver removed -- this text should go only to the standard outputs.\n"); 68 | 69 | sleep_ms(1000); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /stdio/pio/uart_tx.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | .program uart_tx 8 | .side_set 1 opt 9 | 10 | ; An 8n1 UART transmit program. 11 | ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin. 12 | 13 | pull side 1 [7] ; Assert stop bit, or stall with line in idle state 14 | set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks 15 | bitloop: ; This loop will run 8 times (8n1 UART) 16 | out pins, 1 ; Shift 1 bit from OSR to the first OUT pin 17 | jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. 18 | 19 | 20 | % c-sdk { 21 | #include "hardware/clocks.h" 22 | 23 | static inline void uart_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx, uint baud) { 24 | // Tell PIO to initially drive output-high on the selected pin, then map PIO 25 | // onto that pin with the IO muxes. 26 | pio_sm_set_pins_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx); 27 | pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx); 28 | pio_gpio_init(pio, pin_tx); 29 | 30 | pio_sm_config c = uart_tx_program_get_default_config(offset); 31 | 32 | // OUT shifts to right, no autopull 33 | sm_config_set_out_shift(&c, true, false, 32); 34 | 35 | // We are mapping both OUT and side-set to the same pin, because sometimes 36 | // we need to assert user data onto the pin (with OUT) and sometimes 37 | // assert constant values (start/stop bit) 38 | sm_config_set_out_pins(&c, pin_tx, 1); 39 | sm_config_set_sideset_pins(&c, pin_tx); 40 | 41 | // We only need TX, so get an 8-deep FIFO! 42 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 43 | 44 | // SM transmits 1 bit per 8 execution cycles. 45 | float div = (float)clock_get_hz(clk_sys) / (8 * baud); 46 | sm_config_set_clkdiv(&c, div); 47 | 48 | pio_sm_init(pio, sm, offset, &c); 49 | pio_sm_set_enabled(pio, sm, true); 50 | } 51 | 52 | static inline void uart_tx_program_putc(PIO pio, uint sm, char c) { 53 | pio_sm_put_blocking(pio, sm, (uint32_t)c); 54 | } 55 | 56 | static inline void uart_tx_program_puts(PIO pio, uint sm, const char *s) { 57 | while (*s) 58 | uart_tx_program_putc(pio, sm, *s++); 59 | } 60 | 61 | %} 62 | --------------------------------------------------------------------------------