I wrote a program to run this sensor (Parallax PING))) ) for ports 1&2, and it worked fine, but I decided I would rather have them on ports 3-6 and have 1&2 for my encoders, so I re-wrote my code, and now it doesn’t work properly. I end up getting one high number when I first turn on the robot, then a steady number of 35 for the distance and 25 for the time. Here is my code:
I had several printf’s spread around in it to see if I was getting to all of the places in my code, thats what that phasetest is. My implementation of it in the default routine is:
I think you are not using the interrupt register properly. INTCONbits.RBIE enables the interrupts if equal to 1. It looks like you have that backwards in some places. To see if the interrupt occurred (in your ISR) check INTCONbits.RBIF, this will be 1 if an interrupt occurred and should be cleared before exiting the ISR. I do not see where you access RBIF at all.
Kevin’s encoder source is an excellent resource. Examine the functions where he enables the encoders and his ISR.
I have that interrupt routine function called inside the one that is written in the default code under user_routines_fast.c. Thats where I check the INCONbits.RBIF flag as well as the RBIE. Sorry I forgot to mention that. I do turn the actual interrupt off a lot so I do not get any bad data (from when I send the pulse) and each time I access it in Get_Distance() I turn it off, copy it, and turn it back on. I turn it off when I turn off the timer on the falling edge as well. I did see one where I have “//disable it” then I have it set to 1, but that shouldn’t have effected it. Although I don’t need to re-enable it in Get_Distance now that I think about it, because I re-enable it every time I call Ping(). But that shouldn’t effect it either.
I was looking at Kevin’s encoder code as a guide on how to get the interrupts 3-6 to work, and I have it almost how he has it.
I didn’t look at your code, but will remind you that interrupts 3-6 are change of state, and 1-2 are edge triggered (or something like that). The point is that any change on any pin in the 3-6 range will fire the same interrupt, where for 1 and 2 you get a different vector. I also remember something about cautionary tales about re-reading the status register. Maybe the real point is that I don’t remember how they are different but 3-6 behave differently than 1 and 2, so dig into the PIC datasheet for details.
Also note that the rise time of inputs greater than 6 is different than for the first 6.
I have my code distinguish if the PORTB changes from a 1 to a 0 or a 0 to a 1. That gives me an idea that I will look at when I get back to school Monday. Thanks for that picture too, thats interesting. Thank you.
Clearing and setting RBIE around critical regions of code is good practice. You are implementing mutual exclusion between the interrupt service context and normal context. But you should not have to do this in the ISR since all the interrupt sources result in the same ISR running.
Double check what clears the interrupts, reading the status registers can cause problems. I think 1-2 are edge triggered and have individual IE and IF bits. But remember that the RBIF and RBIE flags apply to all of port B thus to inputs 3, 4, 5 & 6 on the robot controller.
“Four of the PORTB pins (RB7:RB4) have an
interrupt-on-change feature. Only pins configured as
inputs can cause this interrupt to occur (i.e., any
RB7:RB4 pin configured as an output is excluded from
the interrupt-on-change comparison). The input pins (of
RB7:RB4) are compared with the old value latched on
the last read of PORTB. The “mismatch” outputs of
RB7:RB4 are ORed together to generate the RB Port
Change Interrupt with Flag bit, RBIF (INTCON<0>).”
This is the multiple read phenomena mentioned in this post. It is the difference between the last and current read of port B that creates the interrupt condition. So if you read port B anywhere you must process all possible bit changes at that time. So keep it simple, read port B in one place only (in the ISR) and process the changes for each bit just afterwards. If you need to know the state of port B, save the value in a variable for use in other places.
That and in my initialize are the only places that I look at the actual register
I tried two things so far, so I am thinking maybe its a problem where I am comparing the 1-0 or 0-1 change on each? Also from what you are saying, there can be multiple changes at once, saying that I should have a different variable for the different sensors because if two of them change, the last port checked will get the data back. Here is what is happening as of now. I have two variables that I have printing back in the ISR. One where it looks for a 1 and one where it looks for a 0.
I switched the if and else around and instead of getting 35 and 25 I get a much higher number. I guess that means that it is getting a fairly static time? I also unplugged the sensor while it was running and the values went to 0, so atleast my interrupts are firing somewhat correctly. The two variables move up by 2 at the same time if I have a fairly long delay between my ping() and getdistance() calls.
Thank you very much for your help so far!
Edit: When I changed sensors to make sure it worked my output value changed by about 20, I guess that means that it is timing some set thing about the sensors?
Hmmm… it is a little hard to follow. It is possible to miss an transition because you don’t check each bit every time you enter Ultrasonic_Interrupt_Routine but this is not likely to happen all that often if you are accessing one sensor at a time. This is the only thing I can see that is wrong for sure.
“I switched the if and else around and instead of getting 35 and 25 I get a much higher number.” The much higher number is measuring the time between pulses I reckon, that makes sense.
Do you have enough dynamic range (in the values of timer3) to do the measurement properly? What is the max sized pulse you expect to see (looks like timer3 will only give you 52.4ms) ? What is the minimum (800ns is the min for timer3) ? In what units in timer3 counting (800ns I assume)? Are you possibly calling GetDistance before the measurement is finished?
return ReturnValue * .34029;
phasetest2++;
The second line will never execute of course. And floating point on this processor is verrrrrrry slow.
I still don’t get the purpose of this. If GetDistance is called with an out of range sensor number, return an error code or something.
Lastly, are you doing a controlled test? Are you doing the test with the same shaped object the same distance from you every time?
I think I’d simplify this setup. You could let timer 3 run free all the time, increment a number in the rollover ISR and end up with a 32-bit timestamp (rollover count << 16 + TMR3H << 8 | TMR3L). Then you measure the pulses on all 4 channels all of the time w/o regard to when they were triggered. You might even Ping them regularly in some particular order. Do that all the time and have a global flag that you check when you need to see if anything is close to your robot. This would reduce the interaction between the ISR and your main code. Make sense?
The actual time I need is very very little. The spec sheet from parallax says ~19ms is the highest the pulse it gives. I had the sensors working 99% on ports 1&2, but I am now using something else on there. As far as the test goes, the number I recieve back is the same. Before I switched the if and the else I had 35 for distance and 25 for time and it stayed on that. When I switched it the value it stayed at around 876 or 875.
I could simplify it so that it saves to all 4 of the timers times all of the time instead of that one variable “Ultrasonic_Number” that I have
What numbers were you getting when using inputs 1 & 2? How and when are you using this in your code? Is timer 3 being used for anything else?
“As far as the test goes, the number I receive back is the same.” I don’t understand. The same as when using inputs 1 and 2?
“The spec sheet from parallax says ~19ms”. So the max count from timer 3 with a prescalar of 8 should be 24,000 or so. Is this what you get when nothing is in front of the sensor? The 35/25 numbers would indicate something is blocking the sensor. What does the data sheet say about the minimum pulse width?
I have been working on similar code for Ultrasonic sensors on port 3-6. I will post it once I get it working. I don’t see anything that is wrong with your code though.
The second line never executes and your function is declared like this:
unsigned int Get_Distance(unsigned char Sensor_Number)
You can’t return a float. I’m surprised the compiler did not issue a warning. This would never have worked no matter what input pin you were using. So the distance you are printing means nothing. But your Ultrasonic_3_Time variable should be changing. One more thing…
Leave your B-port interrupts on or reset the value Old_PortB just before the Ping. You can be incorporating all kinds of transitions the next time you read the port.
I had a quick look at your code and found what I think are a few errors. Have a look at the attached file for my comments (search on “RKW”).
-Kevin
Edit: It looks like you’re driving the sensor with a digital I/O pin and then using a separate interrupt pin to time the pulse. I had assumed you were doing both from one pin, so you can ignore my comment about the TRISB register.
No matter the distance of my object. I am only using the timer for this so its ok, I took out the code for 1&2. They gave me back a time, and a distance that was accurate to about .5mm or so. Yeah, I was getting something around 24000 when there was nothing. I used that floating point because my shift method wasn’t working. Thanks Kevin for pointing that out!
I am only using one sensor right now (sensor 3). I will make a few corrections right now, test it again, and re-upload my code to the same link.
Okay, its updated with a few minor tweaks. I still can’t get this though. More testing to be done…
Okay, I see your edit. I did have my code having only one digital io, but that was failing worse then this one is.
Did you get rid of the floating point? 1, it won’t fly unless you typecast around the block, and 2, floating point math takes a long while and is something you should probably never put in an ISR. You might instead try using fractional multiplication instead (multiply by numerator, divide by denominator).
Also, where you reset the timer after catching the first edge of the pulse to be timed, I don’t know if I’d reset the timer that way… perhaps you can instead zero the timer (with it off) when you send your triggering pulse, then when you catch the first edge just set the TMR3ON bit.
A lot of your other stuff looks fine to me… far as I can tell you’re close.
Again, I’ll be releasing my four channel single-pin simultaneous measurement ultrasonic code here in about 5 days… unfortunately have tons of code I have to write for our robot right now so I don’t really have time to clean up the (now messy looking) driver. Again, if you’re planning on using something like this, my driver utilizes TMR1, and of course the second half of PORTB (digio 3-6). I didn’t make it reconfigureable, but do with it what you want, just don’t repost it or anything… more on that later when I release it. Best part about being able to have four pings flying at the same time… we (1024) have a tested rig that gets 200Hz update guaranteed at nine feet… and amazingly the ping’s don’t get confused.