|
@@ -0,0 +1,476 @@
|
|
1
|
+//
|
|
2
|
+// Created by robin on 2/24/17.
|
|
3
|
+//
|
|
4
|
+
|
|
5
|
+// When set, _DEBUG co-opts pins 11 and 13 for debugging with an
|
|
6
|
+// oscilloscope or logic analyzer. Beware: it also slightly modifies
|
|
7
|
+// the bit times, so don't rely on it too much at high baud rates
|
|
8
|
+#define _DEBUG 0
|
|
9
|
+#define _DEBUG_PIN1 11
|
|
10
|
+#define _DEBUG_PIN2 13
|
|
11
|
+//
|
|
12
|
+// Includes
|
|
13
|
+//
|
|
14
|
+#include <avr/interrupt.h>
|
|
15
|
+#include <avr/pgmspace.h>
|
|
16
|
+#include <Arduino.h>
|
|
17
|
+#include "SoftwareSerial8e1.h"
|
|
18
|
+#include <util/delay_basic.h>
|
|
19
|
+
|
|
20
|
+//
|
|
21
|
+// Statics
|
|
22
|
+//
|
|
23
|
+SoftwareSerial8e1 *SoftwareSerial8e1::active_object = 0;
|
|
24
|
+uint8_t SoftwareSerial8e1::_receive_buffer[_SS_MAX_RX_BUFF];
|
|
25
|
+volatile uint8_t SoftwareSerial8e1::_receive_buffer_tail = 0;
|
|
26
|
+volatile uint8_t SoftwareSerial8e1::_receive_buffer_head = 0;
|
|
27
|
+
|
|
28
|
+//
|
|
29
|
+// Debugging
|
|
30
|
+//
|
|
31
|
+// This function generates a brief pulse
|
|
32
|
+// for debugging or measuring on an oscilloscope.
|
|
33
|
+#if _DEBUG
|
|
34
|
+inline void DebugPulse(uint8_t pin, uint8_t count)
|
|
35
|
+{
|
|
36
|
+ volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin));
|
|
37
|
+
|
|
38
|
+ uint8_t val = *pport;
|
|
39
|
+ while (count--)
|
|
40
|
+ {
|
|
41
|
+ *pport = val | digitalPinToBitMask(pin);
|
|
42
|
+ *pport = val;
|
|
43
|
+ }
|
|
44
|
+}
|
|
45
|
+#else
|
|
46
|
+inline void DebugPulse(uint8_t, uint8_t) {}
|
|
47
|
+#endif
|
|
48
|
+
|
|
49
|
+//
|
|
50
|
+// Private methods
|
|
51
|
+//
|
|
52
|
+
|
|
53
|
+/* static */
|
|
54
|
+inline void SoftwareSerial8e1::tunedDelay(uint16_t delay) {
|
|
55
|
+ _delay_loop_2(delay);
|
|
56
|
+}
|
|
57
|
+
|
|
58
|
+// This function sets the current object as the "listening"
|
|
59
|
+// one and returns true if it replaces another
|
|
60
|
+bool SoftwareSerial8e1::listen()
|
|
61
|
+{
|
|
62
|
+ if (!_rx_delay_stopbit)
|
|
63
|
+ return false;
|
|
64
|
+
|
|
65
|
+ if (active_object != this)
|
|
66
|
+ {
|
|
67
|
+ if (active_object)
|
|
68
|
+ active_object->stopListening();
|
|
69
|
+
|
|
70
|
+ _buffer_overflow = false;
|
|
71
|
+ _receive_buffer_head = _receive_buffer_tail = 0;
|
|
72
|
+ active_object = this;
|
|
73
|
+
|
|
74
|
+ setRxIntMsk(true);
|
|
75
|
+ return true;
|
|
76
|
+ }
|
|
77
|
+
|
|
78
|
+ return false;
|
|
79
|
+}
|
|
80
|
+
|
|
81
|
+// Stop listening. Returns true if we were actually listening.
|
|
82
|
+bool SoftwareSerial8e1::stopListening()
|
|
83
|
+{
|
|
84
|
+ if (active_object == this)
|
|
85
|
+ {
|
|
86
|
+ setRxIntMsk(false);
|
|
87
|
+ active_object = NULL;
|
|
88
|
+ return true;
|
|
89
|
+ }
|
|
90
|
+ return false;
|
|
91
|
+}
|
|
92
|
+
|
|
93
|
+//
|
|
94
|
+// The receive routine called by the interrupt handler
|
|
95
|
+//
|
|
96
|
+void SoftwareSerial8e1::recv()
|
|
97
|
+{
|
|
98
|
+
|
|
99
|
+#if GCC_VERSION < 40302
|
|
100
|
+// Work-around for avr-gcc 4.3.0 OSX version bug
|
|
101
|
+// Preserve the registers that the compiler misses
|
|
102
|
+// (courtesy of Arduino forum user *etracer*)
|
|
103
|
+ asm volatile(
|
|
104
|
+ "push r18 \n\t"
|
|
105
|
+ "push r19 \n\t"
|
|
106
|
+ "push r20 \n\t"
|
|
107
|
+ "push r21 \n\t"
|
|
108
|
+ "push r22 \n\t"
|
|
109
|
+ "push r23 \n\t"
|
|
110
|
+ "push r26 \n\t"
|
|
111
|
+ "push r27 \n\t"
|
|
112
|
+ ::);
|
|
113
|
+#endif
|
|
114
|
+
|
|
115
|
+ uint8_t d = 0;
|
|
116
|
+
|
|
117
|
+ // If RX line is high, then we don't see any start bit
|
|
118
|
+ // so interrupt is probably not for us
|
|
119
|
+ if (_inverse_logic ? rx_pin_read() : !rx_pin_read())
|
|
120
|
+ {
|
|
121
|
+ // Disable further interrupts during reception, this prevents
|
|
122
|
+ // triggering another interrupt directly after we return, which can
|
|
123
|
+ // cause problems at higher baudrates.
|
|
124
|
+ setRxIntMsk(false);
|
|
125
|
+
|
|
126
|
+ // Wait approximately 1/2 of a bit width to "center" the sample
|
|
127
|
+ tunedDelay(_rx_delay_centering);
|
|
128
|
+ DebugPulse(_DEBUG_PIN2, 1);
|
|
129
|
+
|
|
130
|
+ // Read each of the 8 bits
|
|
131
|
+ for (uint8_t i=8; i > 0; --i)
|
|
132
|
+ {
|
|
133
|
+ tunedDelay(_rx_delay_intrabit);
|
|
134
|
+ d >>= 1;
|
|
135
|
+ DebugPulse(_DEBUG_PIN2, 1);
|
|
136
|
+ if (rx_pin_read())
|
|
137
|
+ d |= 0x80;
|
|
138
|
+ }
|
|
139
|
+
|
|
140
|
+ if (_inverse_logic)
|
|
141
|
+ d = ~d;
|
|
142
|
+
|
|
143
|
+ // if buffer full, set the overflow flag and return
|
|
144
|
+ uint8_t next = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF;
|
|
145
|
+ if (next != _receive_buffer_head)
|
|
146
|
+ {
|
|
147
|
+ // save new data in buffer: tail points to where byte goes
|
|
148
|
+ _receive_buffer[_receive_buffer_tail] = d; // save new byte
|
|
149
|
+ _receive_buffer_tail = next;
|
|
150
|
+ }
|
|
151
|
+ else
|
|
152
|
+ {
|
|
153
|
+ DebugPulse(_DEBUG_PIN1, 1);
|
|
154
|
+ _buffer_overflow = true;
|
|
155
|
+ }
|
|
156
|
+
|
|
157
|
+ // skip the parity bit
|
|
158
|
+ tunedDelay(_rx_delay_stopbit);
|
|
159
|
+ DebugPulse(_DEBUG_PIN1, 1);
|
|
160
|
+
|
|
161
|
+ // skip the stop bit
|
|
162
|
+ tunedDelay(_rx_delay_stopbit);
|
|
163
|
+ DebugPulse(_DEBUG_PIN1, 1);
|
|
164
|
+
|
|
165
|
+ // Re-enable interrupts when we're sure to be inside the stop bit
|
|
166
|
+ setRxIntMsk(true);
|
|
167
|
+
|
|
168
|
+ }
|
|
169
|
+
|
|
170
|
+#if GCC_VERSION < 40302
|
|
171
|
+// Work-around for avr-gcc 4.3.0 OSX version bug
|
|
172
|
+// Restore the registers that the compiler misses
|
|
173
|
+ asm volatile(
|
|
174
|
+ "pop r27 \n\t"
|
|
175
|
+ "pop r26 \n\t"
|
|
176
|
+ "pop r23 \n\t"
|
|
177
|
+ "pop r22 \n\t"
|
|
178
|
+ "pop r21 \n\t"
|
|
179
|
+ "pop r20 \n\t"
|
|
180
|
+ "pop r19 \n\t"
|
|
181
|
+ "pop r18 \n\t"
|
|
182
|
+ ::);
|
|
183
|
+#endif
|
|
184
|
+}
|
|
185
|
+
|
|
186
|
+uint8_t SoftwareSerial8e1::rx_pin_read()
|
|
187
|
+{
|
|
188
|
+ return *_receivePortRegister & _receiveBitMask;
|
|
189
|
+}
|
|
190
|
+
|
|
191
|
+//
|
|
192
|
+// Interrupt handling
|
|
193
|
+//
|
|
194
|
+
|
|
195
|
+/* static */
|
|
196
|
+inline void SoftwareSerial8e1::handle_interrupt()
|
|
197
|
+{
|
|
198
|
+ if (active_object)
|
|
199
|
+ {
|
|
200
|
+ active_object->recv();
|
|
201
|
+ }
|
|
202
|
+}
|
|
203
|
+
|
|
204
|
+#if defined(PCINT0_vect)
|
|
205
|
+ISR(PCINT0_vect)
|
|
206
|
+{
|
|
207
|
+ SoftwareSerial8e1::handle_interrupt();
|
|
208
|
+}
|
|
209
|
+#endif
|
|
210
|
+
|
|
211
|
+#if defined(PCINT1_vect)
|
|
212
|
+ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
|
|
213
|
+#endif
|
|
214
|
+
|
|
215
|
+#if defined(PCINT2_vect)
|
|
216
|
+ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect));
|
|
217
|
+#endif
|
|
218
|
+
|
|
219
|
+#if defined(PCINT3_vect)
|
|
220
|
+ISR(PCINT3_vect, ISR_ALIASOF(PCINT0_vect));
|
|
221
|
+#endif
|
|
222
|
+
|
|
223
|
+//
|
|
224
|
+// Constructor
|
|
225
|
+//
|
|
226
|
+SoftwareSerial8e1::SoftwareSerial8e1(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) :
|
|
227
|
+ _rx_delay_centering(0),
|
|
228
|
+ _rx_delay_intrabit(0),
|
|
229
|
+ _rx_delay_stopbit(0),
|
|
230
|
+ _tx_delay(0),
|
|
231
|
+ _buffer_overflow(false),
|
|
232
|
+ _inverse_logic(inverse_logic)
|
|
233
|
+{
|
|
234
|
+ setTX(transmitPin);
|
|
235
|
+ setRX(receivePin);
|
|
236
|
+}
|
|
237
|
+
|
|
238
|
+//
|
|
239
|
+// Destructor
|
|
240
|
+//
|
|
241
|
+SoftwareSerial8e1::~SoftwareSerial8e1()
|
|
242
|
+{
|
|
243
|
+ end();
|
|
244
|
+}
|
|
245
|
+
|
|
246
|
+void SoftwareSerial8e1::setTX(uint8_t tx)
|
|
247
|
+{
|
|
248
|
+ // First write, then set output. If we do this the other way around,
|
|
249
|
+ // the pin would be output low for a short while before switching to
|
|
250
|
+ // output high. Now, it is input with pullup for a short while, which
|
|
251
|
+ // is fine. With inverse logic, either order is fine.
|
|
252
|
+ digitalWrite(tx, _inverse_logic ? LOW : HIGH);
|
|
253
|
+ pinMode(tx, OUTPUT);
|
|
254
|
+ _transmitBitMask = digitalPinToBitMask(tx);
|
|
255
|
+ uint8_t port = digitalPinToPort(tx);
|
|
256
|
+ _transmitPortRegister = portOutputRegister(port);
|
|
257
|
+}
|
|
258
|
+
|
|
259
|
+void SoftwareSerial8e1::setRX(uint8_t rx)
|
|
260
|
+{
|
|
261
|
+ pinMode(rx, INPUT);
|
|
262
|
+ if (!_inverse_logic)
|
|
263
|
+ digitalWrite(rx, HIGH); // pullup for normal logic!
|
|
264
|
+ _receivePin = rx;
|
|
265
|
+ _receiveBitMask = digitalPinToBitMask(rx);
|
|
266
|
+ uint8_t port = digitalPinToPort(rx);
|
|
267
|
+ _receivePortRegister = portInputRegister(port);
|
|
268
|
+}
|
|
269
|
+
|
|
270
|
+uint16_t SoftwareSerial8e1::subtract_cap(uint16_t num, uint16_t sub) {
|
|
271
|
+ if (num > sub)
|
|
272
|
+ return num - sub;
|
|
273
|
+ else
|
|
274
|
+ return 1;
|
|
275
|
+}
|
|
276
|
+
|
|
277
|
+//
|
|
278
|
+// Public methods
|
|
279
|
+//
|
|
280
|
+
|
|
281
|
+void SoftwareSerial8e1::begin(long speed)
|
|
282
|
+{
|
|
283
|
+ _rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0;
|
|
284
|
+
|
|
285
|
+ // Precalculate the various delays, in number of 4-cycle delays
|
|
286
|
+ uint16_t bit_delay = (F_CPU / speed) / 4;
|
|
287
|
+
|
|
288
|
+ // 12 (gcc 4.8.2) or 13 (gcc 4.3.2) cycles from start bit to first bit,
|
|
289
|
+ // 15 (gcc 4.8.2) or 16 (gcc 4.3.2) cycles between bits,
|
|
290
|
+ // 12 (gcc 4.8.2) or 14 (gcc 4.3.2) cycles from last bit to stop bit
|
|
291
|
+ // These are all close enough to just use 15 cycles, since the inter-bit
|
|
292
|
+ // timings are the most critical (deviations stack 8 times)
|
|
293
|
+ _tx_delay = subtract_cap(bit_delay, 15 / 4);
|
|
294
|
+
|
|
295
|
+ // Only setup rx when we have a valid PCINT for this pin
|
|
296
|
+ if (digitalPinToPCICR(_receivePin)) {
|
|
297
|
+#if GCC_VERSION > 40800
|
|
298
|
+ // Timings counted from gcc 4.8.2 output. This works up to 115200 on
|
|
299
|
+ // 16Mhz and 57600 on 8Mhz.
|
|
300
|
+ //
|
|
301
|
+ // When the start bit occurs, there are 3 or 4 cycles before the
|
|
302
|
+ // interrupt flag is set, 4 cycles before the PC is set to the right
|
|
303
|
+ // interrupt vector address and the old PC is pushed on the stack,
|
|
304
|
+ // and then 75 cycles of instructions (including the RJMP in the
|
|
305
|
+ // ISR vector table) until the first delay. After the delay, there
|
|
306
|
+ // are 17 more cycles until the pin value is read (excluding the
|
|
307
|
+ // delay in the loop).
|
|
308
|
+ // We want to have a total delay of 1.5 bit time. Inside the loop,
|
|
309
|
+ // we already wait for 1 bit time - 23 cycles, so here we wait for
|
|
310
|
+ // 0.5 bit time - (71 + 18 - 22) cycles.
|
|
311
|
+ _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 75 + 17 - 23) / 4);
|
|
312
|
+
|
|
313
|
+ // There are 23 cycles in each loop iteration (excluding the delay)
|
|
314
|
+ _rx_delay_intrabit = subtract_cap(bit_delay, 23 / 4);
|
|
315
|
+
|
|
316
|
+ // There are 37 cycles from the last bit read to the start of
|
|
317
|
+ // stopbit delay and 11 cycles from the delay until the interrupt
|
|
318
|
+ // mask is enabled again (which _must_ happen during the stopbit).
|
|
319
|
+ // This delay aims at 3/4 of a bit time, meaning the end of the
|
|
320
|
+ // delay will be at 1/4th of the stopbit. This allows some extra
|
|
321
|
+ // time for ISR cleanup, which makes 115200 baud at 16Mhz work more
|
|
322
|
+ // reliably
|
|
323
|
+ _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (37 + 11) / 4);
|
|
324
|
+#else // Timings counted from gcc 4.3.2 output
|
|
325
|
+ // Note that this code is a _lot_ slower, mostly due to bad register
|
|
326
|
+ // allocation choices of gcc. This works up to 57600 on 16Mhz and
|
|
327
|
+ // 38400 on 8Mhz.
|
|
328
|
+ _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 97 + 29 - 11) / 4);
|
|
329
|
+ _rx_delay_intrabit = subtract_cap(bit_delay, 11 / 4);
|
|
330
|
+ _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (44 + 17) / 4);
|
|
331
|
+#endif
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+ // Enable the PCINT for the entire port here, but never disable it
|
|
335
|
+ // (others might also need it, so we disable the interrupt by using
|
|
336
|
+ // the per-pin PCMSK register).
|
|
337
|
+ *digitalPinToPCICR(_receivePin) |= _BV(digitalPinToPCICRbit(_receivePin));
|
|
338
|
+ // Precalculate the pcint mask register and value, so setRxIntMask
|
|
339
|
+ // can be used inside the ISR without costing too much time.
|
|
340
|
+ _pcint_maskreg = digitalPinToPCMSK(_receivePin);
|
|
341
|
+ _pcint_maskvalue = _BV(digitalPinToPCMSKbit(_receivePin));
|
|
342
|
+
|
|
343
|
+ tunedDelay(_tx_delay); // if we were low this establishes the end
|
|
344
|
+ }
|
|
345
|
+
|
|
346
|
+#if _DEBUG
|
|
347
|
+ pinMode(_DEBUG_PIN1, OUTPUT);
|
|
348
|
+ pinMode(_DEBUG_PIN2, OUTPUT);
|
|
349
|
+#endif
|
|
350
|
+
|
|
351
|
+ listen();
|
|
352
|
+}
|
|
353
|
+
|
|
354
|
+void SoftwareSerial8e1::setRxIntMsk(bool enable)
|
|
355
|
+{
|
|
356
|
+ if (enable)
|
|
357
|
+ *_pcint_maskreg |= _pcint_maskvalue;
|
|
358
|
+ else
|
|
359
|
+ *_pcint_maskreg &= ~_pcint_maskvalue;
|
|
360
|
+}
|
|
361
|
+
|
|
362
|
+void SoftwareSerial8e1::end()
|
|
363
|
+{
|
|
364
|
+ stopListening();
|
|
365
|
+}
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+// Read data from buffer
|
|
369
|
+int SoftwareSerial8e1::read()
|
|
370
|
+{
|
|
371
|
+ if (!isListening())
|
|
372
|
+ return -1;
|
|
373
|
+
|
|
374
|
+ // Empty buffer?
|
|
375
|
+ if (_receive_buffer_head == _receive_buffer_tail)
|
|
376
|
+ return -1;
|
|
377
|
+
|
|
378
|
+ // Read from "head"
|
|
379
|
+ uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte
|
|
380
|
+ _receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF;
|
|
381
|
+ return d;
|
|
382
|
+}
|
|
383
|
+
|
|
384
|
+int SoftwareSerial8e1::available()
|
|
385
|
+{
|
|
386
|
+ if (!isListening())
|
|
387
|
+ return 0;
|
|
388
|
+
|
|
389
|
+ return (_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head) % _SS_MAX_RX_BUFF;
|
|
390
|
+}
|
|
391
|
+
|
|
392
|
+size_t SoftwareSerial8e1::write(uint8_t b)
|
|
393
|
+{
|
|
394
|
+ if (_tx_delay == 0) {
|
|
395
|
+ setWriteError();
|
|
396
|
+ return 0;
|
|
397
|
+ }
|
|
398
|
+// setTX(_receivePin);
|
|
399
|
+
|
|
400
|
+ // By declaring these as local variables, the compiler will put them
|
|
401
|
+ // in registers _before_ disabling interrupts and entering the
|
|
402
|
+ // critical timing sections below, which makes it a lot easier to
|
|
403
|
+ // verify the cycle timings
|
|
404
|
+ volatile uint8_t *reg = _transmitPortRegister;
|
|
405
|
+ uint8_t reg_mask = _transmitBitMask;
|
|
406
|
+ uint8_t inv_mask = ~_transmitBitMask;
|
|
407
|
+ uint8_t oldSREG = SREG;
|
|
408
|
+ bool inv = _inverse_logic;
|
|
409
|
+ uint16_t delay = _tx_delay;
|
|
410
|
+ uint8_t p = 0;
|
|
411
|
+ for (uint8_t t = 0x80; t; t >>= 1)
|
|
412
|
+ if (b & t) p++;
|
|
413
|
+
|
|
414
|
+ if (inv)
|
|
415
|
+ b = ~b;
|
|
416
|
+
|
|
417
|
+ cli(); // turn off interrupts for a clean txmit
|
|
418
|
+
|
|
419
|
+ // Write the start bit
|
|
420
|
+ if (inv)
|
|
421
|
+ *reg |= reg_mask;
|
|
422
|
+ else
|
|
423
|
+ *reg &= inv_mask;
|
|
424
|
+
|
|
425
|
+ tunedDelay(delay);
|
|
426
|
+
|
|
427
|
+ // Write each of the 8 bits
|
|
428
|
+ for (uint8_t i = 8; i > 0; --i)
|
|
429
|
+ {
|
|
430
|
+ if (b & 1) // choose bit
|
|
431
|
+ *reg |= reg_mask; // send 1
|
|
432
|
+ else
|
|
433
|
+ *reg &= inv_mask; // send 0
|
|
434
|
+
|
|
435
|
+ tunedDelay(delay);
|
|
436
|
+ b >>= 1;
|
|
437
|
+ }
|
|
438
|
+
|
|
439
|
+ if (p & 0x01)
|
|
440
|
+ *reg |= reg_mask; // send 1
|
|
441
|
+ else
|
|
442
|
+ *reg &= inv_mask; // send 0
|
|
443
|
+ tunedDelay(_tx_delay);
|
|
444
|
+
|
|
445
|
+ // restore pin to natural state
|
|
446
|
+ if (inv)
|
|
447
|
+ *reg &= inv_mask;
|
|
448
|
+ else
|
|
449
|
+ *reg |= reg_mask;
|
|
450
|
+
|
|
451
|
+ SREG = oldSREG; // turn interrupts back on
|
|
452
|
+ tunedDelay(_tx_delay);
|
|
453
|
+
|
|
454
|
+// setRX(_receivePin);
|
|
455
|
+// pinMode(_receivePin, INPUT);
|
|
456
|
+
|
|
457
|
+ return 1;
|
|
458
|
+}
|
|
459
|
+
|
|
460
|
+void SoftwareSerial8e1::flush()
|
|
461
|
+{
|
|
462
|
+ // There is no tx buffering, simply return
|
|
463
|
+}
|
|
464
|
+
|
|
465
|
+int SoftwareSerial8e1::peek()
|
|
466
|
+{
|
|
467
|
+ if (!isListening())
|
|
468
|
+ return -1;
|
|
469
|
+
|
|
470
|
+ // Empty buffer?
|
|
471
|
+ if (_receive_buffer_head == _receive_buffer_tail)
|
|
472
|
+ return -1;
|
|
473
|
+
|
|
474
|
+ // Read from "head"
|
|
475
|
+ return _receive_buffer[_receive_buffer_head];
|
|
476
|
+}
|