CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Expressions not evaluating correctly

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
demedeiros



Joined: 27 Dec 2013
Posts: 71

View user's profile Send private message

Expressions not evaluating correctly
PostPosted: Wed Dec 16, 2015 2:40 pm     Reply with quote

Hi All,

I am using a PIC16F1829 with PCM compiler, version 5.051.

I am working on a program to communicate with a Measurement Specialties 89BSD pressure sensor.http://meas-spec.com/product/pressure/89BSD.aspx

The majority of the program is working correctly, but I am having problems with two long/complex expressions that are not evaluating correctly.

Code:

float32 GetTopCoef(){

   float32 TOP = D1+C0*pow(2,Q[0]) + C3*pow(2, Q[3]) * (D2/pow(2, 24)) + C4*pow(2, Q[4]) * pow((D2/pow(2, 24)),2);

   printf("TOP %f\r", TOP);
   return TOP;
   
}

float32 GetBOTCoef(){

   float32 BOT = C1 * pow(2, Q[1]) + C5 * pow(2, Q[5]) * (D2/pow(2,24)) + C6*pow(2, Q[6])*pow((D2/pow(2,24)),2);
   printf("BOT %f\r", BOT);
   return BOT;
}


This function works in excel, and has worked on an Arduino. Is there something I'm missing here? Am I using a function incorrectly?

Also, it may be important to note that I am approaching my ROM limit on this PIC, currently at 91%.

Thank you!
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Dec 16, 2015 3:02 pm     Reply with quote

Post a test program that shows the variable declarations and the
initialization values, and shows the #define statements for the constants.

Tell us what results you get when you run it in a PIC, and what results
you expected to get.
demedeiros



Joined: 27 Dec 2013
Posts: 71

View user's profile Send private message

PostPosted: Wed Dec 16, 2015 3:12 pm     Reply with quote

Actual code is below. Here are the values of the variables that are not shown/derived.
Code:

D1: 4098692
D2: 2940222

C0:-4742
C1: 3088
C2: 99
C3: -122
C4: 181
C5: -142
C6: 137

Q0: 9
Q1: 11
Q2: 9
Q3: 15
Q4: 15
Q5: 16
Q6: 16




TOP should evaluate to 1152346.526 and BOT should evaluate to 4969073.057. They are currently evaluating to -18441256.94 and -15428157.40 respectively.

Code:

#include <main.h>
#include <math.h>

#define PSEN_ADDRESS 0x77
#define CMD_RESET 0x1E // ADC reset command
#define CMD_ADC_READ 0x00 // ADC read command
#define CMD_ADC_CONV 0x40 // ADC conversion command
#define CMD_ADC_D1 0x00 // ADC D1 conversion
#define CMD_ADC_D2 0x10 // ADC D2 conversion
#define CMD_ADC_256 0x00 // ADC OSR=256
#define CMD_ADC_512 0x02 // ADC OSR=512
#define CMD_ADC_1024 0x04 // ADC OSR=1024
#define CMD_ADC_2048 0x06 // ADC OSR=2048
#define CMD_ADC_4096 0x08 // ADC OSR=4096
#define CMD_PROM_RD 0xA0 // Prom read command

unsigned int16 C[8];
float C0=0, C1=0, C2=0, C3=0, C4=0, C5=0, C6=0;
int16 SENA0=0, SENA1=0, SENA2=0;
float Q[7] = {9, 11, 9, 15, 15, 16 ,16};
unsigned int32 D1, D2;
float32 P,TOP, BOT;

//=================================

void PSEN_CMD_RESET(void);
unsigned int16 PSEN_CMD_PROM(unsigned char coef_num);
void Get_Coefficients(unsigned int16 array[]);
int16 TenBitConvertion( int16 Value);
int16 FourteenBitConversion( int16 Value);
unsigned int32 cmd_adc(char cmd);
float32 GetPressure();
float32 GetTopCoef();
float32 GetBOTCoef();
void GetD1D2();


void main()
{
 
 while(1){
   
   
   if(input(PIN_C6)){   //Run only if user presses button
   
     
      PSEN_CMD_RESET(); //Reset the pressure sensor
     
      for(int i = 0; i < 8; i++){
     
         C[i] = PSEN_CMD_PROM(i);   //Read in the sensor register map
     
      }
     
   
   Get_Coefficients(C); //Calculate the sensor coefficients
   
   float Pressure =  GetPressure(); //Get pressure from sensor

   printf("PRESSURE: %f\r\n",Pressure);   //Print pressure
   delay_ms(250); //Delay for debounce
   }
 
 
 
 
 }


//Sends the sensor reset command
void PSEN_CMD_RESET(void){

      i2c_start();   //Start I2C
      i2c_write(PSEN_ADDRESS << 1);   //Write device address
      i2c_write(CMD_RESET);   //Write the reset command
      i2c_stop();    //Stop I2C
      delay_ms(3);
}

//Read the calibration coefficients
unsigned int16 PSEN_CMD_PROM(char coef_num){

   unsigned int16 ret;
   unsigned int16 rC = 0;
   
   i2c_start();
   i2c_write(PSEN_ADDRESS << 1);

   i2c_write(CMD_PROM_RD+coef_num*2);

   i2c_start();

   i2c_write((PSEN_ADDRESS << 1) | 1); //Send a read request
   ret = i2c_read(); //Read with ACK
   rC = 256 * ret;
   ret = i2c_read(0);   //Read with no ACK
   rC = rC + ret;
   i2c_stop();
   
return rC;
}

//Calculate the coefficients from the PROM array
void Get_Coefficients(unsigned int16 array[]){


C0 = (array[1] & 0xFFFC) >> 2;
C0 = FourteenBitConversion(C0);


C1 = ((array[1] & 0x03) << 12) + ((array[2] & 0xFFF0) >> 4);
C1 = FourteenBitConversion(C1);


C2 = (((array[2] & 0xF) << 6) + (array[3] >> 10)) & 0x3FF;
C2 = TenBitConvertion(C2);



C3 = array[3] & 0x3FF;
C3 = TenBitConvertion(C3);



C4 = (array[4] >> 6) & 0x3FF;
C4 = TenBitConvertion(C4);



C5 =  (((array[4] & 0x3F) << 4) + (array[5] >> 12)) & 0x3FF;
C5 = TenBitConvertion(C5);


C6 = (array[5] >> 2) & 0x3FF;
C6 = TenBitConvertion(C6);



SENA0 = (((array[5] & 0x3) << 8) + (array[6] >> 8)) & 0x3FF;
SENA0 = TenBitConvertion(SENA0);



SENA1 = (((array[6] & 0xFF) << 2) + (array[7] >> 14)) & 0x3FF;
SENA1 = TenBitConvertion(SENA1);



SENA2 = (array[7] >> 4) & 0x3FF;
SENA2 = TenBitConvertion(SENA2);

}

//Convert from twos complement if required
int16 TenBitConvertion( int16 Value){
 
  int16 Converted = Value;
 
 if(Value > 512){
 
  Converted = ((Value - 512 -1)^511)*-1;
 }
 
 
  return Converted;
}

//Convert from twos complement if required
int16 FourteenBitConversion( int16 Value){
 
  int16 Converted = Value;
 
   if(Value > 8192){
 
     Converted = ((Value - 8192 -1)^8191)*-1;
 }
 
 
  return Converted;
}


//Read ADC
unsigned int32 cmd_adc(char cmd){
 
   unsigned int16 ret;
   unsigned int32 temp = 0;
   
   i2c_start();
   i2c_write(PSEN_ADDRESS << 1);
   i2c_write(CMD_ADC_CONV+cmd);
   i2c_stop();
   
   switch(cmd & 0x0F){
     
      case CMD_ADC_256 : delay_us(900); break;
      case CMD_ADC_512 : delay_ms(3); break;
      case CMD_ADC_1024: delay_ms(4); break;
      case CMD_ADC_2048: delay_ms(6); break;
      case CMD_ADC_4096: delay_ms(10); break;

   }
    i2c_start();
    i2c_write(PSEN_ADDRESS << 1);
    i2c_write(CMD_ADC_READ);
    i2c_stop();
    i2c_start();
   
    i2c_write((PSEN_ADDRESS << 1) | 1);
    ret = i2c_read();
    temp = 65536*ret;
    ret = i2c_read();
    temp = temp+256*ret;
    ret = i2c_read(0);
    i2c_stop();
    temp = temp+ret;
    return temp;
}

//Get the pressure values
float32 GetPressure(){
 
   GetD1D2();
 
   float32 TOP = GetTopCoef();
   float32 BOT = GetBOTCoef();
 
   float32 Y = TOP/BOT;

   float32 P = Y*(1-C2*pow(2,Q[2])/pow(2,24))+(C2*pow(2,Q[2])/pow(2,24))*pow(Y,2);

   float32 Pressure = ((P-0.1)/0.8)*(6-0);
   return Pressure;
 
}

float32 GetTopCoef(){

   float32 TOP = D1+C0*pow(2,Q[0]) + C3*pow(2, Q[3]) * (D2/pow(2, 24)) + C4*pow(2, Q[4]) * pow((D2/pow(2, 24)),2);

   printf("TOP %f\r", TOP);
   return TOP;
   
}

float32 GetBOTCoef(){

   float32 BOT = C1 * pow(2, Q[1]) + C5 * pow(2, Q[5]) * (D2/pow(2,24)) + C6*pow(2, Q[6])*pow((D2/pow(2,24)),2);
   printf("BOT %f\r", BOT);
   return BOT;
}

void GetD1D2(){

  D2=cmd_adc(CMD_ADC_D2+CMD_ADC_4096); // read D2
  D1=cmd_adc(CMD_ADC_D1+CMD_ADC_4096); // read D1
   
  printf("D1: %ld\r\n", D1);
  printf("D2: %ld\r\n", D2);
 

}
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Dec 16, 2015 4:19 pm     Reply with quote

demedeiros wrote:

TOP should evaluate to 1152346.526 and BOT should evaluate to
4969073.057. They are currently evaluating to -18441256.94 and
-15428157.40 respectively.

I took your code and made it into a test program and it works OK.
I compiled it with CCS vs. 5.051 and ran it in MPLAB vs. 8.92 simulator.
I got the following results in the MPLAB Output window:
Quote:

TOP 1152346.08
BOT 4969073.60

Test program:
Code:

#include <16F1829.h>
#fuses INTRC_IO, NOWDT, NOMCLR
#use delay(clock=4M)
#use rs232(baud=9600, UART1, ERRORS)

#include <math.h>


unsigned int16 C[8];
float C0=0, C1=0, C2=0, C3=0, C4=0, C5=0, C6=0;
int16 SENA0=0, SENA1=0, SENA2=0;
float Q[7] = {9, 11, 9, 15, 15, 16 ,16};
unsigned int32 D1, D2;
float32 P,TOP, BOT;


float32 GetTopCoef();
float32 GetBOTCoef();

//=================================
void main()
{
float top_result;
float bot_result;

D1 = 4098692;
D2 = 2940222;

C0 = -4742;
C1 = 3088;
C2 = 99;
C3 = -122;
C4 = 181;
C5 = -142;
C6 = 137;

top_result = GetTopCoef();
   
bot_result = GetBOTCoef();
   
while(TRUE);


//----------------------------------
float32 GetTopCoef(){

   float32 TOP = D1+C0*pow(2,Q[0]) + C3*pow(2, Q[3]) * (D2/pow(2, 24)) + C4*pow(2, Q[4]) * pow((D2/pow(2, 24)),2);

   printf("TOP %f\r", TOP);
   return TOP;
}

//-----------------------------
float32 GetBOTCoef(){

   float32 BOT = C1 * pow(2, Q[1]) + C5 * pow(2, Q[5]) * (D2/pow(2,24)) + C6*pow(2, Q[6])*pow((D2/pow(2,24)),2);
   printf("BOT %f\r", BOT);
   return BOT;
}
Ttelmah



Joined: 11 Mar 2010
Posts: 19503

View user's profile Send private message

PostPosted: Thu Dec 17, 2015 6:01 am     Reply with quote

PCM_programmer, has shown that in fact it does work OK.
His code, without doing anything else though, uses 58% of the ROM.

Lets see if we can improve things.

There are several things being 'missed'. Most of the 'pow' calls are being used to evaluate _integer_ 2^x calls. On a simple int32, this is equivalent to a rotation, that can be performed in a handful of instructions, instead of a pow call (hundreds of instructions). The remaining pow calls are used to evaluate squares, which is much more efficiently done as x*x.
Then there are two terms that are evaluated twice in each routine. Much more efficient to solve these once, and store the result.
Then the coefficients for the rotations are always integers. No point in storing these as floats.
So:
Code:

#include <16F1829.h>
#fuses INTRC_IO, NOWDT, NOMCLR
#use delay(clock=4M)
#use rs232(baud=9600, UART1, ERRORS)

unsigned int16 C[8];
float C0=0, C1=0, C2=0, C3=0, C4=0, C5=0, C6=0;
int16 SENA0=0, SENA1=0, SENA2=0;
int Q[7] = {9, 11, 9, 15, 15, 16 ,16}; //These factors are always integer
unsigned int32 D1, D2;
float32 P,TOP, BOT;

float32 GetTopCoef();
float32 GetBOTCoef();

float32 twoto(int factor) //Efficiently return integer 2^factor
{
   int32 res=1;
   res<<=(factor);
   return (float32)res;
}

#define SQUARE(x) ((x)*(x)) //efficient square function

//=================================
void main()
{
   float top_result;
   float bot_result;
   float result;
   D1 = 4098692;
   D2 = 2940222;

   C0 = -4742;
   C1 = 3088;
   C2 = 99;
   C3 = -122;
   C4 = 181;
   C5 = -142;
   C6 = 137;

   top_result = GetTopCoef();
   bot_result = GetBOTCoef();
   result=top_result/bot_result;
   printf("result %7.4f\r", result);

   while(TRUE);
}


//----------------------------------
float32 GetTopCoef()
{
   float32 recip;
   float32 TOP;
   recip=D2/twoto(24); //save calculation - do this once
   TOP = D1 + C0*twoto(Q[0]) + (C3*twoto(Q[3]) * recip) + (C4*twoto(Q[4]) * (SQUARE(recip)));
   printf("TOP %f\r", TOP);
   return TOP;
}

//-----------------------------
float32 GetBOTCoef()
{
   //similar recvoding for the bottom
   float32 recip;
   float32 BOT;
   recip=D2/twoto(24);
   BOT = C1*twoto(Q[1]) + (C5*twoto(Q[5]) * recip) + (C6*twoto(Q[6]) * (SQUARE(recip)));
   printf("BOT %f\r", BOT);
   return BOT;
}

I've added code to generate the final result (since I wanted to test with the values in the data sheet, and verify that I did get a result to match the example (got 0.7672). This uses less than half the ROM, and takes 1/10th the time (removing the prints, takes 17mSec against 171mSec.....). The result is also slightly more accurate (avoids some of the float rounding errors...).

Hopefully this will give the space needed, and bring the speed to a more useful level as well as emphasising the point I often make here to avoid float except where they must be used. Very Happy

Just edited:
Just to add one thing, if you are not getting the right numbers, how are you testing these?. If it is in a debugger, like MPLAB, make sure you are selecting the correct number format (Microchip, not IEEE). Would explain silly results...
Ttelmah



Joined: 11 Mar 2010
Posts: 19503

View user's profile Send private message

PostPosted: Thu Dec 17, 2015 6:42 am     Reply with quote

as a further comment, your conversions are 'dubious'.

You have a value that is potentially _signed_. It is going to need to be returned as a signed number to work. The conversion is relatively simple. It is called 'sign extending'. For a 10bit value (for example):
Code:

signed int16 convert10(int16 source_val)
{
   signed int16 rval;
   rval=source_val & 0x1FF; //low 9 bits
   if (bit_test(source_val,9))
   {
       rval |= 0xFE00); //set top 7 bits
   }
   return rval;
}


All you do is test the tenth bit (9, since they are numbered from 0), and set the top 7 bits in the result if this is set. So the 'sign' bit, is 'extended' into the result value.

The same technique will work for the other conversions.


Last edited by Ttelmah on Thu Dec 17, 2015 8:06 am; edited 1 time in total
demedeiros



Joined: 27 Dec 2013
Posts: 71

View user's profile Send private message

PostPosted: Thu Dec 17, 2015 7:49 am     Reply with quote

Man, you guys are incredible!

Optimizing is still something I need more experience with, thank you for providing the examples on pow() and the twoto() function. That is incredibly helpful!

After banging my head against my desk for a few more hours, I ended up determining that my error was where I perform the two's complement conversion on some of the C constants. Specifically, these two functions:

Code:
//Convert from twos complement if required
int16 TenBitConvertion( int16 Value){
 
  int16 Converted = Value;
 
 if(Value > 512){
 
  Converted = ((Value - 512 -1)^511)*-1;
 }
 
 
  return Converted;
}

//Convert from twos complement if required
int16 FourteenBitConversion( int16 Value){
 
  int16 Converted = Value;
 
   if(Value > 8192){
 
     Converted = ((Value - 8192 -1)^8191)*-1;
 }
 
 
  return Converted;
}


I changed the function type and casted "Converted" to float32, all seems well now!

Code:

//Convert from twos complement if required
float32 TenBitConvertion( int16 Value){
 
  float32 Converted = Value;
 
 if(Value > 512){
 
  Converted = ((Value - 512 -1)^511)*-1;
 }
 
  //printf("Converted: %ld\r\n", Converted);
 
  return (float32)Converted;
}

//Convert from twos complement if required
float32 FourteenBitConversion( int16 Value){
 
  float32 Converted = Value;
 
   if(Value > 8192){
 
     Converted = ((Value - 8192 -1)^8191)*-1;
 }
 
 // printf("Converted: %ld\r\n", Converted);
  return (float32)Converted;
}


One thing I am not understanding though, is why this does not work as an int16? This numbers (C) are all integers (pos and negative). Even if I declared all the constants as int16 as opposed to float32, they still didnt work.

Thanks again!!!
Ttelmah



Joined: 11 Mar 2010
Posts: 19503

View user's profile Send private message

PostPosted: Thu Dec 17, 2015 8:05 am     Reply with quote

See the comment on how to do the conversions already posted....

_signed_ int16.

You are not returning the sign.
demedeiros



Joined: 27 Dec 2013
Posts: 71

View user's profile Send private message

PostPosted: Thu Dec 17, 2015 8:06 am     Reply with quote

Sorry! I hadn't seen that most recent reply! Thanks again!!
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Thu Dec 17, 2015 6:02 pm     Reply with quote

MR. T has given an excellent summary of your problem but there is a generic message that can't be repeated enough to all who read this:

When dealing with a complicated bit of math in the PIC -
1- always reduce the expression you want to solve by pre-calculating all the constants you can combine. CONSOLIDATE constant values !
2-factor multiplications and divisions into shifts EG: /96 is really /3 then>>5
3- work with scaled INTS where ever possible.
4- store intermediate values that will be needed again. Clever use of ram saves cycles.
5-avoid POW , SQR , trig-fns, transcendental math // floats like the processor plague they are.
6- Avoid all but the simplest math , and especially floats inside an ISR handler.
Gabriel



Joined: 03 Aug 2009
Posts: 1067
Location: Panama

View user's profile Send private message

PostPosted: Mon Dec 21, 2015 5:57 am     Reply with quote

Ive read this briefly, but just wanted to suggest a lookup table.
Use an Excel sheet to precalculate the values.

Ive done this to linearize a thermocouple, which seems very similar to what you are doing.

G.
_________________
CCS PCM 5.078 & CCS PCH 5.093
Ttelmah



Joined: 11 Mar 2010
Posts: 19503

View user's profile Send private message

PostPosted: Mon Dec 21, 2015 8:12 am     Reply with quote

I'ts 'eeny meeny' on that. Very Happy

The maths is directly from the formulae given by the manufacturer, and with the tweaks already given, is not too bad on size. Problem is that a look up might well end up larger....
In this case the coefficients are being read from the chip itself, so for general use (as opposed to coding for a single chip), the maths has to be done.

If there were 'log' terms higher powers, or other functions like this, that wouldn't simplify, and the coefficients were fixed, then definitely worth considering.
Now where I really strongly 'might' recommend this, is on the other thread running at the moment about variable width PWM. However even on this, with any reasonable number of terms, the poster may well run into size problems on his PIC.

An approach that always must be remembered.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group