# Convert 2's Complement Floating Point to ASCII

#### jpanhalt

Joined Jan 18, 2008
11,087
I am just beginning a project with the MAX31856 thermocouple digital converter. The results are in "19-bit" 2's complement and cover a range from -250°C to 1600°C.

For example, b'0000 0110 0100 1111 0000 0000' translates to 100.9375°C. I am planning to use it for timing roasts and do not need that range (no roast I have ever cooked reached 255°C internal temperature). All values will be positive, and for internal calculations, I will use the 2's complement after shifting to get rid of unnecessary precision. I would like to display current temperature as the roast cooks.

For that purpose, the above binary value can be shortened to b'0000 0110 0100 1111' (0x064F) and still give me plenty of range with a single digit decimal, e.g., 0x64F can be converted to decimal and divided by 16 to give 100.9375 (0.9375 = 15x2^-4) . For display only, here's what I came up with:
Code:
     movlw     0x00
movwf     tempL
movlw     0x4F
movwf     tempM
movlw     0x06
movwf     tempH
Start     ;preserve tempX regs for further calculations
movf      tempM,w
andlw     0xF0
xorwf     tempH,w
swapf     WREG
call      bin2bcd   ;or bin2ascii
;     print     bcd      ;1-wire serial
;     print     '.'      ;decimal point, 0x2E
movf      tempM,w
andlw     0x0F
call      Table
;     print     ascii
Table     ;Table can be 1 or 2 digits with a little extra work
brw
dt   '0','1','1','2','2','3','4','4'
dt   '5','6','6','7','8','8','9','9'
This is NOT homework, but I thought I would show my work before asking for help. Are there more generalized and relatively simple ways to do that conversion? Also, for me °C doesn't matter much, but I would like to send this gadget to my daughters for whom °F is more familiar. So, a simple °C to °F routine (with decimal) would be helpful.

#### MrChips

Joined Oct 2, 2009
29,864
This is very standard code. I can help you convert from °C to °F and reverse.
You do not need floating point to do this. At this point all you need to decide is how many places after the decimal you need.
If you are simply cooking a roast would whole degrees °C to °F be good enough for you?

#### jpanhalt

Joined Jan 18, 2008
11,087
Yes, of course, whole degrees is all the cook needs to know. But, I like the anticipation of watching the decimals advance. And of course, you have to have some fun while cooking an expensive roast while everyone is in the kitchen. It makes for family discussion and wagers. My real motive is to approximate the cooking curve (see studies by MIT and others) to something a bit more accurate than "30 minutes/pound." That can be important when the meal includes last-minute items like popovers and some vegetables.*

My plan is that the cook will enter some basics, like type of meat, shape (e.g., cylinder, flat, fowl, fish), and weight to get a very rough first approximate based on initial temperature and desired final temperature. At a point, the the cooking curves (see: MIT's results) tend to become almost linear for beef, pork, and lamb roasts (all that I have experience with). For years, I have used that point to predict "doneness" (e.g., 135°F = rare to medium rare) and it has been pretty accurate (< 5 min error), but my daughters don't appreciate the fun in predicting the end with a watch, pencil, and paper. So, I am hoping that by Valentine's day, I can give one a prototype. I will probably miss that deadline by a month or two like last year. No one complained. Great daughters. Everyone liked my belated gift.

Writing the question is often the first step to solving it in my experience. After posting, I got an idea for a polynomial solution (like my 17-bit bin2bcd2ascii snippet) and will try solving it tomorrow or over the weekend. If that fails, I am all ears. The GUI will take far more time than the arithmetic for me to write.

Thanks for the interest, John

*One daughter's turkey started out a "little" frozen this year. My calculations indicated quite a bit more time (at least 30 minutes); hers were different. End result, the turkey was allowed to "sit" more than expected after roasting. Still a great dinner. Decimals are definitely not important for that. I am not as fussy about "doneness" for fowl as I am for mammal meats.

#### MrChips

Joined Oct 2, 2009
29,864
Is it in °C or °F?
16-bit binary can have a range from 0 to 65535 or -32768 to 32767.
One place after the decimal with negative values will give a range from -3276.8 to 3276.7.
I think that should be ample for either temperatures in °C or °F.
I just need to know what scale is the raw data. If it is some random scale from an ADC I can work with that too, linear or non-linear.

#### jpanhalt

Joined Jan 18, 2008
11,087
Is it in °C or °F?
16-bit binary can have a range from 0 to 65535 or -32768 to 32767.
One place after the decimal with negative values will give a range from -3276.8 to 3276.7.
I think that should be ample for either temperatures in °C or °F.
I just need to know what scale is the raw data. If it is some random scale from an ADC I can work with that too, linear or non-linear.
The chip includes linearization, which was a major attraction to me, plus on-chip cold junction compensation.

The raw data is °C (linear!). It is 19-bit, but read from 3 registers as 24 bit -- hence the swapf's/xorwf's I used to get the nibbles I needed aligned into bytes. The chip has been out for a few years. Nothing critical about its layout, but I bought the Adafruit backpack just to save time. Here's a link to the chip's datasheet, page 13 shows some examples of the math: https://datasheets.maximintegrated.com/en/ds/MAX31856.pdf My preliminary plan is to write code for a 12+4 binary -- only because those are convenient bytes/nibbles to work with -- to a 3 bcd + 1bcd (1 decimal) conversion. So far, except for my little surprise at the start, I like the chip. SPI Clock polarity is automatic. The Fault and DRDY flags can be LED's, if you want. I may use an LED for the Fault. Will probably use TMR1 for a 2-second interrupt to read. Choices for read include on-chip averaging for 1x thru 16x (with increased conversion times). I am leaning toward a 4x read. There will be plenty of time for other things.

The only reason it is 19 bits is to cover the full range of type K, including negative temperatures, and other thermocouples. I am using type K, but clearly, I do not need to cover that range.

#### MrChips

Joined Oct 2, 2009
29,864
Knowing that you are using a MAX31856 and having access to the data sheet makes a world of difference.
The MAX31856 outputs 24 bits of data. You can ignore the least 8 bits leaving 16 bits of data and still have 0.0625°C resolution.
That is, you still have 1/16 of a °C to play with and 12 bits of signed integers.

You may choose to ignore two more bits leaving you with 0.25°C resolution. I will leave you with that choice to make.

Incidentally, this is not floating point format. This is called fixed point integer format.

#### MrChips

Joined Oct 2, 2009
29,864
Now, looking over your code I can see what you are doing. That makes sense to me.

1) If the value is negative, convert to positive, and insert the negative sign at the display routine.
2) Convert the upper 12 bits to BCD ASCII
3) Use the lower four bits into a look-up table to display the single digit after the decimal place.

#### jpanhalt

Joined Jan 18, 2008
11,087
Yes, poor terminology on my part. I was using it to mean carrying a fractional value by left shifting a set amount. Depending on how many bytes one uses, the 2's complement results in a value that can to be divided by 2^n to get the temperature + fractional result, which simply prolongs the problem addressed by the table in my first code..

After more thought, I decided it would be easier to convert to °F before doing bin2bcd. That allows me to shift by decimal 10 (i.e., multiply by d.18 instead of 1.8). BCD conversion then gives me a value that can be printed correctly to 0.1 degree (approximate). For example, the binary for 100.9375 °C yields "2136" decimal, which is easily printed as 213.6°F. Similarly, the binary for 25.00 °C yields 770°F, which can be printed as 77.0°F.

I have not tested the code completely, and it is pretty raw, but a few values have worked. I need to test a range of temperatures and add a test for temperatures <32°F ("error"), which will simply be a test of the sign bit.

Here's the preliminary code. Sorry for the sparse comments -- for me only. Regx_bak is simply a duplicate of Regx. Not sure at this point whether I need to preserve the original regs.

Code:
Start
lsrf      tempH_bak,f    ;divide H/M registers by 8
rrf       tempM_bak,f
lsrf      tempH_bak,f
rrf       tempM_bak,f
lsrf      tempH_bak,f
rrf       tempM_bak,w
addwf     tempM,w        ;tempM = 18X integer
btfsc     STATUS,0
incf      tempH_bak,f    ;bump on carry
movwf     tempM_bak
movf      tempH_bak,w
movwf     tempH_bak
Result (x10 in degrees F) is in the tempH/M_bak registers.

EDIT: Sorry, I missed your post #7 while writing my reply. John

Last edited:

#### MrChips

Joined Oct 2, 2009
29,864
I will look at how I would convert to °F perhaps later today.
I am going to try an preserve the sign since you never know when such an application will arrive.

#### MrChips

Joined Oct 2, 2009
29,864
Here it is.

1) Take the 16-bit value and divide by 8 (while preserving the sign) (arithmetic shift right 3 bits).
The result is °F x10.

#### jpanhalt

Joined Jan 18, 2008
11,087
Thanks,

I believe that is what my code (post #8) does.

At least that was my algebra too. Haven't tested the code completely yet, though.

John

#### jpanhalt

Joined Jan 18, 2008
11,087
UPDATE
Made some progress to earlier and thought I would update as a final post in this thread.

Top line is air temperature on top of my desk top -- warmest day of the past two weeks. MAX31856 operating mode is "one-shot" (config reg bit<6> is set) triggered by TMR1 overflow clocked with a 32.768 kHz crystal. Most other conditions are default. The exception is that I am averaging 4 readings (config reg 2, bits<6:4> = 010). Readings are quite stable considering drafts and such.

The second line is a test I included to measure conversion time. That equates to d.7963 ticks = 243 ms and is exactly what value calculated using an equation in the datasheet should be.

After some reading about "floating point," I came across a term to describe sifting the registers ("decimal point") to give a whole number (not fraction), then adjusting the "decimal" based on that shift. That is, creating an integer + exponent versus creating a fraction + exponent. It was called, "biased point."

#### MrChips

Joined Oct 2, 2009
29,864
I call that "fixed point arithmetic".
Typically, I scale all my numbers up by x10 or x100 then I simply put the decimal point in the correct place.
It would be safe for me to say that I never use floating point arithmetic in embedded systems.
I calculate dewpoint from temperature and relative humidity using integer arithmetic.
I also do FFT using integers.

#### MrChips

Joined Oct 2, 2009
29,864
Also, a lot of people take averages by adding N values and dividing by N.
I can show you how to take a running average of N points without having to collect N data points.

#### Ian Rogers

Joined Dec 12, 2012
1,134
Also, a lot of people take averages by adding N values and dividing by N.
I can show you how to take a running average of N points without having to collect N data points.
Interesting.... I have seen this done.......badly.. I would love to see how it should be done.. I have seen each sample join a queue with a shift and a poke then a smoothed output.. but was still a reduced N data points 4 rolling points to be exact.

#### jpanhalt

Joined Jan 18, 2008
11,087
Also, a lot of people take averages by adding N values and dividing by N.
I can show you how to take a running average of N points without having to collect N data points.
I would like that, if not too much trouble to find.

I have generally chosen N as a simple power of 2 and used shifting + adding new value to get that running average. That is, I only keep the sum + N or the average + N. Usually an N of 8 or 16 was used. As the sum may get quite large, keeping the average and subtracting 1/8 or 1/16 before adding the new value may save registers.

#### MrChips

Joined Oct 2, 2009
29,864
You have more or less said it.

You create a variable sum. You make sure the size of sum is N times larger than the largest data point to prevent overflow. You may have to go to double precision for sum.

Every new data point is added to sum via:

sum = sum + (new_data - average_data)
average_data = sum / N

where N is a power to 2 to utilize shift operation.

#### jpanhalt

Joined Jan 18, 2008
11,087
I did quite a bit of that when messing with accelerometers and a magnetic rotary encoder a few years back.

My challenge now is to predict a finish time based on acquired data/slope. A typical cooking curve looks like this:

Blue is actual, dotted is predicted from a thermodynamics model (diffusion), and meat is turkey. I am not too worried about the start, I think I can do the linear portion relatively easily -- been doing that with my handheld calculator for 20 + years (two straight lines intersecting halfway through). I usually do my first calculation when the meat starts heating above 40°F and then second around 80°F for beef. It is the end exponential I am worried about. Many cooks just assume a 5° to 7° rise after removal from the oven. I may resort to something like that adjusted empirically for the weight and thickness of the roast. But, I am hoping I can do a little better. I have no idea how to approximate an exponent given the data. Trial and error? Table? Use a trig function?

John

#### MrChips

Joined Oct 2, 2009
29,864
I think I might be able to find my log and exp code.

But not sure that I'll get this done in time for your Xmas turkey dinner.

#### jpanhalt

Joined Jan 18, 2008
11,087
Don't worry, it's for Valentines day. Log and exponent code would be great. I do not have either.

I have done a little reading since posting. This bit of wisdom was one lead in:

My need is somewhere between chalk and ax levels of accuracy. There were also some interesting posts on Arduino forums. For example, this: https://forum.arduino.cc/index.php?topic=450560.0

John