|
| 1 | +#include "quantum.h" |
| 2 | +#include "serial.h" |
| 3 | +#include "printf.h" |
| 4 | + |
| 5 | +#include "ch.h" |
| 6 | +#include "hal.h" |
| 7 | + |
| 8 | +#ifndef USART_CR1_M0 |
| 9 | +# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so |
| 10 | +#endif |
| 11 | + |
| 12 | +#ifndef USE_GPIOV1 |
| 13 | +// The default PAL alternate modes are used to signal that the pins are used for USART |
| 14 | +# ifndef SERIAL_USART_TX_PAL_MODE |
| 15 | +# define SERIAL_USART_TX_PAL_MODE 7 |
| 16 | +# endif |
| 17 | +#endif |
| 18 | + |
| 19 | +#ifndef SERIAL_USART_DRIVER |
| 20 | +# define SERIAL_USART_DRIVER SD1 |
| 21 | +#endif |
| 22 | + |
| 23 | +#ifndef SERIAL_USART_CR1 |
| 24 | +# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length |
| 25 | +#endif |
| 26 | + |
| 27 | +#ifndef SERIAL_USART_CR2 |
| 28 | +# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits |
| 29 | +#endif |
| 30 | + |
| 31 | +#ifndef SERIAL_USART_CR3 |
| 32 | +# define SERIAL_USART_CR3 0 |
| 33 | +#endif |
| 34 | + |
| 35 | +#ifdef SOFT_SERIAL_PIN |
| 36 | +# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN |
| 37 | +#endif |
| 38 | + |
| 39 | +#ifndef SELECT_SOFT_SERIAL_SPEED |
| 40 | +# define SELECT_SOFT_SERIAL_SPEED 1 |
| 41 | +#endif |
| 42 | + |
| 43 | +#ifdef SERIAL_USART_SPEED |
| 44 | +// Allow advanced users to directly set SERIAL_USART_SPEED |
| 45 | +#elif SELECT_SOFT_SERIAL_SPEED == 0 |
| 46 | +# define SERIAL_USART_SPEED 460800 |
| 47 | +#elif SELECT_SOFT_SERIAL_SPEED == 1 |
| 48 | +# define SERIAL_USART_SPEED 230400 |
| 49 | +#elif SELECT_SOFT_SERIAL_SPEED == 2 |
| 50 | +# define SERIAL_USART_SPEED 115200 |
| 51 | +#elif SELECT_SOFT_SERIAL_SPEED == 3 |
| 52 | +# define SERIAL_USART_SPEED 57600 |
| 53 | +#elif SELECT_SOFT_SERIAL_SPEED == 4 |
| 54 | +# define SERIAL_USART_SPEED 38400 |
| 55 | +#elif SELECT_SOFT_SERIAL_SPEED == 5 |
| 56 | +# define SERIAL_USART_SPEED 19200 |
| 57 | +#else |
| 58 | +# error invalid SELECT_SOFT_SERIAL_SPEED value |
| 59 | +#endif |
| 60 | + |
| 61 | +#define TIMEOUT 100 |
| 62 | +#define HANDSHAKE_MAGIC 7 |
| 63 | + |
| 64 | +static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) { |
| 65 | + msg_t ret = sdWrite(driver, data, size); |
| 66 | + |
| 67 | + // Half duplex requires us to read back the data we just wrote - just throw it away |
| 68 | + uint8_t dump[size]; |
| 69 | + sdRead(driver, dump, size); |
| 70 | + |
| 71 | + return ret; |
| 72 | +} |
| 73 | +#undef sdWrite |
| 74 | +#define sdWrite sdWriteHalfDuplex |
| 75 | + |
| 76 | +static inline msg_t sdWriteTimeoutHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size, uint32_t timeout) { |
| 77 | + msg_t ret = sdWriteTimeout(driver, data, size, timeout); |
| 78 | + |
| 79 | + // Half duplex requires us to read back the data we just wrote - just throw it away |
| 80 | + uint8_t dump[size]; |
| 81 | + sdReadTimeout(driver, dump, size, timeout); |
| 82 | + |
| 83 | + return ret; |
| 84 | +} |
| 85 | +#undef sdWriteTimeout |
| 86 | +#define sdWriteTimeout sdWriteTimeoutHalfDuplex |
| 87 | + |
| 88 | +static inline void sdClear(SerialDriver* driver) { |
| 89 | + while (sdGetTimeout(driver, TIME_IMMEDIATE) != MSG_TIMEOUT) { |
| 90 | + // Do nothing with the data |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +static SerialConfig sdcfg = { |
| 95 | + (SERIAL_USART_SPEED), // speed - mandatory |
| 96 | + (SERIAL_USART_CR1), // CR1 |
| 97 | + (SERIAL_USART_CR2), // CR2 |
| 98 | + (SERIAL_USART_CR3) // CR3 |
| 99 | +}; |
| 100 | + |
| 101 | +void handle_soft_serial_slave(void); |
| 102 | + |
| 103 | +/* |
| 104 | + * This thread runs on the slave and responds to transactions initiated |
| 105 | + * by the master |
| 106 | + */ |
| 107 | +static THD_WORKING_AREA(waSlaveThread, 2048); |
| 108 | +static THD_FUNCTION(SlaveThread, arg) { |
| 109 | + (void)arg; |
| 110 | + chRegSetThreadName("slave_transport"); |
| 111 | + |
| 112 | + while (true) { |
| 113 | + handle_soft_serial_slave(); |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +__attribute__((weak)) void usart_init(void) { |
| 118 | +#if defined(USE_GPIOV1) |
| 119 | + palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN); |
| 120 | +#else |
| 121 | + palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); |
| 122 | +#endif |
| 123 | +} |
| 124 | + |
| 125 | +void usart_master_init(void) { |
| 126 | + usart_init(); |
| 127 | + |
| 128 | + sdcfg.cr3 |= USART_CR3_HDSEL; |
| 129 | + sdStart(&SERIAL_USART_DRIVER, &sdcfg); |
| 130 | +} |
| 131 | + |
| 132 | +void usart_slave_init(void) { |
| 133 | + usart_init(); |
| 134 | + |
| 135 | + sdcfg.cr3 |= USART_CR3_HDSEL; |
| 136 | + sdStart(&SERIAL_USART_DRIVER, &sdcfg); |
| 137 | + |
| 138 | + // Start transport thread |
| 139 | + chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); |
| 140 | +} |
| 141 | + |
| 142 | +static SSTD_t* Transaction_table = NULL; |
| 143 | +static uint8_t Transaction_table_size = 0; |
| 144 | + |
| 145 | +void soft_serial_initiator_init(SSTD_t* sstd_table, int sstd_table_size) { |
| 146 | + Transaction_table = sstd_table; |
| 147 | + Transaction_table_size = (uint8_t)sstd_table_size; |
| 148 | + |
| 149 | + usart_master_init(); |
| 150 | +} |
| 151 | + |
| 152 | +void soft_serial_target_init(SSTD_t* sstd_table, int sstd_table_size) { |
| 153 | + Transaction_table = sstd_table; |
| 154 | + Transaction_table_size = (uint8_t)sstd_table_size; |
| 155 | + |
| 156 | + usart_slave_init(); |
| 157 | +} |
| 158 | + |
| 159 | +void handle_soft_serial_slave(void) { |
| 160 | + uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id |
| 161 | + SSTD_t* trans = &Transaction_table[sstd_index]; |
| 162 | + |
| 163 | + // Always write back the sstd_index as part of a basic handshake |
| 164 | + sstd_index ^= HANDSHAKE_MAGIC; |
| 165 | + sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index)); |
| 166 | + |
| 167 | + if (trans->initiator2target_buffer_size) { |
| 168 | + sdRead(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size); |
| 169 | + } |
| 170 | + |
| 171 | + if (trans->target2initiator_buffer_size) { |
| 172 | + sdWrite(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size); |
| 173 | + } |
| 174 | + |
| 175 | + if (trans->status) { |
| 176 | + *trans->status = TRANSACTION_ACCEPTED; |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +///////// |
| 181 | +// start transaction by initiator |
| 182 | +// |
| 183 | +// int soft_serial_transaction(int sstd_index) |
| 184 | +// |
| 185 | +// Returns: |
| 186 | +// TRANSACTION_END |
| 187 | +// TRANSACTION_NO_RESPONSE |
| 188 | +// TRANSACTION_DATA_ERROR |
| 189 | +#ifndef SERIAL_USE_MULTI_TRANSACTION |
| 190 | +int soft_serial_transaction(void) { |
| 191 | + uint8_t sstd_index = 0; |
| 192 | +#else |
| 193 | +int soft_serial_transaction(int index) { |
| 194 | + uint8_t sstd_index = index; |
| 195 | +#endif |
| 196 | + |
| 197 | + if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; |
| 198 | + SSTD_t* trans = &Transaction_table[sstd_index]; |
| 199 | + msg_t res = 0; |
| 200 | + |
| 201 | + sdClear(&SERIAL_USART_DRIVER); |
| 202 | + |
| 203 | + // First chunk is always transaction id |
| 204 | + sdWriteTimeout(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index), TIME_MS2I(TIMEOUT)); |
| 205 | + |
| 206 | + uint8_t sstd_index_shake = 0xFF; |
| 207 | + |
| 208 | + // Which we always read back first so that we can error out correctly |
| 209 | + // - due to the half duplex limitations on return codes, we always have to read *something* |
| 210 | + // - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready |
| 211 | + res = sdReadTimeout(&SERIAL_USART_DRIVER, &sstd_index_shake, sizeof(sstd_index_shake), TIME_MS2I(TIMEOUT)); |
| 212 | + if (res < 0 || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) { |
| 213 | + dprintf("serial::usart_shake NO_RESPONSE\n"); |
| 214 | + return TRANSACTION_NO_RESPONSE; |
| 215 | + } |
| 216 | + |
| 217 | + if (trans->initiator2target_buffer_size) { |
| 218 | + res = sdWriteTimeout(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size, TIME_MS2I(TIMEOUT)); |
| 219 | + if (res < 0) { |
| 220 | + dprintf("serial::usart_transmit NO_RESPONSE\n"); |
| 221 | + return TRANSACTION_NO_RESPONSE; |
| 222 | + } |
| 223 | + } |
| 224 | + |
| 225 | + if (trans->target2initiator_buffer_size) { |
| 226 | + res = sdReadTimeout(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size, TIME_MS2I(TIMEOUT)); |
| 227 | + if (res < 0) { |
| 228 | + dprintf("serial::usart_receive NO_RESPONSE\n"); |
| 229 | + return TRANSACTION_NO_RESPONSE; |
| 230 | + } |
| 231 | + } |
| 232 | + |
| 233 | + return TRANSACTION_END; |
| 234 | +} |
0 commit comments