serial_usart_duplex.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /* Copyright 2021 QMK
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include "serial_usart.h"
  17. #include <stdatomic.h>
  18. #if !defined(USE_GPIOV1)
  19. // The default PAL alternate modes are used to signal that the pins are used for USART
  20. # if !defined(SERIAL_USART_TX_PAL_MODE)
  21. # define SERIAL_USART_TX_PAL_MODE 7
  22. # endif
  23. # if !defined(SERIAL_USART_RX_PAL_MODE)
  24. # define SERIAL_USART_RX_PAL_MODE 7
  25. # endif
  26. #endif
  27. #if !defined(SERIAL_USART_DRIVER)
  28. # define SERIAL_USART_DRIVER UARTD1
  29. #endif
  30. #if !defined(SERIAL_USART_TX_PIN)
  31. # define SERIAL_USART_TX_PIN A9
  32. #endif
  33. #if !defined(SERIAL_USART_RX_PIN)
  34. # define SERIAL_USART_RX_PIN A10
  35. #endif
  36. #define SIGNAL_HANDSHAKE_RECEIVED 0x1
  37. void handle_transactions_slave(uint8_t sstd_index);
  38. static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake);
  39. /*
  40. * UART driver configuration structure. We use the blocking DMA enabled API and
  41. * the rxchar callback to receive handshake tokens but only on the slave halve.
  42. */
  43. // clang-format off
  44. static UARTConfig uart_config = {
  45. .txend1_cb = NULL,
  46. .txend2_cb = NULL,
  47. .rxend_cb = NULL,
  48. .rxchar_cb = NULL,
  49. .rxerr_cb = NULL,
  50. .timeout_cb = NULL,
  51. .speed = (SERIAL_USART_SPEED),
  52. .cr1 = (SERIAL_USART_CR1),
  53. .cr2 = (SERIAL_USART_CR2),
  54. .cr3 = (SERIAL_USART_CR3)
  55. };
  56. // clang-format on
  57. static SSTD_t* Transaction_table = NULL;
  58. static uint8_t Transaction_table_size = 0;
  59. static atomic_uint_least8_t handshake = 0xFF;
  60. static thread_reference_t tp_target = NULL;
  61. /*
  62. * This callback is invoked when a character is received but the application
  63. * was not ready to receive it, the character is passed as parameter.
  64. * Receive transaction table index from initiator, which doubles as basic handshake token. */
  65. static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake) {
  66. /* Check if received handshake is not a valid transaction id.
  67. * Please note that we can still catch a seemingly valid handshake
  68. * i.e. a byte from a ongoing transfer which is in the allowed range.
  69. * So this check mainly prevents any obviously wrong handshakes and
  70. * subsequent wakeups of the receiving thread, which is a costly operation. */
  71. if (received_handshake > Transaction_table_size) {
  72. return;
  73. }
  74. handshake = (uint8_t)received_handshake;
  75. chSysLockFromISR();
  76. /* Wakeup receiving thread to start a transaction. */
  77. chEvtSignalI(tp_target, (eventmask_t)SIGNAL_HANDSHAKE_RECEIVED);
  78. chSysUnlockFromISR();
  79. }
  80. __attribute__((weak)) void usart_init(void) {
  81. #if defined(USE_GPIOV1)
  82. palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
  83. palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT);
  84. #else
  85. palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
  86. palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
  87. #endif
  88. }
  89. /*
  90. * This thread runs on the slave half and reacts to transactions initiated from the master.
  91. */
  92. static THD_WORKING_AREA(waSlaveThread, 1024);
  93. static THD_FUNCTION(SlaveThread, arg) {
  94. (void)arg;
  95. chRegSetThreadName("slave_usart_tx_rx");
  96. while (true) {
  97. /* We sleep as long as there is no handshake waiting for us. */
  98. chEvtWaitAny((eventmask_t)SIGNAL_HANDSHAKE_RECEIVED);
  99. handle_transactions_slave(handshake);
  100. }
  101. }
  102. void soft_serial_target_init(SSTD_t* const sstd_table, int sstd_table_size) {
  103. Transaction_table = sstd_table;
  104. Transaction_table_size = (uint8_t)sstd_table_size;
  105. usart_init();
  106. #if defined(USART_REMAP)
  107. USART_REMAP;
  108. #endif
  109. tp_target = chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
  110. // Start receiving handshake tokens on slave halve
  111. uart_config.rxchar_cb = receive_transaction_handshake;
  112. uartStart(&SERIAL_USART_DRIVER, &uart_config);
  113. }
  114. /**
  115. * @brief React to transactions started by the master.
  116. * This version uses duplex send and receive usart pheriphals and DMA backed transfers.
  117. */
  118. void inline handle_transactions_slave(uint8_t sstd_index) {
  119. size_t buffer_size = 0;
  120. msg_t msg = 0;
  121. SSTD_t* trans = &Transaction_table[sstd_index];
  122. /* Send back the handshake which is XORed as a simple checksum,
  123. to signal that the slave is ready to receive possible transaction buffers */
  124. sstd_index ^= HANDSHAKE_MAGIC;
  125. buffer_size = (size_t)sizeof(sstd_index);
  126. msg = uartSendTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT));
  127. if (msg != MSG_OK) {
  128. if (trans->status) {
  129. *trans->status = TRANSACTION_NO_RESPONSE;
  130. }
  131. return;
  132. }
  133. /* Receive transaction buffer from the master. If this transaction requires it.*/
  134. buffer_size = (size_t)trans->initiator2target_buffer_size;
  135. if (buffer_size) {
  136. msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
  137. if (msg != MSG_OK) {
  138. if (trans->status) {
  139. *trans->status = TRANSACTION_NO_RESPONSE;
  140. }
  141. return;
  142. }
  143. }
  144. /* Send transaction buffer to the master. If this transaction requires it. */
  145. buffer_size = (size_t)trans->target2initiator_buffer_size;
  146. if (buffer_size) {
  147. msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
  148. if (msg != MSG_OK) {
  149. if (trans->status) {
  150. *trans->status = TRANSACTION_NO_RESPONSE;
  151. }
  152. return;
  153. }
  154. }
  155. if (trans->status) {
  156. *trans->status = TRANSACTION_ACCEPTED;
  157. }
  158. }
  159. void soft_serial_initiator_init(SSTD_t* const sstd_table, int sstd_table_size) {
  160. Transaction_table = sstd_table;
  161. Transaction_table_size = (uint8_t)sstd_table_size;
  162. usart_init();
  163. #if defined(SERIAL_USART_PIN_SWAP)
  164. uart_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins
  165. #endif
  166. #if defined(USART_REMAP)
  167. USART_REMAP;
  168. #endif
  169. uartStart(&SERIAL_USART_DRIVER, &uart_config);
  170. }
  171. /**
  172. * @brief Start transaction from the master to the slave.
  173. * This version uses duplex send and receive usart pheriphals and DMA backed transfers.
  174. *
  175. * @param index Transaction Table index of the transaction to start.
  176. * @return int TRANSACTION_NO_RESPONSE in case of Timeout.
  177. * TRANSACTION_TYPE_ERROR in case of invalid transaction index.
  178. * TRANSACTION_END in case of success.
  179. */
  180. #if !defined(SERIAL_USE_MULTI_TRANSACTION)
  181. int soft_serial_transaction(void) {
  182. uint8_t sstd_index = 0;
  183. #else
  184. int soft_serial_transaction(int index) {
  185. uint8_t sstd_index = index;
  186. #endif
  187. if (sstd_index > Transaction_table_size) {
  188. return TRANSACTION_TYPE_ERROR;
  189. }
  190. SSTD_t* const trans = &Transaction_table[sstd_index];
  191. msg_t msg = 0;
  192. size_t buffer_size = (size_t)sizeof(sstd_index);
  193. /* Send transaction table index to the slave, which doubles as basic handshake token. */
  194. uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT));
  195. uint8_t sstd_index_shake = 0xFF;
  196. buffer_size = (size_t)sizeof(sstd_index_shake);
  197. /* Receive the handshake token from the slave. The token was XORed by the slave as a simple checksum.
  198. If the tokens match, the master will start to send and receive possible transaction buffers. */
  199. msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index_shake, TIME_MS2I(SERIAL_USART_TIMEOUT));
  200. if (msg != MSG_OK || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) {
  201. dprintln("USART: Handshake Failed");
  202. return TRANSACTION_NO_RESPONSE;
  203. }
  204. /* Send transaction buffer to the slave. If this transaction requires it. */
  205. buffer_size = (size_t)trans->initiator2target_buffer_size;
  206. if (buffer_size) {
  207. msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
  208. if (msg != MSG_OK) {
  209. dprintln("USART: Send Failed");
  210. return TRANSACTION_NO_RESPONSE;
  211. }
  212. }
  213. /* Receive transaction buffer from the slave. If this transaction requires it. */
  214. buffer_size = (size_t)trans->target2initiator_buffer_size;
  215. if (buffer_size) {
  216. msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
  217. if (msg != MSG_OK) {
  218. dprintln("USART: Receive Failed");
  219. return TRANSACTION_NO_RESPONSE;
  220. }
  221. }
  222. return TRANSACTION_END;
  223. }