1
+ /*
2
+ SoftwareSerial.cpp - library for Arduino Primo
3
+ Copyright (c) 2016 Arduino. All rights reserved.
4
+
5
+ This library is free software; you can redistribute it and/or
6
+ modify it under the terms of the GNU Lesser General Public
7
+ License as published by the Free Software Foundation; either
8
+ version 2.1 of the License, or (at your option) any later version.
9
+
10
+ This library is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ Lesser General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Lesser General Public
16
+ License along with this library; if not, write to the Free Software
17
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
+
19
+ */
20
+
21
+ #include < Arduino.h>
22
+ #include < SoftwareSerial.h>
23
+ #include < variant.h>
24
+ #include < WInterrupts.h>
25
+
26
+ SoftwareSerial *SoftwareSerial::active_object = 0 ;
27
+ char SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF];
28
+ volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0 ;
29
+ volatile uint8_t SoftwareSerial::_receive_buffer_head = 0 ;
30
+
31
+
32
+ SoftwareSerial::SoftwareSerial (uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */ ) :
33
+ _rx_delay_centering(0 ),
34
+ _rx_delay_intrabit(0 ),
35
+ _rx_delay_stopbit(0 ),
36
+ _tx_delay(0 ),
37
+ _buffer_overflow(false ),
38
+ _inverse_logic(inverse_logic)
39
+ {
40
+ _receivePin = receivePin;
41
+ _transmitPin = transmitPin;
42
+ }
43
+
44
+
45
+ SoftwareSerial::~SoftwareSerial ()
46
+ {
47
+ end ();
48
+ }
49
+
50
+ void SoftwareSerial::begin (long speed)
51
+ {
52
+ setTX (_transmitPin);
53
+ setRX (_receivePin);
54
+ // Precalculate the various delays
55
+ // Calculate the distance between bit in micro seconds
56
+ uint32_t bit_delay = (float (1 )/speed)*1000000 ;
57
+
58
+ _tx_delay = bit_delay;
59
+
60
+ // Wait 1/2 bit - 2 micro seconds (time for interrupt to be served)
61
+ _rx_delay_centering = (bit_delay/2 ) - 2 ;
62
+ // Wait 1 bit - 2 micro seconds (time in each loop iteration)
63
+ _rx_delay_intrabit = bit_delay - 1 ;// 2
64
+ // Wait 1 bit (the stop one)
65
+ _rx_delay_stopbit = bit_delay;
66
+
67
+
68
+ delayMicroseconds (_tx_delay);
69
+
70
+ listen ();
71
+ }
72
+
73
+ bool SoftwareSerial::listen ()
74
+ {
75
+ if (!_rx_delay_stopbit)
76
+ return false ;
77
+
78
+ if (active_object != this )
79
+ {
80
+ if (active_object)
81
+ active_object->stopListening ();
82
+
83
+ _buffer_overflow = false ;
84
+ _receive_buffer_head = _receive_buffer_tail = 0 ;
85
+ active_object = this ;
86
+
87
+ if (_inverse_logic)
88
+ // Start bit high
89
+ _intMask = attachInterrupt (_receivePin, handle_interrupt, RISING);
90
+ else
91
+ // Start bit low
92
+ _intMask = attachInterrupt (_receivePin, handle_interrupt, FALLING);
93
+
94
+ return true ;
95
+ }
96
+ return false ;
97
+ }
98
+
99
+ bool SoftwareSerial::stopListening ()
100
+ {
101
+ if (active_object == this )
102
+ {
103
+ detachInterrupt (_receivePin);
104
+ active_object = NULL ;
105
+ return true ;
106
+ }
107
+ return false ;
108
+ }
109
+
110
+ void SoftwareSerial::end ()
111
+ {
112
+ stopListening ();
113
+ }
114
+
115
+ int SoftwareSerial::read ()
116
+ {
117
+ if (!isListening ()){
118
+ return -1 ;}
119
+
120
+
121
+ // Empty buffer?
122
+ if (_receive_buffer_head == _receive_buffer_tail){
123
+ return -1 ;}
124
+
125
+ // Read from "head"
126
+ uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte
127
+ _receive_buffer_head = (_receive_buffer_head + 1 ) % _SS_MAX_RX_BUFF;
128
+ return d;
129
+ }
130
+
131
+ int SoftwareSerial::available ()
132
+ {
133
+ if (!isListening ())
134
+ return 0 ;
135
+
136
+ return (_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head) % _SS_MAX_RX_BUFF;
137
+ }
138
+
139
+ size_t SoftwareSerial::write (uint8_t b)
140
+ {
141
+ if (_tx_delay == 0 ) {
142
+ setWriteError ();
143
+ return 0 ;
144
+ }
145
+
146
+ // By declaring these as local variables, the compiler will put them
147
+ // in registers _before_ disabling interrupts and entering the
148
+ // critical timing sections below, which makes it a lot easier to
149
+ // verify the cycle timings
150
+ volatile uint32_t * reg = _transmitPortRegister;
151
+ uint32_t reg_mask = _transmitBitMask;
152
+ uint32_t inv_mask = ~_transmitBitMask;
153
+ bool inv = _inverse_logic;
154
+ uint16_t delay = _tx_delay;
155
+
156
+ if (inv)
157
+ b = ~b;
158
+ // turn off interrupts for a clean txmit
159
+ NRF_GPIOTE->INTENCLR = _intMask;
160
+ // Write the start bit
161
+ if (inv)
162
+ *reg |= reg_mask;
163
+ else
164
+ *reg &= inv_mask;
165
+
166
+ delayMicroseconds (delay);
167
+
168
+
169
+ // Write each of the 8 bits
170
+ for (uint8_t i = 8 ; i > 0 ; --i)
171
+ {
172
+ if (b & 1 ) // choose bit
173
+ *reg |= reg_mask; // send 1
174
+ else
175
+ *reg &= inv_mask; // send 0
176
+
177
+ delayMicroseconds (delay);
178
+ b >>= 1 ;
179
+ }
180
+
181
+ // restore pin to natural state
182
+ if (inv)
183
+ *reg &= inv_mask;
184
+ else
185
+ *reg |= reg_mask;
186
+
187
+ NRF_GPIOTE->INTENSET = _intMask;
188
+
189
+ delayMicroseconds (delay);
190
+
191
+ return 1 ;
192
+ }
193
+
194
+ void SoftwareSerial::flush ()
195
+ {
196
+ if (!isListening ())
197
+ return ;
198
+
199
+ NRF_GPIOTE->INTENCLR = _intMask;
200
+
201
+ _receive_buffer_head = _receive_buffer_tail = 0 ;
202
+
203
+ NRF_GPIOTE->INTENSET = _intMask;
204
+ }
205
+
206
+ int SoftwareSerial::peek ()
207
+ {
208
+ if (!isListening ())
209
+ return -1 ;
210
+
211
+ // Empty buffer?
212
+ if (_receive_buffer_head == _receive_buffer_tail)
213
+ return -1 ;
214
+
215
+ // Read from "head"
216
+ return _receive_buffer[_receive_buffer_head];
217
+ }
218
+
219
+
220
+ // private methods
221
+
222
+ void SoftwareSerial::recv ()
223
+ {
224
+ uint8_t d = 0 ;
225
+
226
+ // If RX line is high, then we don't see any start bit
227
+ // so interrupt is probably not for us
228
+ if (_inverse_logic ? rx_pin_read () : !rx_pin_read ())
229
+ {
230
+
231
+ NRF_GPIOTE->INTENCLR = _intMask;
232
+
233
+ // Wait approximately 1/2 of a bit width to "center" the sample
234
+ delayMicroseconds (_rx_delay_centering);
235
+
236
+ // Read each of the 8 bits
237
+ for (uint8_t i=8 ; i > 0 ; --i)
238
+ {
239
+
240
+ delayMicroseconds (_rx_delay_intrabit);
241
+ // nRF52 needs another delay less than 1 uSec to be better synchronized
242
+ // with the highest baud rates
243
+ __ASM volatile (
244
+ " NOP\n\t "
245
+ " NOP\n "
246
+ " NOP\n "
247
+ " NOP\n "
248
+ " NOP\n "
249
+ " NOP\n "
250
+ " NOP\n "
251
+ " NOP\n "
252
+ " NOP\n "
253
+ " NOP\n "
254
+ " NOP\n "
255
+ " NOP\n "
256
+ " NOP\n "
257
+ " NOP\n "
258
+ " NOP\n "
259
+ " NOP\n "
260
+ );
261
+
262
+ d >>= 1 ;
263
+
264
+ if (rx_pin_read ()){
265
+ d |= 0x80 ;
266
+ }
267
+
268
+ }
269
+ if (_inverse_logic){
270
+ d = ~d;
271
+ }
272
+
273
+ // if buffer full, set the overflow flag and return
274
+ uint8_t next = (_receive_buffer_tail + 1 ) % _SS_MAX_RX_BUFF;
275
+ if (next != _receive_buffer_head)
276
+ {
277
+ // save new data in buffer: tail points to where byte goes
278
+ _receive_buffer[_receive_buffer_tail] = d; // save new byte
279
+ _receive_buffer_tail = next;
280
+ }
281
+ else
282
+ {
283
+ _buffer_overflow = true ;
284
+ }
285
+
286
+ // skip the stop bit
287
+ delayMicroseconds (_rx_delay_stopbit);
288
+
289
+ NRF_GPIOTE->INTENSET = _intMask;
290
+ }
291
+ }
292
+
293
+ uint32_t SoftwareSerial::rx_pin_read ()
294
+ {
295
+ return *_receivePortRegister & digitalPinToBitMask (_receivePin);
296
+ }
297
+
298
+ /* static */
299
+ inline void SoftwareSerial::handle_interrupt ()
300
+ {
301
+ if (active_object)
302
+ {
303
+ active_object->recv ();
304
+ }
305
+ }
306
+
307
+ void SoftwareSerial::setTX (uint8_t tx)
308
+ {
309
+ // First write, then set output. If we do this the other way around,
310
+ // the pin would be output low for a short while before switching to
311
+ // output hihg. Now, it is input with pullup for a short while, which
312
+ // is fine. With inverse logic, either order is fine.
313
+ digitalWrite (tx, _inverse_logic ? LOW : HIGH);
314
+ pinMode (tx, OUTPUT);
315
+ _transmitBitMask = digitalPinToBitMask (tx);
316
+ NRF_GPIO_Type * port = digitalPinToPort (tx);
317
+ _transmitPortRegister = portOutputRegister (port);
318
+ }
319
+
320
+ void SoftwareSerial::setRX (uint8_t rx)
321
+ {
322
+ pinMode (rx, INPUT);
323
+ if (!_inverse_logic)
324
+ digitalWrite (rx, HIGH); // pullup for normal logic!
325
+ _receivePin = rx;
326
+ _receiveBitMask = digitalPinToBitMask (rx);
327
+ NRF_GPIO_Type * port = digitalPinToPort (rx);
328
+ _receivePortRegister = portInputRegister (port);
329
+ }
0 commit comments