mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
hw-spi: add SPI peripheral gem for embedded platforms
Add hw-spi gem with SPI communication support: - Common Ruby API with write, read, and full-duplex transfer - ports/esp32/ using ESP-IDF SPI master driver - ports/rp2040/ using Pico SDK Based on picoruby-spi by HASUMI Hitoshi. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# hw-spi - SPI peripheral interface for mruby
|
||||
|
||||
This gem provides the `SPI` class for Serial Peripheral Interface
|
||||
communication from mruby. It is designed for embedded platforms
|
||||
such as ESP32 and RP2040.
|
||||
|
||||
## Architecture
|
||||
|
||||
Platform-specific HAL implementations are in `ports/` directories:
|
||||
|
||||
- `ports/esp32/` - ESP32 using ESP-IDF SPI master driver
|
||||
- `ports/rp2040/` - RP2040 using Pico SDK
|
||||
|
||||
The build system automatically compiles matching port sources based
|
||||
on `conf.ports` setting.
|
||||
|
||||
## Build Configuration
|
||||
|
||||
```ruby
|
||||
MRuby::CrossBuild.new('esp32') do |conf|
|
||||
conf.ports :esp32
|
||||
conf.gem core: 'hw-spi'
|
||||
end
|
||||
```
|
||||
|
||||
## Ruby API
|
||||
|
||||
### SPI.new
|
||||
|
||||
```ruby
|
||||
spi = SPI.new(
|
||||
unit: :RP2040_SPI0, # SPI unit name (required)
|
||||
frequency: 100_000, # clock frequency in Hz (default: 100kHz)
|
||||
sck_pin: -1, # SCK GPIO pin (default: platform default)
|
||||
copi_pin: -1, # COPI/MOSI GPIO pin
|
||||
cipo_pin: -1, # CIPO/MISO GPIO pin
|
||||
cs_pin: -1, # CS GPIO pin (-1 for manual control)
|
||||
mode: 0, # SPI mode 0-3 (default: 0)
|
||||
first_bit: SPI::MSB_FIRST # bit order (default: MSB_FIRST)
|
||||
)
|
||||
```
|
||||
|
||||
#### Unit Names
|
||||
|
||||
| Platform | Available Units |
|
||||
|----------|-----------------------------------------------------|
|
||||
| ESP32 | `:ESP32_SPI2_HOST`, `:ESP32_HSPI_HOST`, |
|
||||
| | `:ESP32_SPI3_HOST`\*, `:ESP32_VSPI_HOST`\* |
|
||||
| RP2040 | `:RP2040_SPI0`, `:RP2040_SPI1` |
|
||||
|
||||
\*SPI3 availability depends on ESP32 variant.
|
||||
|
||||
### SPI#write(*data)
|
||||
|
||||
Write data to the SPI bus. Data can be Integer, Array, or String.
|
||||
|
||||
```ruby
|
||||
spi.write(0x01, [0x02, 0x03])
|
||||
```
|
||||
|
||||
### SPI#read(len, tx_value = 0)
|
||||
|
||||
Read `len` bytes. Optionally specify the value to transmit during
|
||||
read.
|
||||
|
||||
```ruby
|
||||
data = spi.read(4) # transmits 0x00 while reading
|
||||
data = spi.read(4, 0xFF) # transmits 0xFF while reading
|
||||
```
|
||||
|
||||
### SPI#transfer(*data, additional_read_bytes: 0)
|
||||
|
||||
Full-duplex transfer. Sends data and returns received bytes.
|
||||
Use `additional_read_bytes:` to append zero-filled read bytes.
|
||||
|
||||
```ruby
|
||||
# Send 1 byte command, read 4 bytes response
|
||||
rx = spi.transfer(0x9F, additional_read_bytes: 4)
|
||||
```
|
||||
|
||||
### SPI#select / SPI#deselect
|
||||
|
||||
Manually control the CS pin (when using GPIO-based chip select).
|
||||
|
||||
```ruby
|
||||
spi.select do |s|
|
||||
s.write(0x01)
|
||||
data = s.read(4)
|
||||
end # CS automatically deasserted
|
||||
```
|
||||
|
||||
## HAL Interface
|
||||
|
||||
To add support for a new platform, create a `ports/<name>/`
|
||||
directory and implement the following C functions declared in
|
||||
`<mruby/spi.h>`:
|
||||
|
||||
```c
|
||||
int mrb_spi_unit_name_to_num(const char *name);
|
||||
mrb_spi_status mrb_spi_init(mrb_spi_info *info);
|
||||
int mrb_spi_read(mrb_spi_info *info, uint8_t *dst, size_t len,
|
||||
uint8_t tx_val);
|
||||
int mrb_spi_write(mrb_spi_info *info, const uint8_t *src, size_t len);
|
||||
int mrb_spi_transfer(mrb_spi_info *info, const uint8_t *tx,
|
||||
uint8_t *rx, size_t len);
|
||||
```
|
||||
|
||||
The `mrb_spi_info` struct contains all configuration (unit, pins,
|
||||
frequency, mode, bit order) and is passed to every HAL call.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef MRUBY_SPI_H
|
||||
#define MRUBY_SPI_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MRB_SPI_MSB_FIRST 1
|
||||
#define MRB_SPI_LSB_FIRST 0
|
||||
|
||||
typedef enum {
|
||||
MRB_SPI_OK = 0,
|
||||
MRB_SPI_ERROR_UNIT = -1,
|
||||
MRB_SPI_ERROR_MODE = -2,
|
||||
MRB_SPI_ERROR_FIRST_BIT = -3,
|
||||
MRB_SPI_ERROR_INIT = -4,
|
||||
} mrb_spi_status;
|
||||
|
||||
typedef struct {
|
||||
uint32_t frequency;
|
||||
uint8_t unit_num;
|
||||
int8_t sck_pin;
|
||||
int8_t copi_pin;
|
||||
int8_t cipo_pin;
|
||||
int8_t cs_pin;
|
||||
uint8_t mode;
|
||||
uint8_t first_bit;
|
||||
} mrb_spi_info;
|
||||
|
||||
/* HAL functions - implemented in ports/<platform>/spi.c */
|
||||
int mrb_spi_unit_name_to_num(const char *name);
|
||||
mrb_spi_status mrb_spi_init(mrb_spi_info *info);
|
||||
int mrb_spi_read(mrb_spi_info *info, uint8_t *dst, size_t len, uint8_t tx_val);
|
||||
int mrb_spi_write(mrb_spi_info *info, const uint8_t *src, size_t len);
|
||||
int mrb_spi_transfer(mrb_spi_info *info, const uint8_t *tx, uint8_t *rx, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MRUBY_SPI_H */
|
||||
@@ -0,0 +1,5 @@
|
||||
MRuby::Gem::Specification.new('hw-spi') do |spec|
|
||||
spec.license = 'MIT'
|
||||
spec.authors = ['HASUMI Hitoshi', 'mruby developers']
|
||||
spec.summary = 'SPI peripheral interface'
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
class SPI
|
||||
MSB_FIRST = 1
|
||||
LSB_FIRST = 0
|
||||
DEFAULT_FREQUENCY = 100_000
|
||||
|
||||
def select
|
||||
@cs&.write 0
|
||||
if block_given?
|
||||
begin
|
||||
yield self
|
||||
ensure
|
||||
deselect
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def deselect
|
||||
@cs&.write 1
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,85 @@
|
||||
#include <string.h>
|
||||
#include "driver/spi_master.h"
|
||||
#include <mruby/spi.h>
|
||||
|
||||
static spi_device_handle_t handles[SPI_HOST_MAX];
|
||||
|
||||
int
|
||||
mrb_spi_unit_name_to_num(const char *name)
|
||||
{
|
||||
if (strcmp(name, "ESP32_SPI2_HOST") == 0) return SPI2_HOST;
|
||||
if (strcmp(name, "ESP32_HSPI_HOST") == 0) return SPI2_HOST;
|
||||
#if (SOC_SPI_PERIPH_NUM == 3)
|
||||
if (strcmp(name, "ESP32_SPI3_HOST") == 0) return SPI3_HOST;
|
||||
if (strcmp(name, "ESP32_VSPI_HOST") == 0) return SPI3_HOST;
|
||||
#endif
|
||||
return MRB_SPI_ERROR_UNIT;
|
||||
}
|
||||
|
||||
mrb_spi_status
|
||||
mrb_spi_init(mrb_spi_info *info)
|
||||
{
|
||||
if (handles[info->unit_num] != NULL) return MRB_SPI_OK;
|
||||
|
||||
spi_bus_config_t buscfg = {
|
||||
.mosi_io_num = info->copi_pin,
|
||||
.miso_io_num = info->cipo_pin,
|
||||
.sclk_io_num = info->sck_pin,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
};
|
||||
|
||||
esp_err_t err = spi_bus_initialize(info->unit_num, &buscfg, SPI_DMA_CH_AUTO);
|
||||
if (err != ESP_OK) return MRB_SPI_ERROR_INIT;
|
||||
|
||||
spi_device_interface_config_t devcfg = {
|
||||
.clock_speed_hz = info->frequency,
|
||||
.mode = info->mode,
|
||||
.spics_io_num = info->cs_pin,
|
||||
.queue_size = 7,
|
||||
};
|
||||
|
||||
err = spi_bus_add_device(info->unit_num, &devcfg, &handles[info->unit_num]);
|
||||
if (err != ESP_OK) {
|
||||
spi_bus_free(info->unit_num);
|
||||
handles[info->unit_num] = NULL;
|
||||
return MRB_SPI_ERROR_INIT;
|
||||
}
|
||||
return MRB_SPI_OK;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_spi_read(mrb_spi_info *info, uint8_t *dst, size_t len, uint8_t tx_val)
|
||||
{
|
||||
spi_transaction_t t = {
|
||||
.length = len * 8,
|
||||
.tx_buffer = NULL,
|
||||
.rx_buffer = dst,
|
||||
};
|
||||
esp_err_t err = spi_device_polling_transmit(handles[info->unit_num], &t);
|
||||
return (err == ESP_OK) ? (int)len : -1;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_spi_write(mrb_spi_info *info, const uint8_t *src, size_t len)
|
||||
{
|
||||
spi_transaction_t t = {
|
||||
.length = len * 8,
|
||||
.tx_buffer = src,
|
||||
.rx_buffer = NULL,
|
||||
};
|
||||
esp_err_t err = spi_device_polling_transmit(handles[info->unit_num], &t);
|
||||
return (err == ESP_OK) ? (int)len : -1;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_spi_transfer(mrb_spi_info *info, const uint8_t *tx, uint8_t *rx, size_t len)
|
||||
{
|
||||
spi_transaction_t t = {
|
||||
.length = len * 8,
|
||||
.tx_buffer = tx,
|
||||
.rx_buffer = rx,
|
||||
};
|
||||
esp_err_t err = spi_device_polling_transmit(handles[info->unit_num], &t);
|
||||
return (err == ESP_OK) ? (int)len : -1;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#include <string.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/spi.h"
|
||||
#include <mruby/spi.h>
|
||||
|
||||
#define UNIT_SELECT(info) \
|
||||
spi_inst_t *inst; \
|
||||
switch ((info)->unit_num) { \
|
||||
case 0: inst = spi0; break; \
|
||||
case 1: inst = spi1; break; \
|
||||
default: return MRB_SPI_ERROR_UNIT; \
|
||||
}
|
||||
|
||||
int
|
||||
mrb_spi_unit_name_to_num(const char *name)
|
||||
{
|
||||
if (strcmp(name, "RP2040_SPI0") == 0) return 0;
|
||||
if (strcmp(name, "RP2040_SPI1") == 0) return 1;
|
||||
return MRB_SPI_ERROR_UNIT;
|
||||
}
|
||||
|
||||
mrb_spi_status
|
||||
mrb_spi_init(mrb_spi_info *info)
|
||||
{
|
||||
UNIT_SELECT(info);
|
||||
spi_init(inst, info->frequency);
|
||||
|
||||
if (info->sck_pin < 0) info->sck_pin = PICO_DEFAULT_SPI_SCK_PIN;
|
||||
if (info->cipo_pin < 0) info->cipo_pin = PICO_DEFAULT_SPI_RX_PIN;
|
||||
if (info->copi_pin < 0) info->copi_pin = PICO_DEFAULT_SPI_TX_PIN;
|
||||
|
||||
gpio_set_function(info->sck_pin, GPIO_FUNC_SPI);
|
||||
gpio_set_function(info->cipo_pin, GPIO_FUNC_SPI);
|
||||
gpio_set_function(info->copi_pin, GPIO_FUNC_SPI);
|
||||
|
||||
if (info->first_bit != MRB_SPI_MSB_FIRST) {
|
||||
return MRB_SPI_ERROR_FIRST_BIT;
|
||||
}
|
||||
|
||||
spi_cpol_t cpol;
|
||||
spi_cpha_t cpha;
|
||||
switch (info->mode) {
|
||||
case 0: cpol = 0; cpha = 0; break;
|
||||
case 1: cpol = 0; cpha = 1; break;
|
||||
case 2: cpol = 1; cpha = 0; break;
|
||||
case 3: cpol = 1; cpha = 1; break;
|
||||
default: return MRB_SPI_ERROR_MODE;
|
||||
}
|
||||
spi_set_format(inst, 8, cpol, cpha, info->first_bit);
|
||||
|
||||
return MRB_SPI_OK;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_spi_read(mrb_spi_info *info, uint8_t *dst, size_t len, uint8_t tx_val)
|
||||
{
|
||||
UNIT_SELECT(info);
|
||||
return spi_read_blocking(inst, tx_val, dst, len);
|
||||
}
|
||||
|
||||
int
|
||||
mrb_spi_write(mrb_spi_info *info, const uint8_t *src, size_t len)
|
||||
{
|
||||
UNIT_SELECT(info);
|
||||
return spi_write_blocking(inst, src, len);
|
||||
}
|
||||
|
||||
int
|
||||
mrb_spi_transfer(mrb_spi_info *info, const uint8_t *tx, uint8_t *rx, size_t len)
|
||||
{
|
||||
UNIT_SELECT(info);
|
||||
return spi_write_read_blocking(inst, tx, rx, len);
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
#include <string.h>
|
||||
#include <mruby.h>
|
||||
#include <mruby/presym.h>
|
||||
#include <mruby/variable.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/array.h>
|
||||
#include <mruby/data.h>
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/spi.h>
|
||||
|
||||
#define STACK_BUF_SIZE 256
|
||||
#define E_IO_ERROR mrb_exc_get_id(mrb, MRB_SYM(IOError))
|
||||
|
||||
static void
|
||||
spi_info_free(mrb_state *mrb, void *ptr)
|
||||
{
|
||||
mrb_free(mrb, ptr);
|
||||
}
|
||||
|
||||
static const struct mrb_data_type spi_info_type = { "SPI", spi_info_free };
|
||||
|
||||
#define SPI_INFO(self) \
|
||||
((mrb_spi_info*)mrb_data_get_ptr(mrb, self, &spi_info_type))
|
||||
|
||||
static size_t
|
||||
spi_calc_size(mrb_state *mrb, mrb_value *args, mrb_int argc)
|
||||
{
|
||||
size_t total = 0;
|
||||
for (mrb_int i = 0; i < argc; i++) {
|
||||
switch (mrb_type(args[i])) {
|
||||
case MRB_TT_ARRAY:
|
||||
total += RARRAY_LEN(args[i]);
|
||||
break;
|
||||
case MRB_TT_INTEGER:
|
||||
total += 1;
|
||||
break;
|
||||
case MRB_TT_STRING:
|
||||
total += RSTRING_LEN(args[i]);
|
||||
break;
|
||||
default:
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "Integer, Array, or String expected");
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
static void
|
||||
spi_fill_buf(mrb_state *mrb, uint8_t *buf, mrb_value *args, mrb_int argc,
|
||||
size_t pad)
|
||||
{
|
||||
size_t pos = 0;
|
||||
for (mrb_int i = 0; i < argc; i++) {
|
||||
switch (mrb_type(args[i])) {
|
||||
case MRB_TT_ARRAY: {
|
||||
mrb_int alen = RARRAY_LEN(args[i]);
|
||||
const mrb_value *aptr = RARRAY_PTR(args[i]);
|
||||
for (mrb_int j = 0; j < alen; j++) {
|
||||
if (!mrb_integer_p(aptr[j])) {
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "array element must be Integer");
|
||||
}
|
||||
buf[pos++] = (uint8_t)mrb_integer(aptr[j]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MRB_TT_INTEGER:
|
||||
buf[pos++] = (uint8_t)mrb_integer(args[i]);
|
||||
break;
|
||||
case MRB_TT_STRING:
|
||||
memcpy(&buf[pos], RSTRING_PTR(args[i]), RSTRING_LEN(args[i]));
|
||||
pos += RSTRING_LEN(args[i]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
memset(&buf[pos], 0, pad);
|
||||
}
|
||||
|
||||
/* SPI.new(unit:, frequency:, sck_pin:, cipo_pin:, copi_pin:,
|
||||
cs_pin:, mode:, first_bit:) */
|
||||
static mrb_value
|
||||
mrb_spi_s_new(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
const char *unit_name;
|
||||
mrb_int freq = 100000, sck = -1, cipo = -1, copi = -1, cs = -1;
|
||||
mrb_int mode = 0, first_bit = MRB_SPI_MSB_FIRST;
|
||||
|
||||
const mrb_sym kw_names[] = {
|
||||
MRB_SYM(unit), MRB_SYM(frequency), MRB_SYM(sck_pin),
|
||||
MRB_SYM(cipo_pin), MRB_SYM(copi_pin), MRB_SYM(cs_pin),
|
||||
MRB_SYM(mode), MRB_SYM(first_bit)
|
||||
};
|
||||
mrb_value kw_values[8];
|
||||
mrb_kwargs kwargs = { 8, 7, kw_names, kw_values, NULL };
|
||||
mrb_get_args(mrb, ":", &kwargs);
|
||||
|
||||
/* unit is required */
|
||||
if (mrb_undef_p(kw_values[0])) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "unit: is required");
|
||||
}
|
||||
unit_name = mrb_str_to_cstr(mrb, mrb_sym_str(mrb, mrb_symbol(kw_values[0])));
|
||||
|
||||
if (!mrb_undef_p(kw_values[1])) freq = mrb_integer(kw_values[1]);
|
||||
if (!mrb_undef_p(kw_values[2])) sck = mrb_integer(kw_values[2]);
|
||||
if (!mrb_undef_p(kw_values[3])) cipo = mrb_integer(kw_values[3]);
|
||||
if (!mrb_undef_p(kw_values[4])) copi = mrb_integer(kw_values[4]);
|
||||
if (!mrb_undef_p(kw_values[5])) cs = mrb_integer(kw_values[5]);
|
||||
if (!mrb_undef_p(kw_values[6])) mode = mrb_integer(kw_values[6]);
|
||||
if (!mrb_undef_p(kw_values[7])) first_bit = mrb_integer(kw_values[7]);
|
||||
|
||||
int num = mrb_spi_unit_name_to_num(unit_name);
|
||||
if (num < 0) {
|
||||
mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown SPI unit: %s", unit_name);
|
||||
}
|
||||
|
||||
mrb_spi_info *info = (mrb_spi_info*)mrb_malloc(mrb, sizeof(mrb_spi_info));
|
||||
info->unit_num = (uint8_t)num;
|
||||
info->frequency = (uint32_t)freq;
|
||||
info->sck_pin = (int8_t)sck;
|
||||
info->cipo_pin = (int8_t)cipo;
|
||||
info->copi_pin = (int8_t)copi;
|
||||
info->cs_pin = (int8_t)cs;
|
||||
info->mode = (uint8_t)mode;
|
||||
info->first_bit = (uint8_t)first_bit;
|
||||
|
||||
mrb_value self = mrb_obj_value(
|
||||
Data_Wrap_Struct(mrb, mrb_class_ptr(klass), &spi_info_type, info));
|
||||
|
||||
mrb_spi_status st = mrb_spi_init(info);
|
||||
if (st != MRB_SPI_OK) {
|
||||
mrb_raise(mrb, E_IO_ERROR, "SPI init failed");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/* SPI#write(*data) */
|
||||
static mrb_value
|
||||
mrb_spi_m_write(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value *args;
|
||||
mrb_int argc;
|
||||
mrb_get_args(mrb, "*", &args, &argc);
|
||||
|
||||
mrb_spi_info *info = SPI_INFO(self);
|
||||
size_t total = spi_calc_size(mrb, args, argc);
|
||||
if (total == 0) return mrb_fixnum_value(0);
|
||||
|
||||
uint8_t sbuf[STACK_BUF_SIZE];
|
||||
uint8_t *buf = sbuf;
|
||||
mrb_bool need_free = FALSE;
|
||||
if (total > STACK_BUF_SIZE) {
|
||||
buf = (uint8_t*)mrb_malloc(mrb, total);
|
||||
need_free = TRUE;
|
||||
}
|
||||
spi_fill_buf(mrb, buf, args, argc, 0);
|
||||
|
||||
int ret = mrb_spi_write(info, buf, total);
|
||||
if (need_free) mrb_free(mrb, buf);
|
||||
if (ret < 0) mrb_raise(mrb, E_IO_ERROR, "SPI write failed");
|
||||
return mrb_fixnum_value(ret);
|
||||
}
|
||||
|
||||
/* SPI#read(len, tx_value=0) */
|
||||
static mrb_value
|
||||
mrb_spi_m_read(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int len, tx_val = 0;
|
||||
mrb_get_args(mrb, "i|i", &len, &tx_val);
|
||||
if (len <= 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "length must be positive");
|
||||
|
||||
mrb_spi_info *info = SPI_INFO(self);
|
||||
uint8_t *buf = (uint8_t*)mrb_malloc(mrb, len);
|
||||
int ret = mrb_spi_read(info, buf, (size_t)len, (uint8_t)tx_val);
|
||||
if (ret < 0) {
|
||||
mrb_free(mrb, buf);
|
||||
mrb_raise(mrb, E_IO_ERROR, "SPI read failed");
|
||||
}
|
||||
mrb_value str = mrb_str_new(mrb, (const char*)buf, ret);
|
||||
mrb_free(mrb, buf);
|
||||
return str;
|
||||
}
|
||||
|
||||
/* SPI#transfer(*data, additional_read_bytes: 0) */
|
||||
static mrb_value
|
||||
mrb_spi_m_transfer(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value *args;
|
||||
mrb_int argc, extra = 0;
|
||||
const mrb_sym kw_names[] = { MRB_SYM(additional_read_bytes) };
|
||||
mrb_value kw_values[1];
|
||||
mrb_kwargs kwargs = { 1, 0, kw_names, kw_values, NULL };
|
||||
mrb_get_args(mrb, "*:", &args, &argc, &kwargs);
|
||||
|
||||
if (!mrb_undef_p(kw_values[0])) extra = mrb_integer(kw_values[0]);
|
||||
|
||||
mrb_spi_info *info = SPI_INFO(self);
|
||||
size_t total = spi_calc_size(mrb, args, argc) + (size_t)extra;
|
||||
if (total == 0) return mrb_str_new(mrb, "", 0);
|
||||
|
||||
uint8_t sbuf_tx[STACK_BUF_SIZE], sbuf_rx[STACK_BUF_SIZE];
|
||||
uint8_t *tx = sbuf_tx, *rx = sbuf_rx;
|
||||
mrb_bool need_free = FALSE;
|
||||
if (total > STACK_BUF_SIZE) {
|
||||
tx = (uint8_t*)mrb_malloc(mrb, total * 2);
|
||||
rx = tx + total;
|
||||
need_free = TRUE;
|
||||
}
|
||||
spi_fill_buf(mrb, tx, args, argc, (size_t)extra);
|
||||
|
||||
int ret = mrb_spi_transfer(info, tx, rx, total);
|
||||
if (ret < 0) {
|
||||
if (need_free) mrb_free(mrb, tx);
|
||||
mrb_raise(mrb, E_IO_ERROR, "SPI transfer failed");
|
||||
}
|
||||
mrb_value str = mrb_str_new(mrb, (const char*)rx, ret);
|
||||
if (need_free) mrb_free(mrb, tx);
|
||||
return str;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_hw_spi_gem_init(mrb_state *mrb)
|
||||
{
|
||||
struct RClass *cls = mrb_define_class_id(mrb, MRB_SYM(SPI), mrb->object_class);
|
||||
MRB_SET_INSTANCE_TT(cls, MRB_TT_CDATA);
|
||||
|
||||
mrb_define_class_method_id(mrb, cls, MRB_SYM(new), mrb_spi_s_new, MRB_ARGS_KEY(8, 1));
|
||||
mrb_define_method_id(mrb, cls, MRB_SYM(write), mrb_spi_m_write, MRB_ARGS_REST());
|
||||
mrb_define_method_id(mrb, cls, MRB_SYM(read), mrb_spi_m_read, MRB_ARGS_ARG(1, 1));
|
||||
mrb_define_method_id(mrb, cls, MRB_SYM(transfer), mrb_spi_m_transfer, MRB_ARGS_REST()|MRB_ARGS_KEY(1, 0));
|
||||
}
|
||||
|
||||
void
|
||||
mrb_hw_spi_gem_final(mrb_state *mrb)
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user