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