Project 2 - Measuring distance of an object using ultrasonic sensor (HC-SR04)
Project 2 - Measuring distance of an object using ultrasonic sensor (HC-SR04)
Aim:
In this project, the implementation of code for “Measuring distance of an object using ultrasonic distance sensor (HC-SR04) using Atmega32

Certainly! In this project, an AVR microcontroller is employed to measure the distance of an object using an HC-SR04 ultrasonic sensor and display the result on a 16x2 LCD screen. The AVR microcontroller is programmed in C and utilizes a set of functions to control the LCD display, initialize hardware components, and manage the timing for distance measurement. The core functionality revolves around the ultrasonic sensor. When the program begins, it initializes the LCD display and sets up the necessary hardware configurations. It also enables global interrupts and specifically sets up an interrupt service routine for Timer1 overflow. Timer1 is crucial for accurately measuring the time it takes for an ultrasonic pulse to travel to an object and return. Within the main loop, the microcontroller periodically sends a 10-microsecond trigger pulse to the HC-SR04 sensor. This pulse initiates the emission of an ultrasonic wave. The program then uses Timer1 and input capture (ICP) to precisely measure the time it takes for the echo signal to return. This timing information is crucial for distance calculation.
The distance is calculated based on the measured time and the speed of sound. It is then converted into a string format and displayed on the LCD screen. The formula used for distance calculation is distance = (count / 466.47), where "count" represents the time measured by Timer1. The constant 466.47 is empirically determined and takes into account the speed of sound and the AVR's clock frequency (8MHz). The LCD display is updated with the newly calculated distance every 200 milliseconds, providing real-time feedback on the object's distance from the sensor. This project demonstrates a practical application of ultrasonic distance measurement and serves as a foundation for various applications like robotics, automation, and obstacle detection systems.
ATmega32 – 8 Bit AVR Microcontroller
The AVR microcontrollers are based on the advanced RISC architecture. ATmega32 is a low power CMOS 8-bit microcontroller based on the AVR enhanced RISC architecture. AVR can execute 1 million instructions per second if cycle frequency is 1MHz.
Key Features:
- Features
• High-performance, Low-power AVR® 8-bit Microcontroller
• Advanced RISC Architecture
– 131 Powerful Instructions – Most Single-clock Cycle Execution
– 32 x 8 General Purpose Working Registers
– Fully Static Operation
– Up to 16 MIPS Throughput at 16 MHz
– On-chip 2-cycle Multiplier
• Nonvolatile Program and Data Memories
– 32K Bytes of In-System Self-Programmable Flash
Endurance: 10,000 Write/Erase Cycles
– Optional Boot Code Section with Independent Lock Bits
In-System Programming by On-chip Boot Program
True Read-While-Write Operation
– 1024 Bytes EEPROM
Endurance: 100,000 Write/Erase Cycles
– 2K Byte Internal SRAM
– Programming Lock for Software Security
• JTAG (IEEE std. 1149.1 Compliant) Interface
– Boundary-scan Capabilities According to the JTAG Standard
– Extensive On-chip Debug Support
– Programming of Flash, EEPROM, Fuses, and Lock Bits through the JTAG Interface
• Peripheral Features
– Two 8-bit Timer/Counters with Separate Prescalers and Compare Modes
– One 16-bit Timer/Counter with Separate Prescaler, Compare Mode, and Capture
Mode
– Real Time Counter with Separate Oscillator
– Four PWM Channels
– 8-channel, 10-bit ADC
8 Single-ended Channels
7 Differential Channels in TQFP Package Only
2 Differential Channels with Programmable Gain at 1x, 10x, or 200x
– Byte-oriented Two-wire Serial Interface
– Programmable Serial USART
– Master/Slave SPI Serial Interface
– Programmable Watchdog Timer with Separate On-chip Oscillator
– On-chip Analog Comparator
• Special Microcontroller Features
– Power-on Reset and Programmable Brown-out Detection
– Internal Calibrated RC Oscillator
– External and Internal Interrupt Sources
– Six Sleep Modes: Idle, ADC Noise Reduction, Power-save, Power-down, Standby
and Extended Standby
• I/O and Packages
– 32 Programmable I/O Lines
– 40-pin PDIP, 44-lead TQFP, and 44-pad MLF
• Operating Voltages
– 2.7 - 5.5V for ATmega32L
– 4.5 - 5.5V for ATmega32
• Speed Grades
– 0 - 8 MHz for ATmega32L
– 0 - 16 MHz for ATmega32
• Power Consumption at 1 MHz, 3V, 25°C for ATmega32L
– Active: 1.1 mA
– Idle Mode: 0.35 mA
– Power-down Mode: < 1>
Overview:
The ATmega32 is a low-power CMOS 8-bit microcontroller based on the AVR enhanced RISC architecture. By executing powerful instructions in a single clock cycle, the ATmega32 achieves throughputs approaching 1 MIPS per MHz allowing the system designer to optimize power consumption versus processing speed.
Special Microcontroller Features:
- Six sleep modes: Idle, ADC noise reduction, power-save, power-down, standby and extended standby.
- Internal calibrated RC oscillator
- External and internal interrupt sources
- Power on reset and programmable brown-out detection.
All the 32 registers are directly connected to the Arithmetic Logic Unit (ALU), allowing two independent registers to be accessed in one single instruction executed in one clock cycle.
The power-down saves the register contents but freezes the oscillator. All other chip functions will be disabled until the next external interrupt arises. Asynchronous timer allows the user to maintain a timer based in power-save mode while the rest of the device is sleeping.
ADC noise reduction mode stops the CPU and all I/O modules except ADC and asynchronous timer. In standby mode, except crystal oscillator the rest of the device is sleeping. Both the main oscillator and asynchronous timer continue to run in extended standby mode.
ATmega32 is a powerful microcontroller because of its in system self-programmable flash on a monolithic chip, provides a high flexible and cost effective solution to many embedded control applications.
Pin Descriptions:
VCC: Digital voltage supply
GND: Ground
Port A (PA7-PA0): This port serves as analog inputs to the A/D converter. It also serves as an 8-bit bidirectional I/O port if the A/D converter is not used.
Port B (PB7-PB0) & Port D (PD7-PD0): It is an 8-bit bidirectional I/O port. Its output buffers have symmetrical drive characteristics with both high sink and source capability. As inputs, these are extremely pulled low if the pull-up resistors are activated. It also serves various special function features of the ATmega32.
Port C (PC7-PC0): It is an 8-bit bidirectional I/O port. If the JTAG interface is enabled, the pull-up resistors on pins PC5 (TDI), PC3 (TMS), and PC2 (TCK) will be activated.
Reset: It is an input.
XTAL1: It is an input to the inverting oscillator amplifier and input to the internal clock operating circuit.
XTAL2: It is an output from the inverting oscillator amplifier.
AVCC: It is the supply voltage pin for Port A and A/D converter. It should be connected to VCC.
AREF: AREF is the analog reference pin for the A/D converter.
ATmega32 Memories:
It is having two main memory spaces data memory and the program memory space. In addition it features an EEPROM memory for data storage.
In System Programmable Flash Program Memory:
ATmega32 contains 32Kbytes on-chip in system reprogrammable flash memory for program storage. Flash is organized as 16k X 16 and its memory is divided into two sections Boot program section and application program section.
SRAM Data Memory:
The Register file, I/O memory, and the internal data SRAM are addressed by the lower 2144 data memory locations. The first 96 locations address the Register file and I/O memory, and the internal data SRAM is addressed by the next 2048 locations. Direct, indirect with displacement, indirect, indirect with pre-decrement, and in-direct with post decrement are the 5 different addressing modes for the data memory covering. The 32 general purpose registers, 64 I/O registers, and 2048 bytes of internal data SRAM are accessible by using these addressing modes.
EEPROM Data Memory:
It contains 1024 bytes of data EEPROM memory. It can be accessed as a separate data space in which single bytes can be read and written.
I/O Memory:
All I/Os and peripherals are placed in the I/O space. The I/O locations are accessed by the IN and OUT instructions, transferring the data between the 32 general purpose registers and the I/O space. I/O registers with in the address 00-1F are directly bit accessible using the SBI and CBI instructions.
HD44780 LCD
The Hitachi HD44780 LCD controller is an alphanumeric dot matrix liquid crystal display (LCD) controller developed by Hitachi in the 1980s. The character set of the controller includes ASCII characters, Japanese Kana characters, and some symbols in two 40 character lines. Using an extension driver, the device can display up to 80 characters.[1] Numerous third-party displays are compatible with its 16-pin interface and instruction set, making it a popular and cheap LCD driver.
The Hitachi HD44780 LCD controller is limited to monochrome text displays and is often used in copiers, fax machines, laser printers, industrial test equipment, and networking equipment, such as routers and storage devices.
Compatible LCD screens are manufactured in several standard configurations. Common sizes are one row of eight characters (8×1), and 16×2, 20×2 and 20×4 formats. Larger custom sizes are made with 32, 40 and 80 characters and with 1, 2, 4 or 8 lines. The most commonly manufactured larger configuration is 40×4 characters, which requires two individually addressable HD44780 controllers with expansion chips as a single HD44780 chip can only address up to 80 characters.
Character LCDs may have a backlight, which may be LED, fluorescent, or electroluminescent. The nominal operating voltage for LED backlights is 5V at full brightness, with dimming at lower voltages dependent on the details such as LED color. Non-LED backlights often require higher voltages.
Interface
Dragon12-P Freescale HCS12/9S12 microcontroller project development board utilizing an HD44780-based LC display
Character LCDs use a 16-contact interface, commonly using pins or card edge connections on 0.1 inch (2.54 mm) centers. Those without backlights may have only 14 pins, omitting the two pins powering the light. This interface was designed to be easily hooked up to the Intel MCS-51 XRAM interface, using only two address pins, which allowed displaying text on LCD using simple MOVX commands, offering a cost effective option for adding text display to devices.[citation needed]
The predominant pinout is as follows (exceptions exist):
|
Pin# |
Name |
Direction |
Description |
|
1 |
Vss |
Power |
Power Supply Ground |
|
2 |
Vcc |
Power |
Power Supply (+3.3 or +5 V depending on module) |
|
3 |
Vee |
Input |
Contrast Adjustment (analog input) |
|
4 |
RS |
Input |
Register Select (0 = command, 1 = data) |
|
5 |
R/W |
Input |
Read/Write (0 = write to display module, 1 = read from display module) |
|
6 |
E |
Input |
Clock Enable (falling-edge triggered) |
|
7 |
DB0 |
I/O |
Data Bit 0 (not used in 4-bit operation) |
|
8 |
DB1 |
I/O |
Data Bit 1 (not used in 4-bit operation) |
|
9 |
DB2 |
I/O |
Data Bit 2 (not used in 4-bit operation) |
|
10 |
DB3 |
I/O |
Data Bit 3 (not used in 4-bit operation) |
|
11 |
DB4 |
I/O |
Data Bit 4 |
|
12 |
DB5 |
I/O |
Data Bit 5 |
|
13 |
DB6 |
I/O |
Data Bit 6 |
|
14 |
DB7 |
I/O |
Data Bit 7 |
|
15 |
LED+ |
Power |
Backlight Anode (+) (if applicable) |
|
16 |
LED− |
Power |
Backlight Cathode (−) (if applicable) |
Notes:
- Vee (also V0): This is an analog input, typically connected to a potentiometer. The user must be able to control this voltage independent of all other adjustments, in order to optimise visibility of the display that varies i. a. with temperature and, in some cases, height above the sea level. With a wrong adjustment, the display will seem to malfunction.
- R/W: In most applications, reading from the HD44780 is not necessary. In that case, this pin can be permanently connected to ground and no processor pins need to be allocated to control it.
Mode selection
A 20×4-character display
In 8-bit mode, all transfers happen in one cycle of the enable pin (E) with all 8 bits on the data bus and the RS and R/W pins stable. In 4-bit mode, data are transferred as pairs of 4-bit "nibbles" on the upper data pins, D7–D4, with two enable pulses and the RS and R/W pins stable. The four most significant bits (7–4) must be written first, followed by the four least significant bits (3–0). The high/low sequence must be completed each time or the controller will not properly receive further commands.
Selecting 4-bit or 8-bit mode requires careful selection of commands. There are two primary considerations. First, with D3–D0 unconnected, these lines will always appear high (binary 1111) to the HD44780 since there are internal pull-up MOSFETs.[3] Second, the LCD may initially be in one of three states:
State 1: 8-bit mode
State 2: 4-bit mode, waiting for the first set of 4 bits
State 3: 4-bit mode, waiting for the second set of 4 bits
State 3 may occur, for example, if a prior control was aborted after sending only the first 4 bits of a command while the HD44780 was in 4-bit mode.
The following algorithm ensures that the LCD is in the desired mode:
The same command is sent three times, Function Set with 8-bit interface D7–D4 = binary 0011, the lower four bits are "don't care", using single enable pulses. If the controller is in 4-bit mode, the lower four bits are ignored so they cannot be sent until the interface is in a known size configuration.
Starting in state 1 (8-bit configuration):
Send Function Set command. Command will be executed, set 8-bit mode.
Send Function Set command. Command will be executed, set 8-bit mode.
Send Function Set command. Command will be executed, set 8-bit mode.
Starting in state 2 (4-bit configuration, waiting for first 4-bit transfer):
Send Function Set command. First 4 bits received.
Send Function Set command. Last 4 bits, command accepted, set 8-bit mode.
Send Function Set command. Command will be executed, set 8-bit mode.
Starting in state 3 (4-bit configuration, waiting for last 4-bit transfer):
Send Function Set command. Last 4 bits, unknown command executed.
Send Function Set command. In 8-bit mode command will be executed, otherwise first 4 bits received.
Send Function Set command. 8-bit command will be executed or last 4 bits of previous command; set 8-bit mode.
In all three starting cases, the bus interface is now in 8-bit mode, 1 line, 5×8 characters. If a different configuration 8-bit mode is desired, an 8-bit bus Function Set command should be sent to set the full parameters. If 4-bit mode is desired, binary 0010 should be sent on D7–D4 with a single enable pulse. Now the controller will be in 4-bit mode and a full 4-bit bus Function Set command sequence (two enables with command bits 7–4 and 3–0 on subsequent cycles) will complete the configuration of the Function Set register.
Instruction set
The HD44780 instruction set is shown below:
|
Instruction |
Code |
Description |
Execution |
|||||||||
|
RS |
R/W |
B7 |
B6 |
B5 |
B4 |
B3 |
B2 |
B1 |
B0 |
|||
|
Clear display |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
Clears display and returns cursor to the home position (address 0). |
1.52 ms |
|
Cursor home |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
* |
Returns cursor to home position. Also returns display being shifted to the original position. DDRAM content remains unchanged. |
1.52 ms |
|
Entry mode set |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
I/D |
S |
Sets cursor move direction (I/D); specifies to shift the display (S). These operations are performed during data read/write. |
37 μs |
|
Display on/off control |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
D |
C |
B |
Sets on/off of all display (D), cursor on/off (C), and blink of cursor position character (B). |
37 μs |
|
Cursor/display shift |
0 |
0 |
0 |
0 |
0 |
1 |
S/C |
R/L |
* |
* |
Sets cursor-move or display-shift (S/C), shift direction (R/L). DDRAM content remains unchanged. |
37 μs |
|
Function set |
0 |
0 |
0 |
0 |
1 |
DL |
N |
F |
* |
* |
Sets interface data length (DL), number of display line (N), and character font (F). |
37 μs |
|
Set CGRAM address |
0 |
0 |
0 |
1 |
CGRAM address |
Sets the CGRAM address. CGRAM data are sent and received after this setting. |
37 μs |
|||||
|
Set DDRAM address |
0 |
0 |
1 |
DDRAM address |
Sets the DDRAM address. DDRAM data are sent and received after this setting. |
37 μs |
||||||
|
Read busy flag & address counter |
0 |
1 |
BF |
CGRAM/DDRAM address |
Reads busy flag (BF) indicating internal operation being performed and reads CGRAM or DDRAM address counter contents (depending on previous instruction). |
0 μs |
||||||
|
Write CGRAM or DDRAM |
1 |
0 |
Write Data |
Write data to CGRAM or DDRAM. |
37 μs |
|||||||
|
Read from CG/DDRAM |
1 |
1 |
Read Data |
Read data from CGRAM or DDRAM. |
37 μs |
|||||||
|
Instruction bit names — I/D – 0 = decrement cursor position, 1 = increment cursor position; S – 0 = no display shift, 1 = display shift; D – 0 = display-off, 1 = display on; C – 0 = cursor off, 1 = cursor on; B – 0 = cursor blink off, 1 = cursor blink on; S/C – 0 = move cursor, 1 = shift display; R/L – 0 = shift left, 1 = shift right; DL – 0 = 4-bit interface, 1 = 8-bit interface; N – 0 = 1/8 or 1/11 duty (1 line), 1 = 1/16 duty (2 lines); F – 0 = 5×8 dots, 1 = 5×10 dots; BF – 0 = can accept instruction, 1 = internal operation in progress. |
||||||||||||
DDRAM is Display Data RAM and CGRAM is Character Generator RAM.[5] The DDRAM is
80 bytes (40 per row) addressed with a gap between the two rows. The first row is addresses 0 to 39 decimal or 0 to 27 hex. The second row is addresses 64 to 103 decimal or 40 to 67 hex.
The CGRAM is read/write memory used to encode up to 8 characters in the character generator. It consists of 64 fields at addresses 0 to 3F hex. Each field is 5 bits mapping to a row of pixels of each character. Each 8 fields in the CGRAM are used for each character. The lower 3 bits of the character codes from 0–7 and 8–15 select the groups of 8 fields in the CGRAM memory.
Reading and writing to the DDRAM is done by setting the RS input high during bus transfers. The DDRAM must also be selected by using the Set DDRAM address command which selects the DDRAM for access and also sets the starting address for DDRAM access.
Likewise reading and writing to the CGRAM is done by setting the RS input high during bus transfers. The CGRAM must also be selected by the Set CGRAM address command which selects the CGRAM for access and also sets the starting address for CGRAM access.
The execution times listed in this table are based on an oscillator frequency of 270 kHz. The data sheet indicates that for a resistor of 91 kΩ at VCC=5 V the oscillator can vary between 190 kHz and 350 kHz resulting in wait times of 52.6 µs and 28.6 µs instead of 37 µs. If a display with the recommended 91 kΩ resistor is powered from 3.3 volts the oscillator will run much slower. If the busy bit is not used and instructions are timed by the external circuitry, this should be taken into account.
Font:
Amber backlight on a 8×2-character HD44780 display
The original HD44780 character generator ROM contains 208 characters in a 5×8 dot matrix, and 32 characters in a 5×10 dot matrix. More recent compatible chips are available with higher resolution, matched to displays with more pixels.[citation needed]
Two versions of the ROM have been developed:
HD44780UA00, the standard (Japanese) version, which includes katakana characters and some Greek letters and mathematical symbols
HD44780UA02, a European version, which includes the complete set of Greek, Cyrillic and Western European characters (with diacritics)
The 7-bit ASCII subset for the Japanese version is non-standard: it supplies a Yen symbol where the backslash character is normally found, and left and right arrow symbols in place of tilde and the rubout character.
A limited number of custom characters can be programmed into the device in the form of a bitmap using special commands. These characters have to be written to the device each time it is switched on, as they are stored in volatile memory
Code:
/*
* Ultrasonic.c
*
* Created: 9/2/2023 7:45:31 PM
* Author : laxma
*/
#define F_CPU 8000000UL
#include
#include
#include
#include
#include
#define LCD_Data_Dir DDRD /* Define LCD data port direction */
#define LCD_Command_Dir DDRC /* Define LCD command port direction register */
#define LCD_Data_Port PORTD /* Define LCD data port */
#define LCD_Command_Port PORTC /* Define LCD data port */
#define EN PC2 /* Define Enable signal pin */
#define RW PC3 /* Define Read/Write signal pin */
#define RS PC4 /* Define Register Select (data reg./command reg.) signal pin */
void LCD_Command (char); /* LCD command write function */
void LCD_Char (char); /* LCD data write function */
void LCD_Init (void); /* LCD Initialize function */
void LCD_String (char*); /* Send string to LCD function */
void LCD_String_xy (char,char,char*); /* Send row, position and string to LCD function */
void LCD_Command (char cmd) /* LCD command write function */
{
LCD_Data_Port = cmd; /* Write command data to LCD data port */
LCD_Command_Port &= ~((1<
LCD_Command_Port |= (1<
_delay_us(1);
LCD_Command_Port &= ~(1<
_delay_ms(3); /* Wait little bit */
}
void LCD_Char (char char_data) /* LCD data write function */
{
LCD_Data_Port = char_data; /* Write data to LCD data port */
LCD_Command_Port &= ~(1<
LCD_Command_Port |= (1<
_delay_us(1);
LCD_Command_Port &= ~(1<
_delay_ms(1); /* Wait little bit */
}
void LCD_Init (void) /* LCD Initialize function */
{
LCD_Command_Dir |= (1<
LCD_Data_Dir = 0xFF; /* Make LCD data port direction as o/p */
_delay_ms(20); /* LCD power up time to get things ready, it should always >15ms */
LCD_Command (0x38); /* Initialize 16X2 LCD in 8bit mode */
LCD_Command (0x0C); /* Display ON, Cursor OFF command */
LCD_Command (0x06); /* Auto Increment cursor */
LCD_Command (0x01); /* Clear LCD command */
LCD_Command (0x80); /* 8 is for first line and 0 is for 0th position */
}
void LCD_String (char *str) /* Send string to LCD function */
{
int i;
for(i=0;str[i]!=0;i++) /* Send each char of string till the NULL */
{
LCD_Char (str[i]); /* Call LCD data write */
}
}
void LCD_String_xy (char row, char pos, char *str) /* Send string to LCD function */
{
if (row == 1)
LCD_Command((pos & 0x0F)|0x80); /* Command of first row and required position<16>
else if (row == 2)
LCD_Command((pos & 0x0F)|0xC0); /* Command of Second row and required position<16>
LCD_String(str); /* Call LCD string function */
}
#define Trigger_pin PA0 /* Trigger pin */
int TimerOverflow = 0;
ISR(TIMER1_OVF_vect)
{
TimerOverflow++; /* Increment Timer Overflow count */
}
int main(void)
{
char string[10];
long count;
double distance;
DDRA = 0x01; /* Make trigger pin as output */
PORTB = 0xFF; /* Turn on Pull-up */
LCD_Init();
LCD_String_xy(1, 0, "Ultrasonic");
sei(); /* Enable global interrupt */
TIMSK = (1 << TOIE1>
TCCR1A = 0; /* Set all bit to zero Normal operation */
while(1)
{
/* Give 10us trigger pulse on trig. pin to HC-SR04 */
PORTA |= (1 << Trigger>
_delay_us(10);
PORTA &= (~(1 << Trigger>
TCNT1 = 0; /* Clear Timer counter */
TCCR1B = 0x41; /* Capture on rising edge, No prescaler*/
TIFR = 1<
TIFR = 1<
/*Calculate width of Echo by Input Capture (ICP) */
while ((TIFR & (1 << ICF1>
TCNT1 = 0; /* Clear Timer counter */
TCCR1B = 0x01; /* Capture on falling edge, No prescaler */
TIFR = 1<
TIFR = 1<
TimerOverflow = 0; /* Clear Timer overflow count */
while ((TIFR & (1 << ICF1>
count = ICR1 + (65535 * TimerOverflow); /* Take count */
/* 8MHz Timer freq, sound speed =343 m/s */
distance = (double)count / 466.47;
dtostrf(distance, 2, 2, string);/* distance to string */
strcat(string, " cm "); /* Concat unit i.e.cm */
LCD_String_xy(2, 0, "Dist = ");
LCD_String_xy(2, 7, string); /* Print distance */
_delay_ms(200);
}
}
Output:
This project successfully implements a distance measurement system using an HC-SR04 ultrasonic sensor and an AVR microcontroller. It continuously sends ultrasonic pulses, measures their round-trip time accurately, and calculates the distance to an object in centimeters. This distance is then displayed on a 16x2 LCD screen, providing real-time feedback. The project demonstrates the practical application of ultrasonic sensors for distance measurement, making it valuable for applications like robotics, automation, and obstacle detection.
