SPI communication between GPS and microcontroller

Hey everyone. I'm working with a GPS module u-blox 6 neo6, M10382 connected via SPI to an Arduino-like board. To be exact it's a somewhat customized board with a pic32, but the coding and everything is very similar to arduino.

I'm having trouble getting the GPS to spit out anything intelligible.

I have the four SPI lines hooked to GPIO pins. (I've also used a very similar approach using a gyroscope, and I was able to read the data off of it through SPI just fine using similar methods)

My code is as follows (and is explained afterward):

#include <Wire.h>
#include <plib.h>  
#include <Math.h>
#include <p32xxxx.h> 

#define RB05_GPSmosi 21
#define RB04_GPScs   15
#define RB03_GPSsclk 20
#define RB02_GPSmiso 14

// data buffer
uint8_t buf[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char gpsbuf[80];

void setup() {
  Serial.begin(9600);

  // setup GPS
  pinMode( RB05_GPSmosi, OUTPUT );
  pinMode( RB02_GPSmiso, INPUT );
  pinMode( RB03_GPSsclk, OUTPUT );
  pinMode( RB04_GPScs  , OUTPUT );
  digitalWrite( RB04_GPScs, 1 );
  digitalWrite( RB03_GPSsclk, 0 );
}

void loop() {
  gpsRead();
  Serial.println();
  
  delay(50);
}


void gpsRead() {
  gpsbuf[0] = 0;
  
  //Select GPS
  digitalWrite(RB04_GPScs, LOW);
  
  //Write 0xFF while reading the MISO line and store each char in gpsbuf
  int i = 0, j =0;
  for(j = 0; j < 80; j++) {
    for(i = 0; i < 8; i++) {
      digitalWrite(RB05_GPSmosi, ((0xFF >> (7-i)) & 0x01));
      digitalWrite(RB03_GPSsclk, 1);
      delay(1);
      gpsbuf[j] |= digitalRead(RB02_GPSmiso) << (7-i);
      digitalWrite(RB03_GPSsclk, 0);
    }
    //Clean gpsbuf while reading
    if(j != 79)
      gpsbuf[j+1] = 0;
    Serial.print(gpsbuf[j], HEX);
  }
  
  //Deselect GPS
  digitalWrite(RB04_GPScs, HIGH);
}

So basically, I select the GPS by setting the cs low, I then go through sending 0xFF while reading the incoming bits and storing them in a buffer. The datasheet says that if you have nothing to send, then send 0xFF and after 50 sends of 0xFF it will stop parsing the incoming data. The data that I read and print out to the Serial port however just ends up being 0xFF as well. I've been looking through several data sheets and can't really find much about how to access the data on the GPS.

I'm used to just grabbing data from registers, but it seems here that the data should come back to me in a string format, and I should be able to parse that and get the data I need, but I just keep getting all 1's back (0xFF).

The data sheet I'm looking at states:

4.4 SPI Port
A Serial Peripheral Interface (SPI) bus is available with selected receivers. See our online product selector matrix
for availability.
SPI is a four-wire synchronous communication interface. In contrast to UART, the master provides the clock
signal, which therefore doesn't need to be specified for the slave in advance. Moreover, a baud rate setting is
not applicable for the slave. SPI modes 0-3 are implemented and can be configured using the field mode.
spiMode in CFG-PRT for SPI (default is SPI mode 0).
The SPI clock speed is limited depending on hardware and firmware versions!

Maximum SPI clock speed
Generation Firmware Max SPI speed
u-blox 6 7 200 kHz
u-blox 6 6.02 100 kHz
u-blox 5 all 25 kHz

4.4.1 Read Access
As the register mode is not implemented for the SPI port, only the UBX/NMEA message stream is provided. This
stream is accessed using the Back-To-Back Read and Write Access (see section Back-To-Back Read and Write
Access). When no data is available to be written to the receiver, MOSI should be held logic high, i.e. all bytes
written to the receiver are set to 0xFF.
To prevent the receiver from being busy parsing incoming data, the parsing process is stopped after 50
subsequent bytes containing 0xFF. The parsing process is re-enabled with the first byte not equal to 0xFF. The
number of bytes to wait for deactivation (50 by default) can be adjusted using the field mode.ffCnt in
CFG-PRT for SPI, which is only necessary when messages shall be sent containing a large number of subsequent
0xFF bytes.
If the receiver has no more data to send, it sets MISO to logic high, i.e. all bytes transmitted decode to 0xFF. An
efficient parser in the host will ignore all 0xFF bytes which are not part of a message and will resume data
processing as soon as the first byte not equal to 0xFF is received.
GPS.G6-SW-10018-F Public Release Page 12 of 210

4.4.2 Back-To-Back Read and Write Access
The receiver does not provide any write access except for writing UBX and NMEA messages to the receiver,
such as configuration or aiding data. For every byte written to the receiver, a byte will simultaneous be read
from the receiver. While the master writes to MOSI, at the same time it needs to read from MISO, as any
pending data will be output by the receiver with this access. The data on MISO represents the results from a
current address read, returning 0xFF when no more data is available.
SPI Back-To-Back Read/Write Access

So I don't really want to write anything to the GPS, I just want to read from it, which is why I send it a bunch of 0xFF's. But it seems like I need to set an enable bit somewhere or something so that it knows to send me back the string of data that I'm looking for (latitude, longitude, and stuff like that). I'm just having trouble figuring out how to go about getting that string. The parsing shouldn't be an issue once I get the string.

The datasheets I'm looking at are linked here:
http://www.u-blox.com/images/downloads/Product_Docs/u-blox6_ReceiverDescriptionProtocolSpec_(GPS.G6-SW-10018).pdf

http://www.u-blox.com/images/downloads/Product_Docs/NEO-6_DataSheet_(GPS.G6-HW-09005).pdf

http://www.u-blox.com/images/downloads/Product_Docs/NEO-6_ProductSummary_(GPS.G6-HW-09003).pdf

Here's some SPI GPS discussion WRT using a library I wrote.

Cheers,
/dev

Hey /dev,

I took a look at that topic. I tried to use your library, but since I'm using something similar to a chipKit, I got some errors and I don't really know what they mean. Also, could I even use your library effectively since I'm using GPIO pins to read from and write to the GPS? I'm pretty sure I can't use the SPI library since I'm doing it this way... I'm really at a loss here when it comes to this stuff :confused:

On another note, I edited my gpsRead() function, sending it something other than 0xFF for the first few bytes in an attempt to access the CFG-PRT defined here (pic attached is much more helpful (Attachments are the same, I added it twice and couldn't remove it, sorry)):

31.16 CFG-PRT (0x06 0x00)
31.16.1 Polls the configuration of the used I/O Port
Message CFG-PRT
Description Polls the configuration of the used I/O Port
Firmware Supported on u-blox 6 from firmware version 6.00 up to version 7.03.
Type Poll Request
Comment Polls the configuration of the I/O Port on which this message is received
Header ID Length (Bytes) Payload Checksum
Message Structure 0xB5 0x62 0x06 0x00 0 see below CK_A CK_B
No payload

So in my gpsRead() function I did the following:

void gpsRead() {
  gpsReadBuf[0] = 0;
  
  //Select GPS
  digitalWrite(RB04_GPScs, LOW);
  
  //Write 0xFF while reading the MISO line and store each char in gpsbuf
  int i = 0, j =0;
  for(j = 0; j < 80; j++) {
    for(i = 0; i < 8; i++) {
      if(j == 0)
        digitalWrite(RB05_GPSmosi, ((0xB5 >> (7-1)) & 0x01));
      else if(j==1)
        digitalWrite(RB05_GPSmosi, ((0x62 >> (7-1)) & 0x01));
      else if(j==2)
        digitalWrite(RB05_GPSmosi, ((0x06 >> (7-1)) & 0x01));
      else if(j==3)
        digitalWrite(RB05_GPSmosi, ((0x00 >> (7-1)) & 0x01));
      else if(j==4)
        digitalWrite(RB05_GPSmosi, ((0x00 >> (7-1)) & 0x01));
      else if(j==5)
        digitalWrite(RB05_GPSmosi, ((0x00 >> (7-1)) & 0x01));
      else
        digitalWrite(RB05_GPSmosi, ((0xFF >> (7-i)) & 0x01));
      digitalWrite(RB03_GPSsclk, 1);
      delay(1);
      gpsReadBuf[j] |= digitalRead(RB02_GPSmiso) << (7-i);
      digitalWrite(RB03_GPSsclk, 0);
    }
    //Clean gpsbuf while reading
    if(j != 79)
      gpsReadBuf[j+1] = 0;
    Serial.print(gpsReadBuf[j]);
  }
  
  //Deselect GPS
  digitalWrite(RB04_GPScs, HIGH);
}

So basically, the first 6 bytes I send are 0xB5, 0x62, 0x06, 0x00, 0x00, and 0x00.
0xB5 and 0x62 for the header, 0x06 and 0x00 for the ID, 0x00 for the length, and 0x00 for the payload. I don't really know what to do about checksum, so after this point I just started sending 0xFF for everything after.

When reading the serial port I get something like this:

$GPVTG,,,,,,,,,N30
$GPGGA,,,,,,0,00,99.99,,,,,,

48
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.9930
$GPRMC,,V,,,,,,,,,,N
53
$GPVTG
,,,,,,,,,N30
$GPGGA,,,,,,0,00,99.99,,,,,,48
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.
99,99.99
30
$GPRMC,,V,,,,,,,,,,N
53
$GPVTG,,,,,,,,,N30
$GPGGA,,,,,,0,00,99.9
9,,,,,,48
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99
30
$GPRMC,,V,,,,,,,,,,N
53

$GPVTG,,,,,,,,,N*30
$GPGGA,,,,,,0,00,99.99,,,,,,48
$GPRMC,,V,,,,,,,,,,N
53

This is exciting because it actually looks like something. But basically it doesn't have any data.
And I don't really know what to do with this or where to go from here..

This was also in the datasheet:

If Time is unknown (e.g. during a cold-start):
$GPGLL,,,,,,V,N*64

So I'm getting a checksum at the end and something else. But everything concerning actual data is unknown.

As you can probably tell, I'm flying by the seat of my pants and have no real idea as to how this all works. Any information would be greatly appreciated!

(Attachments are the same, I added it twice and couldn't remove it, sorry)

Well, I saw that you're using PIC, not Arduino. Nobody else responded, so I thought you might be able to use the basic approach I suggested for an SPI GPS device. I do not think my Arduino library is of any use to you! And I'm not sure you can get a lot of PIC help on an Arduino forum. :frowning: Maybe someone else can suggest a PIC forum?

I would start by searching for a PIC library for SPI (a "bit-bang" version like you're trying). 'Round these parts we would call the library something like "SoftSPI", as in a "software" SPI interface, not a "hardware" interface that's part of the MCU. Try this... the top results look promising.

I've come across PIC code examples, too, while looking at GPS stuff. Once you've got some SPI routines that can read and write, then the post I referred you to may make more sense.

Cheers,
/dev

You're right, I probably shouldn't be asking about PIC stuff on an Arduino forum, haha. Arduino is just what I'm used to and felt the stuff I'm using was similar enough to ask about here. I will look for a PIC forum and do some more digging on the stuff you've linked me to.

I greatly appreciate the help : )

I guess my main problem isn't so much with the communication now as it is with the actual GPS itself. I just don't know what exactly to send it to get the information that I want from it. My SPI communication seems to be working, it's just what exactly I need to send to receive what I want.

So I will do some more digging and maybe ask somewhere else about my GPS specifically, because the data sheet is just confusing me more and more...

Again, thanks so much for your help!

#include <SPI.h>

// pins used for the connection with the sensor
// the other you need are controlled by the SPI library):
const int chipSelectPin = D10;
void setup() {
Serial.begin(115200);
Serial.println("SPI");
// start the SPI library:
SPI.begin();
// initalize the data ready and chip select pins:
pinMode(chipSelectPin, OUTPUT);
}

unsigned int car;

void loop() {
car = readRegister();
if(!(car == 255))
{
Serial.print(car, HEX);
Serial.print(" ");
}
else
delay(1);
}

unsigned int readRegister() {
unsigned int result = 0; // result to return
// take the chip select low to select the device:
digitalWrite(chipSelectPin, LOW);
// send the device the register you want to read:
result = SPI.transfer(0x00);
// take the chip select high to de-select:
digitalWrite(chipSelectPin, HIGH);
// return the result:
return (result);
}

void writeRegister() {
// take the chip select low to select the device:
digitalWrite(chipSelectPin, LOW);

SPI.transfer(0x55); //Send register location

// take the chip select high to de-select:
digitalWrite(chipSelectPin, HIGH);
}

Reply #2 - In case you didn't know, the printout from the serial port you showed contained NMEA sentences. You can find out what they mean here.

The GPGSA sentence is indicating that the GPS receiver isn't picking up any satellites (SVs).

I'm guessing that you are indoors somewhere.

Oh, and if this type of GPS Rx is anything like the military ones I've used in the past, then it may well need a cold start to get it going. Usually we gave our GPS Rx a clue by telling it the rough location of where it was using lat/long coordinates. That way it had a fighting chance of listening for the right SVs. It sometimes took a while (several minutes if I recall correctly) before the cold start completed and the GPS locked onto the SVs and started presenting information.

If basic4ever has a question then the forum needs to be told what it is.

Best to raise your own topic rather than attach to a 5 year dead thread.