Krimskrams: STM32G431 and GPS

What are you developing?
Post Reply
STM32ardui
Posts: 131
Joined: Mon May 06, 2024 1:46 pm
Answers: 1
Location: Germany

Krimskrams: STM32G431 and GPS

Post by STM32ardui »

Odds & Ends: various things of different types, usually small and not important, or of little value.
In german language "Krimskrams".


Prologue:
My final goal is to have a stable time base to check accuracy of LSE circuit.

The solution can be a GPS receiver. Each of these boards have a 1PPS-output. That's the same output, where the blinking LED is connected.

So the easiest way would be to use this signal as interrupt. A small STM32C011 counts up to 30 or 60 and then send an interrupt to the STM32-board under test.

But I want more ...



Normally a GPS receiver sends NMEA-sentences via serial port. A modern receiver like uBlox NEO-M9N uses GPS, GLOSNASS, BEIDOO and GALILEO. Depending on which sentences you activate or deactivate, it will spit out a lot of data.

So I looked around for a library, which can use I²C instead of UART and found "SparkFun u-blox GNSS". You can install it by Arduino IDE library manager. If you have a NEO-9M or NEO-10 library version 3 is the right one for you. For older uBlox devices you have to choose version 2. SparFun wrote for their own GPS receiver, but it is public domain. And judging by all those  Serial.println(F(...))  it was made for Arduino UNO etc. No information about tests with different MCUs. But it works flawless on a STM32G431! :D

Of course GPS needs free view to the sky, best would be a place with free view in each direction. I'm using a Mikroe GSSN 7 click. Even it sounds liek an older generation, it has a NEO-M9N module. The good thing is the SMA-socket on pcb. So receiver can stay inside and GPS antenna is outside. I²C lines have to short. It would be difficult to place whole GPS receiver outside. Long I²C lines may work with special line drivers.
BTW: I don't like these tiny u.FL-conncetors. You have to be careful not to damage them and they are made for only 30 connects/disconnects.

Because GPS antenna is at a fixed place, I don't want to know latitude, longitude and altitude. But you find teh methods outcommented in the sketch. I'm interested in:
- number of Satellites in view (SIV)
- dillution of precision (DOP)
If you want to more about it, please check at Wikipedia. From my experience on lot of hiking tours, I can tell you that values for horizontal DOP below 2 are good, close to 1 is excellent. Values below 1 are an effect of Multi-GNSS and a overdetermined system.

Also: you will have a fix with 3 satellites, but for acurate position you need 10 or more satellites. So forget all those marketing speech about a short cold start fix etc. Place a GPS receiver at a place with good sky view and let it "warm up" for 15 minutes!

Another hint: my GPS board has pin headers. Don't put the receiver into a breadboard. At EEV-Blog I read, that capacity between rows is only 2 pF. But 1PPS-line showed a awful jitter of 600 ns. May be TX-signal on next row influence signal? In datasheet you can see, that jitter should be only ±60 ns.

And for last: you can get GPS receivers for small money from chinese dealers. The chip may act like a uBlox. But when you want to upload a new firmware or make some settings, it may fail because it is a fake chip.

uBloxNEO9M deviationmap.jpg
uBloxNEO9M deviationmap.jpg (61.79 KiB) Viewed 2292 times
Deviation Map: left for the first 15 minutes, right 3 hours
It shows calculated positions in a latitude over longitude map. If GPS receiver sends exact same position all the time, you would see only a dot. So this kind of graphic is a visualisation of the position error.

For this first try I connected 3.3 V, GND, SCL and SDA from STM32G431 board to the GPS board with jumper cables.

Code: Select all

//-----------------------------------------------------------------------------
// Board: STM32G431CBU6 (WeactStudio "long type")
// https://github.com/stm32duino/Arduino_Core_STM32/blob/main/variants/STM32G4xx/G431C(6-8-B)U_G441CBU/PeripheralPins.c
// https://github.com/stm32duino/Arduino_Core_STM32/blob/main/variants/STM32G4xx/G431C(6-8-B)U_G441CBU/variant_generic.h
// DAC:   PA4, PA5
// I2C:   PA8 (SDA), PA9 (SCL)
// UART1: PA3 (RX), PA2 (TX)  
// LED:   PC6
// 
// https://github.com/sparkfun/SparkFun_u-blox_GNSS_v3/tree/main (only uBlox M9/M10)
// code from Example4_CustomI2C.ino (V3) and Example15_GetDateTime.ino (V2)
// Example1_AutoPVT.ino genauere Prüfung auf valide Werte
//
// https://en.wikipedia.org/wiki/Dilution_of_precision_(navigation)
// - HDOP 	Horizontal DOP  
// - PDOP         3D horizontal and vertical
// - GDOP 	Geometric DOP (3D and time)
//
// The fix type is as follows:
//    0 = no fix
//    1 = dead reckoning (requires external sensors)
//    2 = 2D (not quite enough satellites in view)
//    3 = 3D (the standard fix)
//    4 = GNSS + dead reckoning (requires external sensors)
//    5 = Time fix only

//-----------------------------------------------------------------------------

#include <Wire.h>                           // Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_v3.h>        // SparkFun u-blox GNSS v3@3.1.5

SFE_UBLOX_GNSS myGNSS;

#define myWire      Wire    // Connect using the Wire (not Wire1) port. Change this if required
#define gnssAddress 0x42    // The default I2C address for u-blox modules is 0x42. Change this if required

void setup() {
  Serial.begin(115200);
  delay(2000); 
  Serial.printf("\n\n ----- u-blox NEO-9M (Time, Satellites and DOP) ----- \n");

  myWire.begin(); // Start I2C
  //myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial

  while (myGNSS.begin(myWire, gnssAddress) == false) 
  {
    Serial.println("u-blox GNSS not detected. Retrying...");
    delay (1000);
  }

  myGNSS.setI2COutput(COM_TYPE_UBX);   // Set the I2C port to output UBX only (turn off NMEA noise)
  //myGNSS.setNavigationFrequency(2);  // Produce two solutions per second --- seems not to work
  myGNSS.setAutoPVT(true);             // Tell the GNSS to "send" each solution
  //myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Optional: save (only) the communications port settings to flash and BBR
}


void loop() {
  uint8_t   fix;        // Fix-Type
  uint8_t   siv;        // satellites in view (satellites in use would be better ...)
  uint16_t  gdop;
  uint16_t  hdop;
  bool      dop;
  int32_t   lat;
  int32_t   lon;
  int32_t   alt;
  uint16_t  year;
  uint8_t   mon;
  uint8_t   day;
  uint8_t   hour;
  uint8_t   min;
  uint8_t   sec;
  uint16_t  ms;
   
  // Request (poll) the position, velocity and time (PVT) information.
  // The module only responds when a new position is available. Default is once per second.
  // getPVT() returns true when new data is received.
  if (myGNSS.getPVT() == true)   
  {
    //lat = myGNSS.getLatitude();
    //lon = myGNSS.getLongitude();
    //alt = myGNSS.getAltitudeMSL(); 
    
    year = myGNSS.getYear();
    mon  = myGNSS.getMonth();
    day  = myGNSS.getDay();
    hour = myGNSS.getHour();
    min  = myGNSS.getMinute();
    sec  = myGNSS.getSecond();
    ms   = myGNSS.getMillisecond();

    siv = myGNSS.getSIV();
    dop = myGNSS.getDOP();
    gdop = myGNSS.getGeometricDOP();
    hdop = myGNSS.getHorizontalDOP();
    fix  = myGNSS.getFixType();

    //Serial.printf("Lat = %d  Lon = %d Alt = %d \n", lat,lon, alt);
    Serial.printf("  %02d.%02d.%04d  %02d:%02d:%02d.%03d UTC   ",day,mon,year,hour,min,sec,ms);
    Serial.printf("SIV = %d FIX = %d HDOP = %01d.%02d GDOP = %01d.%02d \n",siv, fix, hdop/100, hdop%100, gdop/100, gdop%100);
   }
}

And here some output:

Code: Select all

11:49:23.421 ->  ----- u-blox NEO-9M (Time, Satellites and DOP) ----- 
11:49:24.802 ->   09.07.2024  09:49:24.500 UTC   SIV = 21 FIX = 3 HDOP = 0.77 GDOP = 1.46 
11:49:25.825 ->   09.07.2024  09:49:25.500 UTC   SIV = 21 FIX = 3 HDOP = 0.77 GDOP = 1.46 
11:49:26.870 ->   09.07.2024  09:49:26.500 UTC   SIV = 22 FIX = 3 HDOP = 0.77 GDOP = 1.46 
11:49:27.909 ->   09.07.2024  09:49:27.500 UTC   SIV = 21 FIX = 3 HDOP = 0.77 GDOP = 1.46 
11:49:28.817 ->   09.07.2024  09:49:28.500 UTC   SIV = 21 FIX = 3 HDOP = 0.77 GDOP = 1.46 
11:49:29.845 ->   09.07.2024  09:49:29.500 UTC   SIV = 21 FIX = 3 HDOP = 0.77 GDOP = 1.46
12:01:17.845 ->   09.07.2024  10:01:17.500 UTC   SIV = 28 FIX = 3 HDOP = 0.53 GDOP = 1.02   
GPS don't care about timezones, so time is always UTC.


[Update]
Somewhere in the library is a

Code: Select all

#define kUBLOXGNSSDefaultMaxWait 1100
The effect is, that values comes in an intervall of 2 seconds.
An active

Code: Select all

myGNSS.setNavigationFrequency(2);
compensates it, so I got values each second.

Is redefining of kUBLOXGNSSDefaultMaxWait to 300 ms a solution? No, one time it works, after the next power up anymore. So there is something, I don't really understand until now ...
STM32ardui
Posts: 131
Joined: Mon May 06, 2024 1:46 pm
Answers: 1
Location: Germany

Krimskrams: STM32G431 and GPS(2)

Post by STM32ardui »

Here is the second version.

The rising edge of 1PPS-signal of GPS receiver causes an interrupt. Every minute - after 60 interrupts - the STM32G431-board should trigger the device under test to check its RTC-time. In addition timestamp and information about GPS signal quality should be indicate on a LCD display with SPI interface.

BTW: GPS antenna is an ANTDOM-05-01-WPM from Inventek Systems. It's placed outside 2m away from building. So reception is good from east to west, not so good to north. Yesterday there was heavy rain. So strong, that TV program via satellite was freezing. GDOP values stays below 1.5 during that time.

Timing problems
I'm afraid, I don't really understand GPS libray from SparkFun. Normally one round through loop() will last about 125 ms. After adding conditional statement for count == 60 one round takes 600 ms when if-condition is false and 500 ms when if-condition is true (at that moment no instructions for display were inside block). Other variations let jump lap time to 1100 ms after the first time program flows goes into if-block. Or GPS receiver only delivers values every 2 seconds.

OK I made a mistake: my alarm clock (DCF77) jumps to the next second at teh same moment, when PPS-LED on GPS receiver went off. But there is a transistor to switch the LED. So I have to set interrupt to rising edge, not to falling.

Code: Select all

//-----------------------------------------------------------------------------
// Board: STM32G431CBU6 (WeactStudio "long type")
// https://github.com/stm32duino/Arduino_Core_STM32/blob/main/variants/STM32G4xx/G431C(6-8-B)U_G441CBU/PeripheralPins.c
// https://github.com/stm32duino/Arduino_Core_STM32/blob/main/variants/STM32G4xx/G431C(6-8-B)U_G441CBU/variant_generic.h
// DAC:   PA4, PA5
// I2C:   PA8 (SDA), PA9 (SCL)
// SPI:   PA7 (MOSI), PA6 (MISO), PA5 (CLK), PA4 (CS)
// UART1: PA3 (RX), PA2 (TX)  
// LED:   PC6
// 
// https://github.com/sparkfun/SparkFun_u-blox_GNSS_v3/tree/main (only uBlox M9/M10)
//
// https://en.wikipedia.org/wiki/Dilution_of_precision_(navigation)
// - HDOP 	Horizontal DOP  
// - PDOP         3D horizontal and vertical
// - GDOP 	Geometric DOP (3D and time)
//
// The fix type is as follows:
//    0 = no fix
//    1 = dead reckoning (requires external sensors)
//    2 = 2D (not quite enough satellites in view)
//    3 = 3D (the standard fix)
//    4 = GNSS + dead reckoning (requires external sensors)
//    5 = Time fix only
//
// Display: "Tzt LCD19264" 192*64Pixel  3,3V  UC1609C
//-----------------------------------------------------------------------------

#include <Wire.h>                                        // Needed for I2C to GNSS
#include <SparkFun_u-blox_GNSS_v3.h>        // SparkFun u-blox GNSS v3@3.1.5
#include <U8g2lib.h>

#define myWire         Wire     // Connect using the Wire (not Wire1) port. Change this if required
#define gnssAddress   0x42    // The default I2C address for u-blox modules is 0x42. Change this if required
#define int_pin          PC4     // Interrupt-pin for 1PPS-signal
#define led               PC6      // bultin-LED
#define intervall       60        // intervall for triggering another STM32-board
#define kUBLOXGNSSDefaultMaxWait  250  // original value 1100ms is too long?

#define LCD_CS        PA10
#define LCD_DC        PA11
#define LCD_RES       PA12

U8G2_UC1609_SLG19264_F_4W_HW_SPI u8g2(U8G2_R0,  LCD_CS, LCD_DC, LCD_RES);
SFE_UBLOX_GNSS myGNSS;


volatile bool       ppsFlag = false;
volatile int8_t     count   = 50;
uint32_t      a,b;
uint8_t   fix;        // Fix-Type
uint8_t   siv;        // satellites in view (satellites in use would be better ...)
uint16_t  gdop;
uint16_t  hdop;
bool      dop;
uint16_t  year;
uint8_t   mon;
uint8_t   day;
uint8_t   hour;
uint8_t   minu;
uint8_t   sec;

// Interrupt routine
void ppsISR(){
  ppsFlag = true; 
  count++;
  //digitalWrite(led, LOW);
}

void setup() {
  pinMode(led, OUTPUT);         // builtin-LED will bve ON during setup()
  digitalWrite(led, HIGH);      // and flash in loop() every minute
  
  Serial.begin(115200);
  delay(2000); 
  Serial.printf("\n\n ----- u-blox NEO-9M (Time, Satellites and DOP) ----- \n");

  myWire.begin(); // Start I2C
  while (myGNSS.begin(myWire, gnssAddress) == false) 
  {
    digitalWrite(led, LOW);
    Serial.println("u-blox GNSS not detected. Retrying...");
    delay(500);
    digitalWrite(led, HIGH);
    delay(500);
  }
  
  myGNSS.setI2COutput(COM_TYPE_UBX);      // Set the I2C port to output UBX only (turn off NMEA noise)
  myGNSS.setNavigationFrequency(2);           // Produce two solutions per second --- seems to work
  myGNSS.setAutoPVT(true);                        // Tell the GNSS to "send" each solution
  delay(1000);                                              // sometimes GPS receiver is not ready?

  u8g2.begin();
  u8g2.clearBuffer();					          

  // if GPS is not ready, second may be larger than 100,
  // so I check the range
  sec  = myGNSS.getSecond();
  if (sec < 60 ) count = sec -1;
 
  digitalWrite(led, LOW);
  pinMode(int_pin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(int_pin), ppsISR, RISING);
}


void loop() {
  if (ppsFlag) {                    // loop takes 125 ms or 590 ms with trigger pulse/display update
    a = millis();
    if (count == 60) {
      // LED-pin used also as trigger for device under test (rising edge)
      // so LED flashes short, 50 ms is long enough to recognise
      digitalWrite(led, HIGH);
      delay(50);
      digitalWrite(led, LOW);
      
      u8g2.clearBuffer();	
      u8g2.setFont(u8g2_font_ncenB10_tr);
      u8g2.setCursor(5,15);
      u8g2.printf("%02d.%02d.%04d  %02d:%02d UTC",day,mon,year,hour,minu);	
      u8g2.setFont(u8g2_font_ncenB14_tr);
      u8g2.setCursor(5,36);
      u8g2.printf("%d SAT  %dD Fix",siv, fix);  
      u8g2.setCursor(5,56);
      u8g2.printf("GDOP : %01d.%02d", gdop/100, gdop%100);
      u8g2.sendBuffer();
      count = 0;
    } 
    ppsFlag = false;
    
    if (!myGNSS.getInvalidLlh())    
      {
      // receiving datetime values
      // millisecodns are useless, I only observed 0 or 500 ms 
      year = myGNSS.getYear();
      mon  = myGNSS.getMonth();
      day  = myGNSS.getDay();
      hour = myGNSS.getHour();
      minu = myGNSS.getMinute();
      sec  = myGNSS.getSecond();

      siv = myGNSS.getSIV();
      gdop = myGNSS.getGeometricDOP();
      fix  = myGNSS.getFixType();

      Serial.printf("  %02d.%02d.%04d  %02d:%02d:%02d UTC   ",day,mon,year,hour,minu,sec);
      Serial.printf("SIV = %d FIX = %d  GDOP = %01d.%02d  ",siv, fix,  gdop/100, gdop%100);
    }    
     b = millis();
    Serial.printf(" t = %d ms\n",b-a); 
    //count++;
  }
}
Sketch uses 71928 bytes (54%) of program storage space. Maximum is 131072 bytes.
Global variables use 4216 bytes (12%) of dynamic memory, leaving 28552 bytes for local variables. Maximum is 32768 bytes.


I want synchronise counting of the seconds with the seconds from GPS time. Of course it can take nearly a minute after power-on, until display shows something. Triggering of DUT is at the beginning to add no jitter. At this moment timestamp is one second old. So I set count to sec -1.

One rule about interrupt routines: make them short!
So ppsISR() only sets a flag and count up seconds. All the rest is done at the beginning of loop() by checking the flag.

GPSReceiver+STM32.jpg
GPSReceiver+STM32.jpg (64.18 KiB) Viewed 2217 times

GPS-Signal.jpg
GPS-Signal.jpg (49.17 KiB) Viewed 2217 times
GPS-Signal-Delay.jpg
GPS-Signal-Delay.jpg (47.1 KiB) Viewed 2217 times
Screenshot 1 show 1PPS-signal (yellow) and the trigger-pulse (cyan). Screenshot 2 is made in singleshot-mode with higher timebase. Delay is about 3 µs.
Post Reply

Return to “Projects”