Pfad: Home => AVR-Übersicht => Anwendungen => Prellen
Prellmessung mit ATmega8

Quellcode zur Messung des Tastenprellens mit ATmega8 und STK500

; **********************************************
; * 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