Bewegungsmelder & Pulsdetektor          

auch zur optischen Blitzerkennung                    
von Gerd Sinning   
Elektronik-Labor  AVR  HF  Projekte 

Ein passiver optischer Bewegungsmelder mit LDR oder Photodiode reagiert auf Änderungen der Umgebungshelligkeit. Das gibt es schon mit analoger Elektronik, hier wird versucht, das in digitaler Technik zu realisieren. Das hat noch weitere Vorteile, siehe Text.

Dazu braucht man zunächst den Mittelwert der Helligkeit und dann das Signal, das durch die Abschattung des Sensors entsteht. Der LDR liegt mit einem Bein an +5V und über 10 k an GND. ADC3 sampled die Spannung alle 10 ms. Wenn das Licht weniger wird verringert sich die Spannung am ADC Eingang.. Dieser negative Puls bezogen auf den Mittelwert triggert dann den Bewegungsmelder und eine LED geht an, mit 100 ms als Alarmsignal.

Der Mittelwert (running average) wird allgemein so berechnet, averagevalue = ((x– 1) * averagevalue + currentvalue) / x . Im Grunde ist x beliebig, aber für digitale Systeme wird die Division durch 2^n zu einer einfachen shift Operation, daher averagevalue = ((2^n) – 1) * averagevalue + currentvalue) / 2^n. Hier wird der Mittelwert Avglight = (Avglight*15 + Currentlight)/16 ermittelt. Wenn man das mal zeichnet, dann sieht das so aus:
Ein digitales rekursives Filter 1. Ordnung, ausgehend von 10 wird alle 10 ms jeweils der konstante Wert 500 addiert.
Der erwartete Endwert 500 wird nicht ganz erreicht, das liegt an der Integer Arithmetik. Es sieht so aus wie ein RC Filter und dann lässt sich die Zeitkonstante t = RC berechnen, das ist die Zeit in der der Kondensator auf 63,2 % des Endwertes aufgeladen ist. V(RC) = E(1 – e–1) ≈ 0.632 . Nach 160 ms ist der Wert erreicht. In analoger Technik entspricht das einem RC mit 16 kOhm und einem 10 uF Kondensator.
Vom Mittelwert Avglight aus wird der Schwellwert berechnet Thresh = Avglight / Threshval, mit Threshval = 32 liegt der Wert bei 3,1% vom Mittelwert. Dann wird der Triggerwert berechnet Trig = Avglight - Thresh. Wenn das Eingangssignal diesen Wert unterschreitet dann wird Alarm ausgelöst If Currentlight < Trig Then... die LED an PB1 geht an, 100 ms lang als Alarm, auch einen Buzzer könnte man anschliessen. Thresh und Trig passen sich an das vorhandene Eingangssignal an, mit Threshval lässt sich einstellen wie empfindlich das System reagiert.



Zum Funktionstest schnell aufgebaut
Jetzt hat man einen LDR am ADC Eingang also kann man auch die Umgebungshelligkeit messen. Der Wert für Avglight wird jede Sekunde über die serielle Schnittstelle übertragen, an PB4. Ein Programm auf dem PC übernimmt die Daten, die kann man speichern und mit einem spreadsheet auswerten und als Diagramm anzeigen.
Das PC Programm, das Ausleseintervall ist einstellbar, das Bild rechts zeigt die letzten 100 Daten, die linke Skala geht von 0 bis 1023.
Damit kann man z.B. alle 5 Minuten die Umgebungshelligkeit messen und den Verlauf über einen Tag darstellen. Die Zeit ist die PC Systemzeit, im Statusbar wird die Startzeit, die nächste Lesezeit, die aktuelle Zeit und die Anzahl der Messungen angezeigt. Es geht auch mit jedem anderen Terminalprogramm auf dem PC, dann gibt es jede Sekunde einen Wert.

Wenn die Umgebungshelligkeit abnimmt und den Wert von Lolight = 40 unterschreitet (If Avglight < Lolight Then...) dann wird die LED an PB2 eingeschaltet, als Nachtlicht sozusagen. Bei Hilight = 60 wird sie wieder ausgeschaltet. Beide Werte kann man anpassen. LDR und LED muss man gegeneinander abschirmen, sonst blinkt es, die LED geht an und wieder aus..
Mit der digitalen Variante des Bewegungsmelders lässt sich einiges anfangen was analog nicht geht, ein 3 in 1 Programm in einem ATtiny13.

Pulsdetektor
Mit einem fast gleichen Programm lässt sich ein Pulsdetektor für einen Gewittermelder realisieren. Der Unterschied ist die sampling Zeit von 1 ms fur den ADC wegen der kürzeren Impulse und es fehlt die serielle Schnittstelle, dafür ist die Zeit von 1 ms zu kurz. Aber es gibt 3 LEDs, die an PB4 blinkt wenn getriggert ist, die an PB1 leuchtet 100ms und die an PB2 10 Sekunden lang. Wenn man einen Zähler an die Trigpulse LED PB4 anschließt, dann kann man die Gewitterpulse zählen.

Freundlicherweise stellt sich der Mittelwert auf die Ausgangsspannung des Vorverstärkers ein, so dass eine DC Kopplung funktioniert. Hier ist Threshval = 8, also 12.5% vom Mittelwert. Das muss man evtl. anpassen, je nach Vorverstärker.
Das Programm kann z.B. auch auf einem Mega48 laufen, dann mit Zähler und LCD Display.
Zitate:
http://www.elektronik-labor.de/AVR/Filter.html
http://www.elektronik-labor.de/Projekte/Blitzwarner3.html%20#opv
Microchip AN1202, Capacitive Sensing with PIC10F, Equation 1
2008 ARRL Handbook, Ch 4
datasheet ATtiny13, Atmel.com

Softwaredownload:  PulsDet.zip

'negative pulse detector Pdet2.bas
'
'running average to establish Avgvalue, threshold and trigger
'Trigpulse when triggerd
'Ledpulse turned on 100 ms
'Ledindic turned on 10 sec
'1 ms sampling time
'
'***************************************************************************
'
' Pinout ATtiny13/ATtiny13V 8-PDIP/SOIC
'
' (PCINT5/RESET/ADC0/dW) PB5 VCC
' (PCINT3/CLKI/ADC3) PB3 PB2 (SCK/ADC1/T0/PCINT2)
' (PCINT4/ADC2) PB4 PB1 (MISO/AIN1/OC0B/INT0/PCINT1)
' GND PB0 (MOSI/AIN0/OC0A/PCINT0)
'
'***************************************************************************
' PB0 output pulse IRQ
' PB1 output Ledpulse, hi
' PB2 output Ledindic, hi
' PB4 output Trigpulse
' PB3 input ADC channel 3
'***************************************************************************

$regfile = "attiny13.dat"
$crystal = 1200000
'$crystal = 9600000
$hwstack = 8
$swstack = 8
$framesize = 4

Ipulse Alias Portb.0
Ledpulse Alias Portb.1
Ledindic Alias Portb.2
Trigpulse Alias Portb.4

Const C_value = 150 - 1 ' Compare Match IRQ 1 ms
' Const C_value = 189 - 1 Compare Match IRQ 10 ms, 53 x 189
' C_value = 94 for 5 ms w Presc 64
' C_value = 150 for 1 ms w Presc 8
Const Threshval = 8 'Threshold 12.5%

Dim Currentvalue As Word , Avgvalue As Word
Dim Trig As Word , Icnt As Word
Dim Dcnt As Byte , Alarmflag As Byte , Thresh As Byte

Portb = &B00000000
Ddrb = &B00010111

Config Adc = Single , Prescaler = Auto , Reference = Avcc
Didr0.adc3d = 1
Start Adc

Config Timer0 = Timer , Prescale = 8
Ocr0a = C_value ' value for 1.2 MHz
Tccr0a = &B00000010 ' ctc
Timsk0.ocie0a = 1
On Oc0a Tim0_compa
Start Timer0

'CLKPR = &B10000000 'Clock Prescaler Change Enable
'CLKPR = &B00000000 'Clock Division Factor=0, 9.6MHz
Acsr.acd = 0 ' switch off analog comparator

'Open "comb.4:38400,8,n,1,INVERTED" For Output As #1
Enable Interrupts

Avgvalue = 1000

Do

If Icnt > 10000 Then
Ledindic = 0 'show longer '
Icnt = 0
End If

Loop

'***************************************************************************
'Tim0_compa Interrupt 1 ms
'***************************************************************************
Tim0_compa: '1 ms

Ipulse = 1
Incr Icnt

Currentvalue = Getadc(3)
Avgvalue = Avgvalue * 15 ' (Avgvalue*15 + Currentvalue)/16
Avgvalue = Avgvalue + Currentvalue
Shift Avgvalue , Right , 4 'running average

Thresh = Avgvalue / Threshval 'Threshold %
Trig = Avgvalue - Thresh
If Currentvalue < Trig Then
Alarmflag = 1
Trigpulse = 1 'pulse detected
Else
Trigpulse = 0
End If

If Alarmflag = 1 Then 'sw monoflop
Ledpulse = 1 ' Led on
Ledindic = 1
Incr Dcnt
If Dcnt > 99 Then 'reset sw monoflop
Ledpulse = 0
Alarmflag = 0
Dcnt = 0
End If
End If 'if Alarmflag = 1

Ipulse = 0

Return


End





'Motion detector with LDR or BPW34 MotDet2.bas
'
'running average to establish Avglight, threshold and trigger
'Ledalarm turned on 100 ms
'Lednight turned on when low light
'10 ms sampling time
'
'+5V -LDR- PB3 -10k- -GND
'
'***************************************************************************
'
' Pinout ATtiny13/ATtiny13V 8-PDIP/SOIC
'
' (PCINT5/RESET/ADC0/dW) PB5 VCC
' (PCINT3/CLKI/ADC3) PB3 PB2 (SCK/ADC1/T0/PCINT2)
' (PCINT4/ADC2) PB4 PB1 (MISO/AIN1/OC0B/INT0/PCINT1)
' GND PB0 (MOSI/AIN0/OC0A/PCINT0)
'
'***************************************************************************
' PB0 output Ipulse (calibrate 100 Hz)
' PB1 output Ledalarm hi
' PB2 output Lednight hi
' PB4 output COM1
' PB3 input ADC channel 3 LDR or BPW34
'***************************************************************************

$regfile = "attiny13.dat"
$crystal = 1200000
$hwstack = 8
$swstack = 8
$framesize = 4

Const C_value = 189 - 1 ' Compare Match IRQ 10 ms, 53 x 189
' C_value = 94 for 5 ms w Presc 64
' C_value = 150 for 1 ms w Presc 8

Const Threshval = 32 'Avglight divide by Threshval
Const Lolight = 40 'turn on Lednight
Const Hilight = 60 'turn off Lednight

Ipulse Alias Portb.0
Ledalarm Alias Portb.1
Lednight Alias Portb.2

Dim Currentlight As Word , Avglight As Word
Dim Thresh As Byte , Trig As Word
Dim Icnt As Byte , Dcnt As Byte , Alarmflag As Byte

Portb = &B00000000
Ddrb = &B00010111

Config Adc = Single , Prescaler = Auto , Reference = Avcc
Didr0.adc3d = 1
Start Adc

Config Timer0 = Timer , Prescale = 64
Ocr0a = C_value ' value for 1.2 MHz
Tccr0a = &B00000010 ' ctc
Timsk0.ocie0a = 1
On Oc0a Tim0_compa
Start Timer0

Acsr.acd = 0 ' switch off analog comparator

Open "comb.4:9600,8,n,1,INVERTED" For Output As #1
Enable Interrupts

Avglight = 10

Do
If Icnt = 100 Then '1000 ms
Icnt = 0
Print #1 , Avglight
End If
Loop 'main loop

'***************************************************************************
'Tim0_compa Interrupt 10 ms IRQ needs 600us
'***************************************************************************

Tim0_compa: '10 ms
Ipulse = 1
Incr Icnt

Currentlight = Getadc(3)
Avglight = Avglight * 15 ' (Avglight*15 + Currentlight)/16
Avglight = Avglight + Currentlight
Shift Avglight , Right , 4 'running average

Thresh = Avglight / Threshval 'Threshold 3.1 %
Trig = Avglight - Thresh
If Currentlight < Trig Then
Alarmflag = 1 'Alarm on
End If

If Avglight < Lolight Then 'when low light turn on Led
Lednight = 1
Ledalarm = 0 ' Ledalarm off
Alarmflag = 0 'no alarm when dark
Elseif Avglight > Hilight Then 'turn off Led
Lednight = 0
End If

If Alarmflag = 1 Then 'sw monoflop 100 ms
Ledalarm = 1 ' Led on
Incr Dcnt
If Dcnt = 10 Then 'reset sw monoflop
Ledalarm = 0
Alarmflag = 0
Dcnt = 0
End If
End If 'if Alarmflag = 1
Ipulse = 0

Return


End



Assembler-Vesion

Als Anhänger der Assemblerfraktion hat mich interessiert, ob der Pulsdetektor auch in Assembler geht. Er geht auch, die ganze Berechnung in 120 us auf einem AT15. Es ist Pinkompatibel zum Basic Programm und funktioniert auch so. Die Routine binasc konvertiert nur bis 999, ich hab es mit einer anderen binbcd versucht, aber die ging nicht. C'est la vie.
Download: Pdet1.zip


;***************************************************************************
; ATtiny15 Motion / Pulse Detector with LDR or BPW34
;
; RS232 9600 bps (inverted) sends: average+cr+lf
; binasc converts between 0,..,999
;
; running average to establish Avglight, threshold and trigger
; Ledpulse turned on 100 ms
; Lednight turned on when low light
; sampling 10ms @ 1.6 MHz
;
; GS July-2013
;
;
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License.
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY;
;
;***************************************************************************
; The timing is adapted for 1.6 MHz
; this At15 reads OSCCAL config val=0x7E, must calibrate for RS232
;***************************************************************************
; ATtiny15 dip
; (RESET/ADC0) PB5 VCC
; (ADC3) PB4 PB2 (ADC1/SCK/T0/INT0)
; (ADC2) PB3 PB1 (AIN1/MISO/OC1A)
; GND PB0 (AIN0/AREF/MOSI)
;***************************************************************************
;
; PB4 input ADC3: + LDR PB4 10k GND
; PB3 output RS232
; PB2 output Lednight
; PB1 output Ledpulse
; PB0 output Ipulse
;
;***************************************************************************

.DEVICE ATtiny15 ;for gavrasm 3.3

;***************************************************************************

.equ Ipulse = PB0 ;
.equ Ledpulse = PB1 ;Ledpulse
.equ Lednight = PB2
.equ Txd = PB3

.equ compval = 250-1 ; 10 ms @ 1.6 MHz
.equ hilight = 60
.equ lolight = 40

;***************************************************************************
;Register Definitions
;***************************************************************************
; r0 - ASCII- Low
; r1 - ASCII- Mid
; r2 - ASCII- High

.def zero = r8
.def trg_L = r9
.def trg_H = r10
.def Adc_L = r11
.def Adc_H = r12

.def Avg_L = r13
.def Avg_H = r14
.def sr = r15

.def temp = r16
.def temp1 = r17
.def counter1 = r18
.def counter2 = r19
.def flag = r20

.def tmp_L = r22
.def tmp_H = r23

.def bitcnt = r24
.def Txbyte = r25 ;Data to be transmitted
.def delay = r26

;***************************************************************************
.cseg
.org 0
; ***** INTERRUPT VECTORS **************************************************
rjmp reset ; Reset-vector address 0000
reti ; INT0addr= 0x0001 External Interrupt 0
reti ; PCI0addr= 0x0002 External Interrupt Request 0
rjmp TIM1_cmp ; OC1addr = 0x0003 Timer/Counter1 Compare Match
reti ; OVF1addr= 0x0004 Timer/Counter1 Overflow
reti ; OVF0addr= 0x0005 Timer/Counter0 Overflow
reti ; ERDYaddr= 0x0006 EEPROM Ready
reti ; ACIaddr = 0x0007 Analog Comparator
reti ; ADCCaddr= 0x0008 ADC Conversion Ready

;***************************************************************************
; "TIM1_Cmp" - Timer/counter 1 Compare Match interrupt handler
;
; interrupts every 10 ms, calibrate, IRQ needs 120 us
;
;***************************************************************************
TIM1_cmp:
in sr,SREG ;Get Status reg
sbi PORTB,Ipulse ;

inc counter2 ; 1sec in main

sbi ADCSR, ADIF ; clear ADIF flag
sbi ADCSR, ADSC ; start conversion
TIM1_w1: ; read ADC value
sbis ADCSR, ADIF
rjmp TIM1_w1
; ADC read:
in Adc_L, ADCL ; low byte
in Adc_H, ADCH ; then high byte

mov tmp_L, Avg_L ; calculate (avg * 15 + adc)/16
mov tmp_H, Avg_H
lsl Avg_L ; * 2
rol Avg_H
lsl Avg_L ; * 4
rol Avg_H
lsl Avg_L ; * 6
rol Avg_H
lsl Avg_L ; * 16
rol Avg_H
sub Avg_L, tmp_L ; -1 => *15
sbc Avg_H, tmp_H
add Avg_L, Adc_L ; avg * 15 + adc
adc Avg_H, Adc_H
lsr Avg_H
ror Avg_L ; l/2
lsr Avg_H
ror Avg_L ; l/4
lsr Avg_H
ror Avg_L ; l/8
lsr Avg_H
ror Avg_L ; l/16
mov tmp_L, Avg_L ; calculate thresh = avg / 16
mov tmp_H, Avg_H
lsr tmp_H
ror tmp_L ; l/2
lsr tmp_H
ror tmp_L ; l/4
lsr tmp_H
ror tmp_L ; l/8
lsr tmp_H
ror tmp_L ; l/16
mov trg_L, Avg_L ; calculate trig
mov trg_H, Avg_H
sub trg_L, tmp_L ; avg - thresh
sbc trg_H, tmp_H
cp trg_L, Adc_L
cpc trg_H, Adc_H ; compare trig with avg
brlo TIM1_ck0
inc flag
TIM1_ck0:
cpi flag, 0
breq TIM1_ck1
sbi PORTB, Ledpulse
inc counter1
cpi counter1, 10 ;100ms
brne TIM1_ck1
clr counter1
clr flag
cbi PORTB, Ledpulse

TIM1_ck1:
ldi tmp_L, lolight ; check avglight
clr tmp_H
cp Avg_L, tmp_L
cpc Avg_H, tmp_H ; compare lolight with avg
brsh TIM1_ck2
sbi PORTB, Lednight
cbi PORTB, Ledpulse
TIM1_ck2:
ldi tmp_L, hilight ; check avglight
clr tmp_H
cp Avg_L, tmp_L
cpc Avg_H, tmp_H ; compare hilight with avg
brlo TIM1_ex
cbi PORTB, Lednight
TIM1_ex:
cbi PORTB, Ipulse ; off
out SREG,sr ;
reti ;

;***************************************************************************
;*
;* "putchar" mod GS for inverted
;*
;* This subroutine transmits the byte stored in the "Txbyte" register
;* The number of stop bits used is set with the sb constant
;*
;* Number of words :14 including return
;* Number of cycles :Depens on bit rate
;* Low registers used :None
;* High registers used :2 (bitcnt,Txbyte)
;* Pointers used :None
;*
;***************************************************************************
.equ sb =1 ;Number of stop bits (1, 2, ...)
.equ b =52 ;9600 bps @ 1.6 MHz 167-9 cycles delay

putchar: ldi bitcnt,9+sb ;1+8+sb (sb is # of stop bits)
com Txbyte ;Invert everything, direct
sec ;Start bit

putchar0: brcc putchar1 ;If carry set
sbi PORTB,TxD ; send a '0' 1
rjmp putchar2 ; else

putchar1: cbi PORTB,TxD ; send a '1' 0
nop

putchar2:

ldi delay,b ; no more nested subroutines in AT15
putdelay: dec delay ; 2 * delay
brne putdelay

lsr Txbyte ;Get next bit
dec bitcnt ;If not all bit sent
brne putchar0 ; send next
;else
ret ; return

;***************************************************************************
reset:
; writing calibration byte to OSCCAL Register.
ldi temp,0x7E ; config val
out OSCCAL,temp ; for the cpu clock
nop
nop

ldi temp,0b00000000
out PORTB,temp
ldi temp,0b00001111
out DDRB,temp

sbi AcsR, Acd ; Disable Comparator

; AD-Converter setup
; ADMUX: REFS1 REFS0 ADLAR – – MUX2 MUX1 MUX0
ldi temp, 0b00000011 ; ADC3 input=PB4, REFS0,1 =0 Vcc is RefVoltage, result 0 - 1023
out ADMUX, temp
; ADCSR: ADEN ADSC ADFR ADIF ADIE ADPS2 ADPS1 ADPS0
ldi temp,0b11010011 ; ADC initialise single conversion and prescaler 8:
out ADCSR, temp
; timer1 setup:

ldi temp, compval ; Timer/Counter 1 top count
out OCR1A,temp ; compare value
ldi temp,0b10001011 ; Timer/Counter 1 clocked at CK/64 = 40 us
out TCCR1,temp ;
ldi temp, 0b01000000 ; enable OCIE1A interrupt
out TIFR,temp ; clear pending Interrupt
out TIMSK,temp

sei ; enable interrupts
;***************************************************************************

Main:
cpi counter2, 100 ; 1sec
brne Main
clr counter2
mov tmp_L, Avg_L ; convert and send
mov tmp_H, Avg_H ; needs 5 ms
rcall binasc
mov Txbyte, r2
rcall putchar
mov Txbyte, r1
rcall putchar
mov Txbyte, r0
rcall putchar
ldi Txbyte, 13 ; cr lf
rcall putchar
ldi Txbyte, 10
rcall putchar

rjmp Main ; loop


; ----------------------------------------------
; Unterprogramm zur Konvertierung Binär (10 Bit)
; in ASCII (3 Zeichen)
; benutzt Register r0,r1,r2,r22,r23
; Eingang: r22 - Binärzahl low
; r23 - Binärzahl High
; Ausgang: r0 - ASCII-Zahl Low
; r1 - ASCII-Zahl Mid
; r2 - ASCII-Zahl High
; ----------------------------------------------
;
binasc: clr r0 ;0-Wert für 16-Bit Subtraktion
clr r2 ;ASCII-High löschen
bin010: mov r1,r22 ;Binärzahl zwischenspeichern L
subi r22,100 ;100 subtrahieren
sbc r23,r0 ;Übertrag subtrahieren
brcs bin020 ;war Zahl>99? nein -> weiter
inc r2 ;ASCII-High erhöhen
rjmp bin010 ;Schleife
bin020: mov r22,r1 ;letzten Binärwert L holen
clr r1 ;ASCII-Mid löschen
bin030: cpi r22,10 ;Wert>9?
brcs bin040 ;nein -> Rest auswerten
inc r1 ;sonst ASCII-Mid erhöhen
subi r22,10 ;Wert um 10 vermindern
rjmp bin030 ;Schleife
bin040: mov r0,r22 ;Rest in ASCII-Low legen
ldi r23,0x30 ;Offset zur ASCII-Wandlung
or r0,r23 ;Ergebnis in ASCII wandeln
or r1,r23
or r2,r23
ret


Elektronik-Labor  AVR  HF  Projekte