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