Forum Index
HomeZBasic Home   Forum RulesForum Rules   Forum FAQForum FAQ   MemberlistMemberlist   UsergroupsUsergroups   RSS FeedRSS Feed
Site SearchSite Search   LinksLinks   DownloadDownload   Digests and SubscriptionsDigests and Subscriptions
ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in   RegisterRegister
Setting PWM values
Goto page 1, 2  Next
 
Post new topic   Reply to topic    Forum Index -> ZX-24
Author Message
sturgessb



Joined: 25 Apr 2008
Posts: 246
Location: Norwich, UK

Posted: 26 June 2008, 13:27 PM    Post subject: Setting PWM values Reply with quote

Just a quick one..

Wondering if there is any way (being hopeful) of speeding up the setting of new PWM values. It seems that the PWM() command takes longer than sending 16bits over I2C bus, so at the moment I would be better to move the Pulse generation over to another IC, but I would rather keep it onboard the ZX for simplicity.

It's probably already optimized to hell, but sometimes I know you guys have tricks to get things working a little differently.

FYI, here is my latest flight http://www.ustream.tv/recorded/509806

So its looking good, main loop (gyros, accelerometers, flightcode, PWM output) is running at 400hz, then i have another loop at 100hz for slower sensors. I would really like to boost this up by a 100hz as I have a whole load of extra functions to come and i need the headroom.

Any suggestions would be great

Also, has anyone used the PAK-VIII IC, im really struggling to get it working, would love some help.

CHeers

Ben
Back to top
dkinzer
Site Admin


Joined: 03 Sep 2005
Posts: 2493
Location: Portland, OR

Posted: 26 June 2008, 16:27 PM    Post subject: Re: Setting PWM values Reply with quote

sturgessb wrote:
It seems that the PWM() command takes longer than sending 16bits over I2C bus, [...]
On a ZX-24a, the PWM() call takes about 76uS, on a ZX-24n it takes about 58uS. The execution of PWM() involves validating the channel number, determining which registers to use, calculating the OCR value corresponding to the specified duty cycle given the designated PWM frequency and, finally, setting the OCR register value. If you know the correct OCR value for the timer being used, you can set it directly. For example, for PWM on channel 1 of a ZX-24a or ZX-24n, you can use:
Code:
Register.OCR1A = ocrVal
This takes about 14uS on a ZX-24a and about 600nS on a ZX-24n.

Note, however, that the times above reflect only the time needed to set the OCR register to the required value. The new value does not actually take effect until the timer reaches the end of a counting cycle (the OCR register is double buffered). If the PWM frequency is low, that delay may be the dominant one.
Back to top
sturgessb



Joined: 25 Apr 2008
Posts: 246
Location: Norwich, UK

Posted: 26 June 2008, 16:55 PM    Post subject: Reply with quote

this is great Don.

How do I calculate the correct OCR value?

For instance im opening the PWM channel at 400hz
and setting values for the PWM of 0.10 - 0.20

Code:
Call OpenPWM(1, 400, zxCorrectPWM)
pwmvalue = 1500 / 10000.0
DO
    Call PWM(1, pwmvalue)
LOOP


Presumably if Im going to have to convert my values to OCR is going to take much the same time?

You will notice that my pwmvalue is an Integer and I have to convert to single for the PWM() call. I guess if I can convert to OCR in integer only math, this will save lots of time.

Cheers

Ben
Back to top
dkinzer
Site Admin


Joined: 03 Sep 2005
Posts: 2493
Location: Portland, OR

Posted: 26 June 2008, 17:34 PM    Post subject: Reply with quote

sturgessb wrote:
How do I calculate the correct OCR value?
Perhaps the best place to start is to read Tony's PWM application note AN216.

sturgessb wrote:

Presumably if Im going to have to convert my values to OCR is going to take much the same time?
I would guess so. This technique is best used when you can pre-compute the values and store them in a table.

sturgessb wrote:
You will notice that my pwmvalue is an Integer and I have to convert to single for the PWM() call.
The PWM() routine can take either a Single or an integral parameter for the duty cycle. You may be able to perform the necessary computations using UnsignedInteger values.
Back to top
sturgessb



Joined: 25 Apr 2008
Posts: 246
Location: Norwich, UK

Posted: 26 June 2008, 17:56 PM    Post subject: Reply with quote

ah ok, forgot It could use Integer.

But i see that only supports 1-100% so not the same level of resolution than using single.

thats a shame, as it would has speeded things up being able to use integer values.

Ben
Back to top
dkinzer
Site Admin


Joined: 03 Sep 2005
Posts: 2493
Location: Portland, OR

Posted: 26 June 2008, 19:01 PM    Post subject: Reply with quote

sturgessb wrote:
But i see that only supports 1-100% so not the same level of resolution than using single.
That is true. However, the underlying function for setting the duty cycle uses an integral value representing hundredths of a percent. The compiler adds code for the integral case to multiply the supplied value by 100 and for the Single case it adds code to convert the supplied value to an integral value representing hundredths of a percent.

If you're using a native mode device you can exploit this fact and call the underlying function directly like this:
Code:
Sub pwmDuty(ByVal chan as Byte, ByVal duty as UnsignedInteger)
#c
   pwmStart(zp_chan, zp_duty);
#endc
End Sub

Sub Main()
   Call pwmDuty(1, 1500)
End Sub

Or, more simply:
Code:
Sub Main()
#c
   pwmStart(1, 1500);
#endc
End Sub
Back to top
sturgessb



Joined: 25 Apr 2008
Posts: 246
Location: Norwich, UK

Posted: 26 June 2008, 19:50 PM    Post subject: Reply with quote

that looks good, but I'm slightly confused as to what the code would be for my instance.

I want a pulse train frequency of 400hz (i.e 400 individual pulses per second), with a pulse high time of 1.5ms in order to center servo (the servo is modified to support rates up to 400hz rather than the standard 50hz).

Do i need to use OpenPWM(1, 400.00, zxCorrectPWM), and then use...

Code:

#c
   pwmStart(1, 1500);
#endc


.. to send 1.5ms command (to center the servo)?

Sorry to ask again, just want to get this spot on, as these servos are not cheap, hence not wanting to fry them!

Thanks

Ben
Back to top
dkinzer
Site Admin


Joined: 03 Sep 2005
Posts: 2493
Location: Portland, OR

Posted: 26 June 2008, 20:16 PM    Post subject: Reply with quote

sturgessb wrote:
I want a pulse train frequency of 400hz with a pulse high time of 1.5ms in order to center servo.
A 1.5mS pulse at 400Hz is a 60% duty cycle. Consequently, any of the following should produce the desired pulse train after properly opening PWM channel 1.

Code:
PWM(1, 60.0)

Code:
PWM(1, 60)

Code:
#c
   pwmStart(1, 6000);
#endc


Note, particularly, that you need to add the correct prefixes to variable and parameter names in embedded C and assembly language as noted in the ZBasic Reference Manual.
Back to top
sturgessb



Joined: 25 Apr 2008
Posts: 246
Location: Norwich, UK

Posted: 26 June 2008, 20:24 PM    Post subject: Reply with quote

ok thats great, So i just need to figure out a way of converting my 1500 to 60 using integer only math. I should then see a big speed improvement, and I presume have a greater range of resolution (4000-8000) rather than (40-80) for my 1.0ms to 2.0ms pulses.

Thanks Don
Back to top
sturgessb



Joined: 25 Apr 2008
Posts: 246
Location: Norwich, UK

Posted: 26 June 2008, 21:00 PM    Post subject: Reply with quote

This method... Register.OCR1A = ocrVal

does not seem to work on the zx-128ne
Back to top
dkinzer
Site Admin


Joined: 03 Sep 2005
Posts: 2493
Location: Portland, OR

Posted: 26 June 2008, 21:17 PM    Post subject: Reply with quote

sturgessb wrote:
using integer only math. I should then see a big speed improvement
It should be faster using integer math but I can't say offhand how large the improvement will be. You can easily write some code to measure the elapsed time for the combination of doing the calculation and then setting the duty cycle using either the PWM() routine or the inline C version.

The simplest way to do this is to save the current Register.RTCTick, perform the sequence a number of times (say, 1000) and then get the value of Register.RTCTick again. The execution time for a single pass will be the difference in the two values divided by the iteration count times the RTC tick interval (1/512th of a second).

sturgessb wrote:
I presume have a greater range of resolution (4000-8000) rather than (40-80) for my 1.0ms to 2.0ms pulses.
Yes, that's the only advantage of using pwmStart() directly.

You could also see a performance increase by performing the integer-only calculations in AN-216 and writing the result to Register.OCR1A (for channel 1) or Register.OCR1B (for channel two). As you'll see in the application note, the formula for the TOP value in Phase/Frequency Correct mode is:
Code:
F_cpu / (2 * prescaler * F_pwm)

This value is calculated by OpenPWM() and written to the ICR register for the associated timer from which it can be read back. For channels 1 and 2, use the ICR for Timer1, i.e. Register.ICR1. To set the duty cycle for other than the 0% and 100% cases (which are special cases) it is only necessary to set the OCR value to the desired percentage of ICR. For 40% duty cycle, use something like this:
Code:
Register.OCR1A = CUInt(CULng(Register.ICR1) * 40 \ 100)

For resolutions in tenths of a percent, use this:
Code:
Register.OCR1A = CUInt(CULng(Register.ICR1) * 400 \ 1000)


One final note, it is probably necessary to call PWM() at least once after calling OpenPWM() - I'd have to go check the code to be sure. After that, you can modify the duty cycle by simply manipulating the OCR register.


Last edited by dkinzer on 27 June 2008, 0:48 AM; edited 1 time in total
Back to top
dkinzer
Site Admin


Joined: 03 Sep 2005
Posts: 2493
Location: Portland, OR

Posted: 26 June 2008, 21:22 PM    Post subject: Reply with quote

sturgessb wrote:
does not seem to work on the zx-128ne
As noted in my last post (which I was typing while you posted this), I suspect that you need to call PWM() at least once after the call to OpenPWM() in order to get the PWM started. After that, you should be able to change the duty cycle by updating the OCR register only.
Back to top
sturgessb



Joined: 25 Apr 2008
Posts: 246
Location: Norwich, UK

Posted: 26 June 2008, 21:37 PM    Post subject: Reply with quote

do those registers exists for all 6 of the PWM channels on the zx-128ne?

is it A B C D E F?
Back to top
dkinzer
Site Admin


Joined: 03 Sep 2005
Posts: 2493
Location: Portland, OR

Posted: 26 June 2008, 21:55 PM    Post subject: Reply with quote

sturgessb wrote:
do those registers exists for all 6 of the PWM channels on the zx-128ne? is it A B C D E F?
Yes to the first, no to the second. You have to consult the documentation for OpenPWM() to discover that for the ZX-128ne, channels 1-3 are on Timer1 and channels 4-6 are on Timer3. There is one ICR register for each timer: Register.ICR1 and Register.ICR3. The OCR registers for channels 1-6 are OCR1A, OCR1B, OCR1C, OCR3A, OCR3B, OCR3C, respectively.

If you're going to be fiddling with registers you're well advised to get the Atmel datasheet for the underlying processor.
Back to top
sturgessb



Joined: 25 Apr 2008
Posts: 246
Location: Norwich, UK

Posted: 26 June 2008, 22:27 PM    Post subject: Reply with quote

sorry last question...

how is Register.ICR1 calculated?

im getting a value of 18432 whether the PWM is opened at 50hz or 400hz, confused.

Ben
Back to top
Display posts from previous:   
Post new topic   Reply to topic    Forum Index -> ZX-24 Time synchro. with the server - Timezone/DST with your computer
Goto page 1, 2  Next
Page 1 of 2

 


All content Copyright © 2005-2012 Elba Corp. All Rights Reserved.
Opinions expressed in posts are those of the author and not necessarily those of Elba Corp.
Powered by phpBB © 2001, 2005 phpBB Group