;MultTrk03.spt - example program for MMi201cDK16

;This program controls a series of solenoid valves to produce a "squirt" pattern
;that is controlled partly by time, partly by external inputs. It illustrates
;a number of programming techniques.

;Function:
;---------

;There are 3 outputs called oSquirt0 to oSquirt2

;There are 4 inputs: iStart and iTrigger2_2, iTrigger3_1, iTrigger3_2, 

;The output sequence is always triggered by iStart. The sequence consists
;of a delay for each output followed by the output pulsing on for a preset time.
;The delay/pulse is initiated by different things for each "user program".

;There are 3 user selectable "programs". iStart always starts the program.

;Prog1: All output sequences are triggered by the start signal

;Prog2: oSquirt0 and oSquirt1 sequences start with the start signal.
;	oSquirt2 sequence starts with iTrigger2_2

;Prog3: oSquirt0 sequence starts with the start signal.
;	oSquirt1 sequence starts with iTrigger3_1
;	oSquirt2 sequence starts with iTrigger3_2
;					      | |
;				Program-------  |
;				Output ---------

;There is also a manual mode where the outputs are directly controlled by front 
;panel buttons.

;The program is written for an MMi201cDK216.

;Overall structure:
;------------------

;Each output has its own timer task. 
;The task gets started by a command that includes the program number.
;Delays are hardwired into each task. In that sense it is like a dishwasher controller
;where the user selects a program but cannot change the timings of each program.
;Each timer task has a code segment dedicated to each program.

;There is a simple user menu for selecing the program. The program is stored 
;in nonvolatile shadow memory.


;Menu structure:
;---------------

;The menu will only allow parameter changes when the machine is idle.
;Once started, the machine is made idle by pressing the designated stop button
;and waiting for the current cycle to complete. The menu design is 
;charted in MultTrk03.xls


;-------------- Constants definitions -----------------------
;Define inputs
iStart		iEQU		0
iTrigger2_2	iEQU		1	;Triggers oSquirt2 in prog 2
iTrigger3_1	iEQU		2	;Triggers oSquirt1 in prog 3
iTrigger3_2	iEQU		3	;Triggers oSquirt2 in prog 3

;Define buttons for manual operation. These re-use menu buttons.
;NB! can't use the escape button for this!
iMan0		iEQU		12
iMan1		iEQU		11
iMan2		iEQU		10


;Define menu button inputs
iUpKey		iEQU		12
iDnKey		iEQU		11
iYesKey		iEQU		10
iNoKey		iEQU		9
iXKey		iEQU		8	;escape


;Define outputs
oBeep		oEQU		8	;Beeper
oSquirt0	oEQU		0
oSquirt1	oEQU		1
oSquirt2	oEQU		2

;Define times. These are all in units of 10mS

;There are 3 programs and 3 outputs, each with 1 delay time and one ON time
;giving a total of 18 times.

;The values below are place holders only, suited best to simulation testing

;       __Pgm
;      |  __ Output
;      | |
tDelay_1_0:	EQU		1
tOn_1_0:	EQU		2

tDelay_1_1:	EQU		2
tOn_1_1:	EQU		2

tDelay_1_2:	EQU		3
tOn_1_2:	EQU		2

tDelay_2_0:	EQU		1
tOn_2_0:	EQU		2

tDelay_2_1:	EQU		1
tOn_2_1:	EQU		2

tDelay_2_2:	EQU		1
tOn_2_2:	EQU		2


tDelay_3_0:	EQU		1
tOn_3_0:	EQU		2

tDelay_3_1:	EQU		1
tOn_3_1:	EQU		2

tDelay_3_2:	EQU		1
tOn_3_2:	EQU		2


********  Start of executable code **************************************************

		Launchtask	MNU_Start
		LaunchTask	TT_0
		LaunchTask	TT_1
		LaunchTask	TT_2
		RunTasksForever

;============== Menu ===================================
;Short name: MNU_

;RAM:

MNU_Prog	defByte

;The menu gets control at first. Run mode is a sub task.
		
MNU_Start:
		GoIfMNZ		MNU_Prog,MNU_Set0	;Initialize: Check for valid program number
		SetMem		MNU_Prog,4		;Default manual
		ShadowWrite	MNU_Prog,1

MNU_Set0:
		OBLCD_Cls
				;0123456789012345
		OBLCD_Text	"Idle     Y=Start"
		OBLCD_SetCur	1,0
		Gosub		MNU_ShowProg
MNU_0:
		YieldTask
		GoIfInK		iUpKey,MNU_Set3
		GoIfInK		iDnKey,MNU_Set3
		GoIfInK		iYesKey,MNU_Set1
		GoTo		MNU_0

;This is the main run state
MNU_Set1:
		OBLCD_Cls
				;0123456789012345
		OBLCD_Text	"Running   X=Stop"
		OBLCD_SetCur	1,0
		Gosub		MNU_ShowProg
		LaunchTask	RUN
MNU_1:					
		WaitForSF	sRUN_Busy
		GoTo		MNU_Set0

MNU_Set3:
		OBLCD_Cls
				;0123456789012345
		OBLCD_Text	"Change program? "
		OBLCD_SetCur	1,0
		OBLCD_Text	"Current ="
		Gosub		MNU_ShowProgNum
MNU_3:
		YieldTask
		GoIfInK		iUpKey,MNU_Set0
		GoIfInK		iDnKey,MNU_Set0
		GoIfInK		iYesKey,MNU_Set4
		GoTo		MNU_3

MNU_Set4:
		OBLCD_Cls
				;0123456789012345
		Gosub		MNU_ShowProg
		OBLCD_SetCur	1,0
		OBLCD_Text	"Up/Down = change"
MNU_4:
		Yieldtask
		GoIfInK		iUpKey,MNU_IncProg
		GoIfInK		iDnKey,MNU_DecProg
		GoIfInK		iYesKey,MNU_SaveProg
		GoIfInK		iNoKey,MNU_UnchangeProg
		GoTo		MNU_4

MNU_IncProg:
		GoIfMGE		MNU_Prog,3,MNU_Wrap1	;test if at max allowed value
		IncM		MNU_Prog
		GoTo		MNU_Set4

MNU_DecProg:
		GoIfMLE		MNU_Prog,1,MNU_Wrap4
		DecM		MNU_Prog
		GoTo		MNU_Set4

MNU_Wrap1:	;Wrap from 3 over to 1
		SetMem		MNU_Prog,1
		GoTo		MNU_Set4

MNU_Wrap4:	;Wrap from 1 under to 4
		SetMem		MNU_Prog,4
		GoTo		MNU_Set4
		

MNU_UnchangeProg:	;Undo changes
		ShadowRead	MNU_Prog,1
		GoTo		MNU_Set3

MNU_SaveProg:		;Lock in changes
		ShadowWrite	MNU_Prog,1
		GoTo		MNU_Set3

;Subroutine to show the program with a label and number at the current cursor posn.
;This can distinguish manual and automatic modes.

MNU_ShowProg:
				;0123456789012345
		OBLCD_Text	"Program = "
MNU_ShowProgNum:
		GoIfMEQ		MNU_Prog,4,MNU_ShowMan
		OBLCD_UDecDispMVW MNU_Prog
		OBLCD_text	"     "		;Clear rest of line
		Return	

MNU_ShowMan:
		OBLCD_Text	"Manual"
		Return


;------ RUN subtask -------
;This is done as a transient task. It use a semaphore sRUN_Busy
;to signal back to MNU when run mode has ceased (due to user pressing X)	

sRUN_Busy:	defSEM

RUN:
RUN_Set0:
		SetS		sRUN_Busy
		InputK		iXKey			;Clear any old input K's
		InputK		iStart			;Clear any old input K's
RUN_0:
		YieldTask
		GoIfInK		iXKey,RUN_Set2		;*FIRST*!! Check for stop
		GoIfInK		iStart,RUN_Set1		;Check for start input
		GoIfMEQ		MNU_Prog,4,RUN_Set3
		GoTo		RUN_0


RUN_Set1:	;Automatic modes
		Recall		MNU_Prog
		GoSub		RUN_BroadcastCommand
RUN_1:
		YieldTask
		GoSub		RUN_Test_Busy		;See if the sequence is complete
		GoIfT		RUN_1			;Loop if anyone is still busy
		GoTo		RUN_0			;Go do  next cycle

RUN_Set3:	;Manual mode
		WaitOff		iYesKey			;Avoid hazard
		WaitOff		iNoKey			;Avoid hazard
		WaitOff		iUpKey			;Avoid hazard
		WaitOff		iDnKey			;Avoid hazard
		InputK		iXKey			;hazard
		Recall		MNU_Prog	
		GoSub		RUN_BroadcastCommand	;Start the timer tasks
Run_3:
		WaitOnK		iXKey			;Wait for manual stop
		ClrS		sRUN_Busy		;Signal all done
		ResetK					;Clear all input latches
		KillTask				;and quit

;STOP: Wait for all timer tasks to complete
RUN_Set2:
RUN_2:
		YieldTask
		GoSub		RUN_Test_Busy		;See if the sequence is complete
		GoIfT		RUN_2			;Loop if anyone is still busy
		ClrS		sRUN_Busy		;Signal all done
		KillTask				;and quit


RUN_BroadcastCommand:	;Send command in X to all timers
		Push
		GoSub		TT_0_SET_Command
		Push
		GoSub		TT_1_SET_Command
		Push
		GoSub		TT_2_SET_Command
		Return

RUN_Test_Busy:
		GoSub		TT_0_GET_Busy
		GoSub		TT_1_GET_Busy
		GoSub		TT_2_GET_Busy
		OR
		OR
		Return


;============== Individual timer tasks ==========================

;Short name: TT_x     	x = timer channel number  e.g. TT_1

;Each output has an associated timer task. This task is designed to be
;started by an external command from the main sequencer. That command
;contains the user program number. The task then decides what to do, either
;start timing right away or wait for its trigger input.

;A program number zero means the timer task is idle.
;Program number 1, 2 and 3 are the 3 automatic programs
;Program 4 is manual control.

;Each task can also be interrogated to see if it is busy or idle.
;This is needed to allow orderly stopping on completion of a cycle.

;Entry points:
;All 3 timer tasks have the same entry points. In the following X denotes task 0, 1 or 2

;	TT_X:			Launches the task
;	TT_X_SET_Command	Called with prgram number in X to trigger one cycle
;	TT_X_GET_Busy		Returns True in X while the task is busy

;Note: Manual mode (command 4) must be stopped by setting to Command 0

;----- Timer task 2 -----

;Ram:

TT_2_Command:	defByte

TT_2:		;LaunchTask entry point

;Back here at the end of a cycle:
;State 0 sits waiting for a non-zero command value
TT_2_Set0:
		SetMem		TT_2_Command,0
		Off		oSquirt1	;Defend against hazard when exiting manual mode
TT_2_0:
		YieldTask
		BranchM		TT_2_Command	; n-way branch depending on Command
		Target		TT_2_0		;Idle
		Target		TT_2_Prog1
		Target		TT_2_Prog2
		Target		TT_2_Prog3
		Target		TT_2_Prog4	;manual

;Prog 4 - Manual - Just copy input to output
TT_2_Prog4:
		Input		iMan2
		Output		oSquirt2
		GoTo		TT_2_0

;Prog 1 - Timed
TT_2_Prog1:
		Pause		tDelay_1_2
		ON		oSquirt2
		Pause		tOn_1_2
		Off		oSquirt2
		GoTo		TT_2_Set0

;Prog 2 - Triggered
TT_2_Prog2:
		InputK		iTrigger2_2	;Guard against an old latched input
		WaitOnK		iTrigger2_2
		Pause		tDelay_2_2
		ON		oSquirt2
		Pause		tOn_2_2
		Off		oSquirt2
		GoTo		TT_2_Set0

;Prog 3 - Triggered
TT_2_Prog3:
		InputK		iTrigger3_2	;Guard against an old latched input
		WaitOnK		iTrigger3_2
		Pause		tDelay_3_2
		ON		oSquirt2
		Pause		tOn_3_2
		Off		oSquirt2
		GoTo		TT_2_Set0

;SET and GET functions
TT_2_GET_Busy:	Recall		TT_2_Command
		Return

TT_2_SET_Command:
		Store		TT_2_Command
		Return

;----- Timer task 1 -----

;Ram:

TT_1_Command:	defByte

TT_1:		;LaunchTask entry point

;Back here at the end of a cycle:
;State 0 sits waiting for a non-zero command value
TT_1_Set0:
		SetMem		TT_1_Command,0
		Off		oSquirt1	;Defend against hazard when exiting manual mode
TT_1_0:
		YieldTask
		BranchM		TT_1_Command
		Target		TT_1_0		;Idle
		Target		TT_1_Prog1
		Target		TT_1_Prog2
		Target		TT_1_Prog3
		Target		TT_1_Prog4	;manual

;Prog 4 - Manual - Just copy input to output
TT_1_Prog4:
		Input		iMan1
		Output		oSquirt1
		GoTo		TT_1_0


;Prog 1 - Timed
TT_1_Prog1:
		Pause		tDelay_1_1
		ON		oSquirt1
		Pause		tOn_1_1
		Off		oSquirt1
		GoTo		TT_1_Set0

;Prog 2 - Timed
TT_1_Prog2:
		Pause		tDelay_2_1
		ON		oSquirt1
		Pause		tOn_2_1
		Off		oSquirt1
		GoTo		TT_1_Set0

;Prog 3 - Triggered
TT_1_Prog3:
		InputK		iTrigger3_1	;Guard against an old latched input
		WaitOnK		iTrigger3_1
		Pause		tDelay_3_1
		ON		oSquirt1
		Pause		tOn_3_1
		Off		oSquirt1
		GoTo		TT_1_Set0


;SET and GET functions
TT_1_GET_Busy:	Recall		TT_1_Command
		Return

TT_1_SET_Command:
		Store		TT_1_Command
		Return

;----- Timer task 0 -----

;Ram:

TT_0_Command:	defByte

TT_0:		;LaunchTask entry point

;Back here at the end of a cycle:
;State 0 sits waiting for a non-zero command value
TT_0_Set0:
		SetMem		TT_0_Command,0
		Off		oSquirt0	;Defend against hazard when exiting manual mode
TT_0_0:
		YieldTask
		BranchM		TT_0_Command
		Target		TT_0_0		;Idle
		Target		TT_0_Prog1
		Target		TT_0_Prog2
		Target		TT_0_Prog3
		Target		TT_0_Prog4

;Prog 4 - Manual - Just copy input to output
TT_0_Prog4:
		Input		iMan0
		Output		oSquirt0
		GoTo		TT_0_0


;Prog 1 - Timed
TT_0_Prog1:
		Pause		tDelay_1_0
		ON		oSquirt0
		Pause		tOn_1_0
		Off		oSquirt0
		GoTo		TT_0_Set0

;Prog 2 - Timed
TT_0_Prog2:
		Pause		tDelay_2_0
		ON		oSquirt0
		Pause		tOn_2_0
		Off		oSquirt0
		GoTo		TT_0_Set0

;Prog 3 - Timed
TT_0_Prog3:
		Pause		tDelay_3_0
		ON		oSquirt0
		Pause		tOn_3_0
		Off		oSquirt0
		GoTo		TT_0_Set0


;SET and GET functions
TT_0_GET_Busy:	Recall		TT_0_Command
		Return

TT_0_SET_Command:
		Store		TT_0_Command
		Return

