serial_usart.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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. #ifndef USE_GPIOV1
  18. // The default PAL alternate modes are used to signal that the pins are used for USART
  19. # ifndef SERIAL_USART_TX_PAL_MODE
  20. # define SERIAL_USART_TX_PAL_MODE 7
  21. # endif
  22. #endif
  23. #ifndef SERIAL_USART_DRIVER
  24. # define SERIAL_USART_DRIVER SD1
  25. #endif
  26. #ifdef SOFT_SERIAL_PIN
  27. # define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN
  28. #endif
  29. static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) {
  30. msg_t ret = sdWrite(driver, data, size);
  31. // Half duplex requires us to read back the data we just wrote - just throw it away
  32. uint8_t dump[size];
  33. sdRead(driver, dump, size);
  34. return ret;
  35. }
  36. #undef sdWrite
  37. #define sdWrite sdWriteHalfDuplex
  38. static inline msg_t sdWriteTimeoutHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size, uint32_t timeout) {
  39. msg_t ret = sdWriteTimeout(driver, data, size, timeout);
  40. // Half duplex requires us to read back the data we just wrote - just throw it away
  41. uint8_t dump[size];
  42. sdReadTimeout(driver, dump, size, timeout);
  43. return ret;
  44. }
  45. #undef sdWriteTimeout
  46. #define sdWriteTimeout sdWriteTimeoutHalfDuplex
  47. static inline void sdClear(SerialDriver* driver) {
  48. while (sdGetTimeout(driver, TIME_IMMEDIATE) != MSG_TIMEOUT) {
  49. // Do nothing with the data
  50. }
  51. }
  52. static SerialConfig sdcfg = {
  53. (SERIAL_USART_SPEED), // speed - mandatory
  54. (SERIAL_USART_CR1), // CR1
  55. (SERIAL_USART_CR2), // CR2
  56. (SERIAL_USART_CR3) // CR3
  57. };
  58. void handle_soft_serial_slave(void);
  59. /*
  60. * This thread runs on the slave and responds to transactions initiated
  61. * by the master
  62. */
  63. static THD_WORKING_AREA(waSlaveThread, 2048);
  64. static THD_FUNCTION(SlaveThread, arg) {
  65. (void)arg;
  66. chRegSetThreadName("slave_transport");
  67. while (true) {
  68. handle_soft_serial_slave();
  69. }
  70. }
  71. __attribute__((weak)) void usart_init(void) {
  72. #if defined(USE_GPIOV1)
  73. palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN);
  74. #else
  75. palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
  76. #endif
  77. #if defined(USART_REMAP)
  78. USART_REMAP;
  79. #endif
  80. }
  81. void usart_master_init(void) {
  82. usart_init();
  83. sdcfg.cr3 |= USART_CR3_HDSEL;
  84. sdStart(&SERIAL_USART_DRIVER, &sdcfg);
  85. }
  86. void usart_slave_init(void) {
  87. usart_init();
  88. sdcfg.cr3 |= USART_CR3_HDSEL;
  89. sdStart(&SERIAL_USART_DRIVER, &sdcfg);
  90. // Start transport thread
  91. chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
  92. }
  93. void soft_serial_initiator_init(void) { usart_master_init(); }
  94. void soft_serial_target_init(void) { usart_slave_init(); }
  95. void handle_soft_serial_slave(void) {
  96. uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id
  97. split_transaction_desc_t* trans = &split_transaction_table[sstd_index];
  98. // Always write back the sstd_index as part of a basic handshake
  99. sstd_index ^= HANDSHAKE_MAGIC;
  100. sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index));
  101. if (trans->initiator2target_buffer_size) {
  102. sdRead(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size);
  103. }
  104. // Allow any slave processing to occur
  105. if (trans->slave_callback) {
  106. trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
  107. }
  108. if (trans->target2initiator_buffer_size) {
  109. sdWrite(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size);
  110. }
  111. if (trans->status) {
  112. *trans->status = TRANSACTION_ACCEPTED;
  113. }
  114. }
  115. /////////
  116. // start transaction by initiator
  117. //
  118. // int soft_serial_transaction(int sstd_index)
  119. //
  120. // Returns:
  121. // TRANSACTION_END
  122. // TRANSACTION_NO_RESPONSE
  123. // TRANSACTION_DATA_ERROR
  124. int soft_serial_transaction(int index) {
  125. uint8_t sstd_index = index;
  126. if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
  127. split_transaction_desc_t* trans = &split_transaction_table[sstd_index];
  128. msg_t res = 0;
  129. if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
  130. sdClear(&SERIAL_USART_DRIVER);
  131. // First chunk is always transaction id
  132. sdWriteTimeout(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index), TIME_MS2I(SERIAL_USART_TIMEOUT));
  133. uint8_t sstd_index_shake = 0xFF;
  134. // Which we always read back first so that we can error out correctly
  135. // - due to the half duplex limitations on return codes, we always have to read *something*
  136. // - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready
  137. res = sdReadTimeout(&SERIAL_USART_DRIVER, &sstd_index_shake, sizeof(sstd_index_shake), TIME_MS2I(SERIAL_USART_TIMEOUT));
  138. if (res < 0 || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) {
  139. dprintf("serial::usart_shake NO_RESPONSE\n");
  140. return TRANSACTION_NO_RESPONSE;
  141. }
  142. if (trans->initiator2target_buffer_size) {
  143. res = sdWriteTimeout(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT));
  144. if (res < 0) {
  145. dprintf("serial::usart_transmit NO_RESPONSE\n");
  146. return TRANSACTION_NO_RESPONSE;
  147. }
  148. }
  149. if (trans->target2initiator_buffer_size) {
  150. res = sdReadTimeout(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT));
  151. if (res < 0) {
  152. dprintf("serial::usart_receive NO_RESPONSE\n");
  153. return TRANSACTION_NO_RESPONSE;
  154. }
  155. }
  156. return TRANSACTION_END;
  157. }