aboutsummaryrefslogtreecommitdiff
path: root/keyboards/ai03/orbit/serial.c
diff options
context:
space:
mode:
authorAkshay <[email protected]>2022-04-10 12:13:40 +0100
committerAkshay <[email protected]>2022-04-10 12:13:40 +0100
commitdc90387ce7d8ba7b607d9c48540bf6d8b560f14d (patch)
tree4ccb8fa5886b66fa9d480edef74236c27f035e16 /keyboards/ai03/orbit/serial.c
Diffstat (limited to 'keyboards/ai03/orbit/serial.c')
-rw-r--r--keyboards/ai03/orbit/serial.c545
1 files changed, 545 insertions, 0 deletions
diff --git a/keyboards/ai03/orbit/serial.c b/keyboards/ai03/orbit/serial.c
new file mode 100644
index 000000000..636dfa090
--- /dev/null
+++ b/keyboards/ai03/orbit/serial.c
@@ -0,0 +1,545 @@
1/*
2 * WARNING: be careful changing this code, it is very timing dependent
3 *
4 * 2018-10-28 checked
5 * avr-gcc 4.9.2
6 * avr-gcc 5.4.0
7 * avr-gcc 7.3.0
8 */
9
10#ifndef F_CPU
11#define F_CPU 16000000
12#endif
13
14#include <avr/io.h>
15#include <avr/interrupt.h>
16#include <util/delay.h>
17#include <stddef.h>
18#include <stdbool.h>
19#include "serial.h"
20
21#ifdef SOFT_SERIAL_PIN
22
23#ifdef __AVR_ATmega32U4__
24 // if using ATmega32U4 I2C, can not use PD0 and PD1 in soft serial.
25 #ifdef USE_AVR_I2C
26 #if SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1
27 #error Using ATmega32U4 I2C, so can not use PD0, PD1
28 #endif
29 #endif
30
31 #if SOFT_SERIAL_PIN >= D0 && SOFT_SERIAL_PIN <= D3
32 #define SERIAL_PIN_DDR DDRD
33 #define SERIAL_PIN_PORT PORTD
34 #define SERIAL_PIN_INPUT PIND
35 #if SOFT_SERIAL_PIN == D0
36 #define SERIAL_PIN_MASK _BV(PD0)
37 #define EIMSK_BIT _BV(INT0)
38 #define EICRx_BIT (~(_BV(ISC00) | _BV(ISC01)))
39 #define SERIAL_PIN_INTERRUPT INT0_vect
40 #elif SOFT_SERIAL_PIN == D1
41 #define SERIAL_PIN_MASK _BV(PD1)
42 #define EIMSK_BIT _BV(INT1)
43 #define EICRx_BIT (~(_BV(ISC10) | _BV(ISC11)))
44 #define SERIAL_PIN_INTERRUPT INT1_vect
45 #elif SOFT_SERIAL_PIN == D2
46 #define SERIAL_PIN_MASK _BV(PD2)
47 #define EIMSK_BIT _BV(INT2)
48 #define EICRx_BIT (~(_BV(ISC20) | _BV(ISC21)))
49 #define SERIAL_PIN_INTERRUPT INT2_vect
50 #elif SOFT_SERIAL_PIN == D3
51 #define SERIAL_PIN_MASK _BV(PD3)
52 #define EIMSK_BIT _BV(INT3)
53 #define EICRx_BIT (~(_BV(ISC30) | _BV(ISC31)))
54 #define SERIAL_PIN_INTERRUPT INT3_vect
55 #endif
56 #elif SOFT_SERIAL_PIN == E6
57 #define SERIAL_PIN_DDR DDRE
58 #define SERIAL_PIN_PORT PORTE
59 #define SERIAL_PIN_INPUT PINE
60 #define SERIAL_PIN_MASK _BV(PE6)
61 #define EIMSK_BIT _BV(INT6)
62 #define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))
63 #define SERIAL_PIN_INTERRUPT INT6_vect
64 #else
65 #error invalid SOFT_SERIAL_PIN value
66 #endif
67
68#else
69 #error serial.c now support ATmega32U4 only
70#endif
71
72#define ALWAYS_INLINE __attribute__((always_inline))
73#define NO_INLINE __attribute__((noinline))
74#define _delay_sub_us(x) __builtin_avr_delay_cycles(x)
75
76// parity check
77#define ODD_PARITY 1
78#define EVEN_PARITY 0
79#define PARITY EVEN_PARITY
80
81#ifdef SERIAL_DELAY
82 // custom setup in config.h
83 // #define TID_SEND_ADJUST 2
84 // #define SERIAL_DELAY 6 // micro sec
85 // #define READ_WRITE_START_ADJUST 30 // cycles
86 // #define READ_WRITE_WIDTH_ADJUST 8 // cycles
87#else
88// ============ Standard setups ============
89
90#ifndef SELECT_SOFT_SERIAL_SPEED
91#define SELECT_SOFT_SERIAL_SPEED 1
92// 0: about 189kbps (Experimental only)
93// 1: about 137kbps (default)
94// 2: about 75kbps
95// 3: about 39kbps
96// 4: about 26kbps
97// 5: about 20kbps
98#endif
99
100#if __GNUC__ < 6
101 #define TID_SEND_ADJUST 14
102#else
103 #define TID_SEND_ADJUST 2
104#endif
105
106#if SELECT_SOFT_SERIAL_SPEED == 0
107 // Very High speed
108 #define SERIAL_DELAY 4 // micro sec
109 #if __GNUC__ < 6
110 #define READ_WRITE_START_ADJUST 33 // cycles
111 #define READ_WRITE_WIDTH_ADJUST 3 // cycles
112 #else
113 #define READ_WRITE_START_ADJUST 34 // cycles
114 #define READ_WRITE_WIDTH_ADJUST 7 // cycles
115 #endif
116#elif SELECT_SOFT_SERIAL_SPEED == 1
117 // High speed
118 #define SERIAL_DELAY 6 // micro sec
119 #if __GNUC__ < 6
120 #define READ_WRITE_START_ADJUST 30 // cycles
121 #define READ_WRITE_WIDTH_ADJUST 3 // cycles
122 #else
123 #define READ_WRITE_START_ADJUST 33 // cycles
124 #define READ_WRITE_WIDTH_ADJUST 7 // cycles
125 #endif
126#elif SELECT_SOFT_SERIAL_SPEED == 2
127 // Middle speed
128 #define SERIAL_DELAY 12 // micro sec
129 #define READ_WRITE_START_ADJUST 30 // cycles
130 #if __GNUC__ < 6
131 #define READ_WRITE_WIDTH_ADJUST 3 // cycles
132 #else
133 #define READ_WRITE_WIDTH_ADJUST 7 // cycles
134 #endif
135#elif SELECT_SOFT_SERIAL_SPEED == 3
136 // Low speed
137 #define SERIAL_DELAY 24 // micro sec
138 #define READ_WRITE_START_ADJUST 30 // cycles
139 #if __GNUC__ < 6
140 #define READ_WRITE_WIDTH_ADJUST 3 // cycles
141 #else
142 #define READ_WRITE_WIDTH_ADJUST 7 // cycles
143 #endif
144#elif SELECT_SOFT_SERIAL_SPEED == 4
145 // Very Low speed
146 #define SERIAL_DELAY 36 // micro sec
147 #define READ_WRITE_START_ADJUST 30 // cycles
148 #if __GNUC__ < 6
149 #define READ_WRITE_WIDTH_ADJUST 3 // cycles
150 #else
151 #define READ_WRITE_WIDTH_ADJUST 7 // cycles
152 #endif
153#elif SELECT_SOFT_SERIAL_SPEED == 5
154 // Ultra Low speed
155 #define SERIAL_DELAY 48 // micro sec
156 #define READ_WRITE_START_ADJUST 30 // cycles
157 #if __GNUC__ < 6
158 #define READ_WRITE_WIDTH_ADJUST 3 // cycles
159 #else
160 #define READ_WRITE_WIDTH_ADJUST 7 // cycles
161 #endif
162#else
163#error invalid SELECT_SOFT_SERIAL_SPEED value
164#endif /* SELECT_SOFT_SERIAL_SPEED */
165#endif /* SERIAL_DELAY */
166
167#define SERIAL_DELAY_HALF1 (SERIAL_DELAY/2)
168#define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY/2)
169
170#define SLAVE_INT_WIDTH_US 1
171#ifndef SERIAL_USE_MULTI_TRANSACTION
172 #define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY
173#else
174 #define SLAVE_INT_ACK_WIDTH_UNIT 2
175 #define SLAVE_INT_ACK_WIDTH 4
176#endif
177
178static SSTD_t *Transaction_table = NULL;
179static uint8_t Transaction_table_size = 0;
180
181inline static void serial_delay(void) ALWAYS_INLINE;
182inline static
183void serial_delay(void) {
184 _delay_us(SERIAL_DELAY);
185}
186
187inline static void serial_delay_half1(void) ALWAYS_INLINE;
188inline static
189void serial_delay_half1(void) {
190 _delay_us(SERIAL_DELAY_HALF1);
191}
192
193inline static void serial_delay_half2(void) ALWAYS_INLINE;
194inline static
195void serial_delay_half2(void) {
196 _delay_us(SERIAL_DELAY_HALF2);
197}
198
199inline static void serial_output(void) ALWAYS_INLINE;
200inline static
201void serial_output(void) {
202 SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
203}
204
205// make the serial pin an input with pull-up resistor
206inline static void serial_input_with_pullup(void) ALWAYS_INLINE;
207inline static
208void serial_input_with_pullup(void) {
209 SERIAL_PIN_DDR &= ~SERIAL_PIN_MASK;
210 SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
211}
212
213inline static uint8_t serial_read_pin(void) ALWAYS_INLINE;
214inline static
215uint8_t serial_read_pin(void) {
216 return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
217}
218
219inline static void serial_low(void) ALWAYS_INLINE;
220inline static
221void serial_low(void) {
222 SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
223}
224
225inline static void serial_high(void) ALWAYS_INLINE;
226inline static
227void serial_high(void) {
228 SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
229}
230
231void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size)
232{
233 Transaction_table = sstd_table;
234 Transaction_table_size = (uint8_t)sstd_table_size;
235 serial_output();
236 serial_high();
237}
238
239void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size)
240{
241 Transaction_table = sstd_table;
242 Transaction_table_size = (uint8_t)sstd_table_size;
243 serial_input_with_pullup();
244
245 // Enable INT0-INT3,INT6
246 EIMSK |= EIMSK_BIT;
247#if SERIAL_PIN_MASK == _BV(PE6)
248 // Trigger on falling edge of INT6
249 EICRB &= EICRx_BIT;
250#else
251 // Trigger on falling edge of INT0-INT3
252 EICRA &= EICRx_BIT;
253#endif
254}
255
256// Used by the sender to synchronize timing with the reciver.
257static void sync_recv(void) NO_INLINE;
258static
259void sync_recv(void) {
260 for (uint8_t i = 0; i < SERIAL_DELAY*5 && serial_read_pin(); i++ ) {
261 }
262 // This shouldn't hang if the target disconnects because the
263 // serial line will float to high if the target does disconnect.
264 while (!serial_read_pin());
265}
266
267// Used by the reciver to send a synchronization signal to the sender.
268static void sync_send(void) NO_INLINE;
269static
270void sync_send(void) {
271 serial_low();
272 serial_delay();
273 serial_high();
274}
275
276// Reads a byte from the serial line
277static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) NO_INLINE;
278static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) {
279 uint8_t byte, i, p, pb;
280
281 _delay_sub_us(READ_WRITE_START_ADJUST);
282 for( i = 0, byte = 0, p = PARITY; i < bit; i++ ) {
283 serial_delay_half1(); // read the middle of pulses
284 if( serial_read_pin() ) {
285 byte = (byte << 1) | 1; p ^= 1;
286 } else {
287 byte = (byte << 1) | 0; p ^= 0;
288 }
289 _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
290 serial_delay_half2();
291 }
292 /* recive parity bit */
293 serial_delay_half1(); // read the middle of pulses
294 pb = serial_read_pin();
295 _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
296 serial_delay_half2();
297
298 *pterrcount += (p != pb)? 1 : 0;
299
300 return byte;
301}
302
303// Sends a byte with MSB ordering
304void serial_write_chunk(uint8_t data, uint8_t bit) NO_INLINE;
305void serial_write_chunk(uint8_t data, uint8_t bit) {
306 uint8_t b, p;
307 for( p = PARITY, b = 1<<(bit-1); b ; b >>= 1) {
308 if(data & b) {
309 serial_high(); p ^= 1;
310 } else {
311 serial_low(); p ^= 0;
312 }
313 serial_delay();
314 }
315 /* send parity bit */
316 if(p & 1) { serial_high(); }
317 else { serial_low(); }
318 serial_delay();
319
320 serial_low(); // sync_send() / senc_recv() need raise edge
321}
322
323static void serial_send_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
324static
325void serial_send_packet(uint8_t *buffer, uint8_t size) {
326 for (uint8_t i = 0; i < size; ++i) {
327 uint8_t data;
328 data = buffer[i];
329 sync_send();
330 serial_write_chunk(data,8);
331 }
332}
333
334static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
335static
336uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) {
337 uint8_t pecount = 0;
338 for (uint8_t i = 0; i < size; ++i) {
339 uint8_t data;
340 sync_recv();
341 data = serial_read_chunk(&pecount, 8);
342 buffer[i] = data;
343 }
344 return pecount == 0;
345}
346
347inline static
348void change_sender2reciver(void) {
349 sync_send(); //0
350 serial_delay_half1(); //1
351 serial_low(); //2
352 serial_input_with_pullup(); //2
353 serial_delay_half1(); //3
354}
355
356inline static
357void change_reciver2sender(void) {
358 sync_recv(); //0
359 serial_delay(); //1
360 serial_low(); //3
361 serial_output(); //3
362 serial_delay_half1(); //4
363}
364
365static inline uint8_t nibble_bits_count(uint8_t bits)
366{
367 bits = (bits & 0x5) + (bits >> 1 & 0x5);
368 bits = (bits & 0x3) + (bits >> 2 & 0x3);
369 return bits;
370}
371
372// interrupt handle to be used by the target device
373ISR(SERIAL_PIN_INTERRUPT) {
374
375#ifndef SERIAL_USE_MULTI_TRANSACTION
376 serial_low();
377 serial_output();
378 SSTD_t *trans = Transaction_table;
379#else
380 // recive transaction table index
381 uint8_t tid, bits;
382 uint8_t pecount = 0;
383 sync_recv();
384 bits = serial_read_chunk(&pecount,7);
385 tid = bits>>3;
386 bits = (bits&7) != nibble_bits_count(tid);
387 if( bits || pecount> 0 || tid > Transaction_table_size ) {
388 return;
389 }
390 serial_delay_half1();
391
392 serial_high(); // response step1 low->high
393 serial_output();
394 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT*SLAVE_INT_ACK_WIDTH);
395 SSTD_t *trans = &Transaction_table[tid];
396 serial_low(); // response step2 ack high->low
397#endif
398
399 // target send phase
400 if( trans->target2initiator_buffer_size > 0 )
401 serial_send_packet((uint8_t *)trans->target2initiator_buffer,
402 trans->target2initiator_buffer_size);
403 // target switch to input
404 change_sender2reciver();
405
406 // target recive phase
407 if( trans->initiator2target_buffer_size > 0 ) {
408 if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer,
409 trans->initiator2target_buffer_size) ) {
410 *trans->status = TRANSACTION_ACCEPTED;
411 } else {
412 *trans->status = TRANSACTION_DATA_ERROR;
413 }
414 } else {
415 *trans->status = TRANSACTION_ACCEPTED;
416 }
417
418 sync_recv(); //weit initiator output to high
419}
420
421/////////
422// start transaction by initiator
423//
424// int soft_serial_transaction(int sstd_index)
425//
426// Returns:
427// TRANSACTION_END
428// TRANSACTION_NO_RESPONSE
429// TRANSACTION_DATA_ERROR
430// this code is very time dependent, so we need to disable interrupts
431#ifndef SERIAL_USE_MULTI_TRANSACTION
432int soft_serial_transaction(void) {
433 SSTD_t *trans = Transaction_table;
434#else
435int soft_serial_transaction(int sstd_index) {
436 if( sstd_index > Transaction_table_size )
437 return TRANSACTION_TYPE_ERROR;
438 SSTD_t *trans = &Transaction_table[sstd_index];
439#endif
440 cli();
441
442 // signal to the target that we want to start a transaction
443 serial_output();
444 serial_low();
445 _delay_us(SLAVE_INT_WIDTH_US);
446
447#ifndef SERIAL_USE_MULTI_TRANSACTION
448 // wait for the target response
449 serial_input_with_pullup();
450 _delay_us(SLAVE_INT_RESPONSE_TIME);
451
452 // check if the target is present
453 if (serial_read_pin()) {
454 // target failed to pull the line low, assume not present
455 serial_output();
456 serial_high();
457 *trans->status = TRANSACTION_NO_RESPONSE;
458 sei();
459 return TRANSACTION_NO_RESPONSE;
460 }
461
462#else
463 // send transaction table index
464 int tid = (sstd_index<<3) | (7 & nibble_bits_count(sstd_index));
465 sync_send();
466 _delay_sub_us(TID_SEND_ADJUST);
467 serial_write_chunk(tid, 7);
468 serial_delay_half1();
469
470 // wait for the target response (step1 low->high)
471 serial_input_with_pullup();
472 while( !serial_read_pin() ) {
473 _delay_sub_us(2);
474 }
475
476 // check if the target is present (step2 high->low)
477 for( int i = 0; serial_read_pin(); i++ ) {
478 if (i > SLAVE_INT_ACK_WIDTH + 1) {
479 // slave failed to pull the line low, assume not present
480 serial_output();
481 serial_high();
482 *trans->status = TRANSACTION_NO_RESPONSE;
483 sei();
484 return TRANSACTION_NO_RESPONSE;
485 }
486 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);
487 }
488#endif
489
490 // initiator recive phase
491 // if the target is present syncronize with it
492 if( trans->target2initiator_buffer_size > 0 ) {
493 if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer,
494 trans->target2initiator_buffer_size) ) {
495 serial_output();
496 serial_high();
497 *trans->status = TRANSACTION_DATA_ERROR;
498 sei();
499 return TRANSACTION_DATA_ERROR;
500 }
501 }
502
503 // initiator switch to output
504 change_reciver2sender();
505
506 // initiator send phase
507 if( trans->initiator2target_buffer_size > 0 ) {
508 serial_send_packet((uint8_t *)trans->initiator2target_buffer,
509 trans->initiator2target_buffer_size);
510 }
511
512 // always, release the line when not in use
513 sync_send();
514
515 *trans->status = TRANSACTION_END;
516 sei();
517 return TRANSACTION_END;
518}
519
520#ifdef SERIAL_USE_MULTI_TRANSACTION
521int soft_serial_get_and_clean_status(int sstd_index) {
522 SSTD_t *trans = &Transaction_table[sstd_index];
523 cli();
524 int retval = *trans->status;
525 *trans->status = 0;;
526 sei();
527 return retval;
528}
529#endif
530
531#endif
532
533// Helix serial.c history
534// 2018-1-29 fork from let's split and add PD2, modify sync_recv() (#2308, bceffdefc)
535// 2018-6-28 bug fix master to slave comm and speed up (#3255, 1038bbef4)
536// (adjusted with avr-gcc 4.9.2)
537// 2018-7-13 remove USE_SERIAL_PD2 macro (#3374, f30d6dd78)
538// (adjusted with avr-gcc 4.9.2)
539// 2018-8-11 add support multi-type transaction (#3608, feb5e4aae)
540// (adjusted with avr-gcc 4.9.2)
541// 2018-10-21 fix serial and RGB animation conflict (#4191, 4665e4fff)
542// (adjusted with avr-gcc 7.3.0)
543// 2018-10-28 re-adjust compiler depend value of delay (#4269, 8517f8a66)
544// (adjusted with avr-gcc 5.4.0, 7.3.0)
545// 2018-12-17 copy to TOP/quantum/split_common/ and remove backward compatibility code (#4669)