main.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include "pico/stdlib.h" 4 #include "pico/multicore.h" 5 #include "hardware/clocks.h" 6 #include "hardware/irq.h" 7 #include "hardware/sync.h" 8 #include "hardware/gpio.h" 9 #include "hardware/vreg.h" 10 #include "hardware/structs/bus_ctrl.h" 11 #include "hardware/structs/ssi.h" 12 #include "hardware/dma.h" 13 #include "pico/sem.h" 14 15 #include "dvi.h" 16 #include "dvi_serialiser.h" 17 #include "common_dvi_pin_configs.h" 18 #include "tmds_encode.h" 19 20 #include "font_8x8.h" 21 #define FONT_CHAR_WIDTH 8 22 #define FONT_CHAR_HEIGHT 8 23 #define FONT_N_CHARS 95 24 #define FONT_FIRST_ASCII 32 25 26 27 // Pick one: 28 #define MODE_640x480_60Hz 29 // #define MODE_800x600_60Hz 30 // #define MODE_960x540p_60Hz 31 // #define MODE_1280x720_30Hz 32 33 #if defined(MODE_640x480_60Hz) 34 // DVDD 1.2V (1.1V seems ok too) 35 #define FRAME_WIDTH 640 36 #define FRAME_HEIGHT 480 37 #define VREG_VSEL VREG_VOLTAGE_1_20 38 #define DVI_TIMING dvi_timing_640x480p_60hz 39 40 #elif defined(MODE_800x600_60Hz) 41 // DVDD 1.3V, going downhill with a tailwind 42 #define FRAME_WIDTH 800 43 #define FRAME_HEIGHT 600 44 #define VREG_VSEL VREG_VOLTAGE_1_30 45 #define DVI_TIMING dvi_timing_800x600p_60hz 46 47 48 #elif defined(MODE_960x540p_60Hz) 49 // DVDD 1.25V (slower silicon may need the full 1.3, or just not work) 50 #define FRAME_WIDTH 960 51 #define FRAME_HEIGHT 540 52 #define VREG_VSEL VREG_VOLTAGE_1_25 53 #define DVI_TIMING dvi_timing_960x540p_60hz 54 55 #elif defined(MODE_1280x720_30Hz) 56 // 1280x720p 30 Hz (nonstandard) 57 // DVDD 1.25V (slower silicon may need the full 1.3, or just not work) 58 #define FRAME_WIDTH 1280 59 #define FRAME_HEIGHT 720 60 #define VREG_VSEL VREG_VOLTAGE_1_25 61 #define DVI_TIMING dvi_timing_1280x720p_30hz 62 63 #else 64 #error "Select a video mode!" 65 #endif 66 67 #define LED_PIN 16 68 69 struct dvi_inst dvi0; 70 struct semaphore dvi_start_sem; 71 72 #define CHAR_COLS (FRAME_WIDTH / FONT_CHAR_WIDTH) 73 #define CHAR_ROWS (FRAME_HEIGHT / FONT_CHAR_HEIGHT) 74 char charbuf[CHAR_ROWS * CHAR_COLS]; 75 76 static inline void prepare_scanline(const char *chars, uint y) { 77 static uint8_t scanbuf[FRAME_WIDTH / 8]; 78 // First blit font into 1bpp scanline buffer, then encode scanbuf into tmdsbuf 79 for (uint i = 0; i < CHAR_COLS; ++i) { 80 uint c = chars[i + y / FONT_CHAR_HEIGHT * CHAR_COLS]; 81 scanbuf[i] = font_8x8[(c - FONT_FIRST_ASCII) + (y % FONT_CHAR_HEIGHT) * FONT_N_CHARS]; 82 } 83 uint32_t *tmdsbuf; 84 queue_remove_blocking(&dvi0.q_tmds_free, &tmdsbuf); 85 tmds_encode_1bpp((const uint32_t*)scanbuf, tmdsbuf, FRAME_WIDTH); 86 queue_add_blocking(&dvi0.q_tmds_valid, &tmdsbuf); 87 } 88 89 void core1_scanline_callback() { 90 static uint y = 1; 91 prepare_scanline(charbuf, y); 92 y = (y + 1) % FRAME_HEIGHT; 93 } 94 95 void __not_in_flash("main") core1_main() { 96 dvi_register_irqs_this_core(&dvi0, DMA_IRQ_0); 97 sem_acquire_blocking(&dvi_start_sem); 98 dvi_start(&dvi0); 99 100 // The text display is completely IRQ driven (takes up around 30% of cycles @ 101 // VGA). We could do something useful, or we could just take a nice nap 102 while (1) 103 __wfi(); 104 __builtin_unreachable(); 105 } 106 107 int __not_in_flash("main") main() { 108 vreg_set_voltage(VREG_VSEL); 109 sleep_ms(10); 110 #ifdef RUN_FROM_CRYSTAL 111 set_sys_clock_khz(12000, true); 112 #else 113 // Run system at TMDS bit clock 114 set_sys_clock_khz(DVI_TIMING.bit_clk_khz, true); 115 #endif 116 117 setup_default_uart(); 118 119 gpio_init(LED_PIN); 120 gpio_set_dir(LED_PIN, GPIO_OUT); 121 122 printf("Configuring DVI\n"); 123 124 dvi0.timing = &DVI_TIMING; 125 dvi0.ser_cfg = DVI_DEFAULT_SERIAL_CONFIG; 126 dvi0.scanline_callback = core1_scanline_callback; 127 dvi_init(&dvi0, next_striped_spin_lock_num(), next_striped_spin_lock_num()); 128 129 printf("Prepare first scanline\n"); 130 for (int i = 0; i < CHAR_ROWS * CHAR_COLS; ++i) 131 charbuf[i] = FONT_FIRST_ASCII + i % FONT_N_CHARS; 132 prepare_scanline(charbuf, 0); 133 134 printf("Core 1 start\n"); 135 sem_init(&dvi_start_sem, 0, 1); 136 hw_set_bits(&bus_ctrl_hw->priority, BUSCTRL_BUS_PRIORITY_PROC1_BITS); 137 multicore_launch_core1(core1_main); 138 139 140 sem_release(&dvi_start_sem); 141 while (1) 142 __wfi(); 143 __builtin_unreachable(); 144 } 145