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