In this article I would describe how I built a simple digital thermometer that is capable of not only showing the current temperature but also send it over RS-232 interface. The idea is that this temperature sensor is one of many which are connected to a common gateway which in turn reads their values and sends them to the Internet.
For this project I’ve chosen the tmp102 sensor from SparkFun (https://www.sparkfun.com/products/11931) but will also describe how to use tcn75 sensor from Michrochip.
Table of Contents
I’ve already shown how to read the tcn75 and tmp102 sensors using their I²C interface. If you haven’t read it please see these two articles: Communicating with tmp102 Sensor Over I2C Using PIC MCU and Assembly – Part 1 and Communicating with tmp102 Sensor Over I2C Using PIC MCU and Assembly – Part 2
Now I begin right after the above article. I assume that we know how to read the temperature value from the sensor and we already have it as two bytes in the data memory. What we have to do is to display it on the 4 digit 7 segment indication and optionally send the data through the USART port of the PIC microcontroller.
Displaying the Temperature on a Four Digit LED Indication
To be able to show the temperature on a seven segment indication we should first see how it actually works. It is actually very simple since these indications are nothing but a bunch of LEDs in one rectangular package. By turning them on and off you can display discrete digits – 1, 2, … 9, 0 this can easily be done through the PICs ports. For a four digit indication though things are slightly more complicated. Apart from the 7 segments comprising the digit we have four more pins controlling which digit we are showing at the moment. Let’s see the schematic of the display I used – Kingbright CA56- 12SRWA. This is common anode indication meaning that all 7 segments have their anode (or positive lead) connected together.
Looking at the circuit it is visible that there are only 7 pins for the 7 segments and 4 pins to switch between the four digits. But how do we show different digits in the different places?
Challenge: How do we show different digits in the different places?
Easy! Let’s see an example – let’s show the number 1234. Let’s assume we have connected the segment pins (a-g and DP) to the pins of PORTB. These occupy the whole port and we have connected the digit pins to PORTA of the controller. Thus by switching on and off bits of PORTB we switch on and off segments on the indication and by switching on and off bits of PORTA we switch between digits on the LED display.
So we start with the first digit. We load the value b’00000110′ into the PORTB register and the value 0x01 into the PORTA register. The value into PORTB means we are lighting the b and c segments indication which form the digit 1 and then we show it in the first position. After that we load the value b’01011011′ into PORTB and 0x02 into PORTA. The digit 2 is shown in the second position because we have switched on segments: a, b, d, e, g. Then we load b’01001111′ to show number 3 in the third place and so on.
If we change the digits repeatedly from the first to the last and again in an infinite loop the we will have “perfect” animation and our eyes will not detect that we are actually switching between the digits and only showing one of them at any given time! Depending on the crystal you have chosen for the microcontroller you will achieve different frame rates. Important thing is that our eyes will detect the switching if we fall below the 60 frames per second so we have to be careful. Another important thing is that we have only one “thread of execution” on the microcontroller. So in the infinite loop in which we display the temperature on the LED indication we have to do everything else: read the sensor, send over UART, etc. thus we have to estimate (or calculate precisely) how much time each such operation takes and adjust our animation accordingly. If we have to do a time consuming calculation with the temperature for example this means that our display will freeze for a moment (until the calculation ends) and show the last digit only!
Separate the 12 bit temperature value into distinct digits
Ok, we now know how to show separate digits on the display, but how do we show the temperature we read from the sensor? Remember we have it as a single 12bit floating point number: 23.4 for example. We have only one digit after the decimal point. We should first multiply by 10 to “get rid of the decimal separator”. Then we would have the integer 234. Then we do integer divide by ten. The result is 23 and reminder 4. So we have separated the digit after the decimal point. We can now send it to the display. After that we take the result (23) and again divide it by ten the result is 2 and reminder 3. The reminder in this case are the ones, so we send it again to the display. Finally we put the 2 in the first position of the display. If we had a bigger number we would have continued the division until we get all distinct digits.
In a high level language like C or Java this division is a piece of cake. On an 8bit controller programmed with assembly things are not so easy though! There are some algorithms with which you can achieve 16bit floating point number division and multiplication however I will not describe them here. What we are going to do is to use routines provided by Microchip in their application notes: 575, 617 and 526:
- FLO1624; Integer to float conversion
; Input: 16 bit 2’s complement integer right justified in AARGB0, AARGB1
; Use: CALL FLO1624 or CALL FLO24
; Output: 24 bit floating point number in AEXP, AARGB0, AARGB1
; Result: AARG <– FLOAT( AARG )
- FPM24; Floating Point Multiply
; Input: 24 bit floating point number in AEXP, AARGB0, AARGB1
; 24 bit floating point number in BEXP, BARGB0, BARGB1
; Use: CALL FPM24
; Output: 24 bit floating point product in AEXP, AARGB0, AARGB1
; Result: AARG <– AARG * BARG
- INT2424; Float to integer conversion
; Input: 24 bit floating point number in AEXP, AARGB0, AARGB1
; Use: CALL INT2424
; Output: 24 bit 2’s complement integer right justified in AARGB0, AARGB1, AARGB2
; Result: AARG <– INT( AARG )
- FXD2416U; 24/16 Bit Unsigned Fixed Point Divide 24/16 -> 24.16
; Input: 24 bit unsigned fixed point dividend in AARGB0, AARGB1,AARGB2
; 16 bit unsigned fixed point divisor in BARGB0, BARGB1
; Use: CALL FXD2416U
; Output: 24 bit unsigned fixed point quotient in AARGB0, AARGB1,AARGB2
; 16 bit unsigned fixed point remainder in REMB0, REMB1
; Result: AARG, REM <– AARG / BARG
With these we can easily convert the twelve bit number coming from the sensor into 16bit microchip floating point one and then divide and multiply it by 10 and convert it into decimal.
One more thing. When we read the temperature from the sensor it comes out as two byte left justified number. Since only 12bit carry temperature data we have to “shift” the bits in those two bytes by four places so that we have our value right justified. Or simply the temp values’ LSB (least significant bit) should be the LSB of the low byte.
Since we know we have exactly 12bits this can be easily achieved with the SWAPF instruction:
;right shifts 12bit number by 4 places ;byte 1 most significant byte ;byte 2 least significant byte right_shift_12bit_number MACRO banksel T_high_byte swapf T_low_byte,1 swapf T_high_byte,1 movfw T_high_byte andlw 0xF0 iorwf T_low_byte,1 movlw 0x0F andwf T_high_byte,1 endm
The whole program can be seen on my github page: https://github.com/hborisov/thermometer