Pfad: Home =>
AVR-Übersicht =>
Anwendungen =>
Funkfernsteuerung => Software
Software für die Funkfernsteuerung mit ATtiny24
;
; **********************************************
; * ATtiny24 PCM Encoder fuer vier Kanaele *
; * Version 1 Juni 2011 *
; * (C)2011 by http://www.avr-asm-tutorial.net *
; **********************************************
;
; Headerdatei fuer den Zielprozessor
.NOLIST
.INCLUDE "tn24def.inc" ; Header fuer ATtiny24
.LIST
;
; Debugging Modus
;
.EQU debug = 0 ; 1: nur Debugging Modus
.EQU signal = 1 ; 1: signalausgang an PB0
;
;
; ============================================
; H A R D W A R E I N F O R M A T I O N E N
; ============================================
;
; Laeuft mit den voreingestellten 1 MHz RC-Takt
; ________
; 1 / ATtn24 |14
; + 5 V O--|VCC GND|--O GND
; LED O--|PB0 ADC0|--O P1
; O--|PB1 ADC1|--O P2
; RESET O--|PB3 ADC2|--O P3
; O--|PB2 ADC3|--O P4
; O--|PA7 USCK|--O USCK
; MOSI/OC1A O--|PA6 PA5|--O OC1B/MISO
; 7|_________|8
;
;
; ============================================
; P O R T S U N D P I N S
; ============================================
;
; P1 bis P4: ADC-Eingaenge Potis/Trimmer
; OC1A: normales Ausgangssignal
; OC1B: invertiertes Ausgangssignal
; LED: LED-Ausgang, blinkt im Takt der ADC-Abfrage
;
; ============================================
; C O N S T A N T S T O C H A N G E
; ============================================
;
.EQU cPulse = 500 ; Pulseweite Startpuls in us
.EQU cMinLen = 800 ; Mindestdauer Kanalsignal in us
.EQU cMaxLen = 2200 ; Maximumdauer Kanalsignal in us
.EQU cTotal = 20000 ; Gesamtdauer alle Kanaele plus Sync-Signal in us
.EQU cAdc = 16 ; Anzahl der aufsummierten Messungen pro Kanal vom ADC
; muss groesser 0 und kleiner oder gleich 64 sein!
;
; =======================================================
; F E S T E + B E R E C H N E T E K O N S T A N T E N
; =======================================================
;
.EQU fClock = 1000000 ; Prozessortakt, default
.EQU cDelta = cMaxLen - cMinLen ; Max-Min Laenge Kanalsignal
.EQU cMulti = ((65536*cDelta+511)/1023+cAdc/2)/cAdc ; Multiplikator fuer ADC-Werte
;
; ============================================
; R E G I S T E R - D E F I N I T I O N E N
; ============================================
;
; R0 benutzt fuer LPM Instruktionen ausserhalb Ints
; R1..R10 benutzt zur Multiplikation
; frei: R11..R14
.DEF rSreg = R15 ; Interrupt Status Register
.DEF rmp = R16 ; Vielzweckregister ausserhalb Ints
.DEF rimp = R17 ; Vielzweckregister innerhalb Ints
.DEF rFlag = R18 ; Flaggenregister
.equ bTxRdy = 0 ; Kopieren-Flagge fuer Transfer
.equ bRxRdy = 1 ; Adc-Ergebniszeile auswerten
.DEF rCopy = R19 ; Kopie eines Bytes
.DEF rAdcN = R20 ; Anzahl Runden ADC-Messungen
.DEF rAdcA = R21 ; ADC Addierregister
; Frei: R21..R23
; Benutzt: R26..R27 (X) als Zeiger auf SRAM innerhalb Int
; Benutzt: R28..R29 (Y) als Zeiger fuer ADC-Werte innerhalb Int
; Benutzt: R30..R31 (Z) als Zeiger ausserhalb Ints
;
; ============================================
; S R A M D E F I N I T I O N E N
; ============================================
;
.DSEG
.ORG Sram_Start
; _______________________________________
; |SPL|SPH|C4L|C4H|C3L|C3H|C2L|C2H|C1L|C1H| 10 Bytes
; ---------------------------------------
TcB:
.BYTE 10 ; Platzreservierung fuer fuenf 16-Bit-Timerwerte
;
; _______________________________________
; |C1L|C1H|C2L|C2H|C3L|C3H|C4L|C4H|SPL|SPH| 10 Bytes
; ---------------------------------------
TcBC:
.BYTE 10 ; Platzreservierung fuer berechnete Timerwerte
;
; _______________________________
; |C1L|C1H|C2L|C2H|C3L|C3H|C4L|C4H 8 Bytes
; -------------------------------
AdcRes:
.BYTE 8 ; Platzreservierung fuer vier 16-bit-ADC-Werte
;
; =============================================
; 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
reti ; Int0 Vektor
reti ; PcInt0 Vektor
reti ; PcInt1 Vektor
reti ; WDT Vektor
rjmp CaptInt ; TC1_CAPT Vektor
reti ; TC1_COMPA Vektor
reti ; TC1_COMPB Vektor
reti ; TC1_OVF Vektor
reti ; TC0_COMPA Vektor
reti ; TC0_COMPB Vektor
reti ; TC0_OVF Vektor
reti ; ANA_COMP Vektor
rjmp AdcInt ; ADC Vektor
reti ; EE_RDY Vektor
reti ; USI_STR Vektor
reti ; USI_OVF Vektor
;
; ============================================
; I N T E R R U P T S E R V I C E
; ============================================
;
; TC1 ICR Interrupt Service Routine
CaptInt:
in rSreg,SREG ; sichere SREG
ld rimp,-X ; lese naechsten ICR-Wert, MSB
out ICR1H,rimp ; schreibe in ICR1H
ld rimp,-X ; lese ICR-Wert, LSB
out ICR1L,rimp ; schreibe in ICR1L
cpi XL,LOW(TcB+2) ; am Tabellenanfang?
brcc CaptInt1 ; nein
ldi XH,HIGH(TcB+10) ; ja, beginne am Tabellenende
ldi XL,LOW(TcB+10)
sbr rFlag,1<<bTxRdy ; Kopierflagge setzen
CaptInt1:
out SREG,rSreg ; wiederherstellen SREG
reti
;
; ADC Interrupt Service Routine
AdcInt:
in rSreg,SREG ; sichere SREG
in rimp,ADCL ; lese ADC-Ergebnis LSB
cpi rAdcN,cAdc ; zur Summe addieren?
brne AdcInt1 ; ja
st Y+,rimp ; ueberschreiben Ergebnis LSB
in rimp,ADCH ; lese ADC-Ergebnis MSB
st Y+,rimp ; ueberschreiben Ergebnis MSB
rjmp AdcInt2 ; weiter
AdcInt1:
ld rAdcA,Y ; lese letzte Summe LSB
add rimp,rAdcA ; addiere letzte Summe LSB
st Y+,rimp ; speichern Ergebnis LSB
in rimp,ADCH ; lese ADC-Ergebnis MSB
ld rAdcA,Y ; lese letzte Summe MSB
adc rimp,rAdcA ; addiere mit Uebertrag
st Y+,rimp ; speichern Ergebnis MSB
AdcInt2:
mov rimp,YL ; Kopiere Zeigeradresse
subi rimp,LOW(AdcRes) ; subtrahiere Anfang
lsr rimp ; teile durch 2
cpi rimp,5 ; Tabellenende?
brcs AdcIntMux ; keine Ueberschreitung
ldi YH,HIGH(AdcRes) ; Neustart am Anfang
ldi YL,LOW(AdcRes)
clr rimp ; mit MUX=0 beginnen
dec rAdcN ; Rundenzaehler verringern
brne AdcIntMux ; noch nicht Null, weiter
sbr rFlag,1<<bRxRdy ; setze Fertigflagge
ldi rAdcN,cAdc ; Neuistart Rundenzaehler
AdcIntMux:
out ADMUX,rimp ; setze naechsten ADC-Kanal
ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
out ADCSRA,rimp ; starte naechste AD-Wandlung
out SREG,rSreg ; wiederherstellen SREG
reti
;
; Blinken wenn eingeschaltet
.IF signal==1
;
; Blinken an PB0
;
Blink:
sbis PORTB,PORTB0 ; PB0 auf 1?
rjmp Blink1 ; nein
cbi PORTB,PORTB0 ; PB0 auf 0
ret
Blink1:
sbi PORTB,PORTB0 ; PB0 auf 1
ret
.ENDIF
;
; =====================================
; H A U P T P R O G R A M M I N I T
; =====================================
;
Main:
; Init bei Programmbeginn
; Init Stapel
ldi rmp,LOW(RAMEND) ; Stapelzeiger LSB
out SPL,rmp
; Init I/O Pins
sbi DDRA,DDA6 ; OC1A als Ausgang
sbi DDRA,DDA5 ; OC1B als Ausgang
cbi PORTA,PORTA6 ; normaler Ausgang auf Low
sbi PORTA,PORTA5 ; invertierter Ausgang auf High
.IF signal==1
sbi DDRB,PB0 ; LED-Ausgang
sbi PORTB,PORTB0
.ENDIF
; Starte Timer1
rcall InitTable ; Timerwerte im SRAM initiieren
ldi XH,HIGH(TcB+10) ; Y auf letzten Tabellenwert
ldi XL,LOW(TcB+10)
ld rmp,-X ; Lese letzten Tabellenwert MSB
out ICR1H,rmp ; in ICR1H schreiben
ld rmp,-X ; lese letzten Tabellenwert LSB
out ICR1L,rmp ; in ICR1L schreiben
ldi rmp,HIGH(cPulse) ; Compare-Wert A MSB
out OCR1AH,rmp ; in Compare-Register AH
ldi rmp,LOW(cPulse) ; Compare-Wert A LSB
out OCR1AL,rmp ; in Compare-Register AL
ldi rmp,HIGH(cPulse) ; Compare-Wert B MSB
out OCR1BH,rmp ; in Compare-Register BH
ldi rmp,LOW(cPulse) ; Compare-Wert B LSB
out OCR1BL,rmp ; in Compare-Register BL
ldi rmp,(1<<WGM11)|(1<<COM1A1)|(1<<COM1B1)|(1<<COM1B0) ; Fast PWM, toggle bei compare match
out TCCR1A,rmp ; in Timer 1 Kontroll-Register
ldi rmp,(1<<WGM13)|(1<<WGM12)|(1<<CS10) ; Fast PWM, Vorteiler=1, Zaehler an
out TCCR1B,rmp ; in Timer 1 Kontroll-Register
ldi rmp,(1<<ICIE1) ; ICR1 Int einschalten
out TIMSK1,rmp ; in Timer1 Int Maske
; Starte ADC
ldi rAdcN,cAdc ; Anzahl Messungen pro Runde einstellen
clr rmp ; setze Kanal auf ADC0, erster Regler
out ADMUX,rmp ; inj ADC Multiplex Register
ldi YH,HIGH(AdcRes) ; Y auf Tabellenanfang
ldi YL,LOW(AdcRes)
ldi rmp,(1<<ADC3D)|(1<<ADC2D)|(1<<ADC1D)|(1<<ADC0D)
out DIDR0,rmp ; Abschalten der Digitaleingaenge der ADC ports
ldi rmp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
out ADCSRA,rmp ; starte erste Wandlung
; Starte Schlafmodus und Interrupts
ldi rmp,1<<SE ; Schlafmodus Idle ermoeglichen
out MCUCR,rmp ; in Kontrollregister
sei ; Interrupts ermoeglichen
.IF debug == 1
ldi ZH,HIGH(AdcRes) ; Debugwerte in SRAM schreiben
ldi ZL,LOW(AdcRes)
ldi rmp,LOW(0)
st Z+,rmp
ldi rmp,HIGH(0)
st Z+,rmp
ldi rmp,LOW(cAdc*1024/4)
st Z+,rmp
ldi rmp,HIGH(cAdc*1024/4)
st Z+,rmp
ldi rmp,LOW(cAdc*1024/2)
st Z+,rmp
ldi rmp,HIGH(cAdc*1024/2)
st Z+,rmp
ldi rmp,LOW(cAdc*1023)
st Z+,rmp
ldi rmp,HIGH(cAdc*1023)
st Z+,rmp
rcall RxFlag
endloop: rjmp endloop ; Ende Debuggen
.ENDIF
;
; ============================================
; P R O G R A M M S C H L E I F E
; ============================================
;
Loop:
sleep ; schlafen legen
nop ; Dummy fuer Aufwachen
sbrc rFlag,bRxRdy ; Adc-Reihe fertig?
rcall RxFlag ; und bearbeiten
rjmp Loop ; zurueck zur Schleife
;
;Rx-Flagge gesetzt, behandle Flagge
;
RxFlag:
cbr rFlag,1<<bRxRdy ; loesche RxRdy-Flagge
.if signal == 1
rcall Blink ; blinken
.endif
ldi ZH,HIGH(TcBC) ; Z auf Berechnungsbereich
ldi ZL,LOW(TcBC)
ldi rmp,8 ; Kopiere 8 Bytes
RxFlag1:
ldd R0,Z+10 ; lese ein Byte
st Z+,R0 ; kopiere in Berechnungsbereich
dec rmp ; naechstes Byte
brne RxFlag1 ; noch eins
ldi ZH,HIGH(TcBC) ; Z auf Berechnungsbereich
ldi ZL,LOW(TcBC)
RxFlag2: ; lese ADC Kanalergebnis
ld R1,Z+ ; lese ADC-Summe LSB aus SRAM
ld R2,Z+ ; lese ADC-Summe MSB aus SRAM
clr R3 ; losche Bytes 3 und 4
clr R4
; lade Multiplikator
ldi rmp,LOW(cMulti) ; Multiplikator in R6:R5
mov R5,rmp
ldi rmp,HIGH(cMulti)
mov R6,rmp
; loesche Multiplikationsergebnis
clr R7
clr R8
clr R9
clr R10
; Multipliziere R2:R1 mit R6:R5
; Ergebnis in R10:R9:R8:R7
RxFlag3:
lsr R6 ; schiebe MSB Multiplikator rechts
ror R5 ; rolle in LSB ein und Bit 0 in Carry
brcc RxFlag4 ; Null herausgerollt, nicht addieren
add R7,R1 ; addiere zum Ergebnis
adc R8,R2
adc R9,R3
adc R10,R4
RxFlag4:
lsl R1 ; Multipliziere Multiplikator mit 2
rol R2
rol R3
rol R4
tst R5 ; LSB = 0?
brne RxFlag3 ; nein
tst R6 ; MSB = 0?
brne RxFlag3 ; nein
ldi rmp,0x80 ; runden
add R7,rmp
adc R8,rmp
ldi rmp,0
adc R9,rmp
adc R10,rmp
ldi rmp,LOW(cMinLen) ; Mindestdauer addieren
add R9,rmp
ldi rmp,HIGH(cMinLen)
adc R10,rmp
sbiw ZL,2 ; Position rueckwaerts
st Z+,R9 ; Ergebnis in Tabelle speichern
st Z+,R10
cpi ZL,LOW(TcBC+8) ; Ende der Tabelle?
brcs RxFlag2 ; nein, weiter
; Berechnen der Restzeit bis 20 ms
ldi rmp,HIGH(cTotal) ; 20.000 in R2:R1
mov R2,rmp
ldi rmp,LOW(cTotal)
mov R1,rmp
ldi ZH,HIGH(TcBC) ; auf Tabellenanfang
ldi ZL,LOW(TcBC)
ldi rmp,4 ; vier Werte abziehen
RxFlag6:
ld R0,Z+ ; lese LSB
sub R1,R0 ; subtrahiere LSB
ld R0,Z+ ; lese MSB
sbc R2,R0 ; subtrahiere MSB
dec rmp ; nachster Tabellenwert
brne RxFlag6 ; noch nicht alle
st Z+,R1 ; kopiere LSB ans Tabellenende
st Z+,R2 ; kopiere MSB ans Tabellenende
; Warte bis ein kompletter Timerzyklus abgelaufen ist
cbr rFlag,1<<bTxRdy ; loesche Tx-Flagge
RxFlag8:
sbrs rFlag,bTxRdy ; warte bis die Flagge gesetzt ist
rjmp RxFlag8
; Kopiere Ergebnisse in TcB Speicherbereich
lds rmp,TcBC+1
sts TcB+9,rmp
lds rmp,TcBC
sts TcB+8,rmp
lds rmp,TcBC+3
sts TcB+7,rmp
lds rmp,TcBC+2
sts TcB+6,rmp
lds rmp,TcBC+5
sts TcB+5,rmp
lds rmp,TcBC+4
sts TcB+4,rmp
lds rmp,TcBC+7
sts TcB+3,rmp
lds rmp,TcBC+6
sts TcB+2,rmp
lds rmp,TcBC+9
sts TcB+1,rmp
lds rmp,TcBC+8
sts TcB,rmp
ret
;
; Init Tabellenwerte
;
InitTable:
ldi XH,HIGH(TcB) ; X auf Tabellenanfang
ldi XL,LOW(TcB)
ldi ZH,HIGH(2*Table) ; Z auf Tabelle im Flash
ldi ZL,LOW(2*Table)
ldi rmp,10 ; lese 10 Byte
InitTable1:
lpm R0,Z+ ; lese Byte
st X+,R0 ; kopiere in SRAM
dec rmp ; naechster Wert
brne InitTable1 ; noch weitere Werte
ret
Table:
.dw 20000-5300, 1500, 1500, 1500, 800
TableEnd:
;
; Ende Quellcode
;
; Copyright
;
.db " Tiny24 PCM Encoder, (C)2011 by DG4FAC "
.db " iTyn42P MCE cndore ,C(2)10 1ybD 4GAF C "
An den Seitenanfang
©2011 by http://www.avr-asm-tutorial.net