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 "debug.h" 22 #include "display.h" 23 #include "font.h" 24 #include "image.h" 25 #include "init.h" 26 #include "pic32_c.h" 27 #include "utils.h" 28 #include "viewport.h" 29 30 /* Specific functionality. */ 31 32 #include "devconfig.h" 33 #include "main.h" 34 #include "vga_display.h" 35 36 37 38 /* Define the scroll increment depending on how many DMA channels are used. 39 CPU-driven transfers permit an increment of 1. */ 40 41 #ifdef TRANSFER_CPU 42 #define SCROLL_XSTEP 1 43 #else 44 #define SCROLL_XSTEP LINE_CHANNELS 45 #endif 46 47 48 49 /* Define timers if not indicated in the build configuration. */ 50 51 #ifndef LINE_TIMER 52 #define LINE_TIMER 2 53 #endif 54 55 #ifndef TRANSFER_TIMER 56 #define TRANSFER_TIMER 0 57 #endif 58 59 /* Define different output ports and pins for parallel mode. */ 60 61 #ifndef PARALLEL_MODE 62 #define VGA_OUTPUT PORTB 63 #define OC1_PIN RPA0R 64 #define OC2_PIN RPA1R 65 #define LED_PIN (1 << 3) 66 #else 67 #define VGA_OUTPUT PM_REG(0, PMxDIN) 68 #define OC1_PIN RPB4R 69 #define OC2_PIN RPB5R 70 #define LED_PIN (1 << 2) 71 #endif 72 73 74 75 /* Initialise memory for a multi-frame display. */ 76 77 static Display(banner_config, LINE_LENGTH, 10, 1); 78 static Display(display_config, LINE_LENGTH, LINE_COUNT - 10, FRAME_COUNT); 79 80 81 82 /* Bundled image and font data. */ 83 84 extern image_t screendata; 85 extern image_t sprite; 86 87 static SpriteOverwriting(scr, &screendata, &display_config, FRAME_COUNT, -1); 88 89 extern uint8_t fontchars[]; 90 extern font_range_t fontbase; 91 92 static Font(font_config, fontchars, &fontbase); 93 94 95 96 /* Plot the revealed region at the edge of the screen after scrolling. */ 97 98 static void plot_screen_edge(int xorigin, int yorigin, int xstep, int ystep) 99 { 100 image_update_scrolled_tiled_image(&scr, xorigin, yorigin, xstep, ystep); 101 } 102 103 104 105 /* Update the sprite position. */ 106 107 static void update_sprite_position(sprite_t *s, int *xdir, int *ydir) 108 { 109 position_t *last = image_get_stored_position(s, wrap_value(display_config.frame - 1, display_config.frames)); 110 position_t *next = image_get_sprite_position(s); 111 112 if (((*xdir > 0) && (last->x < display_config.line_length - s->image->width - *xdir)) || 113 ((*xdir < 0) && (last->x > -*xdir))) 114 { 115 next->x = last->x + *xdir; 116 } 117 else 118 { 119 next->x = last->x; 120 if (*xdir) 121 *xdir = -*xdir; 122 } 123 124 if (((*ydir > 0) && (last->y < display_config.line_count - s->image->height - *ydir)) || 125 ((*ydir < 0) && (last->y > -*ydir))) 126 { 127 next->y = last->y + *ydir; 128 } 129 else 130 { 131 next->y = last->y; 132 if (*ydir) 133 *ydir = -*ydir; 134 } 135 } 136 137 /* Move a sprite around on the framebuffer. */ 138 139 static void animate(uint32_t delay) 140 { 141 /* Stores of background details, replotted when moving the sprite. */ 142 143 Sprite(s1, &sprite, &display_config, FRAME_COUNT, 0x0c); 144 Sprite(s2, &sprite, &display_config, FRAME_COUNT, 0x0c); 145 Sprite(s3, &sprite, &display_config, FRAME_COUNT, 0x0c); 146 147 /* Scrolling viewport. */ 148 149 Viewport(v, &display_config, SCROLL_XSTEP, 1, plot_screen_edge); 150 151 /* Reset the viewport to a well-defined state. */ 152 153 viewport_set_origin(&v, 0, 0); 154 155 /* Sprite and scrolling directions. */ 156 157 int xdir[] = {1, -1, 0}, ydir[] = {1, 0, -1}; 158 159 /* Sprite positions. */ 160 161 image_set_sprite_position(&s1, 0, 0); 162 image_set_sprite_position(&s2, display_config.line_length - s2.image->width, 0); 163 image_set_sprite_position(&s3, 0, display_config.line_count - s3.image->height); 164 165 /* Animation loop. */ 166 167 while (1) 168 { 169 image_plot_sprite(&s1); 170 image_plot_sprite(&s2); 171 image_plot_sprite(&s3); 172 173 /* Update the display with the frame details. */ 174 175 vga_set_frame(&display_config, 1); 176 wait(delay); 177 178 /* Select the next frame to plot to. */ 179 180 display_select_next_frame(&display_config); 181 182 /* Prepare the frame for updates. */ 183 184 viewport_unplot_sprite_from_image(&v, &s1, &scr); 185 viewport_unplot_sprite_from_image(&v, &s2, &scr); 186 viewport_unplot_sprite_from_image(&v, &s3, &scr); 187 188 /* Update the origin to scroll the screen. */ 189 190 /* Due to the effect of a simple screen start increment in the 191 dual channel configuration, horizontal scrolling involves two 192 pixel increments and thus requires a two-pixel column to be 193 plotted per scrolling increment. */ 194 195 /* For vertically-scaled backgrounds, the full resolution image 196 is traversed by multiples of the scrolling increment. */ 197 198 viewport_update_origin(&v, xdir[0] * SCROLL_XSTEP, 199 ydir[0]); 200 201 /* Update the position of the sprite being plotted. */ 202 203 update_sprite_position(&s1, &xdir[0], &ydir[0]); 204 update_sprite_position(&s2, &xdir[1], &ydir[1]); 205 update_sprite_position(&s3, &xdir[2], &ydir[2]); 206 } 207 } 208 209 /* Set up a banner. */ 210 211 static void setup_banner(void) 212 { 213 int frame, x = 0, y = 0; 214 215 for (frame = 0; frame < banner_config.frames; frame++) 216 { 217 /* Obtain the frame. */ 218 219 display_select_frame(&banner_config, frame); 220 221 write_string(&banner_config, &font_config, "VGA display example", &x, &y, 0xff); 222 } 223 224 display_select_frame(&banner_config, 0); 225 } 226 227 /* Set up a background. */ 228 229 static void setup_background(void) 230 { 231 int frame; 232 233 for (frame = 0; frame < display_config.frames; frame++) 234 { 235 /* Obtain the frame. */ 236 237 display_select_frame(&display_config, frame); 238 239 /* Tile the image on the screen. */ 240 241 image_tile_sprite(&scr, 0, 0, display_config.line_length, 242 display_config.line_count, 243 0, 0); 244 } 245 246 display_select_frame(&display_config, 0); 247 } 248 249 static void setup(void) 250 { 251 setup_banner(); 252 setup_background(); 253 } 254 255 256 257 /* Main program. */ 258 259 void main(void) 260 { 261 init_memory(); 262 init_pins(); 263 init_outputs(); 264 265 unlock_config(); 266 config_oc(); 267 config_uart(); 268 lock_config(); 269 270 #ifndef TRANSFER_CPU 271 init_dma(); 272 #endif 273 274 #ifdef PARALLEL_MODE 275 init_pm(); 276 277 /* Configure parallel master mode. */ 278 279 pm_init(0, 0b10); 280 pm_set_output(0, 1, 0); 281 pm_on(0); 282 #endif 283 284 uart_init(1, FPB, 115200); 285 uart_on(1); 286 287 /* Set the display to a well-defined state. */ 288 289 init_display(&banner_config); 290 init_display(&display_config); 291 292 /* Initialise VGA output with one or two line channels, configuring a line 293 timer and any transfer timer, with an initiating channel being introduced 294 if a transfer timer is specified. */ 295 296 init_vga_with_timers(&banner_config, LINE_CHANNELS, LINE_TIMER, TRANSFER_TIMER); 297 vga_add_window(&display_config); 298 299 /* Configure VGA output transfer to the output register, also configuring 300 output compare units for horizontal and vertical sync. */ 301 302 vga_configure_transfer(VGA_OUTPUT); 303 vga_configure_sync(1, 2); 304 305 interrupts_on(); 306 307 /* Move a sprite around on the screen with a delay between each movement. */ 308 309 setup(); 310 animate(1); 311 } 312 313 314 315 /* Exception and interrupt handlers. */ 316 317 void exception_handler(void) 318 { 319 blink(1 << 22, PORTA, LED_PIN); 320 } 321 322 void interrupt_handler(void) 323 { 324 vga_interrupt_handler(); 325 } 326 327 328 329 /* Peripheral pin configuration. */ 330 331 void config_oc(void) 332 { 333 /* Map OC1. */ 334 335 REG(OC1_PIN) = 0b0101; 336 337 /* Map OC2. */ 338 339 REG(OC2_PIN) = 0b0101; 340 } 341 342 void config_uart(void) 343 { 344 /* Map U1RX to RPB13. */ 345 346 REG(U1RXR) = 0b0011; /* U1RXR<3:0> = 0011 (RPB13) */ 347 348 /* Map U1TX to RPB15. */ 349 350 REG(RPB15R) = 0b0001; /* RPB15R<3:0> = 0001 (U1TX) */ 351 352 /* Set RPB13 to input. */ 353 354 SET_REG(TRISB, 1 << 13); 355 }