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.