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 /* Application-specific configuration. */ 25 26 #include "devconfig.h" 27 28 29 30 /* Basic memory and pin initialisation. */ 31 32 void init_memory(void) 33 { 34 /* 35 Configure RAM. 36 See: http://microchipdeveloper.com/32bit:mx-arch-exceptions-processor-initialization 37 */ 38 39 uint32_t config = REG(BMXCON); 40 41 /* Set zero wait states for address setup. */ 42 43 config &= ~(1 << 6); /* BMXCON<6> = BMXWSDRM = 0 */ 44 45 /* Set bus arbitration mode. */ 46 47 config &= ~0b111; 48 config |= 0b010; /* BMXCON<2:0> = BMXARB<2:0> = 2 */ 49 50 REG(BMXCON) = config; 51 } 52 53 void init_pins(void) 54 { 55 /* DEVCFG0<2> also needs setting to 0 before the program is run. */ 56 57 CLR_REG(CFGCON, 1 << 3); /* CFGCON<3> = JTAGEN = 0 */ 58 } 59 60 void init_outputs(void) 61 { 62 /* Remove analogue features from pins. */ 63 64 REG(ANSELA) = 0; 65 REG(ANSELB) = 0; 66 67 /* Set pins as outputs. */ 68 69 REG(TRISA) = 0; 70 REG(TRISB) = 0; 71 72 /* Clear outputs. */ 73 74 REG(PORTA) = 0; 75 REG(PORTB) = 0; 76 } 77 78 79 80 /* Peripheral pin configuration. */ 81 82 void lock_config(void) 83 { 84 SET_REG(CFGCON, 1 << 13); /* IOLOCK = 1 */ 85 86 /* Lock the configuration again. */ 87 88 REG(SYSKEY) = 0x33333333; 89 } 90 91 void unlock_config(void) 92 { 93 /* Unlock the configuration register bits. */ 94 95 REG(SYSKEY) = 0; 96 REG(SYSKEY) = 0xAA996655; 97 REG(SYSKEY) = 0x556699AA; 98 99 CLR_REG(CFGCON, 1 << 13); /* IOLOCK = 0 */ 100 } 101 102 103 104 /* Convenience operations. */ 105 106 void interrupts_on(void) 107 { 108 init_interrupts(); 109 enable_interrupts(); 110 handle_error_level(); 111 } 112 113 114 115 /* DMA configuration. */ 116 117 void init_dma(void) 118 { 119 /* Disable DMA interrupts (DMAxIE). */ 120 121 CLR_REG(DMAIEC, 0b1111 << DMAINTBASE); 122 123 /* Clear DMA interrupt flags (DMAxIF). */ 124 125 CLR_REG(DMAIFS, 0b1111 << DMAINTBASE); 126 127 /* Enable DMA. */ 128 129 SET_REG(DMACON, 1 << 15); 130 } 131 132 /* Initialise the given channel. */ 133 134 void dma_init(int channel, uint8_t pri) 135 { 136 if ((channel < DCHMIN) || (channel > DCHMAX)) 137 return; 138 139 /* Initialise a channel. */ 140 141 REG(DMA_REG(channel, DCHxCON)) = pri & 0b11; 142 REG(DMA_REG(channel, DCHxECON)) = 0; 143 REG(DMA_REG(channel, DCHxINT)) = 0; 144 } 145 146 /* Set the channel repeated enable mode, enabling it again when a block transfer 147 completes. The documentation describes this as auto-enable. */ 148 149 void dma_set_auto_enable(int channel, int enable) 150 { 151 (enable ? SET_REG : CLR_REG)(DMA_REG(channel, DCHxCON), 1 << 4); 152 } 153 154 /* Set the channel chaining mode. */ 155 156 void dma_set_chaining(int channel, enum dma_chain chain) 157 { 158 (chain != dma_chain_none ? 159 SET_REG : CLR_REG)(DMA_REG(channel, DCHxCON), 1 << 5); 160 161 (chain == dma_chain_next ? 162 SET_REG : CLR_REG)(DMA_REG(channel, DCHxCON), 1 << 8); 163 } 164 165 /* Configure a channel's initiation interrupt. */ 166 167 void dma_set_interrupt(int channel, uint8_t int_num, int enable) 168 { 169 if ((channel < DCHMIN) || (channel > DCHMAX)) 170 return; 171 172 /* Allow an interrupt to trigger the transfer. */ 173 174 REG(DMA_REG(channel, DCHxECON)) = (int_num << 8) | 175 ((enable ? 1 : 0) << 4); 176 } 177 178 /* Configure only the channel's initiation interrupt status. */ 179 180 void dma_set_interrupt_enable(int channel, int enable) 181 { 182 if ((channel < DCHMIN) || (channel > DCHMAX)) 183 return; 184 185 (enable ? SET_REG : CLR_REG)(DMA_REG(channel, DCHxECON), 1 << 4); 186 } 187 188 /* Permit the channel to register events while disabled or suspended. A 189 suspended channel is one that is enabled but where the DMA peripheral 190 has been suspended. */ 191 192 void dma_set_receive_events(int channel, int enable) 193 { 194 (enable ? SET_REG : CLR_REG)(DMA_REG(channel, DCHxCON), 1 << 6); 195 } 196 197 /* Set a channel's transfer parameters. */ 198 199 void dma_set_transfer(int channel, 200 uint32_t source_start_address, uint16_t source_size, 201 uint32_t destination_start_address, uint16_t destination_size, 202 uint16_t cell_size) 203 { 204 if ((channel < DCHMIN) || (channel > DCHMAX)) 205 return; 206 207 REG(DMA_REG(channel, DCHxSSIZ)) = source_size; 208 REG(DMA_REG(channel, DCHxSSA)) = source_start_address; 209 REG(DMA_REG(channel, DCHxDSIZ)) = destination_size; 210 REG(DMA_REG(channel, DCHxDSA)) = destination_start_address; 211 REG(DMA_REG(channel, DCHxCSIZ)) = cell_size; 212 } 213 214 /* Configure interrupts caused by the channel. */ 215 216 void dma_init_interrupt(int channel, uint8_t conditions, 217 uint8_t pri, uint8_t sub) 218 { 219 if ((channel < DCHMIN) || (channel > DCHMAX)) 220 return; 221 222 /* Disable channel interrupt and clear interrupt flag. */ 223 224 CLR_REG(DMAIEC, DMA_INT_FLAGS(channel, 1)); 225 CLR_REG(DMAIFS, DMA_INT_FLAGS(channel, 1)); 226 227 /* Produce an interrupt for the provided conditions. */ 228 229 REG(DMA_REG(channel, DCHxINT)) = conditions << 16; 230 231 /* Set interrupt priorities. */ 232 233 REG(DMAIPC) = (REG(DMAIPC) & 234 ~(DMA_IPC_PRI(channel, 7, 3))) | 235 DMA_IPC_PRI(channel, pri, sub); 236 237 /* Enable interrupt. */ 238 239 SET_REG(DMAIEC, DMA_INT_FLAGS(channel, 1)); 240 } 241 242 /* Enable or disable the channel. */ 243 244 void dma_set_enable(int channel, int enable) 245 { 246 if ((channel < DCHMIN) || (channel > DCHMAX)) 247 return; 248 249 (enable ? SET_REG : CLR_REG)(DMA_REG(channel, DCHxCON), 1 << 7); 250 } 251 252 /* Disable a DMA channel. */ 253 254 void dma_off(int channel) 255 { 256 dma_set_enable(channel, 0); 257 } 258 259 /* Enable a DMA channel. */ 260 261 void dma_on(int channel) 262 { 263 dma_set_enable(channel, 1); 264 } 265 266 267 268 /* External interrupt initialisation. */ 269 270 void int_init_interrupt(int int_num, uint8_t pri, uint8_t sub) 271 { 272 if ((int_num < INTMIN) || (int_num > INTMAX)) 273 return; 274 275 /* Disable interrupt and clear interrupt flag. */ 276 277 CLR_REG(INTIEC, INT_INT_FLAGS(int_num, INTxIE)); 278 CLR_REG(INTIFS, INT_INT_FLAGS(int_num, INTxIF)); 279 280 /* Set interrupt priorities. */ 281 282 REG(INT_IPC_REG(int_num)) = (REG(INT_IPC_REG(int_num)) & 283 ~(INT_IPC_PRI(int_num, 7, 3))) | 284 INT_IPC_PRI(int_num, pri, sub); 285 286 /* Enable interrupt. */ 287 288 SET_REG(INTIEC, INT_INT_FLAGS(int_num, INTxIE)); 289 } 290 291 292 293 /* Output compare configuration. */ 294 295 void oc_init(int unit, uint8_t mode, int timer) 296 { 297 if ((unit < OCMIN) || (unit > OCMAX)) 298 return; 299 300 REG(OC_REG(unit, OCxCON)) = (timer == 3 ? (1 << 3) : 0) | (mode & 0b111); 301 } 302 303 /* Set the start value for the pulse. */ 304 305 void oc_set_pulse(int unit, uint32_t start) 306 { 307 if ((unit < OCMIN) || (unit > OCMAX)) 308 return; 309 310 REG(OC_REG(unit, OCxR)) = start; 311 } 312 313 /* Set the end value for the pulse. */ 314 315 void oc_set_pulse_end(int unit, uint32_t end) 316 { 317 if ((unit < OCMIN) || (unit > OCMAX)) 318 return; 319 320 REG(OC_REG(unit, OCxRS)) = end; 321 } 322 323 /* Configure interrupts caused by the unit. */ 324 325 void oc_init_interrupt(int unit, uint8_t pri, uint8_t sub) 326 { 327 if ((unit < OCMIN) || (unit > OCMAX)) 328 return; 329 330 /* Disable interrupt and clear interrupt flag. */ 331 332 CLR_REG(OCIEC, OC_INT_FLAGS(unit, OCxIE)); 333 CLR_REG(OCIFS, OC_INT_FLAGS(unit, OCxIF)); 334 335 /* Set interrupt priorities. */ 336 337 REG(OC_IPC_REG(unit)) = (REG(OC_IPC_REG(unit)) & 338 ~(OC_IPC_PRI(unit, 7, 3))) | 339 OC_IPC_PRI(unit, pri, sub); 340 341 /* Enable interrupt. */ 342 343 SET_REG(OCIEC, OC_INT_FLAGS(unit, OCxIE)); 344 } 345 346 /* Enable a unit. */ 347 348 void oc_on(int unit) 349 { 350 if ((unit < OCMIN) || (unit > OCMAX)) 351 return; 352 353 SET_REG(OC_REG(unit, OCxCON), 1 << 15); 354 } 355 356 357 358 /* Parallel mode configuration. */ 359 360 void init_pm(void) 361 { 362 int i; 363 364 /* Disable PM interrupts (PMxIE). */ 365 366 CLR_REG(PMIEC, 0b11 << PMINTBASE); 367 368 /* Clear PM interrupt flags (PMxIF). */ 369 370 CLR_REG(PMIFS, 0b11 << PMINTBASE); 371 372 /* Disable PM for configuration. */ 373 374 for (i = PMMIN; i <= PMMAX; i++) 375 REG(PM_REG(i, PMxCON)) = 0; 376 } 377 378 /* Configure the parallel mode. */ 379 380 void pm_init(int port, uint8_t mode) 381 { 382 if ((port < PMMIN) || (port > PMMAX)) 383 return; 384 385 REG(PM_REG(port, PMxMODE)) = (mode & 0b11) << 8; 386 REG(PM_REG(port, PMxAEN)) = 0; 387 REG(PM_REG(port, PMxADDR)) = 0; 388 } 389 390 /* Configure output signals. */ 391 392 void pm_set_output(int port, int write_enable, int read_enable) 393 { 394 if ((port < PMMIN) || (port > PMMAX)) 395 return; 396 397 REG(PM_REG(port, PMxCON)) = (write_enable ? (1 << 9) : 0) | 398 (read_enable ? (1 << 8) : 0) | 399 (1 << 1); /* WRSP: PMENB active high */ 400 } 401 402 /* Configure interrupts caused by parallel mode. */ 403 404 void pm_init_interrupt(int port, uint8_t pri, uint8_t sub) 405 { 406 if ((port < PMMIN) || (port > PMMAX)) 407 return; 408 409 /* Disable interrupt and clear interrupt flag. */ 410 411 CLR_REG(PMIEC, PM_INT_FLAGS(port, PMxIE)); 412 CLR_REG(PMIFS, PM_INT_FLAGS(port, PMxIF)); 413 414 /* Set interrupt priorities. */ 415 416 REG(PM_IPC_REG(port)) = (REG(PM_IPC_REG(port)) & 417 ~(PM_IPC_PRI(port, 7, 3))) | 418 PM_IPC_PRI(port, pri, sub); 419 420 /* Enable interrupt. */ 421 422 SET_REG(PMIEC, PM_INT_FLAGS(port, PMxIE)); 423 } 424 425 /* Enable parallel mode. */ 426 427 void pm_on(int port) 428 { 429 if ((port < PMMIN) || (port > PMMAX)) 430 return; 431 432 SET_REG(PM_REG(port, PMxCON), 1 << 15); 433 } 434 435 /* Disable parallel mode. */ 436 437 void pm_off(int port) 438 { 439 if ((port < PMMIN) || (port > PMMAX)) 440 return; 441 442 CLR_REG(PM_REG(port, PMxCON), 1 << 15); 443 } 444 445 446 447 448 /* Timer configuration. */ 449 450 void timer_init(int timer, uint8_t prescale, uint16_t limit) 451 { 452 /* NOTE: Should convert from the real prescale value. */ 453 454 REG(TIMER_REG(timer, TxCON)) = (prescale & 0b111) << 4; 455 REG(TIMER_REG(timer, TMRx)) = 0; 456 REG(TIMER_REG(timer, PRx)) = limit; 457 } 458 459 /* Configure interrupts caused by the timer. */ 460 461 void timer_init_interrupt(int timer, uint8_t pri, uint8_t sub) 462 { 463 if ((timer < TIMERMIN) || (timer > TIMERMAX)) 464 return; 465 466 /* Disable interrupt and clear interrupt flag. */ 467 468 CLR_REG(TIMERIEC, TIMER_INT_FLAGS(timer, TxIE)); 469 CLR_REG(TIMERIFS, TIMER_INT_FLAGS(timer, TxIF)); 470 471 /* Set interrupt priorities. */ 472 473 REG(TIMER_IPC_REG(timer)) = (REG(TIMER_IPC_REG(timer)) & 474 ~(TIMER_IPC_PRI(timer, 7, 3))) | 475 TIMER_IPC_PRI(timer, pri, sub); 476 477 /* Enable interrupt. */ 478 479 SET_REG(TIMERIEC, TIMER_INT_FLAGS(timer, TxIE)); 480 } 481 482 /* Enable a timer. */ 483 484 void timer_on(int timer) 485 { 486 if ((timer < TIMERMIN) || (timer > TIMERMAX)) 487 return; 488 489 SET_REG(TIMER_REG(timer, TxCON), 1 << 15); 490 } 491 492 493 494 /* UART configuration. */ 495 496 void uart_init(int uart, uint32_t baudrate) 497 { 498 /* FPB is configured in the devconfig.h file and set in the start.S file. */ 499 500 if ((uart < UARTMIN) || (uart > UARTMAX)) 501 return; 502 503 /* Disable the UART (ON). */ 504 505 CLR_REG(UART_REG(uart, UxMODE), 1 << 15); 506 507 /* Set the baud rate. For example: 508 509 UxBRG<15:0> = BRG 510 = (FPB / (16 * baudrate)) - 1 511 = (24000000 / (16 * 115200)) - 1 512 = 12 513 */ 514 515 REG(UART_REG(uart, UxBRG)) = (FPB / (16 * baudrate)) - 1; 516 } 517 518 /* Configure interrupts caused by the UART. */ 519 520 void uart_init_interrupt(int uart, uint8_t conditions, 521 uint8_t pri, uint8_t sub) 522 { 523 if ((uart < UARTMIN) || (uart > UARTMAX)) 524 return; 525 526 /* Disable interrupts and clear interrupt flags. */ 527 528 CLR_REG(UARTIEC, UART_INT_FLAGS(uart, UxTIE | UxRIE | UxEIE)); 529 CLR_REG(UARTIFS, UART_INT_FLAGS(uart, UxTIF | UxRIF | UxEIF)); 530 531 /* Set priorities: UxIP = pri; UxIS = sub */ 532 533 REG(UART_IPC_REG(uart)) = (REG(UART_IPC_REG(uart)) & 534 ~UART_IPC_PRI(uart, 7, 3)) | 535 UART_IPC_PRI(uart, pri, sub); 536 537 /* Enable interrupts. */ 538 539 SET_REG(UARTIEC, UART_INT_FLAGS(uart, conditions)); 540 } 541 542 /* Enable a UART. */ 543 544 void uart_on(int uart) 545 { 546 if ((uart < UARTMIN) || (uart > UARTMAX)) 547 return; 548 549 /* Enable receive (URXEN) and transmit (UTXEN). */ 550 551 SET_REG(UART_REG(uart, UxSTA), (1 << 12) | (1 << 10)); 552 553 /* Start UART. */ 554 555 SET_REG(UART_REG(uart, UxMODE), 1 << 15); 556 } 557 558 559 560 /* Utility functions. */ 561 562 /* Return encoded interrupt priorities. */ 563 564 static uint8_t PRI(uint8_t pri, uint8_t sub) 565 { 566 return ((pri & 0b111) << 2) | (sub & 0b11); 567 } 568 569 /* Return the DMA interrupt flags for combining with a register. */ 570 571 int DMA_INT_FLAGS(int channel, uint8_t flags) 572 { 573 return (flags & 0b1) << (DMAINTBASE + (channel - DCHMIN)); 574 } 575 576 /* Return encoded DMA interrupt priorities for combining with a register. */ 577 578 uint32_t DMA_IPC_PRI(int channel, uint8_t pri, uint8_t sub) 579 { 580 return PRI(pri, sub) << (DCHIPCBASE + (channel - DCHMIN) * DCHIPCSTEP); 581 } 582 583 /* Return encoded external interrupt priorities for combining with a register. */ 584 585 uint32_t INT_IPC_PRI(int int_num, uint8_t pri, uint8_t sub) 586 { 587 (void) int_num; 588 return PRI(pri, sub) << INTIPCBASE; 589 } 590 591 /* Return the external interrupt priorities register. */ 592 593 uint32_t INT_IPC_REG(int int_num) 594 { 595 switch (int_num) 596 { 597 case 0: return INT0IPC; 598 case 1: return INT1IPC; 599 case 2: return INT2IPC; 600 case 3: return INT3IPC; 601 case 4: return INT4IPC; 602 default: return 0; /* should not occur */ 603 } 604 } 605 606 /* Return the external interrupt flags for combining with a register. */ 607 608 int INT_INT_FLAGS(int int_num, uint8_t flags) 609 { 610 return (flags & 0b1) << (INTINTBASE + (int_num - INTMIN) * INTINTSTEP); 611 } 612 613 /* Return encoded output compare interrupt priorities for combining with a register. */ 614 615 uint32_t OC_IPC_PRI(int unit, uint8_t pri, uint8_t sub) 616 { 617 (void) unit; 618 return PRI(pri, sub) << OCIPCBASE; 619 } 620 621 /* Return the output compare interrupt priorities register. */ 622 623 uint32_t OC_IPC_REG(int unit) 624 { 625 switch (unit) 626 { 627 case 1: return OC1IPC; 628 case 2: return OC2IPC; 629 case 3: return OC3IPC; 630 case 4: return OC4IPC; 631 case 5: return OC5IPC; 632 default: return 0; /* should not occur */ 633 } 634 } 635 636 /* Return the output compare interrupt flags for combining with a register. */ 637 638 int OC_INT_FLAGS(int unit, uint8_t flags) 639 { 640 return (flags & 0b1) << (OCINTBASE + (unit - OCMIN) * OCINTSTEP); 641 } 642 643 /* Return encoded parallel mode interrupt priorities for combining with a register. */ 644 645 uint32_t PM_IPC_PRI(int port, uint8_t pri, uint8_t sub) 646 { 647 (void) port; 648 return PRI(pri, sub) << PMIPCBASE; 649 } 650 651 /* Return the parallel mode interrupt priorities register. */ 652 653 uint32_t PM_IPC_REG(int port) 654 { 655 (void) port; 656 return PMIPC; 657 } 658 659 /* Return the parallel mode interrupt flags for combining with a register. */ 660 661 int PM_INT_FLAGS(int port, uint8_t flags) 662 { 663 return (flags & 0b11) << (PMINTBASE + (port - PMMIN) * PMINTSTEP); 664 } 665 666 /* Return encoded timer interrupt priorities for combining with a register. */ 667 668 uint32_t TIMER_IPC_PRI(int timer, uint8_t pri, uint8_t sub) 669 { 670 (void) timer; 671 return PRI(pri, sub) << TIMERIPCBASE; 672 } 673 674 /* Return the timer interrupt priorities register. */ 675 676 uint32_t TIMER_IPC_REG(int timer) 677 { 678 switch (timer) 679 { 680 case 1: return TIMER1IPC; 681 case 2: return TIMER2IPC; 682 case 3: return TIMER3IPC; 683 case 4: return TIMER4IPC; 684 case 5: return TIMER5IPC; 685 default: return 0; /* should not occur */ 686 } 687 } 688 689 /* Return the timer interrupt flags for combining with a register. */ 690 691 int TIMER_INT_FLAGS(int timer, uint8_t flags) 692 { 693 return (flags & 0b1) << (TIMERINTBASE + (timer - TIMERMIN) * TIMERINTSTEP); 694 } 695 696 /* Return encoded UART interrupt priorities for combining with a register. */ 697 698 uint32_t UART_IPC_PRI(int uart, uint8_t pri, uint8_t sub) 699 { 700 return PRI(pri, sub) << (uart == 1 ? UART1IPCBASE : UART2IPCBASE); 701 } 702 703 /* Return the UART interrupt priorities register. */ 704 705 uint32_t UART_IPC_REG(int uart) 706 { 707 return uart == 1 ? UART1IPC : UART2IPC; 708 } 709 710 /* Return the UART interrupt flags for combining with a register. */ 711 712 int UART_INT_FLAGS(int uart, uint8_t flags) 713 { 714 return (flags & 0b111) << (UARTINTBASE + (uart - UARTMIN) * UARTINTSTEP); 715 }