View previous topic :: View next topic |
Author |
Message |
umutso
Joined: 23 Jan 2013 Posts: 39 Location: turkey
|
HC-SR04 Ultrasonic Sensor with 16F877A |
Posted: Wed Jan 23, 2013 2:32 pm |
|
|
Dear All,
I'm trying to implement a HC-SR04 ultrasonic sensor with 16F877A. The aim is to detect the distance and write the distance to rs232 serial port using a max232 with hardware uart on 16f877a.
The sensor has 4 pins. Two are VCC and GND and the others are trigger and echo pins. Trigger pin is taking to high with a minimum 10us pulse and echo pin gets the pulse back and counts until receiving the pulse. So I can formulate the distance. But since my trigger is going high and low with a 10/15us pulse, I can not get the distance via rs232 on the pc with a terminal emulator. I get garbage characters sometime and sometime I get nothing. Here is my code using ccs. I use C7 and C6 for serial uart with a 4Mhz external crystal while using the internal timer for counting issues. If you can help me on this I will be very pleased.
Code: |
#include <16F877A.h>
#FUSES XT,NOWDT, NOPUT, PROTECT, NOBROWNOUT, NOLVP, NOCPD, NOWRT, NODEBUG
#use delay(clock=4000000)
#use rs232 ( baud=9600, xmit=pin_C6, rcv=pin_C7, parity=N, stop=1 )
int16 distance, time; // Defining variables
// Defining the pins
#define trig pin_C0
#define echo pin_C1
void main()
{
delay_ms(1000); // Boot-up delay
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8); // initiating timer
while(true)
{
output_high(trig); // ping the sonar
delay_us(20); // sending 20us pulse
output_low(trig);
while(!input(ECHO)) // wait for high state of echo pin
{}
set_timer1(0); // setting timer zero
while(input(ECHO)) // Wait for high state of echo pin
{}
time=get_timer1(); // Getting the time
distance=time*0.028 + 1.093 ; // Calculating the distance
printf("\fTime :%Lu \nDistance = %Lu",time,distance); // Putting the time on RS232 PC
delay_ms(1000);
}
}
|
_________________ Umut Sonkurt |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Wed Jan 23, 2013 2:41 pm |
|
|
What is the frequency of the ultrasonic output ?
Have you looked at any waveforms on a 'scope?
Mike |
|
|
umutso
Joined: 23 Jan 2013 Posts: 39 Location: turkey
|
Pulse type |
Posted: Wed Jan 23, 2013 4:27 pm |
|
|
Hi mike
When triggered as high module sends 8 cycle burst of 40Hz sonic pulses. Cant check with scope that i have no scope. _________________ Umut Sonkurt |
|
|
umutso
Joined: 23 Jan 2013 Posts: 39 Location: turkey
|
|
Posted: Wed Jan 23, 2013 4:33 pm |
|
|
Actually module does the work automatically when triggered. And I'm using a module that works with a similar circuit displays the distance on an lcd. So i think there is something wrong with my rs232, max232.
İ also tried direct connection with the pic to rs232 on hw uart and it works well on isis simulation. But practically i can not get it work :( _________________ Umut Sonkurt |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
Re: Pulse type |
Posted: Wed Jan 23, 2013 4:45 pm |
|
|
umutso wrote: | Hi mike
When triggered as high module sends 8 cycle burst of 40Hz sonic pulses. Cant check with scope that i have no scope. | I'm assuming you really mean 40kHz and not 40Hz.
I don't want to sound harsh but:-
You need to use a 'scope so that you can SEE what you're dealing with.
Otherwise you're wasting your time and everyone elses.
Mike
EDIT Forget Proteus/ISIS. Search this forum to see what we all think of it.
Last edited by Mike Walne on Wed Jan 23, 2013 4:48 pm; edited 1 time in total |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Wed Jan 23, 2013 4:46 pm |
|
|
Basic trouble shooting...
step 1...
Start with your PC, terminal program and confirm that you can send/receive data using a 'loopback' connection.
Simply connect pins 2 and 3 of the PC serial port.Anything you type on the PC keyboard will be echoed(displayed) on the PC screen.Be sure to type all the letters and numbers on the keyboard just to confirm it works.
Use 9600-n-1- no handshaking .
step2...
Connect your MAX232 to the PC serial port,observing TXD-RXD, RXD-TXD and jumper the TTL side of the MAX232 together.Again try typing on the PC keyboard...the screen should display everything you typed.
step3...
Connect the MAX232 to the PIC and program it with the 'echo' program that CCS has in the FAQ section of the help files(press F11 with an open project).This prgram will send a 'B' when you press 'A' on the PC keyboard, '2' for a '1', etc.
step1...confirms that the PC serial port and terminal program are running properly.
step2...confirms the MAX232 hardware is good.
step3..confirms the PIC is configured and running correctly.
2 important items..
1) always add 'errors' to the use RS232(.....) options when using the internal HARDWARE UART. It'll keep the UART from 'stalling' or 'locking' up
2) never EVER trust Proteus! Please read PIC101.
hth
jay |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Jan 23, 2013 4:48 pm |
|
|
Here is the data sheet for the sensor:
http://elecfreaks.com/store/download/HC-SR04.pdf
The timing diagram is shown on page 2 of the data sheet.
Initially, all i/o pins are configured as inputs. So you need to
initialize the TRIG pin to a low level as shown in bold below.
Then it will be ready to go from a logic low to a logic high level,
when you make the TRIG pulse. Add the line shown in bold below,
in that exact position in the program:
Quote: | void main()
{
output_low(TRIG); // Initialize TRIG to inactive level
delay_ms(1000); // Boot-up delay
|
Quote: | I get garbage characters sometime and sometime I get nothing. |
Fix your serial connection first. Make a simple "Hello World" program
with the PIC and get the RS232 working correctly. Then proceed with
the HC-SR04 program. |
|
|
umutso
Joined: 23 Jan 2013 Posts: 39 Location: turkey
|
|
Posted: Wed Jan 23, 2013 4:48 pm |
|
|
Hi mike
Yes it's 40 KHz just typing mistake from mobile.
You are right. Will do it first thing tomorrow and post the result.
Thank you for prompt replies.
Br _________________ Umut Sonkurt |
|
|
umutso
Joined: 23 Jan 2013 Posts: 39 Location: turkey
|
|
Posted: Wed Jan 23, 2013 4:53 pm |
|
|
Actually when i put a printf like hello world string i can see it in my terminal. Things get defected after triggering the module. İ start to get asynchronous garbage characters. So serial port works fine. Just a separate hello world program is working fine. _________________ Umut Sonkurt |
|
|
umutso
Joined: 23 Jan 2013 Posts: 39 Location: turkey
|
|
Posted: Wed Jan 23, 2013 4:55 pm |
|
|
İ will test max232. Did not do that. Thank you for the tip. _________________ Umut Sonkurt |
|
|
umutso
Joined: 23 Jan 2013 Posts: 39 Location: turkey
|
|
Posted: Wed Jan 23, 2013 4:58 pm |
|
|
Did not think to get the trigger pin low first. Will apply this as well. _________________ Umut Sonkurt |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Jan 23, 2013 5:23 pm |
|
|
Make a test program that tests this line:
Code: | printf("\fTime :%Lu \nDistance = %Lu",time,distance); // Putting the time on RS232 PC
|
Comment out all code that talks to the sensor.
Load 'time' and 'distance' with nominal values. See if they print OK.
If it doesn't work then post your CCS compiler version. |
|
|
umutso
Joined: 23 Jan 2013 Posts: 39 Location: turkey
|
|
Posted: Thu Jan 24, 2013 3:25 am |
|
|
Dear All,
I checked with a simple program to send and receive data on MAX232 and it Works very well with 9600 baud both send & receive. The I simplified my program (below). When the program starts "Press a key to start..." comes ok, when I hit any key it again loops to "Press any key to start..." means the beginning. does this mean that the echo pin can not go high or something else?
help will be very appreciated.
Thanks in advance.
here is the code:
Code: | #include <16F877A.h>
#FUSES XT,NOWDT, NOPUT, PROTECT, NOBROWNOUT, NOLVP, NOCPD, NOWRT, NODEBUG
#use delay(clock=4000000)
#use rs232 ( baud=9600, xmit=pin_C6, rcv=pin_C7, parity=N, stop=1, errors )
int time;
#define trig pin_C0
#define echo pin_C1
void main()
{
delay_ms(1000);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
printf("\n\rPress a key to start...");
getch();
output_high(trig);
delay_us(15);
output_low(trig);
while(!(input_state(echo)));
set_timer1(0);
while((input_state(echo)));
time= get_timer1();
printf("\n\rReceive Time=%ius",time);
delay_ms(1000);
printf("\n\rFinished...");
getch();
} |
_________________ Umut Sonkurt |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Thu Jan 24, 2013 7:26 am |
|
|
2 comments...
1)I don't see how this program loops back to the 'press any key' again.
It should just execute once then 'drop off' and sleep after it says' finished...' and gets the 2nd key pressed. CCS puts a sleep command after main().
Normally program will look like this
Code: |
main() {
'put init stuff here'
while(true) {
do real stuff....
} //end of while loop
} //end of main
hidden sleep instruction
|
2) I also don't see why the printf 'finished...' doesn't appear on your PC screen !
Please use the 'code' tag to put the entire program into this thread, as there has to be 'something' funny going on.
Maybe I'm not seeing it right but it's what I see.
hth
jay |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Thu Jan 24, 2013 7:58 am |
|
|
Observations on your code...I really don't like to look at code since I know you will learn more if you discover your own errors.
All PIC programs need an infinite loop in main to stop them from falling asleep. EX while(true); as last line in main.
Here is my code it may give you some ideas.
If not at least you will have learned to avoid this approach.
The code starts with an attempt to describe the ultrasonic timing interface so the pic code can be seen to be married to the needs of the interface.
It doesn't describe the electrical interface the data sheet for the SR-04
is sufficient. If you haven't read it then you really really need to before writing another line of code.
If you are using Proteus to avoid having to read the data sheet and build a test board then have a Proteus board help you. The way you describe your issue could mean you are using Proteus since most don't see a real circuit looping the way you describe it. The code below should not be used with Proteus.
The code has a setup routine it also uses an isr routine to measure the delay and has a notation routine call to translate echo delay time into distance in inches.
Now this is code that will not by cutting and pasting be a solution for you
since I have cut it out of a larger program.
The isr provides a raw count representing time.
HC_SR04_RangeRaw() obtains this raw value in 2us units.
HC_SR04_RangeInches changes the raw value notation to inches.
Code: | ///// ultra sonic ranger
/// ________ ______________________
/// | Trig | | echo |
///_____| 10us |____________| 150us to 25 ms |____________
// 38ms is infinity ( no return)
// ^^^^^^^^
// |||||||| <-8 pulses at 40khz
////
//// <- pulse width ->
//// pulse width of us/148 is distance in inches
///// pulse width measurement using CCP
//1) Read the CCP, into ccp_val[flag].
//2) Test flag - if set, this is the second edge, goto 4
//3) Change the interrupt edge, and set the flag - exit
//4) Now the time is ccp_val[1]-ccp_val[0]. If this is -ve add 65536.
//5) clear flag, and exit.
#define HC_SR04_TRIGGER_PIN PIN_A1
#define HC_SR04_ECHO_PIN PIN_C2
#define FE 0
#define RE 1
unsigned int16 rise_cnt,fall_cnt,pulse_width_cnt;
int8 ccp_mode;
#int_CCP1
void CCP1_isr(void)
{
if (ccp_mode==RE){
rise_cnt = CCP_1;
ccp_mode=FE;
setup_ccp1(CCP_CAPTURE_FE);
}
else {
fall_cnt = CCP_1;
if (fall_cnt>=rise_cnt) pulse_width_cnt = fall_cnt - rise_cnt;
else pulse_width_cnt = 65536+fall_cnt - rise_cnt;
ccp_mode=RE;
setup_ccp1(CCP_CAPTURE_RE);
}
}
unsigned int16 HC_SR04_RangeRaw()
{
unsigned int16 value;
value=0;
pulse_width_cnt=0;
output_low(HC_SR04_TRIGGER_PIN);
delay_us(10);
set_timer1(0);
ccp_mode=RE;
setup_ccp1(CCP_CAPTURE_RE);
//// trigger measurement
output_high(HC_SR04_TRIGGER_PIN);
delay_us(10);
output_low(HC_SR04_TRIGGER_PIN);
delay_ms(40); //// allow CCP interrupt to see both edges of max pulse=38ms
//// pulse_width_cnt is in 2us units
value=pulse_width_cnt;
return value;
}
void HC_SR04_setup()
{
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);//start 2us per inc timer
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
}
int16 HC_SR04_RangeInches(unsigned int16 gRng_Raw)
{unsigned int16 inches;
inches=gRng_Raw/74L;
return inches;
} |
|
|
|
|