-
Notifications
You must be signed in to change notification settings - Fork 151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add proposal for SPI #26
Comments
The trick with SPI is the configuration parameters for each SPI device may differ. Therefore a single SPI_CONFIG message would not be sufficient if parameters are passed via CONFIG. Some options:
|
I haven't been active in Firmata development for a while, but I certainly have with SPI. :) Arduino 1.6.0 was (finally) released 1 week ago, featuring a new SPI library with transaction support (contributed by me), and improved hardware-independent configuration (contributed by Matthijs Kooijman). I know Firmata has historically supported older versions of Arduino. But for supporting SPI, I'd highly recommend using the new SPISettings() feature with SPI.beginTransaction(). If the settings will be delivered over the Firmata protocol, you can use them once to store a SPISettings object, so the overhead of converting the settings to SPISettings's efficient internal storage only needs to be done once. This new SPISettings approach is much more efficient, and it's hardware neutral. There's no need to ever write code dealing with hardware dividers or clock speeds. You just give it the actual SPI settings, with clock speed as a plain 32 bit integer, and the SPISettings feature in the library automatically turns those into the hardware's closest possible configuration. Arduino hasn't documented this stuff yet on their website. Hopefully they will soon. Today, the only docs are the code+comments, and this page on my site: |
It's great to see you chiming in here Paul! Your expertise is appreciated. Thanks for the tip, that actually makes thing much easier for the Arduino implementation. I'm trying to keep the Firmata protocol documentation (this particular repo) from being Arduino-specific in case some day platforms other than Arduino adopt the Firmata protocol. However I think the Arduino SPI api is still generic enough to work on other platforms, especially now that the SPISettings allows specifying a general clockSpeed rather than a AVR-specific clockDivider parameter. |
Here's an initial pass at a generic Firmata SPI protocol. For Arduino, SPI support would be for Arduino 1.6 and higher.
|
One thing I learned last year, while working on the Arduino SPI library, is the tremendous number of different ways SPI actually gets used. I really do not believe it's possible to design a Firmata protocol that will accommodate absolutely all SPI uses. The question is probably how large a fraction of uses should it cover? For example, many of the SPI interface displays take 2 control signals, a chip select and an other CS-like signal which functions as an address bit, for which register within the chip will be accessed. Some devices require an extra dummy byte after chip select is de-asserted. Some require chip select only on the first or last byte. There are lots of special cases. A complex protocol that could cover a lot of cases might involve sending several extra bits with each data byte. Four or 5 bits could indicate which digital pins to assert for chip select, during each byte. Perhaps 1 bit could indicate if the received byte during that transfer needs to be returned, or simply discarded. Or, you could implement the paradigm where a single chip select goes low before the first transfer, and returns high after the last byte. That certainly is enough for a lot of SPI chips. But there are also a good number of special cases, so at least know that other stuff exists. |
Paul do you remember any specific SPI devices you came across that exhibit some of the edge cases you encountered? I'd like to look over their data sheets. I don't plan to support all possible cases. SPI devices requiring continuous high speed data transfer (like a mp3 decoder) would not be possible with Firmata and I'm fine with that. I think most users will want to use SPI for various sensors and perhaps some actuators. Those are the majority of requests for SPI support I've had so far are for sensors that use SPI and I usually point out the fact that the same sensors typically have an I2C interface which is already supported by Firmata. The option of using the extra 6 bits from every other byte is interesting... I had not thought of that approach before. It would require a few extra cycles on the microcontroller to process but it would add a few options to expand the range of supported devices for sure so it may be worth it if that increases overall support by a significant factor. I'll look into this further. My current thought is to return a byte for every byte transferred. If the transfer doesn't return data, I'd just send a value of 0 for that byte. Then it would be the responsibility of the Firmata client to properly parse the response data according to the particular SPI device in use. However, I may vary well be overlooking something here. It's been a while since I've done any extensive work with SPI. I could also keep the CS pin out of the definition and require the user to manage that separately (along with any additional CS-like signal as you mentioned) but there is something convenient of supplying those pins in the initial CONFIG message rather than sending separate messages to toggle CS and transfer data. |
If a SPI device does not need to return data (such as an LCD screen or other write-only device) then my assumption about always returning data would be a waste of bandwidth. In that case, using one of the extra 6 bits to indicate whether or not to return a byte could work, or I could alternatively use a bit in the command byte of the SPI_TRANSFER_REQUEST message:
This would work if no bytes in the message (assuming a multi-byte message) return data. If at least 1 transferred byte in the message returns data, then an padded byte for each message would be returned since a response would be needed anyway. |
For my USB device Nusbio, I implemented part of i2c and spi protocol. Here some interresting chips, I tested Classic SPI
Not Classic SPI
|
The latest iteration of the SPI proposal is here: #27 |
but is there a firmata vertion with at least basic master to slave SPI support? i tried using RC low pass filters for DAC conversion but the quality is terrible. when will firmata have SPI support? regards |
I need someone much more experienced with SPI than myself to contribute to lead this effort as I am not as familiar with all of the edge cases that Paul pointed out. So far no one has taken this on and any existing SPI implementations for Firmata are very basic and only cover one or two common cases. I don't want to lock into an implementation that will not scale well. |
but is there implementation for digipots like the AD5206 or the MCP41100? |
Not for Firmata |
Well it may work with some of the Firmata forks that add SPI. I haven't tried any of them. If you find one that works, encourage the author to submit a PR to add SPI support. |
Ray, |
hello the problem i have is that without at least some basic implementation of spi in firmata r.y |
Try ExtendedFirmata, it includes SPI support. |
tried to load extended firmata but i gives me errors errors like serial1, 2, 3 are not declare on this scope how can i solve this |
I see now that ExtendedFirmata is only compatible with an Arduino Mega or Due. |
ok, i tried in a due and now i have a: fatal error SoftwareSerial |
i took the: #include SoftwareSerial.h and where is the information need it to operate thanks so much for the help regards |
That's right... forgot Due doesn't support SoftwareSerial. It will only work with a Mega then. |
You will have to add SPI functionality to the host (sounds like PD in your case) unless you are using golang. |
in pd, you send a pinout definition message to enable the boards pins i don't know where are the instruction for driving a digipot from pd r.y |
You will likely need to create a new object for PD to implement the Firmata SPI interface as defined in ExtendedFirmata. I'm not a PD user so I can't help you there. |
thanks 4 all d help soundanalogous. the thing is that i cannot find anywhere the commands i need to enable the spi functionality. i tried finding some clues from the extendedfirmata file itself but all i have are errors. where can i find this messages or commands? it could be nice to have this new implementation in the firmata test program so you |
You need to look at this code specifically: https://github.com/kraman/go-firmata/blob/master/contrib/ExtendedFirmata/ExtendedFirmata.ino#L626-L663. However it is probably best to wait until SPI support is officially added to the Firmata protocol. At that point you can engage with the PD community for help on updating the PD Firmata client to add SPI support. It's not simply going to work just because Firmata gets SPI. Each firmata client also needs to implement that part of the protocol in order to get the new functionality. |
This is almost 2 years old. Whats the status of SPI + Firmata? I am in need of this feature as well because customized sensor libraries steal the 32k of space. |
I don't have a ton of experience with SPI so I don't feel the best suited person to take on this task, however no one else is volunteering which is why it has seen no progress in 2 years. |
@madeintheusb this isn't a place to advertise your services. |
I guess no SPI at all if we cannot advertise product that can do SPI. |
What I need here is consensus around a proposal so that a SPI implementation for Firmata can move forward. This is the most recent version: #27. |
Did a substantial revision the SPI proposal today: #27 Feedback is welcome. |
Picking this up again and I think the following changes to the proposal are necessary (and an improvement over past iterations):
There are a couple of different approaches to Option AMake passing the config params to
Option BIntroduce a new We keep the deviceId parameter in this case to use as a lookup. There have been past discussions about using the CS pin as a lookup, but I think there are a few advantages of using a separate ID:
This
Note: In an non-Arduino implementation In this approach we pass the
ConclusionI'm torn between Option A and B. Option A is simple and requires less memory, but Option B is more flexible and may map better to non-Arduino implementations of this protocol. |
Another approach (Option C) that would save memory while also potentially being more friendly with non-Arduino implementations (Raspberry Pi, etc) would be to add the new |
Is this just for configurableFirmata or are you considering it for standardFirmata as well? |
Both because it adds board-level functionality. |
Well, you could set a low number of SPI devices allocated to static memory (2-4) for standardFirmata. If someone needs more devices, it's a small tweak that could probably be made by changing a single constant. Plus they could use configurableFirmata to get something with just the parts they need to save space so I'm in the Option B camp. I took a look at Tessel 2 JS API and the Electric Imp and Particle firmware API's. All the concepts seem to fit well with your proposed protocol so adding support in those that mirror the support in firmata.js shouldn't be a problem. Curiously Tessel doesn't seem to support changing bit order. |
Using a user tweakable |
@soundanalogous I'd like to provide some feedback related to how the proposal would map to Linux boards like the Raspberry Pi but I have to ask a question first. The Raspberry Pi has three SPI controllers. Two of the controllers (SPI0 and SPI1) are broken out onto the GPIO header. Is what's referred to as an SPI controller on the Raspberry Pi referred to a channel in this SPI proposal? |
@fivdi That's correct SPI0 = channel 0, SPI1 = channel 1, etc. Some Arduino boards have multiple channels as well but only channel 0 is exposed via the Arduino SPI api. |
Although there's more than one way to access SPI devices on Linux boards like the Raspberry Pi the assumption here is that character special device as described here are used for access. Comments about the SPI ProposalSPI_BEGINSPI_BEGIN is used to initialize the SPI bus. On Linux systems userspace programs are typically not involved in the initialization of an SPI bus. An SPI bus is typically initialized during the boot process and userspace programs simply use the SPI bus. On Linux systems userspace programs access SPI devices using character special device files. SPI_BEGIN_TRANSACTIONbitOrder
dataMode
maxSpeed
wordSize
csPin
csPinOptions
SPI_TRANSFERFull-duplex write/read transfers are supported using ioctl() requests on Linux. csPinControl
SPI_ENDCall once to release SPI hardware send before quitting a Firmata client application. On Linux systems this can be achieved by closing the character special device files that are used to access SPI devices. TransactionsThis comment above discusses transaction that should generally be framed by begin/end transaction. On an Arduino UNO this will work well as the Firmata sketch running on the UNO has total control over everything and doesn't have to worry about another process/sketch attempting to access SPI devices while it is accessing those devices. On Linux where there are multiple processes this isn't necessarily the case. Multiple processes that are running at the same time may access the same SPI device at the same time. Linux makes such parallel accesses possible by allowing userspace processes to create a buffer containing multiple transfers. This buffer containing multiple transfers can be passed to the SPI driver with an ioctl() request and the driver will ensure that the transfers are performed as a transaction. These Linux transactions are fundamentally different to the transactions that are being proposed in the SPI proposal. Whether or not this is actually an issue is a different question. It would be possible to say that the SPI proposal will only function correctly in scenarios where a single process accesses SPI devices. I haven't said everything that I'd like to say here yet but I'm afraid it's getting late and I should go to bed. I'll return to say the rest asap. |
I'm dropping I think I can also make beginTransaction and endTransaction optional, even for Arduino-based implementations as long as I use SPI_DEVICE_CONFIG as described in this comment. |
This comment assumes that character special device files as described here are being used to access SPI devices on Linux.
Dropping csPinControl would be ok. Linux has a noChipSelect option that can be used to prevent the SPI driver from controlling the CS pin. The user is expected to control the CS pin is such cases.
Linux also supports SPI_READ and SPI_WRITE for convenience.
I'm not sure I fully understand here. Do you mean (1) optional as in they are not needed and will be removed from the proposal or do you mean (2) a mapping of the SPI proposal to Linux need not implement beginTransaction and endTransaction? Option AI don't think the SPI_BEGIN_TRANSACTION described in Option A can be mapped to anything on Linux. If I'm not mistaken SPI_BEGIN_TRANSACTION in Option A expresses the intent to communicate with a currently unknown device on a specific channel. The channel should be configured for communicating with this as of yet unknown device. On Linux it's only possible to set the configuration for a specific device on a specific channel. It's not possible to set the configuration for an unknown device on a specific channel. With Option A it wouldn't be possible to have two transactions running at the same time each of which is for a different device on the same channel. On Linux this is possible and the SPI driver takes care of everything. Option BThe SPI_DEVICE_CONFIG described in Option B would map well to Linux. It would mean opening a file and setting the configuration options for communication on that file. For example, to communicate with device 2 on bus 1 it would mean opening the file On the Raspberry Pi there are still the limitations mentioned for SPI_BEGIN_TRANSACTION in this comment. On other boards there will be similar limitations. The limitations will depend on the SPI driver. There is one remaining issue. The proposal says:
On Linux there can be device 0 on channel 0 and also device 0 on channel 1. These Linux device numbers are currently not available anywhere. Should they be encoded in
To be honest I don't think this encoding is very robust and something better would be needed. Option COption C from this comment is similar to Option A in that it expresses the intent to communicate with a currently unknown device on a specific channel which isn't possible on Linux. It's not necessary to switch to a device to communicate with it on Linux. It's possible to communicate with multiple devices at the same time. For example, a Raspberry Pi has 2 channels, channel 0 and channel 1. There can be 2 devices on channel 0 and 3 devices on channel 1. The corresponding files are:
It's possible to open all 5 five files and communicate with all 5 devices at the same time in 5 different threads (one device per thread). The SPI driver will keep everything synchronized ensuring that everything functions correctly. |
It would be in the proposal, but not a requirement for implementation. If I go with Option B, it sounds like that is not an issue on Linux. I'd implement begin/end transition for Arduino-compatible boards (where they are compiled with the Arduino IDE), but even then if I use Option B, an Arduino user would not be required to use begin/end transaction, but they would have to manually toggle the CS pin so I can't see a good reason to skip it, but the flexibility is there. Essentially it maps more to the old pre transaction way of doing things with SPI on Arduino. Regarding |
Actually I think we'll need the |
Here's an update to the proposal based on recent comments in the above thread: #105. I figured it will be easier to comment on details in context. |
Yes, this is correct.
I don't know of an application processor (for example, a Cortex-A processor capable of running Linux) with more than 4 SPI busses. This doesn't mean that none exist. I have seen microcontrollers (for example, Cortex-M microcontrollers not capable of running Linux) with more than 4 busses. The STM32F746ZG which is a Cortex-M7 microcontroller has up to 6 SPI busses. In addition, Linux also has an SPI driver that uses generic bitbanged GPIO. This means that in addition to hardware controlled SPI busses there can be software controlled SPI busses. Like hardware controlled SPI busses these software controlled SPI busses can be accessed using character special device files I'd say it would be better to allow up to 8 busses with up to 16 devices per bus. |
via conversation with @ajfisher on gitter:
There was a pull-request for SPI opened a while back: firmata/arduino#135. However, it's going to be more complex than just wrapping the Arduino SPI api (since that would be inefficient). Lets start by drafting a protocol here: https://github.com/firmata/protocol.
My initial thoughts are that it should have a SPI_CONFIG message (like I2C and many other features in Firmata). The config message should let the user specify the following:
CS/SS pin (this is also how you'll keep track of multiple SPI devices)
bit order (LSB or MSB first)
data mode (see the 4 modes defined here)
clock divider? (I'm not sure how this would work in Firmata or if it even applies... I think it depends on if a hardware enabled SS pin is selected on the board)
We'll need a way to call SPI.begin and SPI.end. I'm not sure if it's better to call begin when the config message is received or if there is any advantage in having it be a separate command. SPI_END can be a simple message to call SPI.end, perhaps also specifying the CS pin to be compliant with Arduino Due's extended SPI protocol (for other boards this parameter would simply be ignored in the implementation).
We'll need a SPI_REQUEST message. It should contain the CS/SS pin number and the data to be sent via SPI.transfer(val) on the board. It will be more efficient to send multiple bytes rather than a single byte (this likely also depends on the particular device) - so in the implementation you'd call SPI.transfer(val) multiple times for a single SPI_REQUEST rather than sending a separate Request for each byte). We'll need a strategy around buffering as well (more for the implementation than the protocol). It will be helpful to look across a variety of SPI devices to see what what type of data is sent... what would constitute a "packet". Be aware however of the 64 byte serial data buffer on Arduino since this will likely come into play. It would also be helpful to support a READ_CONTINUOUSLY mode like I2C Firmata (this is especially useful for SPI-based sensors).
And we need a SPI_REPLY message to send data back the the Firmata client. This should include the CS pin, maybe the number of bytes in the reply and whether or not this is the full packet (or if it's broken up into multiple packets). Perhaps this is as simple as indicating if this is the LAST set of data.
Also try to support Arduino Due's extended SPI feature as an option. You'd add this to the protocol for SPI_END and SPI_REQUEST (although I'm not sure how beneficial this extended set would be in a Firmata client library... may add more complexity than it's worth). This is lower priority.
The text was updated successfully, but these errors were encountered: