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 72 test_linedata(&display_config); 73 74 init_memory(); 75 init_pins(); 76 init_outputs(); 77 78 unlock_config(); 79 config_oc(); 80 config_uart(); 81 lock_config(); 82 83 init_dma(); 84 85 /* Peripheral relationships: 86 87 Timer2 -> OC1 88 -> OC2 (vertical sync region) 89 -> DMA1: zerodata -> PORTB (visible region) 90 | 91 Timer3 -> DMA0: linedata -> PORTB 92 Timer3 -> DMA2: linedata -> PORTB 93 | 94 Timer3 -> DMA3: zerodata -> PORTB 95 */ 96 97 /* Initiate DMA on the Timer2 interrupt condition, transferring line data to 98 the first byte of PORTB. Do not enable the channel for initiation until 99 the visible region is about to start. */ 100 101 dma_init(1, 3); 102 dma_set_auto_enable(1, 1); 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 108 /* Enable DMA on the zero channel's completion, with the Timer3 109 interrupt condition initiating transfers. */ 110 111 dma_init(0, 2); 112 dma_set_chaining(0, dma_chain_next); 113 dma_set_interrupt(0, T3, 1); 114 dma_set_transfer(0, PHYSICAL((uint32_t) display_config.screen_start), 115 display_config.line_length / 2, 116 HW_PHYSICAL(PORTB), 1, 117 TRANSFER_CELL_SIZE); 118 119 /* Enable DMA on the zero channel's completion, with the Timer3 120 interrupt condition initiating transfers. */ 121 122 dma_init(2, 2); 123 dma_set_chaining(2, dma_chain_previous); 124 dma_set_interrupt(2, T3, 1); 125 dma_set_transfer(2, PHYSICAL((uint32_t) display_config.screen_start + 126 display_config.line_length / 2), 127 display_config.line_length / 2, 128 HW_PHYSICAL(PORTB), 1, 129 TRANSFER_CELL_SIZE); 130 131 /* Enable DMA on the preceding channel's completion, with Timer3 initiating 132 transfers. */ 133 134 dma_init(3, 3); 135 dma_set_chaining(3, dma_chain_previous); 136 dma_set_interrupt(3, T3, 1); 137 dma_set_transfer(3, PHYSICAL((uint32_t) zerodata), ZERO_LENGTH, 138 HW_PHYSICAL(PORTB), 1, 139 ZERO_LENGTH); 140 dma_set_receive_events(3, 1); 141 142 /* Configure a timer for line data transfers. */ 143 144 timer_init(3, 0, 1); 145 timer_on(3); 146 147 /* Configure a timer and output compare units for horizontal and vertical 148 sync. */ 149 150 vga_configure_sync(1, 2, 2); 151 152 uart_init(1, FPB, 115200); 153 uart_on(1); 154 155 interrupts_on(); 156 157 blink(3 << 24, PORTA, 1 << 3); 158 } 159 160 161 162 /* Exception and interrupt handlers. */ 163 164 void exception_handler(void) 165 { 166 blink(3 << 12, PORTA, 1 << 3); 167 } 168 169 void interrupt_handler(void) 170 { 171 uint32_t ifs; 172 173 /* Check for a OC1 interrupt condition. */ 174 175 ifs = REG(OCIFS) & OC_INT_FLAGS(1, OCxIF); 176 177 if (ifs) 178 { 179 vga_interrupt_handler(); 180 CLR_REG(OCIFS, ifs); 181 } 182 } 183 184 185 186 /* Enable the channels for the next line. */ 187 188 void start_visible(vga_display_t *vga_display) 189 { 190 dma_set_source(0, PHYSICAL((uint32_t) vga_display->linedata), 191 display_config.line_length / 2); 192 dma_set_source(2, PHYSICAL((uint32_t) vga_display->linedata + 193 display_config.line_length / 2), 194 display_config.line_length / 2); 195 dma_on(1); 196 } 197 198 /* Update the channels for the next line. */ 199 200 void update_visible(vga_display_t *vga_display) 201 { 202 dma_set_source(0, PHYSICAL((uint32_t) vga_display->linedata), 203 display_config.line_length / 2); 204 dma_set_source(2, PHYSICAL((uint32_t) vga_display->linedata + 205 display_config.line_length / 2), 206 display_config.line_length / 2); 207 } 208 209 /* Disable the channels for the next line. */ 210 211 void stop_visible(vga_display_t *vga_display) 212 { 213 dma_off(1); 214 } 215 216 217 218 /* Peripheral pin configuration. */ 219 220 void config_oc(void) 221 { 222 /* Map OC1 to RPA0. */ 223 224 REG(RPA0R) = 0b0101; /* RPA0R<3:0> = 0101 (OC1) */ 225 226 /* Map OC2 to RPA1. */ 227 228 REG(RPA1R) = 0b0101; /* RPA1R<3:0> = 0101 (OC2) */ 229 } 230 231 void config_uart(void) 232 { 233 /* Map U1RX to RPB13. */ 234 235 REG(U1RXR) = 0b0011; /* U1RXR<3:0> = 0011 (RPB13) */ 236 237 /* Map U1TX to RPB15. */ 238 239 REG(RPB15R) = 0b0001; /* RPB15R<3:0> = 0001 (U1TX) */ 240 241 /* Set RPB13 to input. */ 242 243 SET_REG(TRISB, 1 << 13); 244 }