source: rtos_arduino/trunk/arduino_lib/hardware/arduino/samd/libraries/SoftwareSerial/SoftwareSerial.cpp@ 175

Last change on this file since 175 was 175, checked in by ertl-honda, 8 years ago

ライブラリを Arduino IDE 1.7.9 にupdate

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