hw-uart: add UART peripheral gems for embedded platforms

Add three new gems for UART serial communication:
- hw-uart: common Ruby API, C bindings, ring buffer, and HAL header
- hw-esp32-uart: ESP32 HAL using ESP-IDF UART driver with FreeRTOS
  RX task
- hw-rp2040-uart: RP2040 HAL using Pico SDK with IRQ-driven RX

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-03-27 11:14:12 +09:00
parent bdc3a65601
commit 2b5a389de3
10 changed files with 903 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
MRuby::Gem::Specification.new('hw-esp32-uart') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'UART HAL for ESP32'
spec.add_dependency 'hw-uart'
end
+152
View File
@@ -0,0 +1,152 @@
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "driver/uart.h"
#include <mruby/uart.h>
#define RX_TASK_BUF_SIZE 128
#define QUEUE_LENGTH 20
#define TASK_STACK_SIZE 4096
#define TASK_PRIORITY 12
typedef struct {
int unit;
QueueHandle_t queue;
mrb_uart_ringbuf *rxbuf;
} uart_ctx;
static uart_ctx ctx[UART_NUM_MAX];
static void
rx_task(void *arg)
{
uart_ctx *c = (uart_ctx*)arg;
uart_event_t event;
uint8_t buf[RX_TASK_BUF_SIZE];
for (;;) {
if (xQueueReceive(c->queue, &event, portMAX_DELAY)) {
if (event.type == UART_DATA) {
size_t n = event.size > RX_TASK_BUF_SIZE ? RX_TASK_BUF_SIZE : event.size;
uart_read_bytes(c->unit, buf, n, portMAX_DELAY);
for (size_t i = 0; i < n; i++) {
mrb_uart_ringbuf_push(c->rxbuf, buf[i]);
}
}
}
}
}
int
mrb_uart_unit_name_to_num(const char *name)
{
if (strcmp(name, "ESP32_UART0") == 0) return UART_NUM_0;
if (strcmp(name, "ESP32_UART1") == 0) return UART_NUM_1;
#ifdef UART_NUM_2
if (strcmp(name, "ESP32_UART2") == 0) return UART_NUM_2;
#endif
return MRB_UART_ERROR_UNIT;
}
mrb_uart_status
mrb_uart_init(int unit, uint32_t tx_pin, uint32_t rx_pin,
mrb_uart_ringbuf *rxbuf)
{
if (unit < 0 || unit >= UART_NUM_MAX) return MRB_UART_ERROR_UNIT;
uart_config_t cfg = {
.baud_rate = 9600,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
int bufsize = (rxbuf->mask + 1);
uart_driver_install(unit, bufsize, 0, QUEUE_LENGTH, &ctx[unit].queue, 0);
uart_param_config(unit, &cfg);
uart_set_pin(unit, tx_pin, rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
ctx[unit].unit = unit;
ctx[unit].rxbuf = rxbuf;
char name[32];
snprintf(name, sizeof(name), "uart_rx_%d", unit);
xTaskCreate(rx_task, name, TASK_STACK_SIZE, &ctx[unit], TASK_PRIORITY, NULL);
return MRB_UART_OK;
}
uint32_t
mrb_uart_set_baudrate(int unit, uint32_t baudrate)
{
uart_set_baudrate(unit, baudrate);
return baudrate;
}
void
mrb_uart_set_format(int unit, uint32_t data_bits, uint32_t stop_bits,
uint8_t parity)
{
static const uart_word_length_t wl[] = {
UART_DATA_5_BITS, UART_DATA_6_BITS, UART_DATA_7_BITS, UART_DATA_8_BITS
};
static const uart_stop_bits_t sb[] = {
UART_STOP_BITS_1, UART_STOP_BITS_2
};
static const uart_parity_t pr[] = {
UART_PARITY_DISABLE, UART_PARITY_EVEN, UART_PARITY_ODD
};
if (data_bits >= 5 && data_bits <= 8)
uart_set_word_length(unit, wl[data_bits - 5]);
if (stop_bits >= 1 && stop_bits <= 2)
uart_set_stop_bits(unit, sb[stop_bits - 1]);
if (parity <= 2)
uart_set_parity(unit, pr[parity]);
}
void
mrb_uart_set_flow_control(int unit, bool cts, bool rts)
{
uart_hw_flowcontrol_t mode = UART_HW_FLOWCTRL_DISABLE;
if (cts && rts) mode = UART_HW_FLOWCTRL_CTS_RTS;
else if (cts) mode = UART_HW_FLOWCTRL_CTS;
else if (rts) mode = UART_HW_FLOWCTRL_RTS;
uart_set_hw_flow_ctrl(unit, mode, 122);
}
void
mrb_uart_write(int unit, const uint8_t *src, size_t len)
{
uart_write_bytes(unit, (const char*)src, len);
}
void
mrb_uart_flush(int unit)
{
uart_wait_tx_done(unit, 100);
}
void
mrb_uart_send_break(int unit, uint32_t duration_ms)
{
uart_write_bytes_with_break(unit, NULL, 0, duration_ms);
}
void
mrb_uart_clear_rx(int unit)
{
uart_flush_input(unit);
}
void
mrb_uart_clear_tx(int unit)
{
/* not supported on ESP-IDF */
}
#include <mruby.h>
void mrb_hw_esp32_uart_gem_init(mrb_state *mrb) {}
void mrb_hw_esp32_uart_gem_final(mrb_state *mrb) {}
+7
View File
@@ -0,0 +1,7 @@
MRuby::Gem::Specification.new('hw-rp2040-uart') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'UART HAL for RP2040'
spec.add_dependency 'hw-uart'
end
+141
View File
@@ -0,0 +1,141 @@
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/uart.h"
#include "hardware/irq.h"
#include <mruby/uart.h>
#define UNIT_SELECT(u) \
uart_inst_t *inst; \
switch (u) { \
case 0: inst = uart0; break; \
case 1: inst = uart1; break; \
default: return MRB_UART_ERROR_UNIT; \
}
/* void-returning variant for functions that can't return error */
#define UNIT_SELECT_V(u) \
uart_inst_t *inst; \
switch (u) { \
case 0: inst = uart0; break; \
case 1: inst = uart1; break; \
default: return; \
}
static mrb_uart_ringbuf *rx_bufs[2];
static void
on_uart0_rx(void)
{
while (uart_is_readable(uart0)) {
mrb_uart_ringbuf_push(rx_bufs[0], uart_getc(uart0));
}
}
static void
on_uart1_rx(void)
{
while (uart_is_readable(uart1)) {
mrb_uart_ringbuf_push(rx_bufs[1], uart_getc(uart1));
}
}
int
mrb_uart_unit_name_to_num(const char *name)
{
if (strcmp(name, "RP2040_UART0") == 0) return 0;
if (strcmp(name, "RP2040_UART1") == 0) return 1;
return MRB_UART_ERROR_UNIT;
}
mrb_uart_status
mrb_uart_init(int unit, uint32_t tx_pin, uint32_t rx_pin,
mrb_uart_ringbuf *rxbuf)
{
UNIT_SELECT(unit);
uart_init(inst, 9600);
gpio_set_function(tx_pin, GPIO_FUNC_UART);
gpio_set_function(rx_pin, GPIO_FUNC_UART);
rx_bufs[unit] = rxbuf;
uint irq;
if (unit == 0) {
irq = UART0_IRQ;
irq_set_exclusive_handler(irq, on_uart0_rx);
}
else {
irq = UART1_IRQ;
irq_set_exclusive_handler(irq, on_uart1_rx);
}
irq_set_enabled(irq, true);
uart_set_irq_enables(inst, true, false);
return MRB_UART_OK;
}
uint32_t
mrb_uart_set_baudrate(int unit, uint32_t baudrate)
{
UNIT_SELECT(unit);
return uart_set_baudrate(inst, baudrate);
}
void
mrb_uart_set_format(int unit, uint32_t data_bits, uint32_t stop_bits,
uint8_t parity)
{
UNIT_SELECT_V(unit);
uart_set_format(inst, data_bits, stop_bits, (uart_parity_t)parity);
}
void
mrb_uart_set_flow_control(int unit, bool cts, bool rts)
{
UNIT_SELECT_V(unit);
uart_set_hw_flow(inst, cts, rts);
}
void
mrb_uart_write(int unit, const uint8_t *src, size_t len)
{
UNIT_SELECT_V(unit);
uart_write_blocking(inst, src, len);
}
void
mrb_uart_flush(int unit)
{
UNIT_SELECT_V(unit);
uart_tx_wait_blocking(inst);
}
void
mrb_uart_send_break(int unit, uint32_t duration_ms)
{
UNIT_SELECT_V(unit);
uart_set_break(inst, true);
sleep_ms(duration_ms);
uart_set_break(inst, false);
}
void
mrb_uart_clear_rx(int unit)
{
UNIT_SELECT_V(unit);
while (uart_is_readable(inst)) {
uart_getc(inst);
}
}
void
mrb_uart_clear_tx(int unit)
{
/* not supported on RP2040 */
}
#include <mruby.h>
void mrb_hw_rp2040_uart_gem_init(mrb_state *mrb) {}
void mrb_hw_rp2040_uart_gem_final(mrb_state *mrb) {}
+182
View File
@@ -0,0 +1,182 @@
# hw-uart - UART peripheral interface for mruby
This gem provides the `UART` class for serial communication from mruby.
It is designed for embedded platforms such as ESP32 and RP2040.
## Architecture
- **hw-uart** (this gem) - Ruby API, C bindings, ring buffer, and HAL
function declarations
- **hw-esp32-uart** - HAL implementation for ESP32 (using ESP-IDF UART
driver with FreeRTOS task for RX)
- **hw-rp2040-uart** - HAL implementation for RP2040 (using Pico SDK
with IRQ-driven RX)
Received data is buffered in a ring buffer (allocated by the common
gem) that the platform HAL populates via interrupt or task. The ring
buffer size must be a power of two.
## Build Configuration
```ruby
# For ESP32
MRuby::CrossBuild.new('esp32') do |conf|
# ...
conf.gem "#{root}/mrbgems/hw-esp32-uart"
end
# For RP2040
MRuby::CrossBuild.new('rp2040') do |conf|
# ...
conf.gem "#{root}/mrbgems/hw-rp2040-uart"
end
```
## Ruby API
### Constants
| Constant | Value | Description |
|-------------------------------|-------|---------------------|
| `UART::PARITY_NONE` | `0` | No parity |
| `UART::PARITY_EVEN` | `1` | Even parity |
| `UART::PARITY_ODD` | `2` | Odd parity |
| `UART::FLOW_CONTROL_NONE` | `0` | No flow control |
| `UART::FLOW_CONTROL_RTS_CTS` | `1` | Hardware flow ctrl |
### UART.new
```ruby
uart = UART.new(
unit: :ESP32_UART1, # UART unit name (required)
tx_pin: 17, # TX GPIO pin (default: -1)
rx_pin: 16, # RX GPIO pin (default: -1)
baudrate: 9600, # baud rate (default: 9600)
data_bits: 8, # 5-8 (default: 8)
stop_bits: 1, # 1-2 (default: 1)
parity: UART::PARITY_NONE,
flow_control: UART::FLOW_CONTROL_NONE,
rx_buffer_size: 256 # must be power of two (default: 256)
)
```
#### Unit Names
| Platform | Available Units |
|----------|--------------------------------------------------|
| ESP32 | `:ESP32_UART0`, `:ESP32_UART1`, `:ESP32_UART2`\* |
| RP2040 | `:RP2040_UART0`, `:RP2040_UART1` |
\*UART2 availability depends on ESP32 variant.
### Instance Methods
#### UART#write(str)
Write a string to the UART. Returns number of bytes written.
```ruby
uart.write("Hello\r\n")
```
#### UART#read(len = nil)
Read from the RX buffer. Returns `nil` if no data is available.
- Without argument: returns all available data
- With `len`: returns exactly `len` bytes, or `nil` if fewer are
available
```ruby
data = uart.read # all available
data = uart.read(10) # exactly 10 bytes or nil
```
#### UART#readpartial(maxlen)
Read up to `maxlen` bytes from the RX buffer. Returns `nil` if empty.
```ruby
data = uart.readpartial(64)
```
#### UART#gets
Read a line (up to and including `"\n"`). Returns `nil` if no
complete line is available.
```ruby
line = uart.gets
```
#### UART#bytes_available
Returns the number of bytes in the RX buffer.
```ruby
n = uart.bytes_available
```
#### UART#puts(str)
Write string with line ending appended (if not already present).
```ruby
uart.puts("Hello") # writes "Hello\n"
```
#### UART#flush
Wait for all TX data to be sent.
#### UART#clear_rx_buffer / UART#clear_tx_buffer
Discard buffered data.
#### UART#send_break(duration_ms = 100)
Send a UART break signal for the specified duration.
#### UART#setmode(baudrate:, data_bits:, stop_bits:, parity:, flow_control:)
Reconfigure UART parameters after initialization. All parameters are
optional.
#### UART#baudrate
Returns the current baud rate.
#### UART#line_ending=(ending)
Set the line ending used by `puts`. Must be `"\n"`, `"\r"`, or
`"\r\n"`.
## HAL Interface
To add support for a new platform, create a gem that depends on
`hw-uart` and implements the following C functions declared in
`<mruby/uart.h>`:
```c
int mrb_uart_unit_name_to_num(const char *name);
mrb_uart_status mrb_uart_init(int unit, uint32_t tx_pin, uint32_t rx_pin,
mrb_uart_ringbuf *rxbuf);
uint32_t mrb_uart_set_baudrate(int unit, uint32_t baudrate);
void mrb_uart_set_format(int unit, uint32_t data_bits,
uint32_t stop_bits, uint8_t parity);
void mrb_uart_set_flow_control(int unit, bool cts, bool rts);
void mrb_uart_write(int unit, const uint8_t *src, size_t len);
void mrb_uart_flush(int unit);
void mrb_uart_send_break(int unit, uint32_t duration_ms);
void mrb_uart_clear_rx(int unit);
void mrb_uart_clear_tx(int unit);
```
The `rxbuf` parameter passed to `mrb_uart_init` is a ring buffer
allocated by the common gem. The platform must arrange for received
bytes to be pushed into it using `mrb_uart_ringbuf_push()` (e.g.,
from an interrupt handler or RTOS task).
## License
MIT
+60
View File
@@ -0,0 +1,60 @@
#ifndef MRUBY_UART_H
#define MRUBY_UART_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MRB_UART_PARITY_NONE 0
#define MRB_UART_PARITY_EVEN 1
#define MRB_UART_PARITY_ODD 2
#define MRB_UART_FLOW_NONE 0
#define MRB_UART_FLOW_RTS_CTS 1
typedef enum {
MRB_UART_OK = 0,
MRB_UART_ERROR_UNIT = -1,
} mrb_uart_status;
/* Ring buffer for interrupt-driven RX.
Allocated by common gem, populated by platform interrupt handler.
size must be a power of two. */
typedef struct {
volatile int head;
volatile int tail;
int mask;
uint8_t data[];
} mrb_uart_ringbuf;
/* Ring buffer helpers (implemented in hw-uart/src/ringbuf.c) */
bool mrb_uart_ringbuf_init(mrb_uart_ringbuf *rb, int size);
bool mrb_uart_ringbuf_push(mrb_uart_ringbuf *rb, uint8_t ch);
int mrb_uart_ringbuf_pop(mrb_uart_ringbuf *rb, uint8_t *dst, int len);
int mrb_uart_ringbuf_available(const mrb_uart_ringbuf *rb);
void mrb_uart_ringbuf_clear(mrb_uart_ringbuf *rb);
int mrb_uart_ringbuf_search(const mrb_uart_ringbuf *rb, uint8_t ch);
/* HAL functions - implemented by hw-<platform>-uart gems */
int mrb_uart_unit_name_to_num(const char *name);
mrb_uart_status mrb_uart_init(int unit, uint32_t tx_pin, uint32_t rx_pin,
mrb_uart_ringbuf *rxbuf);
uint32_t mrb_uart_set_baudrate(int unit, uint32_t baudrate);
void mrb_uart_set_format(int unit, uint32_t data_bits, uint32_t stop_bits,
uint8_t parity);
void mrb_uart_set_flow_control(int unit, bool cts, bool rts);
void mrb_uart_write(int unit, const uint8_t *src, size_t len);
void mrb_uart_flush(int unit);
void mrb_uart_send_break(int unit, uint32_t duration_ms);
void mrb_uart_clear_rx(int unit);
void mrb_uart_clear_tx(int unit);
#ifdef __cplusplus
}
#endif
#endif /* MRUBY_UART_H */
+5
View File
@@ -0,0 +1,5 @@
MRuby::Gem::Specification.new('hw-uart') do |spec|
spec.license = 'MIT'
spec.authors = ['HASUMI Hitoshi', 'mruby developers']
spec.summary = 'UART peripheral interface'
end
+54
View File
@@ -0,0 +1,54 @@
class UART
PARITY_NONE = 0
PARITY_EVEN = 1
PARITY_ODD = 2
FLOW_CONTROL_NONE = 0
FLOW_CONTROL_RTS_CTS = 1
attr_reader :baudrate
def initialize(unit:, tx_pin: -1, rx_pin: -1, baudrate: 9600,
data_bits: 8, stop_bits: 1, parity: PARITY_NONE,
flow_control: FLOW_CONTROL_NONE, rx_buffer_size: 256)
__open_rx_buffer(rx_buffer_size)
@unit_num = __open_connection(unit.to_s, tx_pin, rx_pin)
@baudrate = __set_baudrate(baudrate)
__set_format(data_bits, stop_bits, parity)
set_flow_control(flow_control)
@line_ending = "\n"
end
def setmode(baudrate: nil, data_bits: nil, stop_bits: nil,
parity: nil, flow_control: nil)
@baudrate = __set_baudrate(baudrate) if baudrate
__set_format(data_bits || 8, stop_bits || 1, parity || PARITY_NONE)
set_flow_control(flow_control || FLOW_CONTROL_NONE)
self
end
def line_ending=(ending)
unless ["\n", "\r", "\r\n"].include?(ending)
raise ArgumentError, "invalid line ending"
end
@line_ending = ending
end
def puts(str)
write str
write @line_ending unless str.end_with?(@line_ending)
nil
end
private
def set_flow_control(mode)
case mode
when FLOW_CONTROL_NONE
__set_flow_control(false, false)
when FLOW_CONTROL_RTS_CTS
__set_flow_control(true, true)
else
raise ArgumentError, "invalid flow control mode"
end
end
end
+59
View File
@@ -0,0 +1,59 @@
#include <mruby/uart.h>
bool
mrb_uart_ringbuf_init(mrb_uart_ringbuf *rb, int size)
{
/* size must be a power of two */
if (size <= 0 || (size & (size - 1)) != 0) return false;
rb->head = 0;
rb->tail = 0;
rb->mask = size - 1;
return true;
}
bool
mrb_uart_ringbuf_push(mrb_uart_ringbuf *rb, uint8_t ch)
{
int next = (rb->head + 1) & rb->mask;
if (next == rb->tail) return false; /* full */
rb->data[rb->head] = ch;
rb->head = next;
return true;
}
int
mrb_uart_ringbuf_pop(mrb_uart_ringbuf *rb, uint8_t *dst, int len)
{
int i;
for (i = 0; i < len; i++) {
if (rb->tail == rb->head) break; /* empty */
dst[i] = rb->data[rb->tail];
rb->tail = (rb->tail + 1) & rb->mask;
}
return i;
}
int
mrb_uart_ringbuf_available(const mrb_uart_ringbuf *rb)
{
return (rb->head - rb->tail) & rb->mask;
}
void
mrb_uart_ringbuf_clear(mrb_uart_ringbuf *rb)
{
rb->tail = rb->head;
}
int
mrb_uart_ringbuf_search(const mrb_uart_ringbuf *rb, uint8_t ch)
{
int pos = rb->tail;
int i = 0;
while (pos != rb->head) {
if (rb->data[pos] == ch) return i;
pos = (pos + 1) & rb->mask;
i++;
}
return -1;
}
+236
View File
@@ -0,0 +1,236 @@
#include <mruby.h>
#include <mruby/presym.h>
#include <mruby/variable.h>
#include <mruby/string.h>
#include <mruby/data.h>
#include <mruby/class.h>
#include <mruby/uart.h>
#define E_IO_ERROR mrb_exc_get_id(mrb, MRB_SYM(IOError))
#define DEFAULT_RX_BUF_SIZE 256
static void
rxbuf_free(mrb_state *mrb, void *ptr)
{
mrb_free(mrb, ptr);
}
static const struct mrb_data_type rxbuf_type = { "UART", rxbuf_free };
/* UART#__open_rx_buffer(size) */
static mrb_value
mrb_uart_m_open_rxbuf(mrb_state *mrb, mrb_value self)
{
mrb_int size;
mrb_get_args(mrb, "i", &size);
if (size <= 0) size = DEFAULT_RX_BUF_SIZE;
mrb_uart_ringbuf *rb = (mrb_uart_ringbuf*)mrb_malloc(mrb,
sizeof(mrb_uart_ringbuf) + sizeof(uint8_t) * size);
if (!mrb_uart_ringbuf_init(rb, (int)size)) {
mrb_free(mrb, rb);
mrb_raise(mrb, E_ARGUMENT_ERROR, "rx_buffer_size must be a power of two");
}
DATA_PTR(self) = rb;
DATA_TYPE(self) = &rxbuf_type;
return mrb_nil_value();
}
/* UART#__open_connection(unit_name, tx_pin, rx_pin) */
static mrb_value
mrb_uart_m_open_conn(mrb_state *mrb, mrb_value self)
{
const char *name;
mrb_int tx_pin, rx_pin;
mrb_get_args(mrb, "zii", &name, &tx_pin, &rx_pin);
int num = mrb_uart_unit_name_to_num(name);
if (num < 0) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown UART unit: %s", name);
}
mrb_uart_ringbuf *rb = (mrb_uart_ringbuf*)mrb_data_get_ptr(mrb, self, &rxbuf_type);
mrb_uart_status st = mrb_uart_init(num, (uint32_t)tx_pin, (uint32_t)rx_pin, rb);
if (st != MRB_UART_OK) {
mrb_raise(mrb, E_IO_ERROR, "UART init failed");
}
return mrb_fixnum_value(num);
}
/* UART#__set_baudrate(baud) */
static mrb_value
mrb_uart_m_set_baudrate(mrb_state *mrb, mrb_value self)
{
mrb_int baud;
mrb_get_args(mrb, "i", &baud);
mrb_int unit = mrb_integer(mrb_iv_get(mrb, self, MRB_IVSYM(unit_num)));
uint32_t actual = mrb_uart_set_baudrate((int)unit, (uint32_t)baud);
return mrb_fixnum_value(actual);
}
/* UART#__set_format(data_bits, stop_bits, parity) */
static mrb_value
mrb_uart_m_set_format(mrb_state *mrb, mrb_value self)
{
mrb_int data_bits, stop_bits, parity;
mrb_get_args(mrb, "iii", &data_bits, &stop_bits, &parity);
mrb_int unit = mrb_integer(mrb_iv_get(mrb, self, MRB_IVSYM(unit_num)));
mrb_uart_set_format((int)unit, (uint32_t)data_bits, (uint32_t)stop_bits, (uint8_t)parity);
return mrb_nil_value();
}
/* UART#__set_flow_control(cts, rts) */
static mrb_value
mrb_uart_m_set_flow(mrb_state *mrb, mrb_value self)
{
mrb_bool cts, rts;
mrb_get_args(mrb, "bb", &cts, &rts);
mrb_int unit = mrb_integer(mrb_iv_get(mrb, self, MRB_IVSYM(unit_num)));
mrb_uart_set_flow_control((int)unit, cts, rts);
return mrb_nil_value();
}
/* UART#write(str) */
static mrb_value
mrb_uart_m_write(mrb_state *mrb, mrb_value self)
{
mrb_value str;
mrb_get_args(mrb, "S", &str);
mrb_int unit = mrb_integer(mrb_iv_get(mrb, self, MRB_IVSYM(unit_num)));
size_t len = RSTRING_LEN(str);
mrb_uart_write((int)unit, (const uint8_t*)RSTRING_PTR(str), len);
return mrb_fixnum_value(len);
}
/* UART#read(len=nil) */
static mrb_value
mrb_uart_m_read(mrb_state *mrb, mrb_value self)
{
mrb_int len = -1;
mrb_get_args(mrb, "|i", &len);
mrb_uart_ringbuf *rb = (mrb_uart_ringbuf*)mrb_data_get_ptr(mrb, self, &rxbuf_type);
int avail = mrb_uart_ringbuf_available(rb);
if (avail == 0) return mrb_nil_value();
if (len >= 0) {
if (avail < len) return mrb_nil_value();
avail = (int)len;
}
uint8_t *buf = (uint8_t*)mrb_malloc(mrb, avail);
int n = mrb_uart_ringbuf_pop(rb, buf, avail);
mrb_value str = mrb_str_new(mrb, (const char*)buf, n);
mrb_free(mrb, buf);
return str;
}
/* UART#readpartial(maxlen) */
static mrb_value
mrb_uart_m_readpartial(mrb_state *mrb, mrb_value self)
{
mrb_int maxlen;
mrb_get_args(mrb, "i", &maxlen);
mrb_uart_ringbuf *rb = (mrb_uart_ringbuf*)mrb_data_get_ptr(mrb, self, &rxbuf_type);
int avail = mrb_uart_ringbuf_available(rb);
if (avail == 0) return mrb_nil_value();
if (avail > maxlen) avail = (int)maxlen;
uint8_t *buf = (uint8_t*)mrb_malloc(mrb, avail);
int n = mrb_uart_ringbuf_pop(rb, buf, avail);
mrb_value str = mrb_str_new(mrb, (const char*)buf, n);
mrb_free(mrb, buf);
return str;
}
/* UART#bytes_available */
static mrb_value
mrb_uart_m_bytes_available(mrb_state *mrb, mrb_value self)
{
mrb_uart_ringbuf *rb = (mrb_uart_ringbuf*)mrb_data_get_ptr(mrb, self, &rxbuf_type);
return mrb_fixnum_value(mrb_uart_ringbuf_available(rb));
}
/* UART#gets */
static mrb_value
mrb_uart_m_gets(mrb_state *mrb, mrb_value self)
{
mrb_uart_ringbuf *rb = (mrb_uart_ringbuf*)mrb_data_get_ptr(mrb, self, &rxbuf_type);
int pos = mrb_uart_ringbuf_search(rb, (uint8_t)'\n');
if (pos < 0) return mrb_nil_value();
int len = pos + 1;
uint8_t *buf = (uint8_t*)mrb_malloc(mrb, len);
mrb_uart_ringbuf_pop(rb, buf, len);
mrb_value str = mrb_str_new(mrb, (const char*)buf, len);
mrb_free(mrb, buf);
return str;
}
/* UART#flush */
static mrb_value
mrb_uart_m_flush(mrb_state *mrb, mrb_value self)
{
mrb_int unit = mrb_integer(mrb_iv_get(mrb, self, MRB_IVSYM(unit_num)));
mrb_uart_flush((int)unit);
return self;
}
/* UART#clear_tx_buffer */
static mrb_value
mrb_uart_m_clear_tx(mrb_state *mrb, mrb_value self)
{
mrb_int unit = mrb_integer(mrb_iv_get(mrb, self, MRB_IVSYM(unit_num)));
mrb_uart_clear_tx((int)unit);
return self;
}
/* UART#clear_rx_buffer */
static mrb_value
mrb_uart_m_clear_rx(mrb_state *mrb, mrb_value self)
{
mrb_uart_ringbuf *rb = (mrb_uart_ringbuf*)mrb_data_get_ptr(mrb, self, &rxbuf_type);
mrb_uart_ringbuf_clear(rb);
mrb_int unit = mrb_integer(mrb_iv_get(mrb, self, MRB_IVSYM(unit_num)));
mrb_uart_clear_rx((int)unit);
return self;
}
/* UART#send_break(duration_ms=100) */
static mrb_value
mrb_uart_m_send_break(mrb_state *mrb, mrb_value self)
{
mrb_int ms = 100;
mrb_get_args(mrb, "|i", &ms);
mrb_int unit = mrb_integer(mrb_iv_get(mrb, self, MRB_IVSYM(unit_num)));
mrb_uart_send_break((int)unit, (uint32_t)ms);
return self;
}
void
mrb_hw_uart_gem_init(mrb_state *mrb)
{
struct RClass *cls = mrb_define_class_id(mrb, MRB_SYM(UART), mrb->object_class);
MRB_SET_INSTANCE_TT(cls, MRB_TT_CDATA);
mrb_define_method_id(mrb, cls, MRB_SYM(__open_rx_buffer), mrb_uart_m_open_rxbuf, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, cls, MRB_SYM(__open_connection), mrb_uart_m_open_conn, MRB_ARGS_REQ(3));
mrb_define_method_id(mrb, cls, MRB_SYM(__set_baudrate), mrb_uart_m_set_baudrate, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, cls, MRB_SYM(__set_format), mrb_uart_m_set_format, MRB_ARGS_REQ(3));
mrb_define_method_id(mrb, cls, MRB_SYM(__set_flow_control), mrb_uart_m_set_flow, MRB_ARGS_REQ(2));
mrb_define_method_id(mrb, cls, MRB_SYM(write), mrb_uart_m_write, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, cls, MRB_SYM(read), mrb_uart_m_read, MRB_ARGS_OPT(1));
mrb_define_method_id(mrb, cls, MRB_SYM(readpartial), mrb_uart_m_readpartial, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, cls, MRB_SYM(bytes_available), mrb_uart_m_bytes_available, MRB_ARGS_NONE());
mrb_define_method_id(mrb, cls, MRB_SYM(gets), mrb_uart_m_gets, MRB_ARGS_NONE());
mrb_define_method_id(mrb, cls, MRB_SYM(flush), mrb_uart_m_flush, MRB_ARGS_NONE());
mrb_define_method_id(mrb, cls, MRB_SYM(clear_tx_buffer), mrb_uart_m_clear_tx, MRB_ARGS_NONE());
mrb_define_method_id(mrb, cls, MRB_SYM(clear_rx_buffer), mrb_uart_m_clear_rx, MRB_ARGS_NONE());
mrb_define_method_id(mrb, cls, MRB_SYM(send_break), mrb_uart_m_send_break, MRB_ARGS_OPT(1));
}
void
mrb_hw_uart_gem_final(mrb_state *mrb)
{
}