AVR Pulse Per Second & NMEA simulator

As part of building my Satellite Tracking Camera, I’m building a GPS time inserter to insert accurate time stamps on a video feed.

I’m unable to get GPS reception in my test/development area, so I decided it would be easier to simulate the GPS with a microcontroller.

This circuit simulates a PPS (Pulse Per Second) output and the GPGGA and GPRMC NMEA sentences.

The circuit is pretty simple.

I’m using a 32.768kHz real time clock crystal as I’m needing somewhat accurate PPS signals for testing.

The AVR simply runs in a loop, and every 32768 cycles of the external crystal causes a counter overflow and calls the interrupt.

Once the interrupt has been called, the following happens:

  1. The time values (Hour, Min, Second) are incremented
  2. The PPS output (PB0) is brought high
  3. The GPGGA NMEA sentence is transmitted
  4. The GPGGA GPRMC sentence is transmitted
  5. The PPS output (PB0) is brought low

The AVR GCC code (Albeit messy) is at the end of this post. It’s very easy to modify it for other similar purposes. Note that I’m not calculating the Checksum for the NMEA sentences (I’m outputting a checksum of XX). If you need to do this, it’s very simple as it’s just a Hex representation of the XOR of all characters in the sentence between (not including) the $ and the * character.

#include <avr/io.h>
#include <stdlib.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <avr/interrupt.h>
 
// Define baud rate
#define USART_BAUD 9600ul
#define USART_UBBR_VALUE ((F_CPU/(USART_BAUD<<4))-1)
 
volatile uint8_t flagsecond = 0;
 
volatile uint8_t time_h = 12;
volatile uint8_t time_m = 34;
volatile uint8_t time_s = 56;
 
char longstring[96];
 
void USART_vInit(void)
{
// Set baud rate
UBRR0H = (uint8_t)(USART_UBBR_VALUE>>8);
UBRR0L = (uint8_t)USART_UBBR_VALUE;
 
// Set frame format to 8 data bits, no parity, 1 stop bit
UCSR0C = (0<<USBS0)|(1<<UCSZ01)|(1<<UCSZ00);
 
// Enable receiver and transmitter
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
}
 
 
void USART_vSendByte(uint8_t u8Data)
	{
	// Wait if a byte is being transmitted
	while((UCSR0A&(1<<UDRE0)) == 0);
		{
		// Transmit data
		UDR0 = u8Data;
		}
	}
 
void USART_vSendString(const char *str)
{
	while(*str)
	{
	USART_vSendByte(*str);
	str++;
	}	
}
 
void USART_vSendStringWithNewLine(const char *str)
{
	while(*str)
	{
	USART_vSendByte(*str);
	str++;
	}	
	USART_vSendByte(0x0A);
}
 
uint8_t USART_vReceiveByte()
	{
	// Wait until a byte has been received
	while((UCSR0A&(1<<RXC0)) == 0);
		{
		// Return received data
		return UDR0;
		}
	}
 
void TIMER_init() {
	cli();
 
	//Allow a second for oscillator to stabilize
	_delay_ms(1000);
 
	//Disable the timer 1 and 2 interrupts
	TIMSK2 &= ~((1<<OCIE2A)|(1<<OCIE2B)|(1<<TOIE2));
 
	//Enable Asynchronous timer 2
	ASSR |= (1<<AS2);
 
	//Select prescaler: 32.768 kHz / 256 / 128 = 1 sec per overflow
	TCCR2B |= (1<<CS22)|(1<<CS20);
	TIFR2 |= ((1<<OCF2A)|(1<<OCF2B)|(1<<TOV2));
 
	TIMSK2 |= (1<<TOIE2);
}
 
void increment_time()
{
	time_s++;
	if (time_s > 59)
		{
		time_s = 0;
		time_m++;
		}	
	if (time_m > 59)
		{
		time_m = 0;
		time_h++;
		}
	if (time_h > 23)
		{
		time_h = 0;
		}
}	
 
	int main(void)
	{
	// Initialise USART
	USART_vInit();
 
	TIMER_init();
 
	//SET OUTPUT FOR Pulse per second output
	DDRB = 0x01;	
	PORTB &= 0xFE; //Pull PB0 (Pulse per second) Low
 
	sei();
 
	// Repeat indefinitely
	for(;;)
		{
		if (flagsecond==1)
			{
			PORTB |= 0x01; //Pulse the PPS high
 
			increment_time();
			flagsecond = 0;
			sprintf(longstring,"$GPGGA,%2.2u%2.2u%2.2u,3650.911,S,17445.734,E,1,08,0.9,192,M,11.6,M,,*XX", time_h, time_m, time_s);
			USART_vSendStringWithNewLine(longstring);
			sprintf(longstring,"$GPRMC,%2.2u%2.2u%2.2u,A,3650.911,S,17445.734,E,0.0,0.0,290208,019.5,W*XX", time_h, time_m, time_s);
			USART_vSendStringWithNewLine(longstring);
 
			PORTB &= 0xFE; //Pull PB0 (Pulse per second) Low
 
			}
		}
	}
 
//INTERRUPT
ISR(TIMER2_OVF_vect)
{
flagsecond = 1;
}

You May Also Like

About the Author: John

3 Comments

  1. Dear John.
    Please send over your firmware to me. I wold like to modify a bit for external: steering wheel and gas pedal control. This is for my vehicle on Map test purpose.

  2. Hi John:

    I looked over your code; it’s a bit sneaky in that the send and receive byte functions have a “while(…);” construct followed by an open and close brace for the rest of the code. Following the code it’s easy to be tripped up by the semicolon after the while statement – the open and close braces are unnecessary and could be confusing.

    Thanks for posting this; I have used it as a basis for my own GPS simulator for a very similar purpose (actually testing a GPS-disciplined FPGA oscillator, but I also use the NMEA codes for real time stamping).

Leave a Reply to John Cancel reply

Your email address will not be published. Required fields are marked *