Skip to content

Commit 5c58ff3

Browse files
committed
Merge pull request #184 from m000z0rz/softwareserial
Porting SoftwareSerial library to Energia
2 parents d88085c + 1cecedd commit 5c58ff3

File tree

4 files changed

+567
-0
lines changed

4 files changed

+567
-0
lines changed
Lines changed: 381 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
/*
2+
(Ported from Arduino to Energia)
3+
4+
SoftwareSerial.cpp (formerly NewSoftSerial.cpp) -
5+
Multi-instance software serial library for Arduino/Wiring
6+
-- Interrupt-driven receive and other improvements by ladyada
7+
(http://ladyada.net)
8+
-- Tuning, circular buffer, derivation from class Print/Stream,
9+
multi-instance support, porting to 8MHz processors,
10+
various optimizations, PROGMEM delay tables, inverse logic and
11+
direct port writing by Mikal Hart (http://www.arduiniana.org)
12+
-- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com)
13+
-- 20MHz processor support by Garrett Mace (http://www.macetech.com)
14+
-- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/)
15+
16+
This library is free software; you can redistribute it and/or
17+
modify it under the terms of the GNU Lesser General Public
18+
License as published by the Free Software Foundation; either
19+
version 2.1 of the License, or (at your option) any later version.
20+
21+
This library is distributed in the hope that it will be useful,
22+
but WITHOUT ANY WARRANTY; without even the implied warranty of
23+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24+
Lesser General Public License for more details.
25+
26+
You should have received a copy of the GNU Lesser General Public
27+
License along with this library; if not, write to the Free Software
28+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29+
30+
The latest version of this library can always be found at
31+
http://arduiniana.org.
32+
*/
33+
34+
35+
//
36+
// Includes
37+
//
38+
//#include <avr/interrupt.h>
39+
//#include <avr/pgmspace.h>
40+
//#include "Arduino.h"
41+
#include "Energia.h"
42+
#include "SoftwareSerial.h"
43+
44+
//
45+
// Lookup table
46+
//
47+
typedef struct _DELAY_TABLE
48+
{
49+
long baud;
50+
unsigned short rx_delay_centering;
51+
unsigned short rx_delay_intrabit;
52+
unsigned short rx_delay_stopbit;
53+
unsigned short tx_delay;
54+
}
55+
DELAY_TABLE;
56+
57+
#if F_CPU == 16000000L
58+
59+
//formula is roughly rxintra=rxstop=tx = F_CPU / baud / 3 - 5/3
60+
// so, delay = 16000000 / 3 / baud - 5/3
61+
// and, rxcenter = 1/2 rxintra
62+
_DELAY_TABLE table[] =
63+
{
64+
// baud rxcenter rxintra rxstop tx
65+
{115200, 15, 32, 32, 32, },
66+
{57600, 38, 78, 78, 78, },
67+
{38400, 61, 123, 123, 123, },
68+
{31250, 72, 155, 155, 155, },
69+
{28800, 80, 170, 170, 170, },
70+
{19200, 126, 262, 262, 262, },
71+
{14400, 177, 353, 353, 353, },
72+
{9600, 265, 540, 540, 540, },
73+
{4800, 547, 1095, 1095, 1095, },
74+
{2400, 1103, 2190, 2190, 2190, },
75+
{1200, 2200, 4400, 4400, 4400, },
76+
{300, 8881, 17762, 17762, 17762, },
77+
};
78+
79+
const int XMIT_START_ADJUSTMENT = 5;
80+
#else
81+
#error This version of SoftwareSerial supports only 16MHz processors
82+
83+
#endif
84+
85+
86+
//
87+
// Statics
88+
//
89+
SoftwareSerial *SoftwareSerial::active_object = 0;
90+
char SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF];
91+
volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0;
92+
volatile uint8_t SoftwareSerial::_receive_buffer_head = 0;
93+
94+
95+
//
96+
// Private methods
97+
//
98+
99+
/* static */
100+
inline void SoftwareSerial::tunedDelay(uint16_t delay) {
101+
//msp430:
102+
// don't pass in delay of zero, it will not handle it well... could add constant cost of 2 by starting with a jz .+3
103+
// cycles: pre + mid + post ~= 5 + delay*3
104+
asm volatile(
105+
" sub.w #1, %[delay] \n" //sub (not immediate, but special constant) from register: 1 cycle
106+
" jnz .-2 \n" // 2 cycles regardless of branch
107+
: //no output
108+
: [delay] "r" (delay) // input
109+
: // no memory clobber
110+
);
111+
}
112+
113+
// This function sets the current object as the "listening"
114+
// one and returns true if it replaces another
115+
bool SoftwareSerial::listen() {
116+
if (active_object != this) {
117+
_buffer_overflow = false;
118+
uint8_t oldSR = __read_status_register();
119+
noInterrupts();
120+
_receive_buffer_head = _receive_buffer_tail = 0;
121+
active_object = this;
122+
__write_status_register(oldSR);
123+
return true;
124+
}
125+
126+
return false;
127+
}
128+
129+
//
130+
// The receive routine called by the interrupt handler
131+
//
132+
void SoftwareSerial::recv()
133+
{
134+
uint8_t d = 0;
135+
136+
// If RX line is high, then we don't see any start bit
137+
// so interrupt is probably not for us
138+
if (_inverse_logic ? rx_pin_read() : !rx_pin_read())
139+
{
140+
// Wait approximately 1/2 of a bit width to "center" the sample
141+
tunedDelay(_rx_delay_centering);
142+
143+
// Read each of the 8 bits
144+
for (uint8_t i=0x1; i; i <<= 1)
145+
{
146+
tunedDelay(_rx_delay_intrabit);
147+
uint8_t noti = ~i;
148+
if (rx_pin_read())
149+
d |= i;
150+
else // else clause added to ensure function timing is ~balanced
151+
d &= noti;
152+
}
153+
154+
// skip the stop bit
155+
tunedDelay(_rx_delay_stopbit);
156+
157+
if (_inverse_logic)
158+
d = ~d;
159+
160+
// if buffer full, set the overflow flag and return
161+
if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF != _receive_buffer_head)
162+
{
163+
// save new data in buffer: tail points to where byte goes
164+
_receive_buffer[_receive_buffer_tail] = d; // save new byte
165+
_receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF;
166+
}
167+
else
168+
{
169+
_buffer_overflow = true;
170+
}
171+
}
172+
}
173+
174+
void SoftwareSerial::tx_pin_write(uint8_t pin_state)
175+
{
176+
if (pin_state == LOW)
177+
*_transmitPortRegister &= ~_transmitBitMask;
178+
else
179+
*_transmitPortRegister |= _transmitBitMask;
180+
}
181+
182+
uint8_t SoftwareSerial::rx_pin_read()
183+
{
184+
return *_receivePortRegister & _receiveBitMask;
185+
}
186+
187+
//
188+
// Interrupt handling
189+
//
190+
191+
/* static */
192+
void SoftwareSerial::handle_interrupt()
193+
{
194+
if (active_object)
195+
{
196+
active_object->recv();
197+
}
198+
}
199+
200+
//
201+
// Constructor
202+
//
203+
SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) :
204+
_rx_delay_centering(0),
205+
_rx_delay_intrabit(0),
206+
_rx_delay_stopbit(0),
207+
_tx_delay(0),
208+
_buffer_overflow(false),
209+
_inverse_logic(inverse_logic)
210+
{
211+
setTX(transmitPin);
212+
setRX(receivePin);
213+
}
214+
215+
//
216+
// Destructor
217+
//
218+
SoftwareSerial::~SoftwareSerial()
219+
{
220+
end();
221+
}
222+
223+
void SoftwareSerial::setTX(uint8_t tx)
224+
{
225+
pinMode(tx, OUTPUT);
226+
digitalWrite(tx, HIGH);
227+
_transmitBitMask = digitalPinToBitMask(tx);
228+
uint8_t port = digitalPinToPort(tx);
229+
_transmitPortRegister = portOutputRegister(port);
230+
}
231+
232+
void SoftwareSerial::setRX(uint8_t rx)
233+
{
234+
pinMode(rx, INPUT);
235+
if (!_inverse_logic)
236+
digitalWrite(rx, HIGH); // pullup for normal logic!
237+
_receivePin = rx;
238+
_receiveBitMask = digitalPinToBitMask(rx);
239+
uint8_t port = digitalPinToPort(rx);
240+
_receivePortRegister = portInputRegister(port);
241+
}
242+
243+
//
244+
// Public methods
245+
//
246+
247+
void SoftwareSerial::begin(long speed)
248+
{
249+
_rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0;
250+
251+
for (unsigned i=0; i<sizeof(table)/sizeof(table[0]); ++i)
252+
{
253+
long baud = table[i].baud;
254+
if (baud == speed)
255+
{
256+
_rx_delay_centering = table[i].rx_delay_centering;
257+
_rx_delay_intrabit = table[i].rx_delay_intrabit;
258+
_rx_delay_stopbit = table[i].rx_delay_stopbit;
259+
_tx_delay = table[i].tx_delay;
260+
break;
261+
}
262+
}
263+
264+
// Set up RX interrupts, but only if we have a valid RX baud rate
265+
if (_rx_delay_stopbit)
266+
{
267+
attachInterrupt(_receivePin, SoftwareSerial::handle_interrupt, FALLING);
268+
269+
tunedDelay(_tx_delay); // if we were low this establishes the end
270+
}
271+
272+
273+
listen();
274+
}
275+
276+
void SoftwareSerial::end()
277+
{
278+
detachInterrupt(_receivePin);
279+
}
280+
281+
282+
// Read data from buffer
283+
int SoftwareSerial::read()
284+
{
285+
if (!isListening())
286+
return -1;
287+
288+
// Empty buffer?
289+
if (_receive_buffer_head == _receive_buffer_tail)
290+
return -1;
291+
292+
// Read from "head"
293+
uint8_t d = _receive_buffer[_receive_buffer_head]; // grab next byte
294+
_receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF;
295+
return d;
296+
}
297+
298+
int SoftwareSerial::available()
299+
{
300+
if (!isListening())
301+
return 0;
302+
303+
return (_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head) % _SS_MAX_RX_BUFF;
304+
}
305+
306+
size_t SoftwareSerial::write(uint8_t b)
307+
{
308+
if (_tx_delay == 0) {
309+
setWriteError();
310+
return 0;
311+
}
312+
313+
//interrupts off
314+
uint8_t oldSR = __read_status_register();
315+
noInterrupts();
316+
317+
// Write the start bit
318+
tx_pin_write(_inverse_logic ? HIGH : LOW);
319+
tunedDelay(_tx_delay + XMIT_START_ADJUSTMENT);
320+
321+
// Write each of the 8 bits
322+
if (_inverse_logic)
323+
{
324+
for (byte mask = 0x01; mask; mask <<= 1)
325+
{
326+
if (b & mask) // choose bit
327+
tx_pin_write(LOW); // send 1
328+
else
329+
tx_pin_write(HIGH); // send 0
330+
331+
tunedDelay(_tx_delay);
332+
}
333+
334+
tx_pin_write(LOW); // restore pin to natural state
335+
}
336+
else
337+
{
338+
for (byte mask = 0x01; mask; mask <<= 1)
339+
{
340+
if (b & mask) // choose bit
341+
tx_pin_write(HIGH); // send 1
342+
else
343+
tx_pin_write(LOW); // send 0
344+
345+
tunedDelay(_tx_delay);
346+
}
347+
348+
tx_pin_write(HIGH); // restore pin to natural state
349+
}
350+
351+
// turn interrupts back on if they were on before
352+
__write_status_register(oldSR);
353+
tunedDelay(_tx_delay);
354+
355+
return 1;
356+
}
357+
358+
void SoftwareSerial::flush()
359+
{
360+
if (!isListening())
361+
return;
362+
363+
uint8_t oldSR = __read_status_register();
364+
noInterrupts();
365+
_receive_buffer_head = _receive_buffer_tail = 0;
366+
__write_status_register(oldSR);
367+
}
368+
369+
int SoftwareSerial::peek()
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+
return _receive_buffer[_receive_buffer_head];
380+
}
381+

0 commit comments

Comments
 (0)