移植驱动读取SHT30温湿度传感器数据
在我的项目中,会涉及到接入多个传感器设备,并进行自动识别,然后结合AIGC大模型,进行特定情景的生成。
在上一部分,已经测试过I2C设备的读写操作了,在此基础上,进行SHT30温湿度传感器的数据读取。
一、硬件了解
有一点,我最近才注意到:
怪不得我测试SHT30的时候,温度数据偏高。
但是手头暂时没做4.7K的电阻,需要采购。暂时智能硬着头皮先上了。
SHT30如下:
二、I2CPython驱动移植
在官方提供的I2C测试程序中,有关于串口数据读取的实例,参考改示例,编写python调用DLL接口的入口文件:
- import ctypes, sys
- spbdll = ctypes.WinDLL("64" in sys.version and "SPBDLL.dll" or "SPBDLL.dll")
- def __DllFunc(name, ret, args):
- func = spbdll[name]
- func.restype = ret
- func.argtype = args
- return func
- # //-----连接设备驱动---
- SPBDLL_ConnetDev = __DllFunc("SPBDLL_ConnetDev", ctypes.c_void_p, (ctypes.c_char_p))
- SPBDLL_isOK = __DllFunc("SPBDLL_isOK", ctypes.c_bool, (ctypes.c_void_p))
- SPBDLL_ReleaseDev = __DllFunc("SPBDLL_ReleaseDev", ctypes.c_void_p, (ctypes.c_void_p))
- # //----I2C----
- SPBDLL_I2C_Read = __DllFunc("SPBDLL_I2C_Read", ctypes.c_int, (ctypes.c_void_p, ctypes.c_uint16, ctypes.c_buffer, ctypes.c_int32))
- SPBDLL_I2C_Write = __DllFunc("SPBDLL_I2C_Write", ctypes.c_int, (ctypes.c_void_p, ctypes.c_uint16, ctypes.c_buffer, ctypes.c_int32))
- SPBDLL_I2C_Read_RegAddr8bit = __DllFunc("SPBDLL_I2C_Read_RegAddr8bit", ctypes.c_int, (ctypes.c_void_p, ctypes.c_uint8, ctypes.c_buffer, ctypes.c_int32))
- SPBDLL_I2C_Write_RegAddr8bit = __DllFunc("SPBDLL_I2C_Write_RegAddr8bit", ctypes.c_int, (ctypes.c_void_p, ctypes.c_uint8, ctypes.c_buffer, ctypes.c_int32))
复制代码
然后,在编写I2C操作的封装文件:
- import SPBDLL as SPBDLL
- class SpbDllCtrl:
- lpDev = None
- def __init__(self):
- self.lpDev = None
- pass
- # ///
- # /// 连接设备驱动
- # ///
- # /// 驱动名称,在asl和inf中对应的名称
- # ///
- def ConnetDev(self, devName):
- self.lpDev = SPBDLL.SPBDLL_ConnetDev(devName)
- return not self.lpDev == None
- # ///
- # /// 连接设备是否成功
- # ///
- # ///
- def isOK(self):
- if not self.lpDev == None:
- return 1 == SPBDLL.SPBDLL_isOK(self.lpDev)
- return False
- # ///
- # /// 释放设备
- # ///
- def ReleaseDev(self):
- if not self.lpDev == None:
- SPBDLL.SPBDLL_ReleaseDev(self.lpDev)
- self.lpDev = None
- # ///
- # /// 设置中断回调 def fn(fn_Context);需要在asl中配置了IO中断,这里设置才有意义
- # ///
- # /// 回调函数
- # /// 参数
- def SetInterruptCallBack(self, fn, fn_Context):
- if not self.lpDev == None:
- SPBDLL.SPBDLL_SetInterruptCallBack(self.lpDev, fn, fn_Context)
- # //----I2C----
- # ///
- # /// I2C读寄存器内容
- # ///
- # /// 寄存器地址,2字节的地址
- # /// 接收数据
- # /// 计划读取长度
- # ///
- def I2C_Read(self, regAddr, pData, len):
- if not self.lpDev == None:
- return 0 == SPBDLL.SPBDLL_I2C_Read(self.lpDev, regAddr, pData, len)
- return False
- # ///
- # /// I2C写数据到寄存器中
- # ///
- # /// 寄存器地址,2字节的地址
- # /// 数据
- # /// 数据长度
- # ///
- def I2C_Write(self, regAddr, pData, len):
- if not self.lpDev == None:
- return 0 == SPBDLL.SPBDLL_I2C_Write(self.lpDev, regAddr, pData, len)
- return False
- # ///
- # /// I2C读寄存器内容
- # ///
- # /// 寄存器地址,1字节的地址
- # /// 接收数据
- # /// 计划读取长度
- # ///
- def I2C_Read_RegAddr8bit(self, regAddr, pData, len):
- if not self.lpDev == None:
- return 0 == SPBDLL.SPBDLL_I2C_Read_RegAddr8bit(self.lpDev, regAddr, pData, len)
- return False
- # ///
- # /// I2C写数据到寄存器中
- # ///
- # /// 寄存器地址,1字节的地址
- # /// 数据
- # /// 数据长度
- # ///
- def I2C_Write_RegAddr8bit(self, regAddr, pData, len):
- if not self.lpDev == None:
- return 0 == SPBDLL.SPBDLL_I2C_Write_RegAddr8bit(self.lpDev, regAddr, pData, len)
- return False
复制代码
通过以上的入口文件和操作封装,就可以在python程序中,进行I2C接口的操作了。
下面编写一个简单的,与之前的RP2040做的I2C从设备沟通的程序,讲一个计数器的值通过I2C写入到从设备,然后读取该值:
- from SpbDllCtrl import *
- import time
- import atexit
- # 数组转HEX字符串
- to_hex=lambda ary : " ".join(["%02X" % i for i in ary])
- devName = "I2C30001"
- I2CCtrl = None
- def __clear_i2c():
- global I2CCtrl
- if not I2CCtrl == None:
- print("Clear I2C device")
- I2CCtrl.ReleaseDev()
- I2CCtrl = None
- atexit.register(__clear_i2c)
- I2CCtrl = SpbDllCtrl()
- I2CCtrl.ConnetDev(devName)
- if not I2CCtrl.isOK():
- print("I2C device is not connected")
- else:
- print("I2C device is connected")
- rbuf = bytes(4)
- r_len = 4
- bRet = I2CCtrl.I2C_Read_RegAddr8bit(0x00, rbuf, r_len);
- if not bRet:
- print("Read error")
- else:
- print("Read data: ", to_hex(rbuf))
- counter = 0
- while True:
- counter += 1
- if counter > 0xffff:
- break
- hexstr= "%08X" % counter
- rbuf = bytes(bytearray.fromhex(hexstr))
- bRet = I2CCtrl.I2C_Write_RegAddr8bit(0x00, rbuf, r_len);
- if not bRet:
- print("Write error")
- else:
- print("Write data: ", to_hex(rbuf))
- time.sleep(0.1)
- rbuf = bytes(4)
- r_len = 4
- bRet = I2CCtrl.I2C_Read_RegAddr8bit(0x00, rbuf, r_len);
- if not bRet:
- print("Read error")
- else:
- print("Read data: ", to_hex(rbuf))
- time.sleep(1)
- # I2CCtrl.ReleaseDev()
复制代码
运行上述代码,最终结果如下:
从结果中可以看到,python程序将计数器数值写入到从设备,从设备收到并记录。
然后,python程序从从设备读取寄存器的值,就是之前写入的值。
三、SHT30驱动移植
在上一步,已经做好了I2C设备的数据读写了,在此基础上,根据SHT30的数据手册,就可以编写对应的驱动程序了。
为了保持和micropython中的程序一致,我编写了一个伪装接口:
- from SpbDllCtrl import *
- # 数组转HEX字符串
- to_hex=lambda ary : " ".join(["%02X" % i for i in ary])
- class I2C:
- def __init__(self, devname):
- self.devname = devname
- self.I2CCtrl = SpbDllCtrl()
- self.I2CCtrl.ConnetDev(self.devname)
- if not self.I2CCtrl.isOK():
- print("I2C device is not connected")
- raise Exception("连接失败")
- else:
- print("I2C device is connected")
- def write_byte_data(self, i2c_address, register_address, value):
- if self.I2CCtrl.isOK():
- rbuf = bytes(bytearray([value]))
- r_len = len(rbuf)
- bRet = self.I2CCtrl.I2C_Write_RegAddr8bit(register_address, rbuf, r_len);
- if not bRet:
- print("Write error")
- raise Exception("写入错误")
- return False
- else:
- print("Write data: ", to_hex(rbuf))
- return True
- def write_i2c_block_data(self, i2c_address, register_address, values):
- if self.I2CCtrl.isOK():
- rbuf = bytes(values)
- r_len = len(rbuf)
- bRet = self.I2CCtrl.I2C_Write_RegAddr8bit(register_address, rbuf, r_len);
- if not bRet:
- print("Write error")
- raise Exception("写入错误")
- return False
- else:
- print("Write data: ", to_hex(rbuf))
- return True
- def read_byte_data(self, i2c_address, register_address):
- if self.I2CCtrl.isOK():
- r_len = 1
- rbuf = bytes(r_len)
- bRet = self.I2CCtrl.I2C_Read_RegAddr8bit(register_address, rbuf, r_len)
- if not bRet:
- print("Read error")
- raise Exception("读取错误")
- return None
- else:
- print("Read data: ", to_hex(rbuf))
- return rbuf
- else:
- return None
- def read_i2c_block_data(self, i2c_address, register_address, block_width):
- if self.I2CCtrl.isOK():
- r_len = block_width
- rbuf = bytes(r_len)
- bRet = self.I2CCtrl.I2C_Read_RegAddr8bit(register_address, rbuf, r_len)
- if not bRet:
- print("Read error")
- raise Exception("读取错误")
- return None
- else:
- print("Read data: ", to_hex(rbuf))
- return rbuf
- else:
- return None
复制代码
然后,编写实际读取SHT30数据的程序:
- import smbusx as smbus
- import time
- i2c = smbus.I2C("I2C30001")
- addr=0x44
- i2c.write_byte_data(addr,0x30,0xa2)
- time.sleep(0.5)
- while True:
- i2c.write_byte_data(addr,0x2c,0x10)
- data = i2c.read_i2c_block_data(addr,0x00,6)
- if data == None:
- raise Exception("读取错误")
- else:
- rawT = ((data[0]) << 8) | (data[1])
- rawR = ((data[3]) << 8) | (data[4])
- temp = -45 + rawT * 175 / 65535
- RH = 100 * rawR / 65535
- print (str(temp) +"C", str(RH) +"%")
- time.sleep(1)
复制代码
使用RP2040 I2C从设备的时候,读取和返回结果如下:
因为没有实际测量,所以显示的为-45℃。
四、实际硬件数据读取
首先,连接SHT30,务必需要注意第一部分的说明,要上拉电阻。
连接后,具体如下:
实际读取的结果如下:
现在,可以在python程序中,通过I2C接口,读取SHT30的数据了。
在后续的开发中,还会将读取到的数据,融合到AIGC大模型中,进行实际场景的生成和展示。
|