Skip to content

Commit 8c6b5fd

Browse files
soburiDhruvaG2000
authored andcommitted
zephyrSerial: Redesign to supporting hardware serial
Add support for the hardware serial device. The implementation using the UART interrupt, enabling CONFIG_UART_INTERRUPT_DRIVEN. Instantiate as 'Serial' with UART device defined in 'serials' array under zephyr,user node. If the 'serials' array is not defined, try to instantiate with a UART device labeled 'arduino_serial'. If even 'arduino_serial' does not define, it uses the stub implementation that redirects to printk().
1 parent 741090a commit 8c6b5fd

File tree

8 files changed

+270
-59
lines changed

8 files changed

+270
-59
lines changed

Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ config ARDUINO_API
1313
imply NEWLIB_LIBC_FLOAT_PRINTF
1414
imply CBPRINTF_FP_SUPPORT
1515
imply RING_BUFFER
16+
select UART_INTERRUPT_DRIVEN
1617
default n
1718

1819
if ARDUINO_API
@@ -22,4 +23,8 @@ config QEMU_ICOUNT
2223
default n
2324
depends on QEMU_TARGET
2425

26+
config ARDUINO_API_SERIAL_BUFFER_SIZE
27+
int "Buffer size for Arduino Serial API"
28+
default 64
29+
2530
endif

cores/arduino/main.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ int main(void) {
1111

1212
for (;;) {
1313
loop();
14+
if (arduino::serialEventRun) arduino::serialEventRun();
1415
}
1516

1617
return 0;
17-
}
18+
}

cores/arduino/zephyrSerial.cpp

Lines changed: 179 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,70 +4,205 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7+
#include <zephyr/devicetree.h>
8+
#include <zephyr/drivers/uart.h>
9+
10+
#include <api/HardwareSerial.h>
711
#include <zephyrSerial.h>
812

9-
size_t arduino::ZephyrSerial::begin(unsigned long int baudrate){
10-
return 0;
11-
}
13+
namespace
14+
{
1215

13-
size_t arduino::ZephyrSerial::print_char(char ch, bool lf){
14-
printk(lf ? "%c\n" : "%c", ch);
15-
return lf ? 2 : 1;
16+
enum uart_config_parity conf_parity(uint16_t conf)
17+
{
18+
switch (conf & SERIAL_PARITY_MASK) {
19+
case SERIAL_PARITY_EVEN:
20+
return UART_CFG_PARITY_EVEN;
21+
case SERIAL_PARITY_ODD:
22+
return UART_CFG_PARITY_ODD;
23+
default:
24+
return UART_CFG_PARITY_NONE;
25+
}
1626
}
1727

18-
size_t arduino::ZephyrSerial::print_str(const char* ptr, bool lf) {
19-
printk(lf ? "%s\n" : "%s", ptr);
20-
return lf ? strlen(ptr)+1 : strlen(ptr);
28+
enum uart_config_stop_bits conf_stop_bits(uint16_t conf)
29+
{
30+
switch (conf & SERIAL_STOP_BIT_MASK) {
31+
case SERIAL_STOP_BIT_1_5:
32+
return UART_CFG_STOP_BITS_1_5;
33+
case SERIAL_STOP_BIT_2:
34+
return UART_CFG_STOP_BITS_2;
35+
default:
36+
return UART_CFG_STOP_BITS_1;
37+
}
2138
}
2239

23-
size_t arduino::ZephyrSerial::print(char ch){
24-
return print_char(ch, false);
40+
enum uart_config_data_bits conf_data_bits(uint16_t conf)
41+
{
42+
switch (conf & SERIAL_DATA_MASK) {
43+
case SERIAL_DATA_5:
44+
return UART_CFG_DATA_BITS_5;
45+
case SERIAL_DATA_6:
46+
return UART_CFG_DATA_BITS_6;
47+
case SERIAL_DATA_7:
48+
return UART_CFG_DATA_BITS_7;
49+
default:
50+
return UART_CFG_DATA_BITS_8;
51+
}
2552
}
2653

27-
size_t arduino::ZephyrSerial::print(const int val) {
28-
printk("%d",val);
29-
return sizeof(int);
54+
} // anonymous namespace
55+
56+
void arduino::ZephyrSerial::begin(unsigned long baud, uint16_t conf)
57+
{
58+
struct uart_config config = {
59+
.baudrate = baud,
60+
.parity = conf_parity(conf),
61+
.stop_bits = conf_stop_bits(conf),
62+
.data_bits = conf_data_bits(conf),
63+
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE,
64+
};
65+
66+
uart_configure(uart, &config);
67+
uart_irq_callback_user_data_set(uart, arduino::ZephyrSerial::IrqDispatch, this);
68+
uart_irq_rx_enable(uart);
3069
}
3170

32-
size_t arduino::ZephyrSerial::print(double d) {
33-
printk("%.2f",d);
34-
return sizeof(double);
71+
void arduino::ZephyrSerial::IrqHandler()
72+
{
73+
uint8_t buf[8];
74+
int length;
75+
int ret = 0;
76+
77+
if (!uart_irq_update(uart)) {
78+
return;
79+
}
80+
81+
if (ring_buf_size_get(&tx.ringbuf) == 0) {
82+
uart_irq_tx_disable(uart);
83+
}
84+
85+
k_sem_take(&rx.sem, K_NO_WAIT);
86+
while (uart_irq_rx_ready(uart) && ((length = uart_fifo_read(uart, buf, sizeof(buf))) > 0)) {
87+
length = min(sizeof(buf), static_cast<size_t>(length));
88+
ret = ring_buf_put(&rx.ringbuf, &buf[0], length);
89+
90+
if (ret < 0) {
91+
break;
92+
}
93+
}
94+
k_sem_give(&rx.sem);
95+
96+
k_sem_take(&tx.sem, K_NO_WAIT);
97+
while (uart_irq_tx_ready(uart) && ((length = ring_buf_size_get(&tx.ringbuf)) > 0)) {
98+
length = min(sizeof(buf), static_cast<size_t>(length));
99+
ring_buf_peek(&tx.ringbuf, &buf[0], length);
100+
101+
ret = uart_fifo_fill(uart, &buf[0], length);
102+
if (ret < 0) {
103+
break;
104+
} else {
105+
ring_buf_get(&tx.ringbuf, &buf[0], ret);
106+
}
107+
}
108+
k_sem_give(&tx.sem);
35109
}
36110

37-
size_t arduino::ZephyrSerial::print(const int val, const int base) {
38-
if (base == 2) { /* Todo: print Binary */
39-
printk("%d", val);
40-
} else if (base == 16) { /* print Hex value */
41-
printk("%x",val);
42-
} else if (base == 8) { /* Todo: print octal value */
43-
printk("%d", val);
44-
} else if (base == 10) { /* print decimal value */
45-
printk("%d", val);
46-
} else {
47-
return EINVAL;
48-
}
49-
50-
return 1; /* temporarily return 1 byte, but change this to
51-
* return strlen(buffer);
52-
* when we implement octal and binary
53-
*/
111+
void arduino::ZephyrSerial::IrqDispatch(const struct device *dev, void *data)
112+
{
113+
reinterpret_cast<ZephyrSerial *>(data)->IrqHandler();
54114
}
55115

56-
size_t arduino::ZephyrSerial::println(char ch){
57-
return print_char(ch, true);
116+
int arduino::ZephyrSerial::available()
117+
{
118+
int ret;
119+
120+
k_sem_take(&rx.sem, K_FOREVER);
121+
ret = ring_buf_size_get(&rx.ringbuf);
122+
k_sem_give(&rx.sem);
123+
124+
return ret;
58125
}
59126

60-
size_t arduino::ZephyrSerial::print(const char* ptr) {
61-
return print_str(ptr, false);
127+
int arduino::ZephyrSerial::peek()
128+
{
129+
uint8_t data;
130+
131+
k_sem_take(&rx.sem, K_FOREVER);
132+
ring_buf_peek(&rx.ringbuf, &data, 1);
133+
k_sem_give(&rx.sem);
134+
135+
return data;
62136
}
63137

64-
size_t arduino::ZephyrSerial::println(const char* ptr){
65-
return print_str(ptr, true);
138+
int arduino::ZephyrSerial::read()
139+
{
140+
uint8_t data;
141+
142+
k_sem_take(&rx.sem, K_FOREVER);
143+
ring_buf_get(&rx.ringbuf, &data, 1);
144+
k_sem_give(&rx.sem);
145+
146+
return data;
66147
}
67148

68-
size_t arduino::ZephyrSerial::println(void){
69-
printk("\n");
70-
return 0;
149+
size_t arduino::ZephyrSerial::write(const uint8_t *buffer, size_t size)
150+
{
151+
int ret;
152+
153+
k_sem_take(&tx.sem, K_FOREVER);
154+
ret = ring_buf_put(&tx.ringbuf, buffer, size);
155+
k_sem_give(&tx.sem);
156+
157+
if (ret < 0) {
158+
return 0;
159+
}
160+
161+
uart_irq_tx_enable(uart);
162+
163+
return ret;
71164
}
72165

73-
arduino::ZephyrSerial Serial;
166+
#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials)
167+
arduino::ZephyrSerial Serial(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), serials, 0)));
168+
#if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1)
169+
#define ARDUINO_SERIAL_DEFINED_0 1
170+
171+
#define DECL_SERIAL_0(n, p, i)
172+
#define DECL_SERIAL_N(n, p, i) \
173+
arduino::ZephyrSerial Serial##i(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(n, p, i)));
174+
#define DECLARE_SERIAL_N(n, p, i) \
175+
COND_CODE_1(ARDUINO_SERIAL_DEFINED_##i, (DECL_SERIAL_0(n, p, i)), (DECL_SERIAL_N(n, p, i)))
176+
177+
#define CALL_EVENT_0(n, p, i)
178+
#define CALL_EVENT_N(n, p, i) if (_CONCAT(Serial, i).available()) _CONCAT(_CONCAT(serial, i), Event)();
179+
#define CALL_SERIALEVENT_N(n, p, i) \
180+
COND_CODE_1(ARDUINO_SERIAL_DEFINED_##i, (CALL_EVENT_0(n, p, i)), (CALL_EVENT_N(n, p, i)));
181+
182+
#define DECL_EVENT_0(n, p, i)
183+
#define DECL_EVENT_N(n, p, i) __attribute__((weak)) void serial##i##Event() { }
184+
#define DECLARE_SERIALEVENT_N(n, p, i) \
185+
COND_CODE_1(ARDUINO_SERIAL_DEFINED_##i, (DECL_EVENT_0(n, p, i)), (DECL_EVENT_N(n, p, i)));
186+
187+
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DECLARE_SERIAL_N)
188+
#endif // PROP_LEN(serials) > 1
189+
#elif DT_NODE_EXISTS(DT_NODELABEL(arduino_serial))
190+
/* If serials node is not defined, tries to use arduino_serial */
191+
arduino::ZephyrSerial Serial(DEVICE_DT_GET(DT_NODELABEL(arduino_serial)));
192+
#else
193+
arduino::ZephyrSerialStub Serial;
194+
#endif
195+
196+
197+
__attribute__((weak)) void serialEvent() { }
198+
#if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1)
199+
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DECLARE_SERIALEVENT_N)
200+
#endif
201+
202+
void arduino::serialEventRun(void)
203+
{
204+
if (Serial.available()) serialEvent();
205+
#if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1)
206+
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, CALL_SERIALEVENT_N)
207+
#endif
208+
}

cores/arduino/zephyrSerial.h

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,94 @@
66

77
#pragma once
88

9+
#include <zephyr/sys/ring_buffer.h>
10+
911
#include <Arduino.h>
12+
#include <api/HardwareSerial.h>
1013

1114
namespace arduino {
1215

13-
class ZephyrSerial {
14-
char pvt_c;
16+
class ZephyrSerialStub : public HardwareSerial
17+
{
18+
public:
19+
void begin(unsigned long baudRate) { }
20+
void begin(unsigned long baudrate, uint16_t config) { }
21+
void end() { }
22+
int available() { return 0; }
23+
int peek() { return 0; }
24+
int read() { return 0; }
25+
void flush() { }
26+
size_t write(const uint8_t data)
27+
{
28+
printk("%c", static_cast<char>(data));
29+
return 1;
30+
}
1531

16-
private:
17-
size_t print_char(const char c, bool lf);
18-
size_t print_str(const char * ptr, bool lf);
32+
operator bool() { return true; }
33+
};
1934

35+
class ZephyrSerial : public HardwareSerial
36+
{
2037
public:
21-
size_t begin(unsigned long int baudrate); //TODO
38+
template <int SZ>
39+
class ZephyrSerialBuffer
40+
{
41+
friend arduino::ZephyrSerial;
42+
struct ring_buf ringbuf;
43+
uint8_t buffer[SZ];
44+
struct k_sem sem;
45+
46+
ZephyrSerialBuffer()
47+
{
48+
k_sem_init(&sem, 1, 1);
49+
ring_buf_init(&ringbuf, sizeof(buffer), buffer);
50+
}
51+
};
52+
53+
ZephyrSerial(const struct device *dev) : uart(dev) { }
54+
void begin(unsigned long baudrate, uint16_t config);
55+
void begin(unsigned long baudrate) { begin(baudrate, SERIAL_8N1); }
56+
void flush() { }
57+
void end() { }
58+
size_t write(const uint8_t *buffer, size_t size);
59+
size_t write(const uint8_t data) { return write(&data, 1); }
60+
int available();
61+
int peek();
62+
int read();
2263

23-
size_t print(const char c);
24-
size_t print(const int val);
25-
size_t print(double d);
26-
size_t print(const char * ptr);
27-
size_t print(const int val, const int base);
64+
operator bool()
65+
{
66+
return true;
67+
}
2868

29-
size_t println(const char c);
30-
size_t println(const char* ptr);
31-
size_t println(void);
69+
protected:
70+
void IrqHandler();
71+
static void IrqDispatch(const struct device *dev, void *data);
3272

73+
const struct device *uart;
74+
ZephyrSerialBuffer<CONFIG_ARDUINO_API_SERIAL_BUFFER_SIZE> tx;
75+
ZephyrSerialBuffer<CONFIG_ARDUINO_API_SERIAL_BUFFER_SIZE> rx;
3376
};
3477

78+
3579
} // namespace arduino
3680

81+
#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials)
82+
extern arduino::ZephyrSerial Serial;
83+
#if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1)
84+
#define SERIAL_DEFINED_0 1
85+
#define EXTERN_SERIAL_N(i) extern arduino::ZephyrSerial Serial##i;
86+
#define DECLARE_EXTERN_SERIAL_N(n, p, i) COND_CODE_1(SERIAL_DEFINED_##i, (), (EXTERN_SERIAL_N(i)))
87+
88+
/* Declare Serial1, Serial2, ... */
89+
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DECLARE_EXTERN_SERIAL_N)
90+
91+
#undef DECLARE_EXTERN_SERIAL_N
92+
#undef EXTERN_SERIAL_N
93+
#undef SERIAL_DEFINED_0
94+
#endif
95+
#elif DT_NODE_EXISTS(DT_NODELABEL(arduino_serial))
3796
extern arduino::ZephyrSerial Serial;
97+
#else
98+
extern arduino::ZephyrSerialStub Serial;
99+
#endif

variants/arduino_mkrzero/arduino_mkrzero.overlay

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
<&adc 6>,
4444
<&adc 7>;
4545
io-channel-pins = <15 16 17 18 19 20 21>;
46+
47+
serials = <&sercom5>;
4648
};
4749
};
4850

0 commit comments

Comments
 (0)