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