Question: What makes 8 times ‘beep’, but I cannot hear it?
Answer: My ultrasonic range finder :-)
What I have added to my FRDM.
The backside features all the electronics to which simply usage of the sensor:
With simple I mean, it only needs 4 pins:
- Vcc (+5V DC supply)
- Trig (TTL input, to trigger a measurement)
- Echo (TTL output, pulse proportional to distance)
- GND (ground)
Note::
After the 10 us Trigger signal, the sensor sends the burst during ‘1’ and ‘2’ .
:idea: For decimal system challenged engineers: instead of a divider of 58, use a divider of 148 to get the result in inch instead of centimeter.
Hardware Connection
I’m using a breadboard for easy wiring and connection.
To visualize the measurement, I’m using the 2×16 Character display I have used in my earlier post. Of course a LCD is optional. I used the same wiring as before:
=================================================================:
I configure it as Output pin on PTA12:
Additionally I add the Wait component to the project::
For this I’m selecting the TPM0_CNT as counter and configure the channel for ‘capture’:
:idea: You will understand later on why I have selected this particular counter.
As I have connected the Echo signal on PTD4, I need to use that pin as ‘Capture input pin’:
As hinted by the pin name (PTD4/LLWU_P14/SPI1_PCS0/UART2_RX/TPM0_CH4), this pin is available on TPM0_CH4, so I need to configure this as capture device:
:idea::
That’s not enough: I need to enable interrupts for the counter too::
The first thing to do is to disable ‘Auto select timing’:
:idea:):
Counter Overrun
There one thing to consider: when will the counter overflow? I keep in mind that with the speed of sound, around 4 meters distance means 400*58us makes 23.5 ms Echo signal pulse. So I need to make sure that for this distance, my counter does *not* overflow. I can check this in the ‘Overrun period’ settings:
Deselecting ‘auto select timing’ again will show the overrun period time:’):):
With this in mind, the implementation is pretty straight forward. First, an enumeration for the state machine states:
typedef enum { ECHO_IDLE, /* device not used */ ECHO_TRIGGERED, /* started trigger pulse */ ECHO_MEASURE, /* measuring echo pulse */ ECHO_OVERFLOW, /* measurement took too long */ ECHO_FINISHED /* measurement finished */ } US_EchoState;
Next the data structure to keep all my data:
typedef struct { LDD_TDeviceData *trigDevice; /* device handle for the Trigger pin */ LDD_TDeviceData *echoDevice; /* input capture device handle (echo pin) */ volatile US_EchoState state; /* state */ TU1_TValueType capture; /* input capture value */ } US_DeviceType; static US_DeviceType usDevice; /* device handle for the ultrasonic device */
And here is how it gets initialized:
void US_Init(void) { usDevice.state = ECHO_IDLE; usDevice.capture = 0; usDevice.trigDevice = TRIG_Init(NULL); usDevice.echoDevice = TU1_Init(&usDevice); }:
/* ** =================================================================== ** Event : TU1_OnChannel0 (module Events) ** ** Component : TU1 [TimerUnit_LDD] ** Description : ** Called if compare register match the counter registers or ** capture register has a new content. OnChannel0Channel0(LDD_TUserData *UserDataPtr) { US_EventEchoCapture(UserDataPtr); } /* ** =================================================================== ** Event : TU1_OnCounterRestart (module Events) ** ** Component : TU1 [TimerUnit_LDD] ** Description : ** Called if counter overflow/underflow or counter is ** reinitialized by modulo or compare register matching. ** OnCounterRestartCounterRestart(LDD_TUserData *UserDataPtr) { US_EventEchoOverflow(UserDataPtr); }
In case of Overflow I simply set the state machine to the overflow state:
void US_EventEchoOverflow(LDD_TUserData *UserDataPtr) { US_DeviceType *ptr = (US_DeviceType*)UserDataPtr; ptr->state = ECHO_OVERFLOW; }
While in the interrupt/event for raising or falling edge, I reset the counter value at raising edge, or read out the counter value at falling edge:
US_DeviceType *ptr = (US_DeviceType*)UserDataPtr; if (ptr->state==ECHO_TRIGGERED) { /* 1st edge, this is the raising edge, start measurement */ TU1_ResetCounter(ptr->echoDevice); ptr->state = ECHO_MEASURE; } else if (ptr->state==ECHO_MEASURE) { /* 2nd edge, this is the falling edge: use measurement */ (void)TU1_GetCaptureValue(ptr->echoDevice, 0, &ptr->capture); ptr->state = ECHO_FINISHED; }:
uint16_t US_Measure_us(void) { uint16_t us; /* send 10us pulse on TRIG line. */ TRIG_SetVal(usDevice.trigDevice); WAIT1_Waitus(10); usDevice.state = ECHO_TRIGGERED; TRIG_ClrVal(usDevice.trigDevice); while(usDevice.state!=ECHO_FINISHED) { /* measure echo pulse */ if (usDevice.state==ECHO_OVERFLOW) { /* measurement took too long? */ usDevice.state = ECHO_IDLE; return 0; /* no echo, error case */ } } us = (usDevice.capture*1000UL)/(TU1_CNT_INP_FREQ_U_0/1000); return us; }:
static uint16_t calcAirspeed_dms(uint8_t temperatureCelsius) { /* Return the airspeed depending on the temperature, in deci-meter per second */ unsigned int airspeed; /* decimeters per second */ airspeed = 3313 + (6 * temperatureCelsius); /* dry air, 0% humidity, see */ airspeed -= (airspeed/100)*15; /* factor in ~15% for a relative humidity of ~40% */ return airspeed; } uint16_t US_usToCentimeters(uint16_t microseconds, uint8_t temperatureCelsius) { return (microseconds*100UL)/calcAirspeed_dms(temperatureCelsius)/2; /* 2 because of two way */ }
:idea::
static void Measure(void) { uint16_t us, cm; uint8_t buf[8]; us = US_Measure_us(); UTIL1_Num16uToStrFormatted(buf, sizeof(buf), us, ' ', 5); LCD1_GotoXY(1,5); LCD1_WriteString((char*)buf); cm = US_usToCentimeters(us, 22); UTIL1_Num16uToStrFormatted(buf, sizeof(buf), cm, ' ', 5); LCD1_GotoXY(2,5); LCD1_WriteString((char*)buf); LEDR_Put(cm<10); /* red LED if object closer than 10 cm */ LEDB_Put(cm>=10&&cm<=100); /* blue LED if object is in 10..100 cm range */ LEDG_Put(cm>100); /* blue LED if object is in 10..100 cm range */ } /. ***/ /* Write your code here */ US_Init(); LCD1_Clear(); LCD1_WriteLineStr(1, "us: "); LCD1_WriteLineStr(2, "cm: "); for(;;) { Measure(); WAIT1_Waitms(50); /* wait at least for 50 ms until the next measurement to avoid echos */ } /***!!! ***/
:idea: :-)
Hi, Erich!
Great job!, I really enjoy your posts!
I’m having some troubles adapting an example of an USB HOST MSD example to work with PE, I posted the problem in the Freescale Forums:
Can you give some orientation?
Thanks in advance :)
Hi Alex,
sorry, I do not have that K20, so I’m not of any help here I think.
I tried the same in my Freedom board and failed :'( , It would be great for you to do a tutorial on this, when you can, no pressure :)
Hi Alex,
well, I thought that this *is* already a tutorial? Maybe one of your settings is wrong? Could you try the project (link at the end of the tutorial). You might simply remove the LCD code, and if you use the same pins, then it should work for you as well.
Hope this helps,
Erich
Upps!! Sorry for the confusion, I meant to read a USB stick with the Freedom board.
Hi Erich,
Thanks for the above. It may save someone’s life some day (remote chance). I have been pondering how to impliment an emergency stop circuit on a device I have been designing with the FRDM board.
I think using the above idea to detect if someone (or part of someone) is in a dangerous place is a good idea.
Thank you.
You are welcome. Just keep in mind that the ultrasonic beam is rather small and focused. To cover a larger area it is necessary to move the beam around, or to use multiple sensors. Depending on your application: in robotics (I mean big industrial robots) they are using in many cases a ‘laser curtain’: multiple laser beams to form a curtain to detect if something/someone is moving into the danger zone. That’s simpler and more reliable than ultrasonic in my view.
Pingback: The Freedom Robot | MCU on Eclipse
Hi,
After reading your posts I ordered one FRDM-KL25Z and started to have fun:)
Can you please help me with this application:
First of all the compiled code provided is not loading properly. (No LCD attached but the RGB indication should have worked)
Downloaded the project.
Removed the LCD component.
Recompiled the code.
After recompiling and loading the RED led stays always on.
Added ConsoleIO component and printf(“time is:%u\n”, us); after us = US_Measure_us(); so I can read the values on my PC.
Now the I can see that us has some values as I move an object in front of the sensor but they are like:27, 24, 21,18,15,12, 9 and only at very close range(5 cm). If the object is farther away then the us value displayed is always 12.
Some representation problem? Missing some bits from the uint16_t?
Thank you.
Cristian
Hi Cristian,
Could you verify that your sensor is returning the correct pulse on the echo line? Best if you could use a logic analyzer or oszilloscope.
Erich
Hi,
thank you for the quick answer,
-No logic analyzer, maybe will borrow a scope.
-Will check the setup again tomorrow
-When trying to load the precompiled code provided by you (Freedom_HC-SR04.hex) the green led near the pemicro chip acts strangely (series of 7 or 8 rapid blinks, pause, then blinks again). Something is wrong with that code?
-I am not mistreating that bird, that baby entered my house and just before relieving it I took a snapshot :) They are little owls.The whole family lives right under my roof.
Something like this:
Nice birds, indeed!
I suggest that you check as well the wiring:Make sure you have that voltage divider in place, plus everything connected to the right pins on the Freedom board.
And it could be possible as well that your ultrasonic module is not working properly. I assume you are using the HC-SR04? Because there might be different types of modules around.
Hi!
Problem Solved!
The HC-SR04 module was defective! Replaced it and now the application works nicely :)
Maybe some people are used to it, but I discovered today: The PicKit2 programmer has a Logic Tool function which is very useful.
Still 2 problems remain:
1-The code provided by you Erich has the Flash image format set as “ihex” and I cannot load it into the FRDM-KL25Z. I have to set it as “srec” and rebuild the project. Also the LCD component has to be removed if LCD module not available.
2-Measuring the Trig signal I noticed it is 120us long, not 10us as expected. Trying to decrease the argument of the WAIT1_Waitus(10); function I can only obtain 3 us for WAIT1_Waitus(5) and no other values in between. Why could this happen?
Keep up the good work!
Cristian
Hi Cristian,
good to hear you are making progress.
About 1): Thanks, I have fixed this now on
About 2): Are you sampling with a high enough frequency in your logic analycer. I mean it should be at least in the 1 us range if not faster.
And are you using the latest version of ‘Wait’ component? I had fixed some timing back in December (). Wait is using ‘busy waiting’ and not very precise, and it can be interrupted by other interrupts. So you might disable interrupts around the Wait() call.
Hi, do you have any project in which you read analogic signal (outside of the board) and display it at LCD?
Hello, no, not at this moment.
I can use this sensor in PIC18F4550?
Hi John, yes, I do not see why this should be a problem. All what you need is an output pin plus an input pin. And of course adopt the driver/software for the PIC.
I’m now learning PIC am newbie. I know very little about PIC
I have a project, and I have to measure the distance from this sensor (HC-SR04) using PIC18F4550.
Do you know any tutorial to learn how to do?
Hi John,
I did an internet search for “HC-SR04 PIC” and received plenty of hits/pages. I suggest to start there?
I found this:
but does not use the program that work, work with MPLAB.
Do you know any tutorial for this program?
Hi John,
no, I do not have a tutorial for MPLAB, sorry.
Pingback: Tracked Robot Update: Ultrasonic, Line Sensor and Shell with SMAC 802.15.4 | MCU on Eclipse
Pingback: Mini Sumo Robot with Proximity Sensors | MCU on Eclipse
Hello,
First, sorry my english.
My name is Fernando, I from Brazil. Congratulation foi job.
I wonder if it is possible to add an additional ultrasonic sensor using the same component TimerUnit_LDD. And how do I?
Thank you
Hi Fernando,
thanks :-)
no worries about English: mine is not perfect too ;-)
I think it should be possible to add another sensor, but I have not done this. Would need to try first myself. Sure the same time base could be used to measure the signal.?
Or a sensor picks up the same distance from each other!
It could be much better if you expand the tutorial in order to build more complex robotics applications. A series of tutorial can be found in this article
Yes, agreed. This tutorial is just a starting point. On GitHub I have placed some more advanced usage examples, see
BTW: Nice summary you wrote about different ultrasonic modules, thanks for this!
Hi Erich,
Great tutorial- been really helpful so far! I am new to programming and have a couple of questions.
With the code, what header file should be used?
Should the code you have given on this page be in one .c source file or a different file for each section?
Thank you for your help
Jonny
Hi Jonny,
best if you check out the sources here:
Spot on, thank you!
The only problem I’m having is that some of the headers have errors (unresolved inclusions?). These are the following:
#include “LEDR.h”
#include “LEDpin1.h”
#include “BitIoLdd1.h”
#include “LEDG.h”
#include “LEDpin2.h”
#include “BitIoLdd2.h”
#include “LEDB.h”
#include “LEDpin3.h”
#include “BitIoLdd3.h”
#include “TU1.h”
#include “LCD1.h”
#include “RW1.h”
#include “BitIoLdd15.h”
#include “EN1.h”
#include “BitIoLdd4.h”
#include “RS1.h”
#include “BitIoLdd5.h”
#include “DB41.h”
#include “BitIoLdd10.h”
#include “DB51.h”
#include “BitIoLdd11.h”
#include “DB61.h”
#include “BitIoLdd12.h”
#include “DB71.h”
#include “BitIoLdd13.h”
#include “UTIL1.h”
Do you know what the problem might be with this? Do I need to install a library somewhere?
Thanks
Jonny
Yes, you need to load additional Processor Expert libraries/components (see) and generate code.
Once loaded do I need to add each component into the project? Many of them have errors- will these need setting for the specific pins etc on the board? Thanks
Hi Jonny,
The components are already added to the project. If you have all components installed, you can simply generate code and everything should work out of the box. Of xourse if you have the sensor on different pins, then you need to change the pin settings. Maybe you are not (yet?) familiar with Processor Expert and how this works? I suggest to have a look at this tutorial () as it goes through the individual steps.
Erich,
First off, thank you so much for all your tutorials. I’ve been playing with the Kinetis boards for some hobby projects over the last year and your tutorials and insights have been invaluable.
I’ve modified your code to use 4 sensors. I added three additional channels to the timer unit. I also added function pointers for the trigger pin functions to your US_DeviceType struct and then created another struct (US_Devices) that holds an array of 4 US_DeviceType structs and a counter. In the init function I set the function pointers for each sensor to the appropriate pin function. Then I modified your code to loop through the 4 sensors updating the counter in the struct using the counter and pin function pointers in the struct pointer passed to the events. Theoretically it should be usable for any number of sensors.
I’ve posted it to GitHub so others can use it if they like.
It has some other code from FreeRTOS; I keep the US sensors in their own task since US_Measure blocks and I send off the results to another task via a queue.
At some point I’d like to get it so I can fire the sensors in pairs (front/back and left/right) at the same time, but since the timer unit’s overflow event fires for all 4 channels without indicating which channel overflowed I’m not sure yet how to go about that.
I’m a relative C newb so I wouldn’t be surprised if there are any bugs I haven’t caught yet.
Once again thank you so much for all of your tutorials!
Hi,
many thanks for sharing your work. I plan to build a dual-sensor system in the near future, so this for sure would be helpful. Many thanks again!
Thank you for your blog, it’s very helpful.
I made robot on FRDM-KL05Z with HC-SR04 too.
thanks for sharing, that one really looks very nice!
Hi Erich,
Thank for your great work. I have some troubles with load your ready code to CodeWarrior and programm to board.
Can you tell how can I do it properly ?
(sorry for that simple issue but I’m a newbie ;P)
Best regards
Hi Bartek,
are you using the FRDM-KL25Z board? Keep in mind that the board comes with a bootloader shipped (and not with the debug application). So you need to load the right firmware. I recommend that you use the latest P&E one (see).
See how to program the new firmware.
Hi,
yes I use FRDM-KL25Z. I did like this: enter the bootloader mode, than I droped this file “MSD-DEBUG-FRDM-KL25Z_Pemicro_v114.SDA” to root and replug the board.
Yes, that sounds ok. What operating system are you using? Windows 8.1? If so, then there is a known problem:
That problem is solved with the V1.14 firmware (but you need a Windows 7 machine (or 8.0)) to update the bootloader firmware.
I use Windows 7 (home premium version x64).
Then this should not be an issue. Can you copy the file with the DOS shell/command prompt?
And if you have a virus scanner, to disable it?
All this stuff was done earlier (open sda update). Thing is that my board is working fine (I can run the .srec precompiled program and it works). The point is I want to load ultra sonic project on board properly. I use “flash programmer” –> “flash file to target”, I choose .hex file (“file to flash”) from your project and press “erase and program”. It shows that transfer is succesful but after replug nothing is happend (rgb diode is dead). All devices are connect properly.
Ok I did it ! I load .elf file instead .hex and it’s work :D
Thank for your help
Best regards !
Try with an S19 file (Motorola S19 file). .Hex are usually intel hex files, and I think this is not supported by the bootloader.
Pingback: Programmable Ultrasonic Sensor Shield for FRDM Board | MCU on Eclipse
Pingback: Sensor and Communication Shield for Sumo Robot | MCU on Eclipse