Encoder
Afterallthistheory,let’slook
atsomecode.Program6-3
willreada16position,two-bitrelativeshaftencoder(CTS
model288T232R161A2)and
illuminateanLEDindicating
whethertheshaftisrotated
clockwiseorcounter-clock-wise.We’llalsoincrementa
counterforclockwiserotation
anddecrementitforcounter-clockwiserotationandoutput
thecountervalueoverthe
RS-232port.Thecircuitas-sociatedwithProgram6-3is
showninFigure6-10.
Program6-3
;Variables
;---CurVal Var Byte ;currentencoder OldVal Var Byte ;oldencoder
Counter Var Sbyte ;countsstatechanges
;Initialize
;---Clear
OutputA0 ;clockwiseLED
OutputA1 ;counter-clockwiseLED LowA0 ;initializebothLEDsoff LowA1
EnableHSerial
SetHSerialH115200 ;changeiflowerspeedneeded
CurVal=(PortB&%00110000)>>4 ;getcurrentreading
OldVal=CurVal ;initializeforfuturechange HSerOut[“Ctr“,SDECCounter,13] ;outputthecounter
Main
;---CurVal=(PortB&%00110000)>>4 ;returns0…3
IfCurVal<>OldValThen
BranchCurVal,[S0,S1,S2,S3] ;ifchanged,lookatold
EndIf
AfterBranch GoToMain
S0 ;readingis0,checkifcamefrom2or1
;--- IfOldVal=2Then
GoSubClockWise
ELSE
GoSubCounterClockWise
EndIf
Figure6-10:Schematicforincrementalencoderreadprogram.
GoToAfterBranch
S1 ;readingis1,checkifcamefrom0or3
;--- IfOldVal=0Then
GoSubClockWise
ELSE
GoSubCounterClockWise
EndIf
GoToAfterBranch
S2 ;readingis2,checkifcamefrom3or0
;--- IfOldVal=3Then
GoSubClockWise
ELSE
GoSubCounterClockWise
EndIf
GoToAfterBranch
S3 ;readingis3,checkifcamefrom1or2
;--- IfOldVal=1Then
GoSubClockWise
ELSE
GoSubCounterClockWise
EndIf
GoToAfterBranch
ClockWise
;--- HighA0 ;turnCWLEDon
LowA1 ;turnCCWLEDoff
OldVal=CurVal ;readytocheckfornextinput
Counter=Counter+1 ;bumpupcounter
HSerOut[“Ctr“,SDECCounter,13] ;outputthecounter Return
CounterClockWise
;--- LowA0 ;turnCWLEDoff
HighA1 ;turnCCWLEDon
OldVal=CurVal ;readyfornextinput
Counter=Counter–1 ;decrementcounter
HSerOut[“Ctr“,SDECCounter,13] ;outputthecounter Return
End
Let’sgothroughtheinterestingportionsofthecode.
;Variables
;---CurVal Var Byte
OldVal Var Byte
Counter Var SByte
We’llreadandholdtheencodervalueinCurVal.OldValholdsthepreviousencodervalue,andwewill
incrementordecrementCounterdependingonthedirectionofshaftrotation.Counterisasignedbyte,
withpermittedvaluesfrom–128to+127,thusallowingustoincrementordecrementCounterfromits
initializedvalueof0.
CurVal=(PortB&%00110000)>>4 OldVal=CurVal
Weinitializebyreadingthecurrentencodervalueandsettheoldvalueequaltothecurrentvalue.This
permitsustodetectthefirstrotationbycheckingforCurVal<>OldVal.First,weperformabitwise
AND(“&”)ofthereadvaluewith%00110000.TheANDfunctionmasksoffallbitsreadonPortBexcept
forthosecorrespondingto1’sinthemaskoperator%00110000,B5andB4.MaskingallexceptB5andB4
permitsustoignorethevaluesreturnedbytheremainderofPortB’spins.Sincetheencoderisconnectedto
B4andB5,thereadvalueisshiftedtotheleftbyfourbits,whichweremovebyshiftingthereturnedvalue
fourbitstotheright.(ThereasonweconnectedtheencodertoB4andB5insteadofB0andB1willbeseen
whenwelookattheinterruptversionofProgram6-3.)Toforcethecorrectorderofoperation,weenclose
thelogicalANDoperationwithparentheses.Intheabsenceoftheparentheses,MBasic5.3.0.0wouldfirst
divide%00110000by16andthenlogicallyANDtheresultwiththevaluereadonPortB.Thisrepresentsa
changefromearlierversionsofMBasic,wheretheorderofprecedencewasdifferent.
CTS’sdatasheetshowstheoutputsequence(assumingclockwiserotation)forthefirsteightpositionsas:
ContactClosure ToCommon
B 0 0 1 1 0 0 1 1
A 0 1 1 0 0 1 1 0
PositionNo. 0 1 2 3 4 5 6 7
Inourcircuitacontactclosure(datasheet1)resultsinalogicallowatthePIC’sinputpin,andanopen(data
sheet0)isalogicalhigh.Hence,ourcircuitinvertsthe0’sand1’sfromtheCTS’sdatasheet’sprospective.
AsshowninFigure6-10,pinAisconnectedtoB4,whilepinBisconnectedtoB5.Consequently,ourPIC
inputpinsequencelookslike:
PICPin B5 1 1 0 0 1 1 0 0
B4 1 0 0 1 1 0 0 1
PositionNo. 0 1 2 3 4 5 6 7
PortB&b00110000 48 32 0 16 48 32 0 16
PortB&b00110000/16 3 2 0 1 3 2 0 1
Wenowrewrite(PortB&b00110000)>>4intworows,representingclockwiseandcounter-clockwise
rotation,startingwith0forconvenience:
Clockwise 0 1 3 2 0 1 3 2
Counter-Clockwise 0 2 3 1 0 2 3 1
Tounderstandhowwedifferentiateclockwiseandcounter-clockwiserotation,supposetheencoder’scurrent
valueisreadas0.Ifthepreviousvaluewas0,theencodershafthasnotmovedsinceitslastread.Ifthepre-viousvaluewas2,therotationisclockwise;ifthepreviousvaluewas1,therotationiscounter-clockwise.
Main
;---CurVal=(PortB&%00110000)>>4
IfCurVal<>OldValThen
BranchCurVal,[S0,S1,S2,S3]
EndIf
AfterBranch GoToMain
Withthisunderstanding,themainprogramshouldbeclearer.Wehavetwosequentialcomparisonstomake.
First,hastheencodermoved,whichwetestforwiththeIfCurVal<>OldValThen… test.Iftheen-codershafthasrotatedsinceitslastread,theconditionalistrueandwebranchtooneoffourprogramlabels
basedonthecurrentencodervalue,S0ifthevalueis0,andsoon.
S0 ;readingis0,checkifcamefrom2or1
;--- IfOldVal=2Then
GoSubClockWise
ELSE
GoSubCounterClockWise
EndIf
GoToAfterBranch
ProgramexecutionreachesS0onlyifCurVal=0.Hence,todeterminetherotationdirection,weonly
needtestthepriorvalue,whichwedowiththeIfOldVal=2test.Iftrue,thedirectionisclockwiseand
weexecutetheClockWisesubroutine;iffalse,thedirectionmustbecounter-clockwise,soweexecutethe
CounterClockWisesubroutine.
ClockWise
;--- HighA0 ;turnCWLEDon
LowA1 ;turnCCWLEDoff
OldVal=CurVal ;readytocheckfornextinput
Counter=Counter+1 ;bumpupcounter
HSerOut[“Ctr“,SDECCounter,13] ;outputthecounter Return
CounterClockWise
;--- LowA0 ;turnCWLEDoff
HighA1 ;turnCCWLEDon
OldVal=CurVal ;readyfornextinput
Counter=Counter–1 ;decrementcounter
HSerOut[“Ctr“,SDECCounter,13] ;outputthecounter Return
TheClockWiseandCounterClockWisesubroutinesaremirrorimagesofeachother;theysetandclear
thedirectionLEDsandbumpupordecrementthecounterandwritetheupdatedvalueofthepositioncoun-tertothehardwareserialport.
AfterreadingChapter4,youmaywonderwherethedebounceroutineis.Afterall,wearesensingamechan-icalswitchwhosedatasheetquotesa5msmaximumbounceperiod.Theansweristhattheexecutiontime
oftheprogram,includingtheserialoutputstatement,providessufficientdelaytoprovidetroublefreeread-ing.Inmostcases,arealprogramwouldbranchintoenoughcodethatconsumedenoughtimetoprovide
debounceasanincidentalbenefit.Ifnot,wecouldaddaPause5statementaftereachswitchread.Or,we
mightadddebouncecircuitrytoourdesign.
Asitstandsnow,witha20MHzclock,Program6-3’smainprogramloopexecutesin400µswherethe
encoderhasnotchangedvalue,andin750µswhereithaschangedvalue.Thus,itaccuratelyreadswellover
1,000steps/second,or3700RPM,farfasterthansomeonetwistingaknobattachedtotheshaftwilloperate
theencoder.
Figures6-11and6-12showtheinputwaveformsasIturnedtheshaftquicklybyhand.Incidentally,you
mayreadreferencestoquadratureencoderoutputsbeingreadbycomparingthephaseofthetwooutputsfor
clockwiseandcounter-clockwiserotation.Figure6-11showsChannel2(B5)leadingChannel1(B4)during
clockwiserotation,whileFigure6-12showsChannel1(B4)leadsChannel2(B5)duringcounter-clockwise
rotation.And,infact,thewaveformsatB4andB5are90°outofphase.Iviewthisphasecomparisonanaly-sisfarmoreconfusingthanbeneficial,particularlyinabeginningleveldiscussion.Hencewehaveinstead
focusedonreadingtheencodervalueandcomparingitwiththepriorvalue.
Program6-3spendsagreatdealoftimepotentiallydoingnothing;inatypicalprogramthatusesarotary
encodertoselectmenuitems,operatorinteractionisinfrequent.Howmanytimesdoyouchangethether-mostatsettinginyourhouse?Certainlynothundredsoftimesasecond.Wouldn’titbeniceifweourmain
programcouldspenditstimemakingimportantcomputationsandbranchtodealwithachangeduserinput
onlywhentheencoderischanged,withoutcheckingtheencoder’svalueeverytimewegothroughourmain
loop?Theanswer,ofcourse,isthatnotonlywouldthisbenice,thefolksatMicrochipandBasicMicrohave
comeupwithawaytodoexactlythatthroughthemechanismofinterrupts.We’regoingtosaveinterrupts
untilChapter10,buthere’sasneakpreview.
WheneverthestateofinputpinsRB4…RB7change,wecancausethePICtostopwhatevercodeitisrun-ningandinsteadrunnewcode.Thisprocessiscalledan“interrupt”incomputer-speakanditdoesexactly
that;itinterruptsthecurrentcodeandswitchesexecutiontoan“interrupthandler.”
DeletetheCodeBelowfromProgram6-3 ReplaceitwiththeFollowingInterruptVersion CurVal=(PortB&%00110000)>>4
OldVal=CurVal
Main
;---CurVal=(PortB&%00110000)>>4
;returns0…3
IfCurVal<>OldValThen
BranchCurVal,[S0,S1,S2,S3]
;ifchanged,lookatold
EndIf
AfterBranch GoToMain
CurVal=(PortB&%00110000)>>4 OldVal=CurVal
OnInterruptRBINT,ReadEncoder EnableRBINT
Main
;---GoToMain
Disable ReadEncoder
CurVal=(PortB&%00110000)>>4
IfCurVal<>OldValThen
BranchCurVal,[S0,S1,S2,S3]
EndIf
AfterBranch Resume
Figure6-11:ClockwisemanualrotationCh1:
B4Ch2:B5.
Figure6-12:Counter-clockwisemanualrotation
Ch1:B4Ch2:B5.
WheneveranypinofRB4…RB7changesstate(from0to1orviceversa),ifenabled,anRBINT typeinter-ruptisissued.TouseaninterruptinMBasic,wefirstassociatetheinterrupttypewiththeprogramlabel
oftheinterrupthandlerwiththeOnInterruptRBINT,ReadEncoderprocedure.Thissimplysaysthat
wheneveranRBINTistriggered,weexecutecodefollowingthelabelReadEncoder.Next,weturnonor
“enable”theRBINTinterruptwiththeprocedureEnableRBINT.Now,wheneverweturntheencoder
shaft,eitherB4orB5willchangevalueandprogramexecutionwilljumptoReadEncoder.
Disable ReadEncoder
CurVal=(PortB&%00110000)>>4
IfCurVal<>OldValThen
BranchCurVal,[S0,S1,S2,S3]
EndIf
AfterBranch Resume
Inordertoavoidinterruptsofinterrupts,wemustturnoffadditionalinterruptsbeforetheinterrupthandler
viatheDisablefunction.TheinterruptprocedureitselfduplicatesthecodewedevelopedinProgram6-3.
Attheendoftheinterruptprocedure,wereturntonormalprogramflowthroughaResumestatement.
ThecodeexecutedintheMain…GoToMainloopinourinterruptprogramissimplyaserialoutput.But,
itcouldhavebeenanything,suchaslengthymathematicalcalculations,LCDwrites,oranythingelseyou
candoinMBasic.Whentheinterruptoccurs,MBasicwillseamlesslyexecutetheinterrupthandlerand
returnflowtoyourmainprogram.Hence,itisuptoyourcodetorecognizeandaccommodatechangesto
Counter—asaresultofuserinputcommands—betweenprogramstatements.
There’sanothersubtletyhereaswell.MBasicwillnotbreakexecutionofastatementtoserviceaninterrupt.
Suppose,forexample,yourmaincodehasaoneseconddelayinitthroughaPause1000procedure.If
youtwisttheshaftontheencoderjustasthePause1000procedurestartsexecution,theinterrupthandler
willnotbeinvokeduntilafterthe
Pause1000completes.So,ifthis
happensyoumaytwisttheshaft
encoderknobthroughadozenposi-tions,buttheinterruptprocedure
won’trecognizethem.(Inalater
chapterwe’lllaterseethatanas-semblerlanguageinterruptdoesn’t
sharethisproblem.)