Support » PIC-Based, Obstacle-Avoiding Robot »
4. Software for the PIC
Using the motor controller is very simple, even if you program your PIC in assembly. This sample program makes our little robot drive forward until it hits an obstacle; once it does, it backs up, turns away from the side where the collision occured, and resumes moving forward. All of the code below uses the standard assembly language supported by Microchip’s MPLAB development software. Even if you are using a different assembler or compiler, this example should give you a good start.
First, we name the registers and bits that we will use throughout the rest of the program:
;****** Equates **************************************************************** Bank0RAM equ 020h ;start of bank 0 RAM area SMC_PORT equ PORTB ;motor controller on port b BMP_PORT equ PORTB ;bumper switches on port b ;bit equates SOUT equ 2 ;serial output to motor controller SRST equ 3 ;to reset pin on motor controller LBMP equ 4 ;left bumper switch RBMP equ 5 ;right bumper switch ;****** Variables ************************************************************** cblock Bank0RAM ARG1L ARG1H BYTE3 ;for storing bytes 3 and 4 in the serial protocol BYTE4 endc
It’s also convenient to have a subroutine for making precise pauses. This routine takes the 16-bit value in ARG1H and ARG1L and delays for approximately that many milliseconds. Of course, the length of the delay is dependent on the clock speed, which is 4 MHz in our example.
milliDelay movlw .250 ;outer loop addlw 0xFF ;inner loop btfss STATUS,Z goto $-2 ;goto inner loop movlw 1 ;16-bit decrement subwf ARG1L,f btfss STATUS,C decf ARG1H,f movf ARG1H,f ;16-bit test if zero btfsc STATUS,Z movf ARG1L,f btfsc STATUS,Z return goto milliDelay
We are now ready to approach the main program, which begins by configuring the UART and resetting the motor controller. The 2 millisecond pause at the end gives the motor controller some time between resetting and receiving serial input.
org 0x05 startMain ;set up I/O ports and serial port for 19,200 baud UART bsf STATUS,RP0 movlw b'11110111' ;smc reset is the only normal movwf TRISB ; output--all others inputs or serial out bcf OPTION_REG,NOT_RBPU ;enable PORTB pull-up resistors movlw .12 ;set baud rate to 19,200 (assuming BRGH=1) movwf SPBRG ;(address 99h) movlw b'00100100' ;bit 6 clear - 8-bit transmission ;bit 5 set - enable transmit ;bit 4 clear - UART asynchronous mode ;bit 2 set - high baud rate mode ;bits 7, 3, 1, 0 - don't care movwf TXSTA ;address 98h bcf STATUS,RP0 ;select bank 0 movlw b'10010000' ;bit 7 set - enable serial port ;bit 6 clear - 8-bit reception ;bit 4 set - continuous receive ;bits 5, 3:0 - don't care movwf RCSTA ;address 18h ;reset motor controller bcf SMC_PORT,SRST nop nop bsf SMC_PORT,SRST movlw 0x00 movwf ARG1H movlw 0x02 movwf ARG1L call milliDelay
The program is now ready to run its main loop, in which it
checks the bumper switches and takes the appropriate action.
Two supporting subroutines, updateMotor
and pause
, are
shown later; updateMotor
sends a 4-byte command to the
motor controller based on BYTE3
and BYTE4
, and pause
stops
both motors for 50 ms. pause
is used to keep the motors
from having to instantly switch from forward to reverse,
which causes a current surge that can exceed the
motor controller’s maximum current specification of 1 A.
mainLoop btfss BMP_PORT,LBMP goto left_bump btfss BMP_PORT,RBMP goto right_bump ;no bumps, so just go straight movlw 0x00 ;right motor, forward movwf BYTE3 movlw 0x7F ;full speed movwf BYTE4 call updateMotor movlw 0x02 ;right motor, forward movwf BYTE3 movlw 0x7F ;full speed movwf BYTE4 call updateMotor goto mainLoop left_bump call pause movlw 0x03 ;right motor, backward movwf BYTE3 movlw 0x7F ;full speed movwf BYTE4 call updateMotor movlw 0x01 ;left motor, backward movwf BYTE3 movlw 0x3F ;half speed movwf BYTE4 call updateMotor movlw HIGH .1500 ;pause 1.5 seconds (1500 ms) movwf ARG1H movlw LOW .1500 movwf ARG1L call milliDelay call pause goto mainLoop right_bump call pause movlw 0x03 ;right motor, backward movwf BYTE3 movlw 0x3F ;half speed movwf BYTE4 call updateMotor movlw 0x01 ;left motor, backward movwf BYTE3 movlw 0x7F ;full speed movwf BYTE4 call updateMotor movlw HIGH .1500 ;pause 1.5 seconds (1500 ms) movwf ARG1H movlw LOW .1500 movwf ARG1L call milliDelay call pause goto mainLoop
Finally, here are the subroutines called from the main loop. The updateMotor
subroutine sends the motor controller the 4-byte control sequence of 0x80 and 0x00
followed by the motor number and direction, specified in BYTE3
, and the
speed, specified in BYTE4
. To keep this example program simple,
this subroutine does not exit until all four
bytes have been copied to the transmit buffer. The program could be
made more efficient by using interrupts, allowing the PIC to perform
other tasks while the UART is busy transmitting.
updateMotor btfss PIR1,TXIF goto updateMotor movlw 0x80 movwf TXREG nop updateMotor2 btfss PIR1,TXIF goto updateMotor2 movlw 0x00 movwf TXREG nop updateMotor3 btfss PIR1,TXIF goto updateMotor3 movf BYTE3,W movwf TXREG nop updateMotor4 btfss PIR1,TXIF goto updateMotor4 movf BYTE4,W movwf TXREG return pause movlw 0x02 ;right motor off movwf BYTE3 movlw 0x00 movwf BYTE4 call updateMotor movlw 0x00 ;left motor off movwf BYTE3 movlw 0x00 movwf BYTE4 call updateMotor movlw HIGH .50 ;pause 0.05 second (50 ms) movwf ARG1H movlw LOW .50 movwf ARG1L call milliDelay return
Note: Make sure the watchdog timer is disabled in the configuration bits. The brown-out detection feature must also be turned off for the PIC to operate off of the 3.6 V power source. |