📄 picservo.asm
字号:
CLRF DataByte
ResetMsgLoop
MOVF DataByte, w
CALL GetResetMsg
ANDLW 0ffh ; check for end of message
BTFSC STATUS, Z
GOTO EndResetLoop ; was 0, exit
CALL TransmitDataByte
INCF DataByte, f
GOTO ResetMsgLoop
EndResetLoop
BSF INTCON, GIE
RETURN
;+=============================================================================
;| Command Parsing Routines.
;|
;| The following routines form the command parser and state machine to
;| handle multi-byte commands.
;+=============================================================================
;+-----------------------------------------------------------------------------
;| SUBROUTINE: ParseCommand
;|
;| Parse the given byte in W as a command. All incoming data is sent to this
;| routine to be interrupted. If the command is a single byte command it is
;| processed immediatly, if not the Parser waits for the next byte to execute
;| the command.
;+-----------------------------------------------------------------------------
ParseCommand
; Determine if the Parser is waiting for a data byte, it is
; store the incoming byte in the data field, if not store it
; in the command field.
BTFSS Parser_WaitForData
MOVWF Parser_Command ; Store in command
BTFSC Parser_WaitForData
MOVWF Parser_Data ; Store in Data
; Access the Command nibble and store it in the Parser_Temp variable
; so we can access it and determine what the command is
SWAPF Parser_Command, w ; Place the upper nibble from the command
; into the W register's lower nibble
ANDLW 0Fh ; Mask out what was the channel select
MOVWF Parser_Temp ; Store in a Temp Variable.
; Check for each command against the stored value and if we
; match one, goto the handler for that command, if we don't match
; any command we need to discard the command.
MOVLW 0h ; Check Reset Command
SUBWF Parser_Temp, w
BTFSC STATUS, Z ; Does it match?
GOTO ParseResetCmd ; Yes
MOVLW 1h ; Check Set Servo Position
SUBWF Parser_Temp, w
BTFSC STATUS, Z ; Does it match?
GOTO ParseSetServoPosCmd ; Yes
MOVLW 2h ; Check Set Servo Offset
SUBWF Parser_Temp, w
BTFSC STATUS, Z ; Does it match?
GOTO ParseSetServoOffsetCmd ; Yes
MOVLW 3h ; Check Enable Servo Output
SUBWF Parser_Temp, w
BTFSC STATUS, Z ; Does it match?
GOTO ParseEnableServoCmd ; Yes
MOVLW 4h ; Check Disable Servo Output
SUBWF Parser_Temp, w
BTFSC STATUS, Z ; Does it match?
GOTO ParseDisableServoCmd ; Yes
; We have an invalid command, reset the parser and return
BCF Parser_WaitForData ; next byte is a command
RETURN ; Return to the caller
;------------------------------------------------------------------------------
; Handle the Reset Command
ParseResetCmd
; If the Parser_WaitForData value is not set, then we need to set it and
; get the data byte before proceeding
BTFSS Parser_WaitForData
GOTO ParserWaitForDataByte
; We have the data byte, now lets parse the command and execute it
; If the data byte is NOT Zero it means that this is not a valid
; command and are are out of sync with the host so we make the
; current data byte a command and restart parsing
MOVF Parser_Data, f
BTFSS STATUS, Z ; is it zero?
GOTO ParseResetInvalid ; No.
; the data byte is zero so all is well,
CALL ResetState
BCF Parser_WaitForData ; next byte is a command
RETURN
ParseResetInvalid
BCF Parser_WaitForData ; next byte is a command
MOVF Parser_Data, w ; data byte is the next command
GOTO ParseCommand
;------------------------------------------------------------------------------
; Handle the Set Servo Position Command
ParseSetServoPosCmd
; If the Parser_WaitForData value is not set, then we need to set it and
; get the data byte before proceeding
BTFSS Parser_WaitForData
GOTO ParserWaitForDataByte
; We have the data byte, now lets parse the command and execute it
; Get the channel number in W
MOVF Parser_Command, w
ANDLW 07h ; ensure we only have 0-7 as the channel
; Conver the channel number into the address of the Mask data byte for the
; given servo, then add the offset to the "Position value"
CALL GetAddressOfServoData
ADDLW 2
; set the indirect address register to the RAM address of the Servos
; Position regiter, then save the data byte into that RAM location
MOVWF FSR
MOVF Parser_Data, w
MOVWF INDF
BCF Parser_WaitForData ; next byte is a command
RETURN
;------------------------------------------------------------------------------
; Handle the Set Servo Offset Command
ParseSetServoOffsetCmd
; If the Parser_WaitForData value is not set, then we need to set it and
; get the data byte before proceeding
BTFSS Parser_WaitForData
GOTO ParserWaitForDataByte
; We have the data byte, now lets parse the command and execute it
; Get the channel number in W
MOVF Parser_Command, w
ANDLW 07h ; ensure we only have 0-7 as the channel
; Conver the channel number into the address of the Mask data byte for the
; given servo, then add the offset to the "Offset value"
CALL GetAddressOfServoData
ADDLW 1
; set the indirect address register to the RAM address of the Servos
; Position regiter, then save the data byte into that RAM location
MOVWF FSR
MOVF Parser_Data, w
MOVWF INDF
BCF Parser_WaitForData ; next byte is a command
RETURN
;------------------------------------------------------------------------------
; Handle the Enable Servo Command
ParseEnableServoCmd
; load the command into W and convert the channel into a bit mask
MOVF Parser_Command, w
ANDLW 07h ; ensure we only have 0-7 as the channel
CALL ParserChannelToBitMask
; Cheat and directly manipulate the Enables byte as it matches our
; channel mask
IORWF Enables, f ; bit is set, any other are left unchanged
BCF Parser_WaitForData ; next byte is a command
RETURN
;------------------------------------------------------------------------------
; Handle the Disable Servo Command
ParseDisableServoCmd
; load the command into W and convert the channel into a bit mask
MOVF Parser_Command, w
ANDLW 07h ; ensure we only have 0-7 as the channel
CALL ParserChannelToBitMask
; Cheat and directly manipulate the Enables byte as it matches our
; channel mask
XORWF Enables, f ; bit is cleared, any other are left unchanged
BCF Parser_WaitForData ; next byte is a command
RETURN
;------------------------------------------------------------------------------
; Have the parser setup to get a data byte then return to the caller
ParserWaitForDataByte
BSF Parser_WaitForData ; next byte is a data byte
RETURN
;+=============================================================================
;| RS232 Serial Interface Routines
;|
;| Out Serial interface is going to be 2400 baud, 8 data bits, no parity and
;| one stop bit. This means that a bit takes about 416us (1/2400). Therefore
;| we can use our DelayWByFour loop with a value of 103 ((416/4)-1) to delay
;| for bit samples. Note that this is not exactly accurate, but we are scanning
;| quite offen for the beginning of the start bit an over the next 10 bits (8
;| data + start + stop) the timing isn't going to drift too much.
;|
;| We will also use Hardware flow control so the host doesn't send us anything
;| when we're not in a position to receive it (ie, in the servo pulse loop).
;| The RS232 Request to Send line is routed to the PIC, but we won't use it
;| because it takes a little extra effort to set it to work properly on the PC.
;| and by default it is active all the time. We will be doing half duplex which
;| means that we can't send and receive at the same time so CTS is disabled
;| while we're transmitting.
;|
;| The RS232 Clear To Send line (active low) is also routed to the PIC, we will
;| enable is when we are able to receive data and disable it when we are not.
;|
;| The serial code and the interrupt routine to generate the servo pulses are
;| mutually exclusive as each will corrupt the timing of the other. Therefore
;| if we detect a start bit, we will disable the interrupts until we have
;| received a byte.
;|
;| Receiving Data
;|
;| The Serial In line is normally high (1) but when a byte is to be sent the
;| start bit (a zero bit) brings the line low. This is our queue to start
;| reading bits. We will wait for half a bit read the input line and check that
;| the list is still low (0). If it isn't then it was a glitch so we will start
;| looking for the start bit again. If it is still low we will wait for a full
;| bit period and sample the input line again. This is the stored in bit 0 of
;| the received data byte. We then continue sampling a bit intervals until we
;| have all 8 bits of data. Then we wait one more full bit period and sample the
;| stop bit. This should be high, it is isn't, we have a framing error and the
;| byte should be discarded. This normally happens if the sender's baud rate
;| is different from our own.
;|
;| Transmitting Data
;|
;| We will set the data output line low, wait for one bit period and the set
;| the output to the value of bit 0 in the output byte, We then wait for
;| another bit period and send the next byte and contine until we're finished
;| all the 8 data bits, we then set the output high and wait for another bit
;| period to send the stop bit.
;+=============================================================================
;+-----------------------------------------------------------------------------
;| SUBROUTINE: InitSerialPort
;|
;| Initialise the serial port. Set all the lines to the apporpriate levels but
;| ensure that CTS is inactive (high).
;+-----------------------------------------------------------------------------
InitSerialPort
; Set the output ports to the default states and disable CTS
BSF Serial_TX ; Output High by default
BSF Serial_CTS ; CTS is inactive so the host can't send us anything
; Delay for at least one byte incase the host was sending data
MOVLW 10
MOVWF Serial_LoopCount
InitSerialDelay
MOVLW Serial_BitDelay
CALL DelayWByFour
DECFSZ Serial_LoopCount, f ; subtract 1 from loop count
BTFSS STATUS, Z ; If zero, exit loop
GOTO InitSerialDelay
RETURN
;+-----------------------------------------------------------------------------
;| SUBROUTINE: TransmitDataByte
;|
;| Transmit the byte in the W register out the serial port. All interrupts
;| need to be disabled prior to calling and reenabled after calling.
;+-----------------------------------------------------------------------------
TransmitDataByte
MOVWF Serial_CurrentByte ; store the byte to send
; Check if the CTS line is active (0). if it is we need to disable it and
; remember to restore it when we exit
BTFSS Serial_CTS ; if CTS is set (0), set the flag to restore it
BSF Serial_RestoreCTS
BSF Serial_CTS ; clear CTS
; Send the Start bit
BCF Serial_TX
MOVLW Serial_BitDelay ; 1 bit delay
CALL DelayWByFour
; loop through the byte sending each bit as we go
MOVLW 8
MOVWF Serial_LoopCount
TxDataByteLoop
BSF STATUS, C
RRF Serial_CurrentByte, f ; get the current bit into the C flag
BTFSS STATUS, C
BCF Serial_TX ; If the bit = 0 set TX = 0
BTFSC STATUS, C
BSF Serial_TX ; If the bit = 1 set TX = 1
MOVLW Serial_BitDelay ; 1 bit delay
CALL DelayWByFour
DECFSZ Serial_LoopCount, f ; subtract 1 from loop count
GOTO TxDataByteLoop
; Send the Stop bit
BSF Serial_TX
MOVLW Serial_BitDelay ; 1 bit delay
CALL DelayWByFour
; restore CTS if needed
BTFSC Serial_RestoreCTS ; if flag is set, we must restore CTS
BCF Serial_CTS
BCF Serial_RestoreCTS ; Clear the flag
RETURN
;+-----------------------------------------------------------------------------
;| SUBROUTINE: ReceiveDataByte
;|
;| Receive a byte of data and return it in the W register. Set the flag
;| Serial_ReceiveValid if the byte was received correctly.
;|
;| This must be called as soon as possible after detecting the falling edge
;| at the start of the start bit. All interrupts need to be disabled prior to
;| calling and reenabled after calling.
;+-----------------------------------------------------------------------------
ReceiveDataByte
; initialise the return valid flag and return value
BCF Serial_ReceiveValid ; Assume invalid
MOVLW 0
MOVWF Serial_CurrentByte
MOVLW Serial_HalfBitDelay ; 1/2 bit delay
CALL DelayWByFour
; Check that we are still in a start bit (Serial_RX low)
BTFSC Serial_RX
RETURN ; not in start bit, return with fail
; Receive the bits
MOVLW 8
MOVWF Serial_LoopCount
RxDataByteLoop
MOVLW Serial_BitDelay ; 1 bit delay
CALL DelayWByFour
; Sample the value
BTFSS Serial_RX
BCF STATUS, C ; If the bit = 0, set Carry = 0
BTFSC Serial_RX
BSF STATUS, C ; If the bit = 1, set Carry = 1
RRF Serial_CurrentByte, f ; Shift Carry into the output bit
DECFSZ Serial_LoopCount, f ; subtract 1 from loop count
GOTO RxDataByteLoop
; check the stop bit is valid
MOVLW Serial_BitDelay ; 1 bit delay
CALL DelayWByFour
BTFSC Serial_RX
BSF Serial_ReceiveValid ; Stop bit != 1, invalid byte
; load the byte to return
MOVF Serial_CurrentByte, w
RETURN
; End of Code
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -