; **********************************************
; * Prellmessgeraet mit ATmega8 auf STK500 *
; * Version 1, Januar 2013, ext. Quarz 16 MHz *
; * (C)2013 by http://www.avr-asm-tutorial.net *
; **********************************************
;
; Misst die Prellsignale eines angeschlossenen
; Tasters und gibt sie ueber die serielle
; Schnittstelle aus.
;
.NOLIST
.include "m8def.inc"
.LIST
;
; *****************
; H A R D W A R E
; *****************
; __________
; / ATmega8 |
; RESET o--| |--o
; RXD o--|PD0 |--o
; TXD o--|PD1 |--o
; Taste o--|PD2 |--o
; o--| |--o
; o--| |--o
; o--| |--o
; o--| |--o
; Xtal1 o--|PB6 |--o
; Xtal2 o--|PB7 |--o
; o--| |--o
; o--| |--o
; o--| |--o
; o--| |--o
; |___________|
;
;
; *****************************
; F U N K T I O N S W E I S E
; *****************************
;
; Zeiten:
; Die CPU wird mit einem externen 16-MHz-Quarz getaktet.
; Die ST_CKSEL-Fuse ist auf "Ext.Crystal/Resonator High
; Freq.; Start-up time 16K CK + 64 ms" zu programmieren.
; Das entspricht HIGH=0xD9, LOW=0xFF.
; Alle Zeiten werden vom 16-Bit-Timer TC1 abgeleitet.
; Dieser laeuft mit 16 MHz Takt und dem Vorteiler von 8
; mit 2 MHz. Alle angegebenen Zeiten sind daher 0,5 Mi-
; krosekunden. Der Zaehler laeuft durch und setzt bei
; Ueberlaeufen (Overflow-Interrupt) einen weiteren
; 15-Bit-Zaehler in einem Registerpaar hoch, so dass
; Zeiten von etwa 17 Minuten (65536*32768/2µs = 1037 s =
; 17,9 min) messbar sind. Das 32.-te Bit des Zaehlers
; ist beim Zaehlen immer Null, dieses Bit enthaelt bei
; der Uebertragung das Polaritaetsbit des Eingangs.
; Messen:
; Pegelaenderungen an PD2 loesen Interrupts aus.
; Bei jedem Interrupt wird der aktuelle Zaehlerstand
; des 31-Bit-Timers in einen Puffer kopiert, im obersten
; Bit wird der logische Zustand des Tasters dazu kopiert.
; Ausgabe:
; Die Ausgabe ueber die serielle Schnittstelle erfolgt
; in der Hauptprogrammschleife. Die 4-Byte-Pufferwerte
; werden in acht Hex-Nibble umgewandelt und mit CR-LF
; abgeschlossen. Diese Sende-Sequenz wird in einen
; 10-Byte-Puffer im SRAM geschrieben und der Sendevorgang
; mit dem Aussenden des ersten Bytes gestartet. Das
; weitere Senden erfolgt interruptgesteuert (TX-Buffer-
; Empty), bis das abschliessende LF ausgesendet ist.
; Die UART-Schnittstelle ist auf 38.400 Baud mit 8N2
; eingestellt.
; SRAM-Organisation:
; Fuer den Sendepuffer sind die ersten 10 Byte reserviert.
; Der Messpuffer nimmt 996 Bytes SRAM ein. Das entspricht
; 249 Ereignissen. Fuer den Stapel bleiben 18 Byte uebrig.
; Erreicht der Stapelzeiger das Ende des Puffers (bei hex
; 044D) beginnt das Befuellen beim Beginn des Puffers
; (hex 006A). Bis dahin nicht gesendete Werte im Puffer
; werden ueberschrieben und gehen verloren, daher
; zwischen dem Tasten Pausen einlegen, bis die Ausgabe
; komplett erfolgt ist.
;
; *********************************************
; E D I T I E R B A R E K O N S T A N T E N
; *********************************************
;
.EQU cXtal = 16000000 ; Frequenz des externen Quarzes
.EQU cBaud = 38400 ; Baudrate beim Senden/Empfangen
;
; *******************************
; F I X E K O N S T A N T E N
; *******************************
;
.EQU cLF = 0x0A ; Line Feed
.EQU cCR = 0x0D ; Carriage Return
;
; ***********************************
; S R A M - D E F I N I T I O N E N
; ***********************************
;
.DSEG
.ORG 0x0060 ; SRAM-Start
; definiere Sendepuffer
sTxBufStart:
.Byte 10 ; reserve ten bytes
sTxBufEnd:
; definiere Messdatenpuffer
sEntryBufStart:
.Byte 249*4 ; 249 Datensaetze
sEntryBufEnd:
;
; *****************
; R E G I S T E R
; *****************
; frei: R0 .. R11
.DEF rPtoL = R12 ; LSB Zeiger fuer Ausgabe aus Puffer
.DEF rPtoH = R13 ; dto., MSB
; frei: R14
.DEF rSreg = R15 ; Register zur Sicherung SREG
.DEF rmp = R16 ; Vielzweckregister ausserhalb Ints
.DEF rimp = R17 ; Vielzweckregister innerhalb Ints
.DEF rFlg = R18 ; Flaggenregister
.EQU fSend = 0 ; Senden ist gestartet
.DEF rHex = R19
; frei: R20 .. R23
.DEF rTimerL = R24 ; Byte 3 Timer
.DEF rTimerH = R25 ; Byte 4 Timer
; benutzt: X (R27:R26) Zeiger fuer Puffer-Eingabe
; benutzt: Y (R29:R28) Zeiger fuer UART-Senden, ausserhalb/innerhalb
; benutzt: Z (R31:R30) Zeiger ausserhalb Ints
;
; ***********************************************
; R E S E T - U N D I N T - V E K T O R E N
; ***********************************************
;
.CSEG
.ORG $0000
rjmp Main ; Reset-Vektor
rjmp Int0Isr ; INT0-Vektor
reti ; INT1-Vektor
reti ; TC2-COMP-Vektor
reti ; TC2-OVF-Vektor
reti ; TC1-CAPT-Vektor
reti ; TC1-CompA-Vektor
reti ; TC1-CompB-Vektor
rjmp TC1OvfIsr ; TC1-Ovflw-Vektor
reti ; TC0-Ovflw-Vektor
reti ; SPI-STC-Vektor
rjmp UartRxcIsr ; UART-RXC-Vektor
rjmp UartUdreIsr ; UART-UDRE-Vektor
reti ; UART-TXC-Vektor
reti ; ADC-Vektor
reti ; EE-RDY-Vektor
reti ; ANA-COMP-Vektor
reti ; TWI-Vektor
reti ; SPM-RDY-Vektor
;
; *****************************************
; I N T - S E R V I C E - R O U T I N E N
; *****************************************
;
; INT0-ISR
;
Int0Isr:
in rSreg,SREG ; SREG-Zustand sichern
in rimp,PIND ; Lese Port
bst rimp,PIND2 ; Portbit in T-Flag
in rimp,TCNT1L ; lese Timer LSB
st X+,rimp ; im SRAM ablegen
in rimp,TCNT1H ; lese Timer MSB
st X+,rimp ; im SRAM ablegen
st X+,rTimerL ; Timer Bits 17 bis 24 ablegen
mov rimp,rTimerH ; Bits 25 bis 32 kopieren
bld rimp,7 ; Bit 32 aus T-Flag setzen
st X+,rimp ; im SRAM speichern
cpi XL,LOW(sEntryBufEnd) ; LSB Ende Puffer
brne Int0IsrRet ; noch nicht
cpi XH,HIGH(sEntryBufEnd) ; MSB Ende Puffer
brcs Int0IsrRet ; noch nicht
ldi XH,HIGH(sEntryBufStart) ; Pufferzeiger auf Start
ldi XL,LOW(sEntryBufStart)
Int0IsrRet:
out SREG,rSreg ; SREG wieder herstellen
reti
;
; TC1-Overflow-ISR
;
TC1OvfIsr:
in rSreg,SREG ; sichern SREG
adiw rTimerL,1 ; Timer hochzaehlen
andi rTimerH,0x7F ; Ueberlauf in das achte Bit loeschen
out SREG,rSreg ; stelle SREG wieder her
reti
;
; UART-RXC-ISR
;
UartRxcIsr:
in rSreg,SREG ; sichern SREG
in rimp,UDR ; lese Zeichen
sbis UCSRB,UDRIE ; interrupt disabled?
out UDR,rimp ; Echo Zeichen
out SREG,rSreg ; wieder herstellen SREG
reti
;
; UART-UDRE-ISR
;
UartUdreIsr:
in rSreg,SREG ; sichern SREG
sbrs rFlg,fSend ; testen ob Senden aktiv
rjmp UartUdreIsrInact ; Senden nicht aktiv
ld rimp,Y+ ; lese naechstes Byte aus Puffer
out UDR,rimp ; sende Byte
cpi rimp,cLF ; letztes Zeichen gesendet?
brne UartUdreIsrRet ; nein
cbr rFlg,1<<fSend ; Senden Ende, Flagge loeschen
rjmp UartUdreIsrRet
UartUdreIsrInact: ; Inactive, clear interrupt enable
cbi UCSRB,UDRIE ; loesche Int enable
UartUdreIsrRet:
out SREG,rSreg ; stelle SREG wieder her
reti
;
; *************************************
; H A U P T P R O G R A M M - I N I T
; *************************************
Main:
; Stapel einrichten
ldi rmp,HIGH(RAMEND) ; Stapel definieren
out SPH,rmp
ldi rmp,LOW(RAMEND)
out SPL,rmp
; Ports initiieren
ldi rmp,1<<DDD1 ; TxD ist Ausgabepin
out DDRD,rmp ; Portrichtung setzen
sbi PORTD,PORTD2 ; INT0/D2-Pullup einschalten
; Zeiger initiieren
clr rFlg ; Flaggen loeschen
ldi XH,HIGH(sEntryBufStart) ; Zeiger Schreibpuffer
ldi XL,LOW(sEntryBufStart)
mov rPtoH,XH ; Lesezeiger
mov rPtoL,XL
rcall Starttext ; Starttext in Sendepuffer
; UART initiieren
.EQU cUbrr = cXtal / 16 / cBaud - 1
ldi rmp,HIGH(cUbrr) ; schreibe Baudrate
out UBRRH,rmp
ldi rmp,LOW(cUbrr)
out UBRRL,rmp
clr rmp ; TXC, U2X und MPCM auf Null
out UCSRA,rmp
ldi rmp,(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0)|(1<<USBS) ; 8N2
out UCSRC,rmp
ldi rmp,(1<<RXCIE)|(1<<RXEN)|(1<<TXEN) ; RX und TX an
out UCSRB,rmp
; TC1 initiieren
clr rmp ; WGM11 und WGM10 auf Null
out TCCR1A,rmp
ldi rmp,(1<<CS11) ; presc=8
out TCCR1B,rmp
ldi rmp,1<<TOIE1 ; Overflow Int enable
out TIMSK,rmp
; INT0 initiieren
ldi rmp,(1<<ISC00)|(1<<SE) ; INT0-Edges und Sleep
out MCUCR,rmp
ldi rmp,1<<INT0 ; INT0-Int ermoeglichen
out GICR,rmp
; UART-Ausgabe Startmeldung starten
rcall SendBuffer ; starte Ausgabe
; General Int
sei ; Int enable
;
; *************************************
; H A U P T P R O G R A M M - L O O P
; *************************************
Loop:
sleep ; schlafen legen
nop ; Aufwachdummy
sbic UCSRB,UDRIE ; TX Int disabled
rjmp Loop ; nein, Schleife
sbrs rFlg,fSend ; weiter senden?
rcall BufferTest ; check Zeichen in Buffer
rjmp Loop
;
; *******************************
; Zeichen in Puffer zum Senden?
; *******************************
;
BufferTest:
cp rPtoL,XL ; LSB Ein- und Ausgabezeiger gleich?
brne SendEntry ; nein, sende Eintrag
cp rPtoH,XH ; MSB gleich?
brne SendEntry ; nein, sende Eintrag
ret ; Ein- und Ausgabezeiger gleich, nichts zu tun
;
; *******************************************
; S E N D E N E I N E S E I N T R A G S
; *******************************************
;
SendEntry:
mov ZH,rPtoH ; kopiere Zeiger nach Z
mov ZL,rPtoL
cpi ZL,LOW(sEntryBufEnd) ; LSB Ende des Puffers?
brne SendEntryConvert
cpi ZH,HIGH(sEntryBufEnd) ; MSB Pufferende
brne SendEntryConvert
ldi ZH,HIGH(sEntryBufStart) ; setze auf Pufferanfang
ldi ZL,LOW(sEntryBufStart)
SendEntryConvert:
adiw ZL,4 ; Zeige auf Ende
ldi YH,HIGH(sTxBufStart) ; setze Y auf Sendepufferanfang
ldi YL,LOW(sTxBufStart)
ldi rmp,4 ; Zaehler
SendEntryBytes:
rcall SendEntryByte ; konvertiere Byte in Hex
dec rmp ; naechstes Byte
brne SendEntryBytes
adiw ZL,4 ; zeige auf Ende
mov rPtoH,ZH ; kopiere Zeiger
mov rPtoL,ZL
ldi rmp,cCR ; Carriage Return dazu
st Y+,rmp
ldi rmp,cLF ; Line Feed dazu
st Y,rmp
;
; **********************
; Beginn Pufferausgabe
; **********************
;
SendBuffer:
ldi YH,HIGH(sTxBufStart) ; setze Y auf Sendepufferanfang
ldi YL,LOW(sTxBufStart)
ld rmp,Y+ ; lese erstes Zeichen
out UDR,rmp ; sende erstes Byte
sbr rFlg,1<<fSend ; setze Sende-Flagge
sbi UCSRB,UDRIE ; enable UDRE interrupts
ret
;
; ***********************
; Sende ein Byte in Hex
; ***********************
;
SendEntryByte:
ld rHex,-Z ; lese Byte rueckwaerts
swap rHex ; wechseln oberes Nibble
rcall SendEntryNibble
ld rHex,Z ; lese Byte noch mal
SendEntryNibble:
andi rHex,0x0F ; loesche oberes Nibble
subi rHex,-'0' ; addiere Ascii-Null
cpi rHex,'9'+1 ; A..F?
brcs SendEntryChar ; nicht A..F
subi rHex,-('A'-'9'-1) ; addiere fuer A..F
SendEntryChar:
st Y+,rHex ; in Sendepuffer ablegen
ret
;
; Fuelle Sendepuffer mit Startmeldung
;
Starttext:
.db "Prellen1",cCr,cLF
Startmeldung:
ldi ZH,HIGH(2*Starttext) ; Anfang Text
ldi ZL,LOW(2*Starttext)
ldi YH,HIGH(sTxBufStart) ; setze Y auf Sendepufferanfang
ldi YL,LOW(sTxBufStart)
StartmeldungKopie:
lpm rmp,Z+ ; lese Byte aus Flash
st Y+,rmp ; schreibe in Sendepuffer
cpi rmp,cLF ; bis linefeed
brne StartmeldungKopie
ret
;
; Copyright
;
Copyright:
.DB "(C)2013 by Gerhard Schmidt "
.DB "C(2)10 3vyG reahdrS hcimtd "
;
; Ende Quellcode
;
Zum Inhaltsverzeichnis
©2013 by http://www.avr-asm-tutorial.net