This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Math resources for MSP430G2553

Other Parts Discussed in Thread: MSP430G2553, MSP430WARE

I'm a beginner at writing embedded code using integer math.  My application requires that I use several math functions: sin, cos, asin, atan2, sqrt, and matrix multiplications.  

Is there a good resource for adding these functions to my project without taking up too much of the limited code space (16KB) on MSP430G2553?  Would you suggest doing lookup tables for all the trig functions?

  • The IAR Workbench for MSP430 does include math and float libraries. I would assume CCS does also.

    I think you'll find that the 512 byte RAM limit to be a problem when I think about the math operations you're talking about.

    I would recommend just using the library functions first (it's easy) and see where you end up. If necessary, and you think you can do a better optimization than the library already did, roll your own functions.

  • @Brian, Thanks for the quick response.  I am using CCS, and am not sure where to look for the math libraries.

    In the TI Resource Explorer>>MSP430ware>>Libraries, I only see libraries for peripherals.  I do see math libraries (fixed point and floating point) in the Libraries for my Delfino and Piccolo MCUs, but nothing for MSP430

    I am also a bit worried that I won't be able to get by with 512 byte of RAM.  

  • Just try adding

    #include <math.h>

    #include <float.h>

    and see what you get. If the compiler doesn't complain then the libraries should be available.

  • Thanks, looks like that worked...now I'll see what I can do with it.

  • Joris Lambrecht said:
    I'm a beginner at writing embedded code using integer math.  My application requires that I use several math functions: sin, cos, asin, atan2, sqrt, and matrix multiplications.  

    I don't think there is a ready-to-use integer (or rather: fixed point) math library available. And the geometric fucntions usually require either a lot of processing power and local variables, or large luookup tables (well, 16k are enough when using fixed point arithmetic).
    For matrix multiplication, you'll need quire some ram and that's somehting the G2553 doesn't have much. Not to mention that a 32bit hardware multiplier would be a good thing to use (and the MSps supports fractional numbers, which is of good use for fixed point arithmetics)

  • Right now it's taking much too long to do the operations necessary in floating point.  CCS gives the message "Detected floating point operation(s). Recommend moving them to RAM during run time or not using as these are processing/power" 

    How much will running from RAM really help?  Is there some good sample code to show how to do this?

  • Joris Lambrecht said:
    How much will running from RAM really help?

    On the G series nothing at all. The FR (FRAM) devices have waitstates when the system clock is above 8MHz, so running funcitons from RAM is faster. Not so on the Flash based MSPs. Here it makes no difference except for the slightly higher current when running from flash.
    However, the 'not using' recommendation is a good one :)

  • However, the 'not using' recommendation is a good one :)

    Agreed.  I've started coding all the atan, sin, and cos functions using a CORDIC implementation (using this as a starting point: https://github.com/the0b/MSP430-CORDIC-sine-cosine.)  I'll post the code here when I'm done. 


  • Joris Lambrecht said:
    I'll post the code here when I'm done. 

    That would be great. Thanks in advance :)

  • Here's my code.  Use at your own risk.  I'm pretty sure it works, but there might still be some bugs.  There's also probably ways to optimize it further, but it's sufficient for my needs.

    cordic.h

    //******************************************************************************
    //
    // Description: Header file for CORDIC, see .c file for
    // more detailed description.
    //
    //******************************************************************************


    #ifndef CORDIC_H
    #define CORDIC_H

    #include "types.h"

    #define SIGN(x) x>0 ? 1 : -1
    #define ABS(x) x>0 ? x : -x
    #define FLOAT2FIX_DEG 182 //(2^16)/360
    #define ROT360 65520 //360*FLOAT2FIX_DEG
    #define ROT180 32760 //180*FLOAT2FIX_DEG
    #define ROT90 16380 //90*FLOAT2FIX_DEG
    #define ROT45 8190 //45*FLOAT2FIX_DEG
    #define ROTSMALL 129 //angle less than this will lead to maximum cosine result (32767)

    #define ITER_CORDIC_SINCOS 12
    #define ITER_CORDIC_ATAN2 12


    //sin_result and cos_result will be 2^14*floating point representation
    //y and x used in atan2 should be 2^14*floating point representation
    //angle input to sincos and angle output of atan2 are (2^16)/360*floating point representation

    void cordic_sincos(INT16 theta, INT16 *sin_result, INT16 *cos_result);
    INT16 cordic_atan2(INT16 y, INT16 x);
    INT16 cordic_asin(INT16 x);
    UINT16 cordic_sqrt(UINT32 x);

    #endif /* CORDIC_H */

    cordic.c

    //******************************************************************************
    //
    // Description: This file contains functions to calculate sine and cosine,
    // atan2, asin, and sqrt using CORDIC rotation on a 16 bit MSP430.
    // See function heading comments, for the input/outputs and fixedpoint
    // format expected by each.
    //
    //
    //modified from:
    // "MSP430-CORDIC-sine-cosine" by T.Bowers
    // (https://github.com/the0b/MSP430-CORDIC-sine-cosine)
    //and:
    // "Fixed-Point ATAN2 using CORDIC" by Giorgia Zucchelli
    // (http://www.mathworks.com/matlabcentral/fileexchange/19316-fixed-point-atan2-using-cordic)
    //
    // 20130318 JML
    // Revisions:
    //
    //******************************************************************************

    #include "cordic.h"

    // Table of arctan's for use with CORDIC algorithm
    // Store in representation N = angle * FLOAT2FIX_DEG

    const INT16 atanAngles[15] = { 0x1FFE, //atan(2^0) = 45 degrees
    0x12E3, //atan(2^-1) = 26.5651
    0x09FB, //atan(2^-2) = 14.0362
    0x0511, //7.12502
    0x028B, //3.57633
    0x0146, //1.78981
    0x00A3, //0.895174
    0x0051, //0.447614
    0x0029, //0.223808
    0x0014, //0.111904
    0x000A, //0.05595
    0x0005, //0.0279765
    0x0003, //0.0139882
    0x0002, //0.0069941
    0x0001 //0.0035013
    };

    // CORDIC_SINCOS
    // Function to compute sine/cosine using CORDIC
    // Inputs:
    // theta = any angle in degrees*FLOAT2FIX_DEG
    // *sin_result = pointer for the sine result *2^14
    // *cos_result = pointer for the cosine result *2^14
    // Outputs:
    // none
    void cordic_sincos(INT16 theta, INT16 *sin_result, INT16 *cos_result){
    INT16 sigma, s, x1, x2, y, i;
    INT8 quadAdj, shift;
    const INT16 *atanptr = atanAngles;

    //Shift angle to be in range -180 to 180
    while(theta < -ROT180) theta += ROT180;
    while(theta > ROT180) theta -= ROT180;

    //Shift angle to be in range -90 to 90
    if (theta < -ROT90)
    {
    theta = theta + ROT180;
    quadAdj = -1;
    }
    else if (theta > ROT90)
    {
    theta = theta - ROT180;
    quadAdj = -1;
    }
    else
    {
    quadAdj = 1;
    }

    //Shift angle to be in range -45 to 45
    if (theta < -ROT45)
    {
    theta = theta + ROT90;
    shift = -1;
    }
    else if (theta > ROT45)
    {
    theta = theta - ROT90;
    shift = 1;
    }
    else
    {
    shift = 0;
    }

    //Initial values
    x1 = 9949; //x1 will be the cosine result,
    //initially the magic number 0.60725293*2^14
    y = 0; //y will contain the sine result
    s = 0; //s will contain the final angle
    sigma = 1; //direction from target angle

    for (i=0; i<ITER_CORDIC_SINCOS; i++)
    {
    sigma = SIGN(theta - s);
    if(sigma < 0)
    {
    x2 = x1 + (y >> i);
    y = y - (x1 >> i);
    x1 = x2;
    s -= *atanptr++;
    }
    else
    {
    x2 = x1 - (y >> i);
    y = y + (x1 >> i);
    x1 = x2;
    s += *atanptr++;
    }
    }



    //Push final values to appropriate registers
    if(shift > 0)
    {
    *sin_result = x1;
    *cos_result = -y;
    }
    else if (shift < 0)
    {
    *sin_result = -x1;
    *cos_result = y;
    }
    else
    {
    *sin_result = y;
    *cos_result = x1;
    }

    //Adjust for sign change if angle was in quadrant 3 or 4
    *sin_result = quadAdj * *sin_result;
    *cos_result = quadAdj * *cos_result;
    }

    // CORDIC_ATAN2
    // Function to compute atan2
    // Inputs:
    // y = y value from unit circle *2^14
    // x = x value from unit circle *2^14
    // Outputs:
    // theta=angle*FLOAT2FIX_DEG
    INT16 cordic_atan2(INT16 y, INT16 x)
    {
    INT16 theta = 0, x1, y1, reg;
    UINT8 i;
    INT8 shift = 0;
    const INT16 *atanptr = atanAngles+1; //for atan, we don't need the first iteration

    x1 = ABS(x);
    y1 = ABS(y);
    if(x1 < y1) //resulting angle will be larger than 45deg, swap x1 and y1 to get angle within 45deg and fix output later
    {
    reg = x1;
    x1 = y1;
    y1 = reg;
    shift = 1;
    }

    for( i=1; i<=ITER_CORDIC_ATAN2; i++ ) //we can skip the first step of table because we know angle is between 0 and 45deg
    {
    if(y1 < 0)
    {
    theta -= *atanptr++;
    reg = x1 - (y1 >> i);
    y1 = (x1 >> i) + y1;
    x1 = reg;
    }
    else
    {
    theta += *atanptr++;
    reg = x1 + (y1 >> i);
    y1 = -(x1 >> i) + y1;
    x1 = reg;
    }
    }

    if(shift) theta = ROT90 - theta; //fix for angles that should be greater than 45

    if(x < 0) //second and third quadrant
    {
    if(y < 0) theta -= ROT180;
    else theta = ROT180 - theta;
    }
    else
    {
    if(y < 0) theta = -theta;
    }
    return(theta);
    }

    // CORDIC_ASIN
    //Function to compute arcsin using: asin(u) = atan2(u, sqrt(1-u^2)), where 1->(2^14)*(2^14)
    //Inputs:
    // y = value from unit circle *2^14
    //Outputs:
    // theta=angle*FLOAT2FIX_DEG
    //TODO: might need a CORDIC multiply algorithm because no Hardware multiplier!
    INT16 cordic_asin(INT16 y)
    {
    INT32 x = 0x10000000;
    x -= (INT32)y*(INT32)y; //cast to int32 to get product
    x = (INT32) cordic_sqrt( (UINT32)x );
    return( cordic_atan2( y,(INT16)x ) ); //theta
    }

    // CORDIC_SQRT
    //Function to take the integer square root of numbers between 0 and 2^32, resulting in output between 0 and 2^16.
    //Inputs:
    // x = number to take square root of (UINT32)
    //Outputs:
    // y = sqrt(x) (UINT16)
    //TODO:Since we don't need the full range (we only need to take square roots of inputs up to 2^28), we could start base at 2^13=0x2000,
    //and do only 13 iterations
    UINT16 cordic_sqrt(UINT32 x)
    {
    UINT8 i;
    UINT16 base = 0x8000; //starting point for squareroot guess-->sqrt(2^32) = 2^16...half way to 2^16 is 2^15
    UINT32 y=0;

    for( i=0; i<16; i++)
    {
    y += (UINT32)base;
    if(y*y > x)
    y -= (UINT32)base;
    base = base >> 1;
    }
    return( (UINT16) y);
    }

    types.h

    #ifndef TYPES
    #define TYPES

    //This file simply makes it easier to refer to the various data types

    typedef unsigned char UINT8;
    typedef signed char INT8;
    typedef unsigned int UINT16;
    typedef signed int INT16;
    typedef unsigned long UINT32;
    typedef signed long INT32;

    #endif

  • Thanks for posting this. Looks nice.

    Sure, there are some things that can be optimized.
    Especially the multiplications. Instead of using a 32*32 bit multiplication for a 32 bit result, a direct (macro) programming of the hardware MPY16 (or 32) could do a (much) faster 16x16->32 multiplication.

    SQRT could be optimized in the last bit to return the closer result instead of the result that gives the best result below the 'real' one. Maybe by parameter.

    But in any case, this should be much smaller and faster than using floats.

  • Could you explain the MPY16 in more detail and how this would be implemented on the MSP430G2553?

    Thanks!

  • Joris Lambrecht said:
    Could you explain the MPY16 in more detail and how this would be implemented on the MSP430G2553?


    Alas, the 2553 doesn't seem to have a hardware multiplier. Mos tMSPs do, but not the G series. (one of teh reasons why they can be cheap)

    For other MSPs, here a code snippet:

    extern inline __attribute__((always_inline)) unsigned long int umul16 ( unsigned short int mula, unsigned short int mulb) {
      volatile unsigned long int result;
      _disable_interrupts();
       MPY=mula;
       OP2=mulb;
       ((volatile unsigned short int *)(&result))[0]= RESLO;
       ((volatile unsigned short int *)(&result))[1]= RESHI;
      _enable_interrupts();
      return result;
    }

    This is for MSPGCC (the function attribute forces inlining)
    Instead, it could as well be a real macro, but then there would be no type checking.

  • I do not know MSPGCC nor multi-tasking. For what I know,

    (1) If you do not use the snippet inside ISR, there is no need to do _disable_interrupts(); and _enable_interrupts(); in that snippet.

    (2) If you you do use the snippet inside ISR or other places where the interrupt is already disabled, there is no effect in doing _disable_interrupts(); and it is wrong to do _enable_interrupts(); in that snippet.

  • Though I haven't done this yet, I may in future. I dug up this document which may be useful:

    http://www.johnsjacob.com/uploads/Embedded_Function2011.pdf

  • old_cow_yellow said:
    (1) If you do not use the snippet inside ISR, there is no need to do _disable_interrupts(); and _enable_interrupts(); in that snippet.

    It's actually the other way 'round: if you don't use the hardwar emultiplier (that means, any multiplication) inside an ISR, then you don't need them.

    old_cow_yellow said:
    (2) If you you do use the snippet inside ISR or other places where the interrupt is already disabled, there is no effect in doing _disable_interrupts(); and it is wrong to do _enable_interrupts(); in that snippet.

    Right. It was a samaple only, indicatign that there mustn't be a context switch of any kind while working on the multiplier.

    My original code uses an ATOMIC(x) macro that surrounds its parameter (C code) with saving the GIE state and restoring it after. Since saving something on stack invalidates the stack frame for local variables (depends on compiler optimization), I expanded this to incrementing a counter, clearing GIE and only setting it when the counter reaches zero after decrementing it at the end of the macro. This way nested atomic sections are possible. Outside, GIE is assumed to be set.
    However, this macro adds soem overhead that partly nullifies the savings of the optimized multiply code. So for maximum performance, it is best to group all multiplications together and disable interrupts around the whole block.

**Attention** This is a public forum