Sign In

Published by on


/******************************************************************************

A smart walking stick made for visually impaired persons. Three ultrasonic
range finder (HC-SR04) are used to sense any obstacles that are in front,
left and right of the person. When obstacles are sensed, an audio warning
is given.

Written By
 Avinash Gupta
Contact
 gmail@avinashgupta.com

For more interesting microcontroller tutorials and projects. Please visit
http://www.extremeelectronics.co.in

NOTICE:
PROGRAM SAMPLE PROVIDED FOR SELF LEARNING PURPOSE ONLY!
NO PART OF THIS WORK SHOULD BE USED IN ANY COMMERCIAL PROJECTS OR IN ANY
TEACHING INSTITUTES FOR TEACHING THEIR STUDENTS
NO PART OF THIS WORK SHOULD BE PUBLISHED IN ANY FORM LIKE PRINTED OR ELECTRONIC
MEDIA

COPYRIGHT (C) 2008-2015 EXTREME ELECTRONICS, INDIA
******************************************************************************/
#include <xc.h>
#include <stdint.h>


// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF       // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = ON         // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3/PGM pin has PGM function; low-voltage programming enabled)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

/********************************************************************

Configuration Area.
UltraSonic (US) sensor connection.

in this example it is connected to as follows

Sensor | MCU
_____________
Trig   | PC0
Echo   | PC1

********************************************************************/

#define US_PORT PORTA
#define  US_TRIS  TRISA


#define US_TRIG_POS  0


#define US_ERROR     -1
#define  US_NO_OBSTACLE          -2

#define THRESHOLD_DISTANCE  60  //CM



void HCSR04Init()
{
        //Set trigger port as output
   US_TRIS&=~(1<<US_TRIG_POS);

        //Setup Timer1
        T1CKPS0=1;  //Prescaller = 1:2
}

void HCSR04Trigger()
{
   //Send a 10uS pulse on trigger line
   US_PORT|=(1<<US_TRIG_POS); //high

        __delay_us(15);       //wait 15uS

   US_PORT&=~(1<<US_TRIG_POS);   //low
}
/*
 Returns the width of pulse in uS (micro seconds)
 */
int16_t GetPulseWidth(uint8_t n)
{
   uint32_t i,result;

        n++;//since echo lines starts at PORTA1 so convert 0 to 1 and so on

   //Wait for the rising edge
   for(i=0;i<600000;i++)
   {
      if(!(US_PORT & (1<<n)))
                    continue; //Line is still low, so wait
      else
                    break; //High edge detected, so break.
   }

   if(i==600000)
            return US_ERROR;  //Indicates time out

   //High Edge Found


   TMR1=0x00;  //Init counter
        TMR1ON=1;   //Start timer

        //Now wait for the falling edge
        for(i=0;i<600000;i++)
        {
           if(US_PORT & (1<<n))
           {
              if(TMR1 > 60000) break; else continue;
           }
           else
                         break;
        }



   if(i==600000)
            return US_NO_OBSTACLE;  //Indicates time out

        //Falling edge found
   //Stop Timer
   TMR1ON=0;

   result=TMR1;

   if(result > 60000)
            return US_NO_OBSTACLE;  //No obstacle
   else
            return (result * 0.4);  // since period of timer is 0.4uS
}

void Alert(uint8_t n)
{
    for(uint8_t i=0;i<n;i++)
    {
        RB1=1;//Start Vibrator

        __delay_ms(50);

        RB1=0;//Stop Vibrator
        __delay_ms(30);
    }
}

void main (void)
{
    int16_t r;

    //All PORTA pins are digital input and NOT analog
    PCFG2=1;
    PCFG1=1;

    //Vibrator motor or buzzer i/o port pin as output
    TRISBbits.TRISB1=0;
    RB1=0;//No alert at startup

    HCSR04Init();

    int d[3];

    while(1)
    {


        //loop for 3 sensors
        for(uint8_t i=0;i<3;i++)
        {
                //wait for all ECHO line to come down
                while(RA1==1);
                while(RA2==1);
                while(RA3==1);

      //Send a trigger pulse
      HCSR04Trigger();

      //Measure the width of pulse
      r=GetPulseWidth(i);


                if(r!=US_ERROR && r!=US_NO_OBSTACLE)
      {
         d[i]=(r*0.01718); //Convert to cm
      }
            }//For loop of 3 sensor

            //Alert
            if(d[1]<THRESHOLD_DISTANCE)
            {
                Alert(2);
            }
            else
            {
                if(d[2]<(THRESHOLD_DISTANCE-30))
                    Alert(3);
                else if (d[0]<(THRESHOLD_DISTANCE-30))
                    Alert(1);
            }
        __delay_ms(250);
    }//main while loop

}

The most important and complex part of this program is the part which measures the distance using the HC-SR04 sensor. Reading the sensor involves two important steps.

  • Triggering the sensor.
  • Measuring the width of pulse on the ECHO line.

From the circuit diagram you can see that we have connected the TRIG lines of all the sensor together. And this TRIG line is connected to bit 0 of PORTA. We have written a function HCSR04Trigger() to trigger the sensor. The implementation of this function is very simple. We just pull the line high by writing 1 to it, then wait for 15uS using the built in delay function __delay_us() then we pull the line low by writing low to it.

We have not directly manipulated the port pin, because this hinders portability of the code. Instead we have defined constants using the C's #define directive. This also enhances the readability of the code. US_PORT (short for Ultrasonic port) conveys much more information to the reader than PORTA. Also in future due to any reason we can to connect the sensor to PORTC or PORTD we can simply change this at one location.


void HCSR04Trigger()
{
   //Send a 10uS pulse on trigger line
   US_PORT|=(1<<US_TRIG_POS); //high

        __delay_us(15);       //wait 15uS

   US_PORT&=~(1<<US_TRIG_POS);   //low
}

Above is the complete listing of the function, in the function two constants are defined. US_PORT and US_TRIG_POS. US_PORT stands for the port name in which the sensor is connected, its value is PORTA. While US_TRIG_POS stands for the position of trigger pin on the port. Since we have connected the trigger line to bit 0, its value is 0. Thus these two variable completely describes the connection pin and makes it easy to change the connection. For example if you wish to connect the trigger signal to PORTA5 instead of PORTA0 you can simply change value of US_TRIG_POS to 5.

Switching on and off of the bits is done by the bitwise OR | and AND & operator. For understanding of it one should be good at C and the bitwise operators.

Once the trigger pulse is sent to the sensor, the MCU waits for a high edge on the ECHO line. This is done inside the function GetPulseWidth(). Since we have three different sensors (each for left, right and front obstacle sensing) this function accepts and argument which should be 0, 1 or 2 depending on which sensor you wish to read.

Variable name for this argument is n.

At the start of this function we define two local variables :-

Variable Use
i for looping variable
result storing final result of measurement of pulse width

Then we wait for the rising edge on the ECHO line of the selected sensor. Some people use while loop in such situation, but it has a great disadvantage! Suppose if the sensor wire is broken and we never get a high edge on the echo line (because the high edge is generated by the sensor, if no sensor that means no high edge!). In that case we will be waiting forever, from the user's ends it will appear the the program has hung!

So instead of waiting forever, we wait for a limited number of tries.

Below is the complete listing of the part which waits for a high edge.

//Wait for the rising edge
   for(i=0;i<600000;i++)
   {
      if(!(US_PORT & (1<<n)))
                    continue; //Line is still low, so wait
      else
                    break; //High edge detected, so break.
   }

   if(i==600000)
            return US_ERROR;  //Indicates time out

   //High Edge Found

At line two we have created a for loop, are we are looping 6,00,000 times, each time checking the status of bit represented by n on the US_PORT. If n is 1 value of bit number 1 is tested using simple bitwise & operator of the C language. If the bit is found to be low we continue the loop. If it is found high, then we break the loop. So their is two ways to reach line # 10. Either we have just broken the loop due to a high edge detection or the for loop has counted full 6,00,000 iterations. In the second case the value of i would be 6,00,000 so we check it at line #10. If it happens to be so, that means whole 6,00,000 iterations has been done without getting a high edge on the MCUs i/o line, it is a clear indication that the sensor has some problem. Either the power line of sensor is disconnected or the ECHO line from the sensor to MCU is broken or the sensor has developed any kind of fault. So in this case we end the function here and return an error. Error values are returned in form of negative integer numbers. These constants are also defined using C's #define directive.

As soon as we get a high edge on the ECHO line we start the TIMER1 for measuring the width of this pulse. This is done by first writing 0 to the counter register (TMR1) and then starting the counter by writing 1 to the TMR1ON bit. This is analogous to pressing the start button of a stopwatch as soon as players start to run in a running competition.

Timer1 is configured with a prescaler of 1:2, that means its counting frequency is half the CPU frequency. And CPU frequency is 5MHz (as we are using a crystal oscillator or 20MHz) So the frequency of the timer is 2.5MHz. That means its period is 0.4 microseconds.

We then wait for the falling edge of the pulse. This is simple, check the i/o line connected to the ECHO signal output from the sensor, if it is high then continue else if it low then break. As soon as we break we stop the timer by writing 0 to the TMR1ON bit. And we copy the counter register TMR1 to the result variable. And we return the value of result variable multiplied by 0.4, as we discussed above that the period of timer is 0.4uS.

Now this function is returning the length of the pulse in microsecond (uS). Its time to convert this to cm. For this we have to refresh our knowledge of Kinematics and recall the relationship of speed, time and distance.

We know that speed = distance traveled / time required to travel the distance.

So

distance = speed x time (simple maths!) Eq. 1

And since actual distance to obstacle is only half of the distance traveled by sound

Distance to obstacle = (speed x time)/2 Eq. 2

Speed of sound in air is

34359cm/sec = 0.034359cm/uS

substituting this value in equation 2 we get

Distance to obstacle = (0.034359 x time) /2

or

Distance to obstacle = (0.01718 x time)

So whenever we require the distance, we multiply time as returned by GetPulseWidth() function by 0.01718

We cordially thanks the following peoples who shared this page on various social networks and insprided us to develop more quality contents!

Jack Martin, Akshay , Midhun, Bbb, MKAK, Akshay Vishwakarma , Matiyas, Arun, Jamshan, Usman, Pronay Roy, Harshit, Lucia Salas, Sachin Singhal , Avinash Ie Home, Avinash Gupta,

Comments