View previous topic :: View next topic |
Author |
Message |
dossdev
Joined: 08 Jul 2009 Posts: 24
|
#define math |
Posted: Tue Jul 21, 2009 10:31 pm |
|
|
I'm attempting to do some integer math using #define's and am having trouble getting the expected results. I'm reading a pot and using the delay_ms() as a way of testing the result. MPLAB Sim might be a better way to verify. I'm expecting a result of 2000 to 12000, depending on the ADC counts. This should yield a delay of that number of mSec.
Code: |
#include <16F526.h>
#device ADC=8
#device *=8 //needed to keep from running out of ram during compile
.
.
.
#define VAR_MIN 2 ;in seconds
#define VAR_MAX 12
#define VAR_SPAN VAR_MAX - VAR_MIN ;VAR_SPAN = 10 in this example
#define VAR_SPAN_CNT (VAR_SPAN * 10000) / 255 ;approx 392
.
.
.
unsigned int8 ADC_Value;
unsigned int16 Delay_Time;
.
.
.
ADC_Value = read_adc();
Delay_Time = ((VAR_MIN * 10000) + VAR_SPAN_CNT * ADC_Value) / 10;
unsigned int8 ADC_Value;
unsigned int16 Delay_Time;
.
.
.
; turn on test LED here
delay_ms(Delay_Time); //should delay between 2000 and 12000 mSec
;turn off test LED
|
|
|
|
mbradley
Joined: 11 Jul 2009 Posts: 118 Location: California, USA
|
|
Posted: Tue Jul 21, 2009 10:40 pm |
|
|
I do not see this line in your code:
// substitue the 40000000 for your clock speed
#use delay(clock=40000000)
The compiler needs to know what the clock rate is so it can create the delays. The delays are software delay routines, and does not use a timer. |
|
|
dossdev
Joined: 08 Jul 2009 Posts: 24
|
|
Posted: Tue Jul 21, 2009 10:51 pm |
|
|
I actually do have the clock speed set - just didn't show it (and more) in the code snippit. I've verified the delay_ms() with fixed values and it it correct. Since I posted, I've been running the simulator and playing with different values for the ADC value and have found that the result is correct for ADC value = 0. There appears to be some rollover/under going on - just haven't found out why just yet. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jul 21, 2009 10:53 pm |
|
|
You can debug problems like this if you can display intermediate values.
This can be done with an ICD debugger (with a watch window), or with
a serial port with printf statements. Do you have a debugger ?
Are you using a board that has a MAX232-type chip and DB-9 connector ?
If so, you can connect the board to your PC, and put in a #use rs232()
statement to define the serial port.
If you don't have a MAX232 chip and DB-9 on your board, this thread
explains how you can still add a serial port for debugging purposes:
http://www.ccsinfo.com/forum/viewtopic.php?t=22551 |
|
|
dossdev
Joined: 08 Jul 2009 Posts: 24
|
|
Posted: Tue Jul 21, 2009 11:04 pm |
|
|
I am using an ICD2, but the '526 requires a debug adapter so I can't trace through the code, etc. I may end up adding a MAX232 and using a sw uart to do printf's. The simulator is actually pretty good for showing what is going on. Not a solution yet, but it looks like the in-line calculation blows up whenever I use the #define values. If I replace them with their numerical equivalents, the result is correct. Now I just have to figure out how to get the #define's to behave. |
|
|
dossdev
Joined: 08 Jul 2009 Posts: 24
|
|
Posted: Tue Jul 21, 2009 11:40 pm |
|
|
I've narrowed the problem down to what looks to be an issue with using the ADC variable result versus a fixed ADC value. I've tried all sizes of variables, etc. May be a compiler problem.
Code: |
Delay_Time = (20000 + (392 * ADC_Value)) / 10; //doesn't work for certain ADC values like 255
Delay_Time = (20000 + (392 * 255)) / 10; //works
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jul 21, 2009 11:52 pm |
|
|
Do you have the PCM compiler ? If so, create a test program for the
16F877 and run it in the MPLAB simulator. Use the UART1 feature of
MPLAB to display printf values in the Output window. (UART1 requires
a PIC with a hardware UART). The following post explains how to do this:
http://www.ccsinfo.com/forum/viewtopic.php?t=23408&start=1
Math problems with #define values are usually caused by:
1. Lack of parenthesis to force the intended order of math operations.
2. Lack of an 'L' on the end of numerical constants larger than 255.
3. Lack of casting of an expression to an int16 or int32, etc.
Here's an example of a program that displays intermediate values.
This will quickly show you where the problem is.
Code: |
#include <16F877.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#define VAR_MIN 2 //in seconds
#define VAR_MAX 12
#define VAR_SPAN VAR_MAX - VAR_MIN
#define VAR_SPAN_CNT (VAR_SPAN * 10000) / 255
//=====================================
void main()
{
unsigned int8 ADC_Value;
unsigned int16 Delay_Time;
printf("VAR_SPAN = %ld \r", VAR_SPAN);
printf("VAR_SPAN * 10000 = %ld \r", (VAR_SPAN * 10000));
printf("VAR_SPAN_CNT = %ld \r", VAR_SPAN_CNT);
//ADC_Value = read_adc();
ADC_Value = 100;
printf("VAR_MIN * 10000 = %ld \r", VAR_MIN * 10000);
printf("VAR_CNT * ADC_Value = %ld \r", VAR_SPAN_CNT * ADC_Value);
printf("Numerator = %ld \r", (VAR_MIN * 10000) + (VAR_SPAN_CNT * ADC_Value));
Delay_Time = ((VAR_MIN * 10000) + VAR_SPAN_CNT * ADC_Value) / 10;
printf("Delay_Time = %ld \r", Delay_Time);
while(1);
} |
|
|
|
Ttelmah Guest
|
|
Posted: Wed Jul 22, 2009 2:30 am |
|
|
Remember that ';' marks the end of an expression, _not_ a remark.
In the original defines
Code: |
#define VAR_MIN 2 ;in seconds
#define VAR_MAX 12
#define VAR_SPAN VAR_MAX - VAR_MIN ;VAR_SPAN = 10 in this example
#define VAR_SPAN_CNT (VAR_SPAN * 10000) / 255 ;approx 392
|
I'd actually expect 'VAR_SPAN', to be set to 10, then to get a syntax error for the text 'in this example'. Similarly for the next line.
Correct these, and things may start to give expected values.....
Also, in C, be _very_ careful with bracketting, in defines. '*', has a higher priority than '-', so, if the defines didn't have their incorrect terminating ';', or the remarks, but were:
Code: |
#define VAR_SPAN VAR_MAX - VAR_MIN
#define VAR_SPAN_CNT (VAR_SPAN * 10000) / 255
|
Then VAR_SPAN_CNT the equation would expand as:
(VAR_MAX - VAR_MIN * 10000)/255
Which will evaluate VAR_MIN * 10000, subtract this from VAR_MAX, then divide the result by 255. Not what is expected I think....
Best Wishes |
|
|
mbradley
Joined: 11 Jul 2009 Posts: 118 Location: California, USA
|
|
Posted: Wed Jul 22, 2009 3:04 am |
|
|
I just now cought this, but it is mentioned above,
Quote: | #define VAR_SPAN VAR_MAX - VAR_MIN ;VAR_SPAN = 10 in this #define VAR_SPAN_CNT (VAR_SPAN * 10000) / 255 ;approx 392
Delay_Time = ((VAR_MIN * 10000) + VAR_SPAN_CNT * ADC_Value) / 10;
|
perhaps, the paranthesis?
I like to look at defines as a global find and replace.
#define VAR_SPAN ( VAR_MAX - VAR_MIN ) |
|
|
mskala
Joined: 06 Mar 2007 Posts: 100 Location: Massachusetts, USA
|
|
Posted: Wed Jul 22, 2009 7:02 am |
|
|
Ttelmah wrote: | Remember that ';' marks the end of an expression, _not_ a remark. |
I second this remark. You must not use ';' in the #define followed by comment. |
|
|
andrewg
Joined: 17 Aug 2005 Posts: 316 Location: Perth, Western Australia
|
|
Posted: Wed Jul 22, 2009 7:24 am |
|
|
Code: | Delay_Time = (20000 + (392 * ADC_Value)) / 10; //doesn't work for certain ADC values like 255 |
392 is an int16, that will promote ADC_Value to be an int16, if it isn't already. The result of the multiplication will also be an int16, which will cause problems for values of ADC_Value greater than 65535/392 = 167.
The solution is to cast 392 or ADC_Value to int32, or to use the CCS _mul() function that automatically expands the size of the result. i.e.
Code: | Delay_Time = (20000 + _mul(392, ADC_Value)) / 10; |
_________________ Andrew |
|
|
dossdev
Joined: 08 Jul 2009 Posts: 24
|
|
Posted: Thu Jul 23, 2009 9:29 pm |
|
|
Got it all sorted out. My biggest problem was the order of precedence in the #define statements.
Thanks so much for everyone's insightful input.
BR's. |
|
|
pgin
Joined: 01 Jun 2009 Posts: 1 Location: Canada
|
|
Posted: Fri Jul 24, 2009 11:49 pm |
|
|
Here is your source code. The errors are fixed and documented.
Code: | #include <16F526.h>
#device ADC=8
#device *=8 //needed to keep from running out of ram during compile
.
.
.
// #define VAR_MIN 2 ;in seconds
// comment take // In a define using ; is acceptable. You can do
// #define A a=2; b=3; c=4;
// The expansion will created 3 assignations.
// If you type A; The expansion will produced
// a=2; b=3; c=4; ;
// The last semicolumn is the one after the A
#define VAR_MIN 2 // in seconds
#define VAR_MAX 12
#define VAR_SPAN VAR_MAX - VAR_MIN ;VAR_SPAN = 10
#define VAR_SPAN (VAR_MAX - VAR_MIN) // VAR_SPAN = 10
// Always put parentesis around expression. you never know where it will be used.
#define VAR_SPAN_CNT (VAR_SPAN * 10000) / 255 ;approx 392
#define VAR_SPAN_CNT (((VAR_SPAN) * 10000) / 255) //approx392
// When in a define (macro), you refer to an argument, always put parenthesis around it to avoid wrong evaluation order.
// In your code, you had VA_MAX - VAR_MIN * 10000 / 255
// Evaluation order will be VA_MAX - ( ( VAR_MIN * 10000 ) / 255 )
// Also, in order to have a better accuracy in the computing, you may cast and group the operation.
// Example : If VAR_SPAN happen to contain a variable, then 10000 / 255 will be evaluted immediately.
// #define BAD_ORDER AAAA a * 100 / 255 // always 0
// better
// #define GOD_ORDER AAAA ((a * 100) / 255 )
.
.
.
unsigned int8 ADC_Value;
unsigned int16 Delay_Time;
.
.
.
ADC_Value = read_adc();
// depending on the compiler wou are using and the with of the ADC
// results, chances are that you will have an overflow.
// VAR_MIN * 10000 = 20000, ok for 16-bit
// VAR_SPAN_CNT * ADC_Value : 392 * 256 required 17-bit, you need 24 (available with some DSPs) or 32 bits (with PIC and CCS)...
Delay_Time = ((VAR_MIN * 10000) + VAR_SPAN_CNT * ADC_Value) / 10; |
|
|
|
|