serial_vendor.c (15960B)
1 // Copyright 2022 Stefan Kerkmann 2 // SPDX-License-Identifier: GPL-2.0-or-later 3 4 #include "serial_usart.h" 5 #include "serial_protocol.h" 6 #include "hardware/pio.h" 7 #include "hardware/clocks.h" 8 #include "wait.h" 9 #include "debug.h" 10 11 #if !defined(MCU_RP) 12 # error PIO Driver is only available for Raspberry Pi 2040 MCUs! 13 #endif 14 15 static inline bool receive_impl(uint8_t* destination, const size_t size, sysinterval_t timeout); 16 static inline bool send_impl(const uint8_t* source, const size_t size); 17 static inline void pio_serve_interrupt(void); 18 19 #define MSG_PIO_ERROR ((msg_t)(-3)) 20 21 #if defined(SERIAL_PIO_USE_PIO1) 22 static const PIO pio = pio1; 23 24 OSAL_IRQ_HANDLER(RP_PIO1_IRQ_0_HANDLER) { 25 OSAL_IRQ_PROLOGUE(); 26 pio_serve_interrupt(); 27 OSAL_IRQ_EPILOGUE(); 28 } 29 #else 30 static const PIO pio = pio0; 31 32 OSAL_IRQ_HANDLER(RP_PIO0_IRQ_0_HANDLER) { 33 OSAL_IRQ_PROLOGUE(); 34 pio_serve_interrupt(); 35 OSAL_IRQ_EPILOGUE(); 36 } 37 #endif 38 39 #define UART_TX_WRAP_TARGET 0 40 #define UART_TX_WRAP 3 41 42 // clang-format off 43 #if defined(SERIAL_USART_FULL_DUPLEX) 44 static const uint16_t uart_tx_program_instructions[] = { 45 // .wrap_target 46 0x9fa0, // 0: pull block side 1 [7] 47 0xf727, // 1: set x, 7 side 0 [7] 48 0x6001, // 2: out pins, 1 49 0x0642, // 3: jmp x--, 2 [6] 50 // .wrap 51 }; 52 #else 53 static const uint16_t uart_tx_program_instructions[] = { 54 // .wrap_target 55 0x9fa0, // 0: pull block side 1 [7] 56 0xf727, // 1: set x, 7 side 0 [7] 57 0x6081, // 2: out pindirs, 1 58 0x0642, // 3: jmp x--, 2 [6] 59 // .wrap 60 }; 61 #endif 62 // clang-format on 63 64 static const pio_program_t uart_tx_program = { 65 .instructions = uart_tx_program_instructions, 66 .length = 4, 67 .origin = -1, 68 }; 69 70 #define UART_RX_WRAP_TARGET 0 71 #define UART_RX_WRAP 8 72 73 // clang-format off 74 static const uint16_t uart_rx_program_instructions[] = { 75 // .wrap_target 76 0x2020, // 0: wait 0 pin, 0 77 0xea27, // 1: set x, 7 [10] 78 0x4001, // 2: in pins, 1 79 0x0642, // 3: jmp x--, 2 [6] 80 0x00c8, // 4: jmp pin, 8 81 0xc020, // 5: irq wait 0 82 0x20a0, // 6: wait 1 pin, 0 83 0x0000, // 7: jmp 0 84 0x8020, // 8: push block 85 // .wrap 86 }; 87 // clang-format on 88 89 static const pio_program_t uart_rx_program = { 90 .instructions = uart_rx_program_instructions, 91 .length = 9, 92 .origin = -1, 93 }; 94 95 thread_reference_t rx_thread = NULL; 96 static int rx_state_machine = -1; 97 98 thread_reference_t tx_thread = NULL; 99 static int tx_state_machine = -1; 100 101 void pio_serve_interrupt(void) { 102 uint32_t irqs = pio->ints0; 103 104 // The RX FIFO is not empty any more, therefore wake any sleeping rx thread 105 if (irqs & (PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS << rx_state_machine)) { 106 // Disable rx not empty interrupt 107 pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, false); 108 109 osalSysLockFromISR(); 110 osalThreadResumeI(&rx_thread, MSG_OK); 111 osalSysUnlockFromISR(); 112 } 113 114 // The TX FIFO is not full any more, therefore wake any sleeping tx thread 115 if (irqs & (PIO_IRQ0_INTF_SM0_TXNFULL_BITS << tx_state_machine)) { 116 // Disable tx not full interrupt 117 pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, false); 118 osalSysLockFromISR(); 119 osalThreadResumeI(&tx_thread, MSG_OK); 120 osalSysUnlockFromISR(); 121 } 122 123 // IRQ 0 is set on framing or break errors by the rx state machine 124 if (pio_interrupt_get(pio, 0UL)) { 125 pio_interrupt_clear(pio, 0UL); 126 127 osalSysLockFromISR(); 128 osalThreadResumeI(&rx_thread, MSG_PIO_ERROR); 129 osalSysUnlockFromISR(); 130 } 131 } 132 133 #if !defined(SERIAL_USART_FULL_DUPLEX) 134 // The internal pull-ups of the RP2040 are rather weakish with a range of 50k to 135 // 80k, which in turn do not provide enough current to guarantee fast signal rise 136 // times with a parasitic capacitance of greater than 100pf. In real world 137 // applications, like split keyboards which might have vias in the signal path 138 // or long PCB traces, this prevents a successful communication. The solution 139 // is to temporarily augment the weak pull ups from the receiving side by 140 // driving the tx pin high. On the receiving side the lowest possible drive 141 // strength is chosen because the transmitting side must still be able to drive 142 // the signal low. With this configuration the rise times are fast enough and 143 // the generated low level with 360mV will generate a logical zero. 144 static void __no_inline_not_in_flash_func(enter_rx_state)(void) { 145 osalSysLock(); 146 // Wait for the transmitting state machines FIFO to run empty. At this point 147 // the last byte has been pulled from the transmitting state machines FIFO 148 // into the output shift register. We have to wait a tiny bit more until 149 // this byte is transmitted, before we can turn on the receiving state 150 // machine again. 151 while (!pio_sm_is_tx_fifo_empty(pio, tx_state_machine)) { 152 } 153 // Wait for ~11 bits, 1 start bit + 8 data bits + 1 stop bit + 1 bit 154 // headroom. 155 wait_us(1000000U * 11U / SERIAL_USART_SPEED); 156 // Disable tx state machine to not interfere with our tx pin manipulation 157 pio_sm_set_enabled(pio, tx_state_machine, false); 158 gpio_set_drive_strength(SERIAL_USART_TX_PIN, GPIO_DRIVE_STRENGTH_2MA); 159 pio_sm_set_pins_with_mask(pio, tx_state_machine, 1U << SERIAL_USART_TX_PIN, 1U << SERIAL_USART_TX_PIN); 160 pio_sm_set_consecutive_pindirs(pio, tx_state_machine, SERIAL_USART_TX_PIN, 1U, false); 161 pio_sm_set_enabled(pio, rx_state_machine, true); 162 osalSysUnlock(); 163 } 164 165 static void __no_inline_not_in_flash_func(leave_rx_state)(void) { 166 osalSysLock(); 167 // In Half-duplex operation the tx pin dual-functions as sender and 168 // receiver. To not receive the data we will send, we disable the receiving 169 // state machine. 170 pio_sm_set_enabled(pio, rx_state_machine, false); 171 pio_sm_set_consecutive_pindirs(pio, tx_state_machine, SERIAL_USART_TX_PIN, 1U, true); 172 pio_sm_set_pins_with_mask(pio, tx_state_machine, 0U, 1U << SERIAL_USART_TX_PIN); 173 gpio_set_drive_strength(SERIAL_USART_TX_PIN, GPIO_DRIVE_STRENGTH_12MA); 174 pio_sm_restart(pio, tx_state_machine); 175 pio_sm_set_enabled(pio, tx_state_machine, true); 176 osalSysUnlock(); 177 } 178 #else 179 // All this trickery is gladly not necessary for full-duplex. 180 static inline void enter_rx_state(void) {} 181 static inline void leave_rx_state(void) {} 182 #endif 183 184 /** 185 * @brief Clear the FIFO of the RX state machine. 186 */ 187 inline void serial_transport_driver_clear(void) { 188 osalSysLock(); 189 while (!pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) { 190 pio_sm_clear_fifos(pio, rx_state_machine); 191 } 192 osalSysUnlock(); 193 } 194 195 static inline msg_t sync_tx(sysinterval_t timeout) { 196 msg_t msg = MSG_OK; 197 osalSysLock(); 198 while (pio_sm_is_tx_fifo_full(pio, tx_state_machine)) { 199 pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, true); 200 msg = osalThreadSuspendTimeoutS(&tx_thread, timeout); 201 if (msg < MSG_OK) { 202 pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, false); 203 break; 204 } 205 } 206 osalSysUnlock(); 207 return msg; 208 } 209 210 static inline bool send_impl(const uint8_t* source, const size_t size) { 211 size_t send = 0; 212 msg_t msg; 213 while (send < size) { 214 msg = sync_tx(TIME_MS2I(SERIAL_USART_TIMEOUT)); 215 if (msg < MSG_OK) { 216 return false; 217 } 218 219 osalSysLock(); 220 while (send < size) { 221 if (pio_sm_is_tx_fifo_full(pio, tx_state_machine)) { 222 break; 223 } 224 if (send >= size) { 225 break; 226 } 227 pio_sm_put(pio, tx_state_machine, (uint32_t)(*source)); 228 source++; 229 send++; 230 } 231 osalSysUnlock(); 232 } 233 234 return send == size; 235 } 236 237 /** 238 * @brief Blocking send of buffer with timeout. 239 * 240 * @return true Send success. 241 * @return false Send failed. 242 */ 243 inline bool serial_transport_send(const uint8_t* source, const size_t size) { 244 leave_rx_state(); 245 bool result = send_impl(source, size); 246 enter_rx_state(); 247 248 return result; 249 } 250 251 static inline msg_t sync_rx(sysinterval_t timeout) { 252 msg_t msg = MSG_OK; 253 osalSysLock(); 254 while (pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) { 255 pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, true); 256 msg = osalThreadSuspendTimeoutS(&rx_thread, timeout); 257 if (msg < MSG_OK) { 258 pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, false); 259 break; 260 } 261 } 262 osalSysUnlock(); 263 return msg; 264 } 265 266 static inline bool receive_impl(uint8_t* destination, const size_t size, sysinterval_t timeout) { 267 size_t read = 0U; 268 269 while (read < size) { 270 msg_t msg = sync_rx(timeout); 271 if (msg < MSG_OK) { 272 return false; 273 } 274 osalSysLock(); 275 while (true) { 276 if (pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) { 277 break; 278 } 279 if (read >= size) { 280 break; 281 } 282 *destination++ = *((uint8_t*)&pio->rxf[rx_state_machine] + 3U); 283 read++; 284 } 285 osalSysUnlock(); 286 } 287 288 return read == size; 289 } 290 291 /** 292 * @brief Blocking receive of size * bytes with timeout. 293 * 294 * @return true Receive success. 295 * @return false Receive failed, e.g. by timeout. 296 */ 297 inline bool serial_transport_receive(uint8_t* destination, const size_t size) { 298 return receive_impl(destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT)); 299 } 300 301 /** 302 * @brief Blocking receive of size * bytes. 303 * 304 * @return true Receive success. 305 * @return false Receive failed. 306 */ 307 inline bool serial_transport_receive_blocking(uint8_t* destination, const size_t size) { 308 return receive_impl(destination, size, TIME_INFINITE); 309 } 310 311 static inline void pio_tx_init(pin_t tx_pin) { 312 uint pio_idx = pio_get_index(pio); 313 uint offset = pio_add_program(pio, &uart_tx_program); 314 315 #if defined(SERIAL_USART_FULL_DUPLEX) 316 // clang-format off 317 iomode_t tx_pin_mode = PAL_RP_GPIO_OE | 318 PAL_RP_PAD_SLEWFAST | 319 PAL_RP_PAD_DRIVE4 | 320 (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1); 321 // clang-format on 322 pio_sm_set_pins_with_mask(pio, tx_state_machine, 1U << tx_pin, 1U << tx_pin); 323 pio_sm_set_consecutive_pindirs(pio, tx_state_machine, tx_pin, 1U, true); 324 #else 325 // clang-format off 326 iomode_t tx_pin_mode = PAL_RP_PAD_IE | 327 PAL_RP_GPIO_OE | 328 PAL_RP_PAD_SCHMITT | 329 PAL_RP_PAD_PUE | 330 PAL_RP_PAD_SLEWFAST | 331 PAL_RP_PAD_DRIVE12 | 332 PAL_RP_IOCTRL_OEOVER_DRVINVPERI | 333 (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1); 334 // clang-format on 335 pio_sm_set_pins_with_mask(pio, tx_state_machine, 0U << tx_pin, 1U << tx_pin); 336 pio_sm_set_consecutive_pindirs(pio, tx_state_machine, tx_pin, 1U, true); 337 #endif 338 339 palSetLineMode(tx_pin, tx_pin_mode); 340 341 pio_sm_config config = pio_get_default_sm_config(); 342 sm_config_set_wrap(&config, offset + UART_TX_WRAP_TARGET, offset + UART_TX_WRAP); 343 #if defined(SERIAL_USART_FULL_DUPLEX) 344 sm_config_set_sideset(&config, 2, true, false); 345 #else 346 sm_config_set_sideset(&config, 2, true, true); 347 #endif 348 // OUT shifts to right, no autopull 349 sm_config_set_out_shift(&config, true, false, 32); 350 // We are mapping both OUT and side-set to the same pin, because sometimes 351 // we need to assert user data onto the pin (with OUT) and sometimes 352 // assert constant values (start/stop bit) 353 sm_config_set_out_pins(&config, tx_pin, 1); 354 sm_config_set_sideset_pins(&config, tx_pin); 355 // We only need TX, so get an 8-deep FIFO! 356 sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX); 357 // SM transmits 1 bit per 8 execution cycles. 358 float div = (float)clock_get_hz(clk_sys) / (8 * SERIAL_USART_SPEED); 359 sm_config_set_clkdiv(&config, div); 360 pio_sm_init(pio, tx_state_machine, offset, &config); 361 pio_sm_set_enabled(pio, tx_state_machine, true); 362 } 363 364 static inline void pio_rx_init(pin_t rx_pin) { 365 uint offset = pio_add_program(pio, &uart_rx_program); 366 367 #if defined(SERIAL_USART_FULL_DUPLEX) 368 uint pio_idx = pio_get_index(pio); 369 pio_sm_set_consecutive_pindirs(pio, rx_state_machine, rx_pin, 1, false); 370 // clang-format off 371 iomode_t rx_pin_mode = PAL_RP_PAD_IE | 372 PAL_RP_PAD_SCHMITT | 373 PAL_RP_PAD_PUE | 374 (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1); 375 // clang-format on 376 palSetLineMode(rx_pin, rx_pin_mode); 377 #endif 378 379 pio_sm_config config = pio_get_default_sm_config(); 380 sm_config_set_wrap(&config, offset + UART_RX_WRAP_TARGET, offset + UART_RX_WRAP); 381 sm_config_set_in_pins(&config, rx_pin); // for WAIT, IN 382 sm_config_set_jmp_pin(&config, rx_pin); // for JMP 383 // Shift to right, autopush disabled 384 sm_config_set_in_shift(&config, true, false, 32); 385 // Deeper FIFO as we're not doing any TX 386 sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_RX); 387 // SM transmits 1 bit per 8 execution cycles. 388 float div = (float)clock_get_hz(clk_sys) / (8 * SERIAL_USART_SPEED); 389 sm_config_set_clkdiv(&config, div); 390 pio_sm_init(pio, rx_state_machine, offset, &config); 391 pio_sm_set_enabled(pio, rx_state_machine, true); 392 } 393 394 static inline void pio_init(pin_t tx_pin, pin_t rx_pin) { 395 uint pio_idx = pio_get_index(pio); 396 397 /* Get PIOx peripheral out of reset state. */ 398 hal_lld_peripheral_unreset(pio_idx == 0 ? RESETS_ALLREG_PIO0 : RESETS_ALLREG_PIO1); 399 400 tx_state_machine = pio_claim_unused_sm(pio, true); 401 if (tx_state_machine < 0) { 402 dprintln("ERROR: Failed to acquire state machine for serial transmission!"); 403 return; 404 } 405 pio_tx_init(tx_pin); 406 407 rx_state_machine = pio_claim_unused_sm(pio, true); 408 if (rx_state_machine < 0) { 409 dprintln("ERROR: Failed to acquire state machine for serial reception!"); 410 return; 411 } 412 pio_rx_init(rx_pin); 413 414 // Enable error flag IRQ source for rx state machine 415 pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, true); 416 pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, true); 417 pio_set_irq0_source_enabled(pio, pis_interrupt0, true); 418 419 // Enable PIO specific interrupt vector, as the pio implementation is timing 420 // critical we use the highest possible priority. 421 #if defined(SERIAL_PIO_USE_PIO1) 422 nvicEnableVector(RP_PIO1_IRQ_0_NUMBER, CORTEX_MAX_KERNEL_PRIORITY); 423 #else 424 nvicEnableVector(RP_PIO0_IRQ_0_NUMBER, CORTEX_MAX_KERNEL_PRIORITY); 425 #endif 426 427 enter_rx_state(); 428 } 429 430 /** 431 * @brief PIO driver specific initialization function for the master side. 432 */ 433 void serial_transport_driver_master_init(void) { 434 #if defined(SERIAL_USART_FULL_DUPLEX) 435 pin_t tx_pin = SERIAL_USART_TX_PIN; 436 pin_t rx_pin = SERIAL_USART_RX_PIN; 437 #else 438 pin_t tx_pin = SERIAL_USART_TX_PIN; 439 pin_t rx_pin = SERIAL_USART_TX_PIN; 440 #endif 441 442 #if defined(SERIAL_USART_PIN_SWAP) 443 pio_init(rx_pin, tx_pin); 444 #else 445 pio_init(tx_pin, rx_pin); 446 #endif 447 } 448 449 /** 450 * @brief PIO driver specific initialization function for the slave side. 451 */ 452 void serial_transport_driver_slave_init(void) { 453 #if defined(SERIAL_USART_FULL_DUPLEX) 454 pin_t tx_pin = SERIAL_USART_TX_PIN; 455 pin_t rx_pin = SERIAL_USART_RX_PIN; 456 #else 457 pin_t tx_pin = SERIAL_USART_TX_PIN; 458 pin_t rx_pin = SERIAL_USART_TX_PIN; 459 #endif 460 461 pio_init(tx_pin, rx_pin); 462 }