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