aboutsummaryrefslogtreecommitdiff
path: root/platforms/avr/drivers/serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/avr/drivers/serial.c')
-rw-r--r--platforms/avr/drivers/serial.c529
1 files changed, 529 insertions, 0 deletions
diff --git a/platforms/avr/drivers/serial.c b/platforms/avr/drivers/serial.c
new file mode 100644
index 000000000..9a7345a53
--- /dev/null
+++ b/platforms/avr/drivers/serial.c
@@ -0,0 +1,529 @@
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# define SLAVE_INT_ACK_WIDTH_UNIT 2
228# define SLAVE_INT_ACK_WIDTH 4
229
230inline static void serial_delay(void) ALWAYS_INLINE;
231inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); }
232
233inline static void serial_delay_half1(void) ALWAYS_INLINE;
234inline static void serial_delay_half1(void) { _delay_us(SERIAL_DELAY_HALF1); }
235
236inline static void serial_delay_half2(void) ALWAYS_INLINE;
237inline static void serial_delay_half2(void) { _delay_us(SERIAL_DELAY_HALF2); }
238
239inline static void serial_output(void) ALWAYS_INLINE;
240inline static void serial_output(void) { setPinOutput(SOFT_SERIAL_PIN); }
241
242// make the serial pin an input with pull-up resistor
243inline static void serial_input_with_pullup(void) ALWAYS_INLINE;
244inline static void serial_input_with_pullup(void) { setPinInputHigh(SOFT_SERIAL_PIN); }
245
246inline static uint8_t serial_read_pin(void) ALWAYS_INLINE;
247inline static uint8_t serial_read_pin(void) { return !!readPin(SOFT_SERIAL_PIN); }
248
249inline static void serial_low(void) ALWAYS_INLINE;
250inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); }
251
252inline static void serial_high(void) ALWAYS_INLINE;
253inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); }
254
255void soft_serial_initiator_init(void) {
256 serial_output();
257 serial_high();
258}
259
260void soft_serial_target_init(void) {
261 serial_input_with_pullup();
262
263 // Enable INT0-INT7
264 EIMSK |= EIMSK_BIT;
265 EICRx &= EICRx_BIT;
266}
267
268// Used by the sender to synchronize timing with the reciver.
269static void sync_recv(void) NO_INLINE;
270static void sync_recv(void) {
271 for (uint8_t i = 0; i < SERIAL_DELAY * 5 && serial_read_pin(); i++) {
272 }
273 // This shouldn't hang if the target disconnects because the
274 // serial line will float to high if the target does disconnect.
275 while (!serial_read_pin())
276 ;
277}
278
279// Used by the reciver to send a synchronization signal to the sender.
280static void sync_send(void) NO_INLINE;
281static void sync_send(void) {
282 serial_low();
283 serial_delay();
284 serial_high();
285}
286
287// Reads a byte from the serial line
288static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) NO_INLINE;
289static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) {
290 uint8_t byte, i, p, pb;
291
292 _delay_sub_us(READ_WRITE_START_ADJUST);
293 for (i = 0, byte = 0, p = PARITY; i < bit; i++) {
294 serial_delay_half1(); // read the middle of pulses
295 if (serial_read_pin()) {
296 byte = (byte << 1) | 1;
297 p ^= 1;
298 } else {
299 byte = (byte << 1) | 0;
300 p ^= 0;
301 }
302 _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
303 serial_delay_half2();
304 }
305 /* recive parity bit */
306 serial_delay_half1(); // read the middle of pulses
307 pb = serial_read_pin();
308 _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
309 serial_delay_half2();
310
311 *pterrcount += (p != pb) ? 1 : 0;
312
313 return byte;
314}
315
316// Sends a byte with MSB ordering
317void serial_write_chunk(uint8_t data, uint8_t bit) NO_INLINE;
318void serial_write_chunk(uint8_t data, uint8_t bit) {
319 uint8_t b, p;
320 for (p = PARITY, b = 1 << (bit - 1); b; b >>= 1) {
321 if (data & b) {
322 serial_high();
323 p ^= 1;
324 } else {
325 serial_low();
326 p ^= 0;
327 }
328 serial_delay();
329 }
330 /* send parity bit */
331 if (p & 1) {
332 serial_high();
333 } else {
334 serial_low();
335 }
336 serial_delay();
337
338 serial_low(); // sync_send() / senc_recv() need raise edge
339}
340
341static void serial_send_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
342static void serial_send_packet(uint8_t *buffer, uint8_t size) {
343 for (uint8_t i = 0; i < size; ++i) {
344 uint8_t data;
345 data = buffer[i];
346 sync_send();
347 serial_write_chunk(data, 8);
348 }
349}
350
351static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
352static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) {
353 uint8_t pecount = 0;
354 for (uint8_t i = 0; i < size; ++i) {
355 uint8_t data;
356 sync_recv();
357 data = serial_read_chunk(&pecount, 8);
358 buffer[i] = data;
359 }
360 return pecount == 0;
361}
362
363inline static void change_sender2reciver(void) {
364 sync_send(); // 0
365 serial_delay_half1(); // 1
366 serial_low(); // 2
367 serial_input_with_pullup(); // 2
368 serial_delay_half1(); // 3
369}
370
371inline static void change_reciver2sender(void) {
372 sync_recv(); // 0
373 serial_delay(); // 1
374 serial_low(); // 3
375 serial_output(); // 3
376 serial_delay_half1(); // 4
377}
378
379static inline uint8_t nibble_bits_count(uint8_t bits) {
380 bits = (bits & 0x5) + (bits >> 1 & 0x5);
381 bits = (bits & 0x3) + (bits >> 2 & 0x3);
382 return bits;
383}
384
385// interrupt handle to be used by the target device
386ISR(SERIAL_PIN_INTERRUPT) {
387 // recive transaction table index
388 uint8_t tid, bits;
389 uint8_t pecount = 0;
390 sync_recv();
391 bits = serial_read_chunk(&pecount, 8);
392 tid = bits >> 3;
393 bits = (bits & 7) != (nibble_bits_count(tid) & 7);
394 if (bits || pecount > 0 || tid > NUM_TOTAL_TRANSACTIONS) {
395 return;
396 }
397 serial_delay_half1();
398
399 serial_high(); // response step1 low->high
400 serial_output();
401 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH);
402 split_transaction_desc_t *trans = &split_transaction_table[tid];
403 serial_low(); // response step2 ack high->low
404
405 // If the transaction has a callback, we can execute it now
406 if (trans->slave_callback) {
407 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
408 }
409
410 // target send phase
411 if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size);
412 // target switch to input
413 change_sender2reciver();
414
415 // target recive phase
416 if (trans->initiator2target_buffer_size > 0) {
417 if (serial_recive_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) {
418 *trans->status = TRANSACTION_ACCEPTED;
419 } else {
420 *trans->status = TRANSACTION_DATA_ERROR;
421 }
422 } else {
423 *trans->status = TRANSACTION_ACCEPTED;
424 }
425
426 sync_recv(); // weit initiator output to high
427}
428
429/////////
430// start transaction by initiator
431//
432// int soft_serial_transaction(int sstd_index)
433//
434// Returns:
435// TRANSACTION_END
436// TRANSACTION_NO_RESPONSE
437// TRANSACTION_DATA_ERROR
438// this code is very time dependent, so we need to disable interrupts
439int soft_serial_transaction(int sstd_index) {
440 if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
441 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
442
443 if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
444
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 // send transaction table index
453 int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index));
454 sync_send();
455 _delay_sub_us(TID_SEND_ADJUST);
456 serial_write_chunk(tid, 8);
457 serial_delay_half1();
458
459 // wait for the target response (step1 low->high)
460 serial_input_with_pullup();
461 while (!serial_read_pin()) {
462 _delay_sub_us(2);
463 }
464
465 // check if the target is present (step2 high->low)
466 for (int i = 0; serial_read_pin(); i++) {
467 if (i > SLAVE_INT_ACK_WIDTH + 1) {
468 // slave failed to pull the line low, assume not present
469 serial_output();
470 serial_high();
471 *trans->status = TRANSACTION_NO_RESPONSE;
472 sei();
473 return TRANSACTION_NO_RESPONSE;
474 }
475 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);
476 }
477
478 // initiator recive phase
479 // if the target is present syncronize with it
480 if (trans->target2initiator_buffer_size > 0) {
481 if (!serial_recive_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) {
482 serial_output();
483 serial_high();
484 *trans->status = TRANSACTION_DATA_ERROR;
485 sei();
486 return TRANSACTION_DATA_ERROR;
487 }
488 }
489
490 // initiator switch to output
491 change_reciver2sender();
492
493 // initiator send phase
494 if (trans->initiator2target_buffer_size > 0) {
495 serial_send_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size);
496 }
497
498 // always, release the line when not in use
499 sync_send();
500
501 *trans->status = TRANSACTION_END;
502 sei();
503 return TRANSACTION_END;
504}
505
506int soft_serial_get_and_clean_status(int sstd_index) {
507 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
508 cli();
509 int retval = *trans->status;
510 *trans->status = 0;
511 ;
512 sei();
513 return retval;
514}
515#endif
516
517// Helix serial.c history
518// 2018-1-29 fork from let's split and add PD2, modify sync_recv() (#2308, bceffdefc)
519// 2018-6-28 bug fix master to slave comm and speed up (#3255, 1038bbef4)
520// (adjusted with avr-gcc 4.9.2)
521// 2018-7-13 remove USE_SERIAL_PD2 macro (#3374, f30d6dd78)
522// (adjusted with avr-gcc 4.9.2)
523// 2018-8-11 add support multi-type transaction (#3608, feb5e4aae)
524// (adjusted with avr-gcc 4.9.2)
525// 2018-10-21 fix serial and RGB animation conflict (#4191, 4665e4fff)
526// (adjusted with avr-gcc 7.3.0)
527// 2018-10-28 re-adjust compiler depend value of delay (#4269, 8517f8a66)
528// (adjusted with avr-gcc 5.4.0, 7.3.0)
529// 2018-12-17 copy to TOP/quantum/split_common/ and remove backward compatibility code (#4669)