欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

树莓派与Python实验5——温湿度模块BME?P280采集实验

程序员文章站 2022-06-22 09:43:08
1.实验目的了解温湿度模块的数据读取方式;了解4位LED数码管的显示控制方法;掌握树莓派编程方法;2.实验原理本实验以树莓派开发板作为控制板,通过树莓派的GPIO口连接温湿度模块和4位LED数码管显示模块,通过温湿度控制模块测量环境温度和湿度,并在数码管上显示出来。树莓派3B+开发板上有40个GPIO接口,使用python的第三方库RPi.GPIO进行对各个引脚的控制。我使用的是GPIO引脚的厂家(BCM)编号进行引脚的控制。因此,对与LED模块和电源模块连接的树莓派的GPIO口,通过RPi...

1.实验目的

  1. 了解温湿度模块的数据读取方式;
  2. 了解4位LED数码管的显示控制方法;
  3. 掌握树莓派编程方法;

2.实验原理

本实验以树莓派开发板作为控制板,通过树莓派的GPIO口连接温湿度模块和4位LED数码管显示模块,通过温湿度控制模块测量环境温度和湿度,并在数码管上显示出来。

树莓派3B+开发板上有40个GPIO接口,使用python的第三方库RPi.GPIO进行对各个引脚的控制。我使用的是GPIO引脚的厂家(BCM)编号进行引脚的控制。因此,对与LED模块和电源模块连接的树莓派的GPIO口,通过RPi.GPIO库的函数对其进行操作即可。

模块一,4位LED数码管模块原理。本实验装置采用2个4位LED模块,每个显示模块有4个LED数码管,采用共阳极的接法,由2片74HC595驱动,需要树莓派的3路GPIO口,根据数码管动态扫描原理进行显示。这个数码管模块的驱动方法如下。4位LED的位地址从左到右分别是0x08、0x04、0x02、0x01,要显示的字模和位地址按字节依次通过DIO口输出到显示模块,依次检查字模和地址字节的每个位,如果是1,则给DIO一个高电平,如果是0,则给DIO一个低电平,每一位DIO数据输出后,要给串行时钟SCLK一个向上的跳变信号(先低电平、再高电平),当字模字节和地址字节都通过DIO输出后,要给接收时钟RCLK一个向上的跳变信号(先低电平、再高电平),那么这个位的数据就输出完成,可以输出下一位的数据。

模块二,温湿度及压强传感器使用I2C总线和树莓派通信,使用前需要现在Raspberry PiConfigur中打开I2C总线开关,然后安装i2c-tools工具,连接好模块后,使用sudo i2cdetect-y 1命令查看设备地址。温湿度采集模块的运行,先要对模块的设备寄存器进行初始化,然后要配置传感器的工作模式,然后根据给定的温湿度寄存器地址,读取计算温湿度所需要的参数数值,根据公式进行温湿度的计算。测试的主要模式,有三种。Sleep Mode:作电流达到uA级别,典型值为0.1uA,最大值为0.3uA,所有测量工作都停止;Normal Mode:正常工作,相关工作间隔时间可以通过寄存器控制;Forced Mode:主控发起一次采集命令,传感器采集一次信号,然后进入Sleep Mode,等待下次唤起。整个测量的流程,包括I2C初始化,串口初始化,传感器初始化。对于传感器初始化,又依次为将数据全部清零,写数据0xB6到地址0xE0;读芯片ID,读地址0xD0;设置测量控制寄存器,写数据0xFF到地址0xF4(测量数据位20Bit,Normal Mode);设置配置寄存器,写数据0x00到地址0xF5;读取补偿值数据;I2C循环读取传感器参数,代入公式计算获得结果。

3.仪器和材料

三、使用仪器、材料

  1. 树莓派3B+;
  2. 1个LED数码管模块;
  3. BME/P280 温湿度数据采集模块
  4. 12V的直流电源;
  5. 电源模块;
  6. 杜邦线若干;

4.实验步骤

(1)建立电路
用杜邦线,将树莓派、电源模块、1个LED模块、温湿度数据采集模块的相应引脚连接起来。具体连线见下面表格。注意这里LED模块的电源接法有两种,实验证明,后者的电源接法能有效降低数码管闪烁的情况。
树莓派GPIO(BCM编号)——第一个4位数码管模块
GPIO 4 ——————————————SCLK
GPIO 5 ——————————————RCLK
GPIO 7 ———————————————DIO

树莓派GPIO(BCM编号)——第二个4位数码管模块
GPIO 17 ——————————————SCLK
GPIO 27 ——————————————RCLK
GPIO 22 ——————————————DIO

树莓派GPIO(BCM编号) ——温湿度数据采集模块
SCL1接口(GPIO 3)————— SCL时钟接口
SDA1接口(GPIO 2)——————SDA数字接口

树莓派GPIO(BCM编号)——————电源模块
GND ————————————————GND

第一个4位数码管模块 ————————电源模块
VCC ————————————————3.3V
GND ————————————————GND

第二个4位数码管模块 ————————电源模块
VCC ————————————————3.3V
GND ————————————————GND

温湿度数据采集模块 ————————电源模块
VCC ————————————————3.3V
GND ————————————————GND

(2)编写LED模块显示程序,能显示给定数字;
(3)编写代码,使得温、湿度数据采集功能可行,此时温、湿度值用print函数输出并观察;
(4)编写代码,编写两个线程,一个用于测量温、湿度,一个用于数码管显示,使得所测温、湿度能直接在数码管上显示出来。
(5)编写TK界面,使得温、湿度能在界面中实时显示出来。
(6)调试和观察结果。

5.代码


'''
#########################################
* Title: 树莓派实验
* Name:  温湿度数据采集实验
* Author: AJ
#########################################
'''
import smbus
import time
import threading
import RPi.GPIO as GPIO
import tkinter as tk

#定义GPIO引脚,连接两个数码管的时钟和数据输入口DIO
SCLK = 4
RCLK = 5
DIO = 7
SCLK2 = 17
RCLK2 = 27
DIO2 = 22
LED_PINS = [4,5,7,17,27,22]
GPIO.setmode(GPIO.BCM) #设置引脚编号为BCM编号方式
for led_pin in LED_PINS: #将这些GPIO引脚设置为输出
    GPIO.setup(led_pin,GPIO.OUT)
LED_Library = [0xC0, 0xF9, 0xA4,0xB0,0x99,0x92,0x82,
               0xF8,0x80,0x90,0x8C,0xBF,0xC6,0xA1,0x86,0x8E,0xbf] #定义字模
LED_Library2 = [0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10] #定义带小数点的字模

address = 0x76 #温湿度采集模块的I2C总线地址
# 温度寄存器地址
Temp_MSB = 0xfa 
Temp_LSB = 0xfb
Temp_XLSB = 0xfc

# 湿度寄存器地址
Hum_MSB = 0xfd
Hum_LSB = 0xfe

bus = smbus.SMBus(1) #在树莓派中,I2C设备位于I2C-1,所以此处的编号为1
#初始化设备寄存器
bus.write_byte_data(address,0xe0,0xb6)
#配置传感器工作模式
bus.write_byte(address,0xd0)
.............
.............
.............
.............
.............
.............
.............
.............
S_Temp = S_Hum = 0


'''
* 函数名称:Show(i_data,area)
* 功能:将i_data表示的数值,显示在相应的4位数码管做上。
* 参数含义:
        i_data:要显示在数码管上的数值
        area: 有两个4位数码管,当area为1时,数值显示在第一个4位数码管上,为2时,显示在第2个上
'''
def Show(i_data,area):
    #提取i_data数值的每一位上的值
    while i_data > 10000:
        i_data = i_data - 10000 #将数值限制在10000以下
    if(i_data >= 1000):
        i_show1 = i_data//1000 #提取千位的值,存在i_show1中
        i_data = i_data - i_show1 * 1000  #减去千位
    else:
        i_show1 = 0
    if(i_data >= 100):
        i_show2 = i_data//100 #提取百位值,存在i_show2中
        i_data -= 100 * i_show2 #减去百位
    else:
        i_show2 = 0
    if(i_data >= 10):
        i_show3 = i_data//10 #提取十位值,存在i_show3中
        i_data -= 10*i_show3 #减去十位
    else:
        i_show3 = 0
    i_show4 = i_data #提取个位值,存在i_show4中
    LED4_Display(i_show1,0x08,area,0) #将千位值写入4位数码管模块的从左往右的第一位,且不要小数点
    LED4_Display(i_show2,0x04,area,1) #将百位值写入4位数码管模块的从左往右的第二位,要小数点
    LED4_Display(i_show3,0x02,area,0) #将十位值写入4位数码管模块的从左往右的第三位,且不要小数点
    LED4_Display(i_show4,0x01,area,0) #将个位值写入4位数码管模块的从左往右的第四位,且不要小数点

'''
* 函数名称:LED4_Display(i_index,hx_location,area,p)
* 功能:将i_index数字对应的数模,输入到相应4位数码管的hx_location个位置上显示出来。
        并且可以确定,该位数字是否需要小数点。
* 参数含义:
        i_index:在该位要显示的数字
        hx_location: 表示4位数码管的某一位
        area: 有两个4位数码管,当area为1时,数值显示在第一个4位数码管上,为2时,显示在第2个上
        p: p为1时,表示该位需要小数点,否则不需要
'''
def LED4_Display(i_index,hx_location,area,p):
    if p==1:#需要小数点,因此从第二个字模集合中读取字模
        LED_OUT(LED_Library2[i_index],area)#将要显示的字模型,输入到指定的4位数码管上
    else:#不需要小数点,因此从第一个字模集合中读取字模
        LED_OUT(LED_Library[i_index],area)
    LED_OUT(hx_location,area) #将位地址,输入到指定的4位数码管上
    if area == 1: #第一个4位数码管模块
        GPIO.output(RCLK,GPIO.LOW)  #字模和地址都通过DIO接口输出后,第一个4位数码管的接收时钟RCLK发出一个跳变信号,表示输出完成
        GPIO.output(RCLK,GPIO.HIGH)
    elif area == 2: #第二个4位数码管模块
        #字模和地址都通过DIO接口输出后,第二个4位数码管的接收时钟RCLK2发出一个跳变信号,表示输出完成
        GPIO.output(RCLK2,GPIO.LOW)
        GPIO.output(RCLK2,GPIO.HIGH) #即先低电平,再高电平


'''
* 函数名称:LED_OUT(X,area)
* 功能:将数据X输入到指定的4位数码管模块上
* 参数含义:
        X: 一个字节的数据X,可以是字模数据,也可以是地址数据
        area: 有两个4位数码管,当area为1时,数值显示在第一个4位数码管上,为2时,显示在第2个上
''' 
def LED_OUT(X,area):
    if area == 1: #输入到第一个4位数码管模块
        for i in range(0,8): #一个字节是八位,循环8次将每一位数据通过DIO口输出到显示模块
            if(X&0x80): #如果X最高位是1
                GPIO.output(DIO,GPIO.HIGH) #则给高电平信号
            else:
                GPIO.output(DIO,GPIO.LOW) #低电平
            GPIO.output(SCLK,GPIO.LOW)
            GPIO.output(SCLK,GPIO.HIGH)
            #每一位数据输出后,都要给串行时钟一个跳变信号,(先低电平,后高电平)
            X<<=1 #X数据向左移一位,即准备读取下一位的数据
    elif area == 2:  #输入到第二个4位数码管模块
        for i in range(0,8):
            if(X&0x80):
                GPIO.output(DIO2,GPIO.HIGH)
            else:
                GPIO.output(DIO2,GPIO.LOW)
            GPIO.output(SCLK2,GPIO.LOW)
            GPIO.output(SCLK2,GPIO.HIGH)
            X<<=1

'''
* 函数名称:show_thread()
* 功能:在两个4位数码管模块显示温度值和湿度值
* 参数含义:
''' 
def show_thread():
    global S_Temp,S_Hum
    while True:
        Show(S_Temp,1)  #在第一个4位数码管模块显示温度值
        Show(S_Hum,2)  #在第二个4位数码管模块显示湿度值

#分别是显示温度值、湿度值
global text1,text2,windows

'''
* 函数名称:refreshText():
* 功能:用于更新窗口中得各个信号值文本,每秒更新一次
* 参数含义:
''' 
def refreshText():
 .............
.............
.............
.............
.............
.............
.............
.............
     #调用after函数,实现循环调用,起到定时更新得功能,1000毫秒更新一次
    windows.after(1000,refreshText)

'''
* 函数名称:tk_thread():
* 功能:窗口显示线程,用于绘制和显示窗口,以及更新窗口中得数值
* 参数含义:
''' 
def tk_thread():
    global text1,text2,text3,windows
    windows = tk.Tk()
    windows.geometry('500x500')  # 规定窗口大小500*500像素
    windows.resizable(False, False)  # 规定窗口不可缩放
    lab1 = tk.Label(windows, text='  ___Current Temperature(C):', height=1, width=15, fg='black')
    lab1.grid(row=0, column=0, ipadx=5, ipady=5) #将组件放到第0行第0列,间距单位5
    text1 = tk.Text(windows,width=15,height=1) #创建text组件
    text1.grid(row=0,column=1,padx=10,pady=10) #将组件放到第0行第1列,间距单位10

    lab2 = tk.Label(windows, text='  ___Current Humidity(%):', height=1, width=15, fg='black')
    lab2.grid(row=1, column=0, ipadx=5, ipady=5) #将组件放到第1行第0列,间距单位5
    text2 = tk.Text(windows,width=15,height=1) #创建text组件
    text2.grid(row=1,column=1,padx=10,pady=10) #将组件放到第1行第1列,间距单位10
    
    windows.after(1000,refreshText) #循环更新文本框中的温度值和湿度值
    windows.mainloop() #窗口循环显示

'''
* 函数名称:TH_thread():
* 功能:温湿度采集线程
* 参数含义:
''' 
def TH_thread():
    global S_Temp,S_Hum #温度和湿度的值,全局变量用于在不同线程中传播
    while True:
        time.sleep(1) #采集速率为1秒采集一次
       .............
.............
.............
.............
.............
.............
.............
.............
        Hum = (H_value1<<8|H_value2)
        #湿度转换公式,各项转换公式见datasheet
        H_var = ((t_fine) - 76800.0)
        H_var = (Hum - ((dig_H4) * 64.0 + (dig_H5) / 16384.0 * H_var)) * ((dig_H2) / 65536.0 * (1.0 + (dig_H6) / 67108864.0 * H_var *  (1.0 + (dig_H3) / 67108864.0 * H_var)))
        H_var = H_var * (1.0 - (dig_H1) * H_var / 524288.0)
        Hum = H_var - 48
        #湿度时百分比值,因此范围在0~100
        if (Hum > 100.0):
            Hum = 100.0
        elif (Hum < 0.0):
            Hum = 0.0
        S_Hum = int(Hum * 100) #用于在线程中分享
        print("Hum:",Hum)
        print("================================")
    

if __name__ == "__main__":

    thread1_read = threading.Thread(target = TH_thread,args = ()) #温湿度读取功能得线程
   .............
.............
.............
.............
.............
.............
.............
.............
    thread3_tk.join()
    #等待上面三个线程结束

完整代码见我的github,别忘了帮我点个star呀,谢谢啦

6.实验结果

(1)编制LED模块显示程序,在代码中变换要显示的数字,观察能否在两个4位数码管上显示,实验结果是能正常显示所变数字;
(3)编写代码,使得温、湿度数据采集模块的温湿度采集功能可行,此时距离值用print函数输出并观察。实验结果是,在一秒采样一次的情况下,能够灵敏测量温度和湿度。
(4)编写代码,编写两个线程,一个用于温湿度数据采集,一个用于数码管显示,使得所测温湿度能直接在数码管上显示出来。实验结果是,在一秒采样一次的情况下,能够灵敏测量温湿度,并实时显示在数码管上,且与实际情况相符合。
(5)编写TK界面,使得温、湿度能在界面中实时显示出来。实验结果是,能够在TK界面中完美实时显示温湿度的值。
(6)通过对温湿度模块进行哈气,改变其周围环境的温度和湿度,能够看到温湿度模块的采集值发生明显变化。当哈气是,温度和湿度都在上升,当停止哈气,温度和湿度又逐渐恢复。
(7)进行测试,测试过程中没有问题。

7.备注

  1. 传感器读出的数值与实际环境中的温湿度相差大吗?如何修正?
    在我的实验中,传感器读出的温度数值与实际环境的温度相差不大。但传感器读出的湿度数值,与实际环境的湿度相差交大。根据我的湿度计显示,与传感器读出的值相差40左右。修正方法有两种。第一种是调参方法,调整相应的补偿值,使得传感器读出的湿度值与实际情况贴近。
    第二种方法是,通过传感器模块读出的湿度值与实际湿度值进行校准,然后统一减去差值即可。

  2. 如何解决传感器读取的时间差与数码管的刷新频率之间的矛盾?
    将传感器每秒读取的数值存储起来,然后数码管不断刷新显示该数据,相当于数码管上的值一秒才可能发生一次变化。即使数据没有发生改变,也必须动态扫描数码管。

本文地址:https://blog.csdn.net/weixin_46185085/article/details/107095987