; n18M2.bas 234 lines (203 with data), 6.4 kB
; *************************************************************
;
; Damon Hart-Davis licenses this file to you
; under the Apache Licence, Version 2.0 (the "Licence");
; you may not use this file except in compliance
; with the Licence. You may obtain a copy of the Licence at
;
; http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing,
; software distributed under the Licence is distributed on an
; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
; KIND, either express or implied. See the Licence for the
; specific language governing permissions and limitations
; under the Licence.
;
; *************************************************************
; Author(s) / Copyright (c): Damon Hart-Davis 2013
; Flashing 7 LEDs, a red/green bi-colour in the centre; 6 surrounding it in a circle.
; Bi-colour LED between B.3 and B.6 outputs (via 330R+).
; All other B.0 to B.7 outputs driving LEDs (connected to ground via 330R+) in clockwise order around the circle.
#picaxe 18m2 ; 18M2+ device.
#no_data ; Not using the EEPROM.
dirsB = %11111111 ; All outputs.
symbol LED_BI1_RED_C = B.3 ; Bi-colour LED red cathode (short leg), green anode.
symbol LED_BI1_RED_A = B.6 ; Bi-colour LED red anode (long leg), green cathode.
; C.0: LDR light sensor (ADC input); higher voltage indicates more ambient light.
; Should be pulled low externally whether LDR used or not, so can be left as input or set to low output.
symbol INPUT_LDR = C.0
; Light level (0 is dark, 255 is light).
symbol ambientLighting = B24
readadc INPUT_LDR, ambientLighting ; Take initial reading.
#rem
testPWM:
readadc INPUT_LDR, ambientLighting ; Take initial reading.
pinsB = $ff ; Everything on.
gosub centreRedSet
gosub pauseAndPWMLeds1s
toggle LED_BI1_RED_C, LED_BI1_RED_A
gosub pauseAndPWMLeds1s
goto testPWM
#endrem
; Speed (and possibly brightness) settings.
symbol MAX_SPEED = 4
symbol MIN_SPEED = 0
symbol speed = B25 ; 0 is slowest (can be used with nap).
symbol randWord = W13 ; Random number store
symbol randB1 = B26
symbol randB2 = B27
; Initialise with bits from the internal temperature sensor.
readinternaltemp IT_RAW_L, 0, randWord
; Warm up with some spins...
for speed = MIN_SPEED to MAX_SPEED
gosub spinCW
gosub spinCW
gosub spinCCW
gosub spinCCW
next
sleep 1
mainloop:
readadc INPUT_LDR, ambientLighting
W0 = ambientLighting * MAX_SPEED / 255
speed = W0
; Maybe do some spinning...
do while randB1 >= $40 ; 0 or more times, typically 2 or 3...
random randWord
if randB2 >= $80 then
gosub spinCW
else
gosub spinCCW
endif
loop
random randWord
readadc INPUT_LDR, ambientLighting
; More likely to turn LEDs off and sleep here as ambient lighting falls.
if ambientLighting < randB1 then
pinsB = 0 ; All off...
sleep 1
else
B12 = 255 - ambientLighting min 1
gosub pauseAndPWMLeds
endif
; Do some flashing...
gosub briefRandomRound
pinsB = 0 ; All off...
sleep 1
gosub reseed
goto mainloop
; Stir a little entropy into the pool!
reseed:
readinternaltemp IT_RAW_L, 0, W0
randWord = randWord ^ W0
random randWord
randWord = randWord ^ time
random randWord
; Pause for *nominally* 1s, matching LED output to ambient lighting.
; Destroys B11, B12, B13, B14, B15.
pauseAndPWMLeds1s:
B12 = 100
; Pause in B12 units of *nominally* 10ms, matching LED output to ambient lighting.
; Destroys B11, B13, B14, B15.
pauseAndPWMLeds:
B11 = pinsB ; Capture extant LED state.
; Software PWM on nominal 10ms cycle.
B14 = ambientLighting / 25 min 1 max 10 ; On time: never 0.
B15 = 10 - B14 min 1; Off time: nominally can be zero (for max brightness).
for B13 = 1 to B12
; Aim for LED duty cycle to go down with the ambient light level.
pause B14
pinsB = 0 ; Turn all LEDs off.
pause B15
pinsB = B11 ; Restore state of LEDs.
next
return
; Pauses in inverse proportion to speed and may adjust brightness of LEDs to match (software PWM).
; Attempts to conserve energy too.
; Destroys B10, B11, B12, B13, B14, B15
pauseToMatchSpeed:
B10 = MAX_SPEED - speed ; B10 is pause time on 'nap' (log) scale.
B12 = 1
if B10 > 0 then : for B13 = 1 to B10 : B12 = B12 + B12 : next : endif ; B12 is pause time on (linear) scale.
gosub pauseAndPWMLeds
return
; Spin clockwise at specified speed.
spinCW:
pinsB = 0; All off...
gosub centreRedSet
high B.0
gosub pauseToMatchSpeed
high B.1
low B.0
gosub pauseToMatchSpeed
high B.2
low B.1
gosub pauseToMatchSpeed
high B.4
low B.2
gosub pauseToMatchSpeed
high B.5
low B.4
gosub pauseToMatchSpeed
high B.7
low B.5
gosub pauseToMatchSpeed
low B.7
return
; Spin counter-clockwise at specified speed.
spinCCW:
pinsB = 0; All off...
gosub centreGreenSet
high B.7
gosub pauseToMatchSpeed
high B.5
low B.7
gosub pauseToMatchSpeed
high B.4
low B.5
gosub pauseToMatchSpeed
high B.2
low B.4
gosub pauseToMatchSpeed
high B.1
low B.2
gosub pauseToMatchSpeed
high B.0
low B.1
gosub pauseToMatchSpeed
low B.0
return
; Do brief round of random flashing at current speed, with fewer and dimmer LEDs on with lower ambient lighting.
; Destroys B0 and others.
briefRandomRound:
random randWord
B0 = randB2 & $3f + 32; Between ~32 and 95 rounds...
do while B0 > 0
dec B0
gosub allRandomSet
if ambientLighting < $80 then
random randWord
pinsB = pinsB & randB1 ; For low light knock out about half the LEDs.
if ambientLighting < $20 then
pinsB = pinsB & time ; For very low light knock out about half of the remaining LEDs.
endif
endif
gosub pauseToMatchSpeed
loop
return
; Randomise all the LED states, as normal (B) outputs, using randB2.
allRandomSet:
random randWord
dirsB = %11111111 ; All outputs.
pinsB = randB2
return
; Turn the centre LED red.
centreRedSet:
low LED_BI1_RED_C
high LED_BI1_RED_A
return
; Turn the centre LED red.
centreGreenSet:
high LED_BI1_RED_C
low LED_BI1_RED_A
return