July 20, 2015
In this project, we will talk to the popular ultrasonic distance sensor HC-SR04 using the Nordic nRF51-DK board, and transmit the distance information over BLE using the NUS (Nordic UART Service). We will be utilizing GPIO and Timer1 for this purpose.
Before you read further, please take a look at my previous article on nRF51-DK programming using GCC, since we’re going to use the same development setup here.
Communicating with HC-SR04
- Send a 10us HIGH pulse on the Trigger pin.
- The sensor sends out a “sonic burst” of 8 cycles.
- Listen to the Echo pin, and the duration of the next HIGH signal will give you the time taken by the sound to go back and forth from sensor to target.
So we need (a) a way to send the trigger pulse and (b) a method to measure the echo pulse.
Here is how the nRF51-DK is hooked up to the HC-SR04:
|Echo||P0.02 via R DIV|
The HC-SR04 runs on 5V, but our nRF51422 runs on 3.3V logic. It’s OK to send data to the HC-SR04 on 3.3 V TTL, but it’s not OK to receive a 5V pulse from the sensor. Hence, we route the Echo through a resistor divider as shown below.
Now let’s look at how to send and receive data.
You can send the trigger pulse using the GPIO feature of the nRF51. The code looks like this:
What you are doing above is toggling a GPIO pin HIGH and LOW, creating a pulse, and sure enough, you get a response from the Echo pin of the HC-SR04.
The distance is encoded in the width of the HIGH pulse returned by the HC-SR04. To measure this width, we will make use of the nRF51 Timer1 peripheral on the nRF51822. The Timer1 is a 8/16 bit timer which runs at 16 MHz.
In their SDK, Nordic provides higher level APIs (which start with nrf_drv_timer) to set up these timers. But I found their documentation and API to be confusing, so I decided to use the registers directly. I think this is a better approach in any case, as it corresponds directly to what you read in the nRF51 reference manual. Here is the code that sets up the timer and the interrupt routine:
In the start_timer() method above, we start by setting the prescaler of the timer to 0. The timer frequency is given by . The 16 MHz clock generates timer tick every 1/(16000000) s = 62.5 nano seconds. We then set the compare1 register to a value of 500. This means that the timer generates an interrup every nano seconds or 31.25 microseconds. We set up the flags for compare1, enable the interrupt, and set the SHORTS register to clear the task when the interrupt fires. (This is like a shortcut so that you don’t need to manually do this in the interrupt routine.)
In the interrupt routine, we increment a global counter tCount and clear the compare1 event. If you don’t do the latter, you will not get an interrupt the next time around. It’s best to have minimal amount of code TIMER1_IRQHandler() as otherwise, it will introduce latency into the timer, and that will mess up the accurancy of your timing.
Computing the Distance
Now that we are counting every 31.25 microseconds, let’s see how we put that use to compute the distance. Here is the relevant portion from the distance computation method:
As soon as we send the trigger pulse, we loop around pinEcho till it goes high. Then we reset tCount so that the timer can start counting it, and loop around till pinEcho goes low. At this point tCount will be proportional to the pulse width, and we convert it to microseconds using the factor computed earlier. The distance can then be computed using the good old physics equation . The factor above comes in because the sound is travelling back and forth to the sensor
- so you just need to consider half the time measured by the sensor.
The BLE Part
This project uses the S110 softdevice. Once we have the distance information, we send it over BLE via the Nordic UART Service (NUS). You can view it on your BLE device (phone/tablet) using the Nordic nRFtoolbox app.
You can get the complete source code for this project here:
I’d like to thank Aryan from Nordic Developer Zone for clearing my doubts on Timer1 usage.
- nRF51 Series Reference Manual Version 3.0.