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 init_pm(); 86 87 /* Configure parallel master mode. */ 88 89 pm_init(0, 0b10); 90 pm_set_output(0, 1, 0); 91 pm_on(0); 92 93 /* Initiate DMA on the Timer2 interrupt transferring line data to the first 94 byte of PORTB. Do not enable the channel for initiation until the visible 95 region is about to start. */ 96 97 dma_init(0, 3); 98 dma_set_auto_enable(0, 1); 99 dma_set_interrupt(0, T2, 1); 100 dma_set_transfer(0, PHYSICAL((uint32_t) display_config.screen_start), 101 display_config.line_length, 102 HW_PHYSICAL(PM_REG(0, PMxDIN)), 1, 103 TRANSFER_CELL_SIZE); 104 105 /* Enable DMA on the preceding channel's completion, with the timer event 106 initiating the transfer. This "reset" or "zero" transfer is employed to 107 set the pixel level to black in a connected flip-flop. Without the 108 flip-flop it is superfluous. */ 109 110 dma_init(1, 3); 111 dma_set_chaining(1, dma_chain_previous); 112 dma_set_interrupt(1, T2, 1); 113 dma_set_transfer(1, PHYSICAL((uint32_t) zerodata), ZERO_LENGTH, 114 HW_PHYSICAL(PM_REG(0, PMxDIN)), 1, 115 ZERO_LENGTH); 116 dma_set_receive_events(1, 1); 117 118 /* Configure a timer and output compare units for horizontal and vertical 119 sync. */ 120 121 vga_configure_sync(1, 2, 2); 122 123 uart_init(1, FPB, 115200); 124 uart_on(1); 125 126 interrupts_on(); 127 128 blink(3 << 24, PORTA, 1 << 2); 129 } 130 131 132 133 /* Exception and interrupt handlers. */ 134 135 void exception_handler(void) 136 { 137 blink(3 << 12, PORTA, 1 << 2); 138 } 139 140 void interrupt_handler(void) 141 { 142 uint32_t ifs; 143 144 /* Check for a OC1 interrupt condition. */ 145 146 ifs = REG(OCIFS) & OC_INT_FLAGS(1, OCxIF); 147 148 if (ifs) 149 { 150 vga_interrupt_handler(); 151 CLR_REG(OCIFS, ifs); 152 } 153 } 154 155 156 157 /* Enable the channel for the next line. */ 158 159 void start_visible(vga_display_t *vga_display) 160 { 161 dma_set_source(0, PHYSICAL((uint32_t) vga_display->linedata), 162 display_config.line_length); 163 dma_on(0); 164 } 165 166 /* Update the channel for the next line. */ 167 168 void update_visible(vga_display_t *vga_display) 169 { 170 dma_set_source(0, PHYSICAL((uint32_t) vga_display->linedata), 171 display_config.line_length); 172 } 173 174 /* Disable the channel for the next line. */ 175 176 void stop_visible(vga_display_t *vga_display) 177 { 178 dma_off(0); 179 } 180 181 /* Bring vsync low (single compare, output driven low) when the next line 182 starts. */ 183 184 void vsync_low(void) 185 { 186 oc_init(2, 0b010, 2); 187 oc_on(2); 188 } 189 190 /* Bring vsync high (single compare, output driven high) when the next line 191 starts. */ 192 193 void vsync_high(void) 194 { 195 oc_init(2, 0b001, 2); 196 oc_on(2); 197 } 198 199 200 201 /* Peripheral pin configuration. */ 202 203 void config_oc(void) 204 { 205 /* Map OC1 to RPB4. */ 206 207 REG(RPB4R) = 0b0101; /* RPB4R<3:0> = 0101 (OC1) */ 208 209 /* Map OC2 to RPB5. */ 210 211 REG(RPB5R) = 0b0101; /* RPB5R<3:0> = 0101 (OC2) */ 212 } 213 214 void config_uart(void) 215 { 216 /* Map U1RX to RPB13. */ 217 218 REG(U1RXR) = 0b0011; /* U1RXR<3:0> = 0011 (RPB13) */ 219 220 /* Map U1TX to RPB15. */ 221 222 REG(RPB15R) = 0b0001; /* RPB15R<3:0> = 0001 (U1TX) */ 223 224 /* Set RPB13 to input. */ 225 226 SET_REG(TRISB, 1 << 13); 227 }