]> git.donarmstrong.com Git - qmk_firmware.git/blob - keyboards/helix/serial.c
325c29a3f7045852ae87227fbda423afa60ef2b2
[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 //////////////// for backward compatibility ////////////////////////////////
74 #ifndef SERIAL_USE_MULTI_TRANSACTION
75 /* --- USE Simple API (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
84 SSTD_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
99 void serial_master_init(void)
100 { soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); }
101
102 void 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
108 int serial_update_buffers()
109 {
110     int result;
111     result = soft_serial_transaction();
112     return result;
113 }
114
115 #endif // end of Simple API (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
224 static SSTD_t *Transaction_table = NULL;
225 static uint8_t Transaction_table_size = 0;
226
227 inline static void serial_delay(void) ALWAYS_INLINE;
228 inline static
229 void serial_delay(void) {
230   _delay_us(SERIAL_DELAY);
231 }
232
233 inline static void serial_delay_half1(void) ALWAYS_INLINE;
234 inline static
235 void serial_delay_half1(void) {
236   _delay_us(SERIAL_DELAY_HALF1);
237 }
238
239 inline static void serial_delay_half2(void) ALWAYS_INLINE;
240 inline static
241 void serial_delay_half2(void) {
242   _delay_us(SERIAL_DELAY_HALF2);
243 }
244
245 inline static void serial_output(void) ALWAYS_INLINE;
246 inline static
247 void serial_output(void) {
248   SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
249 }
250
251 // make the serial pin an input with pull-up resistor
252 inline static void serial_input_with_pullup(void) ALWAYS_INLINE;
253 inline static
254 void serial_input_with_pullup(void) {
255   SERIAL_PIN_DDR  &= ~SERIAL_PIN_MASK;
256   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
257 }
258
259 inline static uint8_t serial_read_pin(void) ALWAYS_INLINE;
260 inline static
261 uint8_t serial_read_pin(void) {
262   return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
263 }
264
265 inline static void serial_low(void) ALWAYS_INLINE;
266 inline static
267 void serial_low(void) {
268   SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
269 }
270
271 inline static void serial_high(void) ALWAYS_INLINE;
272 inline static
273 void serial_high(void) {
274   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
275 }
276
277 void 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
285 void 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.
303 static void sync_recv(void) NO_INLINE;
304 static
305 void 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.
314 static void sync_send(void) NO_INLINE;
315 static
316 void sync_send(void) {
317   serial_low();
318   serial_delay();
319   serial_high();
320 }
321
322 // Reads a byte from the serial line
323 static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) NO_INLINE;
324 static 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
350 void serial_write_chunk(uint8_t data, uint8_t bit) NO_INLINE;
351 void 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
369 static void serial_send_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
370 static
371 void 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
380 static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
381 static
382 uint8_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
393 inline static
394 void 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
402 inline static
403 void 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
411 static 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
419 ISR(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
478 int  soft_serial_transaction(void) {
479   SSTD_t *trans = Transaction_table;
480 #else
481 int  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
567 int 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)