Forum: Mikrocontroller und Digitale Elektronik Programmierbeispiele


von Gregi (Gast)


Lesenswert?

Hallo!

Ich würde mich über Programmierbeispiele und -aufgaben im 
Assembler-Bereich erfreuen!

Speziell Aufgaben mit Musterlösungen usw. würden meinen Fleiß noch mehr 
anspornen, um die µC's noch genauerer kennenzulerenen.

Die Seite ist übringens wirklich super für Einsteiger!

Gregi

von Ralf Künstler (Gast)


Lesenswert?

;*********************************************************************** 
****
;PZE4, die PersonalZeitErfassung, smatro, 08/1998
;*********************************************************************** 
****
;Pinbelegungen:
;PA0 39  = CE des LCD-Displays (2*16)
;PA1 38  = 1.Spalte Keyboard über R1k (1,4,7,*)  & R/W Display
;PA2 37  = 2.Spalte Keyboard über R1k (2,5,8,0)  & C/D Display
;PA3 36  = 3.Spalte Keyboard über R1k (3,6,9,#)
;PA4 35  = 1.Zeile  Keyboard über Dio (1,2,3)  & Display D4
;PA5 34  = 2.Zeile  Keyboard über Dio (4,5,6)  & Display D5
;PA6 33  = 3.Zeile  Keyboard über Dio (7,8,9)  & Display D6
;PA7 32  = 4.Zeile  Keyboard über Dio (*,0,#)  & Display D7

;PB0 01  = RST-Chipkarte (braun)
;PB1 02  = CLK-Chipkarte (orange)
;PB2 03  = I/O-Chipkarte (gelb)
;PB3 04  = VPP-Chipkarte (grün)
;PB4 05  = CS Flash
;PB5 06  = PRG
;PB6 07  = PRG + SO(Flash) + SI(Flash)
;PB7 08  = PRG + SCK(Flash)

;PD0 10  = RxD
;PD1 11  = TxD
;PD2 12  = LED & Taster
;PD3 13  = LED & Taster
;PD4 14  = LED & Taster
;PD5 15  =
;PD6 16  = LED (High=On), Piper über Elko
;PD7 17  = Eingang Highpulse der Funkuhr


.equ  RS   = 0x60    ;Start des RAM-Bereiches

.equ    bit0  = 0x01    ;nötig für die Befehle SBR und CBR
.equ    bit1  = 0x02    ;dies sind eigentlich versteckte
.equ    bit2  = 0x04    ;ANDI und ORI-Befehle
.equ    bit3  = 0x05
.equ    bit4  = 0x10
.equ    bit5  = 0x20
.equ    bit6  = 0x40
.equ    bit7  = 0x80

;Kommandos für die Datenübertragung über die ser. Schnittstelle 
(Datentyp)
.equ  DFLASH  = 0b10000000  ;Achtung: in Bits 0-6 = Nr. des 4k-Blocks
.equ    DSYS  = 1
.equ  DRAM  = 121
.equ    DEEPROM  = 122
.equ  DROM   = 123
.equ    ALLFL   = 124
.equ    ECHO    = 127    ;Sende Datentyp zurück, der in den Daten st.

;*********************************************************************** 
****
;Daten, welche eigentlich im Eeprom liegen sollten
.equ  selbst  = 0x10    ;Eepr0x00, eigene Gerätenummer
.equ  wohin  = 0x01    ;Eepr0x01, zu welchem Gerät die Daten hin s.

;*********************************************************************** 
****
.include "8515def.inc"

;Reset- und Interruptvektortabelle:

  rjmp  reset    ;Reset Handle
  rjmp  noint    ;IRQ0  Handle
  rjmp  noint    ;IRQ1  Handle
  rjmp  noint    ;Tim1  Capture Handle
  rjmp  noint    ;Tim1  compareA Handle
  rjmp  noint    ;Tim1  compareB Handle
  rjmp  noint    ;IRQ0  Handle
ti0:  rjmp  timer0int  ;Tim0  overflow Handle
;  rjmp  noint    ;Spi   Handle
;  rjmp  noint    ;Uart Rx complete Handle
;  rjmp  noint    ;Udr empty Handle
;  rjmp  noint    ;Uart Tx complete Handle
;  rjmp  noint    ;Analog-Compare Handle

;************************** Registervariablen 
********************************
.def    stunde  = r1
.def  dcflagl = r2    ;Zaehler, wie lange Funkuhr/Lowpegel
.def   dcflagh = r3    ;Zaehler, wie lange Funkuhr/Highpegel
.def    dcfoldl = r4    ;Zaehler low, alte Sekunde
.def    dcfoldh = r5     ;Zaehler high, alte Sekunde
.def    parity  = r6    ;Parityzwischenspeicher (Min, Stu, Rest)
.def    int1    = r7
.def    int2    = r8    ;Interrupt-Sicherungsregister
.def    temp5   = r9
.def    temp6   = r10
.def    d32cnt  = r11    ;32ms-Counter für die Datenübertragung
.def  mshigh  = r12    ;High-Byte des 32ms-Counter (1831 je Min)

.def  temp1   = r16
.def  temp2   = r17    ;temporaere Variablen für alle Zwecke
.def  temp3  = r18
.def    temp4   = r19

.def  mslow  = r20    ;msxxxx ist der Millisekundencounter
.def  minute  = r21
.def    dcfsek  = r22

.def    flag1  = r23    ;flag1-Bits: für den Datentransfer, u.a.

;Register r24-r31 werden für 16-Bit-Operationen benutzt

;******************************* RAM-Adressen 
*********************************
.equ  dcfmin  = 0x60
.equ  dcfstu  = 0x61
.equ  dcftag  = 0x62    ;DCF-Werte werden vom UP dcflook beschrieben
.equ  dcfmon  = 0x63
.equ  dcfjahr = 0x64
.equ    dcfwota = 0x65    ;Bits0-2 = Wochentag (1=Mo), 
Bit7=Sommerzeitbit
.equ  dcfchk  = 0x66    ;Pruefsumme 2er aufeinanderfolgender Minuten

.equ    tag     = 0x67    ;Tag des Monats
.equ  mon     = 0x68    ;interne Uhr: Monat
.equ  jahr    = 0x69    ;Jahr
.equ  wota  = 0x6a    ;0-2 = Wochentag, Bit7=1=Sommerz. 6=1=DCFsync.
.equ    lastms  = 0x6b    ;Merker fürs's letzte mslow

.equ    pipton  = 0x6c    ;Zaehler für die Tonausgabe

.equ    lastkey = 0x6d    ;Merker für gedr. Taste

.equ    bufl    = 0x6e
.equ    bufh    = 0x6f    ;Stringadresse für die LCD-Laufschrift
.equ    lcdunten= 0x70          ;Puffer fuer untere LCD-Zeile

.equ    outputl = 0x80    ;Adresse (Low-Teil) zur Ausgabe
.equ  outputh = 0x81    ;(Ausgabeadresse: seriell, LCD, Flash, RAM...)

.equ  pageadr = 0x82    ;Flash-Pageadresse (*2, denn Bit0=Buffer-adr8)
.equ  flbufadr= 0x84    ;Flash-Buffer-Adresse

.equ    derrcnt = flbufadr+1  ;Errorcounter
.equ  dptrl   = derrcnt+1  ;2 Bytes Datenpointer (1=anwen,2=vonwem...)
.equ  dptrh  = dptrl+1
.equ    dbytes  = dptrh+1  ;Gesamtzahl der zu übertr. Bytes
.equ    dpcrc  = dbytes+2  ;Prüfsumme
.equ    dtyp    = dpcrc+2  ;Datentyp (Art der Daten des Transfers)
.equ    dwohin  = dtyp+1  ;Adresse, wo die Daten Hin sollen
.equ    flblock = dwohin+1  ;Flash-Blocknr. (für ges. Flash aussenden)
.equ    dbuf    = flblock+1  ;Start des Daten-Buffers (128 Bytes)
.equ  dbufend = dbuf+138  ;Header + 128 Datenbytes + CRC + Reserve


;*********************************************************************** 
*******
;****************** Datenübertragung über die ser. Schnittstelle 
**************
;*********************************************************************** 
*******
;Das DÜ-Format: 0A 34 CDEF ...... 7788
;1.    0A: 1 Byte, Adr. des Gerätes, wohin die Sendung soll (FE=an alle)
;2.    34: 1 Byte, Adresse des Gerätes, von welcher die Sendung kommt
;3.  CDEF: 2 Bytes, Zahl aller Bytes dieses Blockes (ab Daten bis 
CRC-Ende)
;5.    99: 1 Byte, Typ der Daten(7=1=Flash, SYS=1, RAM=121, EEPROM=122)
;6.  ....: die eigentlichen Daten (max 0x8000)
;X.  7788: Prüfsumme (2Bytes:  an,von,Bytezahl und Daten)

;Die Bedeutung der Einzelbits des Flagregisters flag1:
;Bit0=1: TX-Betrieb ist zur Zeit aktiv!
;Bit1=1: RX-Betrieb aktiv!
;Bit2=1: min 32ms war Ruhe auf der Leitung, Empf./Sendung darf starten
;Bit7=1: dann Systemdaten automatisch alle x ms ausgegeben

;*********************************************************************** 
*******
due:  sbrc  flag1,0    ;skip, wenn TX-Daten nicht zu senden sind
  rjmp  dsend    ;TX-Daten senden (nächstes Byte)

  sbrc  flag1,1
  rjmp  receiv    ;wenn zur Zeit Empfang läuft

  sbrc  flag1,2
  rjmp  wzeit_ok    ;Pflicht-Ruhezeit um, nach Zeichen sehen

;*********************************************************************** 
*******
;wir befinden uns noch in der Pflichtruhezeit:
  sbis  USR,7    ;Bit7=1: Zeichen im Buffer
  rjmp  nochar    ;wenn kein Zeichen vorhanden

;innerhalb der Ruhezeit wurde 1 Byte empfangen (verbotener Weise)
  in    temp3,UDR    ;Zeichen verwerfen
dinit2:  rjmp  dinit

;prüfen, ob Ruhezeit schon lange genug
nochar:  mov   temp1,d32cnt
  cpi   temp1,2    ;schon min 32ms (1-2*32) vorbei?
  brcc  de3    ;ja!
  ret      ;nein
de3:  sbr   flag1,bit2  ;Kennung, daß ab jetzt RX/TX erlaubt ist
  ret

;*********************************************************************** 
*******
;Empfang: (min 1 Zeichen wurde schon empfangen)
receiv:  mov   temp1,d32cnt
  cpi   temp1,2    ;schon zu lange Pause?
  brcc  rec8    ;ja: nachsehen, ob Datensatz gültig
  sbis  USR,7    ;Bit7=1: Zeichen im Buffer
  ret
  in    temp3,USR    ;Fehlerbits 3 & 4
  andi  temp3,0x18
  brne  dinit2    ;Frame- oder Overloaderror
  in    temp3,UDR    ;Zeichen laden
rec2:  clr   d32cnt    ;32ms-Counter (TX/RX-Wartezaehler) = 0
  rcall dpinc    ;Datenpointer incrementieren
  cpi   r26,dbufend  ;Datenpufferende schon überschritten?
  breq  dinit2    ;Fehler!!! (zu viele Bytes empfangen)
  st    x,temp3    ;und Daten im Buffer ablegen
  ret

;*********************************************************************** 
*******
;längere Pause nach dem Datenempfang, Datenblock prüfen, zuerst, ob
;die im Block angeg. Datenlänge mit der eingelesenen übereinstimmt
rec8:  ldd   r26,y-RS+dptrl
  ldd   r27,y-RS+dptrh
  ldd   r30,y-RS+dbuf+3  ;Zahl der Bytes laden (Low-Wert)
  ldd   r31,y-RS+dbuf+2
  sub   r26,r30
  sbc   r27,r31
  tst   r27
  brne  dinit2    ;Fehler: falsche Berechn. Bytes zu Blockende
  cpi   r26,dbuf+3
dinit3:  brne  dinit2

;Prüfsumme testen (mitgeschickte mit ausgerechneter)
  rcall dpinc
  sbiw  r26,2    ;Ptr zeigt jetzt auf's 1.Byte der CRC
  ldi   r30,low(dbuf)
  ldi   r31,high(dbuf)  ;z=Bufferanfang
  ldi   temp1,0
  ldi   temp2,0
rec10:  ld    temp3,z+
  add   temp1,temp3
  brcc  rec11
  inc   temp2
rec11:  cp    r30,r26
  brne  rec10
  cp    r31,r27
  brne  rec10    ;noch nicht beim CRC-Pointer angelangt

  ld    temp3,x+
  cp    temp3,temp2  ;Vergleich eingeles. mit  berechn. Pr.-summe
  brne  dinit2    ;High-Byte stimmt nicht!
  ld    temp3,x+
  cp    temp3,temp1
  brne  dinit2    ;Low-Byte stimmt nicht!

;hier Empfang der Bytes OK!, Prüfsumme und CRC waren in Ordnung
  ldi   temp1,0
  std   y-RS+flblock,temp1;erst evt. laufende allFlashausgabe stoppen
  ldd   temp1,y-RS+dbuf+1 ;der Absender ist auch die neue Adresse
  std   y-RS+dwohin,temp1
  ldd   temp1,y-RS+dbuf+4  ;Datentyp laden
  cpi   temp1,ECHO
  brne  rec20    ;war kein Echo-Befehl
  ldd   temp1,y-RS+dbuf+5 ;der Befehl, welcher vom Echo angefordert wur.
  std   y-RS+dtyp,temp1  ;nach Pflichtruhezeit diesen Befehl ausf.
  rjmp  recend

rec20:  cpi   temp1,ALLFL  ;Befehl, alle Flash-4k-Blöcke senden?
  brne  rec30
  rcall allflash
  rjmp  recend

rec30:
recend:  rjmp  dinit
;*********************************************************************** 
*******
;die Pflichtwarteezeit wurde eingehalten, nachschauen, ob 1.Zeichen da 
ist,
;oder ein Block zu senden ist (in dtyp steht Wert >0)
wzeit_ok:
  sbis  USR,7    ;Bit7=1:  Zeichen im Buffer
  rjmp  wz4    ;wenn kein Zeichen empfangen wurde

  in    temp3,USR    ;Fehlerbits 3 & 4
  andi  temp3,0x18
  brne  dinit3    ;Frame- oder Overloaderror
  in    temp3,UDR    ;1.Zeichen laden
  ldi   temp1,low(dbuf-1)  ;Adresse des Datenpuffer (dann pre-inc)
  std   y-RS+dptrl,temp1
  ldi   temp1,high(dbuf-1);wo die Daten hin sollen
  std   y-RS+dptrh,temp1
  cpi   temp3,0xfe  ;Code für Sendung an alle
  breq  adrok
  cpi   temp3,selbst  ;Code für Sendung an mich
  breq  adrok
  rjmp  dinit    ;1.Zeichen weder an alle(FE) noch an mich

;das 1.Z. wurde empfangen, und es stimmt sogar (0xfe oder meine Ge.-Nr)
adrok:
  sbr   flag1,bit1  ;Kennung, RX-Betrieb startet
  rjmp  rec2    ;und Z. normal abspeichern!

wz4:  ;kein Zeichen im Buffer, aber in dtyp nachsehen ob Block zu senden 
ist
  ldd   temp1,y-RS+dtyp
  tst   temp1    ;liegt schon 'ne Sendung an?
  brne  dsstart

  ldd   temp1,y-RS+flblock
  tst   temp1    ;flblock <> 0: Gesamtflashausgabe läuft
  breq  wz6    ;keine solche!

  ;Gesamtflash-Ausgabe (Nr. des 4k-Block steht in fblock)
  bst   d32cnt,4    ;Bit4  (16*32ms) lange Ruhezeit schon um?
  brtc  wz6    ;nein
  dec   temp1
  std   y-RS+flblock,temp1;beim nächsten Mal Block tiefer senden
  ori   temp1,128
  std   y-RS+dtyp,temp1
  rjmp  dsstart

wz6:  bst   flag1,7
  brts  asend    ;wenn autom. Sys.-datenausgabe eingeschaltet

wzret:  ret      ;nein

  ;Zeit abgelaufen? (Vorbereitung für neuen System-Datensatz senden)
asend:  in    temp1,TCNT0  ;letzten Wert des 32ms-Counters laden
  andi  temp1,63    ;Zufallswert (0..63, 0..2sek)
  add   temp1,d32cnt
  brcc  wzret    ;Wartezeit von 6-8 sek.
  ldi   temp1,DRAM  ;Datentyp RAM-Ausgabe
  std   y-RS+dtyp,temp1
  ldi   temp1,0xfe  ;Sendung immer an alle
  std   y-RS+dwohin,temp1

dsstart:
  sbr   flag1,bit0  ;Kennung, Datensendung starten

;*********************************************************************** 
*******
dsend:  sbis  USR,5    ;skip if bit 5 in USR is set (Tx ist frei)
  ret      ;Ende (Zeichen noch nicht ausgesendet)
  clr   d32cnt    ;32ms-Counter (TX/RX-Wartezaehler) = 0
  rcall dpinc    ;Datenpointer+1
  tst   r27    ;schon >255?
  brne  ds8    ;ab 256 sowieso nur Daten
  cpi   r26,1    ;1.Byte (an wen)?
  brne  ds2

;noch vor Aussendung des 1.Bytes einige Initialisierungen
  ldd   temp3,y-RS+dtyp  ;Datentyp laden
  ldi   r26,150+5    ;Anz. der zu übertr. Bytes (RAM)
  ldi   r27,0
  cpi   temp3,DRAM
  breq  ds3

  ldi   r26,5    ;Offset, denn 5Bytes Header + 2Bytes CRC
  ldi   r27,2    ;2*256=512 Bytes (Eepromgröße)
  cpi   temp3,DEEPROM
  breq  ds3

  ldi   r27,16    ;Flash: Blockgröße = 16 * 256 Bytes = 4096

ds3:  rcall dbd4    ;Zahl der Bytes merken
  ldd   temp1,y-RS+dwohin ;wohin die Daten sollen (Adressat)
  cpi   temp1,255    ;Kennung, daß es die Default-Adresse sein soll
  brne  dsout
  ldi   temp1,wohin  ;Default-Adresse
dsout:  ldd   r26,y-RS+dpcrc
  ldd   r27,y-RS+dpcrc+1
  add   r26,temp1
  brcc  dso2
  inc   r27
dso2:  rcall crc2    ;und neue Prüfsumme merken
dso3:  out   UDR,temp1
  ret

ds2:  ldi   temp1,selbst
  cpi   r26,2    ;2.Byte (selbst)?
  breq  dsout
  ldd   temp1,y-RS+dbytes+1
  cpi   r26,3    ;3.Byte (Bytezahl, Highwert)?
  breq  dsout
  ldd   temp1,y-RS+dbytes
  cpi   r26,4    ;4.Byte (Bytezahl, Lowwert)?
  breq  dsout
  ldd   temp1,y-RS+dtyp  ;Datentyp (Befehl)
  cpi   r26,5    ;5.Byte (Typ)
  brne  ds8
  rcall dbdec    ;Bytezähler ab 5.Byte decr.
  rjmp  dsout

ds8:  rcall holedatenbyte  ;Daten ab dem 6.Byte
  rcall dbdec    ;Bytezähler decrementieren (ab 5.Byte)
  tst   r27    ;Highbyte noch >0?
  brne  dsout    ;ja
  cpi   r26,2    ;letzten beiden Bytes (Checksumme)?
  brcc  dsout    ;nein
  ldd   temp1,y-RS+dpcrc+1
  cpi   r26,1    ;vorletzte Byte (=Prüfsumme/High)?
  breq  dso3    ;ja, ohne Prüfsummenberechn. ausgeben

;vor dem Aussenden des letzten Bytes noch einiges initialisieren
  ldd   temp1,y-RS+dpcrc
  ldi   temp2,0
  std   y-RS+dtyp,temp2  ;Datentyp=0: keine ausstehende Datensendung
;  ldi   temp2,255
;  std   y-RS+dwohin,temp2  ;Kennung, daß nächste Sendung an Defaultadr.
  rcall dinit
  rjmp  dso3    ;nach der Initialis. noch letzt. Z. ausgeben

;*********************************************************************** 
*******
;div. Utis für den seriellen Datenstrom  (Initialisierungen u.s.w.)

dpinc:  ;Datenstrom-Datenpointer 1 weiter
  ldd   r26,y-RS+dptrl
  ldd   r27,y-RS+dptrh
  adiw  r26,1    ;Zero gesetzt, wenn jetzt 0
dpi4:  std   y-RS+dptrl,r26
  std   y-RS+dptrh,r27
  ret

;*****
dbdec:  ;Datenstrom-Byte-Zähler -1
  ldd   r26,y-RS+dbytes
  ldd   r27,y-RS+dbytes+1
  sbiw  r26,1
dbd4:  std   y-RS+dbytes,r26
  std   y-RS+dbytes+1,r27
  ret

;*****
dinit:  clr   d32cnt    ;32ms-Counter = 0 (neue Wartezeit beginnt)
  in    r26,UDR    ;Zeichen verwerfen
  ldi   r26,0
  ldi   r27,0
  andi  flag1,0b11111000  ;TX-Sendung fertig, Ruhezeit muß folgen
  rcall dbd4    ;Byte-Zähler rücksetzen
  rcall dpi4    ;Ptr auch (bei Sendestart beginnt's bei 1)
crc2:  std   y-RS+dpcrc,r26
  std   y-RS+dpcrc+1,r27
ret1:  ret

;*********
;1 Datenbyte zum Senden holen, wahlweise vom (ext.)Flash, Ram oder 
Eeprom
holedatenbyte:
  ldi   r30,low(0-6)  ;5 Bytes Header weg (1.Datenbyte>z=0)
  ldi   r31,high(0-6)
  add   r30,r26
  brcc  hob2
  inc   r31
hob2:  add   r31,r27

;Z zeigt auf Byte-Nr. welches zu holen ist
  ldd   temp2,y-RS+dtyp
  sbrc  temp2,7    ;skip, wenn nicht Datentyp Flash (7=1)
  rjmp  flbyte    ;wenn's ein Flash

  ld    temp1,z    ;aus RAM-Zelle (z) Byte holen
  cpi   temp2,DRAM  ;Datentyp RAM?
  breq  ret1

  mov   temp1,r30    ;bleibt noch Eeprom übrig:
  mov   temp2,r31    ;z zeigt auf Adresse im Eeprom
  rjmp  eeread

flbyte: ;Flashbyte nach temp1 laden (dtyp, Bits0-6 ist 
Pageadresse*16,PA4-PA10)
  rcall fl_ok    ;noch busy?
  brpl  flbyte    ;ja: weitert warten!
  ldd   temp1,y-RS+dtyp
  mov   temp4,temp1  ;PA4...PA10
  swap  temp4
  andi  temp4,0x07  ;UP verlangt in temp4 PA8-PA10
  swap  temp1
  andi  temp1,0xf0  ;denn unteren Bits schon in r31
  add   r31,temp1    ;OK: Falshadr. in z & temp4
  rcall flbrd    ;Flashbyteread
  rjmp  foend

;*********************************************************************** 
*******
;Timer 0 Interrupt wird alle 32,768ms aufgerufen (8Mhz-Quarz)
timer0int:
  in    int1,SREG
  inc   d32cnt    ;32ms-Zaehler fuer Datenübertragung nötig
  dec   mslow    ;ms-Zaehler - 1
  brne  t0end
        dec   mshigh
  brpl  t0end    ;1831 gezaehlt, eine min ist um
  ldi   mslow,high(1831)  ;1831: nur 1.792ms fehlen je Minute
  mov   mshigh,mslow  ;(Mist: r0-r15 geht nicht mit ldi)
  ldi   mslow,low(1831)    ;und wieder von vorn anfangen
  inc   minute
t01:  cpi   minute,60    ;Stunde voll?
  brcs  t0end
  ldd   minute,y-RS+wota
  cbr   minute,bit6  ;löschen (Zeit nicht mehr DCF-syncr.)
  std   y-RS+wota,minute
  ldi   minute,0    ;0te Min. und naechste Stunde
  inc   stunde    ;Datumskorrektur außerhalb Int

t0end:  sbis  PIND,7    ;ueberspringe, wenn Funk(pind7) high
  rjmp  t02    ;wenn low-Pegel
  and   dcflagl,dcflagl  ;alte High/Lowwerte schon abgesp.
  breq  t04
  mov   dcfoldl,dcflagl  ;vorherige High-/Lowwerte merken
  mov   dcfoldh,dcflagh
  clr   dcflagh
t04:  inc   dcflagh    ;diesen High-Pegel merken
  clr   dcflagl    ;danach Lowpegelzaehler bei 0
  rjmp  t03
t02:  inc   dcflagl    ;merken: Lowpegel an PORTD-7

t03:  out   SREG,int1
noint:  reti


;*********************************************************************** 
*******
;****************** Hauptprogrammeinsprung nach Reset 
*************************
;*********************************************************************** 
*******
str1:  .db   "     smarti4 - die Zeiterfassung, smartronic 1998",255
  .dw   str1*2

reset:
  ldi   temp1,0xff  ;Watchdog kommt nach 2 Sek
  out   WDTCR,temp1

        ldi   temp1,low(RAMEND)
        out   SPL,temp1         ;init Stack Pointer Low
        ldi   temp1,high(RAMEND)
        out   SPH,temp1         ;init Stack Pointer High

  ldi   r28,RS    ;Pointer Y immer am RAM-Anfang
  ldi   r29,0

  ldi   temp1,51
  out   UBRR,temp1        ;9600 Baud
  ldi   temp1,0x18
  out   UCR,temp1   ;Rx/Tx enable
  ldi   temp1,0x20
  out   MCUCR,temp1  ;Sleep-Mode enabled

  ldi   temp1,0b00111111  ;SI=pull-up(PB5), PB4=Out (CS/Flash)
  out   PORTB,temp1
  ldi   temp1,0b11010000  ;7=SCK, 6=SO,SI, 4=CS  (Flash)
  out   DDRB,temp1

  ldi   temp1,0b01011100  ;PD6=LED/Piper (1=Output)
  out   DDRD,temp1
  ldi   temp1,0b00000111  ;1=high (wenn DDRD=Input, dann pull-up)
  out   PORTD,temp1

  ldi   temp1,5
  out   TCCR0,temp1       ;T0: CLK/1024
  ldi   temp1,2
  out   TIMSK,temp1  ;T0: Int frei

  ldi   temp1,low(serout)  ;Adresse für alle auszugebenden Zeichen
  ldi   temp2,high(serout)
  std   y+outputl-RS,temp1
  std   y+outputh-RS,temp2

  sei      ;set enable interrupt

  rcall lcdinit    ;LCD-Display erstmalig initalisieren
  rcall pip
  rcall kb    ;Tastaturport initialisieren
  rcall fl_ok
  rcall hout

  ldi   temp1,low(str1*2)  ;ganz wichtig: ROM-Adressen immer *2!!
  std   y-RS+bufl,temp1
  ldi   temp1,high(str1*2)
  std   y-RS+bufh,temp1

  ldi   flag1,128    ;Sysdaten automatisch senden!
  rcall dinit

  rcall lcducls

  ldi   temp2,0
  std   y-RS+dtyp,temp2
  std   y-RS+flblock,temp2

;************************** Programm-Hauptschleife 
****************************
mainloop:     ;zuerst die zeitkritischen Funktionen

  wdr      ;Watchdog ruecksetzen
  rcall due    ;Datentransfer (Byte für Byte) pruefen

  in    temp1,TCNT0  ;letzten Wert des 32ms-Counters laden
  andi  temp1,0xf0  ;jeden 16. Zaehlerstand ausfiltern (16*125us)
  ldd   temp2,y-RS+lastms
  cp    temp1,temp2
  std   y-RS+lastms,temp1
  breq  mainloop

;zeitunkritische Funktionen (Aufruf nur alle 2ms)
  rcall kb    ;Tastatur abfragen, Taste in lastkey ablegen
  rcall kstring    ;evt. Tastendr. im Buffer ablegen (lcdunten)
  rcall befehl    ;Buffer(lcdu) auf Befehl prüfen + evt. ausf.

  rcall strout    ;Laufschrift auf ob. Zeile ausgeben
  rcall ton    ;evt. Tonausgabe (Pegel umschalten)
  rcall dcflook    ;Funkuhr-High-Low-Impulse ansehen + abspei.
  rcall uhr    ;schauen, ob nach 23:59, dann Datum korr.

  ldd   temp1,y-RS+lastms  ;ist alle 1000*256*125ns  = 0
  tst   temp1
  brne  mainloop    ;branch, wenn noch nicht 32768usek vorbei

;zeitunkritische Funktionen (Aufruf nur alle 32.7ms)



  mov   temp1,mslow  ;Int-Counter laden (*33ms)
  andi  temp1,7    ;*33ms
  brne  mainloop    ;noch nicht vorbei

;zeitunkritische Funktionen (Aufruf nur alle x * 33ms)



  rcall laufschrift


  mov   temp1,mslow  ;Int-Counter laden (*33ms)
  andi  temp1,31    ;*33ms
  brne  mainloop    ;noch nicht vorbei

;zeitunkritische Funktionen (Aufruf nur alle 32 * 32.768ms = 1.048sek)


  rjmp  mainloop

;*********************************************************************** 
*******
;************************** allgemeine Routinen 
*******************************
;*********************************************************************** 
*******
;Im Buffer (lcdunten) nachsehen, ob ein gültiger Befehl vom Keyboard 
oder
;von der ser. Schnittstelle eingetroffen ist, diesen dann ausführen
befehl:
  ldi   r27,0
  ldi   r26,lcdunten+16  ;Ende des Buffer+1
  ldi   temp2,16
bf10:  ld    temp1,-x    ;vom Bufferende her einlesen
  cpi   temp1,'#'    ;denn Befehl erst gültig mit #-Bestätigung!
  breq  bf40    ;dann Befehl suchen und ausf.
  cpi   temp1,' '
  brne  bfret    ;anders Z. als ' ' oder '#': erstmal Ende
  dec   temp2
  brne  bf10    ;wenn noch nicht alle 16Z. rückwärts
bfret:  ret

;Am Ende der Eingabe stand ein #, jetzt suchen, ob diese Ziffernkette 
exist.
bf40:  ldi   r30,low(beftab*2)
  ldi   r31,high(beftab*2)

bf42:  mov   temp2,r26
  subi  temp2,lcdunten-1
  ldi   r26,lcdunten  ;Länge des String im Buffer
bf41:  ld    temp1,x+
  lpm
;  mov   temp3,r0
;  cpi   temp3,'#'
;  breq  bfound    ;Vergleich stimmt
  cp    temp1,r0    ;Vergleich Eingabe/ROM
  brne  bf44    ;ungleich: nexte Ziffernkette
  cpi   temp1,'#'
  breq  bfound    ;Vergleich stimmt
  ld    temp3,z+    ;Pointer+1
  dec   temp2
  brne  bf41    ;noch nicht Ende der Z.-Kette

bf44:  lpm
  ld    temp1,z+
  mov   temp3,r0
  cpi   temp3,'#'
  brne  bf44    ;# n. gefu.
  ld    temp1,z+
  ld    temp1,z+
  lpm
  mov   temp3,r0
  cpi   temp3,0xff  ;Endez. gefu?
  breq  bfend
  rjmp  bf42

bfound: ld    temp1,z+
  lpm
  mov   temp1,r0
  ld    temp2,z+
  lpm
  mov   r31,r0
  mov   r30,temp1
  rcall lcducls
  ijmp

bfend:  rjmp  lcducls

;*********************************************************************** 
*******
;Befehlstabelle: gerade Anzahl von Ziffern + # (Ende immer mit #)
beftab:  .db   "99901#"
  .dw   sendflash
  .db   "99902#"
  .dw   serout
  .db   "99903#"    ;den gesamten Flash-Inhalt senden
  .dw   allflash
  .db   "9990999#"
  .dw   clearflash

  .dw   0xffff    ;Endekennung
;*******************************
clearflash:
  rjmp  flclr

;*******************************
sendflash:
  ldi   temp1,128    ;Flash, 0ten 4k-Block senden
sf2:  std   y-RS+dtyp,temp1
  ret
;*******************************
allflash:  ;alle Flash-Blöcke (je 4k) senden (beim letzten anfangen)
  rcall flhigh    ;Flashgröße (D021: temp1=4=1024 Pages)
  swap  temp1    ;D021=64 Blöcke a 4096Bytes
  std   y-RS+flblock,temp1
  ret

;*********************************************************************** 
*******
;Eeprom lesen (Adresse in temp1-low und temp2-high, Wertrückgabe in 
tem1):
eer0:  ldi     temp2,0    ;Highadresse ja oft 0
eeread:  sbic  EECR,EEWE  ;OK, wenn EEWE gelöscht
  rjmp  eeread    ;sonst noch warten
  out  EEARH,temp2  ;output address (high)
  out  EEARL,temp1  ;output address (low)
  sbi  EECR,EERE  ;set EEPROM Read strobe
  in  temp1,EEDR  ;get data
  ret

;*********************************************************************** 
*******
;Eingabepuffer loeschen (wird auch gleichzeitig in unterer Displayz. 
angezeigt)
lcducls:
  ldi   r26,lcdunten
  ldi   r27,0
  ldi   temp2,32
  ldi   temp1,16
lu2:  st    x+,temp2    ;in die Speicherstelle ein Leerz.
  dec   temp1
  brne  lu2
  ret

;*********************************************************************** 
*******
;kurzen Pip ausgeben!
pip:  ldi   temp1,50    ;Zahl der Halbwellen
  ldi   temp3,64
pi2:  ldi   r25,2    ;2*128us  (500ns * 256)
pi3:  sbiw  r24,1
  brne  pi3    ;je Schleife 4*125ns=500ns (8Mhz)
  in    temp2,PORTD
  eor   temp2,temp3
  out   PORTD,temp2  ;Piper umschalten
  dec   temp1
  brne  pi2
  ret

;*********************************************************************** 
*******
;Tonausgabe per Hauptprogrammschleife (jeweils einmal Pegel umschalten)
ton:  ldd   temp1,y-RS+pipton
  tst   temp1
  breq  tret    ;keine Tonausgabe
  dec   temp1
  std   y-RS+pipton,temp1
  in    temp1,PORTD
  andi  temp1,0xbf  ;alles ausser Bit 6
  sbis  PORTD,6    ;Bit schon high?
  ori   temp1,0x40  ;ansonsten einschalten
  out   PORTD,temp1
tret:  ret

;*********************************************************************** 
*******
;UP gibt Zeichen in temp1 aus (Pointer in outputl/h)
output:  ldd   r30,y-RS+outputl
  ldd   r31,y-RS+outputh
  ijmp      ;springe nach z (=r30/r31, =(outputl/h) )

;*********************************************************************** 
*******
;UP gibt Zeichen in temp1 auf ser. Schnittstelle aus
serout:
  sbis  USR,5    ;skip if bit 5 in USR is set
  rjmp  serout    ;sonst weiter warten, ob Tx frei
  out   UDR,temp1
  ret

;*********************************************************************** 
*******
zout:        ;UP gibt Zahl (in temp1) aus
  ldi   temp2,255
  subi  temp1,256-10
zo2:  inc   temp2
  subi  temp1,10
  cpi   temp1,10
  brcc  zo2
  push  temp1
  mov   temp1,temp2
  subi  temp1,256-48
  rcall output    ;Ausgabe auf dem akt. Ausgabepfad
  pop   temp1
  subi  temp1,256-48
  rjmp  output


;*********************************************************************** 
*******
;UP gibt Hex-Zahl (in temp1) auf der ser. Schnittstelle aus
hout:  mov   temp2,temp1
  swap  temp2
  rcall ho2
  mov   temp2,temp1
ho2:  andi  temp2,0x0f
  cpi   temp2,10
  brcs  ho3
  subi  temp2,256-7  ;ab 10 +7 (a..f)
ho3:  subi  temp2,256-48
  push  temp1
  mov   temp1,temp2
  rcall output    ;Ausgabe auf dem akt. Ausgabepfad
  pop   temp1
  ret

;*********************************************************************** 
*******
;Uhr wird bis 23.59 vom Timer0-Int bearbeitet. Hier erfolgt evt. 
Datumseinst.
uhr:  mov   temp1,stunde
  cpi   temp1,24
  brcc  u2
  ret

;Mitternacht vorbei, Datum korrigieren!
u2:  ldi   r30,low(dcfmin)
  ldi   r31,high(dcfmin)  ;Z-Register beinhaltet Variblenadresse
  ldd   temp1,z+wota-dcfmin
  mov   temp2,temp1
  andi  temp1,0b10111000  ;DCF-Sync-Kennung abschalten
  inc   temp2
  andi  temp2,0x07  ;Wochentag 1(Mo)...7(So)
  breq  u3
  ldi   temp2,1    ;beim Montag wieder anfangen!
u3:  or    temp2,temp1
  std   z+wota-dcfmin,temp1

  ldd   temp2,z+mon-dcfmin  ;Monat nach temp2
  ldd   temp1,z+tag-dcfmin  ;Tag laden (temp1)
  inc   temp1      ;naechster Tag
  cpi   temp2,8      ;August oder spaeter?
  brcs  u4
  dec   temp2      ;nun hat 1.Monat 31, jeder 2. 30 Tage
u4:  ldi   temp3,31
  sbrc  temp2,0
  ldi   temp3,32      ;verbotene Tageszahl für unger. Monate
  cpi   temp2,2      ;Februar?
  brne  u6
  ldi   temp3,29
u6:  cp    temp1,temp3    ;verbotene Tageszahl erreicht?
  brcs  u11      ;kein neuer Monat

  ldi   temp1,1      ;erster Tag im neuen Monat
  ldd   temp3,z+mon-dcfmin  ;Monat nach temp3
  ldd   temp4,z+jahr-dcfmin  ;Jahr nach temp4
  inc   temp3      ;naechster Monat
  cpi   temp3,13
  brcs  u9
  ldi   temp3,1      ;1.Monat im neuen Jahr
  inc   temp4
  cpi   temp4,100
  brcs  u9      ;Jahr 100 noch nicht erreicht
  subi  temp4,100
u9:  std   z+mon-dcfmin,temp3
  std   z+jahr-dcfmin,temp4

u11:  std   z+tag-dcfmin,temp1
  ret


;*********************************************************************** 
******
.include "keyb34.asm"
.include "dcf.asm"
.include "lcd2_16.asm"
.include "spi.asm"
;*********************************************************************** 
******

.exit

;Buffertransfer
  ldi   r26,low(dbuf+1)
  ldi   r27,high(dbuf+1)
  ldi   r30,low(lcdunten)
  ldi   r31,high(lcdunten)
  ldi   temp1,16
rec99:  ld    temp2,x+
  cpi   temp2,32
  brcc  rec98
  ldi   temp2,'?'
rec98:  st    z+,temp2
  dec   temp1
  brne  rec99

von Stefan Gemmel (Gast)


Lesenswert?

Hallo Ralf,
das Programm find ich gut, könntest du auch die Include Dateien posten ?
Mich interessiert das DCF77 da ich hierzu gerade was programmieren will.
(siehe : "DCF77 Funkuhr für Atmel" in NG: DE.SCI.ELECTRONICS)

Gruss Stefan

Beitrag #6130732 wurde von einem Moderator gelöscht.
Beitrag #6130735 wurde von einem Moderator gelöscht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.