I'm working on adding a more Nim friendly api for SPI on the ESP32. It uses a fairly flexible api similar to many Linux kernel calls where you setup a C struct and pass it into various functions.
I'd like to ensure that the memory buffer sent to the SPI api isn't free'ed early as some of the functions work in the background using interrupts. Also, I'm assuming that the lifetime of a GC'ed object follows to the end of the proc scope and not after the last usage point, though that's just for usage. Using strings would be nice, but the best method of converting back and forth to a seq/array seems tedious.
Here's what I got so far and would like some feedback to see if this will work or if there's a cleaner method:
type
bits* = distinct int
SpiError* = object of OSError
code*: esp_err_t
# A Nim object to wrap the esp native struct and a reference to the data seq's
SpiTrans* = ref object
trn*: spi_transaction_t
tx_data*: seq[uint8]
rx_data*: seq[uint8]
Followed by an initialization proc:
proc newSpiTrans*[N](spi: spi_device_handle_t;
data: array[N, uint8],
rxlen: bits = bits(9),
len: bits = bits(-1),
): SpiTrans =
if len.int < 0:
result.trn.length = 8*data.len().csize_t() ## Command is 8 bits
else:
# Manually set bit length for non-byte length sizes
result.trn.length = len.uint
result.trn.rxlength = rxlen.uint
# For data less than 4 bytes, use data directly
when data.len() <= 3:
for i in 0..high(data):
result.trn.tx.data[i] = data[i] ## The data is set in the pointer
else:
# This order is important, copy the seq then take the unsafe addr
result.tx_data = data.toSeq()
result.trn.tx.buffer = unsafeAddr(result.tx_data[0])
Here's a link to the esp-idf spi api: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html
Since code size might become an issue, you should use openArray instead of a generic array:
proc newSpiTrans*(spi: spi_device_handle_t;
data: openArray[uint8] ...
I don't know if you should avoid the ref object wrapping, usually C code is also happy with data that lives on the stack.
Since code size might become an issue, you should use openArray instead of a generic array:
So the compiler generate a separate function for each array[N, uint8]? Ah yah that'd be unfortunate for code size. Thanks!
I don't know if you should avoid the ref object wrapping, usually C code is also happy with data that lives on the stack.
Yah I've been unsure on that. However, the SPI api does background processing so you might loose that bit of the stack before the transaction is done. In the code example from esp32 they allocate the structs in a static global array to avoid that.