PicoDVI/software/apps/terminal/main.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();
}