Project 2 - Measuring distance of an object using ultrasonic sensor (HC-SR04)
LIVE BATCH (PREETI)
Aim: To implement a distance measuring system with atmega32 and ultrasonic sensor HC_SR04 with the use of bare metal programming.
In this project the object detection alongwith its distance measurement obtained is printed on the 16*2 LCD display. A potentiometer is used here to introduce a variable resistance which results in change in the distance measured on the LCD display.
Key Highlights of the Project:
- Measuring the distance travelled by the wave to the object and gets bounced back from the object to sensor.
- Measuring Pulse width using Input Capture Mode
-
Execution of Project
Ultrasonic Sensor :

HC-SR04 Ultrasonic Sensor - Working
As shown above the HC-SR04 Ultrasonic (US) sensor is a 4 pin module, whose pin names are Vcc, Trigger, Echo and Ground respectively. This sensor is a very popular sensor used in many applications where measuring distance or sensing objects are required. The module has two eyes like projects in the front which forms the Ultrasonic transmitter and Receiver. The sensor works with the simple high school formula that
Distance = Speed × Time
The Ultrasonic transmitter transmits an ultrasonic wave, this wave travels in air and when it gets objected by any material it gets reflected back toward the sensor this reflected .
Input capture function modes inATmega328
The input capture function is used in many applications such as:
- Pulse width measurement
- Period measurement
- Capturing the time of an event
In AVR ATmega32, Timer1 can be used as an input capture to detect and measure events happening outside the microcontroller.
Upon detection of a defined event i.e. rising edge or falling edge on ICP pin (PORTD.6), the TCNT1(Timer / Counter register) value is loaded into the ICR1 (input capture) register and the ICF1 flag will get set.

Programming
To program, first, let us see TCCR1B (Timer Counter Control Register B)
TCCR1B: Timer Counter Control Register B

Bit 7 - ICNC1: Input Capture Noise canceller
Setting this bit activates the noise canceller. It causes a delay of 4 clock cycles as it considers a change only if it persists for at least 4 successive system clocks.
Bit 6 - ICES1: Input Capture Edge select
Select edge detection for input capture function.
0 = Capture on the falling edge
1 = Capture on rising edge
Bit 4: 3 - WGM13 : WGM12: Timer1 Mode select
These bits are used for mode selection like Normal mode, PWM mode, CTC mode, etc. here we will select normal mode, so set these bits to zero.
Bit 2: 0 - CS12: CS10:Timer1 Clock Select
CS02
CS01
CS00
Description
0
0
0
No clock source (Timer / Counter stopped)
0
0
1
clk (no pre-scaling)
0
1
0
clk / 8
0
1
1
clk / 64
1
0
0
clk / 256
1
0
1
clk / 1024
1
1
0
External clock source on T0 pin. Clock on falling edge
1
1
1
External clock source on T0 pin. Clock on rising edge.
Steps tp Program :
- Initialize the TCCR1A and TCCR1B for proper timer mode (any mode other than 8, 10, 12, 14), to select the edge (Positive or Negative).
- Monitor the ICF1 flag in the TIFR register to see if the edge has arrived. Upon the arrival of the edge, the TCNT1 value is loaded into the ICR1 register automatically by the controller.

-
CODE:
#include <avr/io.h>//PREPROCESSOR FOR AVR I/O
#define F_CPU 16000000L//FREQUENCY CLOCK OF CPU
#include <util/delay.h>//TIME DELAY
#define rs_high PORTD |=(1<<0);//DEFINE BIT FOR RS_HIGH
#define rs_low PORTD &=~(1<<0);//DEFINE BIT FOR RS_LOW
#define rw_high PORTD |= (1<<1);//DEFINE BIT FOR EN_HIGH
#define rw_low PORTD &= ~(1<<1);//DEFINE BIT FOR RS_LOW
#define en_high PORTD |= (1<<2);//DEFINE BIT FOR EN_HIGH
#define en_low PORTD &=~(1<<2);//DEFINE BIT FOR EN_LOW
#define US_ERROR -1 //ULTRASONIC SENSOR IS WORKING/NOT
#define US_NO_OBSTACLE -2
#define US_PORT PORTC //ULTRASONIC SENSOR PORT C
#define US_PIN PINC // we need to initialize the pin resistor when we want to take input.
#define US_DDR DDRC // we need data-direction-resistor (DDR) to set the direction of data flow. input or output. we will define it later, now we're just naming it.
#define US_TRIG_POS PC0 // the trigger pin of ultrasonic sound sensor is connected to port C pin 0
#define US_ECHO_POS PC1
int distance, previous_distance;void lcd_cmd(unsigned char cmd)//ASSIGNING LCD COMMAND
{
PORTB = cmd;
rs_low;
rw_high;
en_high;
_delay_ms(1);
en_low;
}
void lcd_data(unsigned char data)//ASSIGNING LCD DATA
{
PORTB = data;
rw_low;
rs_high;
en_high;
_delay_ms(1);
en_low;
}
void lcd_init()//LCD INITILIAZATION
{
rw_high;
rs_high;
en_high;
PORTB = 0xff;
lcd_cmd(0x38);
_delay_ms(5);
lcd_cmd(0x02);
_delay_ms(5);
lcd_cmd(0x01);
_delay_ms(5);
lcd_cmd(0x0c);
_delay_ms(5);
lcd_cmd(0x06);
_delay_ms(5);
lcd_cmd(0x80);
_delay_ms(5);
}
void cursorpos(char x,char y)//CURSOR POSITION
{
if((x<1 || x>2)||(y<1 || y>16))//FOR CHECKING ROWS AND COLUMNS
{
x=1;//X AS ROW
y=1;//Y AS COL
}
if (x==1)
{
lcd_cmd(0x7F+y);
}
else//X==2
{
lcd_cmd(0xBF+y);
}
}
void lcd_out(char a, char b,char *str)//CHECKING THE WHICH POSITION TO PRINT
{
cursorpos(a,b);
while(*str)
{
lcd_data(*str);
str++;
}
}
void lcd_clear()//CLEARING LCD DISPLAY
{
lcd_cmd (0x01); //LCD CLEAR THE DISPLAY
_delay_ms(2); //WAIT FOR DELAY
lcd_cmd (0x80); //LCD ROWS & COL
}
void HCSR04Init()//INITIALIZING ULTRASONIC SENSOR
{
US_DDR|=(1<<US_TRIG_POS);//ULTRASONIC SOUND WAVE GENERATION
}
void HCSR04Trigger()//TRIGGERING THE SENSOR
{
US_PORT|=(1<<US_TRIG_POS); //HIGH
_delay_us(15); //DELAY 15uS
US_PORT&=~(1<<US_TRIG_POS); //LOW
}
uint16_t GetPulseWidth()
{
uint32_t i,result;//DECLARING UNSIGN INT AS I,RESULT
for(i=0;i<600000;i++)
{
if(!(US_PIN & (1<<US_ECHO_POS)))
continue;
else
break;
}
if(i==600000)
return US_ERROR;
TCCR1A=0X00;
TCCR1B=(1<<CS11);//THIS LINE SETS THE RESOLUTION OF THE TIMER. MAXIMUM OF HOW MUCH VALUE IT SHOULD COUNT.
TCNT1=0x00; //THIS LINE START THE COUNTER TO START COUNTING TIME
for(i=0;i<600000;i++) //THE 600000 VALUE IS USED RANDOMLY TO DENOTE A VERY SMALL AMOUNT OF TIME, ALMOST 40 MILISECONDS
{
if(US_PIN & (1<<US_ECHO_POS))//IF ULTRASONIC SENSOR PIN & ECHO VALUE WILL 1
{
if(TCNT1 > 60000) // IF THE TCNT1 VALUE GETS HIGHER THAN 60000 IT MEANS THERE IS NOT OBJECT IN THE RANGE OF THE SENSOR
break;
else
continue;
}
else
break;
}
if(i==600000)
return US_NO_OBSTACLE;
result=TCNT1; //MICROCONTROLLER STORES THE THE VALUE OF THE COUNTED PULSE TIME IN THE TCNT1 REGISTER. SO, WE'RE RETURNING THIS VALUE TO THE MAIN FUNCTION FOR UTILIZING IT LATER
TCCR1B=0x00;
if(result > 60000)
return US_NO_OBSTACLE;
else
return (result>>1);
}
int main()
{
lcd_init(); //INITIALIZE LCD
char numberString[4]; // DEFINE CHARACTER ARRAY
while(1) {
uint16_t r;
_delay_ms(100);
HCSR04Init();//INITALIZE THE SENSOR USING CALLING FUNCTION
while(1)
{
HCSR04Trigger(); //TO TRIGGER THE ULTRASONIC SENSOR USING CALLING FUNCTION
r=GetPulseWidth(); //GETTING THE DURATION OF THE ULTRASOUND TOOK TO ECHO BACK AFTER HITTING THE OBJECT
if(r==US_ERROR) //IF MICROCONTROLLER DOESN'T GET ANY PULSE THEN IT WILL SET THE US_ERROR VARIABLE TO -1
{
cursorpos(1, 1); //LCD COL & ROW
char j[10] = "Error!";
lcd_out(1,1,&j);
}
else
{
distance=(r*0.034/2.0); //DISTANCE FORMULA
char f[15]= "Distance =";//DISPLAY THE DISTANCE IN LCD
lcd_out(1,0,&f);
itoa(distance, numberString, 10); // DISTANCE IS AN INTEGER NUMBER, WE CAN NOT DISPLAY INTEGER DIRECTLY ON THE LCD. THIS LINE CONVERTS INTEGER INTO ARRAY OF CHARACTER
lcd_out(1,11,numberString);//DIPLAY THE MEASURE VALUE IN LCD
char h[6] = " cm";
lcd_out(1,14,&h);//DISPLAY CM IN LCD
previous_distance = distance;
_delay_ms(30);
}
}
}
}