This project is based on Sensirion SHT15 humidity and temperature sensor. The sensor communicates with microcontroller by means of an I2C-like interface. Although this is a proprietary interface and not compatible with the standard I2C, it is very clearly described in the data-sheet.
I use the default 14-bit resolution for measuring the temperature and 12-bit resolution for humidity. The prototype also shows the atmosphere pressure sensor on the board and is an extension of the barometer project. This concerns usage of the pressure sensor, its interface with PIC, and communication with the LCD. The schematics below just shows the added parts to the barometer project.
Schematic | Test circuit | |
Check here for an implementation of these ideas with MSP430 microcontroller.
The temperature code provided by the sensor is almost ready to use. The sensor returns the temperature value in degrees Celsius multiplied by 100 thus providing the resolution of 0.01°C. The sensor output is a linear function of temperature, one just needs to subtract an offset constant from that value. In my case the constant is 4010 for powering the sensor from 5V. The resulting number can be directly used for displaying and contains two digits after the decimal point.
What concerns relative humidity, more computations are needed. First, I planed to display only integer values of humidity without any decimal points because the humidity sensor accuracy is ±2% and displaying fractions makes no sense. Therefore, one can use an 8-bit resolution for humidity. However, using a 14-bit resolution for temperature implies 12-bit resolution for humidity. I just zeroed the last 4 bits to reduce it to an 8-bit value for processing as it is shown below:
0 0 0 0 x x x x x x x x x x x x --> 0 0 0 0 x x x x x x x x 0 0 0 0
where x's denote the humidity bits. The main processing consists of two steps.
The first step is to use a linearization equation of the sensor output, SORH. We start with the one for 8-bit resolution from the data-sheet:
RHlin = -2.0468 + 0.5872·SORH - 0.00040845·SO2RH
The problem with that equation is that it assumes using computations with floating-point numbers, which are not supported by my hardware. Moreover, emulation of floating-point computations takes a lot of resources. So, we approximate this equation by another one that uses only integer arithmetic. First, we multiply it by 107 to get rid of fractions and rewrite it so that only 2 multiplications are involved:
RHlin·107 = (5872000 - 4084.5·SORH)·SORH - 20468000
This is much easier to compute. Further note that the value 4084.5 is pretty close to 4096=212, and multiplication by 4096 can be done by using the left shift operation. However, even this is not needed, since the 2-byte value we get from the sensor after zeroing the 4 low-order bits has the following structure:
0 0 0 0 x x x x x x x x 0 0 0 0
Note that this value can be interpreted as 16·SORH. To obtain the value 4096·SORH all what we need it to add a byte of zeros to the right:
0 0 0 0 x x x x x x x x 0 0 0 0 0 0 0 0 0 0 0 0
This makes multiplication of SORH by 4096 (and also by 16) practically free of charge. However, if we approximate the equation from the data-sheet this way, we will get some different values. As my goal is to display only integer values for humidity, I need to derive an appropriate approximation of the equation that matches the original one in one digit after the decimal point. After a number of experiments with a specially designed computer program I worked out the following approximation equation:
RHlin·107 = (5871300 - 4096·SORH + 16·SORH)·SORH - 19939840
The term in parenthesis is maximum 23-bit and the multiplications involved there reduce to a 24-bit addition and subtraction, as explained above. The only real multiplication involved is an unsigned multiplication of the 24-bit term in parenthesis with an 8-bit value of SORH, which is simple and fast. For this the value of SORH has to be shifted 4 bits to the right in advance to occupy just one byte.
The coefficients involved into my formula are slightly different from the one in the data-sheet. However, my formula matches it up to one digit after the decimal point, which was the goal. The only exclusions are the values SORH = 160 and SORH = 176 where my formula returns a slightly higher result. To compensate this I decrement it by 106 in these cases. Furthermore, at the very end we add 5·106 to the result in order to round it off to an integer value. Therefore, the real constant to subtract at this place is either 15939840 or 14939840.
The humidity sensor output is normalized at 25°C only. For temperatures different from that value it contains an error component. To compensate this error a formula from the data-sheet can be used:
RHtrue = (TC - 25) · (0.01 + 0.00128·SORH) + RHlin
Out goal is again to approximate this equation in order to use only integer arithmetic. Since for the temperature sensor output we have SOT = TC·100, by multiplying the above equation by 100 we get:
RHtrue·102 = (SOT - 2500) · (0.01 + 0.00128·SORH) + RHlin·102
We further multiply this equation by 105 to get rid of fractions:
RHtrue·107 = (SOT - 2500) · (1000 + 128·SORH) + RHlin·107
Now, only integer arithmetic operations are involved. Note that we get RHlin·107 in our approach to Step 1, which can be directly used here. The multiplication involved into the second term in parenthesis can be done by using shifts only. However, to multiply the two terms in parenthesis (which are 16-bit each) a 16-bit by 16-bit multiplication is involved. This is not a big deal, but it would be better if we could reduce it to a 16-bit by 8-bit multiplication, which is much easier and faster. Moreover, this way we could use the multiplication procedure from the first step. To achieve it, we rewrite the formula by dividing the first term in parenthesis by 2 and multiplying the second one by 2 to keep the balance:
RHtrue·107 = ((SOT - 2500)/2) · (2000 + 256·SORH) + RHlin·107
Integer division by 2 can be achieved by using the right shift operation. A multiplication by 256 can be performed by adding a byte of zeros on the right, as indicated below:
x x x x x x x x --> x x x x x x x x 0 0 0 0 0 0 0 0
If the second term in parenthesis would have a zero LSB, then the multiplication of the terms in parenthesis could be done by using a 16-bit by 8-bit integer multiplication (to get a 24-bit value) and adding a byte of zeros on the right to obtain a 24-bit value. However, the constant 2000 involved in the second term does not have all zeros in LSB. So, we replace this constant with 2048, which in binary is 00000100 00000000. This way the second term in parenthesis will have LSB of zeros. However, the computed value will be somehow larger than the required one. To compensate the difference we approximate the equation as follows:
RHtrue·107 = ((SOT - 2500)/2) · (2048 + 256·SORH) - c + RHlin·107
where c = 10240 for |SOT - 2500| < 4096 and c = 61440 otherwise. Those values are optimized by using a specially designed computer program to minimize the difference between the original formula and the approximated one. The difference in the temperature compensating term is ±0.1 and matches the required accuracy for the temperature range -40°C .. 65°C. In most cases the difference is just 0 and is ±0.1 for at most 5% of values of humidity sensor output for each fixed value of temperature. Note that both constants have LSB of zeros which reduces a 32-bit subtraction to a 24-bit one as above.
The value RHlin·107 that we compute this way is maximum 31-bit and consists of 8 or 9 decimal digits (for example, 458,852,361). We only need to extract 3 high-order decimal digits from that value (458 in our example, which corresponds to the percentage value 45.8%). This speeds up convertion of this 32-bit binary constant into BCD for displaying it on LCD, as we do not need to run the BCD conversion for the lower-order decimal digits.
The above considerations lead to the following algorithm for converting a 12-bit value of humidity:
Note that the order of those operations is slightly different in my implementation for the sake of efficiency. Check the supplied code for details. For example, for verifying if an unsigned 16-bit number is smaller than 4096 one can AND the MSB of that number with 0xF0 and check the Z-bit, which will be set if the number is question is smaller than 4096. All this processing is done within the function process_humi().
Last modified:Mon, Jan 23, 2023.