Ultrasonic Sensor HC-SR04 With PIC Microcontroller

How ultrasonic sensor SR-HC04 works.
How Ultrasonic Sensor Works

An ultrasonic sensor is a very useful sensor that measures distances with sounds. We used the PIC16F628A microcontroller for this project. This microcontroller has TMR1 which we are going to use. We need 2 I/O pins for TRIG and ECHO pin. For showing distance, we used an LCD. For more information about the LCD library and usage click button below.

HC-SR04 Ultrasonic Sensor

This sensor sends out ultrasonic sound waves and receives it back. After calculating the traveling time it can measure the distance between sensor and object. This is the power of sound. But the measurement speed of the ultrasonic sensor is low compared to the infrared proximity sensor.

HC-SR04 ultrasonic sensor module.
HC-SR04 Ultrasonic Sensor

The sensor module we are going to use is like the picture above. This module has an ultrasonic transmitter, ultrasonic receiver, and control circuit. With this module, you can measure distances from 2cm to 400cm. The module has 4 pins:

  • VCC: 5V power input pin
  • Trig: Trigger pin
  • Echo: Echo pin
  • GND: Ground pin

Trig and Echo pins are used to communicate with the microcontroller.

Calculating Measurement

Ultrasonic sensor distande calculation.
Ultrasonic Sensor Distance

In the picture above, you can see two-length d and x. The d is the distance from the object to the sensor. The x is the distance from the object to the sensor module. If you want to measure distances under 15cm precisely you must use x distance.

You can see how to calculate d and x lengths in the picture below. For this formula, we choose FOSC = 4 MHz and Prescaler = 1:1.

Ultrasonic sensor distance calculation.
Ultrasonic Sensor Distance Calculation

To show how much error will be if not use x length, you can see examples below.

Ultrasonic sensor error examples.
Error Examples

Timer1 Module

TIMER1 block diagram.
TIMER1 Block Diagram

The Timer1 module is a 16-bit timer/counter consisting of two 8-bit registers (TMR1H and TMR1L) which are readable and writable. The TMR1 register pair (TMR1H:TMR1L) increments from 0000h to FFFFh and rolls over to 0000h. (PIC16F628A Datasheet) We will use the Timer1 module for measure time. In the T1CON register we will use T1CKPS bits to adjust prescale value, TMR1CS bit for clock selection and TMR1ON bit to start/stop the timer. For read and write timer values we will use TMR1H and TMR1L registers.

TIMER1 Control Register
T1CON Register

Ultrasonic Sensor Circuit

Ultrasonic sensor schematic with PIC16F628A microcontroller.
Ultrasonic Sensor Schematic


PIC16F628A microcontroller has a 2Kb program memory. For this reason, we can not calculate x value and show “Out of Range” message. If you have a microcontroller which has more than 2Kb program space you can use it and calculate x value.

main.c File

 * File:   main.c
 * Author: radon
 * Created on March 23, 2020, 12:51 PM

#include "config.h"

void main(void) {
    // Turn comparator off and enable pins for digital I/O functions
    CMCONbits.CM = 0b111;
    // Ultrasonic sensor pins
    TRISAbits.TRISA0 = 0;  // TRIG pin
    TRISAbits.TRISA1 = 1;  // ECHO pin
    // TIMER1 Configuration
    // Prescaler = 1:1
    T1CONbits.T1CKPS = 0b00;
    // Select internal clock (FOSC/4)
    T1CONbits.TMR1CS = 0;
    // LCD initialize
    uint8_t str[13];
    uint16_t timer;
    while(1) {
        // Reset TIMER1 values
        TMR1H = 0;
        TMR1L = 0;
        // Sen 10us pulse to TRIG pin
        PORTAbits.RA0 = 1;
        PORTAbits.RA0 = 0;
        // Wait for ECHO pin goes to HIGH
        T1CONbits.TMR1ON = 1;   // Enable TIMER1 module
        // Wait for ECHO pin goes to LOW
        T1CONbits.TMR1ON = 1;   // Disable TIMER1 module
        // Read TIMER1 values
        timer = (TMR1H << 8) | TMR1L;
        // Calculate distance
        timer = timer/58.8235 + 1;
        // Using round function
        //timer = round(timer/58.8235);
        // For presice calculation
        //timer = round(sqrt(((timer*timer)/3460.2041)-6.25));
        if(timer >=2 && timer <=400) {
            LCDPrintString("Trion Projects", 1, 0);
            sprintf(str, "Dist = %dcm", timer);
            LCDPrintString(str, 2, 0);
        } else {
            LCDPrintString("Out of Range!", 2, 0);

config.h File

 * File:   
 * Author: 
 * Comments:
 * Revision history: 

// This is a guard condition so that contents of this file are not included
// more than once.  
#ifndef CONFIG
#define	CONFIG

// PIC16F628A Configuration Bit Settings

// 'C' source line config statements

#pragma config FOSC = INTOSCCLK // Oscillator Selection bits (INTOSC oscillator: CLKOUT function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON       // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

#define _XTAL_FREQ 4000000

#include <xc.h> // include processor files - each processor file is guarded.  
#include <stdint.h>
#include <stdio.h>
#include <math.h>
#include "lcd.h"

#endif	/* CONFIG */

lcd.c and lcd.h Files

We use our LCD library which you can find in the link below.


Share This Tutorial

Leave a Reply

Your email address will not be published. Required fields are marked *