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.