paul@0 | 1 | #include "cpu.h" |
paul@0 | 2 | #include "pic32_c.h" |
paul@3 | 3 | #include "init.h" |
paul@0 | 4 | |
paul@0 | 5 | |
paul@0 | 6 | |
paul@0 | 7 | /* Basic memory and pin initialisation. */ |
paul@0 | 8 | |
paul@0 | 9 | void init_memory(void) |
paul@0 | 10 | { |
paul@0 | 11 | /* |
paul@0 | 12 | Configure RAM. |
paul@0 | 13 | See: http://microchipdeveloper.com/32bit:mx-arch-exceptions-processor-initialization |
paul@0 | 14 | */ |
paul@0 | 15 | |
paul@0 | 16 | uint32_t config = REG(BMXCON); |
paul@0 | 17 | |
paul@0 | 18 | /* Set zero wait states for address setup. */ |
paul@0 | 19 | |
paul@0 | 20 | config &= ~(1 << 6); /* BMXCON<6> = BMXWSDRM = 0 */ |
paul@0 | 21 | |
paul@0 | 22 | /* Set bus arbitration mode. */ |
paul@0 | 23 | |
paul@0 | 24 | config &= ~0b111; |
paul@0 | 25 | config |= 0b010; /* BMXCON<2:0> = BMXARB<2:0> = 2 */ |
paul@0 | 26 | |
paul@0 | 27 | REG(BMXCON) = config; |
paul@0 | 28 | } |
paul@0 | 29 | |
paul@0 | 30 | void init_pins(void) |
paul@0 | 31 | { |
paul@0 | 32 | /* DEVCFG0<2> also needs setting to 0 before the program is run. */ |
paul@0 | 33 | |
paul@0 | 34 | CLR_REG(CFGCON, 1 << 3); /* CFGCON<3> = JTAGEN = 0 */ |
paul@0 | 35 | } |
paul@0 | 36 | |
paul@0 | 37 | void init_outputs(void) |
paul@0 | 38 | { |
paul@0 | 39 | /* Remove analogue features from pins. */ |
paul@0 | 40 | |
paul@0 | 41 | REG(ANSELA) = 0; |
paul@0 | 42 | REG(ANSELB) = 0; |
paul@0 | 43 | |
paul@3 | 44 | /* Set pins as outputs. */ |
paul@3 | 45 | |
paul@0 | 46 | REG(TRISA) = 0; |
paul@0 | 47 | REG(TRISB) = 0; |
paul@0 | 48 | |
paul@3 | 49 | /* Clear outputs. */ |
paul@3 | 50 | |
paul@0 | 51 | REG(PORTA) = 0; |
paul@0 | 52 | REG(PORTB) = 0; |
paul@0 | 53 | } |
paul@0 | 54 | |
paul@0 | 55 | |
paul@0 | 56 | |
paul@0 | 57 | /* Peripheral pin configuration. */ |
paul@0 | 58 | |
paul@0 | 59 | void config_uart(void) |
paul@0 | 60 | { |
paul@3 | 61 | /* NOTE: Configuring UART1 for specific pins. */ |
paul@3 | 62 | |
paul@0 | 63 | /* Map U1RX to RPB13. */ |
paul@0 | 64 | |
paul@0 | 65 | REG(U1RXR) = 0b0011; /* U1RXR<3:0> = 0011 (RPB13) */ |
paul@0 | 66 | |
paul@0 | 67 | /* Map U1TX to RPB15. */ |
paul@0 | 68 | |
paul@0 | 69 | REG(RPB15R) = 0b0001; /* RPB15R<3:0> = 0001 (U1TX) */ |
paul@0 | 70 | |
paul@0 | 71 | /* Set RPB13 to input. */ |
paul@0 | 72 | |
paul@0 | 73 | SET_REG(TRISB, 1 << 13); |
paul@0 | 74 | } |
paul@0 | 75 | |
paul@0 | 76 | void lock_config(void) |
paul@0 | 77 | { |
paul@0 | 78 | SET_REG(CFGCON, 1 << 13); /* IOLOCK = 1 */ |
paul@0 | 79 | |
paul@0 | 80 | /* Lock the configuration again. */ |
paul@0 | 81 | |
paul@0 | 82 | REG(SYSKEY) = 0x33333333; |
paul@0 | 83 | } |
paul@0 | 84 | |
paul@0 | 85 | void unlock_config(void) |
paul@0 | 86 | { |
paul@0 | 87 | /* Unlock the configuration register bits. */ |
paul@0 | 88 | |
paul@0 | 89 | REG(SYSKEY) = 0; |
paul@0 | 90 | REG(SYSKEY) = 0xAA996655; |
paul@0 | 91 | REG(SYSKEY) = 0x556699AA; |
paul@0 | 92 | |
paul@0 | 93 | CLR_REG(CFGCON, 1 << 13); /* IOLOCK = 0 */ |
paul@0 | 94 | } |
paul@0 | 95 | |
paul@0 | 96 | |
paul@0 | 97 | |
paul@0 | 98 | /* Convenience operations. */ |
paul@0 | 99 | |
paul@0 | 100 | void interrupts_on(void) |
paul@0 | 101 | { |
paul@0 | 102 | init_interrupts(); |
paul@0 | 103 | enable_interrupts(); |
paul@0 | 104 | handle_error_level(); |
paul@0 | 105 | } |
paul@0 | 106 | |
paul@0 | 107 | |
paul@0 | 108 | |
paul@3 | 109 | /* DMA configuration. */ |
paul@3 | 110 | |
paul@3 | 111 | void init_dma(void) |
paul@3 | 112 | { |
paul@3 | 113 | /* Disable DMA interrupts. */ |
paul@3 | 114 | |
paul@3 | 115 | CLR_REG(DMAIEC, 0b1111 << DMAINTBASE); /* DMA3IE...DMA0IE = 0 */ |
paul@3 | 116 | |
paul@3 | 117 | /* Clear DMA interrupt flags. */ |
paul@3 | 118 | |
paul@3 | 119 | CLR_REG(DMAIFS, 0b1111 << DMAINTBASE); /* DMA3IF...DMA0IF = 0 */ |
paul@3 | 120 | |
paul@3 | 121 | /* Enable DMA. */ |
paul@3 | 122 | |
paul@3 | 123 | SET_REG(DMACON, 1 << 15); |
paul@3 | 124 | } |
paul@3 | 125 | |
paul@3 | 126 | /* Initialise the given channel. */ |
paul@0 | 127 | |
paul@3 | 128 | void dma_init(int channel, int auto_enable, uint8_t pri) |
paul@0 | 129 | { |
paul@3 | 130 | if (channel > DCHMAX) |
paul@3 | 131 | return; |
paul@3 | 132 | |
paul@3 | 133 | /* Initialise a channel. */ |
paul@3 | 134 | |
paul@3 | 135 | REG(DMA_REG(channel, DCHxCON)) = (auto_enable ? (1 << 4) : 0) | (pri & 0b11); |
paul@3 | 136 | REG(DMA_REG(channel, DCHxECON)) = 0; |
paul@3 | 137 | REG(DMA_REG(channel, DCHxINT)) = 0; |
paul@3 | 138 | } |
paul@3 | 139 | |
paul@3 | 140 | /* Configure a channel's initiation interrupt. */ |
paul@3 | 141 | |
paul@3 | 142 | void dma_set_interrupt(int channel, uint8_t int_num, int enable) |
paul@3 | 143 | { |
paul@3 | 144 | if (channel > DCHMAX) |
paul@3 | 145 | return; |
paul@0 | 146 | |
paul@3 | 147 | /* Allow an interrupt to trigger the transfer. */ |
paul@3 | 148 | |
paul@3 | 149 | REG(DMA_REG(channel, DCHxECON)) = (int_num << 8) | |
paul@3 | 150 | ((enable ? 1 : 0) << 4); |
paul@3 | 151 | } |
paul@3 | 152 | |
paul@3 | 153 | /* Set a channel's transfer parameters. */ |
paul@3 | 154 | |
paul@3 | 155 | void dma_set_transfer(int channel, |
paul@3 | 156 | uint32_t source_start_address, uint16_t source_size, |
paul@3 | 157 | uint32_t destination_start_address, uint16_t destination_size, |
paul@3 | 158 | uint16_t cell_size) |
paul@3 | 159 | { |
paul@3 | 160 | if (channel > DCHMAX) |
paul@3 | 161 | return; |
paul@0 | 162 | |
paul@3 | 163 | REG(DMA_REG(channel, DCHxSSIZ)) = source_size; |
paul@3 | 164 | REG(DMA_REG(channel, DCHxSSA)) = source_start_address; |
paul@3 | 165 | REG(DMA_REG(channel, DCHxDSIZ)) = destination_size; |
paul@3 | 166 | REG(DMA_REG(channel, DCHxDSA)) = destination_start_address; |
paul@3 | 167 | REG(DMA_REG(channel, DCHxCSIZ)) = cell_size; |
paul@3 | 168 | } |
paul@3 | 169 | |
paul@3 | 170 | /* Configure interrupts caused by the channel. */ |
paul@0 | 171 | |
paul@3 | 172 | void dma_init_interrupt(int channel, uint8_t conditions, |
paul@3 | 173 | uint8_t int_pri, uint8_t int_sub) |
paul@3 | 174 | { |
paul@3 | 175 | if (channel > DCHMAX) |
paul@3 | 176 | return; |
paul@0 | 177 | |
paul@3 | 178 | /* Produce an interrupt for the provided conditions. */ |
paul@3 | 179 | |
paul@3 | 180 | REG(DMA_REG(channel, DCHxINT)) = conditions << 16; |
paul@3 | 181 | |
paul@3 | 182 | /* Set interrupt priorities. */ |
paul@3 | 183 | |
paul@3 | 184 | REG(DMAIPC) = (REG(DMAIPC) & ~(DMA_IPC_PRI(channel, 7, 3))) | |
paul@3 | 185 | DMA_IPC_PRI(channel, int_pri, int_sub); |
paul@0 | 186 | |
paul@0 | 187 | /* Enable interrupt. */ |
paul@0 | 188 | |
paul@3 | 189 | SET_REG(DMAIEC, 1 << (channel + DMAINTBASE)); |
paul@3 | 190 | } |
paul@3 | 191 | |
paul@3 | 192 | /* Enable a DMA channel. */ |
paul@3 | 193 | |
paul@3 | 194 | void dma_on(int channel) |
paul@3 | 195 | { |
paul@3 | 196 | if (channel > DCHMAX) |
paul@3 | 197 | return; |
paul@3 | 198 | |
paul@3 | 199 | /* Enable channel. */ |
paul@3 | 200 | |
paul@3 | 201 | SET_REG(DMA_REG(channel, DCHxCON), 1 << 7); |
paul@3 | 202 | } |
paul@3 | 203 | |
paul@3 | 204 | /* UART configuration. */ |
paul@3 | 205 | |
paul@3 | 206 | void uart_init(int channel, uint32_t baudrate) |
paul@3 | 207 | { |
paul@3 | 208 | /* NOTE: Configured in the initial payload. */ |
paul@3 | 209 | |
paul@3 | 210 | uint32_t FPB = 24000000; |
paul@3 | 211 | |
paul@3 | 212 | if ((channel < UARTMIN) || (channel > UARTMAX)) |
paul@3 | 213 | return; |
paul@3 | 214 | |
paul@3 | 215 | /* Disable the UART (ON). */ |
paul@3 | 216 | |
paul@3 | 217 | CLR_REG(UART_REG(channel, UxMODE), 1 << 15); |
paul@3 | 218 | |
paul@3 | 219 | /* Set the baud rate. For example: |
paul@3 | 220 | |
paul@3 | 221 | UxBRG<15:0> = BRG |
paul@3 | 222 | = (FPB / (16 * baudrate)) - 1 |
paul@3 | 223 | = (24000000 / (16 * 115200)) - 1 |
paul@3 | 224 | = 12 |
paul@3 | 225 | */ |
paul@3 | 226 | |
paul@3 | 227 | REG(UART_REG(channel, UxBRG)) = (FPB / (16 * baudrate)) - 1; |
paul@3 | 228 | |
paul@3 | 229 | /* Disable interrupt (UxRIE) and clear flag (UxRIF). */ |
paul@3 | 230 | |
paul@6 | 231 | CLR_REG(UARTIEC, UART_INT_FLAGS(channel, UxRIE)); |
paul@6 | 232 | CLR_REG(UARTIFS, UART_INT_FLAGS(channel, UxRIF)); |
paul@3 | 233 | } |
paul@3 | 234 | |
paul@3 | 235 | /* Configure interrupts caused by the UART. */ |
paul@3 | 236 | |
paul@3 | 237 | void uart_init_interrupt(int channel, uint8_t pri, uint8_t sub) |
paul@3 | 238 | { |
paul@3 | 239 | if ((channel < UARTMIN) || (channel > UARTMAX)) |
paul@3 | 240 | return; |
paul@3 | 241 | |
paul@3 | 242 | /* Set priorities: UxIP = pri; UxIS = sub */ |
paul@3 | 243 | |
paul@3 | 244 | REG(UART_IPC_REG(channel)) = (REG(UART_IPC_REG(channel)) & ~UART_IPC_PRI(channel, 7, 3)) | |
paul@3 | 245 | UART_IPC_PRI(channel, pri, sub); |
paul@3 | 246 | |
paul@3 | 247 | /* Enable interrupt (UxRIE). */ |
paul@3 | 248 | |
paul@6 | 249 | SET_REG(UARTIEC, UART_INT_FLAGS(channel, UxRIE)); |
paul@3 | 250 | } |
paul@3 | 251 | |
paul@3 | 252 | /* Enable a UART. */ |
paul@3 | 253 | |
paul@3 | 254 | void uart_on(int channel) |
paul@3 | 255 | { |
paul@3 | 256 | if ((channel < UARTMIN) || (channel > UARTMAX)) |
paul@3 | 257 | return; |
paul@3 | 258 | |
paul@3 | 259 | /* Enable receive (URXEN) and transmit (UTXEN). */ |
paul@3 | 260 | |
paul@3 | 261 | SET_REG(UART_REG(channel, UxSTA), (1 << 12) | (1 << 10)); |
paul@0 | 262 | |
paul@0 | 263 | /* Start UART. */ |
paul@0 | 264 | |
paul@3 | 265 | SET_REG(UART_REG(channel, UxMODE), 1 << 15); |
paul@3 | 266 | } |
paul@3 | 267 | |
paul@3 | 268 | |
paul@3 | 269 | |
paul@3 | 270 | /* Utility functions. */ |
paul@3 | 271 | |
paul@3 | 272 | /* Return encoded interrupt priorities. */ |
paul@3 | 273 | |
paul@3 | 274 | static uint8_t PRI(uint8_t pri, uint8_t sub) |
paul@3 | 275 | { |
paul@3 | 276 | return ((pri & 0b111) << 2) | (sub & 0b11); |
paul@3 | 277 | } |
paul@3 | 278 | |
paul@3 | 279 | /* Return encoded DMA interrupt priorities for combining with a register. */ |
paul@3 | 280 | |
paul@3 | 281 | uint32_t DMA_IPC_PRI(int channel, uint8_t pri, uint8_t sub) |
paul@3 | 282 | { |
paul@3 | 283 | return PRI(pri, sub) << (DCHIPCBASE + (channel - DCHMIN) * DCHIPCSTEP); |
paul@0 | 284 | } |
paul@3 | 285 | |
paul@3 | 286 | /* Return encoded UART interrupt priorities for combining with a register. */ |
paul@3 | 287 | |
paul@3 | 288 | uint32_t UART_IPC_PRI(int channel, uint8_t pri, uint8_t sub) |
paul@3 | 289 | { |
paul@3 | 290 | return PRI(pri, sub) << (channel == 1 ? UART1IPCBASE : UART2IPCBASE); |
paul@3 | 291 | } |
paul@3 | 292 | |
paul@3 | 293 | /* Return the UART interrupt priorities register. */ |
paul@3 | 294 | |
paul@3 | 295 | uint32_t UART_IPC_REG(int channel) |
paul@3 | 296 | { |
paul@3 | 297 | return channel == 1 ? UART1IPC : UART2IPC; |
paul@3 | 298 | } |
paul@3 | 299 | |
paul@3 | 300 | /* Return the UART interrupt flags shift offset. */ |
paul@3 | 301 | |
paul@6 | 302 | int UART_INT_FLAGS(int channel, uint8_t flags) |
paul@3 | 303 | { |
paul@6 | 304 | return (flags & 0b111) << (UARTINTBASE + (channel - UARTMIN) * UARTINTSTEP); |
paul@3 | 305 | } |