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