new shared library for fixed point

22 Oct 2010

I have shared a new library that supports fixed point arithmetic.  This allows you to get a few digits of floating point precision without using floats or doubles, which you may have noticed are VERY SLOW since the M3 doesn't have an FPU.  It has a templatized C++ class wrapper around it, so you can declare a fixed point variable and use it in expressions with normal ints and floats.  The template allows you to specify how many bits for the integer portion and how many for the fractional portion.  It also has sin/cos/sqrt and other functions.

It took my ISR, which was using float to do HSV to RGB conversion, from over 400uS to about 100uS, and I still think there's more left.

fixedpoint

To be clear, I didn't write this, the author is credited in the library description.  I have added a few things however.

23 Oct 2010

Hi Steve,

a Fixed Point library may really come in handy for some calculations. But for something so simple like HSV (or HSB) to RGB conversion one actually dosen't even need fixed point math. You can do the whole conversion with just integer calculations, if you know, what value range you want to achieve for R, G and B (0 to 255 for 8-Bit-PWM for example) and if you are then willing to accordingly set the value range for Hue and Saturation to keep the math simple and fast.

Of course if you just stick to the formulas given in Wikipedia for HSV-to-RGB-conversion, then you might be driven to use floating point math.

If you're intersted i can post my plain integer routines for HSV-to-RGB-conversion, which i actually did for Atmel AVR Chips (which are of course by factors slower than an ARM Cortex M3). The routines are in plain C without AVR specifics, so should run ok on a mbed.

Best regards
Nenad

27 Oct 2010

Hi Nenad, I did in fact take some floating point HSV code that I found out there and convert it straight to fixed point using the library.  It has a lot of redundant scaling in it that I haven't tried to optimize yet, although as my ISR gets stuffed fuller I'll need to.  Thanks for offering your HSV code, I'd love to take a look at it.

cheers,

--steve

27 Oct 2010 . Edited: 27 Oct 2010

Hi Steve,

below there are four routines for HSB (HSV) to RGB conversion for different usage conditions. The first two are for 8-Bit-PWM and are specifically designed to use only 16-Bit-Integers (signed and/or unsigned), as all the routines were primarily developed for AVR 8-Bit microcontrollers. For an ARM Cortex M3 it's not that important, because all the registers are 32 Bit wide. The next two routines are for 12-Bit-PWM and use 32 Bit integer math. In all four routines you have 3 input variables (hue for Hue, sat for Saturation and bri for Brightness) and 3 output variables (red, green and blue) with the resulting PWM-values.

Best regards
Nenad

/*---------------8-Bit-PWM--------------------------
hue: 0 to 1529
sat: 0 to 256 (0 to 255 with small inaccuracy)
bri: 0 to 255
all variables uint16_t
*/

while (hue > 1529) hue -= 1530;
while (hue < 0) hue += 1530;

if (hue < 255) {
   red_val = 255;
   green_val = (65280 - sat * (255 - hue)) >> 8;
   blue_val = 255 - sat;
}
elseif (hue < 510) {
   red_val = (65280 - sat * (hue - 255)) >> 8;
   green_val = 255;
   blue_val = 255 - sat;
}
elseif (hue < 765) {
   red_val = 255 - sat;
   green_val = 255;
   blue_val = (65280 - sat * (765 - hue)) >> 8;
}
elseif (hue < 1020) {
   red_val = 255 - sat;
   green_val = (65280 - sat * (hue - 765)) >> 8;
   blue_val = 255;
}
elseif (hue < 1275) {
   red_val = (65280 - sat * (1275 - hue)) >> 8;
   green_val = 255 - sat;
   blue_val = 255;
}
else {
   red_val = 255;
   green_val = 255 - sat;
   blue_val = (65280 - sat * (hue - 1275)) >> 8;
}

red = ((bri + 1) * red_val) >> 8;
green = ((bri + 1) * green_val) >> 8;
blue = ((bri + 1) * blue_val) >> 8;


/*-------8-Bit-PWM-|-Light-Emission-normalized------
hue: 0 to 764
sat: 0 to 128 (0 to 127 with small inaccuracy)
bri: 0 to 255
all variables int16_t
*/

while (hue > 764) hue -= 765;
while (hue < 0) hue += 765;

if (hue < 255) {
   red_val = (10880 - sat * (hue - 170)) >> 7;
   green_val = (10880 - sat * (85 - hue)) >> 7;
   blue_val = (10880 - sat * 85) >> 7;
}
elseif (hue < 510) {
   red_val = (10880 - sat * 85) >> 7;
   green_val = (10880 - sat * (hue - 425)) >> 7;
   blue_val = (10880 - sat * (340 - hue)) >> 7;
}
else {
   red_val = (10880 - sat * (595 - hue)) >> 7;
   green_val = (10880 - sat * 85) >> 7;
   blue_val = (10880 - sat * (hue - 680)) >> 7;
}

red = (uint16_t)((bri + 1) * red_val) >> 8;
green = (uint16_t)((bri + 1) * green_val) >> 8;
blue = (uint16_t)((bri + 1) * blue_val) >> 8;


/*------------------12-Bit-PWM----------------------
hue: 0 to 24569
sat: 0 to 4096 (0 to 4095 with small inaccuracy)
bri: 0 to 4095
all variables uint32_t or int32_t
*/

while (hue > 24569) hue -= 24570;
while (hue < 0) hue += 24570;

if (hue < 4095) {
   red_val = 4095;
   green_val = (16773120 - sat * (4095 - hue)) >> 12;
   blue_val = 4095 - sat;
}
elseif (hue < 8190) {
   red_val = (16773120 - sat * (hue - 4095)) >> 12;
   green_val = 4095;
   blue_val = 4095 - sat;
}
elseif (hue < 12285) {
   red_val = 4095 - sat;
   green_val = 4095;
   blue_val = (16773120 - sat * (12285 - hue)) >> 12;
}
elseif (hue < 16380) {
   red_val = 4095 - sat;
   green_val = (16773120 - sat * (hue - 12285)) >> 12;
   blue_val = 4095;
}
elseif (hue < 20475) {
   red_val = (16773120 - sat * (20475 - hue)) >> 12;
   green_val = 4095 - sat;
   blue_val = 4095;
}
else {
   red_val = 4095;
   green_val = 4095 - sat;
   blue_val = (16773120 - sat * (hue - 20475)) >> 12;
}

red = ((bri + 1) * red_val) >> 12;
green = ((bri + 1) * green_val) >> 12;
blue = ((bri + 1) * blue_val) >> 12;


/*-------12-Bit-PWM-|-Light-Emission-normalized-----
hue: 0 to 12284
sat: 0 to 4096 (0 to 4095 with small inaccuracy)
bri: 0 to 4095
all variables int32_t
*/

while (hue > 12284) hue -= 12285;
while (hue < 0) hue += 12285;

if (hue < 4095) {
   red_val = (5591040 - sat * (hue - 2730)) >> 12;
   green_val = (5591040 - sat * (1365 - hue)) >> 12;
   blue_val = (5591040 - sat * 1365) >> 12;
}
elseif (hue < 8190) {
   red_val = (5591040 - sat * 1365) >> 12;
   green_val = (5591040 - sat * (hue - 6825)) >> 12;
   blue_val = (5591040 - sat * (5460 - hue)) >> 12;
}
else {
   red_val = (5591040 - sat * (9555 - hue)) >> 12;
   green_val = (5591040 - sat * 1365) >> 12;
   blue_val = (5591040 - sat * (hue - 10920)) >> 12;
}


red = ((bri + 1) * red_val) >> 12;
green = ((bri + 1) * green_val) >> 12;
blue = ((bri + 1) * blue_val) >> 12;