HDC1080 Try
AGCB
Posts: 344
in Propeller 1
I've been trying to get some simple data from a new chip I got. The temp data never changes and I'm doing something wrong with the math. I've attached a picture of the TV screen and code.
Thanks for looking.
[img][/img]
Thanks for looking.
CON
_clkmode = xtal1 + pll16x '80 MHz system clock
_xinfreq = 5_000_000
'I2C pins
scl = 20
sda = 21
Bitrate = 10000 'I2C @ 50Khz for longish cables
HDC = $40 'I2C 7 bit address
Config_reg = $02 'config register address
trigger = $00 'Trigger byte & temperature output register
Hum_out = $01 'humidity output register
Config_params = %1001_0000_0000_0000 'see pg 15
OBJ
TV : "TV_TEXT"
I2C : "I2C PASM driver v1.8OD"
VAR
byte data[4] '0 & 1 temp output, 2 & 3 hum output
word temper, humid ,RH
long temp_C
PUB main
TV.start(0)
WaitMS(100)
I2C.start(SCL,SDA,bitrate)
WaitMS(100)
I2C.writeWordB(HDC,Config_reg,Config_params) 'configure device
WaitMS(100)
repeat
I2C.command(HDC,trigger) 'start a temp, hum measurement
WaitMS(100) 'allow time for measurement
tv.home
temper := (I2C.ReadByte(HDC,$00) << 8)
temper |= I2C.ReadNext(HDC)
temper := temper <<= 2
humid := (I2C.ReadByte(HDC,$01) << 8)
humid |= I2C.ReadNext(HDC)
humid := humid <<= 2
tv.cr
tv.str(string ("temp in bin = "))
tv.bin(temper,14)
tv.cr
tv.dec(temper)
tv.cr
tv.str(string("hum in bin = "))
tv.bin(humid,14)
tv.cr
tv.dec(humid)
waitms(1000)
temp_C := (((temper/65536)*165)-40)
RH:=((humid/65536)*100)
tv.cr
tv.str(string("temp in c = "))
tv.dec(temp_c)
tv.cr
tv.str(string("RH = "))
tv.dec(RH)
WaitMS(1000)
PUB WaitMS(millisec) |time
time := clkfreq/1000 * millisec
waitcnt(time + cnt)
[img][/img]

Comments
temp_C := ((temper*165)/65536)-40)
But the temper value looks way too large. 65028 will be 123.72°C ???
Bean
Bean is correct that dividing a 14-bit number by 2^16 results in loss of all digits, but even with multiplying before dividing you still lose fractional digits.
The temperature ranges from -40 to +125, a span of 165. With 14-bit resolution, each lsb equals 0.0107 degrees. The operation temper := temper >> 2 - 4000 gets you degrees x 100; 1 = -3999, 16383 = 12383.
If you don't need that resolution, keep temper and humid at 16-bits and use; temper := temper * 165 / 65536 - 40 and humid := humid * 100 / 65536.
If I'm reading the datasheet properly, the measurements are triggered with a write to register $00. Then after waiting for the HDC1080 to complete, the measurements are read from $00 and $01.
A possible problem that I see here is re-addressing the register for the read begins another sample. If it's not a problem, then the i2c method readWordB can be used instead of the readByte and readNexts with shifting and or'ing. I also don't know if the readNext method even works with this device, as the HDC1080 returns 16 bits from a single register address; I've never seen that before.
I included an arbitrary method for non-standard devices, to use: i2c.arbitrary(@local_long,1,@local_long,4). You'll have to declare local_long and load local_long := HDC << 1 | 1 into it before executing, the temperature will be in bits[31:18] and humidity in bits[15:02] afterward.
Lastly, the PASM version of that I2C driver is intended for high speeds. The Spin version of that driver runs at about 20Kbps and uses the same methods without consuming a cog.
Edited because I forgot how my own code works
I'll make some mods and have another go of it
BUT, although the 32 bit value from the HDC keeps updating every 3 seconds as it should. everything else stays the same and the temper and humid BIN read nothing. I've tried other ways of masking but I think BITWISE AND(&) is the right way.
PUB main | local_long TV.start(0) WaitMS(100) I2C.init(SCL,SDA) WaitMS(100) I2C.writeWordB(HDC,Config_reg,Config_params) 'configure device WaitMS(100) humid~ temper~ repeat local_long := HDC << 1 | 1 I2C.command(HDC,$00) 'start a temp, hum measurement WaitMS(100) 'allow time for measurement temper:=humid:=0 tv.home i2c.arbitrary(@local_long,1,@local_long,4) WaitMS(100) tv.str(string("32 bit output from HDC")) tv.cr tv.bin(local_long,32) tv.cr WaitMS(100) temper:= @local_long & %1111_1111_1111_1111_0000_0000_0000_0000 'AND to mask all except 31:18 tv.cr tv.str(string("Temp in decimal ")) tv.dec(temper) tv.cr tv.str(string ("temp in bin = ")) tv.bin(temper,14) tv.cr tv.cr tv.str(string("hum in bin = ")) tv.bin(humid,14) humid:=@local_long & %0000_0000_0000_0000_1111_1111_1111_1111 'AND to mask all except 15:02 tv.cr tv.str(string("Hum in decimal ")) tv.dec(humid) waitms(1000) temp_C := ((temper*165/65536)-40) RH:=(humid*100/65536) tv.cr tv.cr tv.str(string("temp in c = ")) tv.dec(temp_c) tv.cr tv.str(string("RH = ")) tv.dec(RH) WaitMS(3000)I tried converting the returned binary value into temp and humidity and discovered another problem in that the i2c.arbitrary method reads little-endian.
The easiest/laziest fix is: Looks like it's about 19c and 43% RH there.
For some reason I didn't see your reply. I've been busy and not programming.
That took care of the problem and it now works. I get mixed up with that @ operator. Still no handle on it.
I'll try to clean up the code and add it to my weather station.
Thanks very much Chris!
Aaron
Chris
Could not the value just be reversed with >< to get big-endian? I'm less than novice!
Aaron