Serial interrupt reading head and writing tail method based on 51 single chip microcomputer to receive 1602 display of NMEA0183 longitude and latitude information (cyclic reception)

51 single chip microcomputer serial port receiving navigation message


The traditional serial port receiving program is to set up the interrupt receiving buffer area, and clear the receiving length pointer in time after the main program is processed. This method has no problem for the receiving of single statement or data section with low speed requirement, but for the receiving of a pile of statements with high speed, it is easy to cause the problem of data loss due to the limitation of the speed reading of the single chip processor. This paper discusses how to design the receiving program under the condition that the speed of single chip microcomputer is fixed, so as to avoid packet loss as much as possible.

design scheme

Hardware composition

STC89C52RC is used as the control module and LCD1602 is used as the display screen to display the longitude and latitude information of GGA statement in NMEA0183 received by the serial port.

Software composition

1. The interrupt uses two pointers, head and tail, to write tail when receiving and read head when processing. The tail pointer always points to the next location where the data in the interrupt cache is written (note that it is the next location), and the head pointer always points to the location where the data is to be read.

2. When the head or tail pointer points to the last position of the cache area, the pointer changes to 0, that is to say, jump to the head of the cache area, and the new data covers the old data, which goes back and forth. When the cache area is full, this is the case


In this case, it shows that the processing speed of the single-chip microcomputer is not enough to catch up with the receiving speed. In order to avoid the loss of data packets, the head pointer + 1, that is to say, the old data is covered, so as to achieve the purpose of cyclic receiving. And LED2 light is on to indicate that the cache is full.

3. Because NMEA0183 data starts with the dollar sign and ends with the dollar sign, it is more convenient for the program to process a statement. There may be multiple statements in the cache. We set a flag bit recline in the interrupt function to indicate that there are several statements in the cache. When receiving, recline + + and LED1 are on, indicating that a frame of data is received.

4. The interrupt buffer receives all data, get_ linestr_ The frombuf() function finds a statement from the cache. The getLATstrFromGGA() function filters the longitude and latitude information from a statement and stores it in the 1602 display array. The SendstrLCD1602() function is the 1602 display function.

5,get_ linestr_ The main function of the frombuf() function is to find a complete statement from the cache that begins with a $and ends \ n with a $. The fault tolerance of the system should be fully considered here, that is, if there are no N and dollar symbols in the cache, the recline will be cleared to 0, indicating that there are no correct statements in the cache, and the function will be exited directly. The return value is 0, indicating that the effective length is 0.
In addition, in the first while loop of this function, the function of this statement is to increase the fault tolerance of the system

if(cc == '\n') {				//Increase fault tolerance taking into account the case of two \ n $s
			if(recline>0) recline--;

In the process of looking for the $sign, if you first encounter the \ n, then the statement in the cache must be - 1, which is to prevent the situation that there are more than the dollar sign.

Test situation

Receiving capacity test

Use navigation data sending simulation software to simulate the receiving of navigation data. Send multiple statements at a time to test the receiving capacity of the serial port. LED2 blinks if the cache is full. If you encounter a situation where there is no $receive error in the cache. LED3 will flash.

The test software is as shown in the figure. Three instructions are sent in one second and received normally


25 instructions are sent in one second, LED2 starts to flash wildly, but the display of latitude and longitude is changed once a second. It can be seen that the bearing capacity of the system is OK.




Traditional receiving mode test

From the test results, it can be seen that the traditional way is overloaded with only two sentences per second, and the longitude and latitude can not be displayed correctly. The picture shown in 1602 in the figure has not moved.



Program code

Traditional program interrupt part code

volatile  char NAV_uart_p = 0;                                         
void serial_rec(void) interrupt 4
{
	if(NAV_uart_p < UART_BUFF_SIZE && NAV_RcvDone == 0)            //Within the length
	{
		if(RI)     													//Internal interrupt register
		{
			RI=0;                						
			NAV_rx_buf[NAV_uart_p] = SBUF;
			if(NAV_rx_buf[0] == '$')                               //The navigation data frame header starts with '$', if it is not this field, it means it is not navigation data
			{
				NAV_uart_p++;
				if(NAV_uart_p > 5)									//$BDGGA, receive headers first, judge whether the data has been received, and the data ending format is "XX XX * XX \ R \ n" (\ r = 0x0D \ n = 0x0a)
				{
					if(NAV_rx_buf[NAV_uart_p - 1] == 0X0A && NAV_rx_buf[NAV_uart_p - 2] == 0X0D && NAV_rx_buf[NAV_uart_p - 5] == '*' )
					{
						NAV_rx_buf[NAV_uart_p] = '\0';     			//String Terminator
						NAV_RcvDone = 1;                         	//Data receiving completed
						ES = 0;										//Off interrupt
					}
				}
			}
			else
			{
				NAV_uart_p = 0;
			}
		}		
	}
}

The program with the method of reading head and writing tail


//*********************************************************
// Program: receive NMEA0183 serial port string, buffer end pointer
// Processor: STC90C52RC
// Compiling environment: Keil5 C51 
// System clock: 11.0592MHZ
//*********************************************************

#include <reg52.h>
#include <string.h>

#define uint   unsigned int  
#define uchar  unsigned char
#define BUFLEN 120			//Receive the limit of 51 single chip microcomputer memory, the length of cache area cannot be too long
#define LINE   80           //The maximum length of a statement is 80
xdata char recBuf[BUFLEN];  //All in the Development Zone
xdata char onelineBuf[LINE];
char head=0;
char tail=0;
char char_in;
char recline=0;	

//global variable


/* ***************************************************** */
// Bit definition, defining the pin controlling LCD1602
/* ***************************************************** */
sbit RS = P2^6;			//Data / command selector (H/L)
sbit RW = P2^7;			//Data / write selector (H/L)
sbit EN = P2^5;			//Enable signal
sbit LED1 = P1^0;		//LED1
sbit LED2 = P1^1;		//LED2
sbit LED3 = P1^2;		//LED1
sbit LED4 = P1^3;		//LED2


/* ***************************************************** */
// Function name: DelayMS()
// Function function: millisecond delay
// Entry parameter: delay milliseconds (ValMS)
void DelayMS(uint ValMS)
{
	uint uiVal,ujVal;
	for(uiVal = 0; uiVal < ValMS; uiVal++)
		for(ujVal = 0; ujVal < 113; ujVal++);
}
/* ***************************************************** */
// Function name: DectectBusyBit()
// Function function: check status flag bit (judge whether it is free / busy)
void DectectBusyBit(void)
{   
	P1 = 0xff;			// When reading the state value, assign the high level first
	RS = 0;
	RW = 1;
	EN = 1;
	DelayMS(5);
	while(P0 & 0x80);	// Wait if LCD is busy
	EN = 0;				// Then initialize EN to low level 
}
/* ***************************************************** */
// Function name: WrComLCD()
// Function function: write instructions for LCD
// Entry parameter: instruction (ComVal)
void WrComLCD(uchar ComVal)
{
	DectectBusyBit();
	RS = 0;
	RW = 0;
	P0 = ComVal;
	EN = 1;
	DelayMS(5);
	EN = 0;	
}
/* ***************************************************** */
// Function name: WrDatLCD()
// Function function: write data for LCD
// Entry parameters: data (DatVal)
void WrDatLCD(uchar DatVal)
{
	DectectBusyBit();
	RS = 1;
	RW = 0;
	P0 = DatVal;
	EN = 1;
	DelayMS(5);
	EN = 0;	
}
/* ***************************************************** */
// Function name: LCD_Init()
// Function function: initialize LCD
void LCD_Init(void)
{ 
	WrComLCD(0x38);		// 16 * 2 line display, 5 * 7 dot matrix, 8-bit data interface
	DelayMS(5);			// Delay slightly
	WrComLCD(0x01);    	// Clear screen 
	WrComLCD(0x06);  	// AC auto +, picture not moving  
	WrComLCD(0x0f);    	// Turn on the display, cursor and flash
}



//Serial port initialization baud rate 9600
void	Uartinit(void)				
{
 	TMOD = 0x20;
	SCON = 0x50;
	TH1 = 250;
	TL1 = TH1;
	PCON = 0x80;
	EA = 1;
	ES = 1;
	TR1 = 1;
}


/*----------serial receive interrupt--------------*/
void serial_rec(void) interrupt 4
{
	if(RI)
	{ 
		char_in=SBUF;      
		RI=0; 
		recBuf[tail++] =char_in;
		if(tail == BUFLEN) tail = 0;
		if(char_in == '\n'){
			recline++;				//Newline sign plus 1
			LED1 = ~LED1;
		}
		if(tail == head){      		//Buffer full
			head++;
			if(head == BUFLEN) head = 0;
			LED2 = ~LED2;
		}
	}			
}


//---------------------------------------------------------
//Let 1602 display the string, len is the specified length
void SendstrLCD1602(char str[], int len)
{
	char i=0;
	for(i=0; i<len; i++)
		WrDatLCD(str[i]);		   
}


//-----------------------------------------------------------
int getLATstrFromGGA(char *onelineBuf, char *latstr)
{
	char i;
	if(onelineBuf[3] != 'G' || onelineBuf[4] != 'G' || onelineBuf[5] != 'A' ) return 0;
	for(i=0; i<10; i++)
		latstr[i] = onelineBuf[i+ 17];
	latstr[i] = '\0';
	return 1;
}


//-----------------------------------------------------------
int getLONstrFromGGA(char *onelineBuf,char *lonstr)
{
	char i;
	if(onelineBuf[3] != 'G' || onelineBuf[4] != 'G' || onelineBuf[5] != 'A' ) return 0;
	for(i=0; i<11; i++)
		lonstr[i] = onelineBuf[i+ 28];
	lonstr[i] = '\0';
	return 1;	
}


//-----------------------------------------------------
int  get_linestr_fromBuf()				//Read a statement from the interrupt cache
{
	char cc = 0;
	char index=0;
	//find '$'
	while(cc != '$')
	{
		//I haven't met '$'
		if(head  == tail) 
		{
			recline = 0;
			return 0;
		}
		cc = recBuf[head++];
		if(head == BUFLEN) head = 0;
		if(cc == '$')
		{
			onelineBuf[index++] = cc;
			break;
		}
		if(cc == '\n') {				//Increase fault tolerance taking into account the case of two \ n $s
			if(recline>0) recline--;
		}
	}
	//Read data until line break
	while(cc != '\n'){
		//No line break encountered after reading empty
		if(head  == tail)
		{
			recline = 0;
			LED3 = ~LED3;
			return 0;
		}
		cc = recBuf[head++];
		if(head == BUFLEN) head = 0;
		onelineBuf[index++] = cc;	
	}
	
	//Normal, head and tail intact
	if(recline>0) recline--;
	onelineBuf[index] = '\0';
	return index;       		 //Returns the length of the extraction string
		
}


//--------------------------------------------------------------------
//Convert numbers to 5-bit strings
void  int_to_ASCstr(char str[],uint d)
{
	str[0] = '0' + (d / 100);	
	str[1] = '0' + (d / 10) % 10;
	str[2] = '0' + d % 10;
	str[3] = '\0';
}


/****************************************************** */
void main()
{ 
	idata uint i;
	char  dispstr[20];
	char  len;


	for(i=0; i<10000; i++);				//Delay, and make 1602 and other peripherals ready
	Uartinit();							//Serial port initialization
	LCD_Init();							//1602 initialization
	
	//1602 meaning of data to be displayed after two lines of display
	WrComLCD(0x80);								//Select 1602 line 1, bit 1
	strcpy(dispstr,"Lat:");
	SendstrLCD1602(dispstr,4);
	WrComLCD(0xC0);								//Select 1602 line 2, bit 1
	strcpy(dispstr,"Lon:");
	SendstrLCD1602(dispstr, 4);
		
	while(1){
		if(recline > 0)						//See if there's a row of data
		{	
			len = get_linestr_fromBuf();
			
			if(len > 50)
			{										
				if(getLATstrFromGGA(onelineBuf,dispstr) == 1)
				{
					WrComLCD(0x86);					
					SendstrLCD1602(dispstr, 10);			//Show LAT	
				}
				
				if(getLONstrFromGGA(onelineBuf,dispstr) == 1)
				{
					WrComLCD(0xC5);							//Select 1602 line 2, bit 11
					SendstrLCD1602(dispstr, 11);			//Display LON	
				}
			}
		}
	}
}

conclusion

It can be seen from the test results that the method of reading head and writing tail is adopted to improve the receiving ability of the system without changing the speed of the single chip microcomputer, which is of great significance for the design of receiving many strings at a time.
I am not a computer and embedded professional. This way is a way that we have a course involving single-chip microcomputer this semester. The teacher said it. I think this way is very meaningful. I will summarize it and provide it to you for reference. If you think something is wrong, you are welcome to criticize and correct it.

Posted on Mon, 15 Jun 2020 01:07:00 -0400 by nascarjunky