I am getting random errors in my formula; could it be to do with this error message?
Serious Warning: Possible sw stack corruption, function '__div_16_16' called by more than one asynchronous thread (main/Task, interrupt, interrupt low)
div_16_1_0004 appears in my CASM file in these places:
A long maths formula within TMR0 interrupt
Various formulae outside interrupt
In Flowcode’s LCD routine
Is it corrupting because the interrupt takes over half way through the __div_16_16 routine and picks up the wrong intermediate results? I can understand how that explanation, if true, would cause the value returned by the interrupt to be wrong but less clear how that would corrupt the formulae outside the interrupt, as the formula inside would complete before returning to outside.
Assuming that division in the formulae is the problem, solutions I can think of are:-
1) I note that when I divide by 2 rather than say 20 __div_16_1_00004 lines are not present. So if I converted my interrupt formula into divisors that were all 2, 4, 8, 16, etc. numbers would it avoid ever needing to use __div_16_16? I am only dividing to shorten the value so that Integer1 * Integer2 does not exceed 32k. Thus /16 or /32 is as good as /20 for this. Can I /16 to avoid __div_16_1_00004 lines or does it have to be /2/2/2/2 instead?
2) Similar to 1) is to in C Code:
Convert integers to binary
Shift byte in lower registers to right by a few places to loose the least significant bits, i.e. effectively dividing by 2, 4, 8, 16, etc. Also shift part of upper one to lower one.
Convert back to decimal.
But no idea how to write that in C.
3) Turn off/on the interrupts before/after every division outside the interrupt, but hard to do in your LCD routine. Also it upsets my timing.
4) Make the formula run quicker so that there is less chance of them clashing in time. I can cope with occasional errors but there are far too many at the moment. If I divide by 2, 4, 8, 16 instead of 3, 5, 20, etc. do the formulae execute much faster? Also I’d have to do the same thing in your LCD routine which is probably impossible.
5) Work in the 32bit realm when doing the interrupt formula, so do not need to divide the 2 integers to shorten them, and then somehow convert the most significant bits back to 16bits without doing a division.
The solution for the interrupt should be the one that executes fastest.
I also call __mul_16s_16 in the interrupt to multiply 2 integers, but not currently outside it. If I were to call it outside I assume I’d have similar error problems, but the above neat solution 1) is not going to help me as the 2 integers to be multiplied are continuously varying and so not 2, 4, 8, 16, etc. Is that correct
Formula returning wrong answer
- Steve
- Matrix Staff
- Posts: 3433
- Joined: Tue Jan 03, 2006 3:59 pm
- Has thanked: 114 times
- Been thanked: 422 times
Re: Formula returning wrong answer
Ideally, your interrupt routines should be as short and simple as possible and any lengthy or complex code should be done in the main program loop. So I'd suggest that rather than perform the calculations in your interrupt routine, you simply set a flag to say the calculation needs to be done (e.g. set a global "DO_CALC" variable to 1). At a convenient place in your main loop, you should check is this flag is set and then perform the calculation there. Remember to reset your flag after performing the calculation.
Re: Formula returning wrong answer
Interesting idea but I fear that it may not be practical as my main loop is quite short, with lots of sub macros in which the processor is spending most of its time. So I may miss the calculation occasionally if I don’t carefully put my flag checks everywhere. That is why I put the formula in the interrupt to be sure it is executed before the next interrupt comes along 500us later, no matter what macro was interrupted. E.g. the LCD macro alone takes >>500us so I’d have to pepper it with flag checks. E.g. write one value to LCD, check flag and calculate if flag set, move cursor, check flag and calculate, write next value, check flag and calculate, etc. Would even that be fast enough?
Do you think my option 1 would work instead and is my problem actually due to _div_16_1_00004?
Do you think my option 1 would work instead and is my problem actually due to _div_16_1_00004?
- Steve
- Matrix Staff
- Posts: 3433
- Joined: Tue Jan 03, 2006 3:59 pm
- Has thanked: 114 times
- Been thanked: 422 times
Re: Formula returning wrong answer
You might find that the complex calculation should only be done at the time you present the data onto the LCD. If this is the case, then performing the calculations only just before the LCD write would make sense.
Your suggestion of dividing by multiples of 2 will certainly make the calculations simpler to perform.
Your suggestion of dividing by multiples of 2 will certainly make the calculations simpler to perform.
Re: Formula returning wrong answer
I’d have to save 2000 sets of 2 values from the interrupt (which reads 2 A/Ds) into an integer array (are integer arrays possible?) and then divide /multiply/ sum all these 4000 values in the array before sending to LCD. I guess that might work, as although the A/D reads have to be performed at exactly the right 500us spacing, the calculation can be delayed as LCD is re-written only every 2000 interrupts. Mighty big array?! Maybe have to have several smaller arrays or do sub calculations every 40 samples and save the intermediate answer in a another array?Steve wrote:You might find that the complex calculation should only be done at the time you present the data onto the LCD. If this is the case, then performing the calculations only just before the LCD write would make sense.
Option 1 works well. Using 2, 4, 8,16, etc. divisors gets rid of all the div_16_1 lines and speeds up the calculations. No more errors.
OK a long 200us of the 500us interrupt time is taken up with all these A/D reads and formula. But that is down from 300us before I modded the formula and reduced the TACQ time, so it leaves enough time to do all the other tasks, except LCD writes for which I have to turn off the interrupts.
Re: Formula returning wrong answer
To avoid my multiplications and divisions inside and outside the interrupt clashing I am having to be a bit inventive. E.g. by doing additions instead of multiplications and then dividing by 2, 4, 8, etc. instead of 3. E.g. Instead of value1 = value *10/3 I will approximate to value1 = value *5/2
Is value1 = value 2 + value 2+ value 2 + value 2 + value 2
The same as value 1 = 5 * value 2 as that avoids a multiplication?
Is value1 = value 2 + value 2+ value 2 + value 2 + value 2
The same as value 1 = 5 * value 2 as that avoids a multiplication?
- JonnyW
- Posts: 1230
- Joined: Fri Oct 29, 2010 9:13 am
- Location: Matrix Multimedia Ltd
- Has thanked: 63 times
- Been thanked: 290 times
- Contact:
Re: Formula returning wrong answer
Hi Echase. Firstly, yes, no problems with int arrays, though you may be limited by whatever chip you are using (hardware is way over my head though ion a pic8 this will be 127 element array).
Secondly, is what you are doing an averaging program? If so you can save any large calculations by using a moving average filter. If you already know what this is or this isnt what youre doing then I wouldnt bother reading further.
Moving average typically contains three pieces of data:
* An array of the data being processed in a circular buffer
* A 'number of elements' counter
* The total value of all the elements added together
When you read a new piece of data you add it to your buffer and alter the values accordingly. Sorry, this example is written in pseudo-C but Flowcode can do any of this in a flowchart - I will post a sample if youd like. It assumes a 64-element array:
The average can now be calculated easily at any time using the code:
This can be put in a macro and read at any time without scanning through a large buffer.
Hope this is what youre after,
Jonny
Secondly, is what you are doing an averaging program? If so you can save any large calculations by using a moving average filter. If you already know what this is or this isnt what youre doing then I wouldnt bother reading further.
Moving average typically contains three pieces of data:
* An array of the data being processed in a circular buffer
* A 'number of elements' counter
* The total value of all the elements added together
When you read a new piece of data you add it to your buffer and alter the values accordingly. Sorry, this example is written in pseudo-C but Flowcode can do any of this in a flowchart - I will post a sample if youd like. It assumes a 64-element array:
Code: Select all
total_value = total_value + new_value
if (current_elements >= max_elements) // Buffer is full so have lost an element of data
{
total_value = total_value - data_array[data_index] // Remove the old element from the total
}
else
{
// Have not reached the limit of elements yet
current_elements = current_elements + 1
}
// Add the new value to the buffer
data_array[data_index] = new_value
data_index = (data_index + 1) & 63 // Cycle the 64 element buffer - keeping it a power of 2 saves a division
Code: Select all
if (current_elements == 0)
return = 0
else
return = total_value / current_elements
Hope this is what youre after,
Jonny
-
- Posts: 594
- Joined: Thu Sep 17, 2009 7:52 am
- Location: Belgium
- Has thanked: 63 times
- Been thanked: 102 times
Re: Formula returning wrong answer
Wasn't it 128 max elements in an 8 bit PIC microcontroller ?JonnyW wrote:Hi Echase. Firstly, yes, no problems with int arrays, though you may be limited by whatever chip you are using (hardware is way over my head though ion a pic8 this will be 127 element array).
You maybe meant to say from 0 to 127 elements ?
Anyways, JonnyW's method is the best way to save up memory in your microcontroller.
BR,
Nicolas L. F.
- JonnyW
- Posts: 1230
- Joined: Fri Oct 29, 2010 9:13 am
- Location: Matrix Multimedia Ltd
- Has thanked: 63 times
- Been thanked: 290 times
- Contact:
Re: Formula returning wrong answer
Yes, sorry, 128. its probably cos the 7 is next to the 8 on my keyboard.