1.1 --- a/lib/vga_display_cpu.c Fri Nov 02 02:15:09 2018 +0100
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,242 +0,0 @@
1.4 -/*
1.5 - * Generate a VGA signal using a PIC32 microcontroller.
1.6 - *
1.7 - * Copyright (C) 2017, 2018 Paul Boddie <paul@boddie.org.uk>
1.8 - *
1.9 - * This program is free software: you can redistribute it and/or modify
1.10 - * it under the terms of the GNU General Public License as published by
1.11 - * the Free Software Foundation, either version 3 of the License, or
1.12 - * (at your option) any later version.
1.13 - *
1.14 - * This program is distributed in the hope that it will be useful,
1.15 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 - * GNU General Public License for more details.
1.18 - *
1.19 - * You should have received a copy of the GNU General Public License
1.20 - * along with this program. If not, see <http://www.gnu.org/licenses/>.
1.21 - */
1.22 -
1.23 -#include "pic32_c.h"
1.24 -#include "init.h"
1.25 -#include "vga_display_cpu.h"
1.26 -
1.27 -
1.28 -
1.29 -/* Display state. */
1.30 -
1.31 -vga_display_t vga_display;
1.32 -
1.33 -
1.34 -
1.35 -/* Initialise the state machine. */
1.36 -
1.37 -void init_vga(display_config_t *display_config, int line_timer)
1.38 -{
1.39 - /* Display parameters. */
1.40 -
1.41 - vga_display.display_config = display_config;
1.42 -
1.43 - /* Initial state. */
1.44 -
1.45 - vga_display.state_handler = vbp_active;
1.46 - vga_display.line = 0;
1.47 -
1.48 - /* Configure a general display timer to start line data transfer and for the
1.49 - horizontal sync. */
1.50 -
1.51 - vga_display.line_timer = line_timer;
1.52 -}
1.53 -
1.54 -/* Initialise a separate transfer timer if different from the general display
1.55 - timer. */
1.56 -
1.57 -void init_vga_with_timer(display_config_t *display_config, int line_timer)
1.58 -{
1.59 - /* Initialise the basic properties of the display. */
1.60 -
1.61 - init_vga(display_config, line_timer);
1.62 -
1.63 - /* Configure a line timer for horizontal sync and line data transfers. The
1.64 - interrupt is handled to produce line data and to drive the display state
1.65 - machine. */
1.66 -
1.67 - /* The timer has no prescaling (0). */
1.68 -
1.69 - timer_init(line_timer, 0, display_config->hfreq_limit);
1.70 - timer_init_interrupt(line_timer, 7, 3);
1.71 - timer_on(line_timer);
1.72 -}
1.73 -
1.74 -
1.75 -
1.76 -/* Configure the output port. */
1.77 -
1.78 -void vga_configure_transfer(uint32_t output)
1.79 -{
1.80 - vga_display.output = output;
1.81 -}
1.82 -
1.83 -/* Configure output compare units for horizontal and vertical sync. */
1.84 -
1.85 -void vga_configure_sync(int hsync_unit, int vsync_unit)
1.86 -{
1.87 - /* Record the peripherals in use. */
1.88 -
1.89 - vga_display.hsync_unit = hsync_unit;
1.90 - vga_display.vsync_unit = vsync_unit;
1.91 -
1.92 - /* Horizontal sync. */
1.93 -
1.94 - /* Configure output compare in dual compare (continuous output) mode using
1.95 - the timer as time base. */
1.96 -
1.97 - oc_init(hsync_unit, 0b101, vga_display.line_timer);
1.98 - oc_set_pulse(hsync_unit, vga_display.display_config->hsync_end);
1.99 - oc_set_pulse_end(hsync_unit, vga_display.display_config->hsync_start);
1.100 - oc_init_interrupt(hsync_unit, 7, 3);
1.101 - oc_on(hsync_unit);
1.102 -
1.103 - /* Vertical sync. */
1.104 -
1.105 - /* Configure output compare in single compare (output driven low) mode using
1.106 - the timer as time base. The unit is enabled later. It is only really used
1.107 - to achieve precisely-timed level transitions in hardware. */
1.108 -
1.109 - oc_init(vsync_unit, 0b010, vga_display.line_timer);
1.110 - oc_set_pulse(vsync_unit, 0);
1.111 -}
1.112 -
1.113 -
1.114 -
1.115 -/* Interrupt handlers. */
1.116 -
1.117 -void vga_interrupt_handler(void)
1.118 -{
1.119 - vga_display.line += 1;
1.120 - vga_display.state_handler();
1.121 -}
1.122 -
1.123 -/* Visible region pixel output handler. */
1.124 -
1.125 -void vga_transfer_interrupt_handler(void)
1.126 -{
1.127 - display_config_t *cfg = vga_display.display_config;
1.128 - uint8_t *current, *end, *output;
1.129 -
1.130 - if (vga_display.state_handler != visible_active)
1.131 - return;
1.132 -
1.133 - /* Generate the pixel signal. */
1.134 -
1.135 - output = (uint8_t *) vga_display.output;
1.136 - end = vga_display.linedata + cfg->line_length;
1.137 -
1.138 - /* This is potentially not as efficient as loading words and shifting bytes
1.139 - but it appears difficult to implement that approach without experiencing
1.140 - data load exceptions. */
1.141 -
1.142 - for (current = vga_display.linedata; current < end; current++)
1.143 - REG(output) = *current;
1.144 -
1.145 - /* Reset the signal level. */
1.146 -
1.147 - REG(output) = 0;
1.148 -}
1.149 -
1.150 -
1.151 -
1.152 -/* Vertical back porch region. */
1.153 -
1.154 -void vbp_active(void)
1.155 -{
1.156 - if (vga_display.line < vga_display.display_config->visible_start)
1.157 - return;
1.158 -
1.159 - /* Enter the visible region. */
1.160 -
1.161 - vga_display.state_handler = visible_active;
1.162 -
1.163 - /* Set the line address. */
1.164 -
1.165 - vga_display.linedata = vga_display.display_config->screen_start;
1.166 -}
1.167 -
1.168 -/* Visible region. */
1.169 -
1.170 -void visible_active(void)
1.171 -{
1.172 - display_config_t *cfg = vga_display.display_config;
1.173 -
1.174 - if (vga_display.line < cfg->vfp_start)
1.175 - {
1.176 - /* Update the line address and handle wraparound. */
1.177 -
1.178 - if (!(vga_display.line % cfg->line_multiplier))
1.179 - {
1.180 - vga_display.linedata += cfg->line_length;
1.181 -
1.182 - if (vga_display.linedata >= cfg->screen_limit)
1.183 - vga_display.linedata -= cfg->screen_size;
1.184 - }
1.185 -
1.186 - return;
1.187 - }
1.188 -
1.189 - /* End the visible region. */
1.190 -
1.191 - vga_display.state_handler = vfp_active;
1.192 -}
1.193 -
1.194 -/* Vertical front porch region. */
1.195 -
1.196 -void vfp_active(void)
1.197 -{
1.198 - if (vga_display.line < vga_display.display_config->vsync_start)
1.199 - return;
1.200 -
1.201 - /* Enter the vertical sync region. */
1.202 -
1.203 - vga_display.state_handler = vsync_active;
1.204 -
1.205 - /* Bring vsync low when the next line starts. */
1.206 -
1.207 - vsync_low();
1.208 -}
1.209 -
1.210 -/* Vertical sync region. */
1.211 -
1.212 -void vsync_active(void)
1.213 -{
1.214 - if (vga_display.line < vga_display.display_config->vsync_end)
1.215 - return;
1.216 -
1.217 - /* Start again at the top of the display. */
1.218 -
1.219 - vga_display.line = 0;
1.220 - vga_display.state_handler = vbp_active;
1.221 -
1.222 - /* Bring vsync high when the next line starts. */
1.223 -
1.224 - vsync_high();
1.225 -}
1.226 -
1.227 -
1.228 -
1.229 -/* Bring vsync low (single compare, output driven low) when the next line
1.230 - starts. */
1.231 -
1.232 -void vsync_low(void)
1.233 -{
1.234 - oc_init(vga_display.vsync_unit, 0b010, vga_display.line_timer);
1.235 - oc_on(vga_display.vsync_unit);
1.236 -}
1.237 -
1.238 -/* Bring vsync high (single compare, output driven high) when the next line
1.239 - starts. */
1.240 -
1.241 -void vsync_high(void)
1.242 -{
1.243 - oc_init(vga_display.vsync_unit, 0b001, vga_display.line_timer);
1.244 - oc_on(vga_display.vsync_unit);
1.245 -}