;mdrv1.spt   Example motor drive program using SPice10213
;This program is written for an MMi99/200 with LCD, but can easily
;be ported to an MS12

;The program uses front panel push button inputs as follows:

;    Input  Function
;	12	+ speed
;	11	- speed
;	10	Stop/Start
;	 9	Forward/Reverse
;	 8	Not used

;The program consists of 3 tasks:

;SPD	(Speed)	Simply monitors the + and - buttons and generates a target speed value
;DRV	(Drive) Ramps the motor speed (PWM) towards the target speed
;MSQ	(Motor sequencer) Master sequencer for stop/start, forward/reverse. Uses DRV
;			for actual PWM generation.

;The 3 tasks are written as independent tasks witin a multitasking structure.
;You need to understand the material in the multitasking tutorial to make sense of this

;=============== Constants ====================
SPD_Increment	EQU		10	;Inc/Dec speed by this amount for each button push
StopTime	EQU		10	; x 10mS. Time allowed for motor to stop with dynamic braking

;============== I/O allocations ==============
;Alter for MS12
;----------- Inputs ----------------
iSPeedUp	iEQU		12
iSpeedDn	iEQU		11
iStopStart	iEQU		10
iFwdRev		iEQU		9

;----------- Outputs ---------------
oDirection	oEQU		16

;========= RAM ==============
;-------- Semaphores (1-bit variables) in 1st 8 bytes ---------------
Direction	sEQU		0
DRV_Accelerate	sEQU		1
Tick10		sEQU		2
MSQ_DirFWD	sEQU		3
MSQ_RevFlag	sEQU		4	

;-------- Other RAM ------------
MSQ_Addr	mEQU		20	;2 bytes. Suspend address for MSQ
SPD_Target	mEQU		22	;Target PWM
DRV_Addr	mEQU		23	;2 bytes. Suspend address for DRV
DRV_PWM		mEQU		25	;Current PWM value 
SPD_Count	mEQU		26
MSQ_Count	mEQU		27
MSQ_State	mEQU		28

;-------------------------------------------------------------

Start:
		setu		0,1		;set spice connector pin 4 as DO
		setu		3,1		;set spice connector pin 7 as DO	
		setu		4,4		;set spice connector pin 8 as PWM output
 		setu		5,4		;set spice connector pin 9 as PWM output
		SpiceConfigU

		GoSub		SPD_Init	;Initialize tasks
		GoSub		DRV_Init
		GoSub		MSQ_Init

MainLoop:
		GetTick10			;Generate 10mS heartbeat
		StoreS		Tick10
		GoSub		SPD_UpDate	;Run task
		GoSub		DRV_UpDate	;Run task
		GoSub		MSQ_UpDate	;Run task

;Debug: Display the MSQ state number
		OBLCD_SetCur	1,13
		OBLCD_UDecDispMFW	MSQ_State

		GoTo		MainLoop

;====================================================================
;----------- Motor sequencer ---------------------

;Short name	MSQ

;Entry points
;		MSQ_Init	Initialize
;		MSQ_Update	Update


MSQ_UpDate:
		Resume		MSQ_Addr

MSQ_Init:

;State 0 is the idle state.
MSQ_Set0:
		SetMem		MSQ_State,0		;For debugging monitor
MSQ_0:
		Suspend		MSQ_Addr
		GoIfInK		iStopStart,MSQ_Set1	;Test stop/start button
		GoSub		MSQ_ReadFR		;Test and process F/R
		GoTo		MSQ_0

;In state 1 we set the relay for the correct direction, then wait for it to settle
;prior to starting up the PWM drive
MSQ_Set1:
		SetMem		MSQ_State,1		;For debugging monitor
		RecallS		MSQ_RevFlag		;Set direction relay ...
		Output		oDirection		;... according to direction flag

;Here we mark off two timer ticks to ensure at least a 10mS pause while the relay settles.
MSQ_1:
		Suspend		MSQ_Addr
		GoIfSF		Tick10,GenRet		;Sync to timer tick
		Suspend		MSQ_Addr
		GoIfInK		iStopStart,MSQ_Set0	;Test stop/start button
		GoIfSF		Tick10,GenRet		;Wait for next tick

;State 2 is the run state. the DRV task may well be accelerating, but that does
;not concern us one way or the other here.
MSQ_Set2:
		SetMem		MSQ_State,2		;For debugging monitor
		GoSub		DRV_Run			;Start ramping up speed
MSQ_2:
		Suspend		MSQ_Addr
		GoIfInK		iStopStart,MSQ_Set3	;Test stop/start button
		GoSub		MSQ_ReadFR		;Test and process F/R
		GoIfF		MSQ_2			;G/ not pressed

;State 6 is decellerating prior to reversing direction.
MSQ_Set6:
		SetMem		MSQ_State,6		;For debugging monitor
		GoSub		DRV_Stop		;Commence stop process
MSQ_6:
		Suspend		MSQ_Addr
		GoIfInK		iStopStart,MSQ_Set8	;Test stop/start button
		GoSub		DRV_Get_ZPWM		;Test if PWM is zero
		GoIfT		MSQ_Set7
		GoSub		MSQ_ReadFR		;Test and process F/R
		GoIfF		MSQ_6			;G/ not pressed
		GoTo		MSQ_Set2
	
;State 3 is decellerating prior to stopping	
MSQ_Set3:
		SetMem		MSQ_State,3		;For debugging monitor
		GoSub		DRV_Stop
MSQ_3:
		Suspend		MSQ_Addr
		GoIfInK		iStopStart,MSQ_Set2	;Test stop/start button
		GoSub		DRV_Get_ZPWM
		GoIfT		MSQ_Set4
		GoSub		MSQ_ReadFR		;Test and process F/R
		GoIfF		MSQ_3			;G/ not pressed

;State 5 is if F/R is pushed while decellerating for a stop
MSQ_Set5:
		SetMem		MSQ_State,5		;For debugging monitor
MSQ_5:
		Suspend		MSQ_Addr
		GoIfInK		iStopStart,MSQ_6	;Test stop/start button
		GoSub		DRV_Get_ZPWM
		GoIfT		MSQ_Set4
		GoSub		MSQ_ReadFR		;Test and process F/R
		GoIfF		MSQ_5			;G/ not pressed
		GoTo		MSQ_3

;State 8 is if Stop/start is hit while decelleration prior to reversing
MSQ_Set8:
		SetMem		MSQ_State,8		;For debugging monitor
MSQ_8:
		Suspend		MSQ_Addr
		GoIfInK		iStopStart,MSQ_6	;Test stop/start button
		GoSub		DRV_Get_ZPWM
		GoIfT		MSQ_Set4
		GoTo		MSQ_8		

;State 4 is timing the dynamic braking interval after a stop.
MSQ_Set4:
		SetMem		MSQ_State,4		;For debugging monitor
		InputO		oDirection		;Read back direction relay
		NOT
		Output		oDirection
		SetMem		MSQ_Count,StopTime
MSQ_4:
		Suspend		MSQ_Addr
		GoSub		MSQ_ReadFR		
		GoIfSF		Tick10,MSQ_4
		DMGNZ		MSQ_Count,MSQ_4
		GoTo		MSQ_Set0

;State 7 is timing the dynamic braking interval foir stopping during reversal
MSQ_Set7:
		SetMem		MSQ_State,7		;For debugging monitor
		RecallS		MSQ_RevFlag
		Output		oDirection
		SetMem		MSQ_Count,StopTime
MSQ_7:
		Suspend		MSQ_Addr
		GoSub		MSQ_ReadFR		
		GoIfSF		Tick10,MSQ_7
		DMGNZ		MSQ_Count,MSQ_7
		GoTo		MSQ_Set2

;----- Read and process F/R button. Return True if pressed	
MSQ_ReadFR:
		InputK		iFwdRev
		Push
		RetIfF
		RecallS		MSQ_RevFlag
		NOT
		StoreS		MSQ_RevFlag
		Return

;----------- Drive Task --------------------------
;This task is responsible for ramping the PWM up and down to the target speed
;(when enabled). In essence it simply makes the actual PWM track the target PWM,
;with ramping. There is a coding option for sudden stop or smooth stop.

;Short name	DRV

;Uses lcd 1,0 through 1,3

;Entry points

;		DRV_Init	Initialize
;		DRV_Update	Update. 
;		DRV_Run		Start ramping up. Simply updates DRV_Accelerate semaphore
;		DRV_Stop	Start ramping down. Simply updates DRV_Accelerate semaphore
;		DRV_Get_ZPWM	Return true if PWM=0

DRV_Get_ZPWM:
		Recall		DRV_PWM
		Push
		OR
		NOT
		Return

DRV_Update:
		Resume		DRV_Addr

DRV_Init:
DRV_Set0:
		SetMem		DRV_PWM,0
		GoSub		DRV_PWMOut
DRV_0:
		Suspend		DRV_Addr
		GoIfST		DRV_Accelerate,DRV_Set1	;Test for RUN command
		GoTo		DRV_0

DRV_Set1:		;Here on RUN command
		Suspend		DRV_Addr

;Which of the following 2 code lines determines whether the controller
;executes sudden stops, with dynamic braking, or smooth stop (ramping down the speed)
;Comment out the line you don't want.

;This line for sudden stop:
		GoIfSF		DRV_Accelerate,DRV_Set0	;Test for STOP command
;This line for smooth stop:
		GoIfSF		DRV_Accelerate,DRV_Set2	;Test for STOP command


		GoIfSF		Tick10,DRV_Set1		;wait for time tick
		GoSub		SPD_GetSpeed		;Target PWM
		Recall		DRV_PWM			;Actual PWM
		CompareR				;Compare Target to actual
		BranchR					;Test result
		Target		DRV_Set1		;On target
		Target		DRV_1_Dec		;PWM too high
		Target		DRV_1_Inc		;PWM too low

DRV_1_Dec:		;Here to decrement PWM
		DecM		DRV_PWM
		GoSub		DRV_PWMOut
		GoTo		DRV_Set1

DRV_1_Inc:		;Here to increment PWM
		IncM		DRV_PWM			;
		GoSub		DRV_PWMOut
		GoTo		DRV_Set1

DRV_Set2:		;Here on stop command
		Suspend		DRV_Addr
		GoIfST		DRV_Accelerate,DRV_Set1	;Test for RUN command
		GoIfSF		Tick10,DRV_Set2		;wait for time tick
		GoIfMZ		DRV_PWM,DRV_Set0
		DecM		DRV_PWM
		GoSub		DRV_PWMOut
		GoTo		DRV_Set2


DRV_Run:
		SetS		DRV_Accelerate
		Return

DRV_Stop:
		ClrS		DRV_Accelerate
		Return

DRV_PWMOut:		;Output the PWM
		Recall		DRV_PWM
		AnOutJ				;Alter for MS12
		OBLCD_SetCur	1,0
		OBLCD_UDecDispMFW	DRV_PWM
		Return
		

;----------- Speed task ---------------------------
;This task maintains the target speed.
;Short name	SPD

;Uses lcd 0,0 through 0,3

;Entry points

;		SPD_Init	Initialize
;		SPD_UpDate	Service push buttons, update target accordingly
;		SPD_Get_Speed	return current target speed in X

SPD_Init:
		Return

SPD_GetSpeed:
		Recall		SPD_Target
		Return

SPD_UpDate:
		GoIfInK		iSpeedUp,SPD_Up
		GoIfInK		iSpeedDn,SPD_Dn
GenRet:	Return

SPD_Dn:
		SetMem		SPD_Count,SPD_Increment
SPD_DnA:
		GoIfMZ		SPD_Target,SPD_Show
		DecM		SPD_Target
		DMGNZ		SPD_Count,SPD_DnA
		

SPD_Show:
		OBLCD_SetCur	0,0
		OBLCD_UDecDispMFW	SPD_Target
		Return

SPD_Up:
		SetMem		SPD_Count,SPD_Increment
SPD_UpA:
		GoIfMEQ		SPD_Target,255,SPD_Show
		IncM		SPD_Target
		DMGNZ		SPD_Count,SPD_UpA
		GoTo		SPD_Show

