Quelltext /~heha/Mikrocontroller/sr04/uem.zip/uem.S

#define __SFR_OFFSET 0
#include "avr/io.h"
/* Dieses merkwürdig kurze Programm benötigt keinen Timer, keinen ADC,
keinen RAM, keinen Stack, keinen Interrupt und verschwenderische 4 Register.
Sowas wäre ein typischer Kandidat für PIC12F508,
wenn der nur genügend treiberstark wäre und einen Analogvergleicher hätte.
Oder ATtiny4, wenn der genügend Beinchen hätte.
Oder ATtiny11/12, habe ich gerade nicht zur Hand.
Oder ATtiny25, dessen OSCCAL ist etwas genauer.
TODO: Interrupt fürs Aufwecken bei Pin-Change und Analogvergleicher;
der externe OPV zieht aber ohnehin ständig Strom.
*/
//#define DEBUG

.section .fuse,"a",@progbits	// --set-section-flags=.fuse="alloc,load" ersparen
#ifdef TCNT1	// ATtinyX5
	.byte	0b01100011	// ATtiny15-Modus: 6,4 MHz
	.byte	0b11011111	// unverändert
	.byte	0b11111111	// unverändert
#else		// ATtiny13
	.byte	0b01101010	// unverändert, kein Brown-Out
	.byte	0b11111111	// unverändert
#endif

.section .signature,"a",@progbits
	.byte	SIGNATURE_0,SIGNATURE_1,SIGNATURE_2
.text
// Initialisieren: Takt = 1,2 MHz, kein ADC/Timer/USI
#ifdef TCNT1
	ldi	r24,0b10000000
	out	CLKPR,r24
	ldi	r24,0b00000010	// Taktteiler /4 = 1,2 MHz
	out	CLKPR,r24
	ldi	r24,0b1111
#else
	ldi	r24,0b11
#endif
#ifdef PRR	// Nicht bei ATtiny11/12
	out	PRR,r24		// Alles deaktivieren
// Initialisieren: Portpins, Analogvergleicher
	ldi	r24,0b000011
	out	DIDR0,r24
#endif
	ldi	r24,0b000100
	out	PORTB,r24
	ldi	r24,0b111001
	out	DDRB,r24	// PB5:0 = LLLhzL
	ldi	r24,0
0:	rjmp	1f
1:	dec	r24
	brne	0b		// 1 ms warten: PB1 = Ucc/2 von außen
	ldi	r24,0b010000
	out	DDRB,r24	// PB5:0 = zLzhzz
	ldi	r24,0b00010011
	out	ACSR,r24	// Auslöser = steigende Flanke
	out	ACSR,r24	// Interrupt-Flag löschen
// Analogvergleicher: ACO = AIN0 - AIN1 = UPB0 - UPB1:
// Ohne Empfangssignal liefert die Schaltung an PB1 eine (ein klein wenig)
// höhere Spannung als an PB0 (Spannungsteiler aus R10 und R11).
// Negative Spitzen erscheinen als „positive Interrupts“
#ifdef DEBUG
4:	ldi	r24,0
	out	MCUSR,r24
	in	r24,WDTCR
	sbr	r24,1<<4
	out	WDTCR,r24
	ldi	r24,0b11000001	// Interrupt bei Watchdog alle 32 ms
	out	WDTCR,r24
	wdr
#endif

// Hauptschleife:
// Wenn und solange Analogvergleicher H liefert, PB2 nach L ziehen
3:	sbis	ACSR,4		// Interrupt?
	 rjmp	2f
33:	cbi	PORTB,2
	sbi	DDRB,2		// PB2 = L
1:	sbi	ACSR,4		// Interrupt löschen
#ifdef DEBUG
	sbic	ACSR,5
	 rjmp	33b		// wenn (erneut) H
	sbi	PORTB,2		// wenn L
	cbi	DDRB,2
#else
	ldi	r24,10
0:	dec	r24
	brne	0b		// 1 Periode (40 kHz = 25 µs) abwarten
	sbic	ACSR,4		// Erneute steigende Flanke?
	 rjmp	1b
	cbi	DDRB,2
	sbi	PORTB,2		// PB2 = h: Pullup reaktivieren
	ldi	r24,10
0:	dec	r24
	brne	0b		// warten bis PB2 H-Pegel annimmt (25 µs)
#endif

// Wenn PB2 L ist (von außen), Puls ausgeben, sonst weiter Analogvergleicher abfragen
#ifdef DEBUG
2:	//sbis	PINB,2
	// rjmp	0f
	in	r24,WDTCR
	sbrs	r24,7		// Watchdog-Timeout da?
	 rjmp	3b		// nein, weiter Analogverleicher-Output auslesen
#else
2:	sbic	PINB,2
	 rjmp	3b
#endif

// Burst (40 kHz @ 1,2 MHz) ausgeben: 15 Takte pro Halbwelle
0:	ldi	r25,0b000100	// Clamp aktivieren (Pullup belassen)
	ldi	r24,0b111001	// PB5:0 = LLLhzL
	out	PORTB,r25
	out	DDRB,r24	// Ausgänge aktivieren
	ldi	r22,16		// 8 Vollwellen
	ldi	r23,0b011000
	ldi	r25,0b010100	// [1] PB4:3 = HL
0:	out	PORTB,r25	// [1] Membran bewegen
	eor	r25,r23		// [1] dann HL
	ldi	r24,3		// [1]
1:	dec	r24
	brne	1b		// 3×[3]-[1] = [8]
	dec	r22
	brne	0b		// [3] beim Sprung, [2] beim Ende
// Wie im Oszillogramm des originalen SR04 zu sehen,
// den letzten Impuls doppelt lang machen.
	ldi	r24,4
1:	dec	r24
	brne	1b		// 4×[3]-[1] = [11]
	ldi	r25,0b000100	// PB5:0 = LLLhzL
	ldi	r24,0b111001
	out	PORTB,r25	// Clamp halten, Membran bedämpfen mit 1 kOhm
	out	DDRB,r24
	ldi	r24,50
1:	dec	r24		// Wandler ausschwingen lassen (braucht lange!)
	brne	1b
	ldi	r24,0b011000	// PB5:0 = zLLhzz
	out	DDRB,r24	// Clamp lösen, C6 laden lassen
#ifdef DEBUG
	rjmp	4b
#else
// Warten (= keine Meldemöglichkeit) solange PB2 L ist (von außen):
// Repetierender (Stör-)Ultraschall bei permanent Low (zum Testen)
	ldi	r25,hi8(3000)
	ldi	r24,lo8(3000)	// ca. 20 ms (15 m Echodistanz)
0:
#ifdef ADCL
	sbiw	r24,1		// beeinflusst Z-Flag
#else
	subi	r24,1
	sbci	r25,1
#endif
	sbi	ACSR,4		// Möglichen Analogvergleicher-Interrupt löschen
	sbis	PINB,2
	 brne	0b		// raus wenn Zähler Null
	rjmp	3b		// zur Hauptschleife
#endif
Vorgefundene Kodierung: UTF-80