CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Only works once. . .

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Core2



Joined: 27 Sep 2008
Posts: 22

View user's profile Send private message

Only works once. . .
PostPosted: Wed Jan 20, 2010 11:34 am     Reply with quote

Hello everyone,

I need a little help as I don't understand why it only works correctly ONCE, when the program first starts. In general, it signals the sonar unit (SensComp 6500) to start the process, start Timer1, grab the Timer1 count when the echo line goes HIGH (connected to RB5 as an IOC pin), then calculate the distance and send it out serial port.

It works GREAT for the first iteration. The Timer1 count is~6600, which is correct for 35", and displays 35". But on every iteration after that, the count is~40.

A little incite as to what is going on. About 16uS after the INITialize line goes high, the transducer sends out (16) 50KHz pulses. Unfoutunately you can see these pulses on the ECHO line; they are about 1.4V in amplitude and last for about 340uS.

Initially, the first pulse, on the ECHO line was setting off the RB5 IOC and I was ALWAYS getting a count of ~40. Once I realized what was going on, I changed the Sonar function to:
1) Start the sonar procedure
2) Start Timer1
3) delay_us(350) - to get past the 16 pulses
4) Clear the INT_RB5 flag
5) enable RB5 IOC interrupt.

Thing are getting better!! The first reading is correct and accurate. After that the RB5 IOC is back to tripping on the first transducer spike. I thought that would be skipped as in the first reading. Here is where I'm confused as to what is going on?!?!?

Compiler version is 4.093.

Code:
// This program will fire off the SenseComp Sonar system, detect an echo, and
// calculate the distance.  Timer1 is setup with 400nS 'ticks'(20MHz/4 = 5MHz.
// 1/5MHz = 200nS period.  T1 is setup as a 'Divide by 2' counter, making it
// count in 400nS intervals.)  T1 is a 16-bit counter and will roll over every
// 65356 'ticks'.  This equates to 65536*400nS = 26.21mS.  T1 will 'timeout'
// if the object is more than 29 feet away.

#include <16F882.h>
#device *=16
#fuses HS NOWDT PUT NOPROTECT NOCPD NOBROWNOUT NOIESO NOLVP NOWRT
#use delay (clock=20M)
#use rs232(baud=115200,xmit=PIN_C6, rcv=PIN_C7) //115K for Bluetooth
#use fast_io(B)
#include <stdio.h>

#define TRIGGER PIN_A2

int16 range, time;
boolean trip, echo_rcvd, noEcho;


#int_rb
void getRange(void)
{
   disable_interrupts(GLOBAL);
   if(trip)
      {
         range=get_timer1();           //Get the time
         setup_timer_1(T1_DISABLED);    //Stop the clock
         trip = FALSE;                  //skip the H to L transition INT_RB5
         echo_rcvd = TRUE;             //Got one
      clear_interrupt(INT_RB5);     // Clear the Interrupt flag.
      enable_interrupts (INT_RB5);  // enable interrupt_on_change for pin B5 only         
      }
   else
      {
         trip = TRUE;
         echo_rcvd = FALSE;
         setup_timer_1(T1_DISABLED);    //Stop the clock
      }
   enable_interrupts(GLOBAL);    //Enable all interrupts
   
   
}

#int_timer1
void lostEcho(void)
{
   setup_timer_1(T1_DISABLED);
   set_timer1(0); //Reset the timer to 0
   echo_rcvd = TRUE;
   noEcho = TRUE; //No echo returned
   output_low(TRIGGER);
//   printf("Timer1\n\r");  //Just a test to see if I'm timimg out
   clear_interrupt(INT_TIMER1);
   enable_interrupts(INT_TIMER1);
}

void getSonar (void)
{
   set_timer1(0);          //Set Timer1 to an initial value of 0
   output_high(TRIGGER);   //Initiate the Sonar sequence
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_2); //Start counting, 0.4uS tick time
   delay_us(350);          //Skip past the 16 sonar pulses
   clear_interrupt(INT_RB5);     //Clear the INT_RB5 flag
   enable_interrupts (INT_RB5);  // enable interrupt_on_change for pin B5 only
//   time=get_timer1();    //get a sample of the Timer1
//   printf("Timer1 count = %lu\n\r",time);  //Just to see if Timer1 is running
   while (!echo_rcvd) {}   //Wait for echo
   printf("IOC_Timer1 count = %lu\n\r",range);  //See what the count is
   if(!noEcho)
      printf("Range = %lu inches\n\r",range/185);  //Print range
   else
      printf("No echo returned\n\r");
   output_low(TRIGGER);
   
   
}


void main(void)
{
// Variables   
   trip = TRUE;
   echo_rcvd = FALSE;
   noEcho = FALSE;
// setup interrupts
   enable_interrupts(INT_TIMER1);//Enable TImer1 interrupt
   enable_interrupts (GLOBAL);
   printf("Starting. . . \n\r");

   while (TRUE)
   {
      getSonar();    //Get sonar1 reading
      echo_rcvd = FALSE;
      delay_ms(5000);
   }
}


Thanks for looking at it.
Duane
Core2



Joined: 27 Sep 2008
Posts: 22

View user's profile Send private message

PostPosted: Wed Jan 20, 2010 11:35 am     Reply with quote

And YES, I did get the IOC part working before moving on to connecting the sonar module.

Duane
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Jan 20, 2010 1:45 pm     Reply with quote

Quote:
#int_rb
void getRange(void)
{
disable_interrupts(GLOBAL);
if(trip)
{
range=get_timer1(); //Get the time
setup_timer_1(T1_DISABLED); //Stop the clock
trip = FALSE; //skip the H to L transition INT_RB5
echo_rcvd = TRUE; //Got one
clear_interrupt(INT_RB5); // Clear the Interrupt flag.
enable_interrupts (INT_RB5); // enable interrupt_on_change for pin B5 only
}
else
{
trip = TRUE;
echo_rcvd = FALSE;
setup_timer_1(T1_DISABLED); //Stop the clock
}
enable_interrupts(GLOBAL); //Enable all interrupts


}

This routine about has a few things wrong with it.

1. Don't ever enable/disable global interrupts inside an interrupt service
routine. This can cause the program to crash, and it's unnecessary. It's
handled automatically by the PIC when it executes the interrupt, and
when it exits the interrupt. Delete those two lines.

2. The compiler automatically puts in a line of code to clear the INT_RB
interrupt flag. You don't need to do it yourself inside the routine.
You also don't need to enable it again, inside the interrupt routine,
since you alread enabled it in getSonar().

3. You must read port B when inside the #int_rb routine to clear
the "mismatch" condition that caused the interrupt. See the example
further down in this post. Also add that code to the getRange() routine
above. This "mismatch" condition is described in the PIC data sheet,
in the section on PortB interrupts.

4. In your getSonar() routine below, you should read port B, to clear
any initial "mismatch" condition, before you clear and enable interrupts
for RB5. The "mismatch" condition is what causes the interrupt flag
to become set. If your intention is to look for new RB5 interrupts
after enabling them, then it's important to clear any pre-existing
interrupts first. You can do this by adding the lines shown in bold:
Quote:
void getSonar (void)
{
char c;
set_timer1(0); //Set Timer1 to an initial value of 0
output_high(TRIGGER); //Initiate the Sonar sequence
setup_timer_1(T1_INTERNAL|T1_DIV_BY_2); //Start counting, 0.4uS tick time
delay_us(350); //Skip past the 16 sonar pulses

c = input_b();
clear_interrupt(INT_RB5); //Clear the INT_RB5 flag
enable_interrupts (INT_RB5); // enable interrupt_on_change for


Also, I notice you're using #fast_io mode for Port B, but you don't ever
set the TRIS. It comes up set as all inputs by default, upon power-on
reset of the PIC, so it will work anyway. But I'm mentioning it just to
make you aware that if you use #fast_io, you are responsible for
determining and setting the correct TRIS.


I didn't look at your program design. I only scanned it for CCS or PIC
related bugs. Also, I didn't look at your whole program. There might
be other problems.
Core2



Joined: 27 Sep 2008
Posts: 22

View user's profile Send private message

PostPosted: Wed Jan 20, 2010 5:14 pm     Reply with quote

PCM,

I'd like to think that you're getting some king of compensation for the NUMEROUS people you've helped over the years! You're definitely a GREAT asset to this forum!

I'll make the changes you've noted, and all will work well. It was point #3 that turned the light on for me. I know to do this, just brain-farted on coding it.

Thanks for the reply!

Have a great day,
Duane
Core2



Joined: 27 Sep 2008
Posts: 22

View user's profile Send private message

PostPosted: Wed Jan 20, 2010 10:53 pm     Reply with quote

Well, I thought for sure that reading port B (c = input_b() ) was the fix to my problem. Unfortunately, it didn't. Now I'm even more lost than before.

Its like a flag isn't getting cleared, OR set, that needs to be. . . I just don't see it!!!

The changes PCM noted were made.

Anybody have a clue?

Thanks,
Duane
Wayne_



Joined: 10 Oct 2007
Posts: 681

View user's profile Send private message

PostPosted: Thu Jan 21, 2010 3:27 am     Reply with quote

Code:

void getSonar (void)
{
   set_timer1(0);          //Set Timer1 to an initial value of 0
   output_high(TRIGGER);   //Initiate the Sonar sequence
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_2); //Start counting, 0.4uS tick time
   delay_us(350);          //Skip past the 16 sonar pulses
   clear_interrupt(INT_RB5);     //Clear the INT_RB5 flag
   enable_interrupts (INT_RB5);  // enable interrupt_on_change for pin B5 only
//   time=get_timer1();    //get a sample of the Timer1
//   printf("Timer1 count = %lu\n\r",time);  //Just to see if Timer1 is running
   while (!echo_rcvd) {}   //Wait for echo
   printf("IOC_Timer1 count = %lu\n\r",range);  //See what the count is
   if(!noEcho)
      printf("Range = %lu inches\n\r",range/185);  //Print range
   else
      printf("No echo returned\n\r");
   output_low(TRIGGER);
   
   
}


Your first run through you don't set timer_1 until after you have cleared it. What are the default settings for the timer ?
The second time round it will be set correctly, this is probably why you get the incorrect first reading!

I would put
setup_timer_1(T1_INTERNAL|T1_DIV_BY_2);
in main before enabling your interrupts and just enable/disable the timers interrupt as and when you need to. Remove all other setup_timer lines.

So you would then use
set_timer1(0);
enable_interrupts(timer1);

And disable_interrupts(TIMER1); in your interrupt routines.

There is also some redundant code in your program.
Core2



Joined: 27 Sep 2008
Posts: 22

View user's profile Send private message

PostPosted: Thu Jan 21, 2010 10:06 am     Reply with quote

Wayne_ wrote:
Your first run through you don't set timer_1 until after you have cleared it. What are the default settings for the timer ?
The second time round it will be set correctly, this is probably why you get the incorrect first reading!



Hey Wayne, thanks for your reply. I'll make the changes when I get home. Just to correct something, I get a correct reading the first time, and subsequent reading are wrong.

Its like the program is skipping the 350uS delay that was put in to skip over the 16 pulse transmission spikes.

Thanks,
Duane
rnielsen



Joined: 23 Sep 2003
Posts: 852
Location: Utah

View user's profile Send private message

PostPosted: Thu Jan 21, 2010 12:48 pm     Reply with quote

When you use the command set_timer1(0) it takes several cycles to accomplish this and will _always_ cause your timing sequence to be out. It is better to allow a timer to free run and use it as an interrupt to accomplish your timing. You'll need to calculate what the pre/post registers should be set to and then increment a variable, inside the ISR, that sets a flag.

Ronald
Wayne_



Joined: 10 Oct 2007
Posts: 681

View user's profile Send private message

PostPosted: Fri Jan 22, 2010 2:45 am     Reply with quote

Core2 wrote:
Wayne_ wrote:
Your first run through you don't set timer_1 until after you have cleared it. What are the default settings for the timer ?
The second time round it will be set correctly, this is probably why you get the incorrect first reading!



Hey Wayne, thanks for your reply. I'll make the changes when I get home. Just to correct something, I get a correct reading the first time, and subsequent reading are wrong.

Its like the program is skipping the 350uS delay that was put in to skip over the 16 pulse transmission spikes.

Thanks,
Duane


I know you said that the first timing was correct but it is the first one which is different! As all the rest are the same I would consider that it is the first one which is wrong and there is something you are missing with regards to what you are expecting.

How do you know the Timer1 count shuold be ~6600
Core2



Joined: 27 Sep 2008
Posts: 22

View user's profile Send private message

PostPosted: Fri Jan 22, 2010 1:15 pm     Reply with quote

Wayne,

I see what you are saying now. I got a hold of a friend who has WAY more experience with programming PICs than I do. He pointed out what I was doing wrong and set me straight. A lot of what he explained was what you guys were talking about.

Thanks for ALL your help!


Sound travels, on average, 1125ft/s or 13500 in/s. 1/13500 is~74uS. 35" x 74uS = ~2.6mS 2.6mS / 400ns [ T1 = Fosc/4, 20MHz/4 = 5MHz, 1/5MHz = 200nS, if T1 is setup as DIV_2, then a T1 'tick' = 400nS] = 6500 ticks.

Duane
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group