serial_usart.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // Copyright 2021 QMK
  2. // Copyright 2022 Stefan Kerkmann
  3. // SPDX-License-Identifier: GPL-2.0-or-later
  4. #include "serial_usart.h"
  5. #include "serial_protocol.h"
  6. #include "synchronization_util.h"
  7. #if defined(SERIAL_USART_CONFIG)
  8. static QMKSerialConfig serial_config = SERIAL_USART_CONFIG;
  9. #elif defined(MCU_STM32) /* STM32 MCUs */
  10. static QMKSerialConfig serial_config = {
  11. # if HAL_USE_SERIAL
  12. .speed = (SERIAL_USART_SPEED),
  13. # else
  14. .baud = (SERIAL_USART_SPEED),
  15. # endif
  16. .cr1 = (SERIAL_USART_CR1),
  17. .cr2 = (SERIAL_USART_CR2),
  18. # if !defined(SERIAL_USART_FULL_DUPLEX)
  19. .cr3 = ((SERIAL_USART_CR3) | USART_CR3_HDSEL) /* activate half-duplex mode */
  20. # else
  21. .cr3 = (SERIAL_USART_CR3)
  22. # endif
  23. };
  24. #elif defined(MCU_RP) /* Raspberry Pi MCUs */
  25. /* USART in 8E2 config with RX and TX FIFOs enabled. */
  26. // clang-format off
  27. static QMKSerialConfig serial_config = {
  28. .baud = (SERIAL_USART_SPEED),
  29. .UARTLCR_H = UART_UARTLCR_H_WLEN_8BITS | UART_UARTLCR_H_PEN | UART_UARTLCR_H_STP2 | UART_UARTLCR_H_FEN,
  30. .UARTCR = 0U,
  31. .UARTIFLS = UART_UARTIFLS_RXIFLSEL_1_8F | UART_UARTIFLS_TXIFLSEL_1_8E,
  32. .UARTDMACR = 0U
  33. };
  34. // clang-format on
  35. #else
  36. # error MCU Familiy not supported by default, supply your own serial_config by defining SERIAL_USART_CONFIG in your keyboard files.
  37. #endif
  38. static QMKSerialDriver* serial_driver = (QMKSerialDriver*)&SERIAL_USART_DRIVER;
  39. #if HAL_USE_SERIAL
  40. /**
  41. * @brief SERIAL Driver startup routine.
  42. */
  43. static inline void usart_driver_start(void) {
  44. sdStart(serial_driver, &serial_config);
  45. }
  46. inline void serial_transport_driver_clear(void) {
  47. osalSysLock();
  48. bool volatile queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue);
  49. osalSysUnlock();
  50. while (queue_not_empty) {
  51. osalSysLock();
  52. /* Hard reset the input queue. */
  53. iqResetI(&serial_driver->iqueue);
  54. osalSysUnlock();
  55. /* Allow pending interrupts to preempt.
  56. * Do not merge the lock/unlock blocks into one
  57. * or the code will not work properly.
  58. * The empty read adds a tiny amount of delay. */
  59. (void)queue_not_empty;
  60. osalSysLock();
  61. queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue);
  62. osalSysUnlock();
  63. }
  64. }
  65. #elif HAL_USE_SIO
  66. void clear_rx_evt_cb(SIODriver* siop) {
  67. osalSysLockFromISR();
  68. /* If errors occured during transactions this callback is invoked. We just
  69. * clear the error sources and move on. We rely on the fact that we check
  70. * for the success of the transaction by comparing the received/send bytes
  71. * with the actual received/send bytes in the send/receive functions. */
  72. sioGetAndClearEventsI(serial_driver);
  73. osalSysUnlockFromISR();
  74. }
  75. static const SIOOperation serial_usart_operation = {.rx_cb = NULL, .rx_idle_cb = NULL, .tx_cb = NULL, .tx_end_cb = NULL, .rx_evt_cb = &clear_rx_evt_cb};
  76. /**
  77. * @brief SIO Driver startup routine.
  78. */
  79. static inline void usart_driver_start(void) {
  80. sioStart(serial_driver, &serial_config);
  81. sioStartOperation(serial_driver, &serial_usart_operation);
  82. }
  83. inline void serial_transport_driver_clear(void) {
  84. osalSysLock();
  85. while (!sioIsRXEmptyX(serial_driver)) {
  86. (void)sioGetX(serial_driver);
  87. }
  88. osalSysUnlock();
  89. }
  90. #else
  91. # error Either the SERIAL or SIO driver has to be activated to use the usart driver for split keyboards.
  92. #endif
  93. inline bool serial_transport_send(const uint8_t* source, const size_t size) {
  94. bool success = (size_t)chnWriteTimeout(serial_driver, source, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size;
  95. #if !defined(SERIAL_USART_FULL_DUPLEX)
  96. /* Half duplex fills the input queue with the data we wrote - just throw it away. */
  97. if (likely(success)) {
  98. size_t bytes_left = size;
  99. # if HAL_USE_SERIAL
  100. /* The SERIAL driver uses large soft FIFOs that are filled from an IRQ
  101. * context, so there is a delay between receiving the data and it
  102. * becoming actually available, therefore we have to apply a timeout
  103. * mechanism. Under the right circumstances (e.g. bad cables paired with
  104. * high baud rates) less bytes can be present in the input queue as
  105. * well. */
  106. uint8_t dump[64];
  107. while (unlikely(bytes_left >= 64)) {
  108. if (unlikely(!serial_transport_receive(dump, 64))) {
  109. return false;
  110. }
  111. bytes_left -= 64;
  112. }
  113. return serial_transport_receive(dump, bytes_left);
  114. # else
  115. /* The SIO driver directly accesses the hardware FIFOs of the USART
  116. * peripheral. As these are limited in depth, the RX FIFO might have been
  117. * overflowed by a large that we just send. Therefore we attempt to read
  118. * back all the data we send or until the FIFO runs empty in case it
  119. * overflowed and data was truncated. */
  120. if (unlikely(sioSynchronizeTXEnd(serial_driver, TIME_MS2I(SERIAL_USART_TIMEOUT)) < MSG_OK)) {
  121. return false;
  122. }
  123. osalSysLock();
  124. while (bytes_left > 0 && !sioIsRXEmptyX(serial_driver)) {
  125. (void)sioGetX(serial_driver);
  126. bytes_left--;
  127. }
  128. osalSysUnlock();
  129. # endif
  130. }
  131. #endif
  132. return success;
  133. }
  134. inline bool serial_transport_receive(uint8_t* destination, const size_t size) {
  135. bool success = (size_t)chnReadTimeout(serial_driver, destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size;
  136. return success;
  137. }
  138. inline bool serial_transport_receive_blocking(uint8_t* destination, const size_t size) {
  139. bool success = (size_t)chnRead(serial_driver, destination, size) == size;
  140. return success;
  141. }
  142. #if !defined(SERIAL_USART_FULL_DUPLEX)
  143. /**
  144. * @brief Initiate pins for USART peripheral. Half-duplex configuration.
  145. */
  146. __attribute__((weak)) void usart_init(void) {
  147. # if defined(MCU_STM32) /* STM32 MCUs */
  148. # if defined(USE_GPIOV1)
  149. palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_OPENDRAIN);
  150. # else
  151. palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN);
  152. # endif
  153. # if defined(USART_REMAP)
  154. USART_REMAP;
  155. # endif
  156. # elif defined(MCU_RP) /* Raspberry Pi MCUs */
  157. # error Half-duplex with the SIO driver is not supported due to hardware limitations on the RP2040, switch to the PIO driver which has half-duplex support.
  158. # else
  159. # pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files."
  160. # endif
  161. }
  162. #else
  163. /**
  164. * @brief Initiate pins for USART peripheral. Full-duplex configuration.
  165. */
  166. __attribute__((weak)) void usart_init(void) {
  167. # if defined(MCU_STM32) /* STM32 MCUs */
  168. # if defined(USE_GPIOV1)
  169. palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_PUSHPULL);
  170. palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT);
  171. # else
  172. palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);
  173. palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);
  174. # endif
  175. # if defined(USART_REMAP)
  176. USART_REMAP;
  177. # endif
  178. # elif defined(MCU_RP) /* Raspberry Pi MCUs */
  179. palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_UART);
  180. palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE_UART);
  181. # else
  182. # pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files."
  183. # endif
  184. }
  185. #endif
  186. /**
  187. * @brief Overridable master specific initializations.
  188. */
  189. __attribute__((weak, nonnull)) void usart_master_init(QMKSerialDriver** driver) {
  190. (void)driver;
  191. usart_init();
  192. }
  193. /**
  194. * @brief Overridable slave specific initializations.
  195. */
  196. __attribute__((weak, nonnull)) void usart_slave_init(QMKSerialDriver** driver) {
  197. (void)driver;
  198. usart_init();
  199. }
  200. void serial_transport_driver_slave_init(void) {
  201. usart_slave_init(&serial_driver);
  202. usart_driver_start();
  203. }
  204. void serial_transport_driver_master_init(void) {
  205. usart_master_init(&serial_driver);
  206. #if defined(MCU_STM32) && defined(SERIAL_USART_PIN_SWAP)
  207. serial_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins
  208. #endif
  209. usart_driver_start();
  210. }