Skip to content

Commit 2790d44

Browse files
author
Hasnain Virk
committed
Introducing a BufferedSerial
BufferedSerial is a FileHandle and using SerialBase. It keeps the SerialBase private however lets the user extend FileHandle by keeping it public. It is using CircularBuffer class for having circular buffers. There are some minor amendments in CircularBuffer too. Adding an entry for tx/rx buffer sizes in platform/mbed_lib.json. Default size is 256 bytes. For RTOS read(), write() calls yield for other threads to carry on with their stuff. For non-RTOS blovking read or write would mean a loop where 100 percent resources are consumed by this loop. Need to get a better implementation in. Currently no mechanism to wake the mcu up after WFE.
1 parent 09ae609 commit 2790d44

File tree

4 files changed

+315
-2
lines changed

4 files changed

+315
-2
lines changed

platform/BufferedSerial.cpp

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2006-2017 ARM Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#if DEVICE_SERIAL
18+
19+
#include <errno.h>
20+
#include "platform/BufferedSerial.h"
21+
#include "platform/mbed_poll.h"
22+
#include "platform/mbed_wait_api.h"
23+
24+
namespace mbed {
25+
26+
BufferedSerial::BufferedSerial(PinName tx, PinName rx, int baud) :
27+
SerialBase(tx, rx, baud),
28+
_blocking(true),
29+
_tx_irq_enabled(false)
30+
{
31+
/* Attatch IRQ routines to the serial device. */
32+
SerialBase::attach(callback(this, &BufferedSerial::rx_irq), RxIrq);
33+
}
34+
35+
int BufferedSerial::close()
36+
{
37+
/* Does not let us pass a file descriptor. So how to close ?
38+
* Also, does it make sense to close a device type file descriptor*/
39+
return 0;
40+
}
41+
42+
int BufferedSerial::isatty()
43+
{
44+
return 1;
45+
46+
}
47+
48+
off_t BufferedSerial::seek(off_t offset, int whence)
49+
{
50+
/*XXX lseek can be done theoratically, but is it sane to mark positions on a dynamically growing/shrinking
51+
* buffer system (from an interrupt context) */
52+
return -ESPIPE;
53+
}
54+
55+
int BufferedSerial::sync()
56+
{
57+
lock();
58+
59+
while (!_txbuf.empty()) {
60+
unlock();
61+
// Doing better than wait would require TxIRQ to also do wake() when becoming empty. Worth it?
62+
wait_ms(1);
63+
lock();
64+
}
65+
66+
unlock();
67+
68+
return 0;
69+
}
70+
71+
ssize_t BufferedSerial::write(const void* buffer, size_t length)
72+
{
73+
size_t data_written = 0;
74+
const char *buf_ptr = static_cast<const char *>(buffer);
75+
76+
lock();
77+
78+
while (_txbuf.full()) {
79+
if (!_blocking) {
80+
unlock();
81+
return -EAGAIN;
82+
}
83+
unlock();
84+
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
85+
lock();
86+
}
87+
88+
while (data_written < length && !_txbuf.full()) {
89+
_txbuf.push(*buf_ptr++);
90+
data_written++;
91+
}
92+
93+
core_util_critical_section_enter();
94+
if (!_tx_irq_enabled) {
95+
BufferedSerial::tx_irq(); // only write to hardware in one place
96+
if (!_txbuf.empty()) {
97+
SerialBase::attach(callback(this, &BufferedSerial::tx_irq), TxIrq);
98+
_tx_irq_enabled = true;
99+
}
100+
}
101+
core_util_critical_section_exit();
102+
103+
unlock();
104+
105+
return data_written;
106+
}
107+
108+
ssize_t BufferedSerial::read(void* buffer, size_t length)
109+
{
110+
size_t data_read = 0;
111+
112+
char *ptr = static_cast<char *>(buffer);
113+
114+
lock();
115+
116+
while (_rxbuf.empty()) {
117+
if (!_blocking) {
118+
unlock();
119+
return -EAGAIN;
120+
}
121+
unlock();
122+
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
123+
lock();
124+
}
125+
126+
while (data_read < length && !_rxbuf.empty()) {
127+
_rxbuf.pop(*ptr++);
128+
data_read++;
129+
}
130+
131+
unlock();
132+
133+
return data_read;
134+
}
135+
136+
short BufferedSerial::poll(short events) const {
137+
138+
short revents = 0;
139+
/* Check the Circular Buffer if space available for writing out */
140+
if (!_txbuf.full()) {
141+
revents |= POLLOUT;
142+
}
143+
144+
if (!_rxbuf.empty()) {
145+
revents |= POLLIN;
146+
}
147+
148+
/*TODO Handle other event types */
149+
150+
return revents;
151+
}
152+
153+
void BufferedSerial::lock(void)
154+
{
155+
_mutex.lock();
156+
}
157+
158+
void BufferedSerial::unlock(void)
159+
{
160+
_mutex.unlock();
161+
}
162+
163+
void BufferedSerial::rx_irq(void)
164+
{
165+
bool was_empty = _rxbuf.empty();
166+
167+
/* Fill in the receive buffer if the peripheral is readable
168+
* and receive buffer is not full. */
169+
while (SerialBase::readable()) {
170+
char data = SerialBase::_base_getc();
171+
if (!_rxbuf.full()) {
172+
_rxbuf.push(data);
173+
} else {
174+
/* Drop - can we report in some way? */
175+
}
176+
}
177+
178+
/* Report the File handler that data is ready to be read from the buffer. */
179+
if (was_empty && !_rxbuf.empty()) {
180+
_poll_change(this);
181+
}
182+
}
183+
184+
// Also called from write to start transfer
185+
void BufferedSerial::tx_irq(void)
186+
{
187+
bool was_full = _txbuf.full();
188+
189+
/* Write to the peripheral if there is something to write
190+
* and if the peripheral is available to write. */
191+
while (!_txbuf.empty() && SerialBase::writeable()) {
192+
char data;
193+
_txbuf.pop(data);
194+
SerialBase::_base_putc(data);
195+
}
196+
197+
if (_tx_irq_enabled && _txbuf.empty()) {
198+
SerialBase::attach(NULL, TxIrq);
199+
_tx_irq_enabled = false;
200+
}
201+
202+
/* Report the File handler that data can be written to peripheral. */
203+
if (was_full && !_txbuf.full()) {
204+
_poll_change(this);
205+
}
206+
}
207+
208+
209+
} //namespace mbed
210+
211+
#endif //DEVICE_SERIAL

platform/BufferedSerial.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2006-2017 ARM Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef MBED_BUFFEREDSERIAL_H
18+
#define MBED_BUFFEREDSERIAL_H
19+
20+
#include "platform/platform.h"
21+
22+
#if DEVICE_SERIAL
23+
24+
#include "FileHandle.h"
25+
#include "SerialBase.h"
26+
#include "PlatformMutex.h"
27+
#include "serial_api.h"
28+
#include "CircularBuffer.h"
29+
30+
namespace mbed {
31+
class BufferedSerial : private SerialBase, public FileHandle {
32+
public:
33+
/** Create a BufferedSerial port, connected to the specified transmit and receive pins, with a particular baud rate.
34+
* @param tx Transmit pin
35+
* @param rx Receive pin
36+
* @param baud The baud rate of the serial port (optional, defaults to MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE)
37+
*/
38+
BufferedSerial(PinName tx, PinName rx, int baud = MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE);
39+
40+
/** Equivalent to POSIX poll(). Derived from FileHandle.
41+
* Provides a mechanism to multiplex input/output over a set of file handles.
42+
*/
43+
virtual short poll(short events) const;
44+
45+
virtual ssize_t write(const void* buffer, size_t length);
46+
47+
virtual ssize_t read(void* buffer, size_t length);
48+
49+
/** Acquire mutex */
50+
virtual void lock(void);
51+
52+
/** Release mutex */
53+
virtual void unlock(void);
54+
55+
virtual int close();
56+
57+
virtual int isatty();
58+
59+
virtual off_t seek(off_t offset, int whence);
60+
61+
virtual int sync();
62+
63+
virtual int set_blocking(bool blocking) { _blocking = blocking; return 0; }
64+
65+
private:
66+
67+
/** Software serial buffers
68+
* By default buffer size is 256 for TX and 256 for RX. Configurable through mbed_app.json
69+
*/
70+
CircularBuffer<char, MBED_CONF_PLATFORM_BUFFERED_SERIAL_RXBUF_SIZE> _rxbuf;
71+
CircularBuffer<char, MBED_CONF_PLATFORM_BUFFERED_SERIAL_TXBUF_SIZE> _txbuf;
72+
73+
PlatformMutex _mutex;
74+
75+
bool _blocking;
76+
bool _tx_irq_enabled;
77+
78+
79+
80+
/** ISRs for serial
81+
* Routines to handle interrupts on serial pins.
82+
* Copies data into Circular Buffer.
83+
* Reports the state change to File handle.
84+
*/
85+
void tx_irq(void);
86+
void rx_irq(void);
87+
88+
89+
};
90+
} //namespace mbed
91+
92+
93+
#endif //DEVICE_SERIAL
94+
#endif /* MBED_BUFFEREDSERIAL_H */

platform/CircularBuffer.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class CircularBuffer {
7676
*
7777
* @return True if the buffer is empty, false if not
7878
*/
79-
bool empty() {
79+
bool empty() const {
8080
core_util_critical_section_enter();
8181
bool is_empty = (_head == _tail) && !_full;
8282
core_util_critical_section_exit();
@@ -87,7 +87,7 @@ class CircularBuffer {
8787
*
8888
* @return True if the buffer is full, false if not
8989
*/
90-
bool full() {
90+
bool full() const {
9191
core_util_critical_section_enter();
9292
bool full = _full;
9393
core_util_critical_section_exit();

platform/mbed_lib.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@
1919
"default-serial-baud-rate": {
2020
"help": "Default baud rate for a Serial or RawSerial instance (if not specified in the constructor)",
2121
"value": 9600
22+
},
23+
"buffered-serial-txbuf-size": {
24+
"help": "Default TX buffer size for a BufferedSerial instance (unit Bytes))",
25+
"value": 256
26+
},
27+
"buffered-serial-rxbuf-size": {
28+
"help": "Default RX buffer size for a BufferedSerial instance (unit Bytes))",
29+
"value": 256
2230
}
2331
},
2432
"target_overrides": {

0 commit comments

Comments
 (0)