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. */ 116 117 CLR_REG(DMAIEC, 0b1111 << DMAINTBASE); /* DMA3IE...DMA0IE = 0 */ 118 119 /* Clear DMA interrupt flags. */ 120 121 CLR_REG(DMAIFS, 0b1111 << DMAINTBASE); /* DMA3IF...DMA0IF = 0 */ 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 auto-enable mode. */ 143 144 void dma_set_auto_enable(int channel, int auto_enable) 145 { 146 (auto_enable ? SET_REG : CLR_REG)(DMA_REG(channel, DCHxCON), 1 << 4); 147 } 148 149 /* Set the channel chaining mode. */ 150 151 void dma_set_chaining(int channel, enum dma_chain chain) 152 { 153 (chain != dma_chain_none ? 154 SET_REG : CLR_REG)(DMA_REG(channel, DCHxCON), 1 << 5); 155 156 (chain == dma_chain_next ? 157 SET_REG : CLR_REG)(DMA_REG(channel, DCHxCON), 1 << 8); 158 } 159 160 /* Configure a channel's initiation interrupt. */ 161 162 void dma_set_interrupt(int channel, uint8_t int_num, int enable) 163 { 164 if ((channel < DCHMIN) || (channel > DCHMAX)) 165 return; 166 167 /* Allow an interrupt to trigger the transfer. */ 168 169 REG(DMA_REG(channel, DCHxECON)) = (int_num << 8) | 170 ((enable ? 1 : 0) << 4); 171 } 172 173 /* Set a channel's transfer parameters. */ 174 175 void dma_set_transfer(int channel, 176 uint32_t source_start_address, uint16_t source_size, 177 uint32_t destination_start_address, uint16_t destination_size, 178 uint16_t cell_size) 179 { 180 if ((channel < DCHMIN) || (channel > DCHMAX)) 181 return; 182 183 REG(DMA_REG(channel, DCHxSSIZ)) = source_size; 184 REG(DMA_REG(channel, DCHxSSA)) = source_start_address; 185 REG(DMA_REG(channel, DCHxDSIZ)) = destination_size; 186 REG(DMA_REG(channel, DCHxDSA)) = destination_start_address; 187 REG(DMA_REG(channel, DCHxCSIZ)) = cell_size; 188 } 189 190 /* Configure interrupts caused by the channel. */ 191 192 void dma_init_interrupt(int channel, uint8_t conditions, 193 uint8_t pri, uint8_t sub) 194 { 195 if ((channel < DCHMIN) || (channel > DCHMAX)) 196 return; 197 198 /* Disable channel interrupt and clear interrupt flag. */ 199 200 CLR_REG(DMAIEC, DMA_INT_FLAGS(channel, 1)); 201 CLR_REG(DMAIFS, DMA_INT_FLAGS(channel, 1)); 202 203 /* Produce an interrupt for the provided conditions. */ 204 205 REG(DMA_REG(channel, DCHxINT)) = conditions << 16; 206 207 /* Set interrupt priorities. */ 208 209 REG(DMAIPC) = (REG(DMAIPC) & 210 ~(DMA_IPC_PRI(channel, 7, 3))) | 211 DMA_IPC_PRI(channel, pri, sub); 212 213 /* Enable interrupt. */ 214 215 SET_REG(DMAIEC, DMA_INT_FLAGS(channel, 1)); 216 } 217 218 /* Enable a DMA channel. */ 219 220 void dma_on(int channel) 221 { 222 if ((channel < DCHMIN) || (channel > DCHMAX)) 223 return; 224 225 /* Enable channel. */ 226 227 SET_REG(DMA_REG(channel, DCHxCON), 1 << 7); 228 } 229 230 231 232 /* Output compare configuration. */ 233 234 void oc_init(int unit, uint8_t mode, int timer) 235 { 236 if ((unit < OCMIN) || (unit > OCMAX)) 237 return; 238 239 REG(OC_REG(unit, OCxCON)) = (timer == 3 ? (1 << 3) : 0) | (mode & 0b111); 240 } 241 242 /* Set the start value for the pulse. */ 243 244 void oc_set_pulse(int unit, uint32_t start) 245 { 246 if ((unit < OCMIN) || (unit > OCMAX)) 247 return; 248 249 REG(OC_REG(unit, OCxR)) = start; 250 } 251 252 /* Set the end value for the pulse. */ 253 254 void oc_set_pulse_end(int unit, uint32_t end) 255 { 256 if ((unit < OCMIN) || (unit > OCMAX)) 257 return; 258 259 REG(OC_REG(unit, OCxRS)) = end; 260 } 261 262 /* Configure interrupts caused by the unit. */ 263 264 void oc_init_interrupt(int unit, uint8_t pri, uint8_t sub) 265 { 266 if ((unit < OCMIN) || (unit > OCMAX)) 267 return; 268 269 /* Disable interrupt and clear interrupt flag. */ 270 271 CLR_REG(OCIEC, OC_INT_FLAGS(unit, OCxIE)); 272 CLR_REG(OCIFS, OC_INT_FLAGS(unit, OCxIF)); 273 274 /* Set interrupt priorities. */ 275 276 REG(OC_IPC_REG(unit)) = (REG(OC_IPC_REG(unit)) & 277 ~(OC_IPC_PRI(unit, 7, 3))) | 278 OC_IPC_PRI(unit, pri, sub); 279 280 /* Enable interrupt. */ 281 282 SET_REG(OCIEC, OC_INT_FLAGS(unit, OCxIE)); 283 } 284 285 /* Enable a unit. */ 286 287 void oc_on(int unit) 288 { 289 if ((unit < OCMIN) || (unit > OCMAX)) 290 return; 291 292 SET_REG(OC_REG(unit, OCxCON), 1 << 15); 293 } 294 295 296 297 /* Timer configuration. */ 298 299 void timer_init(int timer, uint8_t prescale, uint16_t limit) 300 { 301 /* NOTE: Should convert from the real prescale value. */ 302 303 REG(TIMER_REG(timer, TxCON)) = (prescale & 0b111) << 4; 304 REG(TIMER_REG(timer, TMRx)) = 0; 305 REG(TIMER_REG(timer, PRx)) = limit; 306 } 307 308 /* Configure interrupts caused by the timer. */ 309 310 void timer_init_interrupt(int timer, uint8_t pri, uint8_t sub) 311 { 312 if ((timer < TIMERMIN) || (timer > TIMERMAX)) 313 return; 314 315 /* Disable interrupt and clear interrupt flag. */ 316 317 CLR_REG(TIMERIEC, TIMER_INT_FLAGS(timer, TxIE)); 318 CLR_REG(TIMERIFS, TIMER_INT_FLAGS(timer, TxIF)); 319 320 /* Set interrupt priorities. */ 321 322 REG(TIMER_IPC_REG(timer)) = (REG(TIMER_IPC_REG(timer)) & 323 ~(TIMER_IPC_PRI(timer, 7, 3))) | 324 TIMER_IPC_PRI(timer, pri, sub); 325 326 /* Enable interrupt. */ 327 328 SET_REG(TIMERIEC, TIMER_INT_FLAGS(timer, TxIE)); 329 } 330 331 /* Enable a timer. */ 332 333 void timer_on(int timer) 334 { 335 if ((timer < TIMERMIN) || (timer > TIMERMAX)) 336 return; 337 338 SET_REG(TIMER_REG(timer, TxCON), 1 << 15); 339 } 340 341 342 343 /* UART configuration. */ 344 345 void uart_init(int uart, uint32_t baudrate) 346 { 347 /* NOTE: Configured in the initial payload. */ 348 349 uint32_t FPB = 24000000; 350 351 if ((uart < UARTMIN) || (uart > UARTMAX)) 352 return; 353 354 /* Disable the UART (ON). */ 355 356 CLR_REG(UART_REG(uart, UxMODE), 1 << 15); 357 358 /* Set the baud rate. For example: 359 360 UxBRG<15:0> = BRG 361 = (FPB / (16 * baudrate)) - 1 362 = (24000000 / (16 * 115200)) - 1 363 = 12 364 */ 365 366 REG(UART_REG(uart, UxBRG)) = (FPB / (16 * baudrate)) - 1; 367 } 368 369 /* Configure interrupts caused by the UART. */ 370 371 void uart_init_interrupt(int uart, uint8_t conditions, 372 uint8_t pri, uint8_t sub) 373 { 374 if ((uart < UARTMIN) || (uart > UARTMAX)) 375 return; 376 377 /* Disable interrupts and clear interrupt flags. */ 378 379 CLR_REG(UARTIEC, UART_INT_FLAGS(uart, UxTIE | UxRIE | UxEIE)); 380 CLR_REG(UARTIFS, UART_INT_FLAGS(uart, UxTIF | UxRIF | UxEIF)); 381 382 /* Set priorities: UxIP = pri; UxIS = sub */ 383 384 REG(UART_IPC_REG(uart)) = (REG(UART_IPC_REG(uart)) & 385 ~UART_IPC_PRI(uart, 7, 3)) | 386 UART_IPC_PRI(uart, pri, sub); 387 388 /* Enable interrupts. */ 389 390 SET_REG(UARTIEC, UART_INT_FLAGS(uart, conditions)); 391 } 392 393 /* Enable a UART. */ 394 395 void uart_on(int uart) 396 { 397 if ((uart < UARTMIN) || (uart > UARTMAX)) 398 return; 399 400 /* Enable receive (URXEN) and transmit (UTXEN). */ 401 402 SET_REG(UART_REG(uart, UxSTA), (1 << 12) | (1 << 10)); 403 404 /* Start UART. */ 405 406 SET_REG(UART_REG(uart, UxMODE), 1 << 15); 407 } 408 409 410 411 /* Utility functions. */ 412 413 /* Return encoded interrupt priorities. */ 414 415 static uint8_t PRI(uint8_t pri, uint8_t sub) 416 { 417 return ((pri & 0b111) << 2) | (sub & 0b11); 418 } 419 420 /* Return the DMA interrupt flags for combining with a register. */ 421 422 int DMA_INT_FLAGS(int channel, uint8_t flags) 423 { 424 return (flags & 0b1) << (DMAINTBASE + (channel - DCHMIN)); 425 } 426 427 /* Return encoded DMA interrupt priorities for combining with a register. */ 428 429 uint32_t DMA_IPC_PRI(int channel, uint8_t pri, uint8_t sub) 430 { 431 return PRI(pri, sub) << (DCHIPCBASE + (channel - DCHMIN) * DCHIPCSTEP); 432 } 433 434 /* Return encoded output compare interrupt priorities for combining with a register. */ 435 436 uint32_t OC_IPC_PRI(int unit, uint8_t pri, uint8_t sub) 437 { 438 (void) unit; 439 return PRI(pri, sub) << OCIPCBASE; 440 } 441 442 /* Return the output compare interrupt priorities register. */ 443 444 uint32_t OC_IPC_REG(int unit) 445 { 446 switch (unit) 447 { 448 case 1: return OC1IPC; 449 case 2: return OC2IPC; 450 case 3: return OC3IPC; 451 case 4: return OC4IPC; 452 case 5: return OC5IPC; 453 default: return 0; /* should not occur */ 454 } 455 } 456 457 /* Return the output compare interrupt flags for combining with a register. */ 458 459 int OC_INT_FLAGS(int unit, uint8_t flags) 460 { 461 return (flags & 0b1) << (OCINTBASE + (unit - OCMIN) * OCINTSTEP); 462 } 463 464 /* Return encoded timer interrupt priorities for combining with a register. */ 465 466 uint32_t TIMER_IPC_PRI(int timer, uint8_t pri, uint8_t sub) 467 { 468 (void) timer; 469 return PRI(pri, sub) << TIMERIPCBASE; 470 } 471 472 /* Return the timer interrupt priorities register. */ 473 474 uint32_t TIMER_IPC_REG(int timer) 475 { 476 switch (timer) 477 { 478 case 1: return TIMER1IPC; 479 case 2: return TIMER2IPC; 480 case 3: return TIMER3IPC; 481 case 4: return TIMER4IPC; 482 case 5: return TIMER5IPC; 483 default: return 0; /* should not occur */ 484 } 485 } 486 487 /* Return the timer interrupt flags for combining with a register. */ 488 489 int TIMER_INT_FLAGS(int timer, uint8_t flags) 490 { 491 return (flags & 0b1) << (TIMERINTBASE + (timer - TIMERMIN) * TIMERINTSTEP); 492 } 493 494 /* Return encoded UART interrupt priorities for combining with a register. */ 495 496 uint32_t UART_IPC_PRI(int uart, uint8_t pri, uint8_t sub) 497 { 498 return PRI(pri, sub) << (uart == 1 ? UART1IPCBASE : UART2IPCBASE); 499 } 500 501 /* Return the UART interrupt priorities register. */ 502 503 uint32_t UART_IPC_REG(int uart) 504 { 505 return uart == 1 ? UART1IPC : UART2IPC; 506 } 507 508 /* Return the UART interrupt flags for combining with a register. */ 509 510 int UART_INT_FLAGS(int uart, uint8_t flags) 511 { 512 return (flags & 0b111) << (UARTINTBASE + (uart - UARTMIN) * UARTINTSTEP); 513 }