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 21 #include "pic32_c.h" 22 #include "init.h" 23 #include "debug.h" 24 25 /* Specific functionality. */ 26 27 #include "devconfig.h" 28 #include "display.h" 29 #include "display_config.h" 30 #include "font.h" 31 #include "main.h" 32 #include "vga.h" 33 #include "vga_display.h" 34 35 36 37 /* Define timers if not indicated in the build configuration. */ 38 39 #ifndef LINE_CHANNELS 40 #define LINE_CHANNELS 1 41 #endif 42 43 #ifndef LINE_TIMER 44 #define LINE_TIMER 2 45 #endif 46 47 #ifndef TRANSFER_TIMER 48 #define TRANSFER_TIMER 0 49 #endif 50 51 /* Define different output ports and pins for parallel mode. */ 52 53 #ifndef PARALLEL_MODE 54 #define VGA_OUTPUT PORTB 55 #define OC1_PIN RPA0R 56 #define OC2_PIN RPA1R 57 #define LED_PIN (1 << 3) 58 #else 59 #define VGA_OUTPUT PM_REG(0, PMxDIN) 60 #define OC1_PIN RPB4R 61 #define OC2_PIN RPB5R 62 #define LED_PIN (1 << 2) 63 #endif 64 65 66 67 /* Bundled image and font data. */ 68 69 extern uint8_t screendata[]; 70 extern uint32_t screendata_width, screendata_height; 71 extern uint8_t sprite[]; 72 extern uint32_t sprite_width, sprite_height; 73 74 extern uint8_t fontchars[]; 75 extern uint32_t fonttable[]; 76 extern uint32_t fontbase, fontlimit; 77 78 static font_config_t font_config; 79 80 81 82 /* Busy wait. */ 83 84 static void wait(uint32_t delay) 85 { 86 uint32_t counter = delay; 87 88 if (!delay) return; 89 while (counter--) __asm__(""); /* retain loop */ 90 } 91 92 /* Blink an attached LED with delays implemented using a loop. */ 93 94 static void blink(uint32_t delay, uint32_t port, uint32_t pins) 95 { 96 /* Clear outputs (LED). */ 97 98 CLR_REG(port, pins); 99 100 while (1) 101 { 102 wait(delay); 103 104 /* Invert outputs (LED). */ 105 106 INV_REG(port, pins); 107 } 108 } 109 110 /* Move a sprite around on the framebuffer. */ 111 112 static void animate(uint32_t delay) 113 { 114 uint8_t background[sprite_width * sprite_height]; 115 int x, y; 116 int dir[] = {1, 0, -1, 0, 1}, i = 0, width, column_width; 117 int xsource, xdisplay, xorigin = 0; 118 119 while (1) 120 { 121 for (y = 0; y < screendata_height - sprite_height; y++) 122 { 123 for (x = 0; x < screendata_width - sprite_width; x++) 124 { 125 /* Copy to the store from the display, then blit the image. */ 126 127 copy_display(&display_config, background, 128 sprite_width, sprite_height, 129 x, y, -1, 0); 130 copy_display(&display_config, sprite, 131 sprite_width, sprite_height, 132 x, y, 0x8c, 1); 133 134 wait(delay); 135 136 /* Copy to the display from the store, restoring the original 137 background. */ 138 139 copy_display(&display_config, background, 140 sprite_width, sprite_height, 141 x, y, -1, 1); 142 143 /* Scroll in the indicated direction. */ 144 145 scroll_display(&display_config, dir[i], dir[i + 1]); 146 147 /* For horizontal scrolling, plot the exposed column at the left 148 (if scrolling left) or at the right (if scrolling right). */ 149 150 if (dir[i]) 151 { 152 /* Due to the effect of a simple screen start increment in 153 the dual channel configuration, horizontal scrolling 154 involves two pixel increments and thus requires a two- 155 pixel column to be plotted. */ 156 157 width = dir[i] * LINE_CHANNELS; 158 column_width = width < 0 ? -width : width; 159 160 /* Plot either at the left or right edge. */ 161 162 xdisplay = width < 0 ? 0 : screendata_width - width; 163 164 /* Determine the location of the column to be plotted. */ 165 166 xorigin += width; 167 xsource = (xdisplay + xorigin) % screendata_width; 168 169 copy_display_section(&display_config, screendata, 170 screendata_width, screendata_height, 171 xsource, 0, 172 column_width, screendata_height, 173 xdisplay, 0, 174 -1, 1); 175 } 176 } 177 178 /* Switch direction periodically. */ 179 180 i++; 181 if (i == 4) 182 i = 0; 183 } 184 } 185 } 186 187 /* Fill the screen with characters. */ 188 189 static void write_chars(void) 190 { 191 int x = 0, y = 0; 192 char c; 193 194 font_config.chars = (char_definition_t *) fontchars; 195 font_config.table = fonttable; 196 font_config.base = fontbase; 197 font_config.limit = fontlimit; 198 199 while (y < display_config.line_count) 200 for (c = (char) font_config.base; c < (char) font_config.limit; c++) 201 { 202 x = write_char(&display_config, &font_config, c, x, y, 0xff); 203 204 if (x > display_config.line_length) 205 { 206 x = 0; y += 9; 207 } 208 } 209 } 210 211 212 213 /* Main program. */ 214 215 void main(void) 216 { 217 init_memory(); 218 init_pins(); 219 init_outputs(); 220 221 unlock_config(); 222 config_oc(); 223 config_uart(); 224 lock_config(); 225 226 init_dma(); 227 228 #ifdef PARALLEL_MODE 229 init_pm(); 230 231 /* Configure parallel master mode. */ 232 233 pm_init(0, 0b10); 234 pm_set_output(0, 1, 0); 235 pm_on(0); 236 #endif 237 238 /* Initialise VGA output with one or two line channels, configuring a line 239 timer and any transfer timer, with an initiating channel being introduced 240 if a transfer timer is specified. */ 241 242 init_vga_with_timers(&display_config, LINE_CHANNELS, LINE_TIMER, TRANSFER_TIMER); 243 244 /* Configure VGA output transfer to the output register, also configuring 245 output compare units for horizontal and vertical sync. */ 246 247 vga_configure_transfer(VGA_OUTPUT); 248 vga_configure_sync(1, 2); 249 250 uart_init(1, FPB, 115200); 251 uart_on(1); 252 253 interrupts_on(); 254 255 /* Plot the image centred on the screen. */ 256 257 copy_display(&display_config, screendata, screendata_width, screendata_height, 258 (LINE_LENGTH - screendata_width) / 2, 259 (LINE_COUNT - screendata_height) / 2, -1, 1); 260 261 /* Write a sequence of characters. */ 262 263 write_chars(); 264 265 /* Move a sprite around on the screen with a delay between each movement. */ 266 267 animate(1 << 21); 268 } 269 270 271 272 /* Exception and interrupt handlers. */ 273 274 void exception_handler(void) 275 { 276 blink(1 << 22, PORTA, LED_PIN); 277 } 278 279 void interrupt_handler(void) 280 { 281 uint32_t ifs; 282 283 /* Check for a OC1 interrupt condition. */ 284 285 ifs = REG(OCIFS) & OC_INT_FLAGS(1, OCxIF); 286 287 if (ifs) 288 { 289 vga_interrupt_handler(); 290 CLR_REG(OCIFS, ifs); 291 } 292 } 293 294 295 296 /* Peripheral pin configuration. */ 297 298 void config_oc(void) 299 { 300 /* Map OC1. */ 301 302 REG(OC1_PIN) = 0b0101; 303 304 /* Map OC2. */ 305 306 REG(OC2_PIN) = 0b0101; 307 } 308 309 void config_uart(void) 310 { 311 /* Map U1RX to RPB13. */ 312 313 REG(U1RXR) = 0b0011; /* U1RXR<3:0> = 0011 (RPB13) */ 314 315 /* Map U1TX to RPB15. */ 316 317 REG(RPB15R) = 0b0001; /* RPB15R<3:0> = 0001 (U1TX) */ 318 319 /* Set RPB13 to input. */ 320 321 SET_REG(TRISB, 1 << 13); 322 }