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