Skip to content

Commit 7bdefb4

Browse files
Prototype SPI implementation
1 parent a939ca1 commit 7bdefb4

File tree

3 files changed

+357
-1
lines changed

3 files changed

+357
-1
lines changed

examples/StandardFirmataPlus/StandardFirmataPlus.ino

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
1212
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
1313
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
14-
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
14+
Copyright (C) 2009-2017 Jeff Hoefs. All rights reserved.
1515
1616
This library is free software; you can redistribute it and/or
1717
modify it under the terms of the GNU Lesser General Public
@@ -52,6 +52,8 @@
5252
// Arduino IDE v1.6.6 or higher. Hardware serial should work back to Arduino 1.0.
5353
#include "utility/SerialFirmata.h"
5454

55+
#include "utility/SPIFirmata.h"
56+
5557
#define I2C_WRITE B00000000
5658
#define I2C_READ B00001000
5759
#define I2C_READ_CONTINUOUSLY B00010000
@@ -76,6 +78,10 @@
7678
SerialFirmata serialFeature;
7779
#endif
7880

81+
#ifdef FIRMATA_SPI_FEATURE
82+
SPIFirmata spiFeature;
83+
#endif
84+
7985
/* analog inputs */
8086
int analogInputsToReport = 0; // bitwise array to store pin reporting
8187

@@ -684,6 +690,9 @@ void sysexCallback(byte command, byte argc, byte *argv)
684690
}
685691
#ifdef FIRMATA_SERIAL_FEATURE
686692
serialFeature.handleCapability(pin);
693+
#endif
694+
#ifdef FIRMATA_SPI_FEATURE
695+
spiFeature.handleCapability(pin);
687696
#endif
688697
Firmata.write(127);
689698
}
@@ -716,6 +725,12 @@ void sysexCallback(byte command, byte argc, byte *argv)
716725
case SERIAL_MESSAGE:
717726
#ifdef FIRMATA_SERIAL_FEATURE
718727
serialFeature.handleSysex(command, argc, argv);
728+
#endif
729+
break;
730+
731+
case SPI_DATA:
732+
#ifdef FIRMATA_SPI_FEATURE
733+
spiFeature.handleSysex(command, argc, argv);
719734
#endif
720735
break;
721736
}
@@ -736,6 +751,10 @@ void systemResetCallback()
736751
serialFeature.reset();
737752
#endif
738753

754+
#ifdef FIRMATA_SPI_FEATURE
755+
spiFeature.reset();
756+
#endif
757+
739758
if (isI2CEnabled) {
740759
disableI2CPins();
741760
}
@@ -759,6 +778,7 @@ void systemResetCallback()
759778

760779
servoPinMap[i] = 255;
761780
}
781+
762782
// by default, do not report any analog inputs
763783
analogInputsToReport = 0;
764784

utility/SPIFirmata.cpp

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
/*
2+
SPIFirmata.cpp
3+
Copyright (C) 2017 Jeff Hoefs. All rights reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
See file LICENSE.txt for further informations on licensing terms.
11+
12+
Last updated September 23rd, 2017
13+
*/
14+
15+
#include "SPIFirmata.h"
16+
17+
SPIFirmata::SPIFirmata()
18+
{
19+
init();
20+
}
21+
22+
void SPIFirmata::init()
23+
{
24+
mDeviceId = 0;
25+
mCsPin = -1;
26+
mCsActiveState = SPI_CS_ACTIVE_LOW; // default
27+
}
28+
29+
bool SPIFirmata::handlePinMode(uint8_t pin, int mode)
30+
{
31+
// There is no reason for a user to manually set the SPI pin modes
32+
return false;
33+
}
34+
35+
void SPIFirmata::handleCapability(uint8_t pin)
36+
{
37+
// ignore SS pin for now
38+
if (IS_PIN_SPI(pin) && pin != SS) {
39+
Firmata.write(PIN_MODE_SPI);
40+
// would actually use a value that corresponds to a specific pin (MOSI, MISO, SCK)
41+
// for now, just set to 1
42+
Firmata.write(1);
43+
}
44+
}
45+
46+
bool SPIFirmata::handleSysex(uint8_t command, uint8_t argc, uint8_t *argv)
47+
{
48+
if (command == SPI_DATA) {
49+
uint8_t mode = argv[0];
50+
// Not using channel yet since the Arduino SPI API currently exposes only one channel.
51+
uint8_t channel = argv[1] & SPI_CHANNEL_MASK;
52+
53+
switch (mode) {
54+
case SPI_BEGIN:
55+
SPI.begin();
56+
// SPI pin states are configured by SPI.begin, but we still register them with Firmata.
57+
Firmata.setPinMode(MOSI, PIN_MODE_SPI);
58+
Firmata.setPinMode(MISO, PIN_MODE_SPI);
59+
Firmata.setPinMode(SCK, PIN_MODE_SPI);
60+
// Ignore SS for now.
61+
//Firmata.setPinMode(SS, PIN_MODE_SPI);
62+
break;
63+
case SPI_BEGIN_TRANSACTION:
64+
{
65+
mDeviceId = argv[1] >> 2;
66+
uint8_t bitOrder = argv[2] & SPI_BIT_ORDER_MASK;
67+
uint8_t dataMode = argv[2] >> 1;
68+
uint32_t clockSpeed = (uint32_t)argv[3] | ((uint32_t)argv[4] << 7) |
69+
((uint32_t)argv[5] << 14) | ((uint32_t)argv[6] << 21) | ((uint32_t)argv[7] << 28);
70+
71+
// argv[8] = wordSize, but not currently used since SPI.transfer only uses 8-bit words
72+
73+
if (argc > 9) {
74+
mCsPin = argv[9];
75+
pinMode(mCsPin, OUTPUT);
76+
77+
if (argv[10] != END_SYSEX) {
78+
mCsActiveState = argv[10] & 0x01;
79+
} else {
80+
// Set default
81+
mCsActiveState = SPI_CS_ACTIVE_LOW;
82+
}
83+
// Set CS pin to opposite of active state
84+
digitalWrite(mCsPin, !mCsActiveState);
85+
86+
// TODO - determine if we need to protect the CS pin.
87+
// If PIN_MODE_SPI is set like this, the user cannot manually control the CS pin
88+
// using DIGITAL_MESSAGE.
89+
// Firmata.setPinMode(mCsPin, PIN_MODE_SPI);
90+
}
91+
92+
SPISettings settings(clockSpeed, getBitOrder(bitOrder), getDataMode(dataMode));
93+
SPI.beginTransaction(settings);
94+
break;
95+
}
96+
case SPI_END_TRANSACTION:
97+
SPI.endTransaction();
98+
break;
99+
case SPI_TRANSFER:
100+
{
101+
uint8_t csPinControl = argv[2];
102+
uint8_t numBytes = argv[3];
103+
104+
if (mCsPin >= 0) setCsPinState(csPinControl, true);
105+
106+
transfer(channel, numBytes, argc, argv);
107+
108+
if (mCsPin >= 0) setCsPinState(csPinControl, false);
109+
110+
break; // SPI_TRANSFER
111+
}
112+
case SPI_WRITE:
113+
{
114+
uint8_t csPinControl = argv[2];
115+
uint8_t numBytes = argv[3];
116+
117+
if (mCsPin >= 0) setCsPinState(csPinControl, true);
118+
119+
writeOnly(channel, numBytes, argc, argv);
120+
121+
if (mCsPin >= 0) setCsPinState(csPinControl, false);
122+
123+
break; // SPI_WRITE
124+
}
125+
case SPI_READ:
126+
{
127+
uint8_t csPinControl = argv[2];
128+
uint8_t numBytes = argv[3];
129+
130+
if (mCsPin >= 0) setCsPinState(csPinControl, true);
131+
132+
readOnly(channel, numBytes);
133+
134+
if (mCsPin >= 0) setCsPinState(csPinControl, false);
135+
136+
break; // SPI_READ
137+
}
138+
case SPI_END:
139+
SPI.end();
140+
break;
141+
} // end switch
142+
return true;
143+
}
144+
return false;
145+
}
146+
147+
void SPIFirmata::reset()
148+
{
149+
init();
150+
}
151+
152+
void SPIFirmata::setCsPinState(uint8_t pinControl, bool start)
153+
{
154+
bool csStartOnly = false;
155+
bool csEndOnly = false;
156+
157+
if (pinControl == SPI_CS_DISABLE) {
158+
return;
159+
}
160+
if (pinControl == SPI_CS_START_ONLY) {
161+
csStartOnly = true;
162+
} else if (pinControl == SPI_CS_END_ONLY) {
163+
csEndOnly = true;
164+
}
165+
166+
// Evaluate whether or not to set the active state at the start or end of the transfer.
167+
if (start && !csEndOnly) {
168+
digitalWrite(mCsPin, mCsActiveState);
169+
} else if (!start && !csStartOnly) {
170+
digitalWrite(mCsPin, !mCsActiveState);
171+
}
172+
}
173+
174+
void SPIFirmata::transfer(uint8_t channel, uint8_t numBytes, uint8_t argc, uint8_t *argv)
175+
{
176+
uint8_t offset = 4; // mode + channel + opts + numBytes
177+
uint8_t buffer[numBytes];
178+
uint8_t bufferIndex = 0;
179+
if (numBytes * 2 != argc - offset) {
180+
// TODO - handle error
181+
Firmata.sendString("SPI transfer fails numBytes test");
182+
}
183+
for (uint8_t i = 0; i < numBytes * 2; i += 2) {
184+
bufferIndex = (i + 1) / 2;
185+
buffer[bufferIndex] = argv[i + offset + 1] << 7 | argv[i + offset];
186+
}
187+
// During the transfer, the received buffer data is stored in the buffer in-place.
188+
SPI.transfer(buffer, numBytes);
189+
190+
reply(channel, numBytes, buffer);
191+
}
192+
193+
void SPIFirmata::writeOnly(uint8_t channel, uint8_t numBytes, uint8_t argc, uint8_t *argv)
194+
{
195+
uint8_t offset = 4; // mode + channel + opts + numBytes
196+
uint8_t txValue;
197+
if (numBytes * 2 != argc - offset) {
198+
// TODO - handle error
199+
Firmata.sendString("SPI write fails numBytes test");
200+
}
201+
for (uint8_t i = 0; i < numBytes * 2; i += 2) {
202+
txValue = argv[i + offset + 1] << 7 | argv[i + offset];
203+
// TODO - consider using SPI.transfer(buffer, size)
204+
SPI.transfer(txValue);
205+
}
206+
}
207+
208+
void SPIFirmata::readOnly(uint8_t channel, uint8_t numBytes)
209+
{
210+
uint8_t replyData[numBytes];
211+
for (uint8_t i = 0; i < numBytes; i++) {
212+
replyData[i] = SPI.transfer(0x00);
213+
}
214+
reply(channel, numBytes, replyData);
215+
}
216+
217+
void SPIFirmata::reply(uint8_t channel, uint8_t numBytes, uint8_t *buffer)
218+
{
219+
Firmata.write(START_SYSEX);
220+
Firmata.write(SPI_DATA);
221+
Firmata.write(SPI_REPLY);
222+
Firmata.write(mDeviceId << 2 | channel);
223+
Firmata.write(numBytes);
224+
225+
for (uint8_t i = 0; i < numBytes; i++) {
226+
Firmata.write(buffer[i] & 0x7F);
227+
Firmata.write(buffer[i] >> 7 & 0x7F);
228+
}
229+
230+
Firmata.write(END_SYSEX);
231+
}

0 commit comments

Comments
 (0)