Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

math problem 16 x 4 thermal array

Status
Not open for further replies.

Dr_Doggy

Well-Known Member
The first one of these i bought was a 8x1, which was really nice since all i needed to do was a 12c lookup in eeprom registers, this 16x4 needs multiple reads and processing before the temperature in the array can be resolved, so first i took the source code provided for adruino, and changed it over to be compatible with my PIC source code and variables, after that i cut out the subroutines and trimmed the code best i could:



unsigned char i2c_readIR(void){



int i;
long IR[64];
double VIRCOMP[64], vircp, alpha[64];
double tmp;
double eC;
double Vth = 0.0;
double Kt1 = 0.0;
double Kt2 = 0.0;
int Air, Bir;
double eX = pow(2.0, EEPROM[0xD9]);
int Acp = EEPROM[0xD4];
int Bcp = EEPROM[0xD5];
double result, result2;
int TGC = EEPROM[0xD8];

double eZ = (EEPROM[0xE5]*256.0 + EEPROM[0xE4])/32768.0;
double e1 = (EEPROM[0xE1]*256.0 + EEPROM[0xE0]) / (pow(2.0, EEPROM[0xE2]));
double e2 = pow(2.0, EEPROM[0xE3]);

i2c_start(); // read ptat value
ack = i2c_write(address); ack = i2c_write(0x02);
ack = i2c_write(0x90); ack = i2c_write(0x00);
ack = i2c_write(0x01); i2c_restart();
ack = i2c_write(address + 1);
PTAT = i2c_readLOOP(0);PTAT += (i2c_readLOOP(0) * 256);
i2c_stop(); i2c_start(); // read Compensation pixel value
ack = i2c_write(address); ack = i2c_write(0x02);
ack = i2c_write(0x91); ack = i2c_write(0x00);
ack = i2c_write(0x01); i2c_restart();
ack = i2c_write(address + 1);
COMPENSATION = i2c_readLOOP(0); COMPENSATION += (i2c_readLOOP(0) * 256);
if(COMPENSATION > 32767L) COMPENSATION -= 65536L;
i2c_stop();

// Load Temperature array from ram
i2c_start();
i2c_write(address);
i2c_write(0x02);
i2c_write(0x00);
i2c_write(0x01);
i2c_write(0x40);
i2c_restart();
i2c_write(address + 1);
for(int c2=0x00;c2<(0x40);c2+=0x01){
IR[c2] = i2c_readLOOP(0);
IR[c2] += i2c_readLOOP(0) * 256;
IRDATuncoded[c2] = IR[c2];
if(IR[c2] > 32767L) IR[c2] -= 65536L;
}
i2c_stop();



if((Vth = EEPROM[0xDB]*256.0 + EEPROM[0xDA]) > 32767.0) Vth -= 65536.0;
if((Kt1 = EEPROM[0xDD]*256.0 + EEPROM[0xDC]) > 32767.0) Kt1 -= 65536.0; Kt1 = Kt1/1024.0;
if((Kt2 = EEPROM[0xDF]*256.0 + EEPROM[0xDE]) > 32767.0) Kt2 -= 65536.0; Kt2 = Kt2/1048576.0;

INTTEMP = ((-1)*Kt1 + sqrt(pow(Kt1, 2) - 4*Kt2*(Vth - PTAT))) / (2*Kt2);

//////////////
//////////////// INTTEMP = pow(Kt1, 2);
////////////////
////////////////// result2 = 0;
////////////////// result2 = Vth - PTAT;
////////////////// result2 = result2 * 4;
////////////////// result2 = result2 * Kt2;
////////////////// INTTEMP = INTTEMP - result2;
////////////////
//////////////
////////////// INTTEMP = sqrt( INTTEMP );
////////////// INTTEMP = INTTEMP + (-1)*Kt1;
////////////// INTTEMP = INTTEMP / (2*Kt2);

INTTEMP += 25.0;
if(Acp > 127) Acp -= 256;
if(Bcp > 127) Bcp -= 256;
vircp = COMPENSATION - (Acp + ((Bcp/(pow(2.0, EEPROM[0xD9]))) * (INTTEMP - 25.0)));
if(TGC > 127) TGC -= 256;
result = (double)(TGC/32.0);
eC = pow(INTTEMP + 273.15, 4);

for(i=0; i<64; i++)
{
Air = EEPROM;
Bir = EEPROM[0x40+i];
if(Air > 127) Air -= 256;
if(Bir > 127) Bir -= 256;
VIRCOMP = IR - (Air + ((Bir/eX) * (INTTEMP - 25.0)));
VIRCOMP = VIRCOMP - (result * vircp);
VIRCOMP = VIRCOMP/eZ;
alpha = e1 + (EEPROM[0x80+i]/e2);
tmp = VIRCOMP/alpha + eC;
//// tmp = mySQRT(tmp);
tmp = sqrt(tmp);
tmp = sqrt(tmp);
IRDAT = tmp - 273.15;
}

xxx=1;
return 1;
}


yes, this is the simplified version, and does all calculations OK, however there is something going on here where i am getting a delay on my interrupt , INT0 is my 9600 baud 232 rx receiver and due to the delay on the interrupt firing my rx bytes are returning with wrong values.

using more rem statements ( // ), i have narrowed it down to this line being a problem:

INTTEMP = ((-1)*Kt1 + sqrt(pow(Kt1, 2) - 4*Kt2*(Vth - PTAT))) / (2*Kt2);

and by breaking that line down to simpler components :

// INTTEMP = ((-1)*Kt1 + sqrt(pow(Kt1, 2) - 4*Kt2*(Vth - PTAT))) / (2*Kt2);

INTTEMP = pow(Kt1, 2);


result2 = 0;
result2 = Vth - PTAT;
result2 = result2 * 4;
result2 = result2 * Kt2;

INTTEMP = INTTEMP - result2;
INTTEMP = sqrt( INTTEMP );
INTTEMP = INTTEMP + (-1)*Kt1;
INTTEMP = INTTEMP / (2*Kt2);

I have discovered that the problem is in one of these following lines, if i rem them out buffer receives ok, but even re - adding the first line (result2 = 0;), or any other of them causes the error:

result2 = 0;
result2 = Vth - PTAT;
result2 = result2 * 4;
result2 = result2 * Kt2;
INTTEMP = INTTEMP - result2;

osccon and osctune are setup during initialization , i cant think of anything else that would effect it, but i cant see that line (result2 = 0;) being the problem either.

Ideas?



 
update: iv narrowed it to each of the 4 lines here, im prety sure im not overflowing anything, and my only guess now is due to the number of digits of the double making the calculation take longer? .

// tmp = Vth - PTAT;
// tmp = tmp * 4;
// tmp = tmp * Kt2;
// INTTEMP = INTTEMP - tmp;
// breakpoint here

this is breakpoint data:
vth = 6747.0
ptat = 6707
tmp -0.7110596
kt2 0.0044441223
inttemp -64.53125

i wonder if I should/could round this off to make it easier on processor.

another though i am having is to switch from internal osc @ 8 mhz, and put a 4mhz XT oscillator and then use hspll to step up to 48mhz, thats how it works right? , it would also solve my processing time problem with this sub. or maybe i should switch to proper tx/rx pins and learn to use uart.h?
 
Is your double really a 64 bit double, or same as float (32 bit)? You can use sizeof(double) to find out. What compiler are you using? Are you linking in the math library?
Maybe helpful or not, but at least interesting reading: https://www.exploringbinary.com/

Edit: I have hard time figuring out what is the question here? What exactly is the problem you are having? Calculations are incorrect, or calculations taking too long.. or something else?

Edit2: Some things that may improve the perfomance (also check your compiler optimization settings):

// INTTEMP = pow(Kt1, 2);
// result2 = 0;
// result2 = Vth - PTAT;
// result2 = result2 * 4;
// result2 = result2 * Kt2;
// INTTEMP = INTTEMP - result2;
//INTTEMP = sqrt( INTTEMP );
//INTTEMP = INTTEMP + (-1)*Kt1;
//INTTEMP = INTTEMP / (2*Kt2);

Could be written:
INTTEMP = Kt1*Kt1; // Get rid of the pow() -call
result2 = Vth - PTAT;
result2 = result2 + result2; // Replace (floating point) multiplication with two additions
result2 = result2 + result2; // Compilers do not do this optimization, because it can cause imprecise intermediate result.
result2 = result2 * Kt2;
INTTEMP = INTTEMP - result2;
INTTEMP = sqrt( INTTEMP );
INTTEMP = INTTEMP - Kt1; // Get rid of multiplication
INTTEMP = INTTEMP / (Kt2+Kt2); // Replace multiplication with addition. Some compilers do optimize this, depending on the settings.
 
Last edited:
-mplabx & xc8;
-found out earlier that doubles here are 24bit and made adequate code adjustments in VB desktop receiver
- I am willing to sacrifice accuracy quite a bit here
-calculations are outputting ok.
-PIC18f2550
-math.h included

calculations taking too long.. or something else?
my interrupt (int0) is triggering every time it should, but when those lines are added the trigger is delayed, and my data sample becomes distorted(offset). I do not think it is feasible that interrupt gets called every time those lines are processing, but there is something about them causing it.
all i can think of is a overflow, as i have seen similar issues when i overload(like when i try to put number 257 into a char) variables sometimes. maybe im accidentally writing over my osctune value for example

OR maybe i am declaring too many doubles at the beginning...

Also I should add that this all worked at one point, but i am not satisfied since the array takes 500ms to transmit, but takes 2.5seconds to calculate this subroutine, initially this code was broken down into several subroutines, but there was alot of looping and passing variables, so i shortened it down to all the single calcs to the beginning of the sub, and a single for loop for the array calculations.
 
I am a little bit annoyed that I am not familiar with the compiler etc. This is interesting problem to me. 24 bit float is strange. But in microcontroller world 'strange' is not unusual. When you multiply two 32 bit variables, you have to operate in 64 bit world and then truncate the result back to 32 bits. You have many double variables and many multiplications etc..

Avr-gcc and avr-libc documentation says this about floating point math:
I am using floating point math. Why is the compiled code so big? Why does my code not work?
GCC has a library that is used for floating point operations, but it is not optimized for the AVR, and so it generates big code, or it could be incorrect. This can happen even when you are not using any floating point math functions from the Standard C library, but you are just doing floating point math operations.

Typically, system libraries like libm.a are given to the final C compiler command line that performs the linking step by adding a flag -lm at the end. (That is, the initial lib and the filename suffix from the library are written immediately after a -l flag. So for a libfoo.a library, -lfoo needs to be provided.) This will make the linker search the library in a path known to the system.

I also agree that this is probably a memory problem. Using floating point does "explode" your memory usage and calculation time.
Most of the calculations could be easily converted to fixed-point (integer) calculations. Only problem is the square root (and dealing with really large and really small numbers at the same time).

I could do some tests (with avr-gcc) when I have time (monday at work).. but I also found this discussion:
https://www.avrfreaks.net/forum/multiply-divide-add-performance-various-data-types
Your compiler is probably designed for microcontollers. Gcc is not. But the general problem is the same.
 
Last edited:
also i think overflow related maybe, even though calculation is able to return proper numbers, the RX array is always off and by the same amount, it is not likely to be receiving data the same time as that calculation is being done every single time.

i will add a few lines tonite to reset osccon & osctune throughout the code, are there other registers that could effect timing?
 
Could you give me an example input that fails. Or typical input to the calculations.
Some calculation may enter a "critical section" that disables interrupts (maybe the square-root).
Can you get a disassembly output from your compiler?
 
Last edited:
see post 2 for common values, also for the first few seconds off startup i notice that i2c device returns high values until it is able to initalise,
let me know if you want to see breakpoint data during startup.
Listing attached.
thnx!

I thought it was the sqrt too, but occurrence happens on any one of those four lines mentioned when "un-rem-ed" even if i do one by one.

// tmp = Vth - PTAT;
// tmp = tmp * 4;
// tmp = tmp * Kt2;
// INTTEMP = INTTEMP - tmp;
// breakpoint here

this is common breakpoint data:
vth = 6747.0
ptat = 6707
tmp -0.7110596
kt2 0.0044441223
inttemp -64.53125
 

Attachments

  • listing.txt
    47.9 KB · Views: 216
another thing i should re - mention, , is that the problem didn't happen until i took each calculation out of each of their individual subroutines and put it all to this one routine( i did it cause: before, each line in the for loop had its own for loop), which was another thing that made me think i was using too many variables...
 
This really does sound like a memory problem. Can you post (link) the original code that you adapted. (The source code provided for arduino).
 
this is link to what was provided(i used windows):
https://www.robotshop.com/content/ZIP/documentation-rm-g212.zip

also , attachments are what i translated to my pic code, this is the code has the several subroutines, works on my pic and does not cause the overflow.

(or maybe it does!, it was after i was getting good readings from here, i set up the TX and then RX subs timing values. (i found this to be tricky(sensitive) as well, at 9600 baud i though i would have more slack in my timing precision.), which is why you see me use so many TX RX subs for different things, and will notice why RXBT(the receiver routine) is so basic and not looped like normal.

i2c_readIR is first sub in there, the only other interaction with that chip is in the start up sub that loads data from the i2c eeprom and sets up i2c configurations (INIT).
 

Attachments

  • newmain2550v1.c
    9.8 KB · Views: 203
  • I2Cthermal.h
    7 KB · Views: 301
  • TxRX.h
    4.9 KB · Views: 231
also , here is newMain, in its current condition , I thought it over and felt it may be worth to see how im calling the RXsub in the interrupt routines , since it wasn't in the last C file
 

Attachments

  • newmain2550v1 - MOST UP TO DATE.c
    15.6 KB · Views: 237
any luck? no worry, i have been thinking about it and i think i will revert back to original code, even though there is not much i can do with baud rate i would still like to speed up the calculation, rite now im using 8mzh internal clk

So i think i want to add this crystal osc to circuit board:
https://www.digikey.ca/scripts/DkSearch/dksus.dll?Detail&itemSeq=184514864&uq=635862082008645677
and then i would use internal pll to step up to 40mhz, is that correct? can i connect part straight to osc pin?

I have never used ext clk before please advise!
thanks!
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top