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