; file: f12.asm ; ; This is an output-only program to blink all 5 available ; outputs of the p12f509 8-pin chip in interesting ways. ; poly@omino.com, 2007 June ; ; About the patterns: ; * There are a collection of separate patterns ; * They play in some sequence ; * They are influenced by a very, very feeble ; pseudorandom number generator (see Rnd) ; * Some use a 1-bit dac with 8-bits resolution, to fade ; * They share a few subroutines down low, as is ; the 12f509 way. ; * The code is heavily macro'd, stay sharp. ; ; Interesting aspects of the PIC12F509 include: ; * CALL instruction has 8 bit range, so subroutines ; must be in the first 256 instructions ; * GOTO instruction has 9 bit range, so jumps must ; be in the first 512 instructions ; * Ok, actually, you can use the STATUS bit 5 "PA0" ; to set bit *nine* before either a GOTO or a ; CALL. So with care you can GOTO anywhere, ; or CALL 0x000-0x0ff and 0x200-0x2ff. So two ; quadrants cannot be CALLed at all. ; * File registers 0x07-0x1F are accessible normally ; * File registers 0x30-0x3F accessible only by FSR/INDF. ; * Two level subroutine stack ; * No interrupts ; INCLUDE "P12F509.INC" INCLUDE "macross12f509.inc" ; config ; ; NOTE! 20090429 Watchdog Timer Must Be ON! ; the end of the patterns sits and waits for the timeout, ; which is cool, because we get extra randomness from the ; two disparate clocks. ; __CONFIG _MCLRE_OFF & _CP_OFF & _WDT_ON & _IntRC_OSC ; available file registers 0x7-0x1f (25 registers) ; (there are more at 0x30-0x3f but only by ; FSR indirect addressing.) CBLOCK 0x07 ; t must be low, to map the same in both ; banks during indirect writes. t ; mm, temp for tiny macros led0:3 ; 1st: linear brightness, 2nd: running sum, 3rd: target brightness led1:3 led2:3 led4:3 led5:3 bitsOut rnd patternCount ; for any pattern, how many patternTime ; for any pattern, the main delay time. r0,r1 ; oh, usually delay registers, fadeout & glide patternVal1 ; owned by the pattern patternVal2 ; owned by the pattern ENDC CBLOCK 0x30 powerBump ; for pattern5 cblock30End ; keep this last ENDC IF cblock30End > 0x40 ERROR "CBLOCK30 exceeded" ENDIF ; stash to one of our indirect registers from W ; uses t! MOVWI MACRO _iReg MOVWF t MOVLF _iReg,FSR MOVFF t,INDF CLRF FSR ; otherwise upper bits affect MOVs ENDM ; Each 1-bit dac is maintained as 2 bytes: ; the brightness and the running-sum. ; (a third byte is sometimes the "target brightness") ; Rotate the current 1-bit dac on-offness ; into the right side of "dest". StepDac MACRO dacState,dest MOVFW dacState ADDWF dacState+1,F RLF dest,F ; put led in low bit ENDM LedsOff MACRO MOVLF 0xff,GPIO ; lights off while we rethink ENDM ; Poke a random value into a file register ; mask is anded, and min is added ; RANDF x,31,10 to get 10..41 RANDF MACRO fregdst,mask,min CALL Rnd MOVFW rnd IF mask != 0xff ANDLW mask ENDIF MOVWF fregdst IF min != 0 MOVLW min ADDWF fregdst,f ENDIF ENDM ORG 0 ; lets begin GOTO Start ; | ; | Keep subroutines down low, due to ; | address-range limitations of ; | P12F509 RefreshLeds: ; advance each dac CLRF bitsOut StepDac led5,bitsOut StepDac led4,bitsOut ; just a zero for bit 3 BCF STATUS,C RLF bitsOut,f StepDac led2,bitsOut StepDac led1,bitsOut StepDac led0,bitsOut MOVFW bitsOut XORLW 0xFF ; invert for sink led MOVWF GPIO CLRWDT RETLW 0 ; ; Put a (very psuedo) random number into ; the 'rnd' file register. Rnd: MOVFW rnd ADDWF TMR0,w RLF rnd,f RLF rnd,f ADDWF rnd,f RETLW 0 ; ; Bring all the dacs down to zero ; Note -- nested subroutine call here! ; All _two_ of our stack is used up ; when you call this. ; uses: r0, r1 ; FadeoutLeds: CLRF r0 CLRF r1 _fadeoutLoop CALL RefreshLeds DECFPIN led0 ; 3 cycles CALL RefreshLeds DECFPIN led1 CALL RefreshLeds DECFPIN led2 CALL RefreshLeds DECFPIN led4 CALL RefreshLeds DECFPIN led5 ; and spin a while at same brightnesses _fadeoutLoop2 CALL RefreshLeds DECFSZ r1,f GOTO _fadeoutLoop2 CALL RefreshLeds ; keep the beat... DECFSZ r0,f GOTO _fadeoutLoop ; theoretically, they're all off now. RETLW 0 ; move each LED to its target value, pausing ; patternTime tween each step. ; do 256 steps to be sure. ; uses: r0, r1. GlideLeds: CLRF r0 ; outer counter, make sure we do 256 altogether _glideLedsLoop1: CALL RefreshLeds MIGFPIN led0,led0+2 CALL RefreshLeds MIGFPIN led1,led1+2 CALL RefreshLeds MIGFPIN led2,led2+2 CALL RefreshLeds MIGFPIN led4,led4+2 CALL RefreshLeds MIGFPIN led5,led5+2 ; now delay by patternTime refreshes... CALL RefreshLeds MOVFF patternTime,r1 ; 2 NOPS 6 _glideLedsLoop: CALL RefreshLeds NOPS 5 DECFSZ r1,f GOTO _glideLedsLoop NOP CALL RefreshLeds NOPS 5 DECFSZ r0,f GOTO _glideLedsLoop1 RETLW 0 Start: ; All five outputs to "out" ; GPIO 3 can only be "in" MOVLW 0 TRIS GPIO MOVLW b'10011111' ; 0x9F 7:wakeup' 6:pullup' 5:timer0Ext 4:timer0ExtEdge 3:prescalar wdt (off=timer0) 2,1,0: prescalar amt OPTION PatternsStart: ;GOTO PatternRampupRed GOTO PatternDormantGreen Pattern6: RANDF patternCount,0x3f,10 RANDF patternTime,0x1f,4 MOVLF 1,patternVal1 ; our light mask _p6loop: ; set each LED target according to the mask RANDF led0+2,0x7f,40 RANDF led1+2,0x7f,40 RANDF led2+2,0x7f,40 RANDF led4+2,0x7f,40 RANDF led5+2,0x7f,40 ; clear the ones that should be off ; (5 bits, but leds 0,1,2,4,5) IFBC patternVal1,0 CLRF led0+2 IFBC patternVal1,1 CLRF led1+2 IFBC patternVal1,2 CLRF led2+2 IFBC patternVal1,3 CLRF led4+2 IFBC patternVal1,4 CLRF led5+2 CALL GlideLeds CALL GlideLeds CALL GlideLeds ; now, choose a new set that matches none ; of the previous set ; note we have a random-number retry going on. ; sure hope the random number thingie doesnt ; get stuck. uck. ; (WDT would help here.) _p6nextLights RANDF patternVal2,0x1f,0 ; new random set MOVFW patternVal1 ; clear out any that were on.. XORLW 0x1f ANDWF patternVal2,f ; done IFZ GOTO _p6nextLights ; no? try again MOVFF patternVal2,patternVal1 DECFSZ patternCount,f GOTO _p6loop CALL FadeoutLeds CALL FadeoutLeds CALL FadeoutLeds ; now, we wait for the Watch Dog Timer ; to fire. since its on a separatre clock, ; it lets the timer advance a very random ; amount, increasing our diversity. WaitUntilTimeout: GOTO WaitUntilTimeout GOTO PatternsStart PatternDormantGreen: ; other leds all off, then just do led0. MOVLF 0x00,led1 MOVLF 0x00,led2 MOVLF 0x00,led4 MOVLF 0x00,led5 MOVLF 0x00,led1+2 MOVLF 0x00,led2+2 MOVLF 0x00,led4+2 MOVLF 0x00,led5+2 GO_AND_WAIT_0 MACRO _goTime,_target,_holdTime MOVLF _goTime,patternTime MOVLF _target,led0+2 CALL GlideLeds MOVLF _holdTime,patternTime CALL GlideLeds ENDM GO_AND_WAIT_0 2,0xb0,0x6 GO_AND_WAIT_0 2,0x30,0x4 GO_AND_WAIT_0 2,0xff,0x6 GO_AND_WAIT_0 2,0x30,0x4 GO_AND_WAIT_0 2,0xff,0x6 GO_AND_WAIT_0 0x20,0x10,0x3 GO_AND_WAIT_0 0x5,0x00,0x1 IFBC GPIO,3 GOTO PatternRampupRed GOTO PatternDormantGreen GO_AND_WAIT_1 MACRO _goTimeReg,_target,_holdTimeReg MOVLF _goTime,patternTime MOVLF _target,led0+2 CALL GlideLeds MOVLF _holdTime,patternTime CALL GlideLeds ENDM PatternRampupRed: MOVLF 0,led0 MOVLF 0,led0+2 MOVLF 0xff,led1+2 ; red tgt MOVLF 0xf0,patternTime CALL GlideLeds ;----------------------- ; enter the 'scape, with white-blinking handshake MOVLF 0xff,led1 MOVLF 0xff,led1+2 MOVLF 0xff,led2+2 MOVLF 0xff,led4+2 MOVLF 0xff,led5+2 MOVLF 0x40,patternTime CALL GlideLeds MOVLF 10,patternVal1 _fastStartBlink ; blink without glide at the end, quite fast MOVLF 0,led4+0 MOVLF 0,led4+2 MOVLF 0,led5+0 MOVLF 0,led5+2 MOVLF 0x3,patternTime CALL GlideLeds MOVLF 0xff,led4+0 MOVLF 0xff,led4+2 MOVLF 0xff,led5+0 MOVLF 0xff,led5+2 MOVLF 0x1,patternTime CALL GlideLeds DECFSZ patternVal1 GOTO _fastStartBlink MOVLF 0x0,led2+2 MOVLF 0x0,led4+2 MOVLF 0x0,led5+2 MOVLF 0x10,led0+2 MOVLF 0x30,patternTime CALL GlideLeds ;----------------------- ; now, do alternating whites _prr: MOVFF patternVal1,led1+2 ; red ORLF 0x3f,led1+2 MOVFF patternVal1,led2+2 MOVFF patternVal2,led4+2 MOVFF patternVal1,led5+2 ADDFF patternVal2,led5+2 MOVLF 1,patternTime CALL GlideLeds ;MOVLF 3,patternTime CALL GlideLeds ADDLF 125,patternVal1 ADDLF 129,patternVal2 IFBC GPIO,3 GOTO _prr ; exit the 'scape MOVLF 0xff,led2+2 MOVLF 0xff,led4+2 MOVLF 0xff,led5+2 MOVLF 0x40,patternTime CALL GlideLeds MOVLF 12,patternVal1 _fastEndBlink ; blink without glide at the end, quite fast MOVLF 0,led4+0 MOVLF 0,led4+2 MOVLF 0,led5+0 MOVLF 0,led5+2 MOVLF 0x2,patternTime CALL GlideLeds MOVLF 0xff,led4+0 MOVLF 0xff,led4+2 MOVLF 0xff,led5+0 MOVLF 0xff,led5+2 MOVLF 0x1,patternTime CALL GlideLeds DECFSZ patternVal1 GOTO _fastEndBlink MOVLF 0x0,led1+2 MOVLF 0x0,led2+2 MOVLF 0x0,led4+2 MOVLF 0x0,led5+2 MOVLF 0x10,led0+2 MOVLF 0xc0,patternTime CALL GlideLeds CALL GlideLeds GOTO PatternDormantGreen ; ----------------------------- ; High Code ; Code up here in needs to track STATUS:PA0 for ; the 9th bit of the address on GOTO or CALL. We ; keep it set while up here. We clear it to GOTO ; a low address, and temporarily clear it to ; CALL a low address ; ;ORG 0x200 Pattern7High: MOVLF 0xf0,led0 MOVLF 0x80,led1 MOVLF 0x40,led2 MOVLF 0x20,led4 MOVLF 0x10,led5 CALLLO FadeoutLeds GOTOHILO PatternsStart END ; EOF