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:
- The time values (Hour, Min, Second) are incremented
- The PPS output (PB0) is brought high
- The GPGGA NMEA sentence is transmitted
- The GPGGA GPRMC sentence is transmitted
- 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; } |
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.
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).
Good spot! I don’t even know how/why that even worked!