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