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