Don,
UNCLE! This is waay to much for me to bite off. . .hopefully when you get your sensor (which I think you said arrives today) you can make sense of it. I think I have to accept the fact that this is over my head. The main problem is that the MLX90614 is SMBus, and not I2C specifically. I think it's doing some funny things that are making the Zbasic I2C functions choke.
Anyways, that's my theory! Hopefully you can shed some light on the situation. Your insights and support are MUCH appreciated. You've personally made it possible for me to take on the challenge of learning Zbasic. . .THANKS!
-Jeff
SMBus Sample Code
The code below produces sensible results using either the low-level I2C routines or the high-level routine. With this as a starting point it should be fairly easy to extend the code to write the EEPROM value to configure it for PWM mode. If this is done, the code below will need to be modified to pulse SCL low for 1.5mS to get it into I2C mode.everest wrote:hopefully when you get your sensor (which I think you said arrives today) you can make sense of it.
Code: Select all
'
'' GetTemp_MLX90614
'
' This function reads the temperature value of object #1 from the
' MLX90614 and converts it to degrees Centigrade.
'
Function GetTemp_MLX90614() as Single
' uncomment this line to use low-level I2C routines
'#define LOW_LEVEL_I2C
' define some constants
Const chan as Byte = 0
Const slaveAddr as Byte = Shl(&H5a, 1) ' factory default I2C address
Const Obj1TempAddr as Byte = 7 ' the RAM address of the object #1 temp
Const BaseTempVal as UnsignedInteger = &H27ad ' the reading for -70.01*C
Const BaseTemp as Single = -70.01
Const TempRes as Single = 0.02 ' the resolution in *C
Dim data(1 to 10) as Byte
' open the I2C channel for operation
Call OpenI2C(chan, 0, 0)
#if defined(LOW_LEVEL_I2C)
' use low-level I2C commands
Dim stat as Boolean
Call I2CStart(chan)
stat = I2CPutByte(chan, slaveAddr And &Hfe)
stat = I2CPutByte(chan, Obj1TempAddr)
Call I2CStart(chan)
stat = I2CPutByte(chan, slaveAddr Or &H01)
data(1) = I2CGetByte(chan, True) ' low byte
data(2) = I2CGetByte(chan, True) ' high byte + error bit (MSB)
data(3) = I2CGetByte(chan, False) ' CRC-8
Call I2CStop(chan)
#else
' use high-level I2C command
Dim cmd as Byte
cmd = Obj1TempAddr
Dim result as Integer
' read the low byte, high byte + error bit (MSB), and CRC-8
result = I2CCmd(chan, slaveAddr, 1, cmd, 3, data)
#endif
Call CloseI2C(chan)
#if defined(DEBUG_I2C)
Dim i as Integer
For i = 1 to 3
Debug.Print CStrHex(data(i))
Next i
#endif
' convert the result to degrees Centigrade
Dim tempVal as UnsignedInteger
tempVal = MakeWord(data(1), data(2) And &H7f)
GetTemp_MLX90614 = (CSng(tempVal - BaseTempVal) * TempRes) + BaseTemp
End Function
- Don Kinzer
Hey Don,
Thanks a million for this. . .you are amazing!
I have a couple of questions that are probably trivial, but I can't seem to answer them digging through the system reference. I've seen you use syntax like this several times:
What does the AND do? It looks like you are combining those two values into one argument, but I don't understand what exactly is going on there.
-Jeff
Thanks a million for this. . .you are amazing!
I have a couple of questions that are probably trivial, but I can't seem to answer them digging through the system reference. I've seen you use syntax like this several times:
Code: Select all
stat = I2CPutByte(chan, slaveAddr And &Hfe)
-Jeff
The And operator in ZBasic is either a bitwise logical operator or a Boolean logic operator depending on how it is used. (Some languages, like C and C++, use two different operators for these distinct operations.) In the example you gave, it is being used as a bitwise logical operator, essentially performing a Boolean And between each of the bits of the operands.everest wrote:What does the AND do?
To review, the result of a Boolean And is true if and only if both operands are true. Consequently, if A has the binary value &B00111101 and B has the binary value &B11111110, the result of A And B is &B00111100. Each bit of the result is the And of the corresponding bits of the two operands.
As used in the I2C code, the And &Hfe ensures that the least significant bit of the result is zero and all the remaining bits retain their original values.
ZBasic has three other logical operators: Or, Xor and Not. For the first two, the idea is the same as described above but the result is the Boolean Or (true if either of the operands are true) or Boolean Exclusive-Or (true if either but not both of the operands are true). The Not operator is different in that it is a unary operator, i.e. it only takes one operand. The result of a Not operation is true if and only if the operand is false.
These are concepts that are very useful in programming and should thus be mastered.
- Don Kinzer
Don,
Wow, thanks for the information. I had no idea one could use an AND that way, I use them in loops all the time, but never like that!!
I'm deeply. . .DEEPLY embarrassed to admit that all of my testing up to this point has been with my MLX90614 plugged in to P11 and P12 on my Parallax BOE, failing to account for the fact that 11, 12 are true pin references. I know this but for some reason just wired this up wrong. *smack*
Thanks again!
-Jeff
Wow, thanks for the information. I had no idea one could use an AND that way, I use them in loops all the time, but never like that!!
I'm deeply. . .DEEPLY embarrassed to admit that all of my testing up to this point has been with my MLX90614 plugged in to P11 and P12 on my Parallax BOE, failing to account for the fact that 11, 12 are true pin references. I know this but for some reason just wired this up wrong. *smack*
Thanks again!
-Jeff
See the table of logical operators. Note that the permitted operand type for logical And is Boolean only (they way that you've been using it) but for the bitwise And the permitted operand type is any integral, the way that I used it.everest wrote:I had no idea one could use an AND that way
- Don Kinzer
The code below can be used to set the I2C slave address for an MLX90614. This is necessary if you want to use multiple MLX90614 devices connected on the same bus since they must each have a unique address.
Note, also, that this code contains a function to assist in the calculation of the CRC-8 value of an SMBus datastream. Because all of the bytes sent, including the slave address byte, contribute to the CRC-8 value, the low-level I2C routines are used. The I2CCmd() function will still work but it hides some of the data bytes sent over the bus that must be factored into the CRC value.
Note, also, that this code contains a function to assist in the calculation of the CRC-8 value of an SMBus datastream. Because all of the bytes sent, including the slave address byte, contribute to the CRC-8 value, the low-level I2C routines are used. The I2CCmd() function will still work but it hides some of the data bytes sent over the bus that must be factored into the CRC value.
Code: Select all
'
' This program can be used to set the SMBus slave address of an MLX90614.
' Note, particularly, that the device being modified must be the only
' device on the bus since "all call" addressing is used.
Private Const chan as Byte = 0
Private Const slaveAddr as Byte = 0 ' slave address 0 is used
Private Const SlaveIdAddr as Byte = &H2e ' the EEPROM address of the I2C slave address
' set this constant to the desired address
Private Const newAddr as Byte = &H6a
Sub Main()
Call OpenI2C(chan, 0, 0)
Call Sleep(0.25)
Dim curAddr as Byte
curAddr = GetAddr_MLX90614()
If (curAddr = newAddr) Then
Debug.Print "The slave address is already set to &H"; CStrHex(newAddr)
Else
Debug.Print "The slave address is currently &H"; CStrHex(curAddr)
' set the address to zero first
Call SetAddr_MLX90614(&H00)
Call Sleep(0.5)
' then set to the desired address
Call SetAddr_MLX90614(newAddr)
Call Sleep(0.5)
Debug.Print "The slave address is now set to &H"; CStrHex(newAddr)
Debug.Print "Cycle the power to have the new address take effect"
End If
Call CloseI2C(chan)
End Sub
'
'' GetAddr_MLX90614
'
' Determine the current slave address setting.
'
Private Function GetAddr_MLX90614() as Byte
Dim data(1 to 10) as Byte
Dim stat as Boolean
Call I2CStart(chan)
stat = I2CPutByte(chan, slaveAddr And &Hfe)
stat = I2CPutByte(chan, SlaveIdAddr)
Call I2CStart(chan)
stat = I2CPutByte(chan, slaveAddr Or &H01)
data(1) = I2CGetByte(chan, True) ' low byte
data(2) = I2CGetByte(chan, True) ' high byte (meaningless)
data(3) = I2CGetByte(chan, False) ' CRC-8
Call I2CStop(chan)
GetAddr_MLX90614 = data(1)
End Function
'
'' SetAddr_MLX90614
'
' Set the slave address value.
'
Private Sub SetAddr_MLX90614(ByVal addr as Byte)
Dim crc as Byte = 0
Call I2CStart(chan)
Call sendByte(chan, slaveAddr And &Hfe, crc)
Call sendByte(chan, SlaveIdAddr, crc)
Call sendByte(chan, addr, crc) ' low address byte
Call sendByte(chan, 0, crc) ' high address byte (meaningless)
Call sendByte(chan, crc, crc) ' the computed CRC-8 value
Call I2CStop(chan)
End Sub
Private Sub sendByte(ByVal i2cChan as Byte, ByVal data as Byte, ByRef crc as Byte)
Dim stat as Boolean
stat = I2CPutByte(i2cChan, data)
crc = CRC8(crc, data)
End Sub
'
'' CRC8
'
' Permute the CRC-8 accumulator with a given data byte. The initial
' CRC-8 accumulator value should be zero.
'
Private Function CRC8(ByVal CRC as byte, ByVal data as byte) as byte
CRC8 = crcTable((CRC xor data) + 1)
End Function
' Polynominal lookup table for CRC calculation
Private crcTable as ByteVectorData({
&H00, &H07, &H0E, &H09, &H1C, &H1B, &H12, &H15, &H38, &H3F, &H36, &H31, &H24, &H23, &H2A, &H2D,
&H70, &H77, &H7E, &H79, &H6C, &H6B, &H62, &H65, &H48, &H4F, &H46, &H41, &H54, &H53, &H5A, &H5D,
&HE0, &HE7, &HEE, &HE9, &HFC, &HFB, &HF2, &HF5, &HD8, &HDF, &HD6, &HD1, &HC4, &HC3, &HCA, &HCD,
&H90, &H97, &H9E, &H99, &H8C, &H8B, &H82, &H85, &HA8, &HAF, &HA6, &HA1, &HB4, &HB3, &HBA, &HBD,
&HC7, &HC0, &HC9, &HCE, &HDB, &HDC, &HD5, &HD2, &HFF, &HF8, &HF1, &HF6, &HE3, &HE4, &HED, &HEA,
&HB7, &HB0, &HB9, &HBE, &HAB, &HAC, &HA5, &HA2, &H8F, &H88, &H81, &H86, &H93, &H94, &H9D, &H9A,
&H27, &H20, &H29, &H2E, &H3B, &H3C, &H35, &H32, &H1F, &H18, &H11, &H16, &H03, &H04, &H0D, &H0A,
&H57, &H50, &H59, &H5E, &H4B, &H4C, &H45, &H42, &H6F, &H68, &H61, &H66, &H73, &H74, &H7D, &H7A,
&H89, &H8E, &H87, &H80, &H95, &H92, &H9B, &H9C, &HB1, &HB6, &HBF, &HB8, &HAD, &HAA, &HA3, &HA4,
&HF9, &HFE, &HF7, &HF0, &HE5, &HE2, &HEB, &HEC, &HC1, &HC6, &HCF, &HC8, &HDD, &HDA, &HD3, &HD4,
&H69, &H6E, &H67, &H60, &H75, &H72, &H7B, &H7C, &H51, &H56, &H5F, &H58, &H4D, &H4A, &H43, &H44,
&H19, &H1E, &H17, &H10, &H05, &H02, &H0B, &H0C, &H21, &H26, &H2F, &H28, &H3D, &H3A, &H33, &H34,
&H4E, &H49, &H40, &H47, &H52, &H55, &H5C, &H5B, &H76, &H71, &H78, &H7F, &H6A, &H6D, &H64, &H63,
&H3E, &H39, &H30, &H37, &H22, &H25, &H2C, &H2B, &H06, &H01, &H08, &H0F, &H1A, &H1D, &H14, &H13,
&HAE, &HA9, &HA0, &HA7, &HB2, &HB5, &HBC, &HBB, &H96, &H91, &H98, &H9F, &H8A, &H8D, &H84, &H83,
&HDE, &HD9, &HD0, &HD7, &HC2, &HC5, &HCC, &HCB, &HE6, &HE1, &HE8, &HEF, &HFA, &HFD, &HF4, &HF3
})
- Don Kinzer