2014-09-11 10:21 PM
Hello all, i started a little project with my discovery board, using stm32f407vg and a ublox neo 6m gps.
The comunication between the two is done via USART1. At every second the gps sends somewhere at 500bytes over serial, the message in ascii looks like this: $GPRMC,173500,A,4562351,N,0250244,E,0.066,,100914,,,D*79\r\n$GPGGA,18000,4562881,N,0250231,E,1,10,0.90,2,M,8,M,,*65\r\n$GPVTG,,T,,M,0.048,N,0.089,K,D*2B\r\n$GP..... and so on GPRMC,GPGGA,GPVTG are NMEA standard 183 message ids for gps data, and i want to extract only the GGA data My rx routine looks like thisvoid USART1_IRQHandler(void)
{ static uint8_t cnt = 0; // counts number of characters int i; if(USART_GetITStatus(USART1, USART_IT_RXNE)) // check if the USART1 receive interrupt flag was set { char t = USART1->DR; // character from data reg is stored into t if (t != '\r') // if not end of string { received_string[cnt] = t; // charstored in receive buffer cnt++; // } else // if end detected { received_string[cnt++] = '\0'; // put terminator character in the end of buffer cnt = 0; // reset character counter USART_Puts(USART1, received_string); // send character to terminal for (i = 0; i <= MAX_STRLEN+1; i++) // flush buffer received_string[i] = '\0'; } } } My main concern is to filter only the dat between $GPGGA and its \r\n, i have tryied instead of t !='\r' to putif (t = 'GGA') .
How can i filter only that part of the data? My experience with serial is very poor, in fact this is my first project with serial dat The init code for usart1 is this #define GPIO_RS232_TX GPIO_Pin_6 #define GPIO_RS232_RX GPIO_Pin_7 #define baudrate 9600 NVIC_InitTypeDef NVIC_InitStruct; void Usart_interrupt_enable(){ USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // enable the USART1 receive interrupt NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; // USART1 interrupts NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;// this sets the priority group of the USART1 interrupts NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; // this sets the subpriority inside the group NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; // the USART1 interrupts are globally enabled NVIC_Init(&NVIC_InitStruct); // the properties are passed to the NVIC_Init function which takes care of the low level stuff } void serial_init(){ USART_InitTypeDef USART_InitStruct; GPIO_InitTypeDef GPIO_InitStruct; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); // enable the peripheral RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // Enable clock for USART1 GPIO_InitStruct.GPIO_Pin = GPIO_RS232_TX | GPIO_RS232_RX ; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1); // Serial TX GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1); // Serial Rx GPIO_Init(GPIOB, &GPIO_InitStruct); USART_InitStruct.USART_BaudRate = baudrate; // the baudrate is set to the value we passed into this init function USART_InitStruct.USART_WordLength = USART_WordLength_8b;// we want the data frame size to be 8 bits (standard) USART_InitStruct.USART_StopBits = USART_StopBits_1; // we want 1 stop bit (standard) USART_InitStruct.USART_Parity = USART_Parity_No; // we don't want a parity bit (standard) USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // we don't want flow control (standard) USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // we want to enable the transmitter and the receiver USART_Init(USART1, &USART_InitStruct); // again all the properties are passed to the USART_Init function which takes care of all the bit setting Usart_interrupt_enable(); USART_Cmd(USART1, ENABLE); } #nmea #gps #state-machine #fsm2014-09-12 12:13 AM
I did it this way, in the USART interrupt handler routine:
First, I catch all full sentences, the differentiation between GGA, RMC, etc. is done elsewhere. Reserve a buffer large enough to hold the longest possible sentence. Upon receiving a '$' character, reset the buffer pointer to zero (sentence start). When receiving a '\n' character, a sentence is done. Copy it immediately to another buffer, for evaluation outside of the interrupt handler. I set a flag here to signal the main loop that a new sentence is ready. Trying to dissect the sentence in the receive buffer is not a good idea, since the GPS receiver is soon starting to overwrite it. And for robustness, check the receive buffer index for overflow on each character. When evaluating the sentence in the main loop, check for the proper start (''$GPGGA'') and termination ('\r','\n'). Testing the checksum is recommended, too. By the way, most GPS receivers have commands to enable/disable individual sentences, set their update rate, and change serial settings. Disabling all undesired sentences reduces the processing load.2014-09-12 12:54 AM
Here's an article on using a State Machine (aka ''Finite State Machine''; ''FSM'') to parse NMEA:
http://www.visualgps.net/WhitePapers/NMEAParser/NMEAParser.html
2014-09-28 02:18 PM
Hello all, i am back with some god news and some bad news as well.
I managed to somehow filter the NMEA GGA sentence, but sometimes it runs out of sync... i dont know how to explain this very well. I will begin with my code#define MAX 512
int ready=0;
char rxbuff[MAX];
char *st1;
char *st2;
char gps[100]='''';
uint16_t l1,l2,ll;
void USART1_IRQHandler(void) {
static uint16_t cnt = 0;
if(USART_GetITStatus(USART1, USART_IT_RXNE)) { // check if the USART1 receive interrupt flag was set
char t = USART1->DR;
if (cnt <
MAX
) {
rxbuff[cnt] = t; // Read data and store it to buffer
cnt++; // Increment string index
} else { // if max lenghts is reached
cnt
=
0
; // reset counter
ready
=
1
; // Ready for parsing GPS data
GPIOD->BSRRH = GPIO_Pin_13; // just some LED blinking state
}}}
int main(void){
SystemInit();
int i=0;
while(1){
if(ready==1){ // if flag is set from interrupt, proces buffer data
ready=0; // reset the flag for another buffer fill
GPIOD->BSRRL = GPIO_Pin_13; // just some LED blink
st1=strstr(rxbuff,''GGA'' ); //returns GGAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx til the end of buffer
st2=strchr(st1 , '$'); // finds first ocourence of $ in st1 ( ex: GGAxxxxxxxxxxxx$
l1=strlen(st1); // get st1 lenght
l2=strlen(st2); // get st2 lenght
ll=l1-l2; // calculatest actual lenght of intrest
for(i=1; i<ll; i++) {
gps[0]=st1[0];
gps[i] = st1[i];
if(i==ll){
gps[ll]='\0';
}}
USART_Puts(USART1, gps); // send data to the new charr buffer
} }}
sometimes if i move the GPS to somewhere i have no signal instead of reading a full GGA,21100,4562676,N,0250226,E,1,11,1.00,7,M,8,M,,*59CrLf
i get
GGA,211400,,,,,0,00,99,,,,,,*6A
E,1,05,4.43,3,M,8,M,,*51
instead of only
GGA,211600,,,,,0,00,99,,,,,,*60
What i want to say is that i dont have always starting from gps[0] GGA and the last CrLf
I get wrong chars on diferent rows
How can i make a stable reading undependent of the strings checksum ?
The strange thing is, that if i reset hardware the MCU, the data is fine aligned, Why?
2014-09-28 07:16 PM
2014-09-29 09:11 AM
2014-09-29 09:28 AM
Well the purpose is two fold, a) the content of the buffer isn't transient and is NUL terminated, and b) it's processed when it's complete. It can resync quickly based on the nature of the message. It also separates the more complicated parsing, from the quick acquisition of characters in the IRQ Handler.
Another solution could be to ping-pong between two input buffers, or more, so you fill one whilst processing the other. Where/if appropriate sscanf() type functionality could be handled with atoi(), or atod() type processing.2014-09-29 10:28 AM
Posted on September 29, 2014 at 19:28
I pulled the other stuff from one of my data sheet/app notes I wrote because people were always fouling up the parsing, especially the NMEA latitude/longitude representation which is designed to be easily passed to a display, not processed directly as a numeric value.
Here's what I have for $GPGGA that I cut from another example/processing app of mine.
else if (((strcmp(field[0],"$GPGGA") == 0) ||
(strcmp(field[0],"$GNGGA") == 0)) && (f > 14))
{
double lat, lon, alt, msl;
int lat_deg, lon_deg;
double lat_min, lon_min;
double fix_time;
int fix_hour, fix_minute;
double fix_second;
char lat_hemi, lon_hemi;
int valid;
double hdop;
int sv;
// Field 1 UTC Time HHMMSS.SSS
// Field 2 Latitude
// Field 3 Lat Hemi
// Field 4 Longitude
// Field 5 Lon Hemi
// Field 6 Position Fix Indicator - 0=Fix Not available, 1=GPS SPS, 2=GPS SPS DIFF, 3=GPS PPS,
// 4=RTK, 5=Float RTK, 6=Estimate/Dead Reckoning, 7=Manual, 8=Simulator
// Field 7 Satellites Used
// Field 8 HDOP
// Field 9 MSL Altitude
// Field 10 Units M
// Field 11 Geoid Separation
// Field 12 Units M
// Field 13 Age of Differential (NULL when not used)
// Field 14 Differential Reference Station ID
sscanf(field[2],"%lf",&lat);
lat_hemi = field[3][0];
sscanf(field[4],"%lf",&lon);
lon_hemi = field[5][0];
sscanf(field[9],"%lf",&msl);
sscanf(field[11],"%lf",&alt);
sscanf(field[1],"%lf",&fix_time);
if (sscanf(field[6],"%d",&valid) != 1)// 0=Fix Not available, 1=GPS SPS, 2=GPS SPS DIFF
valid = 0;
sscanf(field[7],"%d",&sv); // Satellites used (or in view, depends on receiver)
sscanf(field[8],"%lf",&hdop); // Horizontal Dilution of precision (HDOP)
if (valid != 0)
{
lat_deg = (int)lat / 100;
lat_min = lat - (lat_deg * 100);
lat = (double)lat_deg + (lat_min / 60.0);
if (lat_hemi == 'S')
lat = -lat;
lon_deg = (int)lon / 100;
lon_min = lon - (lon_deg * 100);
lon = (double)lon_deg + (lon_min / 60.0);
if (lon_hemi == 'W')
lon = -lon;
alt += msl; // Convert to height above datum
fix_minute = (int)fix_time / 100;
fix_second = fix_time - (fix_minute * 100);
fix_hour = fix_minute / 100;
fix_minute = fix_minute % 100;
printf("GPGGA\t%+14.10lf %+14.10lf %8.2lf @ %02d:%02d:%06.3lf\n",
lat, lon, alt,
fix_hour, fix_minute, fix_second);
}
}
Edit: Fixed dogs breakfast forum conversion managed to do with this