Attached is my "device checkout code" for the HC-SR04 Ultrasonic sensor. This code only triggers and reads the sensor echo.
It uses Timer1 in the capture mode (capture Rising edge and capture falling edge).
The code (sketch) is kind of a bare bones approach used only as a starting point for other programs utilizing the HC-SR04.
Writing this code I learned (from the forum community):
- Interrupts are possible in the setup portion of a sketch.
- To clear the Timer1 capture flag (ICF1) one must set it to 1 !
// Device Test Code
// HC-SR04 Ultrasonic distance sensor
// 2022-03-11 fully functional.
//
// This program is like example code for using Timer1 w/o interrupts
// to measure the sensor echo.
// Captured by Timer1 are:
// 1) Time when echo goes high
// 2) Time when echo goes low.
// It is thought using Timer1 is more accurate than the
// built in PulseLN() function. This has not been proven.
//
// Connect echo pulse to Arduino ICP1 pin.
//
// see end of file for additional information.
#define bit(b) (1UL << (b)) // use this instead of _BV(b)
const byte TriggerPin = 9;
const byte EchoPin = 8; // ICP1 input pin
const byte LedPin = 13;
// TCCR1B values to start Timer1, CSS11 = 1, sets prescale = 8 (1 MHz count rate)
// Input Capture Edge Select, ICSE1 (1=Rising, 0=Falling)
const uint8_t TCCR1BCaptureRisingEdge = bit(CS11) | bit(ICES1);
const uint8_t TCCR1BCaptureFallingEdge = bit(CS11);
void setup()
{
Serial.begin(9600);
delay(50);
while (!Serial);
Serial.println(" Starting ....");
digitalWrite(TriggerPin, LOW);
pinMode(TriggerPin, OUTPUT);
pinMode(EchoPin, INPUT);
pinMode(LedPin, OUTPUT);
digitalWrite(LedPin, LOW);
noInterrupts (); // protected code
// reset Timer 1
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
TIMSK1 = 0;
TIFR1 = _BV(ICF1); // clear Input Capture Flag
TCCR1B = TCCR1BCaptureRisingEdge;
interrupts ();
}
void loop(){
TCNT1 = 0;
TIFR1 = bit(ICF1); // clear Input Capture Flag
TCCR1B = TCCR1BCaptureRisingEdge;
//Trigger:
digitalWrite(TriggerPin, LOW); // just to be sure trig is low
delay(1);
digitalWrite(TriggerPin, HIGH);
delayMicroseconds(8);
digitalWrite(TriggerPin, LOW);
delayMicroseconds(8); // may not be required.
while ((TIFR1 & bit(ICF1)) == 0); // Is the Input Capture Flag set?
noInterrupts ();
TIFR1 = bit(ICF1); // Clear the Input Capture Flag (Yes, by setting it to 1)
Interrupts();
uint16_t riseTime = ICR1;
digitalWrite(LedPin, HIGH); // for troubleshooting.
// Now look for a falling edge
noInterrupts();
TIFR1 = _BV(ICF1); // clear Input Capture Flag
TCCR1B = TCCR1BCaptureFallingEdge;
Interrupts();
while ((TIFR1 & bit(ICF1)) == 0); // Is the Input Capture Flag set?
uint16_t fallTime = ICR1;
Serial.print (riseTime);
Serial.print (" ");
Serial.println (fallTime);
delay(2000);
}
/*
I thank numerous Arduino forum posters from which I've gleaned bits and pieces. Special thanks to
John Wasser and westfw for questions regarding interrupts and using Timer1 ICF1.
This code triggers and reads the echo pulse width of a HC-SR04 ultrasonic transducer.
This version uses no interrupts, however the program is "blocked" by the "while" waits
for the echo pulse edges.
THis program targets the Arduino Pro Mini running at 8Mhz. Timer1 has a prescale of 8 resulting
in 1µs/count.
interrupts in Setup .. see: https://forum.arduino.cc/t/is-there-a-risk-of-interrupts-in-setup/968030/2
Notes:
1) The "riseTime" represents the time between the trigger and the echo pin going high.
2) With the above settings for Timer1 can count to about 65ms. Far longer than required for the HC-SR04
if the prescaler is set to 1 instead of 8 the max count will be 9ms Shorter that the max range of the
sensor (put with higher resolution). However it seems the HC-SR04 is not consistent enough to benefit
from the added resolution at the shorter ranges.
Typical output at 65CM:
1256 4658
1261 4663
1260 4662
1256 4658
1268 4668
1261 4661
1256 4658
1256 4659
*/
// -- eof --