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