' ========================================================================= ' ' File...... Animatronic_Template.SXB ' Purpose... ' Author.... Jon Williams, EFX-TEK ' E-mail.... jwilliams@efx-tek.com ' Started... ' Updated... ' ' ========================================================================= ' ------------------------------------------------------------------------- ' Program Description ' ------------------------------------------------------------------------- ' ' -- 4 trigger inputs ' -- 4 background servo ports ' -- 2 background TX ports ' -- 1 background RX port ' -- 1 Ping))) sensor port ' -- 6 digital outputs ' Triggers may be active-high or active-low (use SETUP jumpers) ' Servos ' -- nomimal servo range is 102 (1 ms) to 205 (2 ms) units ' -- center is 154 units ' Parallax Serial LCD ' -- runs at 19.2K baud ' -- replace ULN2803 on P0..P7 with ULN2003 to free P7 for serial comms ' ------------------------------------------------------------------------- ' Conditional Compilation Symbols ' ------------------------------------------------------------------------- ' ------------------------------------------------------------------------- ' Device Settings ' ------------------------------------------------------------------------- DEVICE SX28, OSCHS3, TURBO, STACKX, OPTIONX, BOR42 FREQ 50_000_000 ID "AniCtrl" ' ------------------------------------------------------------------------- ' I/O Pins ' ------------------------------------------------------------------------- Triggers PIN RC Trigger4 PIN RC.7 INPUT ' P15 Trigger3 PIN RC.6 INPUT ' P14 Trigger2 PIN RC.5 INPUT ' P13 Trigger1 PIN RC.4 INPUT ' P12 ServoCtrl PIN RC ' servo control port Servo4 PIN RC.3 OUTPUT ' P11 Servo3 PIN RC.2 OUTPUT ' P10 Servo2 PIN RC.1 OUTPUT ' P9 Servo1 PIN RC.0 OUTPUT ' P8 TX2 PIN RB.7 OUTPUT ' P7 --> Serial LCD Sonar PIN RB.6 INPUT ' P6 --> Ping))) DigOuts PIN RB ' P0..P5 or OUT0..OUT5 DigOut6 PIN RB.5 OUTPUT DigOut5 PIN RB.4 OUTPUT DigOut4 PIN RB.3 OUTPUT DigOut3 PIN RB.2 OUTPUT DigOut2 PIN RB.1 OUTPUT DigOut1 PIN RB.0 OUTPUT TX1 PIN RA.3 OUTPUT ' to SD-50 RX1 PIN RA.2 INPUT ' from SD-50 SCL PIN RA.1 OUTPUT ' EE clock line (I2C) SDA PIN RA.0 INPUT ' EE data line (I2C) ' ------------------------------------------------------------------------- ' Constants ' ------------------------------------------------------------------------- ' Bit dividers for 3.255 uS interrupt Baud2400 CON 7 ' for ISR bit divisor Baud4800 CON 6 Baud9600 CON 5 Baud19K2 CON 4 Baud38K4 CON 3 Tx1BaudBit CON Baud9600 ' set baud rate Rx1Baud1x0 CON 1 << Tx1BaudBit ' calculate # ISR cycles Rx1Baud1x5 CON Rx1Baud1x0 * 3 / 2 ' start bit cycles LF CON 10 FF CON 12 ' form feed -- for terminal CR CON 13 Tx2BaudBit CON Baud19K2 ' set baud rate LcdBkSpc CON $08 ' move cursor left LcdRt CON $09 ' move cursor right LcdLF CON $0A ' move cursor down 1 line LcdCls CON $0C ' clear LCD (need 5 ms delay) LcdCR CON $0D ' move pos 0 of next line LcdBLon CON $11 ' backlight on LcdBLoff CON $12 ' backlight off LcdOff CON $15 ' LCD off LcdOn1 CON $16 ' LCD on; no crsr, no blink LcdOn2 CON $17 ' LCD on; no crsr, blink on LcdOn3 CON $18 ' LCD on; crsr on, no blink LcdOn4 CON $19 ' LCD on; crsr on, blink on LcdLine1 CON $80 ' move to line 1, column 0 LcdLine2 CON $94 ' move to line 2, column 0 LcdLine3 CON $A8 ' move to line 3, column 0 LcdLine4 CON $BC ' move to line 4, column 0 LcdCC0 CON $F8 ' define custom char 0 LcdCC1 CON $F9 ' define custom char 1 LcdCC2 CON $FA ' define custom char 2 LcdCC3 CON $FB ' define custom char 3 LcdCC4 CON $FC ' define custom char 4 LcdCC5 CON $FD ' define custom char 5 LcdCC6 CON $FE ' define custom char 6 LcdCC7 CON $FF ' define custom char 7 IsOn CON 1 IsOff CON 0 Yes CON 1 No CON 0 ' ------------------------------------------------------------------------- ' Variables ' ------------------------------------------------------------------------- flags VAR Byte isrFlag VAR flags.0 char VAR Byte idx VAR Byte distance VAR Word tmpB1 VAR Byte ' for subs/funcs tmpB2 VAR Byte tmpW1 VAR Word tmpW2 VAR Word tmpW3 VAR Word tmpW4 VAR Word tx1Serial VAR Byte (16) ' tx serial data tx1Buf VAR tx1Serial(0) ' eight-byte buffer tx1Count VAR tx1Serial(8) ' tx bit count tx1Divide VAR tx1Serial(9) ' bit divisor timer tx1Lo VAR tx1Serial(10) ' holds start bit tx1Hi VAR tx1Serial(11) ' tx output reg tx1Head VAR tx1Serial(12) ' buffer head (write to) tx1Tail VAR tx1Serial(13) ' buffer tail (read from) tx1BufCnt VAR tx1Serial(14) ' # bytes in buffer rx1Serial VAR Byte (16) rx1Buf VAR rx1Serial(0) ' 8-byte buffer rx1Count VAR rx1Serial(8) ' rx bit count rx1Divide VAR rx1Serial(9) ' bit divisor timer rx1Byte VAR rx1Serial(10) ' recevied byte rx1Head VAR rx1Serial(11) ' buffer head (write to) rx1Tail VAR rx1Serial(12) ' buffer tail (read from) rx1BufCnt VAR rx1Serial(13) ' # bytes in buffer tx2Serial VAR Byte (16) ' tx serial data tx2Buf VAR tx2Serial(0) ' eight-byte buffer tx2Count VAR tx2Serial(8) ' tx bit count tx2Divide VAR tx2Serial(9) ' bit divisor timer tx2Lo VAR tx2Serial(10) ' holds start bit tx2Hi VAR tx2Serial(11) ' tx output reg tx2Head VAR tx2Serial(12) ' buffer head (write to) tx2Tail VAR tx2Serial(13) ' buffer tail (read from) tx2BufCnt VAR tx2Serial(14) ' # bytes in buffer svoData VAR Byte (16) ' bank servo data pos VAR svoData(0) ' position table pos1 VAR svoData(0) pos2 VAR svoData(1) pos3 VAR svoData(2) pos4 VAR svoData(3) svoTix VAR svoData(8) ' isr divider svoFrame_LSB VAR svoData(9) ' frame timer svoFrame_MSB VAR svoData(10) svoIdx VAR SvoData(11) ' active servo pointer svoTimer VAR svoData(12) ' pulse timer svoPin VAR svoData(13) ' active servo pin ' ========================================================================= INTERRUPT NOPRESERVE 307_200 ' run every 3.255 uS ' ========================================================================= ' -------------------------------- ' Mark ISR - use for timing events ' -------------------------------- ' Marker: ASM SETB isrFlag ' (1) ENDASM ' ---------------------- ' TX UART #1 - for SD-50 ' ---------------------- ' Transmit1: ASM BANK tx1Serial ' (1) CLRB tx1Divide.Tx1BaudBit ' (1) clear tx bit flag INC tx1Divide ' (1) update tx bit timer JNB tx1Divide.Tx1BaudBit, TX1_Done ' (2/4) TEST tx1Count ' (1) transmitting now? JZ TX1_Buffer ' (2/4) if txCount = 0, no STC ' (1) set for stop bit RR tx1Hi ' (1) rotate TX buf RR tx1Lo ' (1) MOVB TX1, tx1Lo.6 ' (4) output the bit DEC tx1Count ' (1) update the bit count JMP TX1_Done ' (3) TX1_Buffer: TEST tx1BufCnt ' (1) anything in buffer? JZ TX1_Done ' (2/4) exit if empty MOV W, #tx1Buf ' (2) point to buffer tail ADD W, tx1Tail ' (1) MOV FSR, W ' (1) MOV tx1Hi, IND ' (2) move byte to TX reg CLR tx1Lo ' (1) clear for start bit MOV tx1Count, #10 ' (2) start + 8 + stop INC tx1Tail ' (1) update tail pointer CLRB tx1Tail.3 ' (1) keep 0..7 DEC tx1BufCnt ' (1) update buffer count TX1_Done: BANK 0 ' (1) ENDASM ' ----------------------- ' RX UART #1 - from SD-50 ' ----------------------- Receive1: ASM BANK rx1Serial ' (1) JB rx1BufCnt.4, RX1_Done ' (2/4) skip if buffer is full MOVB C, RX1 ' (4) sample serial input TEST rx1Count ' (1) receiving now? JNZ RX1_Bit ' (2/4) yes, get next bit MOV W, #9 ' (1) no, prep for next byte SC ' (1/2) MOV rx1Count, W ' (1) if start, load bit count MOV rx1Divide, #Rx1Baud1x5 ' (2) prep for 1.5 bit periods RX1_Bit: DJNZ rx1Divide, RX1_Done ' (2/4) complete bit cycle? MOV rx1Divide, #Rx1Baud1x0 ' (2) yes, reload bit timer DEC rx1Count ' (1) update bit count SZ ' (1/2) RR rx1Byte ' (1) position for next bit SZ ' (1/2) JMP RX1_Done ' (3) RX1_Buffer: MOV W, #rx1Buf ' (1) point to buffer head ADD W, rx1Head ' (1) MOV FSR, W ' (1) MOV IND, rx1Byte ' (2) move rxByte to head INC rx1Head ' (1) update head CLRB rx1Head.3 ' (1) keep 0..7 INC rx1BufCnt ' (1) update buffer count RX1_Done: BANK 0 ' (1) ENDASM ' ------------------------------------ ' TX UART #2 - for Parallax Serial LCD ' ------------------------------------ ' Transmit2: ASM BANK tx2Serial ' (1) CLRB tx2Divide.Tx2BaudBit ' (1) clear tx bit flag INC tx2Divide ' (1) update tx bit timer JNB tx2Divide.Tx2BaudBit, TX2_Done ' (2/4) TEST tx2Count ' (1) transmitting now? JZ TX2_Buffer ' (2/4) if txCount = 0, no STC ' (1) set for stop bit RR tx2Hi ' (1) rotate TX buf RR tx2Lo ' (1) MOVB TX2, tx2Lo.6 ' (4) output the bit DEC tx2Count ' (1) update the bit count JMP TX2_Done ' (3) TX2_Buffer: TEST tx2BufCnt ' (1) anything in buffer? JZ TX2_Done ' (2/4) exit if empty MOV W, #tx2Buf ' (2) point to buffer tail ADD W, tx2Tail ' (1) MOV FSR, W ' (1) MOV tx2Hi, IND ' (2) move byte to TX reg CLR tx2Lo ' (1) clear for start bit MOV tx2Count, #10 ' (2) start + 8 + stop INC tx2Tail ' (1) update tail pointer CLRB tx2Tail.3 ' (1) keep 0..7 DEC tx2BufCnt ' (1) update buffer count TX2_Done: BANK 0 ' (1) ENDASM ' ------------------------ ' Virtual Servo Controller ' ------------------------ ' Test_Servo_Tix: ASM BANK svoData ' (1) INC svoTix ' (1) update divider CJB svoTix, #3, Servo_Exit ' (4/6) done? CLR svoTix ' (1) yes, reset for next ' Servo code below this point runs every 9.766 uS Check_Frame_Timer: CJNE svoFrame_LSB, #0, Update_Frame_Timer ' (4/6) frame timer done? CJNE svoFrame_MSB, #0, Update_Frame_Timer ' (4/6) MOV svoFrame_LSB, #2048 & 255 ' (2) yes, svoFrame = 20 ms MOV svoFrame_MSB, #2048 >> 8 ' (2) MOV svoPin, #%00000001 ' (2) start servo sequence CLR svoIdx ' (1) point to servo 0 MOV FSR, #pos ' (2) MOV svoTimer, IND ' (2) JMP Refesh_Servo_Outs ' (3) Update_Frame_Timer: SUB svoFrame_LSB, #1 ' (2) DEC svoFrame SUBB svoFrame_MSB, /C ' (2) Check_Servo_Timer: TEST svoPin ' (1) any servos on? SNZ ' (1/2) JMP Servo_Exit ' (3) no, exit DEC svoTimer ' (1) yes, update timer SZ ' (1/2) still running? JMP Servo_Exit ' (3) yes, exit Reload_Servo_Timer: INC svoIdx ' (3) point to next servo AND svoidx, #3 ' (2) keep 0 - 3 MOV W, #pos ' (1) get pulse timing ADD W, svoIdx ' (1) MOV FSR, W ' (1) MOV svoTimer, IND ' (2) move to timer Select_Next_Servo: CLC ' (1) RL svoPin ' (1) CLRB svoPin.4 ' (1) limit pins used Refesh_Servo_Outs: AND ServoCtrl, #%11110000 ' (2) clear servo pins OR ServoCtrl, svoPin ' (2) output new servo data Servo_Exit: BANK 0 ' (1) restore for foreground ENDASM RETURNINT ' ========================================================================= PROGRAM Start ' ========================================================================= ' ------------------------------------------------------------------------- ' Subroutine / Function Declarations ' ------------------------------------------------------------------------- DELAY_MS SUB 1, 2 ' delay in milliseconds TX1_BYTE SUB 1 ' transmit a byte TX1_STR SUB 2 ' transmit a string RX1_BYTE FUNC 1, 0 ' receive a byte TX2_BYTE SUB 1 TX2_STR SUB 2 TX2_DEC SUB 1, 3 GET_SONAR FUNC 2, 0 ' get value from Ping))) TURN_ON SUB 1 TURN_OFF SUB 1 MULT FUNC 2, 2, 4 ' multiply two values DIV FUNC 2, 2, 4 ' divide two values MOD FUNC 2, 2, 4 ' remainder of division ' ************************************************************************* ' Program Code ' ************************************************************************* Start: PUT pos, 154, 154, 154, 154 ' center servos TX1 = 1 TX2 = 1 DELAY_MS 100 ' let LCD initialize TX2_BYTE LcdOn1 TX2_BYTE LcdCls DELAY_MS 5 Main: TX2_STR Name_Plate DO ' control code here LOOP GOTO Main ' ------------------------------------------------------------------------- ' Subroutine / Function Code ' ------------------------------------------------------------------------- ' Use: DELAY_MS duration SUB DELAY_MS IF __PARAMCNT = 1 THEN tmpW1 = __PARAM1 ' save byte parameter ELSE tmpW1 = __WPARAM12 ' save word parameter ENDIF DO WHILE tmpW1 > 0 tmpW2 = 307 ' load 1 ms timer DO WHILE tmpW2 > 0 ' let timer expire \ CLRB isrFlag ' clear ISR flag \ JNB isrFlag, @$ ' wait for flag to be set DEC tmpW2 ' update 1 ms timer LOOP DEC tmpW1 ' update delay timer LOOP ENDSUB ' ------------------------------------------------------------------------- ' Use: TX1_BYTE aByte ' -- moves "aByte" to 8-byte circular buffer (when space is available) ' -- will wait if buffer is presently full ' -- tx1BufCnt holds byte count of transmit buffer (0 to 8) SUB TX1_BYTE ASM BANK tx1Serial ' point to tx vars JB tx1BufCnt.3, @TX1_BYTE ' prevent buffer overrun MOV W, #tx1Buf ' point to buffer head ADD W, tx1Head MOV FSR, W MOV IND, __PARAM1 ' move byte to tx buf INC tx1Head ' update head pointer CLRB tx1Head.3 ' keep 0..7 INC tx1BufCnt ' update buffer count BANK 0 ENDASM ENDSUB ' ------------------------------------------------------------------------- ' Use: TX1_STR [String | Label] ' -- moves z-String to tx1 buffer ' -- "String" is an embedded string ' -- "Label" is a DATA label with z-String SUB TX1_STR tmpW1 = __WPARAM12 ' get address of start DO READINC tmpW1, tmpB1 ' read a character IF tmpB1 = 0 THEN EXIT ' done? TX1_BYTE tmpB1 ' no, transmit the char LOOP ENDSUB ' ------------------------------------------------------------------------- ' Use: aByte = RX1_BYTE ' -- returns "aByte" from 8-byte circular buffer ' -- will wait if buffer is presently empty ' -- rxBufCnt holds byte count of receive buffer (0 to 8) FUNC RX1_BYTE ASM BANK rx1Serial TEST rx1BufCnt ' check buffer count JZ @RX1_BYTE ' wait if empty MOV W, #rx1Buf ' point to tail ADD W, rx1Tail MOV FSR, W MOV __PARAM1, IND ' get byte at tail INC rx1Tail ' update tail CLRB rx1Tail.3 ' keep 0 to 7 DEC rx1BufCnt ' update buffer count BANK 0 ENDASM ENDFUNC ' ------------------------------------------------------------------------- ' Use: TX2_BYTE aByte ' -- for Parallax Serial LCD SUB TX2_BYTE ASM BANK tx2Serial ' point to tx vars JB tx2BufCnt.3, @TX2_BYTE ' prevent buffer overrun MOV W, #tx2Buf ' point to buffer head ADD W, tx2Head MOV FSR, W MOV IND, __PARAM1 ' move byte to tx buf INC tx2Head ' update head pointer CLRB tx2Head.3 ' keep 0..7 INC tx2BufCnt ' update buffer count BANK 0 ENDASM ENDSUB ' ------------------------------------------------------------------------- ' Use: TX2_STR [String | Label] SUB TX2_STR tmpW1 = __WPARAM12 ' get address of start DO READINC tmpW1, tmpB1 ' read a character IF tmpB1 = 0 THEN EXIT ' done? TX2_BYTE tmpB1 ' no, transmit the char LOOP ENDSUB ' ------------------------------------------------------------------------- ' Use: TX2_DEC value {, digits } ' -- transmits "value" in DEC format ' -- "digits" MUST be specified with word values ' -- DEC3 or DEC5 format is used if "digits" not specified or invalid SUB TX2_DEC IF __PARAMCNT = 1 THEN ' byte w/o digits tmpW1 = __PARAM1 tmpB2 = 3 ELSEIF __PARAMCNT = 2 THEN ' byte w/digits tmpW1 = __PARAM1 tmpB2 = __PARAM2 ELSE ' word w/digits tmpW1 = __WPARAM12 tmpB2 = __PARAM3 ENDIF IF tmpB2 = 0 THEN ' validate digits tmpB2 = 5 ELSEIF tmpB2 > 5 THEN tmpB2 = 5 ENDIF tmpW2 = 10_000 ' preset divisor IF tmpB2 < 5 THEN ' less than 5 digits? tmpB1 = 5 - tmpB2 DO WHILE tmpB1 > 0 tmpW1 = MOD tmpW1, tmpW2 ' remove unused digit(s) tmpW2 = DIV tmpW2, 10 ' update divisor DEC tmpB1 LOOP ENDIF DO WHILE tmpB2 > 0 tmpB1 = DIV tmpW1, tmpW2 ' extract digit tmpB1 = tmpB1 + "0" ' convert to character TX2_BYTE tmpB1 ' send it tmpW1 = MOD tmpW1, tmpW2 ' remove sent digit tmpW2 = DIV tmpW2, 10 ' adjust divisor DEC tmpB2 ' adjust digit count LOOP ENDSUB ' ------------------------------------------------------------------------- ' Use: result = GET_SONAR ' -- returns measurement in 3.25 uS units ' -- 1" = ~23 units FUNC GET_SONAR HIGH Sonar ' "ping" the sonar tmpB1 = 3 ' about 10 uS DO WHILE tmpB1 > 0 \ CLRB isrFlag ' clear ISR flag \ JNB isrFlag, @$ ' wait for flag to get set DEC tmpB1 ' update timer LOOP Sonar = 0 ' drop ping output NOP ' let it settle INPUT Sonar ' configure for input tmpW1 = 0 ' clear return value \ JNB Sonar, @$ ' wait for leading edge DO ' meausure pulse \ CLRB isrFlag \ JNB isrFlag, @$ INC tmpW1 ' update measurement count IF Sonar = 0 THEN EXIT LOOP UNTIL tmpW1 = 0 ' exit on overflow tmpW1 = tmpW1 >> 1 ' remove return trip RETURN tmpW1 ENDFUNC ' ------------------------------------------------------------------------- ' Use: TURN_ON pinNum ' -- use to activate outputs 0..5 ' -- use in place of HIGH SUB TURN_ON tmpB1 = __PARAM1 IF tmpB1 < 6 THEN ' valid pin? tmpB2 = 1 << tmpB1 ' yes, create pin mask DigOuts = DigOuts | tmpB2 ' and set output ENDIF ENDSUB ' ------------------------------------------------------------------------- ' Use: TURN_OFF pinNum ' -- use to deactivate outputs 0..5 ' -- use in place of LOW SUB TURN_OFF tmpB1 = __PARAM1 IF tmpB1 < 6 THEN ' valid pin? tmpB2 = 1 << tmpB1 ' yes, create pin mask tmpB2 = tmpB2 ^ $FF ' invert mask DigOuts = DigOuts & tmpB2 ' clear selected output ENDIF ENDSUB ' ------------------------------------------------------------------------- ' Use: result = MULT value1, value2 ' -- when mixing types, the word value must be specified first FUNC MULT IF __PARAMCNT = 2 THEN ' two bytes tmpW3 = __PARAM1 tmpW4 = __PARAM2 ELSEIF __PARAMCNT = 3 THEN ' word and byte tmpW3 = __WPARAM12 tmpW4 = __PARAM3 ELSE ' two words tmpW3 = __WPARAM12 tmpW4 = __WPARAM34 ENDIF tmpW3 = tmpW3 * tmpW4 RETURN tmpW3 ENDFUNC ' ------------------------------------------------------------------------- ' Use: result = DIV value1, value2 ' -- when mixing types, the word value must be specified first FUNC DIV IF __PARAMCNT = 2 THEN ' two bytes tmpW3 = __PARAM1 tmpW4 = __PARAM2 ELSEIF __PARAMCNT = 3 THEN ' word and byte tmpW3 = __WPARAM12 tmpW4 = __PARAM3 ELSE ' two words tmpW3 = __WPARAM12 tmpW4 = __WPARAM34 ENDIF tmpW3 = tmpW3 / tmpW4 RETURN tmpW3 ENDFUNC ' ------------------------------------------------------------------------- ' Use: result = MOD value1, value2 ' -- when mixing types, the word value must be specified first FUNC MOD IF __PARAMCNT = 2 THEN ' two bytes tmpW3 = __PARAM1 tmpW4 = __PARAM2 ELSEIF __PARAMCNT = 3 THEN ' word and byte tmpW3 = __WPARAM12 tmpW4 = __PARAM3 ELSE ' two words tmpW3 = __WPARAM12 tmpW4 = __WPARAM34 ENDIF tmpW3 = tmpW3 // tmpW4 RETURN tmpW3 ENDFUNC ' ------------------------------------------------------------------------- ' User Data ' ------------------------------------------------------------------------- Name_Plate: DATA "Animation Control", CR, LF, 0