Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 72 additions & 58 deletions cores/arduino/zephyrSerial.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2022 Dhruva Gole
* Copyright (c) Arduino s.r.l. and/or its affiliated companies
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -9,6 +10,7 @@

#include <api/HardwareSerial.h>
#include <zephyrSerial.h>
#include <Arduino.h>

namespace {

Expand Down Expand Up @@ -58,10 +60,14 @@ void arduino::ZephyrSerial::begin(unsigned long baud, uint16_t conf) {
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE,
};

uart->ops.init(uart);
_reinit_if_needed();

uart_configure(uart, &config);
uart_irq_callback_user_data_set(uart, arduino::ZephyrSerial::IrqDispatch, this);
k_sem_take(&rx.sem, K_FOREVER);
ring_buf_reset(&rx.ringbuf);
k_sem_give(&rx.sem);

uart_irq_rx_enable(uart);
}

Expand All @@ -72,10 +78,6 @@ void arduino::ZephyrSerial::IrqHandler() {

(void)uart_irq_update(uart);

if (ring_buf_size_get(&tx.ringbuf) == 0) {
uart_irq_tx_disable(uart);
}

k_sem_take(&rx.sem, K_NO_WAIT);
while (uart_irq_rx_ready(uart) && ((length = uart_fifo_read(uart, buf, sizeof(buf))) > 0)) {
length = min(sizeof(buf), static_cast<size_t>(length));
Comment thread
soburi marked this conversation as resolved.
Expand All @@ -88,6 +90,11 @@ void arduino::ZephyrSerial::IrqHandler() {
k_sem_give(&rx.sem);

k_sem_take(&tx.sem, K_NO_WAIT);

if (ring_buf_size_get(&tx.ringbuf) == 0) {
uart_irq_tx_disable(uart);
}
Comment on lines 92 to +96

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering addressing this in arduino/ArduinoCore-zephyr.


while (uart_irq_tx_ready(uart) && ((length = ring_buf_size_get(&tx.ringbuf)) > 0)) {
length = min(sizeof(buf), static_cast<size_t>(length));
ring_buf_peek(&tx.ringbuf, &buf[0], length);
Expand All @@ -103,6 +110,7 @@ void arduino::ZephyrSerial::IrqHandler() {
}

void arduino::ZephyrSerial::IrqDispatch(const struct device *dev, void *data) {
(void)dev; // unused
reinterpret_cast<ZephyrSerial *>(data)->IrqHandler();
}

Expand All @@ -116,87 +124,93 @@ int arduino::ZephyrSerial::available() {
return ret;
}

int arduino::ZephyrSerial::availableForWrite() {
int ret;

k_sem_take(&tx.sem, K_FOREVER);
ret = ring_buf_space_get(&tx.ringbuf);
k_sem_give(&tx.sem);

return ret;
}

int arduino::ZephyrSerial::peek() {
uint8_t data;

k_sem_take(&rx.sem, K_FOREVER);
ring_buf_peek(&rx.ringbuf, &data, 1);
uint32_t cb_ret = ring_buf_peek(&rx.ringbuf, &data, 1);
k_sem_give(&rx.sem);

return data;
return cb_ret ? data : -1;
}

int arduino::ZephyrSerial::read() {
uint8_t data;

k_sem_take(&rx.sem, K_FOREVER);
ring_buf_get(&rx.ringbuf, &data, 1);
uint32_t cb_ret = ring_buf_get(&rx.ringbuf, &data, 1);
k_sem_give(&rx.sem);

return data;
return cb_ret ? data : -1;
}

size_t arduino::ZephyrSerial::write(const uint8_t *buffer, size_t size) {
int ret;
size_t idx = 0;

k_sem_take(&tx.sem, K_FOREVER);
ret = ring_buf_put(&tx.ringbuf, buffer, size);
k_sem_give(&tx.sem);

if (ret < 0) {
return 0;
while (1) {
Comment thread
soburi marked this conversation as resolved.
k_sem_take(&tx.sem, K_FOREVER);
auto ret = ring_buf_put(&tx.ringbuf, &buffer[idx], size - idx);
k_sem_give(&tx.sem);
if (ret < 0) {
return 0;
}
idx += ret;
if (ret == 0) {
uart_irq_tx_enable(uart);
yield();
}
if (idx == size) {
break;
}
}

uart_irq_tx_enable(uart);

return ret;
return size;
}

#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials)
arduino::ZephyrSerial Serial(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), serials, 0)));
#if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1)
#define ARDUINO_SERIAL_DEFINED_0 1

#define DECL_SERIAL_0(n, p, i)
#define DECL_SERIAL_N(n, p, i) \
arduino::ZephyrSerial Serial##i(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(n, p, i)));
#define DECLARE_SERIAL_N(n, p, i) \
COND_CODE_1(ARDUINO_SERIAL_DEFINED_##i, (DECL_SERIAL_0(n, p, i)), (DECL_SERIAL_N(n, p, i)))

#define CALL_EVENT_0(n, p, i)
#define CALL_EVENT_N(n, p, i) \
if (_CONCAT(Serial, i).available()) \
_CONCAT(_CONCAT(serial, i), Event)();
#define CALL_SERIALEVENT_N(n, p, i) \
COND_CODE_1(ARDUINO_SERIAL_DEFINED_##i, (CALL_EVENT_0(n, p, i)), (CALL_EVENT_N(n, p, i)));

#define DECL_EVENT_0(n, p, i)
#define DECL_EVENT_N(n, p, i) \
__attribute__((weak)) void serial##i##Event() { \
void arduino::ZephyrSerial::flush() {
while (ring_buf_size_get(&tx.ringbuf) > 0) {
k_yield();
}
#define DECLARE_SERIALEVENT_N(n, p, i) \
COND_CODE_1(ARDUINO_SERIAL_DEFINED_##i, (DECL_EVENT_0(n, p, i)), (DECL_EVENT_N(n, p, i)));

DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DECLARE_SERIAL_N)
#endif // PROP_LEN(serials) > 1
#elif DT_NODE_HAS_STATUS(DT_NODELABEL(arduino_serial), okay)
/* If serials node is not defined, tries to use arduino_serial */
arduino::ZephyrSerial Serial(DEVICE_DT_GET(DT_NODELABEL(arduino_serial)));
#else
arduino::ZephyrSerialStub Serial;
while (uart_irq_tx_complete(uart) == 0) {
k_yield();
}
}

#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials)
#define DEFINE_SERIAL_N(n, p, i) \
arduino::ZephyrSerial ZARD_SERIAL_NAME(i)(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(n, p, i)));
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DEFINE_SERIAL_N)

#elif DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(arduino_serial))
arduino::ZephyrSerial ZARD_SERIAL_NAME(0)(DEVICE_DT_GET(DT_NODELABEL(arduino_serial)));
#endif

__attribute__((weak)) void serialEvent() {
}
#if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1)
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DECLARE_SERIALEVENT_N)
#if ZARD_FIRST_SERIAL_IS_STUB
arduino::ZephyrSerialStub Serial;
#endif

void arduino::serialEventRun(void) {
if (Serial.available()) {
serialEvent();
#define DEFINE_WEAK_SERIALEVENT_N(n, s) \
__attribute__((weak)) void CONCAT(serial, ZARD_SERIAL_STEM(n), Event)() { \
}
#if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1)
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, CALL_SERIALEVENT_N)
#endif

#define CALL_SERIALEVENT_N(n, s) \
if (ZARD_SERIAL_NAME(n).available()) \
CONCAT(serial, ZARD_SERIAL_STEM(n), Event)();

LISTIFY(ZARD_GENERIC_SERIAL_COUNT, DEFINE_WEAK_SERIALEVENT_N, ())

void arduino::serialEventRun(void) {
LISTIFY(ZARD_GENERIC_SERIAL_COUNT, CALL_SERIALEVENT_N, ())
}
116 changes: 96 additions & 20 deletions cores/arduino/zephyrSerial.h
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
/*
* Copyright (c) 2022 Dhruva Gole
* Copyright (c) Arduino s.r.l. and/or its affiliated companies
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <zephyr/sys/ring_buffer.h>

#include <Arduino.h>
#include <zephyr/kernel.h>
#include <api/HardwareSerial.h>

namespace arduino {

class ZephyrSerialStub : public HardwareSerial {
public:
void begin(unsigned long baudRate) {
void begin(__attribute__((unused)) unsigned long baudRate) {
}

void begin(unsigned long baudrate, uint16_t config) {
void begin(__attribute__((unused)) unsigned long baudrate,
__attribute__((unused)) uint16_t config) {
}

void end() {
Expand Down Expand Up @@ -72,8 +73,7 @@ class ZephyrSerial : public HardwareSerial {
begin(baudrate, SERIAL_8N1);
}

void flush() {
}
void flush();

void end() {
#ifdef CONFIG_DEVICE_DEINIT_SUPPORT
Expand All @@ -89,41 +89,117 @@ class ZephyrSerial : public HardwareSerial {
return write(&data, 1);
}

using Print::write; // pull in write(str) and write(buf, size) from Print
int available();
int availableForWrite();
int peek();
int read();

operator bool() {
return true;
}

friend class SerialUSB_;

protected:
void IrqHandler();
static void IrqDispatch(const struct device *dev, void *data);

const struct device *uart;
ZephyrSerialBuffer<CONFIG_ARDUINO_API_SERIAL_BUFFER_SIZE> tx;
ZephyrSerialBuffer<CONFIG_ARDUINO_API_SERIAL_BUFFER_SIZE> rx;

virtual void _reinit_if_needed() {
uart->ops.init(uart);
}
};

} // namespace arduino

/* Return the index of it if matched, oterwise return an empty string. */
#define ZARD_SERIAL_MATCH(n, p, i, node) \
COND_CODE_1(DT_SAME_NODE(DT_PHANDLE_BY_IDX(n, p, i), node), (i), ())

/* Only matched device returns non-empty value, so the overall expansion is
* matched device's index.
*/
#define ZARD_SERIAL_INDEXOF(node) \
DT_FOREACH_PROP_ELEM_VARGS(DT_PATH(zephyr_user), serials, ZARD_SERIAL_MATCH, node)

/* Serial object associated with the Zephyr console. */
#define ARDUINO_CONSOLE_SERIAL ZARD_SERIAL_NAME(ZARD_SERIAL_INDEXOF(DT_CHOSEN(zephyr_console)))

/* Serial object associated with the first HW serial (usually on D0/D1). */
#define ARDUINO_HARDWARE_SERIAL ZARD_SERIAL_NAME(0)

#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm_serial)
/* Devicetree requires a SerialUSB object for 'Serial'. */
#define ZARD_SKIP_FIRST_SERIAL 1
#if (CONFIG_USB_CDC_ACM || CONFIG_USBD_CDC_ACM_CLASS)
/* SerialUSB can be compiled in the project. */
#define ZARD_FIRST_SERIAL_IS_SERIALUSB 1
#else
/* SerialUSB is required but no driver was enabled for the USB CDC ACM device.
* Define a stub Serial object to avoid build errors.
*/
#define ZARD_FIRST_SERIAL_IS_STUB 1
#endif
#endif

#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), arduino_router_serial)
/* If the board has an arduino,router-serial node, and the currently used
* library has the support, then the 'Serial' object is actually the same as
* the Monitor object in the Arduino_RouterBridge library.
*/
#define ZARD_SKIP_FIRST_SERIAL 1
#define ZARD_FIRST_SERIAL_IS_ARDUINO_ROUTER 1
#define ARDUINO_ROUTER_PHANDLE DT_PROP(DT_PATH(zephyr_user), arduino_router_serial)
#define ARDUINO_ROUTER_SERIAL ZARD_SERIAL_NAME(ZARD_SERIAL_INDEXOF(ARDUINO_ROUTER_PHANDLE))
#endif

/* Name of a Serial object for a given index. */
#define ZARD_SERIAL_NAME(n) CONCAT(Serial, ZARD_SERIAL_STEM(n))

/*
* Determine the number of generic ZephyrSerial objects to instantiate.
*/

#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials)
extern arduino::ZephyrSerial Serial;
#if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1)
#define SERIAL_DEFINED_0 1
#define EXTERN_SERIAL_N(i) extern arduino::ZephyrSerial Serial##i;
#define DECLARE_EXTERN_SERIAL_N(n, p, i) COND_CODE_1(SERIAL_DEFINED_##i, (), (EXTERN_SERIAL_N(i)))

/* Declare Serial1, Serial2, ... */
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DECLARE_EXTERN_SERIAL_N)

#undef DECLARE_EXTERN_SERIAL_N
#undef EXTERN_SERIAL_N
#undef SERIAL_DEFINED_0
/* The array property; the total number is the length of the array. */
#define ZARD_GENERIC_SERIAL_COUNT DT_PROP_LEN(DT_PATH(zephyr_user), serials)

#elif DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(arduino_serial))
/* A single node with compatible "arduino,serial" is present. */
#define ZARD_GENERIC_SERIAL_COUNT 1

#elif ZARD_SKIP_FIRST_SERIAL
/* Only a special Serial is present. */
#define ZARD_GENERIC_SERIAL_COUNT 0

#else
/* Neither 'serials' property, nor 'arduino,serial' node, nor a special Serial
* is present. Define a stub Serial object to avoid build errors.
*/
#define ZARD_GENERIC_SERIAL_COUNT 0
#define ZARD_SKIP_FIRST_SERIAL 1
#define ZARD_FIRST_SERIAL_IS_STUB 1
#endif
#elif DT_NODE_HAS_STATUS(DT_NODELABEL(arduino_serial), okay)
extern arduino::ZephyrSerial Serial;

#if ZARD_SKIP_FIRST_SERIAL
// Map index 0 to 'Serial1', index 1 to 'Serial2', etc.
#define ZARD_SERIAL_STEM(i) UTIL_INC(i)
#else
// Map index 0 to 'Serial', index 1 to 'Serial1', etc.
#define ZARD_SERIAL_STEM(i) COND_CODE_0(i, (), (i))
#endif

#if ZARD_FIRST_SERIAL_IS_STUB
extern arduino::ZephyrSerialStub Serial;
#endif

#if ZARD_GENERIC_SERIAL_COUNT > 0
/* Declare all generic ZephyrSerial objects */
#define DECLARE_SERIAL_N(n, s) extern arduino::ZephyrSerial ZARD_SERIAL_NAME(n);
LISTIFY(ZARD_GENERIC_SERIAL_COUNT, DECLARE_SERIAL_N, ())
#undef DECLARE_SERIAL_N
#endif
Loading