pulse counter using LPC1768 proving to very challenging

Hi, I'm trying to counter some pulses (2-10usec width) using the LPC1768 Cortex-M3 microcontroller. There are 2 channels on which I have count the pulses on. I have to allow only pulses that are >=2us pulse width (so we cannot simply use the counter function).

But it is turning out to be an incredibly difficult feat to achieve this on a 100MHz Cortex-M3. The problem arises when we try to measure the pulse width to allow only pulses lasting 2us or higher. We are trying to use a capture pin and the capture interrupt. First we set it for a falling edge and capture the timer value, then set it to rising edge and again capture the timer value. Then take the difference between two values to see if it

2usec. But the processing itself is taking over 6-8usec. We also tried

simply using a external interrupt & reading timer registers with each edge, but with the same results.

We cannot seem to understand how or why the processing is taking so long there are hardly 3-4 "C" statements in the interrupt routine (change edge of capture, take the difference in captured values and compare if it is

=2us). Any ideas how this feat could be accomplished on a LPC1768?

--------------------------------------- Posted through

formatting link

Reply to
navman
Loading thread data ...

I can't help with this specific micro, but I've encountered the problem on various other platforms, and solved it by using polling rather than interrupts. Avoiding the context saving associated with interrupts can save you a significant amount of time if your processing task is otherwise reasonably trivial, as yours seems to be. This solution does depend on being able to sacrifice some processing time for the polling loop (interrupts disabled), that will depend on the pulse frequency and what else the device has to contend with.

You might also consider doing your time-critical coding in asm, if that's possible. Have oyu checked your listings to see how many removeable instructions the compiler is inserting?

Reply to
Bruce Varley

Reply to
Roberto Waltman

Can you connect the signal to two pins, so that one will capture times on a falling edge, and the other will capture times and cause an interrupt on the rising edge?

Have you considered some analogue tricks, assuming you don't need too much accuracy for your measurements? A diode, a capacitor and a couple of resistors should let you charge up a capacitor during the pulse. Measure the voltage on the capacitor with the ADC when the pulse is complete. Or use an analogue comparator to trigger an interrupt on the processor once the capacitor voltage is over a certain level.

The compiler may be generating too much code for context saving. A common cause of that is to call external functions from within the interrupt function - since the compiler doesn't know what registers it uses, it must save everything.

And are you using appropriate flags for the compiler? Many people complain their compiler code is poor, when it turns out they have disabled optimisation...

Reply to
David Brown

I have not used the LPC1768, so these are generic suggestions: a) If they are running in FLASH, make your interrupt routines RAM resident. Ditto for critical functions in normal code.

b) Do the width computation/decision inside the rising edge interrupt handler.

c) If possible, configure the capture to work on either edge to save the time needed to reconfigure it.

-- Roberto Waltman

[ Please reply to the group. Return address is invalid ]
Reply to
Roberto Waltman

You might not even need the comparator if the input pin threshold level is consistent over the operating conditions. A simple RC filter might trigger the interrupt only if the pulse length is over the threshold.

Using a comparator does make it easier to compute the trigger pulse width. If you fed the reference leg of the comparator with the output of one of the LPC1768 DAC outputs, you could tune the triggering pulse width in software.

Mark Borgerson

Reply to
Mark Borgerson

Can you *prevent* (filter) short pulses from ever getting to the pin? I.e., a simple digital filter can excise all short pulses without affecting any *longer* pulses.

Is the control logic "double buffered"? E.g., some devices would allow you to preload the *next* contents -- which would be transferred into the *real* hardware when the first condition had been satisfied.

[sorry, I'm not familiar with the counter/timer channels on the device]

Can you, instead, set up the device to *start* a counter on the "leading edge" (whether that is rising or falling) of the pulse (note NO interrupt generated, here!), program the counter (timer) for "2 us" and have the timer *overflow* trigger THE interrupt (which just polls the state of the pin to see if it is "still" at the right level to be countable)

[there are flaws in this scheme -- depending on the actual nature of your input pulse stream]

Some devices with double buffered logic will let you use this to enqueue the *next* set of controls for the channel so that the timeout/overflow can effectively reprogram the channel for you -- to generate an IRQ on the *trailing* edge of the pulse, for example.

[see initial disclaimer]

Rewrite it in ASM. This is clearly (IMO) a case where the "lack of clarity" of ASM is easily justified by its brevity. What do you need, a handful of instructions??

Don't do *any* work in the IRQ. Just grab the data that you need and make the decision about how to use it *later*.

What guarantees do you have regarding the interarrival time of subsequent pulses, etc.? What happens to your system if, for example, a 100KHz signal suddenly appears on this pin? Will your system effectively "lock up" because it is dutifully catching interrupts (that probably are meaningless in this case)? Consider designing the code so that this is a self-limiting process...

Reply to
D Yuniskis

I think the solution is obvious.

Configure for falling edge interrupt (I assume from your post that LOW is ACTIVE).

In the interrupt, do the following:

1) Clear the interrupt flag 2) Verify that input pin is still ACTIVE. If it is not, exit silently (no event). 3) Do a CPU timed busy-wait for 2us (minus typ/max interrupt latency up to this point). You might need inline asm or a calibrated function for this purpose. 4) When the busy-wait function returns, check the interrupt flag again. If there was another interrupt event, go back to 2). 5) Verify that input pin is still ACTIVE. If it is not, exit silently (no event). 6) Accept the event and exit the interrupt function.

The interrupt function will consume 2us plus overhead. It will be as accurate, as your prediction of typ/max interrupt latency is. That depends mostly on the usage of "critical section" primitives in the rest of your code. The LPC1768 contains an ARM core, so you could reserve the FIQ (fast interrupt) for this purpose only, and use IRQ (slow interrupt) throughout the rest of your code (including OS). That would allow you to never disable interrupts (FIQ) and thus never suffer software-induced latency. You could also move the vector table and the FIQ handler code (which is the function described above) and all of its variables / subfunctions, in an area that has predictable memory timing. I'm not aware of the LPC1768 details, but on other archs you can use the TCM or disable i-cache via MMU for the memory area in question. The memory access doesn't have to be fast, it must just be very very predictable.

Using these tips, you can achieve very reliable acception/rejection. The imperfections are:

A) There is a "blind window" which is from detection of the edge to the clear interrupt flag. If there is a spurious HIGH in this window, it will not be detected. You could possibly extend the proposal, using your counter mode to improve on this imperfection.

B) That most chips (and certainly the LPC1768 too) synchronize the input pin before detecting edges. Thus, some very fast flicker might happen completely unnoticed by the hardware (and software). You would need to move the pulse detection into a specially designed hardware circuit to handle this better.

C) The sampling of step 4 and 5 are not taken from the same-moment-in- time. Therefore the pulse must be longer than X to be accepted reliably, but smaller than Y to be rejected reliably. There is a window in which a pulse may or may not be rejected. This is the crux of using software instead of hardware. Again, you can only handle this better in a specially designed hardware circuit.

Best regards Marc

Reply to
Marc Jet

This is almost everything that I was going to suggest.

Look at the assembly that your compiler is generating, and make sure that it's really as efficient as can possibly be. If it isn't, just go to the well and write the ISR in assembly language.

Even on a 100MHz processor, 2us is an awfully short period of time. Doing some sort of preconditioning makes a lot of sense to me, although Schmitt trigger logic is rarely accurate enough for any practical purpose beyond glitch reduction. It should be possible to use a multivibrator (74xx126??) and some gates to do this if an RC and a Schmitt isn't accurate enough. An asynchronous clear counter would do the trick as well -- hold it in reset when the pulse is inactive, and trigger the micro whenever it counts to it's 'carry out' value. Then you just need to feed it an appropriate clock to hit your "more than 2us" criterion.

If your ISR pops off quickly enough, and if it doesn't waste too much time, spin in the ISR until the signal goes inactive, and check the time. If that ">2us" can mean "sometimes _much_ greater than 2us" then this obviously won't work.

I like the "two pins" approach, if you can make it unambiguous. Make that microcontroller hardware work for you, if you can.

--
http://www.wescottdesign.com
Reply to
Tim Wescott

Is flash prefetch enabled?

-- Roberto Waltman

[ Please reply to the group. Return address is invalid ]
Reply to
Roberto Waltman

I don't know this part, but timer-capture hardware I've seen usually uses the peripheral clock to "filter" out short pulses. Can you lower the peripheral clock to the 1 or 2 MHz range and let the hardware synchronizer do all the work? Obviously, if you have other peripherals that need a higher clock, this wouldn't work, but the core could continue to run at 100MHz.

just an off-the-wall idea, Bob

Reply to
Bob

[...]

Put a capacitor to the ground on the interrupt pin.

Vladimir Vassilevsky DSP and Mixed Signal Design Consultant

formatting link

Reply to
Vladimir Vassilevsky

e

on

e,

another option might be to use a sync serial port running continuously as a sampling engine and then run filtering over the captured stream of bits.

Reply to
Andy

Something seems wrong there. I've got an application where I use an LPC1758 to close a fairly complex PI loop controller. I've only got the clock turned up to 52 MHz, and I'm able to execute a printed page worth of ISR in only about 2us.

A) Everyone who's told you to look at the disassembly for stupidity is right.

B) Setting up all of the clocks on the LPC17s is non-trivial. Have you confirmed that you're actually running at 100 MHz, and that the peripheral clock going to the timer peripheral is too? If I recall correctly, the CMSIS code to do so doesn't actually work. If you've got access to the CLKOUT pin, setting it up and dropping a scope there might do you a world of good. Also, there's an errata note that says you've got to have the main PLL disabled while you're configuring the peripheral clocks

--
Rob Gaddi, Highland Technology
Email address is currently out of order
Reply to
Rob Gaddi

Long before you consider writing the ISR in assembly, check that the C code is decently written (if you can't write appropriate C code for an interrupt routine, you are unlikely to be able to write good assembly), and check that you are using your compiler properly (and that you have a decent compiler). Used properly (which includes studying the generated assembly), C on a processor like this should be very close to the speed of optimal assembly.

I fully agree here. Software is not good at doing things with a few microsecond timing - any processor fast enough to have plenty of instruction cycles in that time will have unpredictable latencies due to caches, pipelines, buffers, etc. But this should be fairly simple code

- with enough care, it could be done.

Reply to
David Brown

An easy way to check timings is to calculate the timer delays needed for one second, and connect it up to an LED. You can quickly tell if your clocks are right, without scopes or other equipment - after all, /every/ electronics board has a blinking LED.

Reply to
David Brown

Another sanity check I use is to blink a LED as fast as possible in a software loop, and see if the frequency matches what I'd expect. Sometimes there's a difference of a factor 100, which usually means the PLL isn't set up correctly, or the CPU clock dividers are still at the most conservative defaults, or it's still running from an internal RC oscillator or something like that.

Reply to
Arlet Ottens

The only caveat would be if the compiler wasn't very good at generating efficient code -- but I'd have a hard time believing that for a Cortex processor. Not setting up the optimization flags correctly, and inadvertently writing inefficient C code -- yes.

I'm just old and suspicious, and remembering too many bad experiences with compilers that _were_ crappy.

However: you'll need to be exceedingly strict about your interrupt response time elsewhere. If you have a habit of turning off interrupts to make sure that operations are atomic, and _particularly_ if you're one of a team of programmers that do this, then you have to be Really Really Strict about just how long these intervals last.

Because all it'll take is one guy turning off interrupts while he calculates pi to 100 decimal places in some bit of shared memory, and your little interval counter will fail.

--

Tim Wescott
Wescott Design Services
http://www.wescottdesign.com

Do you need to implement control loops in software?
"Applied Control Theory for Embedded Systems" was written for you.
See details at http://www.wescottdesign.com/actfes/actfes.html
Reply to
Tim Wescott

The Cortex-M3 the OP is using is actually very good for this task. There are 8 different hardware level nested interrupts, and the CPU will automatically push your registers in 12 cycles. ISRs can be written in pure C without any special pragmas/attributes, and without assembler stubs.

When one ISR follows the other, the registers are not restored/saved again, but are kept in saved state, while it immediately executes the next ISR, only taking 6 cycles latency inbetween.

See

formatting link
for more details.

Reply to
Arlet Ottens

The embedded world's version of the canonical printf("Hello, world!"); One should always, always do something like this when first approaching a new processor or a new compiler on an old processor.

--
Rich Webb     Norfolk, VA
Reply to
Rich Webb

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.