Als Vater eines Kleinkindes kommt man schon mal auf die Idee eine elektronische Spielerei zu basteln. Eine Martins-Laterne muss zwar nicht unbedingt sein, denn die werden in der Kita zu genüge von den Kleinen selbst produziert, Rabimmel Rabammel Rabumm Habt ihr vielleicht auch etwas Elektronik gebastelt nur um die jüngsten zu begeistern? Ich wäre für weitere Projektvorstellungen aus der Spielzeug-Branche dankbar.
Ich habe meine Idee realisiert, die Modellwaschstraße meiner Tochter mit einer Ampel aufzuhübschen. Der PIC16F675 ist für kleine LED-Anwendungen ideal. 6 IO Pins reichen z.B. für zwei Anforderungstaster, zwei grüne und zwei rote LED's.
Und so sah es dann auf der Baustelle aus:
Hier die gefädelte "Steuerungszentrale". Drei Micro-Zellen sind etwas überdimensioniert. Mit zwei Zellen (und entspr. kleineren LED Vorwiderständen) hätte es auch getan, aber der Batteriehalter stammt aus dem Kellerfundus - Reste und Projektüberbleibsel, die man bei solchen Projektchen gerne verbraucht.
Baustelle ist fertig, die Waschstraße kann den Betrieb wiederaufnehmen. Die Fahrzeuge warten schon
Nachtrag am 18.02.14
Ich habe mein Quellcode an der Stelle gelöscht, weil ich niemandem zumuten will, meine Umsetzungsfehler zu kopieren. Statt dessen hier ein PAD (oder Programmablaufplan, PAP, Flußdiagramm, Flow Chart oder wie es auch es sonst noch genannt wird). Ich habe das PAD nachträglich erstellt, um das Programm für mich zu dokumentieren:
Bei der Erstellung des PAD habe ich einige Fehler in der Umsetzung gefunden und festgestellt, dass das ganze nicht optimal strukturiert ist und alles andere als mustergültig ist. Muss es auch nicht, weil das nur eine kleine Spielerei war, entstanden mehr oder weniger aus Codeschnipseln diverser Experimente mit PIC-Timern, Interrupts und Assembler-Schrittketten.
Nachtrag am 19.02.14
Die optimale Art, das Quellcode zu strukturieren und zu dokumentieren habe ich für mich noch nicht gefunden. Das erstellen von FlowCharts z.B. mit dem OpenOffice Draw ist sehr zeitaufwändig. Es stört mich auch, zwei Dateien pflegen zu müssen, aus Programmier-Erfahrung weiß ich dass man nicht immer die Quellcode-Änderungen in die Doku aufnimmt. So lange es keine Möglichkeit gibt, aus einem Diagramm das Assembler Quellcode zu generieren ist die ASCII Art innerhalb des Quellcode glaub ich schon die richtige. Hier mein Quellcode mit überarbeitetem Kommentar. Natürlich ohne Gewährleistung!
Nachtrag am 04.03.14
So ein Projektchen ist immer wieder ein dankbares Versuchsobjekt für Programmiertechniken. Ich habe mal etwas mit Wertetabellen experimentiert und die gleiche Aufgabe mit Tabellen gelöst. Auch die Art der Kommentare ist ein Experiment an sich, im Moment versuche ich es mit einem C-ähnlichen Pseudocode.
Nachtrag am 07.03.14
Den Quellcode mit Software Timern überarbeitet in Anlehnung an A. M. König "Das große PIC-Mikro Handbuch".
Code:
; Filename: main.asm
; - Zwei Rot/Grün Ampeln mit Anforderungstaster
; - Gegenverkehr gesperrt
; - Ampelphasen: Grün/Grün blinkend/Rot
; - nach 3 Minuten Standby-Modus: LED's aus, MCU in SLEEP
; - wecken durch Anforderungstaster
;
; Projekthinweise:
; - Header Files: P12F675.INC, Makrodefs.inc
; - Include Files: InitPIC12F675.asm
; - 4MHz interner Oszillator,
; - alle GPIO Ports digital I/O, Komparator und DAC aus
; - GPIE und IOC aktiv für RA0, RA3 (Wake-Up from SLEEP mit Anforderungstaster)
;
; Version | Datum | Autor | Kommentar
; --------|--------|-------|-----------------------------------------------------
; 2.0.0 |04.03.14| wg| Schaltwerk mit Tabellen
; 2.0.2 |06.03.14| wg| Softwaretimer (keine Verschwendung von TMR0 Interrupt für Blinkerei)
; 2.0.3 |07.03.14| wg| einfachere Programmstruktur ohne Interrupts
;=== Konfiguration =============================================================
LIST P=PIC12F675
#include <P12F675.INC>
errorlevel -302
__CONFIG _CP_OFF & _CPD_OFF & _BODEN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT
#include <MakroDefs.inc>
;=== Variablen Deklaration =====================================================
cblock 0x20
W_TEMP
STATUS_TEMP
BaseEvent ; 4ms Basistakt-Eventwert von Timer0
BaseStep ; Basistakt Counter für abgeleitete Timer
SekEvent ; Sekundentakt-Eventwert von Basistakt
BlinkTmr ; Anwendungstimer für BlinkEvent
SleepTmr ; Anwendungstimer für Schlafmodus, Startwert 180s
AmpelTmr ; Anwendungstimer für Ampelphase-Änderung
AmpelFlags ;
Ampelphase ; Zustandswort der Ampelschaltung
endc
#define maskTaster b'00001001' ; GPIO Binärmaske Taster
#define maskAmpel1Gr b'00000010' ; GPIO Binärmaske Ampel1 Grün
#define maskAmpel1Rd b'00000100' ; GPIO Binärmaske Ampel1 Rot
#define maskAmpel2Gr b'00010000' ; GPIO Binärmaske Ampel2 Grün
#define maskAmpel2Rd b'00100000' ; GPIO Binärmaske Ampel2 Rot
;=== Programmstart =============================================================
org 0x0000
call initMCU ; //InitPIC12F675_Carwash.asm
; *** Initialisierung Variablen Startwerte
bcf STATUS,RP0 ; //change to bank 0
clrf BlinkTmr ; BlinkTmr = 0;
M_MOVLF SleepTmr, d'180' ; SchlafTmr = 3Minuten Startwert;
clrf Ampelphase ; Ampelphase = 0;
;=== Hauptschleife =================================================================
main
; *** Basis Timer 4ms
movf BaseEvent, w ; do{
subwf TMR0, w ;
andlw 0xC0 ;
skpz ;
goto main ; } while ((TMR0 - BaseEvent) = 0 bis 63);
M_ADDLF BaseEvent, d'125' ; BaseEvent += 125; // für nächten 4ms Event
incf BaseStep ; BaseStep += 1; // 4ms Basis Zähler hochzählen
; *** Blinktimer
movf BlinkTmr ; if ( BlinkTmr !=0 )
skpz ;
decf BlinkTmr ; BlinkTmr -= 1;
; *** Sekundentimer ;
movf SekEvent, w ; if (SekEvent == Basistimer)
subwf BaseStep, w ;
skpz ;
goto main_ende ; {
M_ADDLF SekEvent, d'250' ; SekEvent += 250 * 4ms;
; *** Ampeltimer ;
movf AmpelTmr, f ;
skpz ; if AmpelTmr != 0
decf AmpelTmr ; AmpelTmr -= 1;
; *** Sleeptimer ;
decf SleepTmr, f ; SleepTmr -= 1;
skpz ;
goto main_wakeup ; if (SleepTmr == 0) {
movlw maskTaster ;
andwf GPIO, f ; GPIO &= Taster; // LED's löschen
sleep ; Sleep();
nop ; }
main_wakeup
movf GPIO, w ; read(GPIO); //notwendig zum resetten von interrupt-on-change
btfss INTCON, GPIF ; if (GPIF)
goto main_ende ; {
bcf INTCON, GPIF ; GPIO Interruptflag = 0;
M_MOVLF SleepTmr, d'180' ; Schlafcounter = Startwert 3min;
main_ende ; }
;=== Ampelmodul ================================================================
;*** Ampelmodul Definitionen
#define tasteAmpel1Anf GPIO, GP0 ; Anforderung Ampel1
#define tasteAmpel2Anf GPIO, GP3 ; Anforderung Ampel2
#define fAmpel1Anf AmpelFlags,0 ; Ampel1 angefordert
#define fAmpel2Anf AmpelFlags,1 ; Ampel2 angefordert
#define PhaseAmpelrd 0 ; Ampel1 und Ampel2 rot
#define PhaseAmpel1gn 1 ; Ampel1 grün
#define PhaseAmpel1gnblnk 2 ; Ampel1 grün blinkend
#define PhaseAmpel2gn 3 ; Ampel2 grün
#define PhaseAmpel2gnblnk 4 ; Ampel2 grün blinkend
movf AmpelTmr ;
skpz ; if (AmpelTmr == 0)
goto Ampelm_Blinken ; {
movf Ampelphase, w ;
call tabNaechsteAmpelphase ; Ampelphase = naechste(Ampelphase);
movwf Ampelphase ;
call tabPhasendauer ; AmpelTmr = Phasendauer(Ampelphase);
movwf AmpelTmr ;
call subLEDSchalten ; LEDs Schalten();
Ampelm_Blinken ; }
movf BlinkTmr, f ; if (BlinkTmr == 0)
skpz ;
goto Ampelm_Anfordg ; {
movf Ampelphase, w ;
call tabBlinkmaske ; w = Blinkmaske(Ampelphase);
xorwf GPIO, f ; GPIO = GPIO ^ w;
M_MOVLF BlinkTmr, d'62' ; BlinkTmr = 248ms;
Ampelm_Anfordg ; }
btfss tasteAmpel1Anf ; if (taste Ampel1 gedrückt)
bsf fAmpel1Anf ; Ampel1 Anforderung = 1;
btfss tasteAmpel2Anf ; if (taste Ampel2 gedrückt)
bsf fAmpel2Anf ; Ampel2 Anforderung = 1;
movf Ampelphase, w ;
call tabAnforderungssperre ;
andwf AmpelFlags, f ; AmpelFlags &= Anforderungssperre(Ampelphase);
movf Ampelphase, f ; if (Ampelphase = Rot)
skpz ; {
goto Ampelm_Ende ;
Ampelm_Anfordg_1 ;
btfss fAmpel1Anf ; if (Ampel1 angefordert)
goto Ampelm_Anfordg_2 ; {
bcf fAmpel1Anf ; Ampel1 Anforderung = 0;
M_MOVLF Ampelphase, PhaseAmpel1gn ; Ampelphase = Ampel1 grün;
call tabPhasendauer ; AmpelTmr = Phasendauer(Ampelphase);
movwf AmpelTmr ;
call subLEDSchalten ; LEDs Schalten();
goto Ampelm_Ende ; }
Ampelm_Anfordg_2 ;
btfss fAmpel2Anf ; else if (Ampel2 angefordert)
goto Ampelm_Ende ; {
bcf fAmpel2Anf ; Ampel2 Anforderung = 0;
M_MOVLF Ampelphase, PhaseAmpel2gn ; Ampelphase = Ampel2 grün;
call tabPhasendauer ; Ampelcountdown = Phasendauer(Ampelphase);
movwf AmpelTmr ;
call subLEDSchalten ; LEDs Schalten(Ampelphase);
; }
;*** Ampelmodul Programmende
Ampelm_Ende
goto main ;
;*** UP Ampel LEDs Einschalten
subLEDSchalten ; subLEDSchalten() {
movf Ampelphase, w ;
call tabLEDs ; W = LEDs(Ampelphase) | BinärmaskeTaster;
iorlw maskTaster ; // Taster-Eingänge nicht überschreiben wegen IOC
andwf GPIO, f ; GPIO &= W // überflüssige LEDs aus
iorwf GPIO, f ; GPIO |= W // LEDs an
return ; }
;***************************
;*** Ampelmodul Tabellen ***
; Achtung auf die Page!
tabAnforderungssperre ; Anforderungssperre(Ampelphase)
addwf PCL, f
dt 0xFF ; 0 -> Anforderungen erlaubt
dt b'11111110' ; 1 -> Anforderungen Ampel1 gesperrt
dt b'11111110' ; 2 -> Anforderungen Ampel1 gesperrt
dt b'11111101' ; 3 -> Anforderungen Ampel2 gesperrt
dt b'11111101' ; 4 -> Anforderungen Ampel2 gesperrt
tabNaechsteAmpelphase ; NaechsteAmpelphase(Ampelphase) // nach Timer Ablauf
addwf PCL, f
dt PhaseAmpelrd ; 0 -> 0
dt PhaseAmpel1gnblnk ; 1 -> 2
dt PhaseAmpelrd ; 2 -> 0
dt PhaseAmpel2gnblnk ; 3 -> 4
dt PhaseAmpelrd ; 4 -> 0
tabPhasendauer ; Phasendauer(Ampelphase)
addwf PCL, f
dt 0 ; 0: Ampeln rot -> 0s
dt d'7' ; 1: Ampel1 grün -> 7s
dt d'2' ; 2: Ampel1 grün blinkend -> 2s
dt d'7' ; 3: Ampel2 grün -> 7s
dt d'2' ; 4: Ampel2 grün blinkend -> 2s
tabLEDs ; LEDs(Ampelphase)
addwf PCL, f
dt (maskAmpel1Rd | maskAmpel2Rd) ; 0: Ampel1 rot, Ampel2 rot
dt (maskAmpel1Gr | maskAmpel2Rd) ; 1: Ampel1 grün, Ampel2 rot
dt (maskAmpel1Gr | maskAmpel2Rd) ; 2: Ampel1 grün, Ampel2 rot
dt (maskAmpel2Gr | maskAmpel1Rd) ; 3: Ampel1 rot, Ampel2 grün
dt (maskAmpel2Gr | maskAmpel1Rd) ; 4: Ampel1 rot, Ampel2 grün
tabBlinkmaske ; Blinkmaske(Ampelphase)
addwf PCL, f
dt 0 ; 0:
dt 0 ; 1:
dt maskAmpel1Gr ; 2: Ampel1 grün blinkend
dt 0 ; 3:
dt maskAmpel2Gr ; 4: Ampel2 grün blinkend
;=== Ende Gelände ==============================================================
#include <InitPIC12F675_Carwash.asm>
END
;===============================================================================
Code:
; Filename: InitPIC12F675_Carwash.asm
;
; Version | Datum | Autor | Kommentar
; --------|--------|-------|-----------------------------------------------------
; 1.0.0 |05.03.04| WG | Initialisierung 12F675 für Projekt Carwash
; | | | Exclude from build !
; 1.0.1 |07.03.04| WG | kein TMR0 Interrupt, Prescale 1:32
; | | | kein GIE
;
;*** Initialisierung Routine ***
initMCU
; *** OSCCAL auf kalibrierten wert setzen
banksel OSCCAL
call 0x3FF ; retrieve factory calibration value
movwf OSCCAL ; update register with factory cal value
; *** OPTION_REG settings
banksel OPTION_REG
movlw b'11000100'
; b'1-------' GPPU: 1 = GPIO pull-ups are disabled
; b'-1------' INTEDG: 1 = Interrupt on rising edge of GP2/INT pin
; b'--0-----' T0CS: 0 = Internal instruction cycle clock (CLKOUT)
; b'---0----' T0SE: 0 = Increment on low-to-high transition on GP2/T0CKI pin
; b'----0---' PSA: 0 = Prescaler is assigned to the TIMER0 module
; b'-----100' PS2:PS0 = 100, TMR0 prescaler rate 1:32
movwf OPTION_REG
banksel TMR0 ; // Write to TMR um Prescaler zu reseten
clrf TMR0
; clear GPIO
banksel GPIO
clrf GPIO
;*** COMPARATOR MODULE
banksel CMCON
movlw b'00000111'
; b'-----111' CM<2:0>: 111 = Comparators off. CxIN pins are configured as digital I/O
movwf CMCON
;*** ADC MODULE
banksel ANSEL
clrf ANSEL ; digital I/O
; *** GPIO Settings
banksel TRISIO
movlw b'00001001' ; GPI0,GPIO3 Eingang, GPIO<2:1>, GPIO<5:4> Ausgang
movwf TRISIO
; *** WPU Settings
movlw b'00000000'
; b'--00----' WPU<5:4>: <WPU5:WPU4> Weak Pull-up Control bits
; b'-----000' WPU<2:0>: <WPU2:WPU1:WPU0> Weak Pull-up Control bits
; 1 = Pull-up enabled
; 0 = Pull-up disabled
movwf WPU
; *** IOC Settings
movlw b'00001001' ; IOC<5:0>: Interrupt-on-Change GPIO Control bit
; 1 = Interrupt-on-change enabled
; 0 = Interrupt-on-change disabled
movwf IOC
;*** Interrupts
banksel INTCON
movlw b'00001000'
; b'0-------' GIE
; b'-0------' PEIE
; b'--0-----' T0IE
; b'---0----' INTE
; b'----1---' GPIE
; b'-----0--' T0IF
; b'------0-' INTF
; b'-------0' GPIF
movwf INTCON
return
Code:
; Filename: MakroDefs.inc
;
; Version | Datum | Autor | Kommentar
; --------|--------|-------|-----------------------------------------------------
; 1.0.0 |05.03.04| WG | Makro Definitionen für Projekt Carwash
; 1.0.1 |07.03.04| WG | M_ADDLF hinzugef.
;
M_MOVLF macro filereg, literal
movlw literal
movwf filereg
endm
M_ADDLF macro filereg, literal
movlw literal
addwf filereg, f
endm
Lesezeichen