Support » PIC-Based, Obstacle-Avoiding Robot »
4. Software for the PIC
<p>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.</p>
<p>First, we name the registers and bits that we will use throughout
the rest of the program:</p>
<pre>
;****** 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</pre>
<p>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.</p>
<pre>
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</pre>
<p>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.</p>
<pre>
 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</pre>
<p>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, <code>updateMotor</code> and <code>pause</code>, are
shown later; <code>updateMotor</code> sends a 4-byte command to the
motor controller based on <code>BYTE3</code> and <code>BYTE4</code>, and <code>pause</code> stops
both motors for 50 ms. <code>pause</code> 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.</p>
<pre>
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</pre>
<p>Finally, here are the subroutines called from the main loop. The <code>updateMotor</code>
subroutine sends the motor controller the 4-byte control sequence of 0x80 and 0x00
followed by the motor number and direction, specified in <code>BYTE3</code>, and the
speed, specified in <code>BYTE4</code>. 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.</p>
<pre>
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</pre>
<table class="warning"><tr><td>
<strong>Note:</strong>
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.</td><p></tr></table></p>