【问题标题】:Convert I2C Sensor (DS1624) reading into number将 I2C 传感器 (DS1624) 读数转换为数字
【发布时间】:2013-08-29 07:29:17
【问题描述】:

首先,抱歉标题令人困惑。这里已经很晚了,我想不出更好的。

所以,我有一个 I2C 温度传感器,可以将当前温度输出为 16 位字。从左到右读取,第 1 位是 MSB,第 13 位是 LSB,所以 13 位是有效载荷,最后 3 位是零。我想用 Raspberry Pi 读出那个传感器并转换数据。

第一个字节(8 位)是当前温度的整数部分。当且仅当温度为负时,整个单词的二进制补码必须被构建。

第二个字节是小数部分,必须乘以0.03125。

因此,仅举几个例子(TEMP DIGITAL OUTPUT (Binary) / DIGITAL OUTPUT (Hex),取自此处的数据表http://datasheets.maximintegrated.com/en/ds/DS1624.pdf

 +125˚C    | 01111101 00000000 | 7D00h
+25.0625˚C | 00011001 00010000 | 1910h
+½˚C       | 00000000 10000000 | 0080h
0˚C        | 00000000 00000000 | 0000h
-½˚C       | 11111111 10000000 | FF80h
-25.0625˚C | 11100110 11110000 | E6F0h
-55˚C      | 11001001 00000000 | C900h

由于字节顺序不同,读取传感器时字节顺序会颠倒,这不是问题。 例如,第一行会变成 0x007D 而不是 0x7D00,0xE6F0 会变成 F0E6,等等……

但是,一旦我为负值构建了二进制补码,我就无法得出正确的转换。

我想出的(不适用于负值)是:

import smbus
import time
import logging

class TempSensor:
"""

Class to read out an DS1624 temperature sensor with a given address.

DS1624 data sheet: http://datasheets.maximintegrated.com/en/ds/DS1624.pdf

Usage:

    >>> from TempSensor import TempSensor
    >>> sensor = TempSensor(0x48)
    >>> print "%02.02f" % sensor.get_temperature()
    23.66

"""

# Some constants
DS1624_READ_TEMP = 0xAA
DS1624_START = 0xEE
DS1624_STOP = 0x22

def __init__(self, address):
    self.address = address
    self.bus = smbus.SMBus(0)

def __send_start(self):
    self.bus.write_byte(self.address, self.DS1624_START);

def __send_stop(self):
    self.bus.write_byte(self.address, self.DS1624_STOP);

def __read_sensor(self):         
    """    
    Gets the temperature data. As the DS1624 is Big-endian and the Pi Little-endian, 
    the byte order is reversed.

    """            

    """
    Get the two-byte temperature value. The second byte (endianness!) represents
    the integer part of the temperature and the first byte the fractional part in terms
    of a 0.03125 multiplier.
    The first byte contains the value of the 5 least significant bits. The remaining 3
    bits are set to zero.
    """
    return self.bus.read_word_data(self.address, self.DS1624_READ_TEMP)        

def __convert_raw_to_decimal(self, raw):
    # Check if temperature is negative
    negative = ((raw & 0x00FF) & 0x80) == 0x80

    if negative:
        #  perform two's complement
        raw = (~raw) + 1

    # Remove the fractional part (first byte) by doing a bitwise AND with 0x00FF
    temp_integer = raw & 0x00FF

    # Remove the integer part (second byte) by doing a bitwise AND with 0XFF00 and
    # shift the result bits to the right by 8 places and another 3 bits to the right 
    # because LSB is the 5th bit          
    temp_fractional = ((raw & 0xFF00) >> 8) >> 3

    return temp_integer + ( 0.03125 * temp_fractional)

def run_test(self):
    logging.basicConfig(filename='debug.log', level=logging.DEBUG)

    # Examples taken from the data sheet (byte order swapped)
    values = [0x7D, 0x1019, 0x8000, 0, 0x80FF, 0xF0E6, 0xC9]

    for value in values:
        logging.debug('value: ' + hex(value) + ' result: ' + str(self.__convert_raw_to_decimal(value)))

def get_temperature(self):
    self.__send_start();
    time.sleep(0.1);
    return self.__convert_raw_to_decimal(self.__read_sensor())

如果您运行 run_test() 方法,您就会明白我的意思。所有负值都是错误的。

我得到的结果是:

DEBUG:root:value: 0x7d result: 125.0
DEBUG:root:value: 0x1019 result: 25.0625
DEBUG:root:value: 0x8000 result: 0.5
DEBUG:root:value: 0x0 result: 0.0
DEBUG:root:value: 0x80ff result: 1.46875
DEBUG:root:value: 0xf0e6 result: 26.03125
DEBUG:root:value: 0xc9 result: 55.96875

所以,我已经为此努力了好几个小时,但似乎我缺乏按位运算的基础知识。我认为问题在于当值为负时使用逻辑 AND 进行屏蔽。

编辑:网络上有几个实现。它们都不适用于负温度。我尝试将传感器实际放入冰水中。我还没有尝试过 Arduino C++ 版本,但是从源代码来看,它似乎根本没有构建二进制补码,所以也没有负温度 (https://github.com/federico-galli/Arduino-i2c-temperature-sensor-DS1624/blob/master/DS1624.cpp)。

【问题讨论】:

    标签: python bit-manipulation sensors i2c


    【解决方案1】:

    两件事,你已经把你的面具转过来了,raw & 0x00ff 是小数部分,而不是整数部分,第二,这是我的解决方案,考虑到你表中的输入,这似乎对我有用:

    import struct
    
    def convert_temp (bytes):
        raw_temp = (bytes & 0xff00) >> 8
        raw_frac = (bytes & 0x00ff) >> 3
    
        a, b = struct.unpack('bb', '{}{}'.format(chr(raw_temp), chr(raw_frac)))
        return a + (0.03125 * b)
    

    在处理更基本的数据类型(例如有符号字节)时,struct 模块真的很漂亮。希望这会有所帮助!

    编辑:忽略你的面具上的评论,我现在看到了我自己的错误。你可以切换字节,应该没问题。

    结构说明: Struct.(un)pack 都接受 2 个参数,第一个是指定结构属性的字符串(用 C 语言思考)。在 C 中,结构只是一堆字节,其中包含一些关于它们的类型的信息。第二个参数是你需要解码的数据(它需要是一个字符串,解释讨厌的 format())。

    我似乎无法真正进一步解释它,我认为如果您阅读了 struct 模块和 C 中的结构,并意识到结构只不过是一堆字节,那么您应该没问题:)。

    至于二进制补码,即有符号字节的正则表示,无需转换。您遇到的问题是 Python 不理解 8 位整数和符号。例如,您可能有一个有符号字节 0x10101010,但如果您在 Python 中使用 long(),它不会将其解释为有符号的 8 位 int。我的猜测是,它只是将其放入 32 位 int 中,在这种情况下,符号位被解释为只是第 8 位。

    struct.unpack('b', ...) 所做的实际上是将这些位解释为 8 位有符号整数。不确定这是否使它更清晰,但我希望它有所帮助。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-13
    • 2018-04-08
    • 1970-01-01
    • 2014-06-30
    相关资源
    最近更新 更多