PDA

View Full Version : Simple asm delay ?



ultiblade
- 10th November 2009, 10:15
Hi All !

I'm kinda stuck @ the moment. I have a 12F683 and i need to do a variable delay ranging from 1us to 1000us.

The problem is that at 8Mhz the pauseus can't go as low as 12us.

Does anybody have a asm routine or macro that can be used here ?

Something like :


Macro delay(time in us)
if time = 1 then
nop
nop
if time = 2 then
nop
nop
nop
nop
if time = 3 then

etc, etc ...


Thanks !

mikendee
- 10th November 2009, 10:49
I think that you could use something like.

Delay Var Word ' Pause in us
Counter Var Word

FOR Counter = 1 to Delay
@nop
@nop
NEXT Counter

It's untested so I'm guessing it should work but I don't know how long the LOOP will add to the pause.

Regards, Mike

Jerson
- 10th November 2009, 11:26
Assuming a 8MHz clock, some ASM macros


delay1us macro
goto $+1 ; 2 cycle delay
endm

delay2us macro
delay1us
delay1us
endm

delay4us macro
delay2us
delay2us
endm

delay8us macro
delay4us
delay4us
endm

Now, you may want to get something like 5 us. For that you can
@ delay4us
@ delay1us
in your code

Hope this is helpful to you.

ultiblade
- 10th November 2009, 12:35
Wow, Thanks.

I'm gonna try this. Tried the for..next just yet but it won't go below 5us delay.

Jerson, i think this would do the trick, but i need to figure out how to let the software 'choose' the right delay .. I need to be able to feed the number of uS to delay to the asm macro.

Something like ...


ASM
Delay1us macro time
for x = 0 to time
goto $+1
next
ENDASM

Acetronics2
- 10th November 2009, 13:35
Hi,

The 683 has a TMR1 and also a CCP module ...

just use one of them for variable delays !!!

see asm examples in the relevant Datasheet chapter ...

may be 1 and 2 microseconds will need a "special treatment" ...

Alain

ultiblade
- 10th November 2009, 14:18
You are so right !

The thing is however, i have tmr1 setup as a counter and i use its overflow to sync the whole program on 100hz.

I have three different led's that are driven by fet's. Each led has it's own channel and due to the design of the driver, at a given moment only 1 led me be high.

To be able to shift the colors around, i turn on a certain channel for a certain ammount of time.



RED con %001
GRN con %010
BLU con %100

loop60
if ledred = 0 then goto noRED
GPIO = red
pauseus ledred
noRED:
if ledgrn = 0 then goto noGRN
GPIO = grn
pauseus ledgrn
noGRN:
if ledblu = 0 then goto noBLU
GPIO = blu
pauseus ledblu
noBLU:
goto loop60


Once interrupted the ledred / ledgrn / ledblu values are altered, but always in a way that ledred+ ledgrn + ledblu = 1000, the timer get's a reset and it then jumps back into the loop.

The example above works but pauseus can't delay faster then 12us ...

Acetronics2
- 10th November 2009, 14:49
Hi,

1µs to drive leds may really be overkill ...

but I stil think the CCP module can do the trick ...

Alain

ultiblade
- 11th November 2009, 07:27
You are right about the 1us. I'm using this to be able to fade between colors.

If I turn on the red led, wait 333uS, then turn on the green led, wait 333uS, and then the blue led and wait 333uS and then loop again, i have a white color at about 1000Hz, with no flickering. If i then increment the pauseus 333 for red and decrement the pauseus 333 for blu the color shifts smooth from white to about orange.

I use the pauseus command at 8Mhz, which has a shortest delay of 12us. In the fade cycle this results in that one (or two) led are almost off, but not completely. The last 12 cycles the led stay on for 12us, and in the last cycle it will turn off.

It works perfect except for the last few cycles ....

ultiblade
- 12th November 2009, 13:05
Google came up with an interesting piece of code.
I did some tests and it seems to do the trick.

If called using "@ wait 0" it delays 1uS,
if called using "@ wait 1" it delays about 2,5uS,
if called using "@ wait 2" it delays about 3uS.



asm
shortwait macro del ; used by main wait macro
if (del>3) && (del<8)
call del7+(7-(del))
endif
if del <4
if del>=2
goto $+1
endif
if ((del) & 1) == 1
nop
endif
endif
endm
endasm

asm
wait macro del
; if (del)<0 error 'negative delay'
; endif
if (del)>=8
movlw ((del)-8) /4
call delay
shortwait ((del-8) % 4)
else
shortwait (del)
endif
endm

delay
addlw 0xff
skpnc
goto delay
return

del7 goto del5 ; 7 cycle delay
del6 goto del4 ; 6 cycle delay
del5 nop ; 5 cycle delay
del4 return ; 4 cycle delay
ENDasm


So, the delay is not the problem anymore.
However, when called from picbasic like :


led1 var word
led2 var word

led1 = 2
led2 = 2

start:

GPIO = %01
@ wait _led1
GPIO = %10
@ wait _led2

goto start


The delay is about 33uS instead of 2. I understand that the delay would be longer, but 30uS extra ??

Can someone, please, point me in the right direction ?

Jerson
- 12th November 2009, 14:35
Once you start counting instruction cycles, you will realize how they all add up to get you to the 30uS extra that you see. A call takes 2 cycles, a return another 2, and so on.

I am a bit curious about your application and how you achieve colour fading. According to what you have shown here, you seem to be lighting the leds one behind the other with a fixed pause in between. I do not know why you have it that way, but, I would think it would be better to have them work in tandem unless you want just 1 led on at a time.

Why not use 3 PWM channels one per colour to control the intensity of each?
Now, the refresh rate for all the channels will be the same, but, the on-time for each led will be decided by its on-time variable.

Would this work for you?

ultiblade
- 12th November 2009, 15:12
Hi Jerson,

I understand your questions, let me explain some more :

I drive 3 powerled's directly from the mains using a led driver board. These led's are switched by FET's, each on it's own channel. The driver board itself is about 10x10mm, a pic with 3 pwm channels in a SO8 package is just not available. Everything larger won't fit the enclosure.

The driver cannot drive 2 or 3 led's at once, that's why i'm switching between the three. Doing so at a total delay (led1 delay + led2 delay + led3 delay)=1000Hz + some overhead.

Pauseus does it's work, and all is fading smoothly. Except for the first start of a color. This sticks to 12us. When a fade is started, the color that is being faded in does not start at 1 or 2 us but starts with an initial delay of 12uS.

Hope this makes my problem somewhat clearer ...

I did count the instruction cycles, i even did a cross comparison of the asm files, the one using a number @ wait 0 and the one using @ wait _ledRED both files are exactly the same and have the exact same length ! I played around with the variables, changed them to byte's, putting them in bank0 ....

Why would changing from a numeric value to a numeric value stored in a variable result in an overhead of 30uS, i just don't understand.

ultiblade
- 12th November 2009, 15:36
Small update :

Declaring the variables as:

ledRED VAR WORD BANK0

Does reduce the total delay somewhat, the shortest delay (0) now results in 20uS ..

Jerson
- 12th November 2009, 17:35
Try declaring your variable as BYTE instead of word. It will shave more time from the 20uS.

Jerson
- 12th November 2009, 17:51
Assuming you have TMR0 (8 bit timer) free, you can try this code



AsmDelay macro delayus
movlw 0FFh-delayus ; delayus to overflow 1T
movwf TMR0 1T
bcf INTCON, T0IF ; clear the timer0 Overflow flag 1T
btfss INTCON, T0IF ; wait till it overflows 1T/2T
goto $-1 ; back to the btfss 2T

endm

The minimum delay you will achieve will be 4T cycles or roughly 2uS at 8MHz.


The way to use it would be


GPIO = %01
@ AsmDelay 12
GPIO = %10
@ AsmDelay 24


I hope this takes you closer to your goal. The code is just a concept, you may have to correct it. One important point you should note - the maximum delay will be 255uS

Bruce
- 13th November 2009, 05:18
Try this.


asm
DelayCycles macro ncy ; ncy can be from 1 up to 256
local n
n set (ncy) ; assign ncy value to label n

while n > 2 ; inserts goto $+1 while n > 2
goto $+1 ; burn 2 cycles
n set n - 2 ; n = n - 2
endw
; handle left-overs
while n > 0 ; 1 nop for odd numbers
nop ; burn 1 cycle
n set n - 1 ; n = n - 1
endw
endm
endasm

Main:
HIGH 0
@ DelayCycles 1 ; adds 1 nop
LOW 0
@ DelayCycles 5 ; adds 2 x goto $+1 and 1 x nop
GOTO Main

Jerson
- 13th November 2009, 06:00
Brilliant work.

PS. It has a catch though. Te macro expansion occurs at compile time. So, you will not be able to "feed" the delay value at runtime :(

ultiblade
- 13th November 2009, 09:32
Dear Jerson & Bruce,

I could not get here earlier, sorry. I appreciate your efforts !

Jerson, if i declare the delay variable as a byte, it takes even longer.
I just don't understand .. The delay macro i found works perfect if given a number straight from the call, if it is fed by a variable the delay jumps to 20 or 30us. It also seems that the delay is fixed after that, if i compile @ wait 1000, it delays a perfect 1000 cycles, if i compile @ wait ledred (i.e. ledred = 100) it delays 20uS no matter what ledred is ?

Bruce, your asm code also does the trick, perfect 1us or 50us delays, pretty neat. But it also suffers from the same problem. If i compile @ Delaycycles 200 if works perfect, if i compile ledred var byte, ledred = 200, @ delaycycles _ledred it gives a delay of 33uS no matter what the value of the leded var is ??

Apart from that, i need to do a delay between 1 and 1000uS, using tmr0 the max delay will be 256, and i can use the Bruce asm up to 512, the ASM code i posted earlier can go up to about 1024.

I would use the 16-bit TMR1 if it was available but i need that to do a 100Hz sync (count) interrupt...

Any ideas ?

Bruce
- 13th November 2009, 23:21
@ delaycycles _ledred it gives a delay of 33uS no matter what the value of the leded var is ??

That's because when you pass _ledred to the macro as the argument, it uses the 'address'
of ledred, and not the actual value in RAM. Look at the address PBP places ledred at to see
why that happens.

You can't put a macro inside a loop with a variable as the argument. What you're looking to do just
isn't possible (as far as I know) with a macro inside the loop, and calls to any .asm routine, timer, etc,
just aren't going to give you from 1uS to 1000uS delay periods you need immediately after
placing a value on GPIO.

I would re-think the whole process if I'm stuck at 8MHz, or switch osc speed & PIC to support using
the pauseus commands.

ultiblade
- 14th November 2009, 12:19
Hi Bruce,

Thank you for your detailed answer ! All is clear now.

I have adapted the code in a way that the led's never go below 12uS, that way the 'color' switch on and off effect (color switches on for 12uS instead of 1uS) does not occur. This makes the colors somewhat more pastel, but it is still a smooth fade and a cool effect.

And, yes, if i'm going to make a new design i will certainly use a larger external osc ..

Thanks for all your answers and information !

Best regards,
UB

Bruce
- 14th November 2009, 14:44
Grab a new 8-pin PIC12F1822 with 32MHz internal osc when they come out. Then you can
get PAUSEUS down to 2uS. These even have an onboard EUSART.

ultiblade
- 17th November 2009, 15:42
Yep, if only it had eeprom .... :-(

Bruce
- 17th November 2009, 16:13
The preliminary specs state it does. Data EEPROM (bytes) 256

ultiblade
- 18th November 2009, 07:11
You are right ! Missed that one, sorry ..

Will order some samples as soon as possible, hoping that support for the part in PBP will be available soon ..

Best regards,
UB