Hey all, I've been tossing around the idea creating a "Nim-ified" API for SPI & I2C device access. Ideally this would be "cross-platform" API targeted for generic devices similar to Arduino's Wire.h or SPI.h. Any other thoughts people would have on a good API to start from? I know there's a couple of i2c/spi implementations out there for specific systems.
The main high style difference I can think is essentially to use one of:
var myTemp: I2CDev = newI2cDev()
# using type and overloading `write` proc:
myTemp.write(0xA,0xB)
# or use C style prefixes:
myTemp.i2c_write(0xA,0xB)
Another important questions, would it make sense to define the interface using a concept? I'm still a bit unclear what the best(preferred?) technique is in Nim for defining a generic API that different packages can implement.
Prefixes are for C, idiomatic Nim doesn't use them.
I'm still a bit unclear what the best(preferred?) technique is in Nim for defining a generic API that different packages can implement.
While you can use concepts for that, I usually use pretty much "interface by convention". The modules adhere to the "interface" if they happen to implement the right routines. It's not hard to write a test that enforces the proper interface, for example, all your implementations include a common test file that enforces the API.
I like your usage of send/recv vs read/write. Seems to match the nature of the hardware protocols better.
Intriguing usage of static. I could've used that the other day! Though I'm not certain how well static would work for some platforms. On Linux you need to open and configure the i2c/spi devices. Same on some rtos'es. Or is there a way to configure the static devices (looks like you have a start proc)?
Shouldn't be a problem to create a const object that contains information about the registers or file-like objects (for Linux) relevant to the specific bus. Then init can do some some work on those registers/file-like objects.
Some devices have configurable sercoms (https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports/overview) which can be configured as either spi/i2c/uart at runtime. Maybe in this case a macro could be used to "configure" the sercom once (at compile time) as either spi/etc and generate the appropriate const object.
"Configure" here meaning to instantiate a const object, which is either I2C or SPI etc with a reference to the sercom specific registers. Not doing any actual hardware work (obviously, it's a macro, but I wanted to clarify).
Shouldn't be a problem to create a const object that contains information about the registers or file-like objects (for Linux) relevant to the specific bus. Then init can do some some work on those registers/file-like objects.
That part isn't too tricky.
Some devices have configurable sercoms (https://learn.adafruit.com/using-atsamd21-sercom-to-add-more-spi-i2c-serial-ports/overview) which can be configured as either spi/i2c/uart at runtime. Maybe in this case a macro could be used to "configure" the sercom once (at compile time) as either spi/etc and generate the appropriate const object.
This part is a bit trickier. Both the i2c/spi bus drivers on ESP32 FreeRTOS and on Zephyr RTOS expect a pointer to a struct. The struct is different for each device that accesses the bus. For SPI it's usually the CS pin, while I2C it's the address. Sometime you will want your code to allocate the bus device struct on the stack, perform it's bus calls, and then let the struct disappear to save on the RAM once the function ends.
I'm not sure how easy it'd be to create const's that you could ensure were valid C structs. Preferably being able to put them into the .data sections of a program so it'd only be accessed as needed directly from flash. Do you have any suggestions on doing that with Nim const's? I'm at a loss on that.
And Zephyr's Device Tree API is essentially a (complicated C Preprocessor) version of the const driver idea. My problem is just the practical implementation side on embedded targets. There's some odd tricks some devices do.
I'm not sure how easy it'd be to create const's that you could ensure were valid C structs.
My understanding is that nim objects map 1:1 to C structs. c2nim always add the {.bycopy.} pragma, but I'm not sure why that's required. However, I also just learned that you can't create a const pointer, including const objects that contain ptr in their fields. If your struct is all values, then it should be OK, you can take its addr at run time.
Preferably being able to put them into the .data sections of a program so it'd only be accessed as needed directly from flash.
No idea about that. Something something linker script?
However, I also just learned that you can't create a const pointer, including const objects that contain ptr in their fields. If your struct is all values, then it should be OK, you can take its addr at run time.
Yah that’d be a blocker. Most of these device bus structs take a pointer of some type. Making the valid C structs in Nim is easy but doing it at compile time isn’t for some of these device api’s.
Overall it’s probably simpler to make a generic API just take a device config object. Likely a ‘ref object’ so it’d be easy to do end-of-life cleanup if wanted.
But as pointed out by Araq above it seems that Nim api’s more by convention than explicit. So as long as the arguments and names were similar across projects people would know how to use them. So some platforms could use const’s and other objects, etc.