passing variables from task to task

Here you can share completed projects or parts of projects that may be useful to others. You may post files relevant to ZBasic - source code, schematics, etc.
cdavis
Posts: 9
Joined: 12 June 2006, 10:14 AM
Location: Garden Grove, CA
Contact:

passing variables from task to task

Post by cdavis »

Hello, I have been unable to find sufficient information on this topic, can anyone help me out? I was wondering how to accomplish a couple things: first thing I was wondering was how would I count the pulses coming from an encoder? Second was how can the task that is constantly counting the encoder pass the count to a different task that calculates speed? Any info on this would be great, thanks!



Casen
Casen Davis
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Post by mikep »

Check application note AN-210 - Sharing Data Between Tasks and there is sample code as well.
Mike Perks
cdavis
Posts: 9
Joined: 12 June 2006, 10:14 AM
Location: Garden Grove, CA
Contact:

Post by cdavis »

Hey Mike,

I just found that tutorial as you were replying to my post. Thanks for pointing it out though, it has helped a lot. One question still remains however, and that is how would I count encoder pulses? If my thoughts are correct, the encoder just sends a series of high and low signals as the wheel is rolling ( this is an integrated encoder in a precision dc motor). If I wrote a task that looks something like this psuedo code:

Code: Select all

specify pin(20) as input

If pin(20) = 1 then
  
   count++

end if
I dont imagine it would count the encoder pulses correctly this way, but I could be wrong. Im sure the code would be running very fast compared to the pulse train coming from the encoders, but I am assuming there is a possibility this code might miscount the encoder pulses for a number of different reasons mostly having to do with timing issues. Any thoughts?


Casen
Casen Davis
stevech
Posts: 715
Joined: 22 February 2006, 20:56 PM

Post by stevech »

if your encoder's logic level is glitch-free, perhaps you could connect it to the timer ext. clock pin on the ZX and read that timer register every X amount of time. (having setup the timer in this mode).

Or connect to the INT0 pin and get an edge-triggered interrupt to use with waitForInterrupt() in ZBasic.
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Post by mikep »

stevech wrote:Or connect to the INT0 pin and get an edge-triggered interrupt to use with waitForInterrupt() in ZBasic.
One method is have a separate task that does a WaitForInterrupt and increments a counter every time an edge is seen. Another subroutine can be provided that retrieves the counter (and perhaps resets it) using the one of the methods of sharing data discussed in my application note.
Mike Perks
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

If you don't need continuous monitoring, you could use CountTransitions() for a short interval. This method is often used for "tachometer" applications where you only need to sample the speed from time to time. If continuous monitoring is required, and the pulse rate is low enough, using WaitForInterrupt() would work. If the pulse rate is high, clocking a counter, internal or external, would be required.
- Don Kinzer
cdavis
Posts: 9
Joined: 12 June 2006, 10:14 AM
Location: Garden Grove, CA
Contact:

Post by cdavis »

Well, my motors max out at 8000 rpm, and the encoder pulses 512 times per rotation, so if my calculations are correct thats 4,096000 pulses a minute, or 68,266 pulses a second. This seems like it might be a bit fast for the WaitForInterupt() function.
Casen Davis
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

cdavis wrote:This seems like it might be a bit fast for the WaitForInterupt() function.
Indeed. If you don't use any other System Library routines that use Timer1, you can set up Timer 1 to be clocked by the T1 input. Then, you could use the interval timer to control resetting the timer, letting it accumulate, and reading the result. The number of counts divided by the counting interval will give you pulses per second. This is essentially the same idea as CountTransistions() except that you can count over longer intervals without the negative impact on task switching, serial I/O, etc.

If Timer1 is needed for other things, the only other viable option is to add an external counter that you would operate in a similar fashion.

Another option that may work, if the resolution is high enough, is to use a frequency-to-voltage converter and then read the voltage using an A/D channel. Examples of such a device are the Microchip TC9400 and the National LM2917. An Internet search will provide other examples and options
- Don Kinzer
GTBecker
Posts: 616
Joined: 17 January 2006, 19:59 PM
Location: Cape Coral

passing variables from task to task

Post by GTBecker »

... interval timer to control resetting the timer, letting it accumulate, and reading the result.
FWIW, I suggest not adopting that cycle. There is a period between the
counter read and its reset that permits losing external counts.

I think it is better to never reset the counter. Instead, determine the
increment by subtracting the previously read value from the current
value, and account for the overflows.


Tom
Tom
spamiam
Posts: 739
Joined: 13 November 2005, 6:39 AM

Re: passing variables from task to task

Post by spamiam »

GTBecker wrote:
... interval timer to control resetting the timer, letting it accumulate, and reading the result.
FWIW, I suggest not adopting that cycle. There is a period between the
counter read and its reset that permits losing external counts.

I think it is better to never reset the counter. Instead, determine the
increment by subtracting the previously read value from the current
value, and account for the overflows.


Tom
Good point. At that high frequency of signals, it will be very hard to clear the counter. Subtraction is better.

Another option on a ZX24a is to use Timer 1 (16 bit timer) as suggested previously, but, instead of sampling it from time to time, put the timer in CTC mode (clear timer on compare match), and then set the output compare register (OCR1A) to some conveniently large number so that interrupts occur sufficiently infrequently. Then set the OC1A pin to trigger a PinChange interrupt. Set the pin to toggle on compare matches. This way you will get a pin change every time it passes the value on OCR1A.

Note that the pin change occurs on the clock AFTER the counter reaches the value set in OCR1A. So you have had n+1 counts on the timer.

When the PinChange interrupt occurs, you already know how many counts have occured, so you just record WHEN it occured.

-Tony

PS, on a ZX-24 non-a, you could route the OC1A pin to one of the external interrupt pins. Set the pin to state change trigger (rather than rising or falling edge trigger)
cdavis
Posts: 9
Joined: 12 June 2006, 10:14 AM
Location: Garden Grove, CA
Contact:

Post by cdavis »

This makes sense for a constant pulserate, but wont the count be wrong if the pulserate accelerates and deccelerates? Accuracey is fairly neccesary because I will be using my encoders to not only track distance traveled, but velocity as well. I will use the velocity values in my proportional control algorithm to make sure the wheels remain spinning at the same speed ( the robot is a two-wheeled micromouse). I will be using the distance values to compare the mouses actual location with the location it "thinks" it is at. My only dilema is the quickness with which the controller can detect the pulses coming from the encoder.
Casen Davis
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

cdavis wrote:This makes sense for a constant pulserate, but won't the count be wrong if the pulserate accelerates and deccelerates?
Counting the pulses that occur in a known interval will allow you to compute the average speed during that interval. Even if you measured the time from the beginning of one pulse until the beginning of the next, you'd still have the average speed over that interval. The instantaneous speed at any point between pulses could well be different.
- Don Kinzer
GTBecker
Posts: 616
Joined: 17 January 2006, 19:59 PM
Location: Cape Coral

Post by GTBecker »

Tony's suggestion of using Compare Match to trigger an interrupt describes a prescaler which inherently reduces the signal resolution by the divider ratio, reduces the resolution of a rate calculation, differentiation over time, and leaves remnant counts unreported to code.

If you need high rate resolution or absolute position, it's not for you.
Tom
DocJC
Posts: 112
Joined: 16 March 2006, 6:23 AM
Location: Cleveland, OH
Contact:

Post by DocJC »

Agreed, but if he is monitoring two pulse streams, at 68 kHz each, and wants some cpu time for other tasks, I would think some prescaling may be indicated.
JC
spamiam
Posts: 739
Joined: 13 November 2005, 6:39 AM

Post by spamiam »

GTBecker wrote:Tony's suggestion of using Compare Match to trigger an interrupt describes a prescaler which inherently reduces the signal resolution by the divider ratio, reduces the resolution of a rate calculation, differentiation over time, and leaves remnant counts unreported to code.

If you need high rate resolution or absolute position, it's not for you.
Well, there is no actual loss of resolution in the COUNTS, but the reporting interval is variable.

I consider the two techniques to be effectively identical, but which quantity varies is different.

In the polled technique the time period is fixed, and the count varies. In the method I described, the count is fixed, but the time varies. The math is otherwise the same. The velocity/acceleration averaging effect happens in both techniques.

Issues of counter granularity and ZX-time granularity are also significant. At very low speeds, you may have significant issues with granularity (quantization) of the distance readings. At very high speeds you will have similar issues with the time granularity (1/1024 second). Granularity concerns are present at the extremes no matter what method you use in collectiong the distance and time values.

To compensate for very low speeds when using the CTC mode counter, I suppose that you can have escape code in some way (does the watchdog timer only do resets??) so that if you do not get a counter interrupt in a certain time, then you poll the counter manually.

If you find that the temporal granuality of the interrupts generated by the counter is too coarse, lower the value in OCR1A. As a matter of fact, this COULD be done on every counter interrupt so that you can fine tune the counting threshold. It depends to a certain extent on acceleration and decel rates.

IMHO, off-loading as much of the grunt work from the CPU as possible is a good thing, this includes polling inputs.

Some designers I have spoken with try to avoid externally generated interrupts due to noise sensitivity, but internally generated interrupts are fine....

-Tony

P.S. the watchdog function will only work for resetting the cpu. To get an escape interval, you would have to use WaitForInterval(). Select your maximum acceptable interval between odometer reports. The first thing that happens in the WaitForInterval() routine is to see if a flag is cleared. [You will have the OC1A interrupt first clear that flag].

In WaitForInterval(), if the flag is not cleared, then it is time to read the counter register, subtract the reading you just made from that register, do the required calculations for the odometer (or set a separate flag for them) and exit with the first flag still set.

-Tony
Last edited by spamiam on 02 November 2006, 8:57 AM, edited 1 time in total.
Post Reply