The HC-SR04 Ultrasonic Sensor
AVR Assembly Language
There is quite a lot of information on the web regarding this sensor coupled with AVR microcontrollers, but basically they are all done in C with hefty AVRs, like those found on Arduinos. I really wanted to see if I could get this sensor to work on my favorite AVR, the Tiny13, and do it in AVR assembly language. Happily, I've discovered that this sensor can be used quite easily on a Tiny13 in assembly language. And obviously, if it works on a Tiny13, it'll work on other AVR chips just as well.
A good starting point is the sensor datasheet from iteadstudio.com. Download it, print it out, and get familiar with page 2 of the document. There you will find a diagram of the pulse timing. What follows is pretty much that same information in somewhat more detail.
Basic Operation and Timing of the HC-SRO4 Ultrasonic Sensor
- Make "Trig" (pin 2) on the sensor high for 10µs. This initiates a sensor cycle.
- 8x40kHz pulses will be sent from the "T" transmitting piezzo transducer of the sensor, after which time the "Echo" pin on the sensor will go from low to high.
- The 40kHz sound wave will bounce off the nearest object and return to the sensor.
- When the sensor detects the reflected sound wave, the the Echo pin will go low again.
- The distance between the sensor and the detected object can be calculated based on the length of time the Echo pin is high.
- If no object is detected, the Echo pin will stay high for 38ms and then go low.
What follows is the mathmatics behind the code of the project. If you are only interested in the nuts and bolts of getting your sensor working then please feel free to jump directly to the hardware and software sections.
Basic Distance Calculation for Ultrasonic SensingAt first glance there may seem to be a lot of math involved. In fact it's just a few very simple calculations to convert between speed, time, and distance.
The speed of sound: 340.29 m/s (meters per second).
We will be measuring distance to an object by the time it takes a sound wave to make a round trip to the object and back again, so the the useful number is actually:
The speed of sound to an object and back: 170.15 m/s.
Since our sensor can only detect items relatively nearby, let's change to more useful units by converting from m/s to µs/cm:
s m 1x106µs 58.772µs ------- X ----- X ------- = -------- 170.15m 100cm s cmTime for pulse to travel 1cm to an object and then return to the sensor: 58.772µs.
AVR TimingsThe main piece of hardware used in this project (besides the sensor itself) is an Atmel ATtiny13(A) operating at the default fuse setting, which means it's running at 9.6MHz with a /8 prescaler, which comes out to a 1.2MHz clock on the chip. This is the speed at which instructions on the chip are run. Note that some instructions may require 2 or more clock cycles to complete. See the ATtiny13 Datasheet for details.
AVR Clock Cycles (clks) to Time Conversions
============== 1.2MHz clock timings ============== 1,200,000 clks = 1s 1,000,000 clks = 833.3ms 120,000 clks = 100ms 100,000 clks = 83.3ms 12,000 clks = 10ms 10,000 clks = 8.3ms 1,200 clks = 1ms 1,000 clks = 833.3µs 120 clks = 100µs 100 clks = 83.3µs 12 clks = 10µs 10 clks = 8.3µs 1.2 clks = 1µs 1 clk = 833.3nsNOTE: The Tiny13 does not directly accept an external crystal ceramic resonator like other AVRs. Therefore the timings/measurements in this particular project are only accurate to ±10%. An external clock source could be used to drive an input pin, but it would hardly be worth the effort. If you really need more accuracy, I would suggest using an AVR that accepts an external crystal and, if you decide to go that route, the calculations that follow will change depending on the frequency of the crystal used.
Distance to AVR Clock Cycle ConversionsWhat we really need is a quick conversion between the AVR clock cycles and distance. We get that by combining the ultrasound timings with the AVR timings like this:
Distance to AVR Clock Cycles (clks)
58.772µs 1.2 clks -------- X -------- = 70.526 clks/cm cm µsOr to shed more light on what this means, the inverse:
cm µs -------- X -------- = 0.014179 cm/clks 58.772µs 1.2 clksSo the time for a pulse to shoot out, hit something a centimeter away, and return to the sensor would be about 71 clock cycles. Likewise, in a single clock cycle, a distance of 0.14mm can be reckoned. (This is a theoretical value and cannot be attained by this project because the resolution of the sensor itself is only 3mm.)
You can see that the clock-cycle to distance conversions won't end up in exact numbers of clock cycles. This isn't a huge issue since the sensor itself is only rated for a resolution of 0.3cm, so the 1.2MHz clock speed is fast enough to achieve that resolution, albeit with a 10% error factor based on the accuracy of the AVR's internal clock. In practice, without a crystal, the best we can do is probably closer to about 5mm~1cm of resolution -- YMMV.
The HardwareHere is all that is needed to run the HC-SR04 on a Tiny13.
And here is the schematic (click to enlarge).
The sensor itself needs 5V to operate, so a simple 5V regulated supply running off of a 9V battery is used. Other than that, you have the sensor itself, the Tiny13, and 2 LEDs and their current limiting resistors. The "Sense LED" (LED1 on the schematic) indicates if an object is detected, and the "Range LED" (LED2) tells us if the object is within the range specified in the program.
The CodeAt this point, it's just a matter of counting clock cycles in the code. The code is heavily documented and includes most of the information on this page. It lists the number of clock cycles per instruction where it matters. There are 2 timing loops, a single-byte loop (BDELAY) and a word-length loop (WDELAY), to burn through clock cycles.
Here are links to the above code and the AVR hex file.
Here is the avrdude command I use for the USBasp programmer:
sudo avrdude -p t13 -c usbasp -e -B 3 -U ultrasonic-trial-1.2.hex
And, just in case, the avrdude command to reset the Tiny13 fuses to their default values is:
sudo avrdude -p t13 -c usbasp -e -B 3 -U lfuse:w:0x6a:m -U hfuse:w:0xff:m