1 /* 2 * PIC32 peripheral configuration and initialisation. 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 #include "cpu.h" 21 #include "pic32_c.h" 22 #include "init.h" 23 24 25 26 /* Basic memory and pin initialisation. */ 27 28 void init_memory(void) 29 { 30 /* 31 Configure RAM. 32 See: http://microchipdeveloper.com/32bit:mx-arch-exceptions-processor-initialization 33 */ 34 35 uint32_t config = REG(BMXCON); 36 37 /* Set zero wait states for address setup. */ 38 39 config &= ~(1 << 6); /* BMXCON<6> = BMXWSDRM = 0 */ 40 41 /* Set bus arbitration mode. */ 42 43 config &= ~0b111; 44 config |= 0b010; /* BMXCON<2:0> = BMXARB<2:0> = 2 */ 45 46 REG(BMXCON) = config; 47 } 48 49 void init_pins(void) 50 { 51 /* DEVCFG0<2> also needs setting to 0 before the program is run. */ 52 53 CLR_REG(CFGCON, 1 << 3); /* CFGCON<3> = JTAGEN = 0 */ 54 } 55 56 void init_outputs(void) 57 { 58 /* Remove analogue features from pins. */ 59 60 REG(ANSELA) = 0; 61 REG(ANSELB) = 0; 62 63 /* Set pins as outputs. */ 64 65 REG(TRISA) = 0; 66 REG(TRISB) = 0; 67 68 /* Clear outputs. */ 69 70 REG(PORTA) = 0; 71 REG(PORTB) = 0; 72 } 73 74 75 76 /* Peripheral pin configuration. */ 77 78 void config_uart(void) 79 { 80 /* NOTE: Configuring UART1 for specific pins. */ 81 82 /* Map U1RX to RPB13. */ 83 84 REG(U1RXR) = 0b0011; /* U1RXR<3:0> = 0011 (RPB13) */ 85 86 /* Map U1TX to RPB15. */ 87 88 REG(RPB15R) = 0b0001; /* RPB15R<3:0> = 0001 (U1TX) */ 89 90 /* Set RPB13 to input. */ 91 92 SET_REG(TRISB, 1 << 13); 93 } 94 95 void lock_config(void) 96 { 97 SET_REG(CFGCON, 1 << 13); /* IOLOCK = 1 */ 98 99 /* Lock the configuration again. */ 100 101 REG(SYSKEY) = 0x33333333; 102 } 103 104 void unlock_config(void) 105 { 106 /* Unlock the configuration register bits. */ 107 108 REG(SYSKEY) = 0; 109 REG(SYSKEY) = 0xAA996655; 110 REG(SYSKEY) = 0x556699AA; 111 112 CLR_REG(CFGCON, 1 << 13); /* IOLOCK = 0 */ 113 } 114 115 116 117 /* Convenience operations. */ 118 119 void interrupts_on(void) 120 { 121 init_interrupts(); 122 enable_interrupts(); 123 handle_error_level(); 124 } 125 126 127 128 /* DMA configuration. */ 129 130 void init_dma(void) 131 { 132 /* Disable DMA interrupts. */ 133 134 CLR_REG(DMAIEC, 0b1111 << DMAINTBASE); /* DMA3IE...DMA0IE = 0 */ 135 136 /* Clear DMA interrupt flags. */ 137 138 CLR_REG(DMAIFS, 0b1111 << DMAINTBASE); /* DMA3IF...DMA0IF = 0 */ 139 140 /* Enable DMA. */ 141 142 SET_REG(DMACON, 1 << 15); 143 } 144 145 /* Initialise the given channel. */ 146 147 void dma_init(int channel, uint8_t pri) 148 { 149 if ((channel < DCHMIN) || (channel > DCHMAX)) 150 return; 151 152 /* Initialise a channel. */ 153 154 REG(DMA_REG(channel, DCHxCON)) = pri & 0b11; 155 REG(DMA_REG(channel, DCHxECON)) = 0; 156 REG(DMA_REG(channel, DCHxINT)) = 0; 157 } 158 159 /* Set the channel auto-enable mode. */ 160 161 void dma_set_auto_enable(int channel, int auto_enable) 162 { 163 (auto_enable ? SET_REG : CLR_REG)(DMA_REG(channel, DCHxCON), 1 << 4); 164 } 165 166 /* Set the channel chaining mode. */ 167 168 void dma_set_chaining(int channel, enum dma_chain chain) 169 { 170 (chain != dma_chain_none ? 171 SET_REG : CLR_REG)(DMA_REG(channel, DCHxCON), 1 << 5); 172 173 (chain == dma_chain_next ? 174 SET_REG : CLR_REG)(DMA_REG(channel, DCHxCON), 1 << 8); 175 } 176 177 /* Configure a channel's initiation interrupt. */ 178 179 void dma_set_interrupt(int channel, uint8_t int_num, int enable) 180 { 181 if ((channel < DCHMIN) || (channel > DCHMAX)) 182 return; 183 184 /* Allow an interrupt to trigger the transfer. */ 185 186 REG(DMA_REG(channel, DCHxECON)) = (int_num << 8) | 187 ((enable ? 1 : 0) << 4); 188 } 189 190 /* Set a channel's transfer parameters. */ 191 192 void dma_set_transfer(int channel, 193 uint32_t source_start_address, uint16_t source_size, 194 uint32_t destination_start_address, uint16_t destination_size, 195 uint16_t cell_size) 196 { 197 if ((channel < DCHMIN) || (channel > DCHMAX)) 198 return; 199 200 REG(DMA_REG(channel, DCHxSSIZ)) = source_size; 201 REG(DMA_REG(channel, DCHxSSA)) = source_start_address; 202 REG(DMA_REG(channel, DCHxDSIZ)) = destination_size; 203 REG(DMA_REG(channel, DCHxDSA)) = destination_start_address; 204 REG(DMA_REG(channel, DCHxCSIZ)) = cell_size; 205 } 206 207 /* Configure interrupts caused by the channel. */ 208 209 void dma_init_interrupt(int channel, uint8_t conditions, 210 uint8_t pri, uint8_t sub) 211 { 212 if ((channel < DCHMIN) || (channel > DCHMAX)) 213 return; 214 215 /* Disable channel interrupt and clear interrupt flag. */ 216 217 CLR_REG(DMAIEC, DMA_INT_FLAGS(channel, 1)); 218 CLR_REG(DMAIFS, DMA_INT_FLAGS(channel, 1)); 219 220 /* Produce an interrupt for the provided conditions. */ 221 222 REG(DMA_REG(channel, DCHxINT)) = conditions << 16; 223 224 /* Set interrupt priorities. */ 225 226 REG(DMAIPC) = (REG(DMAIPC) & 227 ~(DMA_IPC_PRI(channel, 7, 3))) | 228 DMA_IPC_PRI(channel, pri, sub); 229 230 /* Enable interrupt. */ 231 232 SET_REG(DMAIEC, DMA_INT_FLAGS(channel, 1)); 233 } 234 235 /* Enable a DMA channel. */ 236 237 void dma_on(int channel) 238 { 239 if ((channel < DCHMIN) || (channel > DCHMAX)) 240 return; 241 242 /* Enable channel. */ 243 244 SET_REG(DMA_REG(channel, DCHxCON), 1 << 7); 245 } 246 247 248 249 /* Output compare configuration. */ 250 251 void oc_init(int unit, uint8_t mode, int timer) 252 { 253 if ((unit < OCMIN) || (unit > OCMAX)) 254 return; 255 256 REG(OC_REG(unit, OCxCON)) = (timer == 3 ? (1 << 3) : 0) | (mode & 0b111); 257 } 258 259 /* Set the start value for the pulse. */ 260 261 void oc_set_pulse(int unit, uint32_t start) 262 { 263 if ((unit < OCMIN) || (unit > OCMAX)) 264 return; 265 266 REG(OC_REG(unit, OCxR)) = start; 267 } 268 269 /* Set the end value for the pulse. */ 270 271 void oc_set_pulse_end(int unit, uint32_t end) 272 { 273 if ((unit < OCMIN) || (unit > OCMAX)) 274 return; 275 276 REG(OC_REG(unit, OCxRS)) = end; 277 } 278 279 /* Configure interrupts caused by the unit. */ 280 281 void oc_init_interrupt(int unit, uint8_t pri, uint8_t sub) 282 { 283 if ((unit < OCMIN) || (unit > OCMAX)) 284 return; 285 286 /* Disable interrupt and clear interrupt flag. */ 287 288 CLR_REG(OCIEC, OC_INT_FLAGS(unit, OCxIE)); 289 CLR_REG(OCIFS, OC_INT_FLAGS(unit, OCxIF)); 290 291 /* Set interrupt priorities. */ 292 293 REG(OC_IPC_REG(unit)) = (REG(OC_IPC_REG(unit)) & 294 ~(OC_IPC_PRI(unit, 7, 3))) | 295 OC_IPC_PRI(unit, pri, sub); 296 297 /* Enable interrupt. */ 298 299 SET_REG(OCIEC, OC_INT_FLAGS(unit, OCxIE)); 300 } 301 302 /* Enable a unit. */ 303 304 void oc_on(int unit) 305 { 306 if ((unit < OCMIN) || (unit > OCMAX)) 307 return; 308 309 SET_REG(OC_REG(unit, OCxCON), 1 << 15); 310 } 311 312 313 314 /* Timer configuration. */ 315 316 void timer_init(int timer, uint8_t prescale, uint16_t limit) 317 { 318 /* NOTE: Should convert from the real prescale value. */ 319 320 REG(TIMER_REG(timer, TxCON)) = (prescale & 0b111) << 4; 321 REG(TIMER_REG(timer, TMRx)) = 0; 322 REG(TIMER_REG(timer, PRx)) = limit; 323 } 324 325 /* Configure interrupts caused by the timer. */ 326 327 void timer_init_interrupt(int timer, uint8_t pri, uint8_t sub) 328 { 329 if ((timer < TIMERMIN) || (timer > TIMERMAX)) 330 return; 331 332 /* Disable interrupt and clear interrupt flag. */ 333 334 CLR_REG(TIMERIEC, TIMER_INT_FLAGS(timer, TxIE)); 335 CLR_REG(TIMERIFS, TIMER_INT_FLAGS(timer, TxIF)); 336 337 /* Set interrupt priorities. */ 338 339 REG(TIMER_IPC_REG(timer)) = (REG(TIMER_IPC_REG(timer)) & 340 ~(TIMER_IPC_PRI(timer, 7, 3))) | 341 TIMER_IPC_PRI(timer, pri, sub); 342 343 /* Enable interrupt. */ 344 345 SET_REG(TIMERIEC, TIMER_INT_FLAGS(timer, TxIE)); 346 } 347 348 /* Enable a timer. */ 349 350 void timer_on(int timer) 351 { 352 if ((timer < TIMERMIN) || (timer > TIMERMAX)) 353 return; 354 355 SET_REG(TIMER_REG(timer, TxCON), 1 << 15); 356 } 357 358 359 360 /* UART configuration. */ 361 362 void uart_init(int uart, uint32_t baudrate) 363 { 364 /* NOTE: Configured in the initial payload. */ 365 366 uint32_t FPB = 24000000; 367 368 if ((uart < UARTMIN) || (uart > UARTMAX)) 369 return; 370 371 /* Disable the UART (ON). */ 372 373 CLR_REG(UART_REG(uart, UxMODE), 1 << 15); 374 375 /* Set the baud rate. For example: 376 377 UxBRG<15:0> = BRG 378 = (FPB / (16 * baudrate)) - 1 379 = (24000000 / (16 * 115200)) - 1 380 = 12 381 */ 382 383 REG(UART_REG(uart, UxBRG)) = (FPB / (16 * baudrate)) - 1; 384 } 385 386 /* Configure interrupts caused by the UART. */ 387 388 void uart_init_interrupt(int uart, uint8_t conditions, 389 uint8_t pri, uint8_t sub) 390 { 391 if ((uart < UARTMIN) || (uart > UARTMAX)) 392 return; 393 394 /* Disable interrupts and clear interrupt flags. */ 395 396 CLR_REG(UARTIEC, UART_INT_FLAGS(uart, UxTIE | UxRIE | UxEIE)); 397 CLR_REG(UARTIFS, UART_INT_FLAGS(uart, UxTIF | UxRIF | UxEIF)); 398 399 /* Set priorities: UxIP = pri; UxIS = sub */ 400 401 REG(UART_IPC_REG(uart)) = (REG(UART_IPC_REG(uart)) & 402 ~UART_IPC_PRI(uart, 7, 3)) | 403 UART_IPC_PRI(uart, pri, sub); 404 405 /* Enable interrupts. */ 406 407 SET_REG(UARTIEC, UART_INT_FLAGS(uart, conditions)); 408 } 409 410 /* Enable a UART. */ 411 412 void uart_on(int uart) 413 { 414 if ((uart < UARTMIN) || (uart > UARTMAX)) 415 return; 416 417 /* Enable receive (URXEN) and transmit (UTXEN). */ 418 419 SET_REG(UART_REG(uart, UxSTA), (1 << 12) | (1 << 10)); 420 421 /* Start UART. */ 422 423 SET_REG(UART_REG(uart, UxMODE), 1 << 15); 424 } 425 426 427 428 /* Utility functions. */ 429 430 /* Return encoded interrupt priorities. */ 431 432 static uint8_t PRI(uint8_t pri, uint8_t sub) 433 { 434 return ((pri & 0b111) << 2) | (sub & 0b11); 435 } 436 437 /* Return the DMA interrupt flags for combining with a register. */ 438 439 int DMA_INT_FLAGS(int channel, uint8_t flags) 440 { 441 return (flags & 0b1) << (DMAINTBASE + (channel - DCHMIN)); 442 } 443 444 /* Return encoded DMA interrupt priorities for combining with a register. */ 445 446 uint32_t DMA_IPC_PRI(int channel, uint8_t pri, uint8_t sub) 447 { 448 return PRI(pri, sub) << (DCHIPCBASE + (channel - DCHMIN) * DCHIPCSTEP); 449 } 450 451 /* Return encoded output compare interrupt priorities for combining with a register. */ 452 453 uint32_t OC_IPC_PRI(int unit, uint8_t pri, uint8_t sub) 454 { 455 (void) unit; 456 return PRI(pri, sub) << OCIPCBASE; 457 } 458 459 /* Return the output compare interrupt priorities register. */ 460 461 uint32_t OC_IPC_REG(int unit) 462 { 463 switch (unit) 464 { 465 case 1: return OC1IPC; 466 case 2: return OC2IPC; 467 case 3: return OC3IPC; 468 case 4: return OC4IPC; 469 case 5: return OC5IPC; 470 default: return 0; /* should not occur */ 471 } 472 } 473 474 /* Return the output compare interrupt flags for combining with a register. */ 475 476 int OC_INT_FLAGS(int unit, uint8_t flags) 477 { 478 return (flags & 0b1) << (OCINTBASE + (unit - OCMIN) * OCINTSTEP); 479 } 480 481 /* Return encoded timer interrupt priorities for combining with a register. */ 482 483 uint32_t TIMER_IPC_PRI(int timer, uint8_t pri, uint8_t sub) 484 { 485 (void) timer; 486 return PRI(pri, sub) << TIMERIPCBASE; 487 } 488 489 /* Return the timer interrupt priorities register. */ 490 491 uint32_t TIMER_IPC_REG(int timer) 492 { 493 switch (timer) 494 { 495 case 1: return TIMER1IPC; 496 case 2: return TIMER2IPC; 497 case 3: return TIMER3IPC; 498 case 4: return TIMER4IPC; 499 case 5: return TIMER5IPC; 500 default: return 0; /* should not occur */ 501 } 502 } 503 504 /* Return the timer interrupt flags for combining with a register. */ 505 506 int TIMER_INT_FLAGS(int timer, uint8_t flags) 507 { 508 return (flags & 0b1) << (TIMERINTBASE + (timer - TIMERMIN) * TIMERINTSTEP); 509 } 510 511 /* Return encoded UART interrupt priorities for combining with a register. */ 512 513 uint32_t UART_IPC_PRI(int uart, uint8_t pri, uint8_t sub) 514 { 515 return PRI(pri, sub) << (uart == 1 ? UART1IPCBASE : UART2IPCBASE); 516 } 517 518 /* Return the UART interrupt priorities register. */ 519 520 uint32_t UART_IPC_REG(int uart) 521 { 522 return uart == 1 ? UART1IPC : UART2IPC; 523 } 524 525 /* Return the UART interrupt flags for combining with a register. */ 526 527 int UART_INT_FLAGS(int uart, uint8_t flags) 528 { 529 return (flags & 0b111) << (UARTINTBASE + (uart - UARTMIN) * UARTINTSTEP); 530 }