Unfortunately, not. Since there is now one more thread (the interrupt task) running in parallel with the main one some sort of synchronisation is needed.
One way how to synchronize threads is to use event flags. Because there exist 19 various GPS messages you can set up 19 event flags. For example:
#define GPBOD (1UL << 1)
#define GPBWC (1UL << 2)
#define GPGGA (1UL << 3)
#define GPGLL (1UL << 4)
#define GPGSA (1UL << 5)
#define GPGSV (1UL << 6)
#define GPHDT (1UL << 7)
#define GPR00 (1UL << 8)
#define GPRMA (1UL << 9)
#define GPRMB (1UL << 10)
#define GPRMC (1UL << 11)
#define GPRTE (1UL << 12)
#define GPTRF (1UL << 13)
#define GPSTN (1UL << 14)
#define GPVBW (1UL << 15)
#define GPVTG (1UL << 16)
#define GPWPL (1UL << 17)
#define GPXTE (1UL << 18)
#define GPZDA (1UL << 19)
This will allow the interrupt thread (task) to signal, by setting the corresponding event flag, to the main thread that a certain GPS message has been received.
The main thread can wait in the while(true)
loop for the selected event/message. For that purpose you can design a suitable GPS function. For example as below:
bool GPS::waitFor(uint32_t flag, uint32_t millisec)
{
return eventFlags.wait_all(flag, millisec);
}
Such function can wait in the main thread forever while letting the interrupt task to run in parrallel without blocking it. You can also set a maximum wait time in case you’d like to perform more tasks in the main thread.
The main thread and the interrupt task share some variables (i.e. resources). To prevent data from being scrambled, access to those variables has to be protected by a mutex (mutual exclusion). For example:
GPS.cpp
void GPS::updateCheck()
{
...
if (updates & GPGGA) {
mutex.lock();
sscanf
(
gpgga,
",%f,%f,%c,%f,%c,%d,%d,%f,%f,%c,%f",
&time,
&latitude,
&ns,
&longitude,
&ew,
&lock,
&sats,
&hdop,
&alt,
&unit,
&geoid
);
int_time = static_cast<int>(time);
mutex.unlock();
//printf("GPGGA ");
eventFlags.set(GPGGA);
}
...
}
float GPS::getLat()
{
float result;
mutex.lock();
result = latitude;
mutex.unlock();
return result;
}
This is necessary because it could happen that the GPS::updateCheck()
function is called (writes data to the latitude
variable) in the interruptTask
at the moment when the main task is reading the latitude
variable by executing the GPS::getLat()
function.
For the sake of completeness, find below the code which compiled fine for the LPC1768 target. However, it has not been tested on real hardware:
GPS.h
#include "mbed.h"
#ifndef GPS_H
#define GPS_H
#define GPBOD (1UL << 1)
#define GPBWC (1UL << 2)
#define GPGGA (1UL << 3)
#define GPGLL (1UL << 4)
#define GPGSA (1UL << 5)
#define GPGSV (1UL << 6)
#define GPHDT (1UL << 7)
#define GPR00 (1UL << 8)
#define GPRMA (1UL << 9)
#define GPRMB (1UL << 10)
#define GPRMC (1UL << 11)
#define GPRTE (1UL << 12)
#define GPTRF (1UL << 13)
#define GPSTN (1UL << 14)
#define GPVBW (1UL << 15)
#define GPVTG (1UL << 16)
#define GPWPL (1UL << 17)
#define GPXTE (1UL << 18)
#define GPZDA (1UL << 19)
//
#define RX_MAXTIME 2000 // Maximum time gap in milliseconds between two GPS messages
#define PACKET_SIZE 80
class GPS :
public UnbufferedSerial
{
public:
GPS(PinName serialTX, PinName serialRX, int baud = 9600);
bool waitFor(uint32_t flag, uint32_t millisec = osWaitForever);
int getTime();
float getLat();
float getLong();
int getSats();
float getHDOP();
float getSpeed();
private:
int gpsRxLen;
int updates; //the 19 message types to be masked
bool uart_error = false;
int msg_type;
char gpbod[PACKET_SIZE];
char gpbwc[PACKET_SIZE];
char gpgga[PACKET_SIZE];
char gpgll[PACKET_SIZE];
char gpgsa[PACKET_SIZE];
char gpgsv[PACKET_SIZE];
char gphdt[PACKET_SIZE];
char gpr00[PACKET_SIZE];
char gprma[PACKET_SIZE];
char gprmb[PACKET_SIZE];
char gprmc[PACKET_SIZE];
char gprte[PACKET_SIZE];
char gptrf[PACKET_SIZE];
char gpstn[PACKET_SIZE];
char gpvbw[PACKET_SIZE];
char gpvtg[PACKET_SIZE];
char gpwpl[PACKET_SIZE];
char gpxte[PACKET_SIZE];
char gpzda[PACKET_SIZE];
float time, latitude, longitude, hdop, vdop, pdop, alt, geoid;
float track, mag, speedN, speedM;
char mode, T, M, N, K;
char ns, ew, unit;
int lock, sats, checksum;
int int_time;
Thread interruptThread;
EventFlags interruptFlags;
EventFlags eventFlags;
Mutex mutex;
void interruptHandler();
void interruptTask();
void readData();
void updateCheck();
};
#endif
GPS.cpp
#include "GPS.h"
#define GPS_SERIAL_RX_INTERRUPT_FLAG (1UL << 0)
/**
* @brief
* @note
* @param
* @retval
*/
GPS::GPS(PinName serialTX, PinName serialRX, int baud /*= 9600*/ ) :
UnbufferedSerial(serialTX, serialRX, baud),
interruptThread(osPriorityNormal)
{
interruptThread.start(callback(this, &GPS::interruptTask));
attach(callback(this, &GPS::interruptHandler));
}
/**
* @brief
* @note
* @param
* @retval
*/
void GPS::interruptHandler()
{
attach(nullptr);
interruptFlags.set(GPS_SERIAL_RX_INTERRUPT_FLAG);
}
/**
* @brief
* @note
* @param
* @retval
*/
void GPS::interruptTask()
{
while (true) {
interruptFlags.wait_all(GPS_SERIAL_RX_INTERRUPT_FLAG);
readData();
}
}
/**
* @brief
* @note
* @param
* @retval
*/
void GPS::readData()
{
char gps_byte;
static int step;
static int array_title;
read(&gps_byte, 1);
attach(callback(this, &GPS::interruptHandler));
// '$' begin-packet symbol
if (gps_byte == '$') {
updateCheck();
step = 0;
gpsRxLen = 0;
updates = 0;
}
switch (step) {
case 0:
step++;
break;
case 1:
if (gps_byte == 'G') {
step++;
}
break;
case 2:
if (gps_byte == 'P') {
step++;
}
break;
case 3:
msg_type = gps_byte << 16;
step++;
break;
case 4:
msg_type = msg_type + (gps_byte << 8);
step++;
break;
case 5:
msg_type = msg_type + gps_byte;
if (msg_type == 4345668) {
//BOD
array_title = 0;
updates |= GPBOD;
step++;
}
else
if (msg_type == 4347715) {
//BWC
array_title = 1;
updates |= GPBWC;
step++;
}
else
if (msg_type == 4671297) {
//GGA
array_title = 2;
updates |= GPGGA;
step++;
}
else
if (msg_type == 4672588) {
//GLL
array_title = 3;
updates |= GPGLL;
step++;
}
else
if (msg_type == 4674369) {
//GSA
array_title = 4;
updates |= GPGSA;
step++;
}
else
if (msg_type == 4674390) {
//GSV
array_title = 5;
updates |= GPGSV;
step++;
}
else
if (msg_type == 4736084) {
//HDT
array_title = 6;
updates |= GPHDT;
step++;
}
else
if (msg_type == 5386288) {
//R00
array_title = 7;
updates |= GPR00;
step++;
}
else
if (msg_type == 5393729) {
//RMA
array_title = 8;
updates |= GPRMA;
step++;
}
else
if (msg_type == 5393730) {
//RMB
array_title = 9;
updates |= GPRMB;
step++;
}
else
if (msg_type == 5393731) {
//RMC
array_title = 10;
updates |= GPRMC;
step++;
}
else
if (msg_type == 5395525) {
//RTE
array_title = 11;
updates |= GPRTE;
step++;
}
else
if (msg_type == 5526086) {
//RRF
array_title = 12;
updates |= GPTRF;
step++;
}
else
if (msg_type == 5461070) {
//STN
array_title = 13;
updates |= GPSTN;
step++;
}
else
if (msg_type == 5653079) {
//VBW
array_title = 14;
updates |= GPVBW;
step++;
}
else
if (msg_type == 5657671) {
//VTG
array_title = 15;
updates |= GPVTG;
step++;
}
else
if (msg_type == 5722188) {
//WPL
array_title = 16;
updates |= GPWPL;
step++;
}
else
if (msg_type == 5788741) {
//XTE
array_title = 17;
updates |= GPXTE;
step++;
}
else
if (msg_type == 5915713) {
//ZDA
array_title = 18;
updates |= GPZDA;
step++;
}
else {
uart_error = true;
}
break;
case 6:
if (gpsRxLen < PACKET_SIZE) {
switch (array_title) {
case 0:
gpbod[gpsRxLen++] = gps_byte;
break;
case 1:
gpbwc[gpsRxLen++] = gps_byte;
break;
case 2:
gpgga[gpsRxLen++] = gps_byte;
break;
case 3:
gpgll[gpsRxLen++] = gps_byte;
break;
case 4:
gpgsa[gpsRxLen++] = gps_byte;
break;
case 5:
gpgsv[gpsRxLen++] = gps_byte;
break;
case 6:
gphdt[gpsRxLen++] = gps_byte;
break;
case 7:
gpr00[gpsRxLen++] = gps_byte;
break;
case 8:
gprma[gpsRxLen++] = gps_byte;
break;
case 9:
gprmb[gpsRxLen++] = gps_byte;
break;
case 10:
gprmc[gpsRxLen++] = gps_byte;
break;
case 11:
gprte[gpsRxLen++] = gps_byte;
break;
case 12:
gptrf[gpsRxLen++] = gps_byte;
break;
case 13:
gpstn[gpsRxLen++] = gps_byte;
break;
case 14:
gpvbw[gpsRxLen++] = gps_byte;
break;
case 15:
gpvtg[gpsRxLen++] = gps_byte;
break;
case 16:
gpwpl[gpsRxLen++] = gps_byte;
break;
case 17:
gpxte[gpsRxLen++] = gps_byte;
break;
case 18:
gpzda[gpsRxLen++] = gps_byte;
break;
}
break;
}
}
}
/**
* @brief
* @note
* @param
* @retval
*/
void GPS::updateCheck()
{
if (updates & GPBOD) {
//printf("GPBOD ");
eventFlags.set(GPBOD);
}
if (updates & GPBWC) {
//printf("GPBWC ");
eventFlags.set(GPBWC);
}
if (updates & GPGGA) {
mutex.lock();
sscanf
(
gpgga,
",%f,%f,%c,%f,%c,%d,%d,%f,%f,%c,%f",
&time,
&latitude,
&ns,
&longitude,
&ew,
&lock,
&sats,
&hdop,
&alt,
&unit,
&geoid
);
int_time = static_cast<int>(time);
mutex.unlock();
//printf("GPGGA ");
eventFlags.set(GPGGA);
}
if (updates & GPGLL) {
//printf("GPGLL ");
eventFlags.set(GPGGA);
}
if (updates & GPGSA) {
//printf("GPGSA ");
eventFlags.set(GPGGA);
}
if (updates & GPGSV) {
//printf("GPGSV ");
eventFlags.set(GPGSV);
}
if (updates & GPHDT) {
//printf("GPHDT ");
eventFlags.set(GPHDT);
}
if (updates & GPR00) {
//printf("GPR00 ");
eventFlags.set(GPR00);
}
if (updates & GPRMA) {
//printf("GPRMA ");
eventFlags.set(GPRMA);
}
if (updates & GPRMB) {
//printf("GPRMB ");
eventFlags.set(GPRMB);
}
if (updates & GPRMC) {
//printf("GPRMC ");
eventFlags.set(GPRMC);
}
if (updates & GPRTE) {
//printf("GPRTE ");
eventFlags.set(GPRTE);
}
if (updates & GPTRF) {
//printf("GPRTF ");
eventFlags.set(GPTRF);
}
if (updates & GPSTN) {
//printf("GPSTN ");
eventFlags.set(GPSTN);
}
if (updates & GPVBW) {
//printf("GPVBW ");
eventFlags.set(GPVBW);
}
if (updates & GPVTG) {
//printf("GPVTG ");
mutex.lock();
sscanf(gpvtg, ",%f,%c,,%c,%f,%c,%f,%c,%c", &track, &T, &M, &speedN, &N, &speedM, &K, &mode);
mutex.unlock();
eventFlags.set(GPVTG);
}
if (updates & GPWPL) {
//printf("GPWPL ");
eventFlags.set(GPWPL);
}
if (updates & GPXTE) {
//printf("GPXTE ");
eventFlags.set(GPXTE);
}
if (updates & GPZDA) {
//printf("GPZDA ");
eventFlags.set(GPZDA);
}
uart_error = false;
}
/**
* @brief
* @note
* @param
* @retval
*/
bool GPS::waitFor(uint32_t flag, uint32_t millisec)
{
eventFlags.wait_all(flag, millisec);
return true;
}
/**
* @brief
* @note
* @param
* @retval
*/
int GPS::getTime()
{
int result;
mutex.lock();
result = int_time;
mutex.unlock();
return result;
}
/**
* @brief
* @note
* @param
* @retval
*/
float GPS::getLat()
{
float result;
mutex.lock();
result = latitude;
mutex.unlock();
return result;
}
/**
* @brief
* @note
* @param
* @retval
*/
float GPS::getLong()
{
float result;
mutex.lock();
result = longitude;
mutex.unlock();
return result;
}
/**
* @brief
* @note
* @param
* @retval
*/
int GPS::getSats()
{
float result;
mutex.lock();
result = sats;
mutex.unlock();
return result;
}
/**
* @brief
* @note
* @param
* @retval
*/
float GPS::getHDOP()
{
float result;
mutex.lock();
result = hdop;
mutex.unlock();
return result;
}
/**
* @brief
* @note
* @param
* @retval
*/
float GPS::getSpeed()
{
float result;
mutex.lock();
result = speedM;
mutex.unlock();
return result;
}
main.cpp
/* mbed Microcontroller Library
* Copyright (c) 2019 ARM Limited
*
SPDX
-License-Identifier: Apache-2.0
*/
#include "mbed.h"
#include "GPS.h"
GPS gps(PG_14, PG_9);
/**
* @brief
* @note
* @param
* @retval
*/
int main()
{
printf("Starting...\r\n");
// Initialise the digital pin LED1 as an output
DigitalOut led(LED1);
while (true) {
if (gps.waitFor(GPGGA, RX_MAXTIME)) {
int time = gps.getTime();
int latitude = static_cast<int>(gps.getLat() * 100000.0f);
int longitude = static_cast<int>(gps.getLong() * 100000.0f);
int speed = static_cast<int>(gps.getSpeed() * 100.0f);
int hdop = static_cast<int>(gps.getHDOP() * 10.0f);
int sats = gps.getSats();
printf("time:%d lat:%d long:%d speed:%d hdop:%d sats:%d\r\n", time, latitude, longitude, speed, hdop, sats);
led = !led;
}
ThisThread::sleep_for(1000ms);
}
}