LED Animation Design
Dispatching
For the Crossbar project, I had these design goals:
- To have a main routine which would load frame data and update the display with this data until it was time to load the next frame
- To be able to think of an animation as a single entity, like a movie clip. This meant that I had to have a single routine to access all the data for one animation. This would allow me to easily rearrange the animations.
- To be able to specify different speeds, repeat counts, and coding styles for the different animations.
- To be able to access all the animation data using a single call and some variables (animation index, frame number, etc)
- To not have to specify sizes or counts for any list
Overall I wanted the flexibility to modify, add, or resequence animations without having to touch the main loop, and I wanted all of the control and logic to be centrally located.
These goals resulted in a routine called AnimationDispatch. Here is the routine from Crossbar (with most of the animations removed for brevity).
;---------------------------------------------
; Animation Dispatch
;
;---------------------------------------------
AnimationDispatch
IdxJump AnimationDispatch, CURSEQ
goto SnakeFillLong
goto Swing
goto CrissCrossOne
goto BigSquareWalk
goto DiagListFillOne
; many deleted....
goto CheckerFillShort
bsf SEQFLAGS,FLAG_LIST_DONE
return
The PIC processor allows the application to add a value to the program counter in order to do a computed jump. This is used in the AnimationDispatch routine to jump to an animation routine depending on the value in CURSEQ. (CURSEQ, "current sequence", is a memory location whose content is incremented in the main loop.) Because I use this type of jump frequently, I created a macro, called IdxJump. One of the requirements for this type of jump is that the PCLATH be preloaded with the high byte of the address. The macro takes care of this.
; Macro: IdxJump
; Description: This macro puts the high byte of the label into PCLATH
; and uses the file register as a value for a computed jump
; Params: CallLabel - The label of the routine
; IndexReg - The file register which stores the offset value
;
IdxJump macro CallLabel, IndexReg
movlw HIGH CallLabel
movwf PCLATH
movf IndexReg,W
addwf PCL,F
endm
What does an individual animation routine look like? This example simply flashes all of the LEDs on and off. There are two parts to this data: the animation settings ("FrameByFrameAnim...") and the animation data ("FlashAllData").
;========== FlashAll
FrameByFrameAnim FlashAll, 0x10, 0x30, FlashAllData
FlashAllData
IdxJump FlashAllData, DATAINDEX
retlw 0xff ; 0
retlw 0xff
retlw 0x00 ; 1
retlw 0x00
bsf SEQFLAGS, FLAG_DATA_DONE
return
The animation settings use another macro, FrameByFrameAnim. This macro provides a convenient and consistent way to specify the animation routine name, the number of times to repeat the animation, the speed of the animation, and the name of the routine that has the animation data.
; Macro: FrameByFrameAnim
; Description: This macro creates a frame-by-frame animation routine.
; No additional statements are required or allowed.
; Params: RoutineName - The label of the animation routine
; RepeatCnt - The number of times to repeat the animation
; SpeedValue - The speed value (smaller is faster)
; DataName - The name of the routine that provides the pixel list data
;
FrameByFrameAnim macro RoutineName, RepeatCnt, SpeedValue, DataName
RoutineName IdxJump RoutineName, CURCMD
retlw RepeatCnt ; Repeat
retlw SpeedValue ; Duration (speed)
btfss SEQFLAGS, FLAG_GET_DATA
goto LoadFrameData
goto DataName
endm
This macro also manages the conversion from frame number to index. This is necessary since the main loop advances from frame to frame by index and does not know how the data is generated. In this macro LoadFrameData will actually call back into the animation routine twice, once for each byte in the frame data. This is what the "btfss..." line handles. The first time the animation routine is called to fetch frame data (by the main loop, via AnimationDispatch), the FLAG_GET_DATA bit is off and the btfss line falls through and LoadFrameData is called. LoadFrameData computes the offset needed to get the frame data (2xframe_number and 2xframe_number+1), sets the FLAG_GET_DATA bit in SEQFLAGS, and calls AnimationDispatch again, twice. taking the returned data and populating the frame information.
For frame-by-frame animations, the data is simply a series of byte pairs. Each two bytes describes the on/off value of each LED in the 4x4 matrix.