SUPPORTER BANNERS

ElectronicKits.com







PIC ROBOT  

LITTLE INTRODUCTION

In this page there is all my study about a little robot driven by a pic microcontroller.

I have a lot of experience on C language so my first idea  has been  use this  language instead of the complex assembler.

The C language is easier to learn also for the person without programming experience.

a Leading Provider of Microcontroller & Analog Semiconductors

The PIC FLASH used allow erasing and reprogramming of the MCU program memory.

Reprogrammability offers a highly flexible solution to today's ever-changing market demands and can substantially reduce time to market.
Users can program their systems late in the manufacturing process or update systems in the field. This allows easy code revisions, system parameteriza-tion or customer-specific options with no scrappage. Reprogrammability also reduces the design verification cycle.


PROGRAMMER BOARD

The first step has been get a programmer board and my choice is gone on PROGRAMMATORE DEMOBOARD PER PIC • 8220-VM111



produced by Velleman




The manuals of this board are available on many languages and there is a chepper version in kit called K8048.
This board could be used to program and to make simple test with many PIC from 8 to 28 pins.
The board coming with a PIC 16F627 installed.

To transfert the program from the PC to the board it is necessary a serial port.
My experienze with two different USB to serial converter has been negative.

The samples programs are in assembler language  and  the demo2.asm allow to test  any button and led in the VM111 board.
To compile the code you just need to start the compiler MPASMWIN, included in the CDROM in board package,



select the demo2.asm file and click on Assemble button without change any options but checking in the listbox the processor 16F627.

A file with HEX estention will be generated in the same directory of the source file.

Now we need to start the program PROGPIC2 with the icon



,open the file with HEX estension, select the rigth Controller in the listbox to 16F627, move the switch on the board in the PROG. posistion and click on Write button.



If all is gone fine an OK in the Activity box will be print.

Now move the switch on the board in the RUN position and you should seen the LED game.

Tolking with other DIY I have ear that the best programmer are the parallel version.



THE C COMPILER

To manage a more friendly code I have searched a C Compiler to start the experiment.



A freeware version of the C compiler  could be found at  HI-TEC Software in the Download
/ Demos & free software selectin PICC-LITE (Windows), also a Linux version is available.

The develop environment HTLPIC include a simple editor with compile integration.

You need just to open the source file with extension .C and select Compile / Compile and Link or F3.

Remember to save the source after the modification with File / Save because the compilation does not save the source.



A new windows ask for the microcontroller selection.



Now another windows ask for  float variables type



Now set all the optimizations



Now the output file format



Here leave as it is without changes.



The compilation will start with a red window.

A great help to set the rigth options has been received by the italian website http://www.jofi.it/fiser/ by Sergio Ficco

To compile with a faster method use a command line  like the following:

 



THE CODE PART 1 - SWITCH ON A LED


Now we can start with a simple code to switch on a LED.

Follows the schematic of parts the VM111 board relative to the connection from the PIC with 16 pins to the LED and SW (button).
We can see that the LED6 is connected to the RB5 pin of PIC.


 
This schematic has been extract from the complete available in the manual of the K8048 board.

Usually in the PIC the pins are configurable to do more functions so the same pin could be an input or an output or program input.

 

Finally the code:

#include    <pic.h>

__CONFIG(WDTDIS & XT & UNPROTECT & LVPDIS);

void main()
{
    TRISB  = 0b11000000;

    PORTB  = 0b00100000;
}

Only two lines of source code? yes

The first line assigne to the TRISB register the  0 value to the bit 0 to 5 to set the RA0 - RA5 port to be outputs following the table in the PIC16F627 datasheet:



This table show all the rgister that could change the use of the RB ports.

The second line ofcourse switch on the led on the RB5 port (LED6).

The __CONFIG  specify the PIC processor configuration fuses and I have lost many time to search this because does not apper in many samples on internet.
Without this line the PROGPIC2 program give the warning "No Config-World in File!" and if you go on writing this program this will not run.
The uinique node to program a PIC without specify a __CONFIG line is deselect the checkbox Erase bef. Write and Write Config. Word to preserve a previous configuration and update only the code.

The parameters used in my sample are:

WDTDIS      // disable watchdog
XT          // specify an XT crystal
UNPROTECT   // leave the code space unprotected
LVPDIS      // low voltage programmer disabled


The last parameter has been set to skip the problems found using the RB4 port.
This port is a low voltage programming input pin when this is enable so cannot be used like output.




THE CODE PART 2 - SEQUENTIAL LED LOOP

There is the posibility to set only one bit per time of PORTB using RBx variables.
To create a sequential led loop I have used the DelayMs() function given with the compiler like examples in the delay.c.

#include    <pic.h>

#include    "delay.c"

__CONFIG(WDTDIS & XT & UNPROTECT & LVPDIS);

#define XTAL    4000000         // crystal frequency - 4MHz
#define DELAY   250             // delay time


void main()
{
    TRISB  = 0b11000000;
       
    while(1)
    {
            BB5 =  1; DelayMs(DELAY);
            RB5 =  0; DelayMs(DELAY);
            RB4 =  1; DelayMs(DELAY);
            RB4 =  0; DelayMs(DELAY);
            RB3 =  1; DelayMs(DELAY);
            RB3 =  0; DelayMs(DELAY);
            RB2 =  1; DelayMs(DELAY);
            RB2 =  0; DelayMs(DELAY);
            RB1 =  1; DelayMs(DELAY);
            RB1 =  0; DelayMs(DELAY);
            RB0 =  1; DelayMs(DELAY);
            RB0 =  0; DelayMs(DELAY);    
    }
}

The delay has been created leaving the microcontroller to do nothing.



THE CODE PART 3 - GET BUTTON STATE

To get button state  is possble use the RAx variables and the follwing table given in the PIC16F627 datasheet show the relative registers.



Has been necessary disable the comparators on input pins RA0, RA1 and RA2 setting to 1 the first bits of the register CMCON.

CMCON  = 0b00000111;  // CM0, CM1 and CM2 set to 1

This code run but it have a little problem: the button press cannot be recognized until we are in the led sequential part of the code so to check the buttons functions you need to keep pressed the button until the led sequence is end.

#include    <pic.h>

#include    "delay.c"

__CONFIG(WDTDIS & XT & UNPROTECT & LVPDIS);

#define XTAL    4000000         // crystal frequency - 4MHz
#define DELAY    250


void main()
{
    int  flag = 1;
   
    TRISB  = 0b11000000;
    CMCON  = 0b00000111;    // disable comparator on ra0,1,2
    TRISA  = 0b11111111;
       
    while(1)
    {
        if (RA0 == 1)
        {
            flag = 0;
        }
        if (RA1 == 1)
        {
            flag = 1;
        }
        if (RA2 == 1)
        {
            PORTB = 0b00111111;
            flag = 0;
        }
        if (RA3 == 1)
        {
            PORTB = 0b00000000;
            flag = 0;
        }
        if (flag == 1)
        {
            RB5 =  1; DelayMs(DELAY);
            RB5 =  0; DelayMs(DELAY);
            RB4 =  1; DelayMs(DELAY);
            RB4 =  0; DelayMs(DELAY);
            RB3 =  1; DelayMs(DELAY);
            RB3 =  0; DelayMs(DELAY);
            RB2 =  1; DelayMs(DELAY);
            RB2 =  0; DelayMs(DELAY);
            RB1 =  1; DelayMs(DELAY);
            RB1 =  0; DelayMs(DELAY);
            RB0 =  1; DelayMs(DELAY);
            RB0 =  0; DelayMs(DELAY);
        }
    }
}


To skip the problem of this code you just need to change the DelayMs function inserting the button test in the loop.
Follows the code of function modified:

void DelayMs(unsigned char cnt)
{
#if    XTAL_FREQ <= 2MHZ
    do {
        DelayUs(996);

        if (RA0 == 1 ||
            RA1 == 1 ||
            RA2 == 1 ||
            RA3 == 1)
            return;
    } while(--cnt);
#endif

#if    XTAL_FREQ > 2MHZ   
    unsigned char    i;
    do {
        i = 4;
        do {
            DelayUs(250);

            if (RA0 == 1 ||
                RA1 == 1 ||
                RA2 == 1 ||
                RA3 == 1)
                return;
        } while(--i);
    } while(--cnt);
#endif
}

The button SW1 stop the sequential led loop, SW2 restart the sequentail led loop, SW3 switch on all the leds, SW4 switchoff all the led.



THE CODE PART 4 - TIMER0 AND INTERRUPT

The following is a simple example to check the timer and interrupt function of the PIC.

Understand the Timer function is very easy using the following pictures published by Sergio Tanzilli
in the italian articles on
http://airlab.elet.polimi.it/document/pbe/italiano or  www.tanzilli.com.



To the left are visible the two different frequency source for the timer: the clock divide by 4 or the pin T0CK.
Changing the value of the T0CS and PSA registers  is possible change the flow as show below.





Follows an easy example to switch on and switch off a led every second.

#include    <pic.h>

__CONFIG(WDTDIS & XT & UNPROTECT & LVPDIS);

#define XTAL    4000000         // crystal frequency - 4MHz


long tics = 0l;
int flag = 1;

void interrupt prv_int(void)
{
    //****************************************************************
    // To calculate when will enter here:
    //
    // Freq = clock cycle / 4 / 256 = 4000000Hz / 4 / 256 = 3906Hz
    // Period = 1 / 3906Hz = 0.256msec
    //****************************************************************
    if (!T0IF)
       return;

    //****************************************************************
    // T0IF is 1 when the counter TMR0 is arrived to the
    // overflow = 0xFF = 256 so to calculate when will enter here:
    //
    // Freq = (clock cycle / 4 / 256) / 256 = 3906Hz / 256 = 15Hz
    // Period = 1 / 15Hz = 65msec
    //****************************************************************

    T0IF = 0;

    tics++;

    if (tics == 15l)
    {
        tics = 0l;

        //***********************************************************
        // To calculate when will enter here:
        // Freq = 15Hz / 15 = 1Hz and periord is 1 second
        //******************************+****************************
      
        if (flag == 1)
        {
            RB5 = 1;
            flag = 0;
        }
        else
        {
            RB5 = 0;
            flag = 1;
        }
    }
}

main()
{
    TRISB  = 0b11000000;
    CMCON  = 0b00000111;    // disable comparator on ra0,1,2
    TRISA  = 0b11111111;


    TMR0   = 0;  // Reset the TMR0 counter
    T0CS   = 0;  // Enable timer to get the input from internal counter XTAL/4
    PSA    = 0;  // Enable prescaler to divide the frequency
    PS2    = 1;  //
    PS1    = 1;  // 111 mean prescaler set to 1:256
    PS0    = 1;  //

    INTCON = 0;  // reset all the flag bits for all the interrupts
    GIE    = 1;  // global interrupt enable bit (1: enable all)
    T0IE   = 1;  // TMR0 overflow interrupt enable bit (1: enable TMR0)
  
    while(1)
        continue;    // while forewer
}






THE CODE PART 5 - TIMER2 AND INTERRUPT


The following is a simple example to check the timer and interrupt function of the PIC.

Understand the Timer2 function is very easy using the following picture.




The clock frequency divided by 4 pass to a prescaler with 3 possible set  1:1, 1:4 or 1:16 and it is compared with the PR2 register value.
If the result of compare is an equal the frequency pass to a postscaler with all the value from 1:1 to 1:16.



Follows an easy example to switch on and switch off a led every second. 

#include    <pic.h>

__CONFIG(WDTDIS & XT & UNPROTECT & LVPDIS);

#define XTAL    4000000         // crystal frequency - 4MHz


long tics = 0l;
int flag = 1;

void interrupt prv_int(void)
{
    if (!TMR2IF)
       return;

    //****************************************************************
    // TMR2IF is 1 when the counter TMR2 is arrived to the
    // PR2 register value (default: 0xFF = 255)
    // so to calculate when will enter here:
    //
    // Freq = (clock cycle / 4 / 16 / 255 / 16 = 15Hz
    // Period = 1 / 15Hz = 65msec
    //****************************************************************

    TMR2IF = 0;

    tics++;

    if (tics == 15l)
    {
        tics = 0l;

        //***********************************************************
        // To calculate when will enter here:
        // Freq = 15Hz / 15 = 1Hz and periord is 1 second
        //******************************+****************************
      
        if (flag == 1)
        {
            RB5 = 1;
            flag = 0;
        }
        else
        {
            RB5 = 0;
            flag = 1;
        }
    }
}

main()
{
    TRISB  = 0b11000000;
    CMCON  = 0b00000111;    // disable comparator on ra0,1,2
    TRISA  = 0b11111111;


    //******************************************************
    // The following lines set the T2CON control register

    TOUTPS3 = 1;  // these are the 6,5,4,3 bits of the
    TOUTPS2 = 1;  // control register T2CON and set
    TOUTPS1 = 1;  // the postscale value
    TOUTPS0 = 1;  //

    TMR2ON  = 1;  // this enable the Timer2

    T2CKPS1 = 1;  // these are the 1 and 0 bits of the
    T2CKPS0 = 1;  // control register T2CON and set the
              // prescale value

    // Is also possible set the bits of T2CON control register
    // with a unique comand like the following:
    //
    // T2CON  = 0x7E;   
    //
    // here the hexadecimal value 7E means
    // 76543210
    //  1111110
    //  1111    -> postscale value 1:16
    //      1   -> enable timer2
    //       1x -> prescaler value 1:16
    //******************************************************

    TMR2IE = 1;    // TMR2 to PR2 enable match interrupt
    // PR2 = 0xFF; default value

    INTCON = 0;  // reset all the flag bits for all the interrupts
    PEIE = 1;    // peripheral interrupt enable bit
    GIE  = 1;    // global interrupt enable bit (1: enable all)

    while(1)
        continue;    // while forewer
}




THE CODE PART 6 - COMBINE SEQUENTIAL LED LOOP with INTERRUPT


Here no explanation: code is code.



THE CHASSIE


   

(click on image to enlarge)

The Tamiya 70097 Twin-Motor Gearbox is a small (3in long) plastic gearbox. It contains two small DC motors that drive separate 3mm hexagonal output shafts. There are two ways to put the kit together: with a high-speed 58:1 gear ratio or with a slower 203:1 gear ratio. Either way, the motors provide plenty of power to drive any small robot.

This motor unit togheter with the Tamiya 70144 Ball Caster and with the circular laser-cut plastic robot chassis produced by Pololu makes a great base for small mobile robot.



(click on image to enlarge)

With a diameter of only five inches (slightly bigger than a CD), this acrylic chassis is ideal for building sturdy robots capable of navigating tight spaces. As shown below, the circular chassis has many holes and slots for mounting sensors, motors, and other hardware.

And now start the game ...



You can assemble the gearboxes to have a 58:1 or a 204:1 gear ratio but only the 204:1 (C) is valid for the integration with the Tamiya 70144 Ball Caster .







THE MOTOR CONTROLLER

If we want a speed regulator for the motors become necessary use a motor controller.
The Pololu Micro Dual Serial Motor Controller can individually control two motors and set each motor to go forward or backward at any of 127 speeds.




For complete specifications and information see the user's guide

The connection with the microcontroller and with the two motors are very simple.

Module Pinout:
PIN    FUNCTION
1    motor supply (1.8-9.0 V)
2    ground (0 V)
3    logic supply (2.5-5.5V)
4    serial control input
5    reset
6    motor 1, positive output
7    motor 1, negative output
8    motor 0, negative output
9    motor 0, positive output
 

 
In this following test I have connected both pin 1 and 3 of the motor controller to the power supply pack of the motors 3 x AA in series.



I have used for this porpuse the Densi-Pak battery holders from RS Components (code 410-1444),.click here the size.

The reset pin (5) has been connected to the pin 9 (RB3) of the PIC 16F627 and the input pin (4) to the pin 8 (RB2/TX) of the PIC.


From the datasheet of the PIC:



Follows the simple code to drive the two motors with this Micro Dual Serial Motor Controller.

// please add here the heater of code like the previous examples

// Here I define some constant to create an easy to understand call to the motor() function.

#define MOTOR1    (1)
#define MOTOR2    (2)
#define FOWARD    (1)
#define REVERSE   (2)
#define HALFSPEED (127/2)
#define FULLSPEED (127)


void motor(int motor, int direction, int speed)
{
    int bits = 0;

    while(TXIF == 0);           // loop while the USART transmit buffer is full

    TXREG = 0x80;                                // first byte always 0x80 from the Pololu datasheet

    while(TXIF == 0);

    TXREG = 0x00;               // second byte always 0x00 from the Pololu datasheet

    while(TXIF == 0);

    if (motor == MOTOR2)        // set to 1 the second bit
        bits += 2;

    if (direction == FOWARD)    // set to 1 the first bit
        bits += 1;

    TXREG = bits;

    while(TXIF == 0);

    TXREG = speed;
}

void main()
{
    TRISB  = 0b11000000;
    CMCON  = 0b00000111;    // disable comparator on ra0,1,2
    TRISA  = 0b11111111;
   

   
    //*****************************************************************
    // Following the datasheet in the section of
    // UNIVERSAL SYNCHRONOUS/ASYNCHRONOUS RECEIVER/TRANSMITTER (USART)
    //
    // these lines set the TXSTA register
    //
    CSRC = 0;    // in async. mode don't used
    TX9  = 0;    // 0: set 8 bit transmission
    TXEN = 1;    // 1: transmit enable
    SYNC = 0;    // 0: asynchronous mode
    BRGH = 1;    // 1: async. high speed
    TRMT = 0;    // 0: transmit shift register full
    TX9D = 0;    // 9th bit of transmit data
    //
    // Is also possible set all the bits of TXSTA control register
    // with a unique command like the following:
    // TXSTA = 0b00100100;

    //*****************************************************************
    // these lines set the RCSTA register
    //
    SPEN = 1;    // 1: serial port enable
    RX9  = 0;    // 0: set 8 bit reception
    SREN = 0;    // in async. mode don't used
    CREN = 1;    // 1: enable continuous receive
    ADEN = 0;    // in 8 bit mode don't used
    FERR = 0;    // framing error bit ?
    OERR = 0;    // 0: no overrum error
    RX9D = 0;    // 9th bit of received data
    //
    // Is also possible set all the bits of RCSTA control register
    // with a unique command like the following:
    // RCSTA = 0b10010000;

    //*****************************************************************
    // these lines set the UART Baud Rate Generator
    //
    SPBRG = 51;
    //
    // with the calculation formula is not very easy to find this value
    // but the tables in the datasheet show the decimal value to set for
    // many baud rate and many clock frequency
    // If we use want a baud rate of 19200 (19.2 K) and the cpu quartz is
    // 4 MHz the value is 51.


    // Now reset the Motor controller with these following command because I have
    // connected the reset pin of the controller to the RB3 pin
    RB3 = 0;
    DelayMs(200);
    RB3 = 1;
   
DelayMs(200);

    // This function will be usefull to control the 2 motors and now it is used to
    // start the two motors at half speed
    motor(MOTOR1, FOWARD, HALFSPEED);
    motor(MOTOR2, FOWARD, HALFSPEED);

    // switch-on the 2 led
    RB4 = 1;
    RB5 = 1;

    while(1)
    {
        // Will be easy to control the motors with the four buttons

        if (ra0 == 1)
        {
            motor(MOTOR1, FOWARD, 0);
            RB4 = 0;
        }
        if (ra1 == 1)
        {
            motor(MOTOR1, FOWARD, HALFSPEED);
            RB4 = 1;
        }
        if (ra2 == 1)
        {
            motor(MOTOR2, FOWARD, 0);
            RB5 = 0;
        }
        if (ra3 == 1)
        {
            motor(MOTOR2, FOWARD, HALFSPEED);
            RB5 = 1;
        }
    }
   
}


The following code will insert the foward/reserve test of the two motors.

// add the code of motor() function

void main()
{
    int motor1, motor2;

    TRISB  = 0b11000000;
    CMCON  = 0b00000111;    // disable comparator on ra0,1,2
    TRISA  = 0b11111111;

   
    TXSTA = 0b00100100;
    RCSTA = 0b10010000;
    SPBRG = 51;

    // Now reset the Motor controller with these following command because I have
    // connected the reset pin of the controller to the RB3 pin
    RB3 = 0;
    DelayMs(200);
    RB3 = 1;
    DelayMs(200);

    // This function will be usefull to control the 2 motors
    motor(MOTOR1, FOWARD, HALFSPEED);
    motor(MOTOR2, FOWARD, HALFSPEED);

    // Just to save the status
    motor1 = FOWARD;
    motor2 = FOWARD;

    // switch-on the 2 led
    RB4 = 1;
    RB5 = 1;

    while(1)
    {
        // Will be easy to control the motors with the four buttons

        if (ra0 == 1)
        {
            motor(MOTOR1, FOWARD, 0);
            RB4 = 0;
        }
        if (ra1 == 1)
        {
            if (motor1 == FOWARD)
            {
                motor(MOTOR1, REVERSE, HALFSPEED);
                motor1 = REVERSE;
            }
            else
            {
                motor(MOTOR1, FOWARD, HALFSPEED);
                motor1 = FOWARD;
            }
            RB4 = 1;
        }
        if (ra2 == 1)
        {
            motor(MOTOR2, FOWARD, 0);
            RB5 = 0;
        }
        if (ra3 == 1)
        {
            if (motor2 == FOWARD)
            {
                motor(MOTOR2, REVERSE, HALFSPEED);
                motor2 = REVERSE;
            }
            else
            {
                motor(MOTOR2, FOWARD, HALFSPEED);
                motor2 = FOWARD;
            }
            RB5 = 1;
        }
        DelayMs(200);
    }
   
}





THE SENSORS

Excluding normal switches the first choice are the infrared distance measuring sensors.



The give a wide range of  products with different characteristics:
Ofcourse the GP2D15 is the most simple to use and it is possible to set the detection range at some value between 10cm and 24cm by making a simple modification to the sensor package (see http://www.acroname.com/robotics/info/ideas/adjust15/adjust15.html).