How to do this got me thinking so I set about designing a wiper implementation and describing how I went about that. It all uses "top down" design, then filling in the missing bits which is often a good design strategy.
This is the overall design of a wiper controller as typically implemented in a vehicle. There are four wipe modes; "off", "wipe once" a single wipe then off until activated again; "intermittent" where there is a wipe and a delay before the next wipe; and "continuous" where wipes repeat one after another with no delay.
We don't have to worry yet about how we select the mode, how the wipers are actually controlled, or what the pause between intermittent wipes should be or how that is calculated.
We also don't yet have to worry that wiping for a boat may alternate between port and starboard.
Code:
Symbol MODE_OFF = 0
Symbol MODE_WIPE_ONCE = 1
Symbol MODE_INTERMITTENT = 2
Symbol MODE_CONTINUOUS = 3
Symbol mode = b6
MainLoop:
Do
Gosub ReadMode
Select Case mode
Case MODE_WIPE_ONCE
Gosub Wipe
Do
Gosub ReadMode
Loop Until mode <> MODE_WIPE_ONCE
Case MODE_INTERMITTENT:
Do
Gosub Wipe
Gosub PauseBetweenWipes
Loop Until mode <> MODE_INTERMITTENT
Case MODE_CONTINUOUS:
Do
Gosub Wipe
Loop Until mode <> MODE_CONTINUOUS
If mode = MODE_INTERMITTENT Then
Gosub PauseBetweenWipes
End If
Else
Gosub WipersOff
End Select
Loop
The next ting is to determine the mode. In this case we are reading an ADC and determining which of four settings it has.
Here we'll just assume there's a POT input divided into four equal sections, one per mode, "off", "single", "intermittent" and "continuous".
The order of the modes reflects a typical steering wheel wiper control; down for a single wipe, up for intermittent, then up further for continuous.
Code:
Symbol MODE_ADC = C.0
Symbol reserveW0 = w0 ; b1:b0
ReadMode:
ReadAdc MODE_ADC, b0
Select Case b0
Case < $40 : mode = MODE_WIPE_ONCE
Case < $80 : mode = MODE_OFF
Case < $C0 : mode = MODE_INTERMITTENT
Else : mode = MODE_CONTINUOUS
End Select
Return
Next we'll handle wiper control. This will depend on hardware but here we are simply setting one output high for a time which is the forward wipe, then another pin high for a backwards wipe. When done the wipe has completed.
Code:
Symbol MOTOR_FORWARD = B.4
Symbol MOTOR_BACKWARD = B.5
WipersOff:
Low MOTOR_FORWARD
Low MOTOR_BACKWARD
Return
Wipe:
High MOTOR_FORWARD
Pause 3000
Low MOTOR_FORWARD
High MOTOR_BACKWARD
Pause 3000
Low MOTOR_BACKWARD
Return
Now we'll deal with the delay between wipes when in intermittent modes. We will read another pot which determines a delay, here, of between 1 and 10 seconds, 1000ms to 10000ms.
Code:
Symbol INTERVAL_ADC = C.4
Symbol MIN_INTERVAL_MS = 1000
Symbol MAX_INTERVAL_MS = 10000
Symbol interval = w1 ; b3:b2
PauseBetweenWipes:
ReadAdc INTERVAL_ADC, b0
interval = MAX_INTERVAL_MS - MIN_INTERVAL_MS * b0 / 255 + MIN_INTERVAL_MS
Pause interval
Return
But there's a problem with that. We have a hard delay so any adjustment of the interval delay or mode won't be seen until after that delay is ended. It would be better to have that more responsive.
We do that by counting how long we have waited for, checking what the interval and mode should be and exiting when the mode changes or the interval has been exceeded.
Code:
Symbol waited = w2 ; b5:b4
DetermineInterval:
ReadAdc INTERVAL_POT, b0
interval = MAX_INTERVAL_MS - MIN_INTERVAL_MS * b0 / 255 + MIN_INTERVAL_MS
Return
PauseBetweenWipes:
waited = 0
Do
Pause 100
waited = waited + 100
Gosub ReadMode
Gosub DetermineInterval
Loop Until mode <> MODE_INTERMITTENT or waited >= interval
Return
So we are done. Except for a bug which is an overflow in our 'DetermineInterval' routine. When maximum we read '255' and '255*10000" overflows. We can fix that with -
Code:
DetermineInterval:
ReadAdc INTERVAL_POT, b0
If b0 <= 65 Then
interval = MAX_INTERVAL_MS - MIN_INTERVAL_MS * b0 / 255 + MIN_INTERVAL_MS
Else
interval = MAX_INTERVAL_MS - MIN_INTERVAL_MS / 4 * b0 / 64 + MIN_INTERVAL_MS
End If
Return
Now, for a boat, we are going to add separate and alternative port and starboard wiping.
The first thing we do is update our wiper control code -
Code:
Symbol PORT_MOTOR_FORWARD = B.2
Symbol PORT_MOTOR_BACKWARD = B.3
Symbol STRB_MOTOR_FORWARD = B.4
Symbol STRB_MOTOR_BACKWARD = B.5
WipersOff:
Low PORT_MOTOR_FORWARD
Low PORT_MOTOR_BACKWARD
Low STRB_MOTOR_FORWARD
Low STRB_MOTOR_BACKWARD
Return
WipeBoth:
High PORT_MOTOR_FORWARD
High STRB_MOTOR_FORWARD
Pause 3000
Low PORT_MOTOR_FORWARD
Low STRB_MOTOR_FORWARD
High PORT_MOTOR_BACKWARD
High STRB_MOTOR_BACKWARD
Pause 3000
Low PORT_MOTOR_BACKWARD
Low STRB_MOTOR_BACKWARD
Return
WipePort:
High PORT_MOTOR_FORWARD
Pause 3000
Low PORT_MOTOR_FORWARD
High PORT_MOTOR_BACKWARD
Pause 3000
Low PORT_MOTOR_BACKWARD
Return
WipeStarboard:
High STRB_MOTOR_FORWARD
Pause 3000
Low STRB_MOTOR_FORWARD
High STRB_MOTOR_BACKWARD
Pause 3000
Low STRB_MOTOR_BACKWARD
Return
And now we need to make the alternating sequence work -
Code:
MainLoop:
Do
Gosub ReadMode
Select Case mode
Case MODE_WIPE_ONCE
Gosub WipePort
Gosub WipeStarboard
Do
Gosub ReadMode
Loop Until mode <> MODE_WIPE_ONCE
Case MODE_INTERMITTENT:
Do
Gosub WipePort
Gosub WipeStarboard
Gosub PauseBetweenWipes
Loop Until mode <> MODE_INTERMITTENT
Case MODE_CONTINUOUS:
Do
Gosub WipePort
Gosub WipeStarboard
Loop Until mode <> MODE_CONTINUOUS
If mode = MODE_INTERMITTENT Then
Gosub PauseBetweenWipes
End If
Else
Gosub WipersOff
End Select
Loop
And our complete code, barring changes for any specific application, is as follows -
Code:
Symbol MODE_ADC = C.0
Symbol INTERVAL_ADC = C.4
Symbol PORT_MOTOR_FORWARD = B.2
Symbol PORT_MOTOR_BACKWARD = B.3
Symbol STRB_MOTOR_FORWARD = B.4
Symbol STRB_MOTOR_BACKWARD = B.5
Symbol MIN_INTERVAL_MS = 1000
Symbol MAX_INTERVAL_MS = 10000
Symbol MODE_WIPE_ONCE = 0
Symbol MODE_OFF = 1
Symbol MODE_INTERMITTENT = 2
Symbol MODE_CONTINUOUS = 3
Symbol reserveW0 = w0 ; b1:b0
Symbol interval = w1 ; b3:b2
Symbol waited = w2 ; b5:b4
Symbol mode = b10
MainLoop:
Do
Gosub ReadMode
Select Case mode
Case MODE_WIPE_ONCE
Gosub WipePort
Gosub WipeStarboard
Do
Gosub ReadMode
Loop Until mode <> MODE_WIPE_ONCE
Case MODE_INTERMITTENT:
Do
Gosub WipePort
Gosub WipeStarboard
Gosub PauseBetweenWipes
Loop Until mode <> MODE_INTERMITTENT
Case MODE_CONTINUOUS:
Do
Gosub WipePort
Gosub WipeStarboard
Loop Until mode <> MODE_CONTINUOUS
If mode = MODE_INTERMITTENT Then
Gosub PauseBetweenWipes
End If
Else
Gosub WipersOff
End Select
Loop
ReadMode:
ReadAdc MODE_ADC, b0
Select Case b0
Case < $40 : mode = MODE_WIPE_ONCE
Case < $80 : mode = MODE_OFF
Case < $C0 : mode = MODE_INTERMITTENT
Else : mode = MODE_CONTINUOUS
End Select
Return
WipersOff:
Low PORT_MOTOR_FORWARD
Low PORT_MOTOR_BACKWARD
Low STRB_MOTOR_FORWARD
Low STRB_MOTOR_BACKWARD
Return
WipeBoth:
High PORT_MOTOR_FORWARD
High STRB_MOTOR_FORWARD
Pause 3000
Low PORT_MOTOR_FORWARD
Low STRB_MOTOR_FORWARD
High PORT_MOTOR_BACKWARD
High STRB_MOTOR_BACKWARD
Pause 3000
Low PORT_MOTOR_BACKWARD
Low STRB_MOTOR_BACKWARD
Return
WipePort:
High PORT_MOTOR_FORWARD
Pause 3000
Low PORT_MOTOR_FORWARD
High PORT_MOTOR_BACKWARD
Pause 3000
Low PORT_MOTOR_BACKWARD
Return
WipeStarboard:
High STRB_MOTOR_FORWARD
Pause 3000
Low STRB_MOTOR_FORWARD
High STRB_MOTOR_BACKWARD
Pause 3000
Low STRB_MOTOR_BACKWARD
Return
DetermineInterval:
ReadAdc INTERVAL_ADC, b0
If b0 <= 65 Then
interval = MAX_INTERVAL_MS - MIN_INTERVAL_MS * b0 / 255 + MIN_INTERVAL_MS
Else
interval = MAX_INTERVAL_MS - MIN_INTERVAL_MS / 4 * b0 / 64 + MIN_INTERVAL_MS
End If
Return
PauseBetweenWipes:
waited = 0
Do
Pause 100
waited = waited + 100
Gosub ReadMode
Gosub DetermineInterval
Loop Until mode <> MODE_INTERMITTENT or waited >= interval
Return
All untested except for simulation so there may be some bugs or other operational issues.