kopia lustrzana https://github.com/Wren6991/PicoDVI
152 wiersze
4.0 KiB
C
152 wiersze
4.0 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#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/dma.h"
|
|
#include "pico/sem.h"
|
|
|
|
#include "dvi.h"
|
|
#include "dvi_serialiser.h"
|
|
#include "common_dvi_pin_configs.h"
|
|
#include "tmds_encode.h"
|
|
|
|
#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_720x480_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 480
|
|
#define VREG_VSEL VREG_VOLTAGE_1_20
|
|
#define DVI_TIMING dvi_timing_640x480p_60hz
|
|
|
|
#elif defined(MODE_720x480_60Hz)
|
|
// DVDD 1.2V
|
|
#define FRAME_WIDTH 720
|
|
#define FRAME_HEIGHT 480
|
|
#define VREG_VSEL VREG_VOLTAGE_1_20
|
|
#define DVI_TIMING dvi_timing_720x480p_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
|
|
|
|
#define LED_PIN 16
|
|
|
|
struct dvi_inst dvi0;
|
|
struct semaphore dvi_start_sem;
|
|
|
|
#define CHAR_COLS (FRAME_WIDTH / FONT_CHAR_WIDTH)
|
|
#define CHAR_ROWS (FRAME_HEIGHT / FONT_CHAR_HEIGHT)
|
|
char charbuf[CHAR_ROWS * CHAR_COLS];
|
|
|
|
static inline void prepare_scanline(const char *chars, uint y) {
|
|
static uint8_t scanbuf[FRAME_WIDTH / 8];
|
|
// First blit font into 1bpp scanline buffer, then encode scanbuf into tmdsbuf
|
|
for (uint i = 0; i < CHAR_COLS; ++i) {
|
|
uint c = chars[i + y / FONT_CHAR_HEIGHT * CHAR_COLS];
|
|
scanbuf[i] = font_8x8[(c - FONT_FIRST_ASCII) + (y % FONT_CHAR_HEIGHT) * FONT_N_CHARS];
|
|
}
|
|
uint32_t *tmdsbuf;
|
|
queue_remove_blocking(&dvi0.q_tmds_free, &tmdsbuf);
|
|
tmds_encode_1bpp((const uint32_t*)scanbuf, tmdsbuf, FRAME_WIDTH);
|
|
queue_add_blocking(&dvi0.q_tmds_valid, &tmdsbuf);
|
|
}
|
|
|
|
void core1_scanline_callback() {
|
|
static uint y = 1;
|
|
prepare_scanline(charbuf, y);
|
|
y = (y + 1) % FRAME_HEIGHT;
|
|
}
|
|
|
|
void __not_in_flash("main") core1_main() {
|
|
dvi_register_irqs_this_core(&dvi0, DMA_IRQ_0);
|
|
sem_acquire_blocking(&dvi_start_sem);
|
|
dvi_start(&dvi0);
|
|
|
|
// The text display is completely IRQ driven (takes up around 30% of cycles @
|
|
// VGA). We could do something useful, or we could just take a nice nap
|
|
while (1)
|
|
__wfi();
|
|
__builtin_unreachable();
|
|
}
|
|
|
|
int __not_in_flash("main") main() {
|
|
vreg_set_voltage(VREG_VSEL);
|
|
sleep_ms(10);
|
|
#ifdef RUN_FROM_CRYSTAL
|
|
set_sys_clock_khz(12000, true);
|
|
#else
|
|
// Run system at TMDS bit clock
|
|
set_sys_clock_khz(DVI_TIMING.bit_clk_khz, true);
|
|
#endif
|
|
|
|
setup_default_uart();
|
|
|
|
gpio_init(LED_PIN);
|
|
gpio_set_dir(LED_PIN, GPIO_OUT);
|
|
|
|
printf("Configuring DVI\n");
|
|
|
|
dvi0.timing = &DVI_TIMING;
|
|
dvi0.ser_cfg = DVI_DEFAULT_SERIAL_CONFIG;
|
|
dvi0.scanline_callback = core1_scanline_callback;
|
|
dvi_init(&dvi0, next_striped_spin_lock_num(), next_striped_spin_lock_num());
|
|
|
|
printf("Prepare first scanline\n");
|
|
for (int i = 0; i < CHAR_ROWS * CHAR_COLS; ++i)
|
|
charbuf[i] = FONT_FIRST_ASCII + i % FONT_N_CHARS;
|
|
prepare_scanline(charbuf, 0);
|
|
|
|
printf("Core 1 start\n");
|
|
sem_init(&dvi_start_sem, 0, 1);
|
|
hw_set_bits(&bus_ctrl_hw->priority, BUSCTRL_BUS_PRIORITY_PROC1_BITS);
|
|
multicore_launch_core1(core1_main);
|
|
|
|
|
|
sem_release(&dvi_start_sem);
|
|
while (1)
|
|
__wfi();
|
|
__builtin_unreachable();
|
|
}
|
|
|