serial.c (8376B)
1 /* 2 * WARNING: be careful changing this code, it is very timing dependent 3 */ 4 5 #include "serial.h" 6 #include "gpio.h" 7 #include "wait.h" 8 #include "synchronization_util.h" 9 10 #include <hal.h> 11 12 // TODO: resolve/remove build warnings 13 #if defined(RGBLIGHT_ENABLE) && defined(RGBLED_SPLIT) && defined(PROTOCOL_CHIBIOS) && defined(WS2812_BITBANG) 14 # warning "RGBLED_SPLIT not supported with bitbang WS2812 driver" 15 #endif 16 17 // default wait implementation cannot be called within interrupt 18 // this method seems to be more accurate than GPT timers 19 #if PORT_SUPPORTS_RT == FALSE 20 # error "chSysPolledDelayX method not supported on this platform" 21 #else 22 # undef wait_us 23 // Force usage of polled waiting - in case WAIT_US_TIMER is activated 24 # define wait_us(us) chSysPolledDelayX(US2RTC(REALTIME_COUNTER_CLOCK, us)) 25 #endif 26 27 #ifndef SELECT_SOFT_SERIAL_SPEED 28 # define SELECT_SOFT_SERIAL_SPEED 1 29 // TODO: correct speeds... 30 // 0: about 189kbps (Experimental only) 31 // 1: about 137kbps (default) 32 // 2: about 75kbps 33 // 3: about 39kbps 34 // 4: about 26kbps 35 // 5: about 20kbps 36 #endif 37 38 // Serial pulse period in microseconds. At the moment, going lower than 12 causes communication failure 39 #if SELECT_SOFT_SERIAL_SPEED == 0 40 # define SERIAL_DELAY 12 41 #elif SELECT_SOFT_SERIAL_SPEED == 1 42 # define SERIAL_DELAY 16 43 #elif SELECT_SOFT_SERIAL_SPEED == 2 44 # define SERIAL_DELAY 24 45 #elif SELECT_SOFT_SERIAL_SPEED == 3 46 # define SERIAL_DELAY 32 47 #elif SELECT_SOFT_SERIAL_SPEED == 4 48 # define SERIAL_DELAY 48 49 #elif SELECT_SOFT_SERIAL_SPEED == 5 50 # define SERIAL_DELAY 64 51 #else 52 # error invalid SELECT_SOFT_SERIAL_SPEED value 53 #endif 54 55 inline static void serial_delay(void) { 56 wait_us(SERIAL_DELAY); 57 } 58 inline static void serial_delay_half(void) { 59 wait_us(SERIAL_DELAY / 2); 60 } 61 inline static void serial_delay_blip(void) { 62 wait_us(1); 63 } 64 inline static void serial_output(void) { 65 gpio_set_pin_output(SOFT_SERIAL_PIN); 66 } 67 inline static void serial_input(void) { 68 gpio_set_pin_input_high(SOFT_SERIAL_PIN); 69 } 70 inline static bool serial_read_pin(void) { 71 return !!gpio_read_pin(SOFT_SERIAL_PIN); 72 } 73 inline static void serial_low(void) { 74 gpio_write_pin_low(SOFT_SERIAL_PIN); 75 } 76 inline static void serial_high(void) { 77 gpio_write_pin_high(SOFT_SERIAL_PIN); 78 } 79 80 void interrupt_handler(void *arg); 81 82 // Use thread + palWaitLineTimeout instead of palSetLineCallback 83 // - Methods like gpio_set_pin_output and palEnableLineEvent/palDisableLineEvent 84 // cause the interrupt to lock up, which would limit to only receiving data... 85 static THD_WORKING_AREA(waThread1, 128); 86 static THD_FUNCTION(Thread1, arg) { 87 (void)arg; 88 chRegSetThreadName("blinker"); 89 while (true) { 90 palWaitLineTimeout(SOFT_SERIAL_PIN, TIME_INFINITE); 91 interrupt_handler(NULL); 92 } 93 } 94 95 void soft_serial_initiator_init(void) { 96 serial_output(); 97 serial_high(); 98 } 99 100 void soft_serial_target_init(void) { 101 serial_input(); 102 103 palEnablePadEvent(PAL_PORT(SOFT_SERIAL_PIN), PAL_PAD(SOFT_SERIAL_PIN), PAL_EVENT_MODE_FALLING_EDGE); 104 chThdCreateStatic(waThread1, sizeof(waThread1), HIGHPRIO, Thread1, NULL); 105 } 106 107 // Used by the master to synchronize timing with the slave. 108 static void __attribute__((noinline)) sync_recv(void) { 109 serial_input(); 110 // This shouldn't hang if the slave disconnects because the 111 // serial line will float to high if the slave does disconnect. 112 while (!serial_read_pin()) { 113 } 114 115 serial_delay(); 116 } 117 118 // Used by the slave to send a synchronization signal to the master. 119 static void __attribute__((noinline)) sync_send(void) { 120 serial_output(); 121 122 serial_low(); 123 serial_delay(); 124 125 serial_high(); 126 } 127 128 // Reads a byte from the serial line 129 static uint8_t __attribute__((noinline)) serial_read_byte(void) { 130 uint8_t byte = 0; 131 serial_input(); 132 for (uint8_t i = 0; i < 8; ++i) { 133 byte = (byte << 1) | serial_read_pin(); 134 serial_delay(); 135 } 136 137 return byte; 138 } 139 140 // Sends a byte with MSB ordering 141 static void __attribute__((noinline)) serial_write_byte(uint8_t data) { 142 uint8_t b = 8; 143 serial_output(); 144 while (b--) { 145 if (data & (1 << b)) { 146 serial_high(); 147 } else { 148 serial_low(); 149 } 150 serial_delay(); 151 } 152 } 153 154 // interrupt handle to be used by the slave device 155 void interrupt_handler(void *arg) { 156 split_shared_memory_lock_autounlock(); 157 chSysLockFromISR(); 158 159 sync_send(); 160 161 // read mid pulses 162 serial_delay_blip(); 163 164 uint8_t checksum_computed = 0; 165 int sstd_index = 0; 166 167 sstd_index = serial_read_byte(); 168 sync_send(); 169 170 split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; 171 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { 172 split_trans_initiator2target_buffer(trans)[i] = serial_read_byte(); 173 sync_send(); 174 checksum_computed += split_trans_initiator2target_buffer(trans)[i]; 175 } 176 checksum_computed ^= 7; 177 178 serial_read_byte(); 179 sync_send(); 180 181 // wait for the sync to finish sending 182 serial_delay(); 183 184 // Allow any slave processing to occur 185 if (trans->slave_callback) { 186 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); 187 } 188 189 uint8_t checksum = 0; 190 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { 191 serial_write_byte(split_trans_target2initiator_buffer(trans)[i]); 192 sync_send(); 193 serial_delay_half(); 194 checksum += split_trans_target2initiator_buffer(trans)[i]; 195 } 196 serial_write_byte(checksum ^ 7); 197 sync_send(); 198 199 // wait for the sync to finish sending 200 serial_delay(); 201 202 // end transaction 203 serial_input(); 204 205 // TODO: remove extra delay between transactions 206 serial_delay(); 207 208 chSysUnlockFromISR(); 209 } 210 211 static inline bool initiate_transaction(uint8_t sstd_index) { 212 if (sstd_index > NUM_TOTAL_TRANSACTIONS) return false; 213 214 split_shared_memory_lock_autounlock(); 215 216 split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; 217 218 // TODO: remove extra delay between transactions 219 serial_delay(); 220 221 // this code is very time dependent, so we need to disable interrupts 222 chSysLock(); 223 224 // signal to the slave that we want to start a transaction 225 serial_output(); 226 serial_low(); 227 serial_delay_blip(); 228 229 // wait for the slaves response 230 serial_input(); 231 serial_high(); 232 serial_delay(); 233 234 // check if the slave is present 235 if (serial_read_pin()) { 236 // slave failed to pull the line low, assume not present 237 serial_dprintf("serial::NO_RESPONSE\n"); 238 chSysUnlock(); 239 return false; 240 } 241 242 // if the slave is present synchronize with it 243 uint8_t checksum = 0; 244 // send data to the slave 245 serial_write_byte(sstd_index); // first chunk is transaction id 246 sync_recv(); 247 248 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { 249 serial_write_byte(split_trans_initiator2target_buffer(trans)[i]); 250 sync_recv(); 251 checksum += split_trans_initiator2target_buffer(trans)[i]; 252 } 253 serial_write_byte(checksum ^ 7); 254 sync_recv(); 255 256 serial_delay(); 257 serial_delay(); // read mid pulses 258 259 // receive data from the slave 260 uint8_t checksum_computed = 0; 261 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { 262 split_trans_target2initiator_buffer(trans)[i] = serial_read_byte(); 263 sync_recv(); 264 checksum_computed += split_trans_target2initiator_buffer(trans)[i]; 265 } 266 checksum_computed ^= 7; 267 uint8_t checksum_received = serial_read_byte(); 268 269 sync_recv(); 270 serial_delay(); 271 272 if ((checksum_computed) != (checksum_received)) { 273 serial_dprintf("serial::FAIL[%u,%u,%u]\n", checksum_computed, checksum_received, sstd_index); 274 serial_output(); 275 serial_high(); 276 277 chSysUnlock(); 278 return false; 279 } 280 281 // always, release the line when not in use 282 serial_high(); 283 serial_output(); 284 285 chSysUnlock(); 286 return true; 287 } 288 289 ///////// 290 // start transaction by initiator 291 // 292 // bool soft_serial_transaction(int sstd_index) 293 // 294 // this code is very time dependent, so we need to disable interrupts 295 bool soft_serial_transaction(int sstd_index) { 296 return initiate_transaction((uint8_t)sstd_index); 297 }