diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index bc45a0c..ae80176 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -451,6 +451,35 @@ boolean SFE_UBLOX_GNSS::begin(Stream &serialPort) return (connected); } +// Initialize for SPI +boolean SFE_UBLOX_GNSS::begin(SPIClass &spiPort, uint8_t ssPin, int spiSpeed) +{ + commType = COMM_TYPE_SPI; + _spiPort = &spiPort; + _ssPin = ssPin; + _spiSpeed = spiSpeed; + //New in v2.0: allocate memory for the packetCfg payload here - if required. (The user may have called setPacketCfgPayloadSize already) + if (packetCfgPayloadSize == 0) + setPacketCfgPayloadSize(MAX_PAYLOAD_SIZE); + Serial.println("Creating buffer"); + createFileBuffer(); + boolean connected = isConnected(); + if (!connected) + connected = isConnected(); + + if (!connected) + connected = isConnected(); + + // 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 + for (uint8_t i = 0; i < 20; i++) + { + spiBuffer[i] = 0xFF; + } + + return (connected); +} + + // Allow the user to change I2C polling wait (the minimum interval between I2C data requests - to avoid pounding the bus) // i2cPollingWait defaults to 100ms and is adjusted automatically when setNavigationFrequency() // or setHNRNavigationRate() are called. But if the user is using callbacks, it might be advantageous @@ -598,6 +627,8 @@ boolean SFE_UBLOX_GNSS::checkUbloxInternal(ubxPacket *incomingUBX, uint8_t reque return (checkUbloxI2C(incomingUBX, requestedClass, requestedID)); else if (commType == COMM_TYPE_SERIAL) return (checkUbloxSerial(incomingUBX, requestedClass, requestedID)); + else if (commType == COMM_TYPE_SPI) + return (checkUbloxSpi(incomingUBX, requestedClass, requestedID)); return false; } @@ -755,6 +786,42 @@ boolean SFE_UBLOX_GNSS::checkUbloxSerial(ubxPacket *incomingUBX, uint8_t request } //end checkUbloxSerial() + +//Checks SPI for data, passing any new bytes to process() +boolean SFE_UBLOX_GNSS::checkUbloxSpi(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID) +{ + // process the contents of the SPI buffer if not empty! + uint8_t bufferByte = spiBuffer[0]; + uint8_t bufferIndex = 0; + + while (bufferByte != 0xFF) { + process(bufferByte, incomingUBX, requestedClass, requestedID); + bufferIndex++; + bufferByte = spiBuffer[bufferIndex]; + } + + // reset the contents of the SPI buffer + for(uint8_t i = 0; i < bufferIndex; i++) + { + spiBuffer[i] = 0xFF; + } + + SPISettings settingsA(_spiSpeed, MSBFIRST, SPI_MODE0); + _spiPort->beginTransaction(settingsA); + digitalWrite(_ssPin, LOW); + uint8_t byteReturned = _spiPort->transfer(0x0A); + while (byteReturned != 0xFF || currentSentence != NONE) + { + process(byteReturned, incomingUBX, requestedClass, requestedID); + byteReturned = _spiPort->transfer(0x0A); + } + digitalWrite(_ssPin, HIGH); + _spiPort->endTransaction(); + return (true); + +} //end checkUbloxSpi() + + //PRIVATE: Check if we have storage allocated for an incoming "automatic" message boolean SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID) { @@ -2675,6 +2742,10 @@ sfe_ublox_status_e SFE_UBLOX_GNSS::sendCommand(ubxPacket *outgoingUBX, uint16_t { sendSerialCommand(outgoingUBX); } + else if (commType == COMM_TYPE_SPI) + { + sendSpiCommand(outgoingUBX); + } if (maxWait > 0) { @@ -2781,6 +2852,56 @@ void SFE_UBLOX_GNSS::sendSerialCommand(ubxPacket *outgoingUBX) _serialPort->write(outgoingUBX->checksumB); } + +// 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 +// they can be processed later with process +void SFE_UBLOX_GNSS::spiTransfer(uint8_t byteToTransfer) +{ + uint8_t returnedByte = _spiPort->transfer(byteToTransfer); + if (returnedByte != 0xFF) + { + spiBuffer[spiBufferIndex] = returnedByte; + spiBufferIndex++; + } +} + +// Send a command via SPI +void SFE_UBLOX_GNSS::sendSpiCommand(ubxPacket *outgoingUBX) +{ + SPISettings settingsA(_spiSpeed, MSBFIRST, SPI_MODE0); + _spiPort->beginTransaction(settingsA); + digitalWrite(_ssPin, LOW); + //Write header bytes + spiTransfer(UBX_SYNCH_1); //μ - oh ublox, you're funny. I will call you micro-blox from now on. + if (_printDebug) _debugSerial->printf("%x ", UBX_SYNCH_1); + spiTransfer(UBX_SYNCH_2); //b + if (_printDebug) _debugSerial->printf("%x ", UBX_SYNCH_2); + + spiTransfer(outgoingUBX->cls); + if (_printDebug) _debugSerial->printf("%x ", outgoingUBX->cls); + spiTransfer(outgoingUBX->id); + if (_printDebug) _debugSerial->printf("%x ", outgoingUBX->id); + spiTransfer(outgoingUBX->len & 0xFF); //LSB + if (_printDebug) _debugSerial->printf("%x ", outgoingUBX->len & 0xFF); + spiTransfer(outgoingUBX->len >> 8); + if (_printDebug) _debugSerial->printf("%x ", outgoingUBX->len >> 8); + + //Write payload. + for (uint16_t i = 0; i < outgoingUBX->len; i++) + { + spiTransfer(outgoingUBX->payload[i]); + if (_printDebug) _debugSerial->printf("%x ", outgoingUBX->payload[i]); + } + + //Write checksum + spiTransfer(outgoingUBX->checksumA); + if (_printDebug) _debugSerial->printf("%x ", outgoingUBX->checksumA); + spiTransfer(outgoingUBX->checksumB); + if (_printDebug) _debugSerial->printf("%x \n", outgoingUBX->checksumB); + digitalWrite(_ssPin, HIGH); + _spiPort->endTransaction(); +} + //Pretty prints the current ubxPacket void SFE_UBLOX_GNSS::printPacket(ubxPacket *packet, boolean alwaysPrintPayload) { diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index 42e72eb..015ba3f 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -51,6 +51,8 @@ #include +#include + #include "u-blox_config_keys.h" #include "u-blox_structs.h" @@ -560,6 +562,8 @@ public: boolean begin(TwoWire &wirePort = Wire, uint8_t deviceAddress = 0x42); //Returns true if module is detected //serialPort needs to be perviously initialized to correct baud rate boolean begin(Stream &serialPort); //Returns true if module is detected + //SPI - supply instance of SPIClass, slave select pin and SPI speed (in Hz) + boolean begin(SPIClass &spiPort, uint8_t ssPin, int spiSpeed); void end(void); //Stop all automatic message processing. Free all used RAM @@ -612,6 +616,7 @@ public: boolean checkUbloxI2C(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); //Method for I2C polling of data, passing any new bytes to process() boolean checkUbloxSerial(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); //Method for serial polling of data, passing any new bytes to process() + boolean checkUbloxSpi(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); //Method for spi polling of data, passing any new bytes to process() // Process the incoming data @@ -622,12 +627,13 @@ public: void processUBX(uint8_t incoming, ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID); //Given a character, file it away into the uxb packet structure void processUBXpacket(ubxPacket *msg); //Once a packet has been received and validated, identify this packet's class/id and update internal flags - // Send I2C/Serial commands to the module + // Send I2C/Serial/SPI commands to the module void calcChecksum(ubxPacket *msg); //Sets the checksumA and checksumB of a given messages 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 sfe_ublox_status_e sendI2cCommand(ubxPacket *outgoingUBX, uint16_t maxWait = defaultMaxWait); void sendSerialCommand(ubxPacket *outgoingUBX); + void sendSpiCommand(ubxPacket *outgoingUBX); void printPacket(ubxPacket *packet, boolean alwaysPrintPayload = false); //Useful for debugging @@ -1235,6 +1241,9 @@ private: //Calculate how much RAM is needed to store the payload for a given automatic message uint16_t getMaxPayloadSize(uint8_t Class, uint8_t ID); + //Do the actual transfer to SPI + void spiTransfer(uint8_t byteToTransfer); + boolean initGeofenceParams(); // Allocate RAM for currentGeofenceParams and initialize it boolean initModuleSWVersion(); // Allocate RAM for moduleSWVersion and initialize it @@ -1273,6 +1282,10 @@ private: Stream *_nmeaOutputPort = NULL; //The user can assign an output port to print NMEA sentences if they wish Stream *_debugSerial; //The stream to send debug messages to if enabled + SPIClass *_spiPort; //The instance of SPIClass + uint8_t _ssPin; //The slave select pin + int _spiSpeed; //The speed to use for SPI (Hz) + uint8_t _gpsI2Caddress = 0x42; //Default 7-bit unshifted address of the ublox 6/7/8/M8/F9 series //This can be changed using the ublox configuration software @@ -1292,6 +1305,9 @@ private: uint8_t *payloadCfg = NULL; uint8_t *payloadAuto = NULL; + uint8_t spiBuffer[20]; // A small buffer to store any bytes being recieved back from the device while we are sending via SPI + uint8_t spiBufferIndex = 0; // The index into the SPI buffer + //Init the packet structures and init them with pointers to the payloadAck, payloadCfg, payloadBuf and payloadAuto arrays ubxPacket packetAck = {0, 0, 0, 0, 0, payloadAck, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED}; ubxPacket packetBuf = {0, 0, 0, 0, 0, payloadBuf, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED};