Skip to content

Commit 90c0adb

Browse files
initial prototype of SPI implementation
1 parent ae028fb commit 90c0adb

File tree

3 files changed

+303
-0
lines changed

3 files changed

+303
-0
lines changed

examples/StandardFirmataPlus/StandardFirmataPlus.ino

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

@@ -378,6 +384,11 @@ void setPinModeCallback(byte pin, int mode)
378384
case PIN_MODE_SERIAL:
379385
#ifdef FIRMATA_SERIAL_FEATURE
380386
serialFeature.handlePinMode(pin, PIN_MODE_SERIAL);
387+
#endif
388+
break;
389+
case PIN_MODE_SPI:
390+
#ifdef FIRMATA_SPI_FEATURE
391+
spiFeature.handlePinMode(pin, PIN_MODE_SPI);
381392
#endif
382393
break;
383394
default:
@@ -684,6 +695,9 @@ void sysexCallback(byte command, byte argc, byte *argv)
684695
}
685696
#ifdef FIRMATA_SERIAL_FEATURE
686697
serialFeature.handleCapability(pin);
698+
#endif
699+
#ifdef FIRMATA_SPI_FEATURE
700+
spiFeature.handleCapability(pin);
687701
#endif
688702
Firmata.write(127);
689703
}
@@ -716,6 +730,12 @@ void sysexCallback(byte command, byte argc, byte *argv)
716730
case SERIAL_MESSAGE:
717731
#ifdef FIRMATA_SERIAL_FEATURE
718732
serialFeature.handleSysex(command, argc, argv);
733+
#endif
734+
break;
735+
736+
case SPI_DATA:
737+
#ifdef FIRMATA_SPI_FEATURE
738+
spiFeature.handleSysex(command, argc, argv);
719739
#endif
720740
break;
721741
}
@@ -736,6 +756,10 @@ void systemResetCallback()
736756
serialFeature.reset();
737757
#endif
738758

759+
#ifdef FIRMATA_SPI_FEATURE
760+
spiFeature.reset();
761+
#endif
762+
739763
if (isI2CEnabled) {
740764
disableI2CPins();
741765
}

utility/SPIFirmata.cpp

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
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 January 3rd, 2017
13+
*/
14+
15+
#include "SPIFirmata.h"
16+
17+
SPIFirmata::SPIFirmata()
18+
{
19+
mDeviceId = 0;
20+
mCsPin = -1;
21+
}
22+
23+
boolean SPIFirmata::handlePinMode(byte pin, int mode)
24+
{
25+
// ignore SS pin for now
26+
if (mode == PIN_MODE_SPI && pin != SS) {
27+
Firmata.setPinMode(pin, PIN_MODE_SPI);
28+
return true;
29+
}
30+
return false;
31+
}
32+
33+
void SPIFirmata::handleCapability(byte pin)
34+
{
35+
// ignore SS pin for now
36+
if (IS_PIN_SPI(pin) && pin != SS) {
37+
Firmata.write(PIN_MODE_SPI);
38+
// would actually use a value that corresponds to a specific pin (MOSI, MISO, SCK)
39+
// for now, just set to 1
40+
Firmata.write(1);
41+
}
42+
}
43+
44+
boolean SPIFirmata::handleSysex(byte command, byte argc, byte *argv)
45+
{
46+
if (command == SPI_DATA) {
47+
byte mode = argv[0];
48+
// not using channel yet
49+
byte channel = argv[1] & SPI_CHANNEL_MASK;
50+
51+
switch (mode) {
52+
case SPI_CONFIG:
53+
SPI.begin();
54+
break;
55+
case SPI_BEGIN_TRANSACTION:
56+
{
57+
mDeviceId = argv[1] >> 2;
58+
byte bitOrder = argv[2] & SPI_BIT_ORDER_MASK;
59+
byte dataMode = argv[2] >> 1;
60+
long clockSpeed = (long)argv[3] | ((long)argv[4] << 7) | ((long)argv[5] << 14) |
61+
((long)argv[6] << 21) | ((long)argv[7] << 28);
62+
63+
if (argc > 8) {
64+
mCsPin = argv[8];
65+
pinMode(mCsPin, OUTPUT);
66+
// protect the CS pin
67+
Firmata.setPinMode(mCsPin, PIN_MODE_SPI);
68+
}
69+
SPISettings settings(clockSpeed, bitOrder, dataMode);
70+
SPI.beginTransaction(settings);
71+
break;
72+
}
73+
case SPI_END_TRANSACTION:
74+
SPI.endTransaction();
75+
break;
76+
case SPI_TRANSFER:
77+
{
78+
byte transferOptions = argv[2] & SPI_TRANSFER_OPTS_MASK;
79+
byte numBytes = argv[3];
80+
81+
boolean csIsActive = false;
82+
byte csStartVal = LOW;
83+
byte csEndVal = HIGH;
84+
boolean csStartOnly = false;
85+
boolean csEndOnly = false;
86+
87+
//boolean csToggle = false;
88+
89+
if (mCsPin >= 0) {
90+
if (argv[2] & SPI_CS_ACTIVE_MASK) {
91+
csIsActive = true;
92+
if (argv[2] & SPI_CS_START_ONLY_MASK) csStartOnly = true;
93+
if (argv[2] & SPI_CS_END_ONLY_MASK) csEndOnly = true;
94+
// TODO - handle csToggle
95+
// if (argv[2] & SPI_CS_TOGGLE_MASK) csToggle = true;
96+
if (argv[2] & SPI_CS_INVERT_VAL_MASK) {
97+
csStartVal = HIGH;
98+
csStartVal = LOW;
99+
}
100+
}
101+
}
102+
103+
if ((csIsActive || csStartOnly) && !csEndOnly) {
104+
digitalWrite(mCsPin, csStartVal);
105+
}
106+
107+
if (transferOptions == SPI_READ_WRITE) {
108+
readWrite(channel, numBytes, argc, argv);
109+
} else if (transferOptions == SPI_READ_ONLY) {
110+
readOnly(channel, numBytes);
111+
} else if (transferOptions == SPI_WRITE_ONLY) {
112+
writeOnly(channel, numBytes, argc, argv);
113+
} else {
114+
// TODO - handle error
115+
Firmata.sendString("No transferOptions set");
116+
}
117+
118+
if ((csIsActive || csEndOnly) && !csStartOnly) {
119+
digitalWrite(mCsPin, csEndVal);
120+
}
121+
break; // SPI_TRANSFER
122+
}
123+
case SPI_END:
124+
SPI.end();
125+
break;
126+
} // end switch
127+
return true;
128+
}
129+
return false;
130+
}
131+
132+
void SPIFirmata::reset()
133+
{
134+
mCsPin = -1;
135+
mDeviceId = 0;
136+
}
137+
138+
void SPIFirmata::readWrite(byte channel, byte numBytes, byte argc, byte *argv)
139+
{
140+
Firmata.sendString("readWrite");
141+
byte offset = 4; // mode + channel + opts + numBytes
142+
if (numBytes * 2 != argc - offset) {
143+
// TODO - handle error
144+
Firmata.sendString("fails numBytes test");
145+
}
146+
byte buffer[numBytes];
147+
byte bufferIndex = 0;
148+
for (byte i = 0; i < numBytes * 2; i += 2) {
149+
bufferIndex = (i + 1) / 2;
150+
buffer[bufferIndex] = argv[i + offset + 1] << 7 | argv[i + offset];
151+
}
152+
SPI.transfer(buffer, numBytes);
153+
154+
reply(channel, numBytes, buffer);
155+
}
156+
157+
// TODO - eliminate duplication between readWrite and writeOnly
158+
void SPIFirmata::writeOnly(byte channel, byte numBytes, byte argc, byte *argv)
159+
{
160+
Firmata.sendString("writeOnly");
161+
byte offset = 4;
162+
if (numBytes * 2 != argc - offset) {
163+
// TODO - handle error
164+
Firmata.sendString("fails numBytes test");
165+
}
166+
167+
byte buffer[numBytes];
168+
byte bufferIndex = 0;
169+
for (byte i = 0; i < numBytes * 2; i += 2) {
170+
bufferIndex = (i + 1) / 2;
171+
buffer[bufferIndex] = argv[i + offset + 1] << 7 | argv[i + offset];
172+
}
173+
SPI.transfer(buffer, numBytes);
174+
}
175+
176+
// TODO - combine readWrite and readOnly by sending zero for each byte read
177+
// in read-only mode.
178+
// That leaves us with read and writeOnly
179+
void SPIFirmata::readOnly(byte channel, byte numBytes)
180+
{
181+
Firmata.sendString("readOnly");
182+
byte buffer[numBytes];
183+
for (byte i = 0; i < numBytes; i++) {
184+
buffer[i] = 0;
185+
}
186+
SPI.transfer(buffer, numBytes);
187+
188+
reply(channel, numBytes, buffer);
189+
}
190+
191+
void SPIFirmata::reply(byte channel, byte numBytes, byte *buffer)
192+
{
193+
Firmata.write(START_SYSEX);
194+
Firmata.write(SPI_DATA);
195+
Firmata.write(SPI_REPLY);
196+
Firmata.write(mDeviceId << 2 | channel);
197+
Firmata.write(numBytes);
198+
199+
for (byte i = 0; i < numBytes; i++) {
200+
Firmata.write(buffer[i] & 0x7F);
201+
Firmata.write(buffer[i] >> 7 & 0x7F);
202+
}
203+
204+
Firmata.write(END_SYSEX);
205+
}

utility/SPIFirmata.h

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
SPIFirmata.h
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 January 3rd, 2017
13+
*/
14+
15+
#ifndef SPIFirmata_h
16+
#define SPIFirmata_h
17+
18+
#include <Firmata.h>
19+
#include "FirmataFeature.h"
20+
#include <SPI.h>
21+
22+
#define FIRMATA_SPI_FEATURE
23+
24+
// following 2 defines belong in FirmataConstants.h, but declaring them here
25+
// since this implementation is still a prototype
26+
#define SPI_DATA 0x68
27+
#define PIN_MODE_SPI 0x0C
28+
29+
// SPI mode bytes
30+
#define SPI_CONFIG 0x00
31+
#define SPI_BEGIN_TRANSACTION 0x01
32+
#define SPI_END_TRANSACTION 0x02
33+
#define SPI_TRANSFER 0x03
34+
#define SPI_REPLY 0x04
35+
#define SPI_END 0x05
36+
37+
// transfer options
38+
#define SPI_TRANSFER_OPTS_MASK 0x03
39+
#define SPI_READ_WRITE 0x00
40+
#define SPI_READ_ONLY 0x01
41+
#define SPI_WRITE_ONLY 0x02
42+
43+
// pinOptions
44+
#define SPI_CS_ACTIVE_MASK 0x04
45+
#define SPI_CS_INVERT_VAL_MASK 0x08
46+
#define SPI_CS_START_ONLY_MASK 0x10
47+
#define SPI_CS_END_ONLY_MASK 0x20
48+
#define SPI_CS_TOGGLE_MASK 0x40
49+
50+
#define SPI_CHANNEL_MASK 0x03
51+
#define SPI_DEVICE_ID_MASK 0x7C
52+
#define SPI_BIT_ORDER_MASK 0x01
53+
54+
55+
class SPIFirmata: public FirmataFeature
56+
{
57+
public:
58+
SPIFirmata();
59+
boolean handlePinMode(byte pin, int mode);
60+
void handleCapability(byte pin);
61+
boolean handleSysex(byte command, byte argc, byte *argv);
62+
void reset();
63+
64+
private:
65+
signed char mCsPin;
66+
byte mDeviceId;
67+
68+
void readWrite(byte channel, byte numBytes, byte argc, byte *argv);
69+
void readOnly(byte channel, byte numBytes);
70+
void writeOnly(byte channel, byte numBytes, byte argc, byte *argv);
71+
void reply(byte channel, byte numBytes, byte *buffer);
72+
};
73+
74+
#endif /* SPIFirmata_h */

0 commit comments

Comments
 (0)