;Ansteuerung von Triacs, möglichst pinkompatibel zu SLB0587
;(Nur!) Wegen der günstigen Anordnung der Betriebsspannungsanschlüsse
;wurde hier Microchip PIC12 gegenüber Atmel ATtiny favorisiert.
;Hier gelistete Typen waren gerade zum Test verfügbar.
;Der geeignetste Typ ist 12C508A (OTP: einmal programmierbar, 1,00€)
;oder 12F508 (Flash: wiederprogrammierbar, 1,25€),
;weil billig und keine überflüssige Peripherie eingebaut.
;Für die anderen 8poligen Typen PIC12F629, PIC16F675, PIC12CE673
;sowie den 18poligen „Wald- und Wiesen-PIC“ PIC16F84A
;ist ein Kommandozeilenschalter /p (Prozessor) zu übergeben.
;MPLAB IDE (hierfür überflüssig) schluckt die Endung .A16 nicht.
;Eine Mini-Include-Datei oder Umbenennung zu *.asm löst das Problem.
;Für die Einbindung des PIC-Assemblers MPASM in CR-Edit, UltraEdit
;oder Programmers Notepad (unter Verzicht auf MPLAB IDE und
;damit des Simulators) steht eine PICASM.CMD zur Verfügung,
;die neben MPASM.EXE kopiert werden muss.
;PN2-Einbindung: Parameter "%f /p%?", Prozessortyp angeben!
;MPASM schluckt anstandslos UTF-8 (im Gegensatz zu MPLAB IDE).
;haftmann#software, henrik.haftmann@e-technik.tu-chemnitz.de,
;http://www.tu-chemnitz.de/~heha/Mikrocontroller/SLB0587Ersatz.htm
;061021 erste Ausgabe, ohne "Safety-Cutout", aber schon mit PLL
; Funktion der Modusauswahl OK, Diode von Pin4 nach Pin1
;061104 Test bestanden, Verbindung Pin3 über Widerstand 100 Ohm
; zum Gate des Triac
;071219 Korrektur der Initialisierung für PIC12F629/675
#ifdef __16F84A ;übliches, nicht pinkompatibles Testmuster
list p=16f84a,r=dec
include "p16f84a.inc"
__config _XT_OSC & _WDT_OFF & _PWRTE_ON
RAMSTART equ 10h ;ab hier freie RAM-Arbeitszellen
#define GPIO PORTA
messg "8 Warnungen bitte ignorieren!"
extrainit macro
endm
#else ;dummwerweise kennt MPASM.EXE kein #elseif(def) -> Schachtelung!
#ifdef __12F675 ;Flash, Reichelt: 1,75€
list p=12f675,r=dec
include "p12f675.inc"
__config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _BODEN_ON &_PWRTE_ON
RAMSTART equ 20h ;ab hier freie RAM-Arbeitszellen
messg "8 Warnungen bitte ignorieren!"
extrainit macro
movlw ANSEL
movwf FSR
clrf INDF ;A/D-Wandler totlegen - sonst chaotische Funktion!
movlw 7
movwf CMCON ;Analogvergleicher totlegen
endm
#else
#ifdef __12F629 ;Flash, Reichelt: 1,45€
list p=12f629,r=dec
include "p12f629.inc"
__config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _BODEN_ON & _PWRTE_ON
RAMSTART equ 20h ;ab hier freie RAM-Arbeitszellen
messg "8 Warnungen bitte ignorieren!"
extrainit macro
movlw 7
movwf CMCON ;Analogvergleicher totlegen
endm
#else
#ifdef __12CE673 ;OTP, Farnell
list p=12ce673,r=dec
include "p12ce673.inc"
__config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _PWRTE_ON
RAMSTART equ 20h ;ab hier freie RAM-Arbeitszellen
messg "Warnungen bitte ignorieren! Ungetesteter Zielprozessor!"
extrainit macro
movlw ADCON1
movwf FSR
movlw 7
movwf INDF
endm
#else ; alles andere:
;PIC12C508A (PIC12C508, PIC12C509A, PIC12C509): OTP, Reichelt: 1,00€
;PIC12F508 (PIC12F509): Flash, Reichelt: 1,25€
list p=12c508a,r=dec
include "p12c508a.inc"
__config _IntRC_OSC & _WDT_OFF & _MCLRE_OFF
RAMSTART equ 08h ;ab hier freie RAM-Arbeitszellen
extrainit macro
endm
#endif
#endif
#endif
#endif
__idlocs 1207h ;Monat + Jahr der Firmware (BCD)
;#define SIMULATE ;Warteschleifen abkürzen (für neueres MPLAB unnötig)
;#define DEBUG ;EXT (SENSOR2) ist Debug-Ausgang: Marker für Oszi
;#define HELLER ;minimaler Phasenwinkel Null (geht nicht trotz C4!)
;****************
;** Konstanten **
;****************
;Belegung GPIO (Pin-Nummer in Klammern, nicht gültig für 16F84)
TRIAC2 equ 0 ;(7) Triac-Steuerausgang, 0=EIN, Z=AUS
SENSOR2 equ 1 ;(6) EXTerner Sensor, H=EIN
SENSOR equ 2 ;(5) SENsor, L=EIN (im Abtastzeitpunkt)
USENSE equ 3 ;(4) SYNC, Spannungsüberwachung am Triac
TRIAC equ 4 ;(3) Triac-Steuerausgang, 0=EIN, 1=AUS
MODE equ 5 ;(2) PROG an - (A), offen (B), an + (C)
;Der Triac kann WAHLWEISE an TRIAC oder TRIAC2 angeschlossen werden,
;je nach geeigneterer Modifikation der Leiterplatte oder Schaltung.
;Masseschluss des Ausgangs TRIAC2 ist ohne Folgen.
;************************
;** RAM-Speicherzellen **
;************************
tmp0 equ RAMSTART+0 ;Zwischenwert (Berechnung)
KeyBits equ RAMSTART+1 ;Diverse Bits zur Tastensteuerung
;0 = Sensor-Drückzähler Bit 0 (Schieberegister-Nachbildung)
;1 = Sensor-Drückzähler Bit 1 (beide =1 gilt als "gedrückt" (>50ms))
;2 = Sensor-Langzeitdruck (>400ms)
;3 = Richtungsbit, 1=hoch, 0=runter (Helligkeit)
;4 = MODE klebt an - (Modus "A" lt. Datenblatt)
;5 = MODE klebt an + (Modus "C" lt. Datenblatt)
;6 = Licht EIN (ansonsten ist "Hell" der Speicherwert)
;7 = Sensor-Zustand (0,9 ms nach dem Nulldurchgang ermittelt), 1 = berührt
KeyCnt equ RAMSTART+2 ;Zähler für Autorepeat
Hell equ RAMSTART+3 ;Soll-Helligkeit (Zündwinkel)
Real equ RAMSTART+4 ;Ist-Helligkeit wegen Softstart (Zündwinkel)
HellMin equ RAMSTART+5 ;Mindest-Helligkeit (25%)
HellMax equ RAMSTART+6 ;Höchst-Helligkeit (95%)
T2 equ RAMSTART+7 ;Halbe Periodendauer
CPUCLK equ 1000000 ;Hz (etwa; exakter Wert unkritisch wegen Zeitmessung)
DEFGPIO equ 3Eh ;Normalzustand für GPIO-Ausgaberegister
#ifdef DEBUG
DEFTRIS equ 2Dh ;Normalzustand für TRIS-Ausgaberegister: TRIAC+EXT=Ausgang
#else
DEFTRIS equ 2Fh ;Normalzustand für TRIS-Ausgaberegister: TRIAC=Ausgang
#endif
DEFOPT equ 0C6h ;keine Pull-Ups(!!), Vorteiler=128, Zählerperiode > 32ms
;Zur Begrenzung der Nulldurchgangs-Suche (15 und 22 sind Millisekunden)
MINCT0 equ 15*CPUCLK/1000/128 ;Minimaler "erlaubter" Zählerstand (117)
MAXCT0 equ 22*CPUCLK/1000/128 ;Maximaler "erlaubter" Zählerstand (171)
SAMCT0 equ 1*CPUCLK/1000/128 ;Zählerstand zum Abtasten von SENSOR (7)
;*********************
;** Programm im ROM **
;*********************
org 0
goto startup
;====================
;== Unterprogramme ==
;====================
AdjustLimits:
;Anhand des gespeicherten Wertes für T2 die Werte HellMin(30%), HellMax(95%)
;berechnen und ggf. "Hell" eingrenzen
clrc
rrf T2,w ;viertel Periodendauer (50%) laden
movwf tmp0
clrc
rrf tmp0,w ;achtel Periodendauer (75%)
movwf tmp0
subwf T2,w ;3/8 Periodendauer (25%)
movwf HellMin
subwf Hell,w ;w=Hell-HellMin, C=0 wenn Hell<HellMin (OK)
movfw HellMin
skpnc
movwf Hell
#ifdef HELLER
clrf HellMax ;immer 1
incf HellMax,f
#else
clrc
rrf tmp0,f ;1/16 Periodendauer (88%)
; clrc
; rrf tmp0,f ;dann 1/64 Periodendauer (97%)
clrc
rrf tmp0,w ;1/32 Periodendauer (94%)
movwf HellMax
subwf Hell,w ;w=Hell-HellMax, C=0 wenn Hell<HellMax (nicht OK)
movfw HellMax
skpc
movwf Hell
#endif
retlw 0
CheckSensor:
;Bei (etwa) 0,9 ms nach Nulldurchgang STROBE_SEN ausführen
#ifdef DEBUG
bsf GPIO,SENSOR2
#endif
movlw SAMCT0
;ggf. addwf T2,w
xorwf TMR0,w ;w=TMR0^SAMCT0
skpz
retlw 0
bcf KeyBits,7
;SENSOR ist L, wenn eine externe Kapazität (Mensch) den H-L-Wechsel verzögert
btfss GPIO,SENSOR
bsf KeyBits,7 ;EIN wenn LOW
#ifdef DEBUG
btfsc KeyBits,7
bcf GPIO,SENSOR2 ;Markierung im Oszibild (wenn berührt)
#endif
retlw 0
ZeroCross: ;Sucht Nulldurchgang (des Stroms) so dass Spannung negativ wird
;Bei >22ms Periodendauer wird von einem Fehler ausgegangen
;Warten bis USENSE=H, dabei Zählerüberlauf detektieren
zc_1:
call CheckSensor
movlw MAXCT0
subwf TMR0,w ;w=TMR0-MAXCT0, C=0 wenn TMR0<MAXCT0
skpnc
retlw 0 ;C=1: Fehler
btfss GPIO,USENSE
goto zc_1 ;warte solange USENSE=L
zc_2:
call CheckSensor
movlw MAXCT0
subwf TMR0,w
skpnc
retlw 0
btfsc GPIO,USENSE
goto zc_2 ;warte solange USENSE=H
movlw MINCT0
subwf TMR0,w ;w=TMR0-MINCT0, C=0 wenn TMR0<MINCT0
bnc zc_1 ;warte solange zu wenig Zeit vergangen ist
clrc
rrf TMR0,w ;Zählerwert laden und halbieren
clrf TMR0 ;Zähler neu starten
movwf T2 ;neue halbe Periodendauer
clrc
retlw 0
HandleSensor: ;Aufruf alle 20ms, nachdem USENSE auf L gegangen ist
btfsc KeyBits,7 ;Bit gesetzt wenn Sensor berührt wird
goto hs_touched
#ifndef DEBUG
btfsc GPIO,SENSOR2 ;SENSOR2 ist H, wenn externer Taster betätigt
goto hs_touched
#endif
;Sensor nicht berührt: letzten Zustand prüfen und Zählbits nullsetzen
btfss KeyBits,0
goto hs_release_0 ;Zähler <>3: svw. Schieberegister leeren
btfss KeyBits,1
goto hs_release_0
btfss KeyBits,2
goto hs_release_short;Licht schalten
movlw 1<<3 ;Richtungsbit
btfss KeyBits,4 ;Wenn MODE an + klebt: Richtung NICHT ändern
xorwf KeyBits,f
goto hs_release_0 ;Licht nicht schalten
hs_release_short:
movlw 1<<6
xorwf KeyBits,f ;Licht ein- bzw. ausschalten
hs_release_0: ;svw. Schieberegister leeren
bcf KeyBits,0
bcf KeyBits,1
retlw 0
hs_count_on:
incf KeyBits,f ;Schieberegister-Nachbildung mit Einsen füllen
btfss KeyBits,0
retlw 0
btfss KeyBits,1
retlw 0
;Jetzt geht der Tastendruck los: Wenn Licht aus, dann ggf. Maximum laden
btfsc KeyBits,6 ;Licht aus?
goto hs_noloadmax
movfw HellMax
btfsc KeyBits,4 ;Klebt an -? Maximum laden!
movwf Hell
btfsc KeyBits,5 ;Klebt an +? Maximum laden!
movwf Hell
hs_noloadmax:
bcf KeyBits,2 ;"Kurzer Tastendruck" melden
movlw 8 ;Zähler für 400ms
hs_keycntx:
movwf KeyCnt
retlw 0
hs_touched: ;Sensor für diese Vollwelle berührt
btfss KeyBits,0
goto hs_count_on ;Schieberegister-Nachbildung noch nicht gefüllt
btfss KeyBits,1
goto hs_count_on
decf KeyCnt,f ;Autorepeat-Zähler
skpz
retlw 0 ;nichts tun wenn <>0
btfsc KeyBits,2 ;"Lange" Berührung?
goto hs_longtouch
btfsc KeyBits,6 ;Hier: Übergang von kurzer zu langer Berührung
goto hs_long_on ;Wenn Licht bereits EIN, kein Minimum laden
movfw HellMin
btfsc KeyBits,4 ;Klebt an -? Minimum laden!
movwf Hell
btfsc KeyBits,5 ;Klebt an +? Minimum laden!
movwf Hell
bsf KeyBits,6 ;Licht AN
hs_long_on:
bsf KeyBits,2 ;auf "lange Berührung" umschalten
movlw 5 ;erster Repeat länger als 3
goto hs_keycntx ;Zähler laden
hs_longtouch:
movfw HellMin ;besser mit "subwf" arbeiten!
xorwf Hell,w ;Minimum ist erreicht?
skpnz
bsf KeyBits,3 ;Richtung RAUF
movfw HellMax
xorwf Hell,w ;Maximum ist erreicht?
skpnz
bcf KeyBits,3 ;Richtung RUNTER
btfsc KeyBits,3
decf Hell,f ;RAUF (Anschnittwinkel verkleinern)
btfss KeyBits,3
incf Hell,f ;RUNTER (Anschnittwinkel vergrößern)
movlw 3
goto hs_keycntx
HandleSoftstart: ;Aufruf nach HandleSensor (alle 20ms)
;Nachführung von "Real" an "Hell" - oder AUS
movfw HellMin
btfss KeyBits,6 ;Licht EIN?
goto hs_set ;wenn AUS dann nächsten Softstart vorbereiten
;auf Verdacht Zündwinkel verkleinern, dann begrenzen
decf Real,f
decf Real,f ;in 2 Schritten
movfw Real
subwf Hell,w ;W=Hell-Real, C=0 wenn Hell<Real
movfw Hell
skpnc ;überspringe wenn Real>Hell
hs_set:
movwf Real ;wenn Real<=Hell Zündwinkel halten oder vergrößern
retlw 0
ReadMode: ;Verbindung (nach + oder -) am MODE-Pin prüfen
bcf KeyBits,4
bcf KeyBits,5
#ifdef DEBUG ;weil mit bcf und bsf herumgefuhrwerkt wird...
movlw DEFGPIO&~(1<<SENSOR2)
movwf GPIO ;H vorbereiten
#endif
movlw DEFTRIS&~(1<<MODE)
tris GPIO ;H ausgeben (statt Z vorher)
movlw DEFTRIS ;1 µs warten
btfsc GPIO,MODE ;wenns an Minus (Uss) klebt
goto rm_nominus
tris GPIO ;Treiber sofort entlasten (Kurzschluss!)
bsf KeyBits,4
retlw 0 ;Nulldurchgangsmarker kurz (Mode A)
rm_nominus:
#ifdef DEBUG
movlw DEFGPIO&~((1<<MODE)|(1<<SENSOR2))
#else
movlw DEFGPIO&~(1<<MODE)
#endif
movwf GPIO ;L ausgeben
movlw DEFTRIS ;1 µs warten (reicht offenbar)
btfss GPIO,MODE ;wenns an Plus (Udd) klebt
goto rm_noplus
tris GPIO ;Treiber sofort entlasten (Kurzschluss!)
bsf KeyBits,5
#ifdef DEBUG
bsf GPIO,SENSOR2 ;zusätzliche High-Nadel (Mode C: 2 Nadeln)
bcf GPIO,SENSOR2
#endif
rm_noplus:
#ifdef DEBUG
bsf GPIO,SENSOR2 ;zusätzliche High-Nadel (Mode B: 1 Nadel)
bcf GPIO,SENSOR2
#endif
tris GPIO ;Z ausgeben
#ifdef DEBUG
movlw DEFGPIO&~(1<<SENSOR2)
#else
movlw DEFGPIO
#endif
movwf GPIO
retlw 0 ;Nulldurchgangsmarker mittel (Mode B)
FireNow: ;10 Zündimpulse (je 10µs) ausgeben
movlw 10
movwf tmp0
fn_loop:
movlw DEFGPIO&~(1<<TRIAC)
movwf GPIO ;L ausgeben
movlw DEFTRIS&~(1<<TRIAC2)
tris GPIO ;L ausgeben - Zündung
goto $+1
goto $+1
goto $+1
goto $+1
movlw DEFGPIO
movwf GPIO ;H ausgeben
movlw DEFTRIS
tris GPIO ;Z ausgeben
decf tmp0,f
bnz fn_loop
retlw 0
FireNeg: ;Zündung Triac zur negativen (ersten) Halbwelle
fn_wait:
call CheckSensor
movfw Real
xorwf TMR0,w
bnz fn_wait
goto FireNow ;Maximale Stacktiefe erreicht!
;Bei der negativen Halbwelle wäre die Messung des Zünderfolgs möglich.
;Die dazu notwendige Zeit könnte zur positiven Halbwelle wiederholt werden,
;da hierbei nicht messbar.
;retlw 0
FirePos: ;Zündung Triac zur positiven (zweiten) Halbwelle
fp_wait:
call CheckSensor
movfw Real
addwf T2,w
xorwf TMR0,w
bnz fp_wait
goto FireNow
;retlw 0
;===================
;== Hauptprogramm ==
;===================
startup:
;* Aktion 1:
; Ports einrichten, Option setzen
movlw DEFOPT
option
movlw DEFGPIO
movwf GPIO
movlw DEFTRIS
tris GPIO
extrainit
;Viertelsekunde warten (nur aus Angst!)
clrf KeyCnt
clrf tmp0
s_wait: decf tmp0,f
bnz s_wait
decf KeyCnt,f
bnz s_wait
clrf KeyBits
;* Aktion 2:
; H-L-Flanke abwarten, Timer starten (Timer zählt aufwärts)
fail:
#ifdef DEBUG
bcf GPIO,SENSOR2
#endif
f1: btfss GPIO,USENSE
goto f1
f2: btfsc GPIO,USENSE
goto f2
clrf TMR0
;* Aktion 3:
; Variablen vorbelegen
call ReadMode
call ZeroCross ;Periodendauer messen
call AdjustLimits
movfw HellMax ;damit es im Mode B mit vollem Licht losgeht
movwf Hell ;(ansonsten andere Vorgabewerte)
#ifdef DEBUG
bsf GPIO,SENSOR2
#endif
;Hauptschleife
mainloop:
call ZeroCross
bc fail
#ifdef DEBUG
bcf GPIO,SENSOR2 ;Markierung im Oszibild
#endif
call ReadMode
#ifdef DEBUG
bsf GPIO,SENSOR2
#endif
call HandleSensor ;Etwa nach ZeroCross SENSOR2 einlesen
call AdjustLimits
call HandleSoftstart
btfss KeyBits,6 ;weiter nur wenn EIN
goto mainloop
call FireNeg
call FirePos
goto mainloop
END
Detected encoding: ASCII (7 bit) | 8
|