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 "main.h" 28 #include "devconfig.h" 29 #include "vga.h" 30 #include "display.h" 31 #include "display_config.h" 32 #include "vga_display.h" 33 34 35 36 /* Pixel data. */ 37 38 static const uint8_t zerodata[ZERO_LENGTH] = {0}; 39 40 41 42 /* Blink an attached LED with delays implemented using a loop. */ 43 44 static void blink(uint32_t delay, uint32_t port, uint32_t pins) 45 { 46 uint32_t counter; 47 48 /* Clear outputs (LED). */ 49 50 CLR_REG(port, pins); 51 52 while (1) 53 { 54 counter = delay; 55 56 while (counter--) __asm__(""); /* retain loop */ 57 58 /* Invert outputs (LED). */ 59 60 INV_REG(port, pins); 61 } 62 } 63 64 65 66 /* Main program. */ 67 68 void main(void) 69 { 70 init_vga(&display_config, start_visible, update_visible, stop_visible, 71 vsync_high, vsync_low); 72 73 test_linedata(&display_config); 74 75 init_memory(); 76 init_pins(); 77 init_outputs(); 78 79 unlock_config(); 80 config_oc(); 81 config_uart(); 82 lock_config(); 83 84 init_dma(); 85 86 /* Initiate DMA on the Timer2 interrupt transferring line data to the first 87 byte of PORTB. Do not enable the channel for initiation until the visible 88 region is about to start. */ 89 90 dma_init(0, 3); 91 dma_set_auto_enable(0, 1); 92 dma_set_interrupt(0, T2, 1); 93 dma_set_transfer(0, PHYSICAL((uint32_t) display_config.screen_start), 94 display_config.line_length, 95 HW_PHYSICAL(PORTB), 1, 96 TRANSFER_CELL_SIZE); 97 98 /* Enable DMA on the preceding channel's completion, with the timer event 99 initiating the transfer. */ 100 101 dma_init(1, 3); 102 dma_set_chaining(1, dma_chain_previous); 103 dma_set_interrupt(1, T2, 1); 104 dma_set_transfer(1, PHYSICAL((uint32_t) zerodata), ZERO_LENGTH, 105 HW_PHYSICAL(PORTB), 1, 106 ZERO_LENGTH); 107 dma_set_receive_events(1, 1); 108 109 /* Configure a timer and output compare units for horizontal and vertical 110 sync. */ 111 112 vga_configure_sync(1, 2, 2); 113 114 uart_init(1, FPB, 115200); 115 uart_on(1); 116 117 interrupts_on(); 118 119 blink(3 << 24, PORTA, 1 << 3); 120 } 121 122 123 124 /* Exception and interrupt handlers. */ 125 126 void exception_handler(void) 127 { 128 blink(3 << 12, PORTA, 1 << 3); 129 } 130 131 void interrupt_handler(void) 132 { 133 uint32_t ifs; 134 135 /* Check for a OC1 interrupt condition. */ 136 137 ifs = REG(OCIFS) & OC_INT_FLAGS(1, OCxIF); 138 139 if (ifs) 140 { 141 vga_interrupt_handler(); 142 CLR_REG(OCIFS, ifs); 143 } 144 } 145 146 147 148 /* Enable the channel for the next line. */ 149 150 void start_visible(vga_display_t *vga_display) 151 { 152 dma_set_source(0, PHYSICAL((uint32_t) vga_display->linedata), 153 display_config.line_length); 154 dma_on(0); 155 } 156 157 /* Update the channel for the next line. */ 158 159 void update_visible(vga_display_t *vga_display) 160 { 161 dma_set_source(0, PHYSICAL((uint32_t) vga_display->linedata), 162 display_config.line_length); 163 } 164 165 /* Disable the channel for the next line. */ 166 167 void stop_visible(vga_display_t *vga_display) 168 { 169 dma_off(0); 170 } 171 172 /* Bring vsync low (single compare, output driven low) when the next line 173 starts. */ 174 175 void vsync_low(void) 176 { 177 oc_init(2, 0b010, 2); 178 oc_on(2); 179 } 180 181 /* Bring vsync high (single compare, output driven high) when the next line 182 starts. */ 183 184 void vsync_high(void) 185 { 186 oc_init(2, 0b001, 2); 187 oc_on(2); 188 } 189 190 191 192 /* Peripheral pin configuration. */ 193 194 void config_oc(void) 195 { 196 /* Map OC1 to RPA0. */ 197 198 REG(RPA0R) = 0b0101; /* RPA0R<3:0> = 0101 (OC1) */ 199 200 /* Map OC2 to RPA1. */ 201 202 REG(RPA1R) = 0b0101; /* RPA1R<3:0> = 0101 (OC2) */ 203 } 204 205 void config_uart(void) 206 { 207 /* Map U1RX to RPB13. */ 208 209 REG(U1RXR) = 0b0011; /* U1RXR<3:0> = 0011 (RPB13) */ 210 211 /* Map U1TX to RPB15. */ 212 213 REG(RPB15R) = 0b0001; /* RPB15R<3:0> = 0001 (U1TX) */ 214 215 /* Set RPB13 to input. */ 216 217 SET_REG(TRISB, 1 << 13); 218 }