#include #include #include "pico/stdlib.h" #include "pico/multicore.h" #include "hardware/clocks.h" #include "hardware/irq.h" #include "hardware/sync.h" #include "hardware/gpio.h" #include "hardware/vreg.h" #include "hardware/structs/bus_ctrl.h" #include "hardware/structs/ssi.h" #include "hardware/dma.h" #include "pico/sem.h" #include "dvi.h" #include "dvi_serialiser.h" #include "common_dvi_pin_configs.h" #include "tmds_encode_font_2bpp.h" #include "audio.h" // TODO should put this in scratch_x, it out to fit... #include "font_8x8.h" #define FONT_CHAR_WIDTH 8 #define FONT_CHAR_HEIGHT 8 #define FONT_N_CHARS 95 #define FONT_FIRST_ASCII 32 // Pick one: #define MODE_640x480_60Hz // #define MODE_800x600_60Hz // #define MODE_960x540p_60Hz // #define MODE_1280x720_30Hz #if defined(MODE_640x480_60Hz) // DVDD 1.2V (1.1V seems ok too) #define FRAME_WIDTH 640 #define FRAME_HEIGHT 240 #define VREG_VSEL VREG_VOLTAGE_1_20 #define DVI_TIMING dvi_timing_640x480p_60hz #elif defined(MODE_800x600_60Hz) // DVDD 1.3V, going downhill with a tailwind #define FRAME_WIDTH 800 #define FRAME_HEIGHT 600 #define VREG_VSEL VREG_VOLTAGE_1_30 #define DVI_TIMING dvi_timing_800x600p_60hz #elif defined(MODE_960x540p_60Hz) // DVDD 1.25V (slower silicon may need the full 1.3, or just not work) #define FRAME_WIDTH 960 #define FRAME_HEIGHT 540 #define VREG_VSEL VREG_VOLTAGE_1_25 #define DVI_TIMING dvi_timing_960x540p_60hz #elif defined(MODE_1280x720_30Hz) // 1280x720p 30 Hz (nonstandard) // DVDD 1.25V (slower silicon may need the full 1.3, or just not work) #define FRAME_WIDTH 1280 #define FRAME_HEIGHT 720 #define VREG_VSEL VREG_VOLTAGE_1_25 #define DVI_TIMING dvi_timing_1280x720p_30hz #else #error "Select a video mode!" #endif struct dvi_inst dvi0; #define CHAR_COLS (FRAME_WIDTH / FONT_CHAR_WIDTH) #define CHAR_ROWS (FRAME_HEIGHT / FONT_CHAR_HEIGHT) #define COLOUR_PLANE_SIZE_WORDS (CHAR_ROWS * CHAR_COLS * 4 / 32) char charbuf[CHAR_ROWS * CHAR_COLS]; uint32_t colourbuf[3 * COLOUR_PLANE_SIZE_WORDS]; static inline void set_char(uint x, uint y, char c) { if (x >= CHAR_COLS || y >= CHAR_ROWS) return; charbuf[x + y * CHAR_COLS] = c; } //Audio Related #define AUDIO_BUFFER_SIZE 256 audio_sample_t audio_buffer[AUDIO_BUFFER_SIZE]; struct repeating_timer audio_timer; bool audio_timer_callback(struct repeating_timer *t) { while(true) { int size = get_write_size(&dvi0.audio_ring, false); if (size == 0) return true; audio_sample_t *audio_ptr = get_write_pointer(&dvi0.audio_ring); audio_sample_t sample; static uint sample_count = 0; for (int cnt = 0; cnt < size; cnt++) { sample.channels[0] = commodore_argentina[sample_count % commodore_argentina_len] << 8; sample.channels[1] = commodore_argentina[(sample_count+1024) % commodore_argentina_len] << 8; *audio_ptr++ = sample; sample_count = sample_count + 1; } increase_write_pointer(&dvi0.audio_ring, size); } } // Pixel format RGB222 static inline void set_colour(uint x, uint y, uint8_t fg, uint8_t bg) { if (x >= CHAR_COLS || y >= CHAR_ROWS) return; uint char_index = x + y * CHAR_COLS; uint bit_index = char_index % 8 * 4; uint word_index = char_index / 8; for (int plane = 0; plane < 3; ++plane) { uint32_t fg_bg_combined = (fg & 0x3) | (bg << 2 & 0xc); colourbuf[word_index] = (colourbuf[word_index] & ~(0xfu << bit_index)) | (fg_bg_combined << bit_index); fg >>= 2; bg >>= 2; word_index += COLOUR_PLANE_SIZE_WORDS; } } void core1_main() { dvi_register_irqs_this_core(&dvi0, DMA_IRQ_0); dvi_start(&dvi0); while (true) { for (uint y = 0; y < FRAME_HEIGHT; ++y) { uint32_t *tmdsbuf; queue_remove_blocking(&dvi0.q_tmds_free, &tmdsbuf); for (int plane = 0; plane < 3; ++plane) { tmds_encode_font_2bpp( (const uint8_t*)&charbuf[y / FONT_CHAR_HEIGHT * CHAR_COLS], &colourbuf[y / FONT_CHAR_HEIGHT * (COLOUR_PLANE_SIZE_WORDS / CHAR_ROWS) + plane * COLOUR_PLANE_SIZE_WORDS], tmdsbuf + plane * (FRAME_WIDTH / DVI_SYMBOLS_PER_WORD), FRAME_WIDTH, (const uint8_t*)&font_8x8[y % FONT_CHAR_HEIGHT * FONT_N_CHARS] - FONT_FIRST_ASCII ); } queue_add_blocking(&dvi0.q_tmds_valid, &tmdsbuf); } } } int __not_in_flash("main") main() { vreg_set_voltage(VREG_VSEL); sleep_ms(10); // Run system at TMDS bit clock set_sys_clock_khz(DVI_TIMING.bit_clk_khz, true); // setup_default_uart(); dvi0.timing = &DVI_TIMING; dvi0.ser_cfg = DVI_DEFAULT_SERIAL_CONFIG; dvi_init(&dvi0, next_striped_spin_lock_num(), next_striped_spin_lock_num()); for (uint y = 0; y < CHAR_ROWS; ++y) { for (uint x = 0; x < CHAR_COLS; ++x) { set_char(x, y, (x + y * CHAR_COLS) % FONT_N_CHARS + FONT_FIRST_ASCII); set_colour(x, y, x, y); } } hw_set_bits(&bus_ctrl_hw->priority, BUSCTRL_BUS_PRIORITY_PROC1_BITS); // HDMI Audio related dvi_get_blank_settings(&dvi0)->top = 4 * 0; dvi_get_blank_settings(&dvi0)->bottom = 4 * 0; dvi_audio_sample_buffer_set(&dvi0, audio_buffer, AUDIO_BUFFER_SIZE); dvi_set_audio_freq(&dvi0, 44100, 28000, 6272); add_repeating_timer_ms(-2, audio_timer_callback, NULL, &audio_timer); printf("starting...\n"); multicore_launch_core1(core1_main); while (1) __wfi(); }