-
Notifications
You must be signed in to change notification settings - Fork 516
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 SPI #245
Comments
Has anyone attempted implementation of this yet? If not, I have a definite and immediate need that I'd like to try to implement the proposal towards. |
There is an implementation. Still needs a few tweaks but would be great to have people start testing it to see where or if it falls short. See this thread: #341. The implementation is in this branch: https://github.com/firmata/arduino/tree/spi-alpha. There were a couple of changes I had not yet made in the spi-alpha branch yet that were discussed here: firmata/protocol#88. |
Excellent! I'll give it a spin and give some feedback. Thanks! |
@overdamped I've updated the SPI implementation in the spi-alpha branch to match the firmata SPI protocol proposal. Please try this version and provide any feedback you may have. I have an example client-side nodeJS implementation as well in the firmata.js spi-alpha branch. I have only been able to test on an old LIS3LV02DQ accelerometer as that is the only SPI component I have. I'd love to see this tested on SPI components require sending and receiving more than 64 bytes at a time (this will test the multi-packet transfer case). Because Firmata sends data in 7-bit chunks, this means a 32 byte data packet would require 64 bytes to send. A SPI LCD screen is probably a good test case. The part of the Firmata SPI proposal that I still think is kinda ugly, but don't have a better solution for is the [csPinControl] byte required with every SPI_TRANSFER, SPI_WRITE, or SPI_READ message. This is to allow the firmware to control the CS pin while still allowing multi-packet or streaming transfers (necessary if the packet length exceeds the transport (Serial, TCP, BLE) buffer size, which is 64 bytes for Serial for most architectures). However, this is the most efficient method I've found, the main advantage is that most simple SPI transfers (transfer, write or read) only require a single Firmata message. The 2 less efficient alternatives are: Alternate 1: Requiring the user to frame every transfer with SPI_BEGIN_TRANSACTION and SPI_END_TRANSACTION messages (requires sending 3 Firmata messages for even simple transfers). In this case the SPI_BEGIN_TRANSACTION and SPI_END_TRANSACTION would handle toggling the CS pin appropriately. It would probably be the best approach semantically, but is not as efficient as what is currently proposed and implemented. Alternate 2: Require the user to manually control the CS pin. This would also require sending 3 Firmata messages even for simple transfers. @zfields FYI |
I figure it's easier to understand if I attempt to explain the 3 different options for CS pin handling with a concrete example. To read the x, y, and z axis data from the LIS3LV02DQ accelerometer, the following steps are required:
There are at least 3 different ways this could be implemented in Firmata. Option 1 (current implementation)
In option 1 SPI_BEGIN_TRANSACTION only needs to be sent once per device or when switching to another device on the same SPI bus. // client-side example
board.spiBeginTransaction(deviceId, options);
...
function readAccelData() {
// only requires 2 separate messages, but requires the user to understand
// how to use the pinControl options correctly and is thus more error prone
board.spiWrite([MULTIBYTE_READ], {pinControl: CS_START_ONLY});
board.spiRead(NUM_BYTES_TO_READ, {pinControl: CS_END_ONLY}, function(data) {
// parse and act on the accelerometer data...
});
} Option 2 (frame every transfer as a transaction)
// client-side example
function readAccelData() {
// requires 4 separate messages, but it's clean
board.spiBeginTransaction(deviceId, options);
board.spiWrite([MULTIBYTE_READ]);
board.spiRead(NUM_BYTES_TO_READ, function(data) {
// parse and act on the accelerometer data...
});
board.spiEndTransaction();
} Option 3 (manual control of CS pin)
In option 3 SPI_BEGIN_TRANSACTION only needs to be sent once per device or when switching to another device on the same SPI bus. // client-side example
board.spiBeginTransaction(deviceId, options);
...
function readAccelData() {
// also requires 4 separate messages, but requires manually handling CS pins for every transfer
board.digitalWrite(csPin, LOW);
board.spiWrite([MULTIBYTE_READ]);
board.spiRead(NUM_BYTES_TO_READ, function(data) {
// parse and act on the accelerometer data...
});
board.digitalWrite(csPin, HIGH);
} |
A client library could also provide helper functions to mask some of the complexity in managing the CS pin. For example: // example client library helper function to provide a higher level of abstraction
function readRegister(register, numBytesToRead, callback) {
board.spiWrite(register, {pinControl: CS_START_ONLY});
board.spiRead(numBytesToRead, {pinControl: CS_END_ONLY}, callback);
} This enables our LIS3LV02DQ accelerometer example to use more familiar syntax: board.spiBegin();
var deviceId = 9; // must be unique per device per application
board.spiBeginTransaction(deviceId, {
bitOrder: MSBFIRST,
dataMode: board.SPI_DATA_MODES.MODE0,
maxClockSpeed: 2500000, // 2.5 Mhz
csPin: 2,
csActiveState: board.SPI_CS_ACTIVE_STATE.LOW
});
// Device on, 40hz, normal mode, all axis' enabled
board.spiWrite([register.CTRL_REG1, 0x87]);
// +/- 2g resolution
board.spiWrite([register.CTRL_REG2, 0x40]);
// setup multi-byte read
var readAddress = register.READ | READ_BIT | MULTI_BYTE_BIT;
board.readRegister(readAddress, BYTES_TO_READ, function(data) {
var x = (data[1] << 8) | data[0];
var y = (data[3] << 8) | data[2];
var z = (data[5] << 8) | data[4];
console.log("X: " + x + " Y: " + y + " Z: " + z);
});
board.spiEndTransaction(); |
Also, as a convenience when using more than one device on a single SPI bus,
|
I had started work on the interface a while back: firmata/protocol#27.
And a thread here: firmata/protocol#26.
The tricky part is there are a lot of edge cases to cover. A decision needs to be made on where to draw the line regarding what is in and what is out in terms of support. Ideally this can be done in a way that the interface can scale if it becomes apparent that more functionality is needed.
The text was updated successfully, but these errors were encountered: