logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

serial_usart.c (8287B)


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