PDA

View Full Version : Problem with external interrupts (B0-B2) on 18f452



TDuman
- 26th July 2011, 17:04
Hi,
I am new to this forum, and am trying to find some wisdom on handling interrupts on 18f452. I am using timer 0 to trigger an interrupt once every second. That part is working fine. I am trying to detect and count pulses from a pulley that has 3 pulse generating sensors (Call them A, R, & B) I need to count pulses from R, While A & B determine direction. The timer will be used to calculate rate. My code appears to get the interrupts OK, but I am having difficulty determining which sensor it came from.

There are four magnets in the pulley so I should get 12 interrupts per revolution.
With the INTCON2 set to use the rising edge, I seem to get an interrupt every time there is a change in state on any of the three pins, thus 24 per revolution. For each sensor, most of the time, I get the appropriate int flag showing 0, while the other two show 1's, when the magnet goes off the sensor, it seems to generate another interrupt, with the values reversed. If this were consistent, I could use this and build on it. However, it isn't.

If I change the code to trigger on falling edges, I get something quite different. Then I get only 12 interrupts per revolution (as I would expect), However, the int flags are all 1's most of the time but not always. And it is again, inconsistent.

I'm fairly new to microchip programming with interrupts, so I don't know if my approach is flawed, I have something wrong in the code, or there is some hardware problem.

My main control registers are as follows:
trisb=%00000111
T0CON=%10000011
RCON.7=0
INTCON=%10110000
INTCON2=%10000000
INTCON3=%11011000

I would appreciate if someone could examine my code and tell me how to fix this.

The code is attached as PulseCounter.txt

My interrupt handler has some serial output to an LCD display so I can see the values of the int flags as they happen. I know that this will have to be removed in the final version

cncmachineguy
- 26th July 2011, 18:39
How fast are you spinning the wheel? I have not looked at the code yet, but it seems quite possible from your description the LCD part is taking too long. If you don't get out of the ISR before the next magnet you will have serious trouble.

cncmachineguy
- 26th July 2011, 18:55
OK, Looked at the code. By my very rough estimation, your ISR will take at least 28.125mSec to get through. That works out to around 3RPS or 180RPM assuming 12 interrupts per rev. For the purpose of debuging, How you tried to spin the wheel by hand and stopping between each magnet to see if the display shows the correct info?

Also it may be on interrupt messing you up. You may have better luck with DT_INT.

TDuman
- 26th July 2011, 18:56
Thanks for the response.
With this code, I am moving the wheel very slowly so I can see the interrupts and what values the intflags have. I know that when I actually start spinning the wheel quickly that portion will have to be taken out of the interrupt handler. There is a section of code in the mainloop to display the counters and elapsed time, that is diabled so that the display from the actual interrupt subroutine is long enough to be able to read it.
In the final version (assuming I can solve this problem, the max rpm will be 100-150 RPM

TDuman
- 26th July 2011, 20:00
Hi Bert,
Again, thanks for the reponse.
Does your estimated 28.125 MSec include the serial transmission to the LCD? If so what about without it, as it is only temporary to see what I'm getting. I am turning the wheel very, very slowly, just to see interrupts the intflag values.
Below is a table of the results for one revolution I am getting. The last column is the elapsed minutes.

Int # Int1Flg Int0Flg Int2Flg Count(A) Count(R) Count(B) Seconds
1 1 0 0 1 0 0 31
2 1 1 1 1 0 0 63
3 1 1 1 1 0 0 78
4 1 0 0 2 0 0 96
5 1 1 1 2 0 0 128
6 1 1 1 2 2 2 139
7 1 0 0 3 0 0 154
8 1 1 1 3 0 0 165
9 1 1 1 3 0 0 176
10 1 1 0 3 0 0 189
11 1 1 1 3 0 0 204
12 1 1 1 3 0 0 211

I plan to change timer0 to give me 10 or 100 counts per second, in order to get a more precise time with which to calculate rate. Should that work?
Forgive me if I plead ignorance of the DT_INT, can you enlighten me?
regards
Tom

cncmachineguy
- 26th July 2011, 22:50
Hi Tom, I hope you don't mind I am posting your code from the first post so its easier for folks to see. Plus it just makes it easier for me to look at your results and follow the code.


DEFINE LOADER_USED 1
DEFINE USE_LFSR 1
Include "modedefs.bas" 'this is required for RS232 comms
ADCON1=7
' Assign variable names
DBGDSPL VAR PORTB.5 'Debug display port
CurrPuls var byte
PulseA var word
PulseB var word
PulseR var word
LastACnt Var word
LastBCnt Var word
LastRCnt var word
CurrSec var word
LastSec var word
IntCount var word
pause 10
trisb=%00000111
T0CON = %10000011 'Define Timer0 - 1:16
RCON.7 = 0
INTCON = %10110000 'Enable TMR0 & Pin B0-B2 interrupts
INTCON2= %10000000 'Falling Edge
INTCON3= %11011000
goto start
'---------------------------------------------------------Interrupt Code
Disable ' No interrupts past this point
myint:
if INTCON.1<>0 or INTCON3.0<>0 or INTCON3.1<>0 then 'Pulse Interrupt
IntCount=IntCount+1
if INTCON3.0=1 and INTCON.1=0 and INTCON3.1=0 then PulseA =PulseA +1
if INTCON3.0=0 and INTCON.1=1 and INTCON3.1=0 then PulseR =PulseR +1
if INTCON3.0=0 and INTCON.1=0 and INTCON3.1=1 then PulseB =PulseB +1

SerOut DbgDspl, N9600, [$1b,$2a,$80] 'turn on background lighting of debug display
SerOut DbgDspl, N9600, [$1b,$30]
SerOut DbgDspl, N9600, ["I:",#IntCount, " A:", #INTCON3.0, " R:", #INTCON.1, " B:", #INTCON3.1]
SerOut DbgDspl, N9600, [$1b,$32]
SerOut DbgDspl, N9600, ["A:", #PulseA, "R:", #PulseR, "B:", #PulseB, "S:", #CurrSec]
INTCON.1=0 : INTCON3.0=0 : INTCON3.1=0
endif
if INTCON.2=1 then 'timer overflow interrupt
CurrSec = CurrSec +1
TMR0H=11 : TMR0L=186 'set tmr0 cycle count to 3002 so overflows in 1.0 sec
'TMR0H=231 : TMR0L=147 'set tmr0 cycle count to 59283 so overflows in .1 sec
'TMR0H=253 : TMR0L=143 'set tmr0 cycle count to 64911 so overflows in .01 sec
INTCON.2 = 0
endif
Resume ' Return to where interrupt occured
Enable
'---------------------------------------------------------End of Interrupt code
Start:
PulseA=0 : PulseB=0 : PulseR=0 : CurrSec=0 : LastSec=0 : IntCount=0
LastACnt=0 : LastRCnt=0 : LastBCnt=0
SerOut DbgDspl, N9600, [$1b,$2a,$80] 'turn on background lighting of debug display
SerOut DbgDspl, N9600, [$1b,$30]
SerOut DbgDspl, N9600, ["Start "]
SerOut DbgDspl, N9600, [$1b,$32]
pause 10

On Interrupt Goto myint ' Define interrupt handler
Mainloop:
if PulseR<>LastRCnt or PulseA<>LastACnt or PulseB<>LastBCnt or CurrSec<>LastSec then
' SerOut DbgDspl, N9600, [$1b,$2a,$80] 'turn on background lighting of debug display
' SerOut DbgDspl, N9600, [$1b,$30]
' SerOut DbgDspl, N9600, ["I:",#IntCount, " A:", #INTCON3.0, " R:", #INTCON.1, " B:", #INTCON3.1]
' SerOut DbgDspl, N9600, [$1b,$32]
' SerOut DbgDspl, N9600, ["A:", #PulseA, "R:", #PulseR, "B:", #PulseB, "S:", #CurrSec]
endif
pauseus 1
GOTO Mainloop
end


My time estimate is based soley on the serout stuff. I was just taking a guess at the number of bytes sent at 9600 baud.


As for DT_INT, search it on this forum and you will find tons of help. I am not so good at finding and posting the links or I would. Basically it is an awesome include to handle all your interrupt needs. It gives you true interrupts as if you had ASM interrupt, but without the hassle. With on interrupt, the problem is you are limited to only seeing the INT after each PBP instruction. So for code with a pause for instance, the interrupt won't fire until after the pause is done.

cncmachineguy
- 26th July 2011, 23:00
Tom, for some reason, I am having trouble pisturing you magnet/sensor setup. Is it possible for the magnet to fire the int, but none of the 3 IF conditions are met? Is there anyway you can make a quick sketch showing the relationship of the magnets sensors, and pully? Doesn't need to be fancy, just a visual. Or even a picture might do it.

TDuman
- 26th July 2011, 23:24
Hi Bert,
No, I don't mind.
As a newcomer, I was hesitant to put too much in the post.
I hope you can find whatever flaw I may have. I did a google search for DT_INT. I had seen it before when I was scouring the i-net for articles relating to my problem. I had taken note of it and saved a link for possible future use. It may improve my code and cpu time, but at this point, do see a flaw in the existing approach I am taking? Should this work?
Regards
Tom
PS: I see your next post requesting sketch as I am posting this reply, I'll post a sketch in my next reply

TDuman
- 26th July 2011, 23:53
Hi Bert,
Attached is sketch I drew in Paint.
Regards
Tom

cncmachineguy
- 27th July 2011, 01:18
That helps a ton.


so I don't know if my approach is flawed, I have something wrong in the code, or there is some hardware problem.


It may be a little of each. But first lets attack the hardware. I don't think you need the "R" signal. you can get the counts from the A or B. Now, the reason I want to drop it, besides making the code a little simpler, is I fear you may have some overlap in the fields. This could be why you get the wierd stuff with rising or falling edge trigger. Do you have access to a scope to actually watch B and R to see in B dies before R rises?

TDuman
- 4th August 2011, 19:26
Hi Bert,
I could have sworn that I replied to your last post last week.
I fact, I was anxiously awaiting your response and was checking 2 or three times a day, but never got one.
Then on Friday, I checked this forum again, and my reply was not there. (Maybe it was never there, but I would have sworn that I did see it)
Anyway, since there was no response, I figured you lost interest.
My respose to your last post July 26 was the following (as best as I can remember):

We have put the contraption on a scope and can cleanly see that the pulse finishes before the next one starts. The voltage on all pins is 5 volts and drops to zero when the magnet passes the sensor.
And besides, I am turning the wheel very, very slowly, to watch what I get in the interrupt registers.
I believe that I do need all three signals to tell which direction the wheel is turning. With only A & B, the sequence of signal would always be A-B-A-B-A-B, now matter which direction the wheel is traveling.
With three signals A,B, & R, The sequence would be A-B-R-A-B-R-A-B-R going in one direction, but A-R-B-A-R-B-A-R-B in the reverse direction.

We have an older version of this same contraption, that seems to work perfectly. However, I can't get my hands on the source code to see how it was done. That model was before my time, years ago.

regards
Tom

cncmachineguy
- 4th August 2011, 23:29
I usually never lose intrest in this type stuff. I have come to notice more often then not the OP will either resolve their problem or give up without ever posting about it. Thanks for checking again, I am sorry your post was lost. ANYWAY,

I will have to go back and look at the code again. I must admit I don't remember how I thought you could lose the R, I think I must have been thinking the A & B magnet fields would crossover and be more like a encoder output.

But now we at least know for sure it is not the hardware, so that variable is out of the question. I need to re=read the thread but will post something in a little bit.

cncmachineguy
- 5th August 2011, 00:29
Alright first things first. If it were me, I would put the timer check first, that way if an interrupt occurs, and it is not the timer, it MUST be a portb interrupt. That way you can lose the big 3 way or check.

Next up on my chopping block are the 3 IF's. is there any way for more then 1 int flag to be high at the same time? and if there is, you will do nothing in the ISR because of the AND checks. I would think you will have the correct results by simply checking each flag, 1 at a time and doing whatever there. These 2 things may not improve your current problem, but will certainly have bearing when that wheel gets going.

Now last, I realize the LCD part is just while debugging, but put it before the b port checks. this way you will do the "long" time stuff, then inc the counts based on int flags, then clear the flags. As it now, there is a possibility the int gets called for "b", by the time you get to checking, R flag is now set also. both counts should get updated but they won't. then you clear all 3 flags, so 1 could get missed.

Thats all for now, See if any of this helps.

mackrackit
- 5th August 2011, 01:40
I have not studied the problem but I will put in a couple of things.
1- ON INTERRUPT is not the best for time sensitive operations, ASM types are what you need. This is because ON INTs will not happen instantaneously, they will wait for other things to finish.

2- No matter what type of interrupt you use, you will want to get in and out of the ISR as quick as possible. Set a flag or set a AR while in the ISR and act on that in the main routine.

ASM interrupts are a bit difficult at times so Darrel gave us Instant Ints.
http://www.picbasic.co.uk/forum/showthread.php?t=3251&highlight=instant+interrupts

TDuman
- 8th December 2011, 00:42
All of the responses I have received are very much appreciated.
I have gotten past accounting for the pulses, and I have been able to measure time and distance accurately (so it seems) thanks to the help from you all and from the engineers at my client.
We are getting ready to ship the first one, but now I have another problem that has been dogging me intermitently throughout this development project.
The system measures time and revolutions of a pulley that is lowering a device on a cable. The main display (two 7-segment LEDs) shows distance-out on one display bank, and the rate at which it is going out or returning on the other display bank. Whenever the system is reset, it increments an event counter (cast#), and resets the counters and elapsed time to zero. Intermittently, it will reset on it's own. As part of the start-up code it increments the cast# and resets all counters to zero. Since that is what seems to be happening, I conclude that it is actually restarting from the beginning. As an example, I started a test two days ago. It ran for about 10 minutes and reset, then ran for about 5 minutes and reset again. But since then it has been running for about 52 hours without a problem.
I have thought that perhaps this is being triggered from a power problem, so I burned the chip with the "brown-out reset" feature disabled. That seems to improve it somewhat but not eliminate the problem altogether.
I am looking for advice as to what (programming or hardware) would trigger this.

I also have another problem.
The main display, as I mentioned, is a pair of 7-segment LEDs (two banks of four) . But I also have a serial LCD (2 lines of 16 characters) that I use for debugging. The system also transmits logging data to a PC via a comm port. In the code, a variable (DEBUGMODE) is set to either zero of 1 to control which display to use (either the LEDs or the debug display, but not both.
During the current test (and one time in a prior test) the LED display froze (distance and rate not changing), but I could tell that the system was still running from the data coming into the PC, (distance, rate, and elapsed time). The debug mode was set off so there should not have been anything on the debug display. But on a hunch, I connected it while the system was wtill running without interrupting it. Sure enough, it is displaying data as if it is in debug mode (accounting for why the the main LED displays were not updating). I conclude from this that my DEBUGMODE variable somehow got changed. It is set only once at the beginning of the program, and then is only used in IF/THEN statements. So what can cause this to happen? I can program around this problem, but my concern is why is it happening, and what else could be happening that I don't know about?

TDuman

mackrackit
- 8th December 2011, 01:39
The reset problem does sound like a power problem. Could be something nearby creating noise. Dirty ground, other motors, welders...

But it could be a code problem also. You think that if it works correctly once it should work correctly every time. Not always once a project is off the bench.
The display problem sorta points to code. The code not working in the environment.

Show you code and tell about the running environment and we might be able to help you solve it.