STM32 UART Library for MTK3329 GPS Module
Please read Liability Disclaimer and License Agreement CAREFULLY
We can use this library as per example below in main.c
if(GPS_HasData()){
Time = HAL_GetTick() - Time;
printf("Interval = %d\r\n", Time);
Time = HAL_GetTick();
printf("H=%d M=%d S=%d D=%d M=%d Y=%d Lat=%d Lng=%d Alt=%d SAT=%d\r\n", GPS_Val.UTC_Hour, GPS_Val.UTC_Minute, GPS_Val.UTC_Second, GPS_Val.UTC_Day,GPS_Val.UTC_Month,GPS_Val.UTC_Year, GPS_Val.Latitude, GPS_Val.Longitude, GPS_Val.Altitude, GPS_Val.Satellites);
}
1. Set p U(S)ART and add the IDLE interrupt callback in usart.c. We Will call the function getGPS() when the receive line of usart gps goes idle.
I am using USART1 hardcoded in this example.
void HAL_UART_RxIdleCallback(UART_HandleTypeDef *uartHandle)
{
if(uartHandle->Instance == USART1){
uint8_t rxXferCount = 0;
//wait to finish any transmission first
while(uartHandle->gState == HAL_UART_STATE_BUSY_TX){}
//Determine how many items of data have been received/
rxXferCount = MTK_RX_SIZE - huart1.hdmarx->Instance->NDTR;
//dataCount = rxXferCount - 2;
//Stop DMA
HAL_UART_DMAStop(uartHandle);
//process the message
if(rxXferCount > 0)
getGPS();
uartHandle->RxXferCount = 0;
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
HAL_UART_Receive_DMA(&huart1, (uint8_t *)GPS_RxBuff, MTK_RX_SIZE);
} else if(uartHandle->Instance==USART3){
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
HAL_UART_Receive_DMA(&huart3, (uint8_t *)ESP_RxBuff, ESP_RX_SIZE);
}
}
2. Create the GPS.h file
#ifndef __GPS_H
#define __GPS_H
#ifdef __cplusplus
extern "C" {
#endif
#include "stm32f7xx_hal.h"
#include "main.h"
#include "usart.h"
#define X10 10
#define X100 100
#define X1000 1000
#define X10000 10000
//GPS specific
#define PREWORD0 0x5A //Z
#define PREWORD1 0x44 //D
#define PREWORD2 0x41 //A
#define ENDWORD0 0x0D //CR
#define ENDWORD1 0x0A //LF
#define NORTH 0x4E //N
#define EST 0x45 //E
#define FIX_CHECK 0x30 //E
#define SEPARATOR 0x2C //,
#define OK_POS 18
#define DATE_E 30
#define DATE_C 31
#define CMD_SIZE 15
#define OUT_SIZE 51
#define c_r 0.006
//Disable GPTXT sentence output and save the parameter into flash
//$PQTXT,W,0,1*23
//#define MTK_SET_BINARY "$PMTK253,1,115200*00\r\n" //22
#define MTK_AIC_ON "$PMTK286,1*23\r\n" //15
#define MTK_SBAS_ON "$PMTK313,1*2E\r\n"
#define MTK_WAAS_ON "$PMTK301,2*2E\r\n"
#define MTK_SET_5HZ "$PMTK220,200*2C\r\n"
//#define MTK_NAVTHRES_OFF "$PMTK397,0*23\r\n"
//turn on GGA + ZDA (for date)
#define MTK_SET_OUTPUT "$PMTK314,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0*28\r\n"//51
//UART specific
#define MTK_RX_SIZE 256
struct _mtk_msg {
int32_t Latitude;
int32_t Longitude;
int32_t Altitude;
uint8_t Satellites;
uint8_t Fix_Type;
uint8_t UTC_Day;
uint8_t UTC_Month;
uint16_t UTC_Year;
uint8_t UTC_Hour;
uint8_t UTC_Minute;
uint8_t UTC_Second;
uint16_t UTC_mSecond;
};
extern struct _mtk_msg GPS_Val;
extern volatile uint8_t GPS_Ready;
extern volatile uint8_t GPS_LEN;
extern volatile uint8_t GPS_RxBuff[MTK_RX_SIZE];//buffer for received data from GPS
void getGPS(void);
void GPS_Start(void);
uint8_t GPS_HasData(void);
#ifdef __cplusplus
}
#endif
#endif /* __GPS_H */
3. Create GPS.c file
#include <stdlib.h>
#include <stdio.h>
#include "GPS.h"
struct _mtk_msg GPS_Val;
volatile uint8_t GPS_Ready;
volatile uint8_t GPS_LEN;
static uint8_t gps_data[MTK_RX_SIZE];
volatile uint8_t GPS_RxBuff[MTK_RX_SIZE];
static int32_t Bytes2Long(uint8_t index)
{
return ((uint32_t)GPS_RxBuff[index + 3] << 24) | ((uint32_t)GPS_RxBuff[index + 2] << 16) | ((uint32_t)GPS_RxBuff[index + 1] << 8) | ((uint32_t)GPS_RxBuff[index]);
}
static int getValFromMsg(uint8_t startPos, uint8_t nChars){
uint8_t strLen = nChars + 1;
uint8_t endPos = startPos + nChars;
char buff[strLen];
for(uint8_t idx = startPos; idx < endPos; idx++) {
buff[idx - startPos] = gps_data[idx];
}
buff[strLen] = '\0';
return atoi(buff);
}
static double getDblFromMsg(uint8_t startPos, uint8_t nChars){
uint8_t strLen = nChars + 1;
uint8_t endPos = startPos + nChars;
char buff[strLen];
for(uint8_t idx = startPos; idx < endPos; idx++) {
buff[idx - startPos] = gps_data[idx];
}
buff[strLen] = '\0';
return atof(buff);
}
static uint8_t getCRC(uint8_t startPos){
unsigned int ret;
char buff[2] = {gps_data[startPos], gps_data[startPos + 1]};
sscanf(buff,"%x", &ret);
return (uint8_t)ret;
}
//To Do MTK Binary protocol
//Parameters Length(Byte) Description
//Preamble 2 0x2404
//Length 2 Total number in the packet from Preamble to End Word.
// Maximum packet size: 256 bytes
// Use little endian
// Use one byte alignment
//CommandID 2 0~999: conform to PMTK ASCII protocol
// 1000~65535: designated for MTK binary protocol
//Data Variable Data to be transferred
//Checksum 1 The checksum is the 8-bit exclusive OR of all bytes in the
// packet between (but not including) the “Preamble” and the “Checksum”
//EndWord 2 0x0A0D
//GGA example
//$GPGGA,193229.000,6017.5584,N,00516.4752,E,1,5,3.54,39.9,M,43.8,M,,*6F#CR#LF
//$GPGGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh
//1 = UTC
//2 = Latitude
//3 = N or S
//4 = Longitude
//5 = E or W
//6 = GPS quality indicator (0=invalid; 1=GPS fix; 2=Diff. GPS fix)
//7 = Number of satellites in use [not those in view]
//8 = Horizontal dilution of position
//9 = Antenna altitude above/below mean sea level (geoid)
//10 = Meters (Antenna height unit)
//11 = Geoidal separation (Diff. between WGS-84 earth ellipsoid and
// mean sea level. -=geoid is below WGS-84 ellipsoid)
//12 = Meters (Units of geoidal separation)
//13 = Age in seconds since last update from diff. reference station
//14 = Diff. reference station ID#
//15 = Checksum
//$GPZDA,200436.000,03,05,2020,,*53#CR#LF
//UTC
//Day, 01 to 31
//Month, 01 to 12
//Year
//Local zone description, 00 to +/- 13 hours
void getGPS(void) {
uint8_t cnt = 0;
uint8_t checkSum = 0;
uint8_t checkSumMsg = 0;
uint8_t checkSumLen = GPS_LEN - 4;
//If we have valid data \r\n at the end
if((gps_data[GPS_LEN-1] == ENDWORD0) && (gps_data[GPS_LEN] == ENDWORD1)) {
//first we process the $GPZDA to get time and date
//$GPZDA,200436.000,03,05,2020,,*53rn
for(cnt = 1; cnt < DATE_E; cnt++){
checkSum ^= gps_data[cnt];
}
//t1 = checkSum;
checkSumMsg = getCRC(DATE_C);
//t2 = checkSumMsg;
//If the date/time is OK then we go further
if(checkSum == checkSumMsg){
//get UTC Hour pos 7,8 from buffer
GPS_Val.UTC_Hour = (uint8_t)getValFromMsg(7, 2);
//get UTC Minutes pos 9,10 from buffer
GPS_Val.UTC_Minute = (uint8_t)getValFromMsg(9, 2);
//get UTC Seconds pos 11,12 from buffer
GPS_Val.UTC_Second = (uint8_t)getValFromMsg(11, 2);
//get UTC miliSeconds pos 14,15 and 16 from buffer
//GPS_Val.UTC_mSecond = (uint16_t)getValFromMsg(14, 2);
//get UTC day pos 18,19 from buffer
GPS_Val.UTC_Day = (uint8_t)getValFromMsg(18, 2);
//get UTC month pos 21,22 from buffer
GPS_Val.UTC_Month = (uint8_t)getValFromMsg(21, 2);
//get UTC year pos 24 to 27 from buffer
GPS_Val.UTC_Year = (uint16_t)getValFromMsg(24, 4);
//starting with index 35 we have the $GPGGA sentence
//$GPGGA,200436.000,6017.5616,N,00516.4400,E,1,5,2.21,113.3,M,43.8,M,,*52rn
// first we check if we have a fix on the position
if(gps_data[78] > FIX_CHECK){
//checksum
checkSumMsg = getCRC(GPS_LEN - 3);
//reset the checkSum
checkSum = 0x00;
//perform CheckSum on message
for(cnt = 36; cnt < checkSumLen; cnt++){
checkSum ^= gps_data[cnt];
}
//t1 = checkSum;
//t2 = checkSumMsg;
//Check calculated value against the one from message
if(checkSum == checkSumMsg){
//get Latitude in Seconds pos 18 to 26 from buffer
GPS_Val.Latitude = ((uint8_t)getValFromMsg(53, 2)) * 3600 +
((uint8_t)getValFromMsg(55, 2)) * 60 +
((uint16_t)getValFromMsg(58, 4)) * c_r;
//If North Latitude is +, If South Latitude is -
if(gps_data[63] != NORTH){
GPS_Val.Latitude *= -1;
}
GPS_Val.Longitude = ((uint16_t)getValFromMsg(65, 3)) * 3600 +
((uint8_t)getValFromMsg(68, 2)) * 60 +
((uint16_t)getValFromMsg(71, 4)) * c_r;
//If North Latitude is +, If South Latitude is -
if(gps_data[76] != EST){
GPS_Val.Longitude *= -1;
}
//this is the last fixed position in the string!!!! also what we can use as integer values
// we reuse checkSum variables to save some bytes
//checkSum will store "Horizontal dilution of position" start position
if(gps_data[81] == SEPARATOR) {
GPS_Val.Satellites = (uint8_t)getValFromMsg(80, 1);
checkSum = 82;
} else {
GPS_Val.Satellites = (uint8_t)getValFromMsg(80, 2);
checkSum = 83;
}
// we are left with Horizontal dilution of position, Antenna altitude, Meters
//Geoidal separation, Meters, ,,. We need to get only the antena altitude
checkSumMsg = 0;
for(cnt = checkSum; cnt < GPS_LEN; cnt++) {
//find the next comma in the string
//first one will be the end of Horizontal dilution of position
if(gps_data[cnt] == SEPARATOR){
if(!checkSumMsg){
checkSumMsg = cnt+1;//the start of altitude
} else {
checkSumLen = cnt+1;//the end of altitude
break;//exit the loop
}
}
}
//Altitude is multiplied with 10
GPS_Val.Altitude = (int)(getDblFromMsg(checkSumMsg, checkSumLen - checkSumMsg)*10.0);
} else {
printf("GPS position CRC Error\r\n");
GPS_LEN = 0;
}
} else {
printf("GPS FIX Error\r\n");
GPS_LEN = 0;
}
} else {
printf("GPS Date/Time CRC Error\r\n");
GPS_LEN = 0;
}
} else {
printf("GPS Packet\r\n");
GPS_LEN = 0;
}
}
void GPS_Start(void){
if(HAL_UART_Receive_DMA(&huart1, (uint8_t *)GPS_RxBuff, MTK_RX_SIZE) != HAL_OK){
_Error_Handler(__FILE__, __LINE__);
}
HAL_UART_Transmit(&huart1, (uint8_t*)MTK_SBAS_ON, CMD_SIZE, PORT_TIMEOUT);
__HAL_UART_FLUSH_DRREGISTER(&huart1);
HAL_UART_Transmit(&huart1, (uint8_t*)MTK_WAAS_ON, CMD_SIZE, PORT_TIMEOUT);
__HAL_UART_FLUSH_DRREGISTER(&huart1);
HAL_UART_Transmit(&huart1, (uint8_t*)MTK_AIC_ON, CMD_SIZE, PORT_TIMEOUT);
__HAL_UART_FLUSH_DRREGISTER(&huart1);
HAL_UART_Transmit(&huart1, (uint8_t*)MTK_AIC_ON, CMD_SIZE, PORT_TIMEOUT);
__HAL_UART_FLUSH_DRREGISTER(&huart1);
HAL_UART_Transmit(&huart1, (uint8_t*)MTK_SET_5HZ, 17, PORT_TIMEOUT);
__HAL_UART_FLUSH_DRREGISTER(&huart1);
HAL_UART_Transmit(&huart1, (uint8_t*)MTK_SET_OUTPUT, OUT_SIZE, PORT_TIMEOUT);
__HAL_UART_FLUSH_DRREGISTER(&huart1);
HAL_Delay(PORT_TIMEOUT);
__HAL_UART_FLUSH_DRREGISTER(&huart1);
//Wait to get the GPGGA enabled
while((GPS_RxBuff[3] != PREWORD0) && (GPS_RxBuff[4] != PREWORD1) && (GPS_RxBuff[5] != PREWORD2)){
__HAL_UART_FLUSH_DRREGISTER(&huart1);
//HAL_UART_Transmit(&huart1, (uint8_t*)MTK_SET_OUTPUT, OUT_SIZE, UART_WAIT);
HAL_Delay(100);
}
}
uint8_t GPS_HasData(void){
if(GPS_LEN){
//if we have data first check if we are receiving new data
//while(hdma_uart4_rx.State == HAL_BUSY){}
//process available data
getGPS();
GPS_LEN = 0;
return 1;
} else {
return 0;
}
}
Comments powered by CComment