serial_usart.c 7.7 KB

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