1 /* 2 * A demonstration of various PIC32 peripherals. 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 #include "main.h" 25 26 static const char message1[] = "Hello!\r\n"; 27 28 #define CELLSIZE4 29 30 #ifdef CELLSIZE1 31 static const char message2[] = "Adoc gi,hlo\r"; 32 static const char message3[] = "n neaan el!\n"; 33 #define CELLSIZE 1 34 #endif 35 36 #ifdef CELLSIZE4 37 static const char message2[] = "And agahell"; 38 static const char message3[] = "oncein, o!\r\n"; 39 #define CELLSIZE 4 40 #endif 41 42 static int uart_echo; 43 44 45 46 /* Blink an attached LED with delays implemented using a loop. */ 47 48 static void blink(uint32_t delay, uint32_t port, uint32_t pins) 49 { 50 uint32_t counter; 51 52 /* Clear outputs (LED). */ 53 54 CLR_REG(port, pins); 55 56 while (1) 57 { 58 counter = delay; 59 60 while (counter--) __asm__(""); /* retain loop */ 61 62 /* Invert outputs (LED). */ 63 64 INV_REG(port, pins); 65 } 66 } 67 68 69 70 /* Main program. */ 71 72 void main(void) 73 { 74 uart_echo = 0; 75 76 init_memory(); 77 init_pins(); 78 init_outputs(); 79 80 unlock_config(); 81 config_uart(); 82 lock_config(); 83 84 init_dma(); 85 86 /* Peripheral relationships: 87 88 Timer3 -> OC1 -> DMA0: message2 -> U1TXREG 89 ___/ 90 / 91 Timer2 -> DMA1: message1 -> U1TXREG 92 \___ 93 \ 94 Timer3 -> OC1 -> DMA2: message3 -> U1TXREG 95 */ 96 97 /* Enable DMA on the next channel's completion, with OC1 initiating 98 transfers, raising a transfer completion interrupt to be handled. */ 99 100 dma_init(0, 2); 101 dma_set_chaining(0, dma_chain_next); 102 dma_set_interrupt(0, OC1, 1); 103 dma_set_transfer(0, PHYSICAL((uint32_t) message2), sizeof(message2) - 1, 104 HW_PHYSICAL(UART_REG(1, UxTXREG)), 1, 105 CELLSIZE); 106 107 /* Initiate DMA on the Timer2 interrupt. Since the channel is not 108 auto-enabled, it must be explicitly enabled elsewhere (when a UART 109 interrupt is handled). */ 110 111 dma_init(1, 3); 112 dma_set_interrupt(1, T2, 1); 113 dma_set_transfer(1, PHYSICAL((uint32_t) message1), sizeof(message1) - 1, 114 HW_PHYSICAL(UART_REG(1, UxTXREG)), 1, 115 1); 116 117 /* Enable DMA on the preceding channel's completion, with OC1 initiating 118 transfers, raising a transfer completion interrupt to be handled. */ 119 120 dma_init(2, 2); 121 dma_set_chaining(2, dma_chain_previous); 122 dma_set_interrupt(2, OC1, 1); 123 dma_set_transfer(2, PHYSICAL((uint32_t) message3), sizeof(message3) - 1, 124 HW_PHYSICAL(UART_REG(1, UxTXREG)), 1, 125 CELLSIZE); 126 dma_init_interrupt(2, 0b00001000, 7, 3); 127 128 /* Configure a timer for the first DMA channel whose interrupt condition 129 drives the transfer. The interrupt itself does not need to be enabled. */ 130 131 timer_init(2, 0b111, 60000); 132 timer_on(2); 133 134 /* Configure a timer for the output compare unit below. */ 135 136 timer_init(3, 0b111, 20000); 137 timer_on(3); 138 139 /* Configure output compare in dual compare (continuous output) mode using 140 Timer3 as time base. The interrupt condition drives the second DMA 141 channel but does not need to be enabled. */ 142 143 oc_init(1, 0b101, 3); 144 oc_set_pulse(1, 10000); 145 oc_set_pulse_end(1, 20000); 146 oc_on(1); 147 148 /* Set UART interrupt priority above CPU priority to process events and to 149 enable the first DMA channel. */ 150 151 uart_init(1, 115200); 152 uart_init_interrupt(1, UxRIF, 7, 3); 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 char val; 173 174 /* Check for a UART receive interrupt condition (UxRIF). */ 175 176 ifs = REG(UARTIFS) & UART_INT_FLAGS(1, UxRIF); 177 178 if (ifs) 179 { 180 /* Clear the UART interrupt condition. */ 181 182 CLR_REG(UARTIFS, ifs); 183 184 /* Write the received data back. */ 185 186 while (uart_can_read(1)) 187 { 188 val = uart_read_char(1); 189 if (uart_echo) 190 uart_write_char(1, val); 191 192 /* Initiate transfer upon receiving a particular character. */ 193 194 if (val == '0') 195 dma_on(1); 196 } 197 } 198 199 /* Check for a DMA interrupt condition (CHBCIF). */ 200 201 ifs = REG(DMAIFS) & DMA_INT_FLAGS(2, DCHxIF); 202 203 if (ifs) 204 { 205 uart_write_string("CHBCIF\r\n"); 206 INV_REG(PORTA, 1 << 2); 207 CLR_REG(DMA_REG(2, DCHxINT), 0b11111111); 208 CLR_REG(DMAIFS, ifs); 209 } 210 } 211 212 213 214 /* Peripheral pin configuration. */ 215 216 void config_uart(void) 217 { 218 /* Map U1RX to RPB13. */ 219 220 REG(U1RXR) = 0b0011; /* U1RXR<3:0> = 0011 (RPB13) */ 221 222 /* Map U1TX to RPB15. */ 223 224 REG(RPB15R) = 0b0001; /* RPB15R<3:0> = 0001 (U1TX) */ 225 226 /* Set RPB13 to input. */ 227 228 SET_REG(TRISB, 1 << 13); 229 }