PDA

View Full Version : Is there a faster way to compare?



RussMartin
- 7th February 2010, 19:57
I'm doing something like this (pseudocode):

FOR INDEX=1 TO SOMETHING
IF INDEX>A THEN PIN1=1
IF INDEX>B THEN PIN2=1
IF INDEX>C THEN PIN3=1
IF INDEX>D THEN PIN4=1
IF INDEX>E THEN PIN5=1
NEXT INDEX

Each of those IF . . . THENs appears to chew up a lot of time, each reducing the upper limit on the number of iterations (SOMETHING).

I'm having to do this in/as an interrupt handler and I've got about 7ms (at 8MHz) to do it in.

Is there a faster/better/simpler way? (Someone <i><u>please</u></i> tell me there is!)

Fingers crossed here . . .

Darrel Taylor
- 7th February 2010, 20:23
Are they all BYTE's?
Is it a PWM output?

7ms is a LONG TIME.
<br>

RussMartin
- 7th February 2010, 20:44
INDEX and A through E are bytes. All five pins are on the same port.

Not sure if it's PWM or not. But at the end of the interrupt handler, all pins are reset to 0. Values A through E each represent a proportion of SOMETHING. If A through E were equal, all five pins would need to turn on as nearly as possible at the same instant.

The interrupt (INT_INT) is being driven by a zero-crossing detector at 60 Hz (120 interrupts/second).

Guess it <i>is</i> PWM, period 120 Hz, and I'm varying the duty cycle on each pin.

Darrel Taylor
- 7th February 2010, 21:41
This should be a bit faster ... (untested)

INDEX VAR BYTE BANK0
SOMETHING VAR BYTE BANK0
A VAR BYTE BANK0
B VAR BYTE BANK0
C VAR BYTE BANK0
D VAR BYTE BANK0

PIN1 VAR PORTB.0
PIN2 VAR PORTB.1
PIN3 VAR PORTB.2
PIN4 VAR PORTB.3


ASM
TestStart
movlw 0 ;FOR INDEX=1 TO SOMETHING
movwf _INDEX

ForLoop
incf _INDEX,F

movf _A,W ; IF INDEX>A THEN PIN1=1
subwf _INDEX,W
btfsc STATUS,C
bsf _PIN1

movf _B,W ; IF INDEX>B THEN PIN2=1
subwf _INDEX,W
btfsc STATUS,C
bsf _PIN2

movf _C,W ; IF INDEX>C THEN PIN3=1
subwf _INDEX,W
btfsc STATUS,C
bsf _PIN3

movf _D,W ; IF INDEX>D THEN PIN4=1
subwf _INDEX,W
btfsc STATUS,C
bsf _PIN4

movf _INDEX,W ; Next INDEX
subwf _SOMETHING,W
btfss STATUS,Z
goto ForLoop
ENDASM

tenaja
- 7th February 2010, 21:46
Russ,
I am sure there are other ways to get the pwm you need (such as interrupts), since this method takes up a HUGE amount of cpu time. However, if that doesn't matter and you just need a bit more speed, consider asm.

I compiled your snippet with PBP, and it used up 57 words of program space on a 16F. Proton+ did the same code in 36 words; almost 2/3 the size. PBP's asm file is nearly unreadable, so I can't compare them, but here is the Proton output from a single basic line for both 16F and 18F:

16F...
[TEST.BAS] IF INDEX>A THEN PORTA.1=1
MOVF INDEX,W
SUBWF _A,W
BTFSC STATUS,0
GOTO BC@LL4 'go to the next line...
BSF PORTA,1

18F...
[TEST.BAS] IF INDEX>B THEN PORTA.1=1
MOVF INDEX,W,0
CPFSLT _B,0
F@JUMP BC@LL6 'go to the next line...
BSF PORTA,1,0

RussMartin
- 7th February 2010, 21:59
Thanks for the suggestion, Darrel; I'll try it, expanding to add PIN5 and value E.

Thanks for the comparison, tenaja.

Although now I wonder if I'm looking for the wrong problem. Using the o'scope, I see that each of those IF . . . THENs actually takes only about 22us; that's only 110us for all 5. Maybe it's the FOR . . . NEXT. Each time I added an IF . . . THEN, I had to reduce SOMETHING and rescale each of however many variables I was comparing.

Darrel Taylor
- 7th February 2010, 22:30
... Using the o'scope, I see that each of those IF . . . THENs actually takes only about 22us; that's only 110us for all 5. ...
That's 110us * SOMETHING (the loop count).

If SOMETHING is ever higher than 63, you will have used up the 7ms and more.

With the ASM code I gave, each IF takes only 2us @8mhz.
SOMETHING can be 255 and it will still only take ~2.6ms.

hth,

RussMartin
- 8th February 2010, 03:38
Yes, I knew about the <b>* SOMETHING</B> !

But now I have the opposite problem . . . since yours is so much faster, I would need to run it almost three times (because the variables can range from 0 to whatever SOMETHING is scaled to). Should I just run it three times in succession, or do I need to go to a WORD variable for SOMETHING? (I need to scale SOMETHING through as much of that time span as possible.)

Darrel Taylor
- 8th February 2010, 04:02
since yours is so much faster, I would need to run it almost three times (because the variables can range from 0 to whatever SOMETHING is scaled to). :confused: :confused: :confused:

Totally Lost me on that one.
<br>

RussMartin
- 8th February 2010, 04:58
Okay, let me background this first.

For each of 5 channels: A 0-5V value is read (derived from an op-amp) by an ADC channel (presently at 10 bits, so 0-1023). That value is manipulated depending on other data (an offset or a correction, for example). The complementary value (1023-value), scaled so that 0 (1023-1023) can be managed in x repetitions (the maximum that can be fit into about 7.8ms) of the loop, is how long the delay in the interrupt handler must be before turning on the corresponding output pin to a triac driver. Normally, a 5us pulse would be adequate to keep the triac on until the end of the AC phase, but the indicator for the channel is an LED, so the pin is left high until the end of the phase.

Just sending a pulse on a pin means running the FOR . . . NEXT loop only up to the scaled value and sending the pulse; keeping the indicator on proportionately (well, somewhat) means the pin must stay high until near the end of the phase.

Your code running to 255 and finishing in 2.6ms means that only 36 percent of the 7.8ms period is controlled, because it is so fast. Proportionately, the phase would have to be scaled something like 0-765, probably a little less.

Could I just run additional cycles of the tests before incrementing the INDEX to stretch it out?

The code works as is, but over a very limited signal range.

Ioannis
- 8th February 2010, 13:29
I have not tested the idea.

Instead of writing each IF-THEN test to the port, writing to a variable and at the end that variable to the port will be faster?

Ioannis

Mike, K8LH
- 8th February 2010, 18:50
Hi Russ,

Have you considered using timer derived periodic interrupts and performing just one PWM "step" per interrupt? Perhaps spreading out 256 "steps" across the 8.333-msec period (8333-usecs/256 = 32.55-usec interrupts)?

Here's another method like Darrel's which would work well if you group the outputs consecutively (B7..B3 or B4..B0 for example). Updating the port from a 'shadow' register allows precise bit update timing (multiple bits with same duty cycle value will update at exactly the same time).

Regards, Mike


;
; 1/256th step, 18 cycles (isochronous), active high output
; on duty cycle match until end-of-period
;
clrf shadow ; clear 'shadow' |B0
movf led+4,W ; led[4] duty cycle, 0..255 |B0
subwf dcy,W ; C = led[4] >= dcy |B0
rlf shadow,F ; |B0
movf led+3,W ; led[3] duty cycle, 0..255 |B0
subwf dcy,W ; C = led[3] >= dcy |B0
rlf shadow,F ; |B0
movf led+2,W ; led[2] duty cycle, 0..255 |B0
subwf dcy,W ; C = led[2] >= dcy |B0
rlf shadow,F ; |B0
movf led+1,W ; led[1] duty cycle, 0..255 |B0
subwf dcy,W ; C = led[1] >= dcy |B0
rrf shadow,F ; |B0
movf led+0,W ; led[0] duty cycle, 0..255 |B0
subwf dcy,W ; C = led[0] >= dcy |B0
rlf shadow,W ; W = led 'step' output |B0
movwf PORTB ; update LEDs |B0
incf dcy,F ; bump duty cycle counter |B0
;

RussMartin
- 8th February 2010, 19:48
Thanks, Ioannis and Mike. Let me noodle with your suggestions for a bit.

Meanwhile, for anyone interested:

For an IF A [comparison] B THEN statement, without the appended action, the execution time at 8MHz if A and B are WORDs appears to be about 22-24us; if A and B are BYTEs, it appears to be about 5-6us. These numbers are not down to a gnat's whisker; they are as measured on my scope (Tektronix 465M).