hserout, settimer, and interrupts - do they clash ?

Buzby

Senior Member
Hi All,

My switch capture project is up again, and I'm at the stage where I'm trying to get data out of the PICaxe into a PC, (to be replaced eventually by another PICaxe. )

The attached code works fine with hserout commented out, but not when hserout is active.

In the first case I can create lots of rapid changes on PortC, and pinB.4 indicates each change.

But when the hserout is active the interrupt code locks up for about a minute if I make even just a few changes to PortC. I know that the Main_loop code remains running throughout this lock-up, because the main loop activity indicator is still flashing.

Can anyone give me any ideas what I should do next ?.

( I've got a freq meter, some multimeters, an un-calibrated dual trace scope, and a sore head ! )
 

Attachments

hippy

Technical Support
Staff member
It seems to be a case of simply running the timer too fast. By the time the interrupt routine has handled an interrupt there's another interrupt which it has to handle. This means that the main loop code slows to a crawl when it does get a chance to execute.

This runs but increasing the SetTimer doesn't ( not sure at exactly what point ) ...

#Picaxe 20X2
#NO_TABLE
#NO_DATA

SetFreq M64

SetTimer 60000 ' 65289
Gosub Int_Setup

HSerSetup B2400_64, 0

Do
If w0 <> 0 then
HSerOut 0,(10)
w0 = 0
Toggle B.0
End if
Loop

Interrupt:

w0 = 1

Int_Setup:

timer = $FFFF
toFlag = 0
SetIntFlags %10000000,%10000000
Return
 

Buzby

Senior Member
Hi Hippy,

The 'timer too fast' idea sounded plausible, but I don't think it's that.

I knew the interrupt was triggering every 1mS because the frequency on b.0 was 500Hz, but I didn't know how long the interrupt handler took. So I toggled b.0 twice, once at the start and again at the end of the interrupt handler. Looking at b.0 on a scope I estimate that the interrupt routine takes about 800uS, which leaves a 200uS headway, which should be plenty time to restore the main loop.

I cut down the code to remove the scratchpad and ptr code until I'm happy with the timing. I also added some dummy code to pad out the if/then execution times. I also put some delays around the hserout instruction, and this last change seemed to make everything work OK.

But then it got more confusing !.

If you can run the attached code on a 20X2 you might see the behaviour I'm getting now. The minimum hardware is a 20X2 with LEDs on b.1 and b.2.

Just start it running, don't do anything, just watch.

b.1 should flash about 70 per minute, controlled by the main loop.
b.2 flashes exactly 60 per minute, controlled by the interrupt/1000.

After about 120 seconds b.1 starts flashing very fast, and b.2 stops completley. This indicates that the interrupt is not happening, so b.2 doesn't flash, and the main loop now goes at full speed, so b.1 flashes very fast.

After about a minute the LEDs go back to flashing as they should, then 120 seconds later repeats as above.

I don't think this is a 'timer too fast' issue. If the interrupts occur faster than the handler can cope, then the system should crash on the second interrupt after start up, not two minutes later.

I've got a few other ideas to try, but it's too late tonight !.
 

Attachments

hippy

Technical Support
Staff member
Interesting; I can replicate the behaviour but cannot explain it.

I don't think it's HSEROUT to blame as adding "pinsstate=0" after "pinstate = pinsC & %11110000" means HSEROUT should never be executed ( ptr_In never increments so ptr_In always equals ptr_Out ) but it crashes relatively quickly.

Remove both HSERSETUP and HSEROUT, same crash.

There's apparently some timing issue and that will need to be investigated.
 
Last edited:

Buzby

Senior Member
Hi Hippy,

I've not had a chance to do any more work on this today, but I have put a lot of thought into it.

A confusing part of the issue is presuming that the interrupts always occur every 1mS. This is only true if the handler does its work OK every time.

If the handler misses an interrupt then the timer will carry on counting, through FFFF, 0000, 0001 etc upto the next FFFF, which will cause another interrupt. This will take 65536 counts, each of 1mS, i.e just over 1 minute. This explains the duration of the 'crash'.

Assuming this delayed interrupt is handled correctly, the system will return to normal, but one 1mS interval will have been stretched to 65 seconds.

The above explains what happens during the crash, but doesn't explain why it only happens every two minutes. If the handler is always too slow then the code will crash at every interrupt, and no 500Hz would ever be generated. If the handler is always fast enough then there will never be a crash.

Only if the handler sometimes takes too long would the observed behaviour occur.

All timings in the PICaxe are generated from the resonator through various divider chains. This means all CPU actions, including interrupts triggered by internal counts, have fixed timing relationships with each other. I cannot see how a fixed piece of code could take different times to execute, unless there is an external influence.

I will try with the download cable disconnected, there may be something happening there.

Also, I will try setfreq m32. This will slow evertyhing by half, but all relative timings will be unchanged. If the problem is due to some internal count the crash should occur after 4 mins, not 2.

It will be a day or two before I can test these ideas.

Cheers,

Buzby
 

hippy

Technical Support
Staff member
Having looked further at the issue it does still seem to come down to the high rate of interrupting but needs further investigating. The following changes seem to correct things but have not been exhaustively tested ...

1) Add "return" so the interrupt doesn't drop through to "int_setup".
2) Add "setintflags %10000000,%10000000" before that "return".
3) Add "timer = 0xffff" then "toflag = 0" immediately on entering the interrupt.
4) Delete the "setintflags off" on entry to interrupt.
 

Attachments

Buzby

Senior Member
Hi Hippy,

I've got it working, so here are my findings.

First, I think the original interrupt handler I wrote was just at the limit of available execution time. As I explained earlier the only way it could crash after many thousands of interrupts was if the handler sometimes took a variable time to execute. The clue was that it crashed after exactly two minutes, i.e. the second time the 59999 comparison was 'true'. Changing the value to 5999 made the crash happen after 12 seconds, i.e two cycles of 6 seconds. In both cases the only difference on the second cycle was that the value of 'overflow' flag was already at '1' when the 'overflow = 1' line executed. It looks as if the execution time of this equation differs depending on the value on the left-hand side. Changing it to write a '0', restored the expected behaviour. ( But not much use writing '0' when I needed a '1' ! )

Second, hserout seems to take more time at lower baud rates. I expected that this operation would not be dependant on the speed of the hardware UART, but it seems like it is. The original code was at 1200 baud, so the length of time hserout used was longer than 1mS, and because PICaxe's poll for interrupts between instructions the interrupt was picked up too late.

The attached file has a dramatically shortened interrupt handler, and a very high baud rate. It can now reliably capture, timestamp, and transmit switch states at a continuous rate of over 130 per second.

My next task is to program another PICaxe to generate short bursts of faster pulses to simulate the switches, and see how it performs then.

I am still confident that a PICaxe can do the job.

Thanks for your help,

Buzby.
 

Attachments

hippy

Technical Support
Staff member
Second, hserout seems to take more time at lower baud rates. I expected that this operation would not be dependant on the speed of the hardware UART, but it seems like it is.
The HSEROUT execution should be time independent of baud rate, however there will be some check that new data can be placed in the UART so frequently executing HSEROUT's can slow down depending upon baud rate.

This can be demonstrated with the following code -

#Picaxe 20X2
#No_Table
#Terminal 9600

Symbol TXSTA = $AC

Pause 2000
HSerSetup B115200_8, %000
Do
HSerOut 0,("U")
PeekSfr TXSTA, b0
If bit1 = 0 Then : SerTxd( "Busy", CR, LF ) : End If
Pause 1000
Loop

At 115200 baud, the data has been sent by the time the PEEKSFR is executed to check if the transmit buffer is empty so "Busy" is never reported.

With 300 baud, the HSEROUT returns and PEEKSFR executes while still transmitting the byte so the transmit buffer is not empty and "Busy" will be reported every time.

For ...

HSerOut 0,( "A" )
HSerOut 0,( "B" )

That will run as quickly as possible at 115200 baud but at 300 baud there will be a 3ms or so delay before "B" can be sent because the second HSEROUT waits - not for the previous command to finish executing - but within itself until the transmit buffer becomes empty.
 
Last edited:

Buzby

Senior Member
That makes a lot more sense, and explains why I had some minor success when I put a delay after the hserout.

Does this mean that hserout 0, ("123456") running at a low baud rate will effectivley lock-up the PICaxe for the time it takes to send the first five characters ?
 

hippy

Technical Support
Staff member
Yes, that's correct. I haven't tested it but this will likely toggle the <pin> every 19ms or so -

Code:
HserSetup B300_8, %000
Do
  HserOut 0, ( "123456" )
  Toggle <pin>
Loop
This will toggle the <pin> much quicker but there will be slightly longer gaps between chacaters sent -

Code:
HserSetup B300_8, %000
Do
  PeekSfr TXSTA, b0
  If bit1 = 1 Then
    b1 = b1 + 1 // 6
    LookUp b1, ( "123456" ), b2
    HserOut 0, ( b2 )
  End If
  Toggle <pin>
Loop
 

LarryGrad

Member
Buzby, thanks for alerting me to this particular thread. I have not worked with the larger Picaxe chips, and now see the power of having ports you can address. I have some 20x2's and 28x2's on the way. I do think this will work nicely for my bike computer. I need to measure pulses for heart rate from my Polar strap using this module http://www.sparkfun.com/datasheets/Wireless/General/RMCM01.pdf , as well as the usual speed and cadence using reed switches. The GPS and accelerometer inputs are handled by the FPU, so I don't need to worry about that. The Picaxe gathers the data from the FPU, and puts out to a 4D uOLED.

Question: Do you have a feel for the actual speed of the polling? I am thinking it is very close to real-time and I do not have to worry much about the pulse length on the input pin. The Polar puts out a 6ms pulse. I have yet to measure the action of the reed switches at higher speeds. I need to sneak my bike up the stairs to my "lab" and get the logic analyzer to measure the pulse at high speed (any volunteers to get the bike up to 50MPH on the trainer while I take the measurement?)
 

Buzby

Senior Member
Hi Larry,

My requirement is to measure the intervals between four switches, all of which change state at approximatley the same time. They do this every few seconds, but within a few mS of each other.

The interrupt code takes a snapshot of the switches every 1mS, looks for changes, then puts the results in a buffer. It does this exactly 1000 times per second.

The main loop code unloads the buffer and, in the example here, sends the data out through a serial port.

It is the difference between the rates of filling and emptying the buffer that limits the processing of a continuously changing input. In the code here I can process continuous inputs at about 130 per second. If

But continuous inputs are not what I, or you, need.

We both need to time intervals. My intervals are very short, and I need the relative times between the four switches. You are timing single 'switches' giving intervals between heartbeats or wheel rotations, so I don't know if my code is really what you need. It may be a bit of overkill.

This interrupt code is very good at rapidly capturing multiple switches, but it has a down side. The large amount of time spent by the CPU doing interrupts means that the main loop code runs at a fraction of it's 'normal' speed. That is why I'm outputting the serial data, so I can do all my other work in a seperate CPU.

Reading the datasheet seems to indicate that your unit outputs a 1mS pulse every heartbeat, which I presume won't happen more than twice a second.

I would handle this scenario in a different way. Instead of a software interrupt I would configure a hardware interrupt and a background timer. The interrupt would trigger each heartbeat and read the value of the background timer. This method would let the main loop code run at full speed, and give you timings accurate to the resolution of the background timer.

If you need to bring in the reed switches as well, then set up a different interrupt mask to use more inputs to trigger the interrupt. ( The 20X2 only has one hardware interrupt, so you may be able to use a diode mixer to get the others in. I have a few doubts about this, so don't take my word as gospel !. )

Another even simpler option maybe to use PULSIN to directly measure the interval between the 1mS pulses, but I've not used this command so I don't know how it behaves in real life.

Your project sounds cool !. Keep us informed how you get on.

Cheers,

Buzby
 
Last edited:

hippy

Technical Support
Staff member
I don't think frequent interrupts are what you really require in the case of measuring a heart-rate pulse; that's having numerous interrupts which poll the pulse to determine the timing where it would be better to interrupt on pulse, read time between pulses and set the timer going again.

With heart-rate pulse having likely intervals between 1000ms ( 60 BPM ) and 250ms ( 240 BPM ) interrupting at those rates would have a much lower impact on other operations of the PICAXE and may even be suited to simple polling rather than interrupts.
 

LarryGrad

Member
Thanks Hippy and Buzby for the great ideas.

Basically I will be adapting the port polling concept to get my timings for the three inputs. I agree that I will not need the complexity of relative timing between all three inputs; just absolute lapsed time between pulses for each individual input. I am planning to dedicate one Picaxe to this function. Then task that Picaxe with transferring the timing numbers to my FPU. Let the FPU to the "heavy lifting" on the calculations. Then my "master" Picaxe will gather the results from the FPU on regular intervals, and output to the OLED.

I will report back in once I receive the 20x2's and 28x2's and get them on the breadboard. I am really excited about the concept of polling multiple inputs. I was really stuck before. This has really revived my project.
 

LarryGrad

Member
Ideas on optimizing timing loops please

OK, got the 20x2 up and running, works great at 64m. See my previous post abt. the bike computer project.

I am looking for advice on some code optimization (see attached). My main concern is this: the pin polling routine sees that some pin is high, so goes down to the cadence timing routine start to do stuff. In the meantime the wheel sensor could come around and be missed by the polling routine because it is no longer running.

So I am looking for any advice on how to speed things up a bit. I need to complete the routines and get back to polling ASAP so I don't miss a pulse.

(One thing I do not have a good measurement on yet is the pulse duration for cadence and wheel. I still need to capture accurate measurement on those. If they are too short I might need a Schmitt trigger or latch or some way to stretch the pulse.)

Thanks for any ideas.

-Larry
 

Attachments

Last edited:
Top