passing variables from task to task
passing variables from task to task
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
Casen Davis
Check application note AN-210 - Sharing Data Between Tasks and there is sample code as well.
Mike Perks
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:
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
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
Casen
Casen Davis
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.
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.stevech wrote:Or connect to the INT0 pin and get an edge-triggered interrupt to use with waitForInterrupt() in ZBasic.
Mike Perks
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
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.cdavis wrote:This seems like it might be a bit fast for the WaitForInterupt() function.
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
passing variables from task to task
FWIW, I suggest not adopting that cycle. There is a period between the... interval timer to control resetting the timer, letting it accumulate, and reading the result.
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
Re: passing variables from task to task
Good point. At that high frequency of signals, it will be very hard to clear the counter. Subtraction is better.GTBecker wrote:FWIW, I suggest not adopting that cycle. There is a period between the... interval timer to control resetting the timer, letting it accumulate, and reading the result.
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
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)
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
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.cdavis wrote:This makes sense for a constant pulserate, but won't the count be wrong if the pulserate accelerates and deccelerates?
- Don Kinzer
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.
If you need high rate resolution or absolute position, it's not for you.
Tom
Well, there is no actual loss of resolution in the COUNTS, but the reporting interval is variable.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.
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.