1 /* 2 * Generate a VGA signal using a PIC32 microcontroller. 3 * 4 * Copyright (C) 2017, 2018 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "pic32_c.h" 21 #include "vga_display.h" 22 23 24 25 /* Display state. */ 26 27 vga_display_t vga_display; 28 29 30 31 /* Initialise the state machine. */ 32 33 void init_vga(display_config_t *display_config, 34 void (*start_visible)(), 35 void (*update_visible)(), 36 void (*stop_visible)(), 37 void (*vsync_high)(), 38 void (*vsync_low)()) 39 { 40 /* Display parameters. */ 41 42 vga_display.display_config = display_config; 43 44 /* Display state handlers. */ 45 46 vga_display.start_visible = start_visible; 47 vga_display.update_visible = update_visible; 48 vga_display.stop_visible = stop_visible; 49 50 /* Vertical sync operations. */ 51 52 vga_display.vsync_high = vsync_high; 53 vga_display.vsync_low = vsync_low; 54 55 /* Initial state. */ 56 57 vga_display.state_handler = vbp_active; 58 vga_display.line = 0; 59 } 60 61 62 63 /* Interrupt handlers. */ 64 65 void vga_interrupt_handler(void) 66 { 67 vga_display.line += 1; 68 vga_display.state_handler(); 69 } 70 71 72 73 /* Vertical back porch region. */ 74 75 void vbp_active(void) 76 { 77 if (vga_display.line < vga_display.display_config->visible_start) 78 return; 79 80 /* Enter the visible region. */ 81 82 vga_display.state_handler = visible_active; 83 84 /* Set the line address. */ 85 86 vga_display.linedata = vga_display.display_config->screen_start; 87 vga_display.start_visible(&vga_display); 88 } 89 90 /* Visible region. */ 91 92 void visible_active(void) 93 { 94 if (vga_display.line < vga_display.display_config->vfp_start) 95 { 96 /* Update the line address and handle wraparound. */ 97 98 if (!(vga_display.line % vga_display.display_config->line_multiplier)) 99 { 100 vga_display.linedata += vga_display.display_config->line_length; 101 102 if (vga_display.linedata >= vga_display.display_config->screen_limit) 103 vga_display.linedata -= vga_display.display_config->screen_size; 104 } 105 106 vga_display.update_visible(&vga_display); 107 return; 108 } 109 110 /* End the visible region. */ 111 112 vga_display.state_handler = vfp_active; 113 114 /* Disable the channel for the next line. */ 115 116 vga_display.stop_visible(&vga_display); 117 } 118 119 /* Vertical front porch region. */ 120 121 void vfp_active(void) 122 { 123 if (vga_display.line < vga_display.display_config->vsync_start) 124 return; 125 126 /* Enter the vertical sync region. */ 127 128 vga_display.state_handler = vsync_active; 129 130 /* Bring vsync low when the next line starts. */ 131 132 vga_display.vsync_low(); 133 } 134 135 /* Vertical sync region. */ 136 137 void vsync_active(void) 138 { 139 if (vga_display.line < vga_display.display_config->vsync_end) 140 return; 141 142 /* Start again at the top of the display. */ 143 144 vga_display.line = 0; 145 vga_display.state_handler = vbp_active; 146 147 /* Bring vsync high when the next line starts. */ 148 149 vga_display.vsync_high(); 150 }