SN-HC-SR04 + PIC16F887

SN-HC-SR04 + PIC16F887


We had receive suggestion from user on how to integrate the SN-HC-SR04 sensor with PIC. In this section, we are using the SK40C with PIC16F887 to read the pulse width with proportion to distance and lastly display on LCD.

BTW, if you have further inquiry about this tutorial, please post it in our technical forum as we seldom check the comment section in tutorial site.

Hardware Required


SK40C                                                      PIC16F887


SN-HC-SR04 Ultrasonic Sensor

Software Required

  • XC8 Complier

Additional Information

According to the sensor datasheet, to start measurement, Trig of SR04 must receive a pulse of high (5V) for at least 10us, this will initiate the sensor will transmit out 8 cycle of ultrasonic burst at 40kHz and wait for the reflected ultrasonic burst. When the sensor detected ultrasonic from receiver, it will set the Echo pin to high (5V) and delay for a period (width) which proportion to distance. To obtain the distance, measure the width of Echo pin.


Time = Width of Echo pulse, in uS (micro second)
Distance in centimeters = Time / 58
Distance in inches = Time / 148
Or you can utilize the speed of sound, which is 340m/s


  1. Connect the SN-HC-SR04 VCC and GND to SK40C VDD and GND.
  2. Connect SN-HC-SR04 TRIG to RA0.
  3. Connect SN-HC-SR04 ECHO to RB0.
Untitled Sketch_bb

Code Overview

In this section, the SN-HC-SR04 pulse width from the  ECHO pin is read using the Timer 1 (1:4 Pre-scale) and the the also the PORTB0 Interrupt-On-Change.

1. Configure the Timer 1 to (1:4) pre-scale, Global Interrupt enable and PORTB0 Interrupt-On-Change enable.

[sourcecode language=”cpp” wraplines=”true”]
// Turn on Timer 1 with 4x prescale as counter
T1CON = 0b00100000;
// Turn on PORTC and Global interrupt
INTCONbits.GIE = 1;
INTCONbits.PEIE = 1;
INTCONbits.RBIE = 1;
INTCONbits.RBIF = 0;
// Configuration for Interrupt-On_Change for PORTB0
IOCBbits.IOCB0 = 1;

2.  Clear the Timer before the Timer is start. Then check whether the Echo pin is LOW (which mean no signal).  When there was no signal detected in the Echo pin, PIC will send another 10us LOW-to-HIGH pulse to Ultrasonic sensor.

[sourcecode language=”cpp” wraplines=”true”]
TMR1H = 0; // Clear Timer1
TMR1L = 0;
if(Echo == 0) // If no signal recieved from Ultrasonic
Trig= 0;
Trig = 1;
__delay_us(10); // Send LOW-to-HIGH Pulse of 10us to Ultrasonic
Trig = 0;

3. When PIC detected the changes of signal in PORTB0, it will trigger interrupt and start the timer for Echo Pulse Width counting. Once the Echo is LOW, Timer will stop and combine both 8-bits Timer HIGH and 8-bits Timer LOW to 16-bits.

[sourcecode language=”cpp” wraplines=”true”]
T1CONbits.TMR1ON = 1; // ON Counter
while(Echo == 1);
T1CONbits.TMR1ON = 0;
TMR = (unsigned int)TMR1H << 8;
TMR = TMR + TMR1L; // Combine 2x counter byte into single integer

4.  Convert the Pulse Width into time (us) by using the simple calculation. FYI, We are using 20M OSC in this project.

[sourcecode language=”cpp” wraplines=”true”]
Duration(us) = TMR * Clock speed per instruction(Fosc/4) * timer pre-scale(1:4)
Duration(us) = TMR * 5M * 4
= TMR * 0.2 * 4
= TMR * 2/10  * 4
Duration(us) = (TMR/10) * 2 * 4
duration = (TMR/10) * 8; // Duration Formula = TMR * 0.2us(Clock speed) * 4 (Timer Prescale)
distance_cm = duration / 58 ; // Refer HC-SR04 Datasheet
distance_inc = duration / 148;

5. Compare the result and display the value if within the range which is 2cm to 400cm  or 1″ to 157″. If the result is out or range, LCD will display “***“.

[sourcecode language=”cpp” wraplines=”true”]
if(distance_cm < 400 && distance_inc < 157) // If Ultrasonic sense more than 400cm

6. Lastly, clear the interrupt of PORTB0.

[sourcecode language=”cpp” wraplines=”true”]
INTCONbits.RBIF = 0; // Clear PortB Interrupt Flag


SN-HC-SR04 with PIC16F887