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