How to scale a number/range of numbers in c

3.5k views Asked by At

I want to be able to display the altitude in ft ranging from 0ft to 20000ft on my LCD module. The numbers are read from a potentiometer that I can rotate to change the values. At the moment the potentiometer displays from 0 to 1023 but I need to scale these appropriately so they read from 0-20000ft. As well as this I want the line below to display a 'bar graph' to represent the altitude increasing. The LCD has 20 blocks per line so the bar graph can range from 1 block to 20 blocks.

   sprintf(buf, "Altitude: %d    ", DELVAL2);    // display altitude level
   lcd_putxy(1,0,buf);
   for (delay = 0; delay < 50000; delay++);      // introduce a delay

   sprintf(buf, "*", DELVAL2 );                  // display bar graph
   lcd_putxy(2,0,buf);
   for (delay = 0; delay < 50000; delay++);      // introduce a delay

This is my code so far. It reads the value from the potentiometer DELVAL2 and displays it on the LCD. Can someone please explain a method of how I can scale the data appropriately to produce an altitude and bar graph.

3

There are 3 answers

0
chux - Reinstate Monica On

To scale [0...1023] to [0...20000] multiply by 20,000 and then divide by 1023.

int altitude = (int) ((potentiometer*20000L + 1023/2)/1023);

Code uses long multiplication as int may only be 16-bit on a micro-controller. Not necessary if INT_MAX == 0x7FFFFFFF.

The + 1023/2 is to provide a rounded conversion.

2
O. Jones On

Presumably buf is a 16-bit integer, and contains the pot value. Presumably you can rely on it being limited to the values [0, 1023].

If you do

    int scaledbuf;
    scaledbuf = buf >> 5;

you'll get a value of scaledbuf in the range [0, 32736]. You could then do

    if (scaledbuf > 20000) scaledbuf = 20000;

At the cost of sacrificing some of the range of your pot, this will give you a value in the range [0,20000] without doing any multiplications, only a left shift.

But, you're torching cycles in your delay loops anyway, so you can probably manage the cost of a multiplication by 20.

     int scaledbuf;
     scaledbuf = buf * 20;
     if (scaledbuf > 20000) scaledbuf = 20000;

This preserves considerably more of your pot's range while keeping you in the realm of 16-bit arithmetic.

3
Mazi On

If your DELVAL2 is in range 0-1023 you can scale it to 0-20000, but you cannot get bigger resolution than (1/1024) * 20000. Why? Minimum value (single bit) you can read is 1/1024. Your maximum value is 20000, so one bit change on DELVAL2 will result in 20000/1024 = 19,53 change on scaled value.

You can calculate how to scale it, it's already described on stackoverflow: How to scale down a range of numbers with a known min and max value

You must keep in mind that you may fall into floating point operation and this is something that you may want to avoid. For example, you can do something like this

scaled = (DELVAL2 * 1953) / 1000;

instead of

scaled = DELVAL2 * 19.53;

Keep in mind that maximum value that you can get inside this computation will be 1024*1953 = 1999872, so you need 32 bit variable. Additional cast may be needed, depending on your architecture and compiler, eg

scaled = (DELVAL2 * (uint32_t)1953) / 1000;

About second question - bar graph - you are on good way. Calculate how many symbols do you need and draw them. It's scaling down instead of up. Simple division should be enough. When you know how many symbols you need, generate them in simple loop

for(int i = 0; i < num; i++)
    buf[i] = '*';
buf[i] = 0; //last symbol is 0 to stop drawing
lcd_putxy(2,0,buf);