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 source. */ 198 199 void dma_set_source(int channel, 200 uint32_t source_start_address, uint16_t source_size) 201 { 202 if ((channel < DCHMIN) || (channel > DCHMAX)) 203 return; 204 205 REG(DMA_REG(channel, DCHxSSIZ)) = source_size; 206 REG(DMA_REG(channel, DCHxSSA)) = source_start_address; 207 } 208 209 /* Set a channel's transfer parameters. */ 210 211 void dma_set_transfer(int channel, 212 uint32_t source_start_address, uint16_t source_size, 213 uint32_t destination_start_address, uint16_t destination_size, 214 uint16_t cell_size) 215 { 216 if ((channel < DCHMIN) || (channel > DCHMAX)) 217 return; 218 219 dma_set_source(channel, source_start_address, source_size); 220 221 REG(DMA_REG(channel, DCHxDSIZ)) = destination_size; 222 REG(DMA_REG(channel, DCHxDSA)) = destination_start_address; 223 REG(DMA_REG(channel, DCHxCSIZ)) = cell_size; 224 } 225 226 /* Configure interrupts caused by the channel. */ 227 228 void dma_init_interrupt(int channel, uint8_t conditions, 229 uint8_t pri, uint8_t sub) 230 { 231 if ((channel < DCHMIN) || (channel > DCHMAX)) 232 return; 233 234 /* Disable channel interrupt and clear interrupt flag. */ 235 236 CLR_REG(DMAIEC, DMA_INT_FLAGS(channel, 1)); 237 CLR_REG(DMAIFS, DMA_INT_FLAGS(channel, 1)); 238 239 /* Produce an interrupt for the provided conditions. */ 240 241 REG(DMA_REG(channel, DCHxINT)) = conditions << 16; 242 243 /* Set interrupt priorities. */ 244 245 REG(DMAIPC) = (REG(DMAIPC) & 246 ~(DMA_IPC_PRI(channel, 7, 3))) | 247 DMA_IPC_PRI(channel, pri, sub); 248 249 /* Enable interrupt. */ 250 251 SET_REG(DMAIEC, DMA_INT_FLAGS(channel, 1)); 252 } 253 254 /* Enable or disable the channel. */ 255 256 void dma_set_enable(int channel, int enable) 257 { 258 if ((channel < DCHMIN) || (channel > DCHMAX)) 259 return; 260 261 (enable ? SET_REG : CLR_REG)(DMA_REG(channel, DCHxCON), 1 << 7); 262 } 263 264 /* Disable a DMA channel. */ 265 266 void dma_off(int channel) 267 { 268 dma_set_enable(channel, 0); 269 } 270 271 /* Enable a DMA channel. */ 272 273 void dma_on(int channel) 274 { 275 dma_set_enable(channel, 1); 276 } 277 278 279 280 /* External interrupt initialisation. */ 281 282 void int_init_interrupt(int int_num, uint8_t pri, uint8_t sub) 283 { 284 if ((int_num < INTMIN) || (int_num > INTMAX)) 285 return; 286 287 /* Disable interrupt and clear interrupt flag. */ 288 289 CLR_REG(INTIEC, INT_INT_FLAGS(int_num, INTxIE)); 290 CLR_REG(INTIFS, INT_INT_FLAGS(int_num, INTxIF)); 291 292 /* Set interrupt priorities. */ 293 294 REG(INT_IPC_REG(int_num)) = (REG(INT_IPC_REG(int_num)) & 295 ~(INT_IPC_PRI(int_num, 7, 3))) | 296 INT_IPC_PRI(int_num, pri, sub); 297 298 /* Enable interrupt. */ 299 300 SET_REG(INTIEC, INT_INT_FLAGS(int_num, INTxIE)); 301 } 302 303 304 305 /* Output compare configuration. */ 306 307 void oc_init(int unit, uint8_t mode, int timer) 308 { 309 if ((unit < OCMIN) || (unit > OCMAX)) 310 return; 311 312 REG(OC_REG(unit, OCxCON)) = (timer == 3 ? (1 << 3) : 0) | (mode & 0b111); 313 } 314 315 /* Set the start value for the pulse. */ 316 317 void oc_set_pulse(int unit, uint32_t start) 318 { 319 if ((unit < OCMIN) || (unit > OCMAX)) 320 return; 321 322 REG(OC_REG(unit, OCxR)) = start; 323 } 324 325 /* Set the end value for the pulse. */ 326 327 void oc_set_pulse_end(int unit, uint32_t end) 328 { 329 if ((unit < OCMIN) || (unit > OCMAX)) 330 return; 331 332 REG(OC_REG(unit, OCxRS)) = end; 333 } 334 335 /* Configure interrupts caused by the unit. */ 336 337 void oc_init_interrupt(int unit, uint8_t pri, uint8_t sub) 338 { 339 if ((unit < OCMIN) || (unit > OCMAX)) 340 return; 341 342 /* Disable interrupt and clear interrupt flag. */ 343 344 CLR_REG(OCIEC, OC_INT_FLAGS(unit, OCxIE)); 345 CLR_REG(OCIFS, OC_INT_FLAGS(unit, OCxIF)); 346 347 /* Set interrupt priorities. */ 348 349 REG(OC_IPC_REG(unit)) = (REG(OC_IPC_REG(unit)) & 350 ~(OC_IPC_PRI(unit, 7, 3))) | 351 OC_IPC_PRI(unit, pri, sub); 352 353 /* Enable interrupt. */ 354 355 SET_REG(OCIEC, OC_INT_FLAGS(unit, OCxIE)); 356 } 357 358 /* Enable a unit. */ 359 360 void oc_on(int unit) 361 { 362 if ((unit < OCMIN) || (unit > OCMAX)) 363 return; 364 365 SET_REG(OC_REG(unit, OCxCON), 1 << 15); 366 } 367 368 369 370 /* Parallel mode configuration. */ 371 372 void init_pm(void) 373 { 374 int i; 375 376 /* Disable PM interrupts (PMxIE). */ 377 378 CLR_REG(PMIEC, 0b11 << PMINTBASE); 379 380 /* Clear PM interrupt flags (PMxIF). */ 381 382 CLR_REG(PMIFS, 0b11 << PMINTBASE); 383 384 /* Disable PM for configuration. */ 385 386 for (i = PMMIN; i <= PMMAX; i++) 387 REG(PM_REG(i, PMxCON)) = 0; 388 } 389 390 /* Configure the parallel mode. */ 391 392 void pm_init(int port, uint8_t mode) 393 { 394 if ((port < PMMIN) || (port > PMMAX)) 395 return; 396 397 REG(PM_REG(port, PMxMODE)) = (mode & 0b11) << 8; 398 REG(PM_REG(port, PMxAEN)) = 0; 399 REG(PM_REG(port, PMxADDR)) = 0; 400 } 401 402 /* Configure output signals. */ 403 404 void pm_set_output(int port, int write_enable, int read_enable) 405 { 406 if ((port < PMMIN) || (port > PMMAX)) 407 return; 408 409 REG(PM_REG(port, PMxCON)) = (write_enable ? (1 << 9) : 0) | 410 (read_enable ? (1 << 8) : 0) | 411 (1 << 1); /* WRSP: PMENB active high */ 412 } 413 414 /* Configure interrupts caused by parallel mode. */ 415 416 void pm_init_interrupt(int port, uint8_t pri, uint8_t sub) 417 { 418 if ((port < PMMIN) || (port > PMMAX)) 419 return; 420 421 /* Disable interrupt and clear interrupt flag. */ 422 423 CLR_REG(PMIEC, PM_INT_FLAGS(port, PMxIE)); 424 CLR_REG(PMIFS, PM_INT_FLAGS(port, PMxIF)); 425 426 /* Set interrupt priorities. */ 427 428 REG(PM_IPC_REG(port)) = (REG(PM_IPC_REG(port)) & 429 ~(PM_IPC_PRI(port, 7, 3))) | 430 PM_IPC_PRI(port, pri, sub); 431 432 /* Enable interrupt. */ 433 434 SET_REG(PMIEC, PM_INT_FLAGS(port, PMxIE)); 435 } 436 437 /* Enable parallel mode. */ 438 439 void pm_on(int port) 440 { 441 if ((port < PMMIN) || (port > PMMAX)) 442 return; 443 444 SET_REG(PM_REG(port, PMxCON), 1 << 15); 445 } 446 447 /* Disable parallel mode. */ 448 449 void pm_off(int port) 450 { 451 if ((port < PMMIN) || (port > PMMAX)) 452 return; 453 454 CLR_REG(PM_REG(port, PMxCON), 1 << 15); 455 } 456 457 458 459 460 /* Timer configuration. */ 461 462 void timer_init(int timer, uint8_t prescale, uint16_t limit) 463 { 464 /* NOTE: Should convert from the real prescale value. */ 465 466 REG(TIMER_REG(timer, TxCON)) = (prescale & 0b111) << 4; 467 REG(TIMER_REG(timer, TMRx)) = 0; 468 REG(TIMER_REG(timer, PRx)) = limit; 469 } 470 471 /* Configure interrupts caused by the timer. */ 472 473 void timer_init_interrupt(int timer, uint8_t pri, uint8_t sub) 474 { 475 if ((timer < TIMERMIN) || (timer > TIMERMAX)) 476 return; 477 478 /* Disable interrupt and clear interrupt flag. */ 479 480 CLR_REG(TIMERIEC, TIMER_INT_FLAGS(timer, TxIE)); 481 CLR_REG(TIMERIFS, TIMER_INT_FLAGS(timer, TxIF)); 482 483 /* Set interrupt priorities. */ 484 485 REG(TIMER_IPC_REG(timer)) = (REG(TIMER_IPC_REG(timer)) & 486 ~(TIMER_IPC_PRI(timer, 7, 3))) | 487 TIMER_IPC_PRI(timer, pri, sub); 488 489 /* Enable interrupt. */ 490 491 SET_REG(TIMERIEC, TIMER_INT_FLAGS(timer, TxIE)); 492 } 493 494 /* Enable a timer. */ 495 496 void timer_on(int timer) 497 { 498 if ((timer < TIMERMIN) || (timer > TIMERMAX)) 499 return; 500 501 SET_REG(TIMER_REG(timer, TxCON), 1 << 15); 502 } 503 504 505 506 /* UART configuration. */ 507 508 void uart_init(int uart, uint32_t baudrate) 509 { 510 /* FPB is configured in the devconfig.h file and set in the start.S file. */ 511 512 if ((uart < UARTMIN) || (uart > UARTMAX)) 513 return; 514 515 /* Disable the UART (ON). */ 516 517 CLR_REG(UART_REG(uart, UxMODE), 1 << 15); 518 519 /* Set the baud rate. For example: 520 521 UxBRG<15:0> = BRG 522 = (FPB / (16 * baudrate)) - 1 523 = (24000000 / (16 * 115200)) - 1 524 = 12 525 */ 526 527 REG(UART_REG(uart, UxBRG)) = (FPB / (16 * baudrate)) - 1; 528 } 529 530 /* Configure interrupts caused by the UART. */ 531 532 void uart_init_interrupt(int uart, uint8_t conditions, 533 uint8_t pri, uint8_t sub) 534 { 535 if ((uart < UARTMIN) || (uart > UARTMAX)) 536 return; 537 538 /* Disable interrupts and clear interrupt flags. */ 539 540 CLR_REG(UARTIEC, UART_INT_FLAGS(uart, UxTIE | UxRIE | UxEIE)); 541 CLR_REG(UARTIFS, UART_INT_FLAGS(uart, UxTIF | UxRIF | UxEIF)); 542 543 /* Set priorities: UxIP = pri; UxIS = sub */ 544 545 REG(UART_IPC_REG(uart)) = (REG(UART_IPC_REG(uart)) & 546 ~UART_IPC_PRI(uart, 7, 3)) | 547 UART_IPC_PRI(uart, pri, sub); 548 549 /* Enable interrupts. */ 550 551 SET_REG(UARTIEC, UART_INT_FLAGS(uart, conditions)); 552 } 553 554 /* Enable a UART. */ 555 556 void uart_on(int uart) 557 { 558 if ((uart < UARTMIN) || (uart > UARTMAX)) 559 return; 560 561 /* Enable receive (URXEN) and transmit (UTXEN). */ 562 563 SET_REG(UART_REG(uart, UxSTA), (1 << 12) | (1 << 10)); 564 565 /* Start UART. */ 566 567 SET_REG(UART_REG(uart, UxMODE), 1 << 15); 568 } 569 570 571 572 /* Utility functions. */ 573 574 /* Return encoded interrupt priorities. */ 575 576 static uint8_t PRI(uint8_t pri, uint8_t sub) 577 { 578 return ((pri & 0b111) << 2) | (sub & 0b11); 579 } 580 581 /* Return the DMA interrupt flags for combining with a register. */ 582 583 int DMA_INT_FLAGS(int channel, uint8_t flags) 584 { 585 return (flags & 0b1) << (DMAINTBASE + (channel - DCHMIN)); 586 } 587 588 /* Return encoded DMA interrupt priorities for combining with a register. */ 589 590 uint32_t DMA_IPC_PRI(int channel, uint8_t pri, uint8_t sub) 591 { 592 return PRI(pri, sub) << (DCHIPCBASE + (channel - DCHMIN) * DCHIPCSTEP); 593 } 594 595 /* Return encoded external interrupt priorities for combining with a register. */ 596 597 uint32_t INT_IPC_PRI(int int_num, uint8_t pri, uint8_t sub) 598 { 599 (void) int_num; 600 return PRI(pri, sub) << INTIPCBASE; 601 } 602 603 /* Return the external interrupt priorities register. */ 604 605 uint32_t INT_IPC_REG(int int_num) 606 { 607 switch (int_num) 608 { 609 case 0: return INT0IPC; 610 case 1: return INT1IPC; 611 case 2: return INT2IPC; 612 case 3: return INT3IPC; 613 case 4: return INT4IPC; 614 default: return 0; /* should not occur */ 615 } 616 } 617 618 /* Return the external interrupt flags for combining with a register. */ 619 620 int INT_INT_FLAGS(int int_num, uint8_t flags) 621 { 622 return (flags & 0b1) << (INTINTBASE + (int_num - INTMIN) * INTINTSTEP); 623 } 624 625 /* Return encoded output compare interrupt priorities for combining with a register. */ 626 627 uint32_t OC_IPC_PRI(int unit, uint8_t pri, uint8_t sub) 628 { 629 (void) unit; 630 return PRI(pri, sub) << OCIPCBASE; 631 } 632 633 /* Return the output compare interrupt priorities register. */ 634 635 uint32_t OC_IPC_REG(int unit) 636 { 637 switch (unit) 638 { 639 case 1: return OC1IPC; 640 case 2: return OC2IPC; 641 case 3: return OC3IPC; 642 case 4: return OC4IPC; 643 case 5: return OC5IPC; 644 default: return 0; /* should not occur */ 645 } 646 } 647 648 /* Return the output compare interrupt flags for combining with a register. */ 649 650 int OC_INT_FLAGS(int unit, uint8_t flags) 651 { 652 return (flags & 0b1) << (OCINTBASE + (unit - OCMIN) * OCINTSTEP); 653 } 654 655 /* Return encoded parallel mode interrupt priorities for combining with a register. */ 656 657 uint32_t PM_IPC_PRI(int port, uint8_t pri, uint8_t sub) 658 { 659 (void) port; 660 return PRI(pri, sub) << PMIPCBASE; 661 } 662 663 /* Return the parallel mode interrupt priorities register. */ 664 665 uint32_t PM_IPC_REG(int port) 666 { 667 (void) port; 668 return PMIPC; 669 } 670 671 /* Return the parallel mode interrupt flags for combining with a register. */ 672 673 int PM_INT_FLAGS(int port, uint8_t flags) 674 { 675 return (flags & 0b11) << (PMINTBASE + (port - PMMIN) * PMINTSTEP); 676 } 677 678 /* Return encoded timer interrupt priorities for combining with a register. */ 679 680 uint32_t TIMER_IPC_PRI(int timer, uint8_t pri, uint8_t sub) 681 { 682 (void) timer; 683 return PRI(pri, sub) << TIMERIPCBASE; 684 } 685 686 /* Return the timer interrupt priorities register. */ 687 688 uint32_t TIMER_IPC_REG(int timer) 689 { 690 switch (timer) 691 { 692 case 1: return TIMER1IPC; 693 case 2: return TIMER2IPC; 694 case 3: return TIMER3IPC; 695 case 4: return TIMER4IPC; 696 case 5: return TIMER5IPC; 697 default: return 0; /* should not occur */ 698 } 699 } 700 701 /* Return the timer interrupt flags for combining with a register. */ 702 703 int TIMER_INT_FLAGS(int timer, uint8_t flags) 704 { 705 return (flags & 0b1) << (TIMERINTBASE + (timer - TIMERMIN) * TIMERINTSTEP); 706 } 707 708 /* Return encoded UART interrupt priorities for combining with a register. */ 709 710 uint32_t UART_IPC_PRI(int uart, uint8_t pri, uint8_t sub) 711 { 712 return PRI(pri, sub) << (uart == 1 ? UART1IPCBASE : UART2IPCBASE); 713 } 714 715 /* Return the UART interrupt priorities register. */ 716 717 uint32_t UART_IPC_REG(int uart) 718 { 719 return uart == 1 ? UART1IPC : UART2IPC; 720 } 721 722 /* Return the UART interrupt flags for combining with a register. */ 723 724 int UART_INT_FLAGS(int uart, uint8_t flags) 725 { 726 return (flags & 0b111) << (UARTINTBASE + (uart - UARTMIN) * UARTINTSTEP); 727 }