2021-01-20 17:16:37 +00:00
/*
* Copyright ( c ) 2020 Raspberry Pi ( Trading ) Ltd .
*
* SPDX - License - Identifier : BSD - 3 - Clause
*/
# include <stdio.h>
# include <stdlib.h>
# include "pico.h"
# include "hardware/uart.h"
# include "hardware/gpio.h"
# include "pico/scanvideo.h"
# include "pico/scanvideo/composable_scanline.h"
# include "pico/multicore.h"
# include "pico/sync.h"
# include "pico/stdlib.h"
# include "hardware/clocks.h"
# include "sprite.h"
# include "raspberry_128x128_bgar5515.h"
# include "raspberry_128x128_bgar5515_flip.h"
2023-02-09 23:18:42 +00:00
# include "hardware/vreg.h"
2021-01-20 17:16:37 +00:00
//#define VGA_MODE vga_mode_320x240_60
# define VGA_MODE vga_mode_640x480_60
# define DUAL_CORE_RENDER
2023-02-09 23:18:42 +00:00
// #define TURBO_BOOST 1
2021-01-20 17:16:37 +00:00
# define N_BERRIES 45
CU_REGISTER_DEBUG_PINS ( generation )
CU_SELECT_DEBUG_PINS ( generation )
extern const struct scanvideo_pio_program video_24mhz_composable ;
// to make sure only one core updates the state when the frame number changes
// 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)
static struct mutex frame_logic_mutex ;
static void frame_update_logic ( ) ;
static void render_scanline ( struct scanvideo_scanline_buffer * dest , int core ) ;
// "Worker thread" for each core
void __time_critical_func ( render_loop ) ( ) {
static uint32_t last_frame_num = 0 ;
int core_num = get_core_num ( ) ;
printf ( " Rendering on core %d \n " , core_num ) ;
while ( true ) {
struct scanvideo_scanline_buffer * scanline_buffer = scanvideo_begin_scanline_generation ( true ) ;
mutex_enter_blocking ( & frame_logic_mutex ) ;
uint32_t frame_num = scanvideo_frame_number ( scanline_buffer - > scanline_id ) ;
// 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
if ( frame_num ! = last_frame_num ) {
last_frame_num = frame_num ;
frame_update_logic ( ) ;
}
mutex_exit ( & frame_logic_mutex ) ;
render_scanline ( scanline_buffer , core_num ) ;
// Release the rendered buffer into the wild
scanvideo_end_scanline_generation ( scanline_buffer ) ;
}
}
struct semaphore video_setup_complete ;
void core1_func ( ) {
sem_acquire_blocking ( & video_setup_complete ) ;
render_loop ( ) ;
}
int vga_main ( void ) {
mutex_init ( & frame_logic_mutex ) ;
sem_init ( & video_setup_complete , 0 , 1 ) ;
// Core 1 will wait for us to finish video setup, and then start rendering
# ifdef DUAL_CORE_RENDER
multicore_launch_core1 ( core1_func ) ;
# endif
hard_assert ( VGA_MODE . width + 4 < = PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS * 2 ) ;
scanvideo_setup ( & VGA_MODE ) ;
scanvideo_timing_enable ( true ) ;
sem_release ( & video_setup_complete ) ;
render_loop ( ) ;
}
// Helper functions to:
// - Get a scanbuf into a state where a region of it can be directly rendered to,
// and return a pointer to this region
// - After rendering, manipulate this scanbuffer into a form where PIO can
// yeet it out on VGA
static inline uint16_t * raw_scanline_prepare ( struct scanvideo_scanline_buffer * dest , uint width ) {
assert ( width > = 3 ) ;
assert ( width % 2 = = 0 ) ;
// +1 for the black pixel at the end, -3 because the program outputs n+3 pixels.
dest - > data [ 0 ] = COMPOSABLE_RAW_RUN | ( width + 1 - 3 < < 16 ) ;
// After user pixels, 1 black pixel then discard remaining FIFO data
dest - > data [ width / 2 + 2 ] = 0x0000u | ( COMPOSABLE_EOL_ALIGN < < 16 ) ;
dest - > data_used = width / 2 + 2 ;
assert ( dest - > data_used < = dest - > data_max ) ;
return ( uint16_t * ) & dest - > data [ 1 ] ;
}
static inline void raw_scanline_finish ( struct scanvideo_scanline_buffer * dest ) {
// Need to pivot the first pixel with the count so that PIO can keep up
// with its 1 pixel per 2 clocks
uint32_t first = dest - > data [ 0 ] ;
uint32_t second = dest - > data [ 1 ] ;
dest - > data [ 0 ] = ( first & 0x0000ffffu ) | ( ( second & 0x0000ffffu ) < < 16 ) ;
dest - > data [ 1 ] = ( second & 0xffff0000u ) | ( ( first & 0xffff0000u ) > > 16 ) ;
dest - > status = SCANLINE_OK ;
}
sprite_t berry [ N_BERRIES ] ;
int vx [ N_BERRIES ] ;
int vy [ N_BERRIES ] ;
void __time_critical_func ( render_scanline ) ( struct scanvideo_scanline_buffer * dest , int core ) {
int l = scanvideo_scanline_number ( dest - > scanline_id ) ;
uint16_t * colour_buf = raw_scanline_prepare ( dest , VGA_MODE . width ) ;
DEBUG_PINS_SET ( generation , ( core + 1 ) ) ;
DEBUG_PINS_SET ( generation , 4 ) ;
const uint16_t bgcol = PICO_SCANVIDEO_PIXEL_FROM_RGB8 ( 0x40 , 0xc0 , 0xff ) ;
sprite_fill16 ( colour_buf , bgcol , VGA_MODE . width ) ;
DEBUG_PINS_CLR ( generation , 4 ) ;
for ( int i = 0 ; i < N_BERRIES ; + + i )
sprite_sprite16 ( colour_buf , & berry [ i ] , l , VGA_MODE . width ) ;
DEBUG_PINS_CLR ( generation , ( core + 1 ) ) ;
raw_scanline_finish ( dest ) ;
}
static inline int random_velocity ( ) {
// Never return 0 -- it makes the demo much less interesting
return ( rand ( ) % 5 + 1 ) * ( rand ( ) & 0x8000 ? 1 : - 1 ) ;
}
static inline int clip ( int x , int min , int max ) {
return x < min ? min : x > max ? max : x ;
}
static int xmin ;
static int xmax ;
static int ymin ;
static int ymax ;
void __time_critical_func ( frame_update_logic ) ( ) {
for ( int i = 0 ; i < N_BERRIES ; + + i ) {
berry [ i ] . x + = vx [ i ] ;
berry [ i ] . y + = vy [ i ] ;
int xclip = clip ( berry [ i ] . x , xmin , xmax ) ;
int yclip = clip ( berry [ i ] . y , ymin , ymax ) ;
if ( berry [ i ] . x ! = xclip | | berry [ i ] . y ! = yclip ) {
berry [ i ] . x = xclip ;
berry [ i ] . y = yclip ;
vx [ i ] = random_velocity ( ) ;
vy [ i ] = random_velocity ( ) ;
berry [ i ] . img = vx [ i ] < 0 ? raspberry_128x128_flip : raspberry_128x128 ;
}
}
}
int main ( void ) {
2023-02-09 23:18:42 +00:00
# if TURBO_BOOST
vreg_set_voltage ( VREG_VOLTAGE_MAX ) ;
2021-01-20 17:16:37 +00:00
sleep_ms ( 10 ) ;
2021-04-01 15:43:31 +00:00
set_sys_clock_khz ( 400000 , true ) ;
2021-01-20 17:16:37 +00:00
# else
2022-05-17 21:03:20 +00:00
# if PICO_SCANVIDEO_48MHZ
2021-02-27 03:20:53 +00:00
set_sys_clock_khz ( 192000 , true ) ;
2021-04-01 15:43:31 +00:00
# else
2021-02-27 03:20:53 +00:00
set_sys_clock_khz ( 200000 , true ) ;
2021-04-01 15:43:31 +00:00
# endif
2021-01-20 17:16:37 +00:00
# endif
// Re init uart now that clk_peri has changed
setup_default_uart ( ) ;
# ifdef PICO_SMPS_MODE_PIN
gpio_init ( PICO_SMPS_MODE_PIN ) ;
gpio_set_dir ( PICO_SMPS_MODE_PIN , GPIO_OUT ) ;
gpio_put ( PICO_SMPS_MODE_PIN , 1 ) ;
# endif
xmin = - 100 ;
xmax = VGA_MODE . width - 30 ;
ymin = - 100 ;
ymax = VGA_MODE . height - 30 ;
for ( int i = 0 ; i < N_BERRIES ; + + i ) {
berry [ i ] . x = rand ( ) % ( xmax - xmin + 1 ) - xmin ;
berry [ i ] . y = rand ( ) % ( ymax - ymin + 1 ) - ymin ;
berry [ i ] . img = raspberry_128x128_flip ;
berry [ i ] . log_size = 7 ;
berry [ i ] . has_opacity_metadata = true ;
vx [ i ] = random_velocity ( ) ;
vy [ i ] = random_velocity ( ) ;
}
return vga_main ( ) ;
}