Program code

The program code is reproduced below. You should master the Multitasking tutorial before attempting to understand this code!

;mdrv2.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