Talbit Senior Member
Joined: 07/06/2011 Location: AustraliaPosts: 210 |
Posted: 10:52am 26 Sep 2011 |
Copy link to clipboard |
Print this post |
|
This GPS clock solves one of the basic problems of displaying the correct time. With normal operation, the GPS module will output a Pulse Per Second (PPS). This PPS is very accurate and can be used for precise timing. It can also output a number of NMEA strings. Several of these strings contain the GPS time, along with the date and the position. Extracting the time and displaying it as quickly as possible still results in the time being displayed about 250ms late. The reason is that the NMEA string is output after the PPS.
This Maximite program does the following…
1. Reads the NMEA string into com port 2 (Pin 19) at 19200, 8, N, 1
2. Extracts the GPS time and date.
3. Adds 1 second to the time and waits for the next PPS to arrive.
4. On the PPS (Pin 17), it toggles Pin 18 as an alarm (Time Break). See below.
5. Sends the time and date to the LCD.
6. Displays the time it takes to send the data to the LCD - about 62ms.
7. Starts again by reading the next NMEA string.
Time Break - The critical thing is to toggle the alarm on pin 18 smack on the PPS. It goes high for 2 seconds every minute, 4 seconds every hour and 6 seconds every day.
I’m yet to test how fast his occurs. My guess it will be very close to the PPS. It turns out to be about 940uS of if you like, a millisecond.
The next critical thing is to display the time on the LCD as quickly as possible. This maximite measures this at 62mS. You can see that there is still a slight but acceptable delay.
The Maximite performs these tasks with ease.
The code relies on the NMEA string being a $GPRMC. But there can be other strings output as well. The code will only look for the $GPRMC. So you need to set up your module to output at least the $GPRMC string. Set your GPS output and the Maximite com port to be compatible – in this case 19200, 8, N, 1). Note that this needs to be done manually. Note also that the EM 408 module is not suitable because it doesn’t output a PPS. A shame really because this is a great module.
I’ll post my circuit diagram if there is enough interest. But basically, my circuit is the same as Geoff’s LCD circuit with the following additions…
1. The NMEA string is fed into a MAX232 RS232 to TTL inverter. I divide the TTL voltage with 22K and 10K resistors and then feed the lower voltage to Pin 19 with a 4K7 resistor.
2. My PPS is a 1ms active low pulse – hence the loop at 1100 waiting for the PPS to go low. Some modules will give you a very short PPS that the Maximite might not see. You might need to lengthen the PPS.
A known bug is that the date updates 1 second late. To fix this would mean updating a calendar which is much more difficult than updating the time by 1 second.
Another problem is that if the GPS lock is lost i.e. the ‘A’ does not appear in the $GPRMC string, then the clock will freeze.
I’m sure there are many better ways to do all of this. But this is probably the simplest.
Thanks to Geoff for his elegant ‘Parsing’ of the time and date and for his code for the LCD display.
Not so elegant is my code! Feel free to add or improve and comment.
Regards
Talbit
5 '''''''''''''''''''GPS Clock 25 September 2011'''''''''''''
50 GOSUB 11000 ' Initialise the LCD
60 GOSUB 2000 ' Set the PPS & Time Break pins
100 max = 20 ' Maximum nbr of params
130 '
140 DIM arg$(max) ' used to hold the data items
160 OPEN "COM2:19200" AS #1
170 '
200 DO ' loop forever
210 GOSUB 500 ' get the next line
220 IF arg$(0) = "GPRMC" THEN ' GPRMC contains GPS Time
230 IF arg$(2) = "A" THEN ' A means locked on to satellites
235 GPST$=LEFT$(arg$(1), 6) ' Get the Time
237 GPSD$=LEFT$(arg$(9), 6) ' Get the Date
250 GOSUB 2550 ' Update the Time by 1 Second
260 ELSE
270 PRINT "GPS searching..."
280 ENDIF
290 ENDIF
300 GOSUB 1100
310 LOOP
320 ''''''''''''''''''''''''''''''''''''''''''''''''''''''
330 ' subroutine to load the GPS data items into
340 ' the array arg$(). returns with the array full
500 DO ' subroutine start
505 DO WHILE INPUT$(1, #1) <> "$" : LOOP ' wait for the start
510 FOR i = 0 TO max
520 arg$(i) = "" ' clear ready for data
530 DO ' loops until a specific exit
540 x$ = INPUT$(1, #1) ' get the character
550 IF x$ = "," THEN EXIT ' new data item, increment i
560 IF x$ = "*" THEN RETURN ' we have all the data so return
570 arg$(i) = arg$(i) + x$ ' add to the data
580 LOOP ' keep going
590 NEXT i ' increment i
600 PRINT "Corrupt data..." ' exceeded max data items
610 LOOP
1100 DO WHILE PIN(17)=1 : LOOP 'Wait for PPS to go low then send time to LCD
1110 Timer=0 Start timer to see LCD update delay
1120 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''
1130 IF Day =1 THEN 1510
1140 IF Hour =1 THEN 1550
1150 IF Minute=1 THEN 1590
1200 GOSUB 12000 'Send to the LCD
1210 T$=STR$(TIMER)
1220 RETURN ' Start all over again
1500 '''''''''''''''''Time Break Interupts'''''''''''''
1510 PIN(18)=1 'Day Time Break (6 seconds)
1520 SETTICK 6000,2250
1530 GOTO 1200
1540 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
1550 PIN(18)=1 'Hour Time Break (4 seconds)
1560 SETTICK 4000,2250
1570 GOTO 1200
1580 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
1590 PIN(18)=1 'Minute Time Break (2 seconds)
1600 SETTICK 2000,2250
1610 GOTO 1200
2000 ''''''''''''Routine to set the PPS & Time Break pins''''''''''''
2010 SETPIN 17,2 'Make Pin 17 a digital Input (PPS)
2020 SETPIN 18,8 'Make Pin 18 a digital output (Time Break)
2030 PIN(18)=0 'Make sure Pin 18 is low
2040 RETURN
2250 ''''''''''''''''''''''''''Reset Time Break'''''''''''''''
2260 PIN(18)=0
2270 SETTICK 0,0
2280 IRETURN
2545 ''''''''Subroutine to extract the Time and Date''''''''
2550 hh$ = MID$(GPST$, 1, 2) 'Get the hours
2560 mm$ = MID$(GPST$, 3, 2) 'Get the minutes
2570 ss$ = MID$(GPST$, 5, 2) 'Get the seconds
2580 dd$ = MID$(GPSD$, 1, 2) 'Get the days
2590 ll$ = MID$(GPSD$, 3, 2) 'Get the months (ll=months)
2600 yy$ = MID$(GPSD$, 5, 2) 'Get the years
2610 ss = VAL(ss$) 'Change Time to an integer
2620 mm = VAL(mm$)
2630 hh = VAL(hh$)
3000 '''''''''''''Update time by 1 second''''''''''
3010 Minute=0:Hour=0:Day=0
3020 ss=ss+1 'Add 1 second
3030 IF ss<60 THEN 3110 ELSE ss=00
3040 mm=mm+1 ' Increment the minute
3050 Minute=1
3060 IF mm<60 THEN 3110 ELSE mm=00
3070 hh=hh+1 'Increment the hour
3080 Hour=1
3090 IF hh<24 THEN 3110 ELSE hh=00
3100 Day=1
3110 ss$=STR$(ss) 'Change them all back to Strings...
3120 mm$=STR$(mm)
3130 hh$=STR$(hh)
3140 IF LEN(ss$) = 1 THEN ss$ = "0" +ss$ 'Add leading zero
3150 IF LEN(mm$) = 1 THEN mm$ = "0" +mm$
3160 IF LEN(hh$) = 1 THEN hh$ = "0" +hh$
3170 IF LEN(dd$) = 1 THEN dd$ = "0" +dd$
3180 IF LEN(ll$) = 1 THEN ll$ = "0" +ll$
3190 IF LEN(yy$) = 1 THEN yy$ = "0" +yy$
3195 IF LEN(T$) = 2 THEN T$ = "0" +T$
3200 LCD_line1$ = " "+hh$+":"+mm$+":"+ss$+" "+T$
3210 LCD_line2$ = " "+dd$+"/"+ll$+"/20"+yy$+" "
3220 RETURN
10000 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ''''
10010 ' LCD driver for standard 16 x 2 LCDs
10020 ' Geoff Graham, March 2011
10030 '
10040 ' For example: futurlec.com LCD16X2
10050 ' altronics.com.au Z7001
10060 ' jaycar.com.au QP5512
10070 '
10080 ' To use:
10090 ' - execute GOSUB 11000 to initialise display
10100 ' - then load the text strings into the
10110 ' variablesLCD_line1$ and LCD_line2$
10120 ' - execute GOSUB 12000 to display the text
10130 '
10140 ' See the file lcd.pdf for the schematic.
10150 ' Maximite pin 11 is RS, pin 12 is EN
10160 ' pins 13 to 16 are D4 to D7. R/W is grounded
10180 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ''''
10190 '
10200 '
11000 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ''''
11010 ' Initialise the LCD
11020 '
11030 FOR i = 11 TO 16 : SETPIN i, 9 : NEXT i
11040 _LCD_byte = &B0011 : GOSUB 13090 : PAUSE 5 ' reset
11050 _LCD_byte = &B0011 : GOSUB 13090 : PAUSE 5 ' reset
11060 _LCD_byte = &B0011 : GOSUB 13090 : PAUSE 5 ' reset
11070 _LCD_byte = &B0010 : GOSUB 13090 : PAUSE 2 ' 4 bit mode
11080 _LCD_byte = &B00101100 : GOSUB 13000 ' 4 bit, 2 lines
11090 _LCD_byte = &B00001100 : GOSUB 13000 ' display on, no cursor
11100 _LCD_byte = &B00000110 : GOSUB 13000 ' increment on write
11110 _LCD_byte = &B00000001 : GOSUB 13000 ' clear display
11120 RETURN
11130 '
11140 '
12000 ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ''''
12010 ' send the two lines to the LCD
12020 ' the text is in LCD_Line1$ and LCD_Line2$
12030 '
12040 _LCD_byte = &H80 : GOSUB 13000 ' select the 1st line
12050 FOR _LCD = 1 TO 16
12060 _LCD_byte = ASC(MID$(LCD_Line1$, _LCD, 1))
12070 PIN(11) = 1 : GOSUB 13000 ' send the character
12080 NEXT _LCD
12090 _LCD_byte = &B11000000 : GOSUB 13000 ' select the 2nd line
12100 FOR _LCD = 1 TO 16
12110 _LCD_byte = ASC(MID$(LCD_Line2$, _LCD, 1))
12120 PIN(11) = 1 : GOSUB 13000 ' send the character
12130 NEXT _LCD
12140 RETURN
12150 '
12160 '
13000 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ''''
13010 ' Send a byte to the LCD
13020 ' the data to be sent is in _LCD_byte
13030 '
13040 PIN(13) = _LCD_byte AND &B00010000 ' output the 1st 4 bits
13050 PIN(14) = _LCD_byte AND &B00100000
13060 PIN(15) = _LCD_byte AND &B01000000
13070 PIN(16) = _LCD_byte AND &B10000000
13080 PIN(12) = 1 : PIN(12) = 0 ' tell lcd to accept data
13090 ' Entry point to send just 4 bits to the LCD
13100 PIN(13) = _LCD_byte AND &B00000001 ' output the 2nd 4 bits
13110 PIN(14) = _LCD_byte AND &B00000010
13120 PIN(15) = _LCD_byte AND &B00000100
13130 PIN(16) = _LCD_byte AND &B00001000
13140 PIN(12) = 1 : PIN(12) = 0 : PIN(11) = 0 ' tell lcd to accept data
13150 RETURN
Edited by Talbit 2011-11-02 Talbit |