Pfad: Home => AVR-Übersicht => Anwendungen => Funkfernsteuerung => Software
PCM-Encoder

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