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