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