LED Animation Design

Dispatching

For the Crossbar project, I had these design goals:

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.

Blog posts

LED Animation Design: Dispatching