Skip to content

Commit 34627a5

Browse files
authored
Merge pull request #43 from aberridg/add-spi-aberridg
Add support for SPI communication - thank you @aberridg !
2 parents 46340ea + cb24b64 commit 34627a5

File tree

3 files changed

+171
-1
lines changed

3 files changed

+171
-1
lines changed

Diff for: keywords.txt

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ end KEYWORD2
5555
setI2CpollingWait KEYWORD2
5656
setI2CTransactionSize KEYWORD2
5757
getI2CTransactionSize KEYWORD2
58+
setSpiTransactionSize KEYWORD2
59+
getSpiTransactionSize KEYWORD2
5860
isConnected KEYWORD2
5961
enableDebugging KEYWORD2
6062
disableDebugging KEYWORD2
@@ -66,6 +68,7 @@ disableUBX7Fcheck KEYWORD2
6668
checkUblox KEYWORD2
6769
checkUbloxI2C KEYWORD2
6870
checkUbloxSerial KEYWORD2
71+
checkUbloxSPI KEYWORD2
6972

7073
process KEYWORD2
7174
processNMEA KEYWORD2

Diff for: src/SparkFun_u-blox_GNSS_Arduino_Library.cpp

+142
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,38 @@ boolean SFE_UBLOX_GNSS::begin(Stream &serialPort)
451451
return (connected);
452452
}
453453

454+
// Initialize for SPI
455+
boolean SFE_UBLOX_GNSS::begin(SPIClass &spiPort, uint8_t csPin, uint32_t spiSpeed)
456+
{
457+
commType = COMM_TYPE_SPI;
458+
_spiPort = &spiPort;
459+
_csPin = csPin;
460+
_spiSpeed = spiSpeed;
461+
// Initialize the chip select pin
462+
pinMode(_csPin, OUTPUT);
463+
digitalWrite(_csPin, HIGH);
464+
//New in v2.0: allocate memory for the packetCfg payload here - if required. (The user may have called setPacketCfgPayloadSize already)
465+
if (packetCfgPayloadSize == 0)
466+
setPacketCfgPayloadSize(MAX_PAYLOAD_SIZE);
467+
Serial.println("Creating buffer");
468+
createFileBuffer();
469+
boolean connected = isConnected();
470+
if (!connected)
471+
connected = isConnected();
472+
473+
if (!connected)
474+
connected = isConnected();
475+
476+
// Initialize/clear the SPI buffer - fill it with 0xFF as this is what is received from the UBLOX module if there's no data to be processed
477+
for (uint8_t i = 0; i < 20; i++)
478+
{
479+
spiBuffer[i] = 0xFF;
480+
}
481+
482+
return (connected);
483+
}
484+
485+
454486
// Allow the user to change I2C polling wait (the minimum interval between I2C data requests - to avoid pounding the bus)
455487
// i2cPollingWait defaults to 100ms and is adjusted automatically when setNavigationFrequency()
456488
// or setHNRNavigationRate() are called. But if the user is using callbacks, it might be advantageous
@@ -473,6 +505,19 @@ uint8_t SFE_UBLOX_GNSS::getI2CTransactionSize(void)
473505
return (i2cTransactionSize);
474506
}
475507

508+
//Sets the global size for the SPI buffer/transactions.
509+
//Call this before begin()!
510+
//Note: if the buffer size is too small, incoming characters may be lost if the message sent
511+
//is larger than this buffer. If too big, you may run out of SRAM on constrained architectures!
512+
void SFE_UBLOX_GNSS::setSpiTransactionSize(uint8_t transactionSize)
513+
{
514+
spiTransactionSize = transactionSize;
515+
}
516+
uint8_t SFE_UBLOX_GNSS::getSpiTransactionSize(void)
517+
{
518+
return (spiTransactionSize);
519+
}
520+
476521
//Returns true if I2C device ack's
477522
boolean SFE_UBLOX_GNSS::isConnected(uint16_t maxWait)
478523
{
@@ -598,6 +643,8 @@ boolean SFE_UBLOX_GNSS::checkUbloxInternal(ubxPacket *incomingUBX, uint8_t reque
598643
return (checkUbloxI2C(incomingUBX, requestedClass, requestedID));
599644
else if (commType == COMM_TYPE_SERIAL)
600645
return (checkUbloxSerial(incomingUBX, requestedClass, requestedID));
646+
else if (commType == COMM_TYPE_SPI)
647+
return (checkUbloxSpi(incomingUBX, requestedClass, requestedID));
601648
return false;
602649
}
603650

@@ -755,6 +802,32 @@ boolean SFE_UBLOX_GNSS::checkUbloxSerial(ubxPacket *incomingUBX, uint8_t request
755802

756803
} //end checkUbloxSerial()
757804

805+
806+
//Checks SPI for data, passing any new bytes to process()
807+
boolean SFE_UBLOX_GNSS::checkUbloxSpi(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID)
808+
{
809+
// Process the contents of the SPI buffer if not empty!
810+
for (uint8_t i = 0; i < spiBufferIndex; i++) {
811+
process(spiBuffer[i], incomingUBX, requestedClass, requestedID);
812+
}
813+
spiBufferIndex = 0;
814+
815+
SPISettings settingsA(_spiSpeed, MSBFIRST, SPI_MODE0);
816+
_spiPort->beginTransaction(settingsA);
817+
digitalWrite(_csPin, LOW);
818+
uint8_t byteReturned = _spiPort->transfer(0x0A);
819+
while (byteReturned != 0xFF || currentSentence != NONE)
820+
{
821+
process(byteReturned, incomingUBX, requestedClass, requestedID);
822+
byteReturned = _spiPort->transfer(0x0A);
823+
}
824+
digitalWrite(_csPin, HIGH);
825+
_spiPort->endTransaction();
826+
return (true);
827+
828+
} //end checkUbloxSpi()
829+
830+
758831
//PRIVATE: Check if we have storage allocated for an incoming "automatic" message
759832
boolean SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID)
760833
{
@@ -2675,6 +2748,10 @@ sfe_ublox_status_e SFE_UBLOX_GNSS::sendCommand(ubxPacket *outgoingUBX, uint16_t
26752748
{
26762749
sendSerialCommand(outgoingUBX);
26772750
}
2751+
else if (commType == COMM_TYPE_SPI)
2752+
{
2753+
sendSpiCommand(outgoingUBX);
2754+
}
26782755

26792756
if (maxWait > 0)
26802757
{
@@ -2781,6 +2858,71 @@ void SFE_UBLOX_GNSS::sendSerialCommand(ubxPacket *outgoingUBX)
27812858
_serialPort->write(outgoingUBX->checksumB);
27822859
}
27832860

2861+
2862+
// Transfer a byte to SPI. Also capture any bytes received from the UBLOX device during sending and capture them in a small buffer so that
2863+
// they can be processed later with process
2864+
void SFE_UBLOX_GNSS::spiTransfer(uint8_t byteToTransfer)
2865+
{
2866+
uint8_t returnedByte = _spiPort->transfer(byteToTransfer);
2867+
if ((spiBufferIndex < getSpiTransactionSize()) && (returnedByte != 0xFF || currentSentence != NONE))
2868+
{
2869+
spiBuffer[spiBufferIndex] = returnedByte;
2870+
spiBufferIndex++;
2871+
}
2872+
}
2873+
2874+
// Send a command via SPI
2875+
void SFE_UBLOX_GNSS::sendSpiCommand(ubxPacket *outgoingUBX)
2876+
{
2877+
if (spiBuffer == NULL) //Memory has not yet been allocated - so use new
2878+
{
2879+
spiBuffer = new uint8_t[getSpiTransactionSize()];
2880+
}
2881+
2882+
if (spiBuffer == NULL) {
2883+
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
2884+
{
2885+
_debugSerial->print(F("sendSpiCommand: memory allocation failed for SPI Buffer!"));
2886+
}
2887+
}
2888+
2889+
// Start at the beginning of the SPI buffer
2890+
spiBufferIndex = 0;
2891+
2892+
SPISettings settingsA(_spiSpeed, MSBFIRST, SPI_MODE0);
2893+
_spiPort->beginTransaction(settingsA);
2894+
digitalWrite(_csPin, LOW);
2895+
//Write header bytes
2896+
spiTransfer(UBX_SYNCH_1); //μ - oh ublox, you're funny. I will call you micro-blox from now on.
2897+
if (_printDebug) _debugSerial->printf("%x ", UBX_SYNCH_1);
2898+
spiTransfer(UBX_SYNCH_2); //b
2899+
if (_printDebug) _debugSerial->printf("%x ", UBX_SYNCH_2);
2900+
2901+
spiTransfer(outgoingUBX->cls);
2902+
if (_printDebug) _debugSerial->printf("%x ", outgoingUBX->cls);
2903+
spiTransfer(outgoingUBX->id);
2904+
if (_printDebug) _debugSerial->printf("%x ", outgoingUBX->id);
2905+
spiTransfer(outgoingUBX->len & 0xFF); //LSB
2906+
if (_printDebug) _debugSerial->printf("%x ", outgoingUBX->len & 0xFF);
2907+
spiTransfer(outgoingUBX->len >> 8);
2908+
if (_printDebug) _debugSerial->printf("%x ", outgoingUBX->len >> 8);
2909+
2910+
//Write payload.
2911+
for (uint16_t i = 0; i < outgoingUBX->len; i++)
2912+
{
2913+
spiTransfer(outgoingUBX->payload[i]);
2914+
if (_printDebug) _debugSerial->printf("%x ", outgoingUBX->payload[i]);
2915+
}
2916+
2917+
//Write checksum
2918+
spiTransfer(outgoingUBX->checksumA);
2919+
if (_printDebug) _debugSerial->printf("%x ", outgoingUBX->checksumA);
2920+
spiTransfer(outgoingUBX->checksumB);
2921+
if (_printDebug) _debugSerial->printf("%x \n", outgoingUBX->checksumB);
2922+
digitalWrite(_csPin, HIGH);
2923+
_spiPort->endTransaction();
2924+
}
2925+
27842926
//Pretty prints the current ubxPacket
27852927
void SFE_UBLOX_GNSS::printPacket(ubxPacket *packet, boolean alwaysPrintPayload)
27862928
{

Diff for: src/SparkFun_u-blox_GNSS_Arduino_Library.h

+26-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151

5252
#include <Wire.h>
5353

54+
#include <SPI.h>
55+
5456
#include "u-blox_config_keys.h"
5557
#include "u-blox_structs.h"
5658

@@ -492,6 +494,9 @@ enum sfe_ublox_ls_src_e
492494
//#define MAX_PAYLOAD_SIZE 768 //Worst case: UBX_CFG_VALSET packet with 64 keyIDs each with 64 bit values
493495
#endif
494496

497+
// For storing SPI bytes received during sendSpiCommand
498+
#define SPI_BUFFER_SIZE 128
499+
495500
//-=-=-=-=- UBX binary specific variables
496501
struct ubxPacket
497502
{
@@ -560,6 +565,8 @@ class SFE_UBLOX_GNSS
560565
boolean begin(TwoWire &wirePort = Wire, uint8_t deviceAddress = 0x42); //Returns true if module is detected
561566
//serialPort needs to be perviously initialized to correct baud rate
562567
boolean begin(Stream &serialPort); //Returns true if module is detected
568+
//SPI - supply instance of SPIClass, chip select pin and SPI speed (in Hz)
569+
boolean begin(SPIClass &spiPort, uint8_t csPin, uint32_t spiSpeed);
563570

564571
void end(void); //Stop all automatic message processing. Free all used RAM
565572

@@ -569,6 +576,11 @@ class SFE_UBLOX_GNSS
569576
void setI2CTransactionSize(uint8_t bufferSize);
570577
uint8_t getI2CTransactionSize(void);
571578

579+
//Control the size of the spi buffer. If the buffer isn't big enough, we'll start to lose bytes
580+
//That we receive if the buffer is full!
581+
void setSpiTransactionSize(uint8_t bufferSize);
582+
uint8_t getSpiTransactionSize(void);
583+
572584
//Set the max number of bytes set in a given I2C transaction
573585
uint8_t i2cTransactionSize = 32; //Default to ATmega328 limit
574586

@@ -612,6 +624,7 @@ class SFE_UBLOX_GNSS
612624

613625
boolean checkUbloxI2C(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); //Method for I2C polling of data, passing any new bytes to process()
614626
boolean checkUbloxSerial(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); //Method for serial polling of data, passing any new bytes to process()
627+
boolean checkUbloxSpi(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); //Method for spi polling of data, passing any new bytes to process()
615628

616629
// Process the incoming data
617630

@@ -622,12 +635,13 @@ class SFE_UBLOX_GNSS
622635
void processUBX(uint8_t incoming, ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); //Given a character, file it away into the uxb packet structure
623636
void processUBXpacket(ubxPacket *msg); //Once a packet has been received and validated, identify this packet's class/id and update internal flags
624637

625-
// Send I2C/Serial commands to the module
638+
// Send I2C/Serial/SPI commands to the module
626639

627640
void calcChecksum(ubxPacket *msg); //Sets the checksumA and checksumB of a given messages
628641
sfe_ublox_status_e sendCommand(ubxPacket *outgoingUBX, uint16_t maxWait = defaultMaxWait, boolean expectACKonly = false); //Given a packet and payload, send everything including CRC bytes, return true if we got a response
629642
sfe_ublox_status_e sendI2cCommand(ubxPacket *outgoingUBX, uint16_t maxWait = defaultMaxWait);
630643
void sendSerialCommand(ubxPacket *outgoingUBX);
644+
void sendSpiCommand(ubxPacket *outgoingUBX);
631645

632646
void printPacket(ubxPacket *packet, boolean alwaysPrintPayload = false); //Useful for debugging
633647

@@ -1235,6 +1249,9 @@ class SFE_UBLOX_GNSS
12351249
//Calculate how much RAM is needed to store the payload for a given automatic message
12361250
uint16_t getMaxPayloadSize(uint8_t Class, uint8_t ID);
12371251

1252+
//Do the actual transfer to SPI
1253+
void spiTransfer(uint8_t byteToTransfer);
1254+
12381255
boolean initGeofenceParams(); // Allocate RAM for currentGeofenceParams and initialize it
12391256
boolean initModuleSWVersion(); // Allocate RAM for moduleSWVersion and initialize it
12401257

@@ -1273,6 +1290,10 @@ class SFE_UBLOX_GNSS
12731290
Stream *_nmeaOutputPort = NULL; //The user can assign an output port to print NMEA sentences if they wish
12741291
Stream *_debugSerial; //The stream to send debug messages to if enabled
12751292

1293+
SPIClass *_spiPort; //The instance of SPIClass
1294+
uint8_t _csPin; //The chip select pin
1295+
int _spiSpeed; //The speed to use for SPI (Hz)
1296+
12761297
uint8_t _gpsI2Caddress = 0x42; //Default 7-bit unshifted address of the ublox 6/7/8/M8/F9 series
12771298
//This can be changed using the ublox configuration software
12781299

@@ -1292,6 +1313,10 @@ class SFE_UBLOX_GNSS
12921313
uint8_t *payloadCfg = NULL;
12931314
uint8_t *payloadAuto = NULL;
12941315

1316+
uint8_t *spiBuffer = NULL; // A buffer to store any bytes being recieved back from the device while we are sending via SPI
1317+
uint8_t spiBufferIndex = 0; // Index into the SPI buffer
1318+
uint8_t spiTransactionSize = SPI_BUFFER_SIZE; //Default size of the SPI buffer
1319+
12951320
//Init the packet structures and init them with pointers to the payloadAck, payloadCfg, payloadBuf and payloadAuto arrays
12961321
ubxPacket packetAck = {0, 0, 0, 0, 0, payloadAck, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED};
12971322
ubxPacket packetBuf = {0, 0, 0, 0, 0, payloadBuf, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED};

0 commit comments

Comments
 (0)