Go Down

Topic: tempAmplified = (long) 10000*100*(1.10/1024)*ADCT : Pls clarify! (Read 675 times) previous topic - next topic

GolamMostafa

We are building, as a part of Lab work, a Temperature Meter using UNO, LM35, and 4-digit 7-seg display unit. Everything is fine except that we don't have clear understanding why we need to insert (long) to the RHS of the formula tempAmplified = (long) 10000*100*(1.10/1024)*ADCT in order to get the correct result.  Please note that we are migrators from Assembly Language Platform to Embedded-C Platform.  





Code: [Select]
byte ccArray[10] = {
  0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
                   };

  unsigned int ADCT;                     //ADCT is updated by analogRead(A5) function
  unsigned long tempAmplified;      
  byte x = 0b111111;                   //control cc-pins of the display devices
  byte y;
  byte indexArray[8];                   //d7d6 (d5d4.d3d2) d1d0

void setup()
{
  //Serial.begin(9600);
  DDRD = 0xFF;
  DDRB = 0xFF;
  analogReference(INTERNAL); //1

  TCCR1A = 0x00;
  TCCR1B = 0x00;
  TCNT1 = 0x48E5;   //3-sec Time Tick
  TCCR1B = 0x05;    //TC1 run with CLKTC1 = sysCLK/1024 15625 Hz
}

void loop()
{
  ADCT = analogRead(A5);
  tempAmplified = (long)10000*100*(1.10/1024)*ADCT; //10+E4 times amplified
  getIndices();            //getting indices for the decimal digits of the decimal value of the binary result
  displayRefresh();     //recurrent refresh of the multiplexed display
  timeDelay3sec();     //user defined time delay function. Arduino's delay(3000) does not help.
}

void getIndices()
{
  for (int i = 0; i<8; i++)
  {
    indexArray[i] = tempAmplified % 10;
    tempAmplified = tempAmplified / 10;
  }
}

void displayRefresh()
{
  for(int i = 5, j=0; i>=2, j<4; i--, j++)
  {
    PORTD = ccArray[indexArray[i]];   //to show d5d4.d2d1 among d7d6d5d4d3d2d1d0
    y = x;
    bitClear(y, j);
    PORTB = y;
    delay(1);          //delay is required to synchronize digit's positional data and its cc-pin
  }
}

void timeDelay3sec()
{
    while (bitRead(TIFR1, 0) != HIGH)
   {
      displayRefresh();
   }
  TIFR1 = 0x01;             //TC1's overflow flag is cleared
  TCNT1 = 0x48E5;        //3-sec pre-set value is re-loaded
}

Delta_G

Code: [Select]
tempAmplified = (long) 10000*100*(1.10/1024)*ADCT

The 10000*100 gets evaluated first.  Both of those numbers will fit into a 16 bit int so that's how they are both treated.  Since they are both 16 bit int, the result of their multiplication will also be a 16 bit int.  But the result, 10000000 won't fit into a 16 bit int and overflows it giving you the wrong answer. 

So they cast the first number there to a long.  Now that it is a long you have a long times an int and that gives a long where 1000000 will fit.  Another way to get the same thing would have been like this:

Code: [Select]
tempAmplified =  10000L*100*(1.10/1024)*ADCT

where the L tells the compiler to treat that number as a long.
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Coding Badly


Given the datatypes of ADCT and tempAmplified unsigned long / UL is the correct choice.


GolamMostafa

Quote
Both of those numbers will fit into a 16 bit int so that's how they are both treated.  Since they are both 16 bit int, the result of their multiplication will also be a 16 bit int.
When two 16-bit binary numbers are multiplied, the result is 32-bit binary; why are you saying that it is 16-bit? Is it a typo mistake? 

MarkT

When two 16-bit binary numbers are multiplied, the result is 32-bit binary; why are you saying that it is 16-bit? Is it a typo mistake? 
No, that's not how 8 bit microcontroller CPUs work!  They only multiply 8 by 8 to give 16 bits.  Anything
more ambitious is handled by the compiler / runtime library.

In C operators return the default integer size always unless one of the arguments is larger.  On the ATmega
Arduinos the default integer size is 16 bits, so everything is 16 bit unless you force it otherwise.  So
16 x 16 gives 16.  32 x 16 gives 32.

Most implementations of C have 32 bits as the default size, but this is a tiny microcontroller with 2k of
RAM total, everything is chosen for compactness and frugality, hence the default int size is 16 bit.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Delta_G

When two 16-bit binary numbers are multiplied, the result is 32-bit binary; why are you saying that it is 16-bit? Is it a typo mistake? 
Nope, no typo there.  The processor will do everything in 16 bits if the multiplicands all fit in 16 bits.  Note, I didn't say the real on paper math result would be that, just that the processor will do it that way. 
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

GolamMostafa

Quote
So 16 x 16 gives 16.  32 x 16 gives 32.
In assembly language, we extended the basic '8-bit x 8-bit' multiply instruction of ATmega32A to accomplish multiplication of other higher data sizes through the process of shift and addition.

Based on your post, I performed few simple exercises (given below), and I have come to understand that the casting what you have said forcing provides the required result through the same process of shift and addition. Many many thanks for explaining the issue in a simple language.

Code: [Select]
//0x7123 * 0xA678 = 0x4991 BA68
uint16_t z1 = 0x7123;
uint16_t z2 = 0xA678;  

uint32_t z3 = z1*z2;
Serial.println (z3, HEX);    //prints: BA68   (Correct!)


Now, forcing the Compiler/MCU to produce full 32-bit result through casting
Code: [Select]
uint32_t z3 = (uint32_t) z1*z2;
Serial.println (z3, HEX);    //prints: 4991BA68   (Correct!)


As a matter of curiosity, I performed the above multiplication process using do-while loop (repetitive additions) where I declared the data size of the destination as 32-bit long. I found correct result. (When using * operator for multiplication, we need casting; + operator does not require it!)

Code: [Select]
//0x7123 * 0xA678 = 0x4991 BA68
uint16_t z1 = 0x7123;
uint16_t z2 = 0xA678;

uint32_t z3 = 0;

do
{
  z3 = z3 + z1;
  z2--;
}
while (z2 != 0);
Serial.println(z3, HEX);   //prints: 4991BA68   (Correct!)

aarg

Code: [Select]
tempAmplified = (long) 10000*100*(1.10/1024)*ADCT

The extension of 10000*100 into 32 bits is a non-related issue because the result of that is multiplied by a float, which leads to an implicit type conversion to float. There is no computational advantage here. The compiler can optimize constant arithmetic expressions, so since the end result is a float there is no reason to not use floats:

Code: [Select]
tempAmplified = (long) 10000.0*100*(1.10/1024)*ADCT

It will compile as
Code: [Select]
tempAmplified = (long) <float constant>*ADCT
where <float constant> has the value 10000.0*100*(1.10/1024).
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

GolamMostafa

Quote
since the end result is a float there is no reason to not use floats:
It can be found from the display unit of the OP that the required end result is not float at all. We want flat 32-bit unsigned integer from which we will extract eight decimal digits for the decimal value of the 32-bit binary result. We will discard left-most two digits and right-most two digits. The remaining 4-digit represents the actual temperature with 2-digit precision to the right of the decimal point. We have used the casting -- (long) -- to tell the compiler that it has to produce 32-bit integer value regardless of the presence of the floating point numbers in the expression. I think that the expression is evaluated as:

Code: [Select]
===> tempAmplified = (long) 10000*100*(1.1/1024)*ADCT;
===> tempAmplified = (long) 1074.22*ADCT;
===> tempAmplified = (long) 0x0432*ADCT;


Moreover if we use float temP = 100*(1.1/1024)*ADCT;, we have to look for a function 7seg(temP, 2) to drive the 4-digit 7-segment multiplexed display unit.

Delta_G

I'm not sure but I think if you want the long cast to go with the whole expression after it is evaluated like you wrote there then you need to put the math expression all in a set of parenthesis.  I would do it anyway just because parenthesis are free and make it explicit what you mean.  Even if the compiler understands you, someone reading the code might need a little help.
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

aarg


Why use float? change
10000*100*(1.1/1024)*ADCT

to
10000L*100*ADCT*11/10/1024

the integer truncation still allows 6 decimal digit accuracy, which greatly exceeds the resolution of the ADC.
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.


Coding Badly

...integer truncation still allows 6 decimal digit accuracy...
Adding 5 at the appropriate spot would bump that a scosh.   :)


christop

Why use float? change
10000*100*(1.1/1024)*ADCT

to
10000L*100*ADCT*11/10/1024

the integer truncation still allows 6 decimal digit accuracy, which greatly exceeds the resolution of the ADC.
But with the maximum value of ADCT (1023) that overflows a long (even an unsigned long) variable. Divide by 10 earlier in the calculation to avoid overflow:

10000L*10*ADCT*11/1024

Go Up