GPS_Uhr.asm


1
;GPS Uhr
2
;2008 Björn Wieck
3
;Dieses Programm liest aus dem Datenstrom eines NMEA-GPS-Empfängers die UTC-Uhrzeit und das
4
;Datum aus und zeigt beides auf einem LCD-Display an.
5
;Das Programm sucht nach der Zeichenfolge $GPRMC und wertet die Enthaltenen Daten aus.
6
;
7
;Typische Zeichenfolge für NMEA: 
8
;$GPRMC,191410,A,4735.5634,N,00739.3538,E,0.0,0.0,181102,0.4,E,A*19
9
;       ^^^^^^                                    ^^^^^^        ^^^
10
;       Uhrzeit                                   Datum         Ende+Prüfsumme
11
12
13
14
.include "m8def.inc"
15
 
16
.def temp = R16
17
.def temp1 = R17
18
.def temp2 = R18
19
.def temp3 = R19
20
.def temp4 = R20
21
 
22
.equ F_CPU = 3685400                             ; Systemtakt in Hz
23
.equ BAUD  = 4800                               ; Baudrate
24
 
25
; Berechnungen
26
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden
27
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate
28
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille
29
 
30
.if ((BAUD_ERROR>10) || (BAUD_ERROR<-10))       ; max. +/-10 Promille Fehler
31
  .error "Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!"
32
.endif
33
34
35
;IRQ-Sprungtabelle
36
.org 0x00
37
    rjmp main        ; Reset Handler
38
    reti ;rjmp EXT_INT0    ; IRQ0 Handler
39
    reti ;rjmp EXT_INT1    ; IRQ1 Handler
40
    reti ;rjmp TIM2_COMP  ; Timer2 Compare Handler
41
    reti ;rjmp TIM2_OVF    ; Timer2 Overflow Handler
42
    reti ;rjmp TIM1_CAPT  ; Timer1 Capture Handler
43
    reti ;rjmp TIM1_COMPA  ; Timer1 CompareA Handler
44
    reti ;rjmp TIM1_COMPB  ; Timer1 CompareB Handler
45
    reti ;rjmp TIM1_OVF    ; Timer1 Overflow Handler
46
    reti ;rjmp TIM0_OVF    ; Timer0 Overflow Handler
47
    reti ;rjmp SPI_STC    ; SPI Transfer Complete Handler
48
    rjmp USART_RXC      ; USART RX Complete Handler
49
    reti ;rjmp USART_UDRE  ; UDR Empty Handler
50
    reti ;rjmp USART_TXC  ; USART TX Complete Handler
51
    reti ;rjmp ADC      ; ADC Conversion Complete Handler
52
    reti ;rjmp EE_RDY    ; EEPROM Ready Handler
53
    reti ;rjmp ANA_COMP    ; Analog Comparator Handler
54
    reti ;rjmp TWSI      ; Two-wire Serial Interface Handler
55
    reti ;rjmp SPM_RDY    ; Store Program Memory Ready Handler
56
57
; Hauptprogramm
58
59
main:
60
    cli
61
    ; Stackpointer initialisieren
62
 
63
    ldi     temp, LOW(RAMEND)
64
    out     SPL, temp
65
    ldi     temp, HIGH(RAMEND)
66
    out     SPH, temp
67
 
68
;IO-Ports initalisieren
69
  ldi temp,0b11111111      ;PortB, Bit 0-7 auf Ausgang stellen
70
  out DDRB, temp
71
  ldi temp,0b00000000      ;PortD, Bit 0-7 auf Eingang stellen
72
  out DDRD, temp
73
74
 
75
    ; Baudrate einstellen
76
 
77
    ldi     temp, HIGH(UBRR_VAL)
78
    out     UBRRH, temp
79
    ldi     temp, LOW(UBRR_VAL)
80
    out     UBRRL, temp
81
 
82
    ; Frame-Format: 8 Bit
83
 
84
    ldi     temp, (1<<URSEL)|(3<<UCSZ0)
85
    out     UCSRC, temp
86
 
87
    sbi     UCSRB, RXCIE                ;Interrupt bei Empfang
88
    sbi     UCSRB, RXEN                 ;RX (Empfang) aktivieren
89
90
    rcall   lcd_init          ;LCD initalisieren
91
  rcall   lcd_clear          ;LCD löschen
92
93
    sei                                 ;Interrupts global aktivieren
94
    
95
loop:
96
;Uhrzeit parsen und auf lcd ausgeben
97
98
    ldi     r30,LOW(GPRMC)            ;den Z-Pointer mit dem Start der GPRMC Bytes laden
99
    ldi     r31,HIGH(GPRMC)
100
  ldd   temp1, Z + 0x01        ;Stunde links
101
  rcall  lcd_data
102
  ldd   temp1, Z + 0x02        ;Stunde rechts
103
  rcall  lcd_data
104
  ldi    temp1, ':'          ;trenner
105
  rcall  lcd_data
106
  ldd   temp1, Z + 0x03        ;Minute links
107
  rcall  lcd_data
108
  ldd   temp1, Z + 0x04        ;Minute rechts
109
  rcall  lcd_data
110
  ldi    temp1, ':'          ;trenner
111
  rcall  lcd_data
112
  ldd   temp1, Z + 0x05        ;Sekunde links
113
  rcall  lcd_data
114
  ldd   temp1, Z + 0x06        ;Sekunde rechts
115
  rcall  lcd_data
116
  ldi temp1, 0b11000000         ;LCD umschalten auf Zeile 2
117
  rcall lcd_command
118
119
120
121
;Datum parsen und auf LCD ausgeben
122
;dieser Teil ist aufwendiger weil eine $GPRMC-Zeile nicht immer gleich lang ist
123
;die Anzahl der Kommas ist immer gleich.
124
 
125
  ldi    temp4,0            ;Kommazähler löschen
126
    ldi     r30,LOW(GPRMC)            ;den Z-Pointer mit dem Start der GPRMC Bytes laden
127
    ldi     r31,HIGH(GPRMC)
128
dateloop1:
129
  ld    temp, Z+          ;Byte holen
130
  cpi    temp, ','          ;und mit (ASCII) Komma vergleichen
131
  brne  dateloop1          ;wenn kein Komma dann nächstes Byte holen
132
  inc temp4              ;Komma gefunden, Kommazähler erhöhen
133
  cpi temp4, 9            ;wenn 9 Kommas gezählt sind
134
  breq readdate            ;Datum auslesen
135
  rjmp dateloop1
136
readdate:
137
  ld    temp1, Z+          ;Tag links
138
  rcall  lcd_data
139
  ld    temp1, Z+          ;Tag rechts
140
  rcall  lcd_data
141
  ldi    temp1, '.'          ;Trenner einfügen
142
  rcall  lcd_data
143
  ld    temp1, Z+          ;Monat links
144
  rcall  lcd_data
145
  ld    temp1, Z+          ;Monat rechts
146
  rcall  lcd_data
147
  ldi    temp1, '.'          ;trenner einfügen
148
  rcall  lcd_data
149
  ld    temp1, Z+          ;Jahr links
150
  rcall  lcd_data
151
  ld    temp1, Z+          ;Jahr rechts
152
  rcall  lcd_data
153
  ldi temp1, 0b10000000        ;LCD umschalten auf Zeile 1
154
  rcall lcd_command
155
  ldi temp1, 0b00000010        ;LCD Cursor home 
156
  rcall  lcd_command
157
158
; etwas Pause damit die Anzeige stabiler ist.
159
160
  rcall  delay5ms
161
  rcall  delay5ms
162
  rcall  delay5ms
163
  rcall  delay5ms
164
  rcall  delay5ms
165
  rjmp loop
166
167
168
169
;Interruptroutine: wird ausgeführt sobald ein Byte über UART empfangen wurde
170
 
171
USART_RXC:
172
    push    temp                        ; temp auf dem Stack sichern
173
    in      temp, sreg                  ; SREG sichern
174
    push    temp
175
    
176
    in      temp, UDR                   ; UART Daten lesen
177
    cpi     temp, '$'                   ; empfangenes Byte mit '$' vergleichen
178
    brne    int_rxc_ende                ; wenn nicht gleich, dann zu int_rxc_ende
179
rxloop1:
180
  sbis  UCSRA, RXC
181
    rjmp rxloop1
182
  in      temp, UDR                   ; UART Daten lesen
183
    cpi     temp, 'G'                   ; empfangenes Byte mit 'G' vergleichen
184
    brne    int_rxc_ende                ; wenn nicht gleich, dann zu int_rxc_ende
185
rxloop2:
186
  sbis  UCSRA, RXC
187
    rjmp rxloop2
188
    in      temp, UDR                   ; UART Daten lesen
189
    cpi     temp, 'P'                   ; empfangenes Byte mit 'P' vergleichen
190
    brne    int_rxc_ende                ; wenn nicht gleich, dann zu int_rxc_ende
191
rxloop3:
192
  sbis  UCSRA, RXC
193
    rjmp rxloop3
194
    in      temp, UDR                   ; UART Daten lesen
195
    cpi     temp, 'R'                   ; empfangenes Byte mit 'R' vergleichen
196
    brne    int_rxc_ende                ; wenn nicht gleich, dann zu int_rxc_ende
197
rxloop4:
198
  sbis  UCSRA, RXC
199
    rjmp rxloop4
200
    in      temp, UDR                   ; UART Daten lesen
201
    cpi     temp, 'M'                   ; empfangenes Byte mit 'M' vergleichen
202
    brne    int_rxc_ende                ; wenn nicht gleich, dann zu int_rxc_ende
203
rxloop5:
204
  sbis  UCSRA, RXC
205
    rjmp rxloop5
206
    in      temp, UDR                   ; UART Daten lesen
207
    cpi     temp, 'C'                   ; empfangenes Byte mit 'C' vergleichen
208
    brne    int_rxc_ende                ; wenn nicht gleich, dann zu int_rxc_ende
209
  rjmp  read_gprmc          ;$GPRMC-String wurde erkannt
210
211
int_rxc_ende:
212
213
    pop     temp
214
    out     sreg, temp                  ; SREG wiederherstellen
215
    pop     temp                        ; temp wiederherstellen
216
    reti
217
218
219
220
221
;$GPRMC-String wurde erkannt, String einlesen und ins SRAM kopieren
222
  
223
read_gprmc:
224
225
    ldi     r30,LOW(GPRMC)            ; den Z-Pointer mit dem Start der GPRMC Bytes laden
226
    ldi     r31,HIGH(GPRMC)
227
rxloop6:
228
  sbis  UCSRA, RXC          ; UART Daten lesen
229
  rjmp rxloop6
230
  in    temp, UDR
231
  cpi    temp, '*'          ;empfangenes Byte mit '*' (Stringende) vergleichen
232
  breq    int_rxc_ende              ;wenn gleich, dann zu int_rxc_ende
233
  st    Z+,temp            ;empfangenes Byte im SRAM ablegen und Z erhöhen
234
  rjmp rxloop6
235
236
237
238
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
239
;;                 LCD-Routinen                ;;
240
;;                 ============                ;;
241
;;              (c)andreas-s@web.de            ;;
242
;;                                             ;;
243
;; 4bit-Interface                              ;;
244
;; DB4-DB7:       PB0-PB3                      ;;
245
;; RS:            PB4                          ;;
246
;; E:             PB5                          ;;
247
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
248
249
 
250
 
251
 ;sendet ein Datenbyte an das LCD
252
lcd_data:
253
           mov temp2, temp1             ;"Sicherungskopie" für
254
                                        ;die Übertragung des 2.Nibbles
255
           swap temp1                   ;Vertauschen
256
           andi temp1, 0b00001111       ;oberes Nibble auf Null setzen
257
           sbr temp1, 1<<4              ;entspricht 0b00010000
258
           out PORTB, temp1             ;ausgeben
259
           rcall lcd_enable             ;Enable-Routine aufrufen
260
                                        ;2. Nibble, kein swap da es schon
261
                                        ;an der richtigen stelle ist
262
           andi temp2, 0b00001111       ;obere Hälfte auf Null setzen 
263
           sbr temp2, 1<<4              ;entspricht 0b00010000
264
           out PORTB, temp2             ;ausgeben
265
           rcall lcd_enable             ;Enable-Routine aufrufen
266
           rcall delay50us              ;Delay-Routine aufrufen
267
           ret                          ;zurück zum Hauptprogramm
268
269
 ;sendet einen Befehl an das LCD
270
lcd_command:                            ;wie lcd_data, nur ohne RS zu setzen
271
           mov temp2, temp1
272
           swap temp1
273
           andi temp1, 0b00001111
274
           out PORTB, temp1
275
           rcall lcd_enable
276
           andi temp2, 0b00001111
277
           out PORTB, temp2
278
           rcall lcd_enable
279
           rcall delay50us
280
           ret
281
282
 ;erzeugt den Enable-Puls
283
lcd_enable:
284
           sbi PORTB, 5                 ;Enable high
285
           nop                          ;3 Taktzyklen warten
286
           nop
287
           nop
288
           cbi PORTB, 5                 ;Enable wieder low
289
           ret                          ;Und wieder zurück                     
290
291
 ;Pause nach jeder Übertragung
292
delay50us:                              ;50us Pause
293
           ldi  temp1, $42
294
delay50us_:dec  temp1
295
           brne delay50us_
296
           ret                          ;wieder zurück
297
298
 ;Längere Pause für manche Befehle
299
delay5ms:                               ;5ms Pause
300
           ldi  temp1, $21
301
WGLOOP0:   ldi  temp2, $C9
302
WGLOOP1:   dec  temp2
303
           brne WGLOOP1
304
           dec  temp1
305
           brne WGLOOP0
306
           ret                          ;wieder zurück
307
308
 ;Initialisierung: muss ganz am Anfang des Programms aufgerufen werden
309
lcd_init:
310
           ldi  temp3,50
311
powerupwait:
312
           rcall  delay5ms
313
           dec  temp3
314
           brne  powerupwait
315
           ldi temp1, 0b00000011        ;muss 3mal hintereinander gesendet
316
           out PORTB, temp1             ;werden zur Initialisierung
317
           rcall lcd_enable             ;1
318
           rcall delay5ms
319
           rcall lcd_enable             ;2
320
           rcall delay5ms
321
           rcall lcd_enable             ;und 3!
322
           rcall delay5ms
323
           ldi temp1, 0b00000010        ;4bit-Modus einstellen
324
           out PORTB, temp1
325
           rcall lcd_enable
326
           rcall delay5ms
327
           ldi temp1, 0b00101000        ;noch was einstellen...
328
           rcall lcd_command
329
           ldi temp1, 0b00001100        ;...nochwas...
330
           rcall lcd_command
331
           ldi temp1, 0b00000100        ;endlich fertig
332
           rcall lcd_command
333
           ret
334
335
 ;Sendet den Befehl zur Löschung des Displays
336
lcd_clear:
337
           ldi temp1, 0b00000001   ;Display löschen
338
           rcall lcd_command
339
           rcall delay5ms
340
           ret
341
342
343
344
        .DSEG
345
GPRMC:   .BYTE   200                     ;$GPRMC-String im SRAM