2 * WARNING: be careful changing this code, it is very timing dependent
11 # define F_CPU 16000000
15 #include <avr/interrupt.h>
16 #include <util/delay.h>
21 #ifdef SOFT_SERIAL_PIN
23 # ifdef __AVR_ATmega32U4__
24 // if using ATmega32U4 I2C, can not use PD0 and PD1 in soft serial.
26 # if SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1
27 # error Using ATmega32U4 I2C, so can not use PD0, PD1
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)))
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
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
60 # error invalid SOFT_SERIAL_PIN value
64 # error serial.c now support ATmega32U4 only
67 # define ALWAYS_INLINE __attribute__((always_inline))
68 # define NO_INLINE __attribute__((noinline))
69 # define _delay_sub_us(x) __builtin_avr_delay_cycles(x)
73 # define EVEN_PARITY 0
74 # define PARITY EVEN_PARITY
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
83 // ============ Standard setups ============
85 # ifndef SELECT_SOFT_SERIAL_SPEED
86 # define SELECT_SOFT_SERIAL_SPEED 1
87 // 0: about 189kbps (Experimental only)
88 // 1: about 137kbps (default)
96 # define TID_SEND_ADJUST 14
98 # define TID_SEND_ADJUST 2
101 # if SELECT_SOFT_SERIAL_SPEED == 0
103 # define SERIAL_DELAY 4 // micro sec
105 # define READ_WRITE_START_ADJUST 33 // cycles
106 # define READ_WRITE_WIDTH_ADJUST 3 // cycles
108 # define READ_WRITE_START_ADJUST 34 // cycles
109 # define READ_WRITE_WIDTH_ADJUST 7 // cycles
111 # elif SELECT_SOFT_SERIAL_SPEED == 1
113 # define SERIAL_DELAY 6 // micro sec
115 # define READ_WRITE_START_ADJUST 30 // cycles
116 # define READ_WRITE_WIDTH_ADJUST 3 // cycles
118 # define READ_WRITE_START_ADJUST 33 // cycles
119 # define READ_WRITE_WIDTH_ADJUST 7 // cycles
121 # elif SELECT_SOFT_SERIAL_SPEED == 2
123 # define SERIAL_DELAY 12 // micro sec
124 # define READ_WRITE_START_ADJUST 30 // cycles
126 # define READ_WRITE_WIDTH_ADJUST 3 // cycles
128 # define READ_WRITE_WIDTH_ADJUST 7 // cycles
130 # elif SELECT_SOFT_SERIAL_SPEED == 3
132 # define SERIAL_DELAY 24 // micro sec
133 # define READ_WRITE_START_ADJUST 30 // cycles
135 # define READ_WRITE_WIDTH_ADJUST 3 // cycles
137 # define READ_WRITE_WIDTH_ADJUST 7 // cycles
139 # elif SELECT_SOFT_SERIAL_SPEED == 4
141 # define SERIAL_DELAY 36 // micro sec
142 # define READ_WRITE_START_ADJUST 30 // cycles
144 # define READ_WRITE_WIDTH_ADJUST 3 // cycles
146 # define READ_WRITE_WIDTH_ADJUST 7 // cycles
148 # elif SELECT_SOFT_SERIAL_SPEED == 5
150 # define SERIAL_DELAY 48 // micro sec
151 # define READ_WRITE_START_ADJUST 30 // cycles
153 # define READ_WRITE_WIDTH_ADJUST 3 // cycles
155 # define READ_WRITE_WIDTH_ADJUST 7 // cycles
158 # error invalid SELECT_SOFT_SERIAL_SPEED value
159 # endif /* SELECT_SOFT_SERIAL_SPEED */
160 # endif /* SERIAL_DELAY */
162 # define SERIAL_DELAY_HALF1 (SERIAL_DELAY / 2)
163 # define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2)
165 # define SLAVE_INT_WIDTH_US 1
166 # ifndef SERIAL_USE_MULTI_TRANSACTION
167 # define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY
169 # define SLAVE_INT_ACK_WIDTH_UNIT 2
170 # define SLAVE_INT_ACK_WIDTH 4
173 static SSTD_t
*Transaction_table
= NULL
;
174 static uint8_t Transaction_table_size
= 0;
176 inline static void serial_delay(void) ALWAYS_INLINE
;
177 inline static void serial_delay(void) { _delay_us(SERIAL_DELAY
); }
179 inline static void serial_delay_half1(void) ALWAYS_INLINE
;
180 inline static void serial_delay_half1(void) { _delay_us(SERIAL_DELAY_HALF1
); }
182 inline static void serial_delay_half2(void) ALWAYS_INLINE
;
183 inline static void serial_delay_half2(void) { _delay_us(SERIAL_DELAY_HALF2
); }
185 inline static void serial_output(void) ALWAYS_INLINE
;
186 inline static void serial_output(void) { setPinOutput(SOFT_SERIAL_PIN
); }
188 // make the serial pin an input with pull-up resistor
189 inline static void serial_input_with_pullup(void) ALWAYS_INLINE
;
190 inline static void serial_input_with_pullup(void) { setPinInputHigh(SOFT_SERIAL_PIN
); }
192 inline static uint8_t serial_read_pin(void) ALWAYS_INLINE
;
193 inline static uint8_t serial_read_pin(void) { return !!readPin(SOFT_SERIAL_PIN
); }
195 inline static void serial_low(void) ALWAYS_INLINE
;
196 inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN
); }
198 inline static void serial_high(void) ALWAYS_INLINE
;
199 inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN
); }
201 void soft_serial_initiator_init(SSTD_t
*sstd_table
, int sstd_table_size
) {
202 Transaction_table
= sstd_table
;
203 Transaction_table_size
= (uint8_t)sstd_table_size
;
208 void soft_serial_target_init(SSTD_t
*sstd_table
, int sstd_table_size
) {
209 Transaction_table
= sstd_table
;
210 Transaction_table_size
= (uint8_t)sstd_table_size
;
211 serial_input_with_pullup();
213 // Enable INT0-INT3,INT6
215 # if SOFT_SERIAL_PIN == E6
216 // Trigger on falling edge of INT6
219 // Trigger on falling edge of INT0-INT3
224 // Used by the sender to synchronize timing with the reciver.
225 static void sync_recv(void) NO_INLINE
;
226 static void sync_recv(void) {
227 for (uint8_t i
= 0; i
< SERIAL_DELAY
* 5 && serial_read_pin(); i
++) {
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())
235 // Used by the reciver to send a synchronization signal to the sender.
236 static void sync_send(void) NO_INLINE
;
237 static void sync_send(void) {
243 // Reads a byte from the serial line
244 static uint8_t serial_read_chunk(uint8_t *pterrcount
, uint8_t bit
) NO_INLINE
;
245 static uint8_t serial_read_chunk(uint8_t *pterrcount
, uint8_t bit
) {
246 uint8_t byte
, i
, p
, pb
;
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;
255 byte
= (byte
<< 1) | 0;
258 _delay_sub_us(READ_WRITE_WIDTH_ADJUST
);
259 serial_delay_half2();
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();
267 *pterrcount
+= (p
!= pb
) ? 1 : 0;
272 // Sends a byte with MSB ordering
273 void serial_write_chunk(uint8_t data
, uint8_t bit
) NO_INLINE
;
274 void serial_write_chunk(uint8_t data
, uint8_t bit
) {
276 for (p
= PARITY
, b
= 1 << (bit
- 1); b
; b
>>= 1) {
286 /* send parity bit */
294 serial_low(); // sync_send() / senc_recv() need raise edge
297 static void serial_send_packet(uint8_t *buffer
, uint8_t size
) NO_INLINE
;
298 static void serial_send_packet(uint8_t *buffer
, uint8_t size
) {
299 for (uint8_t i
= 0; i
< size
; ++i
) {
303 serial_write_chunk(data
, 8);
307 static uint8_t serial_recive_packet(uint8_t *buffer
, uint8_t size
) NO_INLINE
;
308 static uint8_t serial_recive_packet(uint8_t *buffer
, uint8_t size
) {
310 for (uint8_t i
= 0; i
< size
; ++i
) {
313 data
= serial_read_chunk(&pecount
, 8);
319 inline static void change_sender2reciver(void) {
321 serial_delay_half1(); // 1
323 serial_input_with_pullup(); // 2
324 serial_delay_half1(); // 3
327 inline static void change_reciver2sender(void) {
331 serial_output(); // 3
332 serial_delay_half1(); // 4
335 static inline uint8_t nibble_bits_count(uint8_t bits
) {
336 bits
= (bits
& 0x5) + (bits
>> 1 & 0x5);
337 bits
= (bits
& 0x3) + (bits
>> 2 & 0x3);
341 // interrupt handle to be used by the target device
342 ISR(SERIAL_PIN_INTERRUPT
) {
343 # ifndef SERIAL_USE_MULTI_TRANSACTION
346 SSTD_t
*trans
= Transaction_table
;
348 // recive transaction table index
352 bits
= serial_read_chunk(&pecount
, 7);
354 bits
= (bits
& 7) != nibble_bits_count(tid
);
355 if (bits
|| pecount
> 0 || tid
> Transaction_table_size
) {
358 serial_delay_half1();
360 serial_high(); // response step1 low->high
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
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();
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
;
377 *trans
->status
= TRANSACTION_DATA_ERROR
;
380 *trans
->status
= TRANSACTION_ACCEPTED
;
383 sync_recv(); // weit initiator output to high
387 // start transaction by initiator
389 // int soft_serial_transaction(int sstd_index)
393 // TRANSACTION_NO_RESPONSE
394 // TRANSACTION_DATA_ERROR
395 // this code is very time dependent, so we need to disable interrupts
396 # ifndef SERIAL_USE_MULTI_TRANSACTION
397 int soft_serial_transaction(void) {
398 SSTD_t
*trans
= Transaction_table
;
400 int 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
];
406 // signal to the target that we want to start a transaction
409 _delay_us(SLAVE_INT_WIDTH_US
);
411 # ifndef SERIAL_USE_MULTI_TRANSACTION
412 // wait for the target response
413 serial_input_with_pullup();
414 _delay_us(SLAVE_INT_RESPONSE_TIME
);
416 // check if the target is present
417 if (serial_read_pin()) {
418 // target failed to pull the line low, assume not present
421 *trans
->status
= TRANSACTION_NO_RESPONSE
;
423 return TRANSACTION_NO_RESPONSE
;
427 // send transaction table index
428 int tid
= (sstd_index
<< 3) | (7 & nibble_bits_count(sstd_index
));
430 _delay_sub_us(TID_SEND_ADJUST
);
431 serial_write_chunk(tid
, 7);
432 serial_delay_half1();
434 // wait for the target response (step1 low->high)
435 serial_input_with_pullup();
436 while (!serial_read_pin()) {
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
446 *trans
->status
= TRANSACTION_NO_RESPONSE
;
448 return TRANSACTION_NO_RESPONSE
;
450 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT
);
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
)) {
460 *trans
->status
= TRANSACTION_DATA_ERROR
;
462 return TRANSACTION_DATA_ERROR
;
466 // initiator switch to output
467 change_reciver2sender();
469 // initiator send phase
470 if (trans
->initiator2target_buffer_size
> 0) {
471 serial_send_packet((uint8_t *)trans
->initiator2target_buffer
, trans
->initiator2target_buffer_size
);
474 // always, release the line when not in use
477 *trans
->status
= TRANSACTION_END
;
479 return TRANSACTION_END
;
482 # ifdef SERIAL_USE_MULTI_TRANSACTION
483 int soft_serial_get_and_clean_status(int sstd_index
) {
484 SSTD_t
*trans
= &Transaction_table
[sstd_index
];
486 int retval
= *trans
->status
;
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)