Math issues on Attiny

Go To Last Post
20 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello

I have been having difficulty in calculating
a speed of a projectile between two light-gates.
The main reason is that multiplication by high numbers
and division with doubles is not working as it should.

The relevant snippet of code is as follows:


#define F_CPU 8000000UL   // 8Mhz clock
#define PRESCALER 1024
#define TIMERFREQUENCY F_CPU/PRESCALER  // (7812 Hz)
#define GATE_SEPARATION 0.078   // in meters

...........
...........

int timer0 = TCNT0;
int timer1 = TCNT1;
	
	
double projectileTime = timer0/TIMERFREQUENCY; // In Seconds    
	
double projectileVelocity = GATE_SEPARATION/projectileTime;   //   v = d/t : v = (0.078m / t-seconds)  :   in m*S^-1


TransmitUART(timer0); // timer0
TransmitUART(timer1); // timer1
TransmitUART(projectileTime);
TransmitUART(projectileVelocity); 

The result is always that the projectile velocity is 0. Or it sometimes gives me some garbage like -2 or -4.

With the UART library i'm using I have confirmed that
transmitting doubles works fine and just gets rounded of to the nearest int.
I've also tried using ints , casting to ints before the transfer etc.
Basically i've spend about 4 hours of my time just on those 4-5 lines of code yesterday and I've tried everything I could think of but it's not working.
I found that the problem is specifically at the

double projectileTime = timer0/TIMERFREQUENCY;

timer0 is in the range of single - double digit integer.

Could someone help me out please?

Probably not relevant:
AS6
Attiny4313

Also , could someone enlighten me as to why something like this gives a false result ? :

int varr = 93;

int(or double) tmp = (varr*1000)/4553; // false result

int(or double) tmp = (varr*10)/4553; // Works fine when it is times 10 , but 1000 produces a false result.

Is there overflow happening somewhere?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
double projectileTime = timer0/TIMERFREQUENCY;

Do you mistakenly believe that just because there's a double on the left of this that the calculation on the right will be done as float/double? It won't. The calculation can only produce one of 9 distinct integer results: 0 .. 8

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The calculation can only produce one of 9 distinct integer results: 0 .. 8

And as "timer0" contains the 8-bit value TCNT0 (max 255) and you are dividing by 7812 that result would always be 0.

Quote:

Or it sometimes gives me some garbage like -2 or -4.

Hmmm--timer0 is a signed variable. Could TCNT0 be sign-extended?

In any case, a few well-chosen casts should help.

In nearly all cases, I would avoud the floating point and work in "millisecond" or "microseconds" and similar units for velocity.

Quote:

Also , could someone enlighten me as to why something like this gives a false result ? :

Code:

int varr = 93;

int(or double) tmp = (varr*1000)/4553; // false result

int(or double) tmp = (varr*10)/4553; // Works fine when it is times 10 , but 1000 produces a false result.

Is there overflow happening somewhere?


Well, yes. What is the largest value for an "int" with your chosen toolchain? See the documentation, or limits.h or look for 32767. ;) 1000*93 is 93000. That doesn't fit into a 16-bit "int" (which would be the default promotion).

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes I do mistakenly believe that :)
So you are saying that such a calculation can only have a result range of 0-8 ?
Okay so how do I go around this?
How do I perform this calculation?
Do I have to define the 'timer0' as a double aswell? What about the constant (timerfrequency)?

Thanks in advance

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Either force the TIMERFREQUENCY to be a f-p expression:

#define F_CPU 8000000.0   // 8Mhz clock
#define PRESCALER 1024.0
double projectileTime = timer0 / TIMERFREQUENCY;

or cast one of the operands:

double projectileTime = (double)timer0 / TIMERFREQUENCY;

You only need to make one of those macros a f-p constant. However, I note that you are economical with parentheses and the space key. Bear in mind that any expression the Compiler sees is as the result of the pre-processor textual replacement.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Theusch , thanks for the answer.

Concerning the 'int' overflows. Yes I have considered what you said in the 2nd-3rd hour of hair pulling over this code and have tried long and long long and anything else I could think of and that didn't work.

Yes I initially wanted to work in the milliseconds range but the above problem comes up.

....
....
#define GATE_SEPARATION 78 mm    

   
double projectileTime = (timer0*1000)/TIMERFREQUENCY; // In milliseconds   
   
double projectileVelocity = GATE_SEPARATION/projectileTime;   // v = (78mm / time in mS)  

This is what I initially started with and that doesn't work either , well you yourself explained why.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'll hazard a guess, though if you read my recent thread, you'd flee from my advice

#define F_CPU 8000000UL   // 8Mhz clock
#define PRESCALER 1024
#define TIMERFREQUENCY F_CPU/PRESCALER  // (7812 Hz)
#define GATE_SEPARATION 0.078   // in meters

...........
...........

double timer0 = TCNT0;
double timer1 = TCNT1;
   
   
double projectileTime = timer0/TIMERFREQUENCY; // In Seconds  

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At the very least, it makes sense to wrap the TIMERFREQUENCY macro in parentheses. A / B / C != A / ( B / C ), if the first is evaluated left to right, which is possible according to language rules. Rather, A / ( B / C ) == ( A * C ) / B. To be safe, any macro that is a compound expression should be wrapped in parens because, since it's just a text replacement, the expected order of operations can be easily circumvented. For instance,

#define A 1 + 2
#define B ( 1 + 2 )

// This will likely give the "wrong" value
//	because of operator priority
int a = 4 * A; // 4 * 1 + 2 = 6

// This gives the "correct" value
//	because the parentheses force order
int b = 4 * B; // 4 * ( 1 + 2 ) = 12 

I would also prefer to do this entirely with integer values ( likely in units of milliseconds given the high prescaler value ), no need for floating point. But, either of David's earlier suggestions should work just fine. No need to force everything to be double from the beginning.

Martin Jay McKee

As with most things in engineering, the answer is an unabashed, "It depends."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
Either force the TIMERFREQUENCY to be a f-p expression:

#define F_CPU 8000000.0   // 8Mhz clock
#define PRESCALER 1024.0
double projectileTime = timer0 / TIMERFREQUENCY;

or cast one of the operands:

double projectileTime = (double)timer0 / TIMERFREQUENCY;

You only need to make one of those macros a f-p constant. However, I note that you are economical with parentheses and the space key. Bear in mind that any expression the Compiler sees is as the result of the pre-processor textual replacement.

David.

I've tried your advice.

#define F_CPU 8000000.0
#define PRESCALER 1024.0
#define TIMERFREQUENCY 7812.0  //F_CPU/PRESCALER   /Hz
#define GATE_SEPARATION 0.078  

Seems to work now. Casting the timer to a double didn't work but I might not have tested it correcly.

Anyhow , thanks for the advice I feel abit better now :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I suggest that you read and re-read the replies.

You should understand where and why you need f-p expressions.

e.g. if you #define TIMERFREQUENCY 7812.0
there is no need for F_CPU or PRESCALER to be f-p values.

However, if you alter F_CPU, you need to manually change the 7812.0

The whole point of "F_CPU" expressions is that you only change F_CPU and the timing will still be correct.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

have tried long and long long and anything else I could think of and that didn't work.

Tell us EXACTLY how you tried. If you simply made the receiving variable in an assignment long then that will not be enough. (Re-read clawsons post above for why this is so.)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now might be the time to point out that in avr-gcc double has the same precision as float.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So, Shagas, what tripped you up is the same thing that often gets me. As programmers, we imagine C would know to do a floating point operation since the destination is a floating point variable, but alas, C looks at the right side and says, "These are both ints, so I'll do an integer operation."

I seldom use floating point. In fact, I don't think I've ever used a floating point value in an AVR, even when it would have made sense. I guess I just think in integers, often cardinals. It gets me when dealing with integers of different length.

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Shagas

It might be that scaled integer arithmetic can do the job for you.

eg if the light gates are 1m apart and you measure the time in (say) increments of tens of mS then simply inserting a decimal point after the two rightmost digits gives you m/s.

depending on the accuracy you need you can sometimes also use shifts. eg does it matter whether you divide by 100 or 128 (7 bit shift).

ATtiny's can have little ram and no hw mul so the ideas above can sometimes be handy.

let us know how you go.

[captcha'ed!!]

regards
Greg

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the replies, they helped alot and I learned a few important things.

So I defined F_CPU and the PRESCALER as f.p
and the rest stems dynamically from that as advised.
Also I added brackets in the define, yes I remembered my teacher talking about that specifically aswell.

Question: Isn't it then enough to just define F_CPU ORthe PRESCALER as f.p? Or does this only apply when the variable that we are assigning the result to is already defined as a f.p?

#define F_CPU 8000000.0
#define PRESCALER 1024.0
#define TIMERFREQUENCY (F_CPU/PRESCALER)        
#define GATE_SEPARATION 0.078  // in m

The code works now so I can finish up the project.(single stage coil accelerator). The results are in the attached file (picture)

Does anyone by any chance know how to format code in AS6?
I've tried looking a few weeks ago and couldn't find how to do it. In netbeans I just press shift+alt+f.

Thanks again for the replies.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hmm , don't know why the picture is unclickable but if you zoom in with your browser then you can make out what it says.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Question: Isn't it then enough to just define F_CPU ORthe PRESCALER as f.p? Or does this only apply when the variable that we are assigning the result to is already defined as a f.p?

See the C standard - I think it's 6.3.1.8 that has the info you need.

(but basically, yes, an integer operation will have the operands promoted to the "higher" type double>float>int but there's also issues regarding whether the int is signed or unsigned so the rule isn't quite this simple).

EDIT: a Google for "6.3.1.8" leads here - see the grey boxes in the first answer:

http://stackoverflow.com/questio...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh and I don't believe AS6 has auto-indent. But there is a GNU tool called indent (indent.exe when built for Windows) you could add this as an external tool in AS6 and assign it to a menu entry or a toolbar button (or chosen key press).

PS when I google "indent for Windows" the first hit is:

http://gnuwin32.sourceforge.net/...

(it's also in WinAVR if you happen to have an old copy of that installed)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AS6 has a to format some source files.

Personally, I am happier with indent.exe that I can configure to my taste.

Likewise, the Arduino IDE has that will tidy up indentation, but is not much good for anything else.

Yes, I like to tidy up code as an automated process. But anyone can subsequently maintain any style without even engaging their brain.

I always feel that formatting the code is the first step in understanding and debugging code.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to note that indent (the GNU tool) does not know about "0bNNNNNN" syntax so any 0b101 will be "0 b101" after indent which breaks it. I have thought of submitting a patch as it's the one thing that (for me) ruins an otherwise perfect tool.