PyBmini开发板的使用

更新了ds18x20的分辨率设置方法。
最终的工程文件跳转:最终文件

满足下面的需求:

需求:需要做一个温湿度测试仪。
一、使用DHT11检测温度值、湿度值;
二、使用DS18B20检测温度值;
竖着拿单片机时(如图1)在OLED上显示DHT11湿度值、温度值、DS18B20温度值三者的曲线(设计的界面应包括坐标轴、单位,屏幕上应实时显示当前三者分别的测量值);
三、横着拿单片机时(如图2)在OLED上显示DHT11湿度值、温度值、DS18B20温度值三者的柱状图(设计的界面应包括坐标轴、单位,屏幕上应实时显示当前三者分别的测量值);
四、以上“横着”、“竖着”屏幕显示模式切换应该是自动的,不是手动按按钮控制的(提醒使用板载的重力加速度传感器);
五、可以通过按钮控制DHT11湿度值、温度值、DS18B20温度值三者的报警阈值,检测到的数据超过阈值则蜂鸣器报警,对应的LED闪烁(或在屏幕上显示是哪个测量值超过阈值了);
六、报警阈值存储到板载EEPROM芯片中,实现掉电保存。

MicroPython的文档

可移动硬盘中的有几个重要的文件 boot.py、main.py、pybcdc.inf。如果板子正常启动,上电先会运行 boot.py,然后再配置 USB,最后运行 main.py。其中 pybcdc.inf 就是上面所说的要安装的驱动。

import machine
import pyb
pyb.country('US') # ISO 3166-1 Alpha-2 code, eg US, GB, DE, AU
pyb.main('main.py')
#这个是指定运行一下main.py的文件

main函数导入的包

import pyb, _thread,time,onewire,ds18x20,dht #引用 pyb,线程,时间,一线通讯,18b20
from pyb import Pin#引用引脚
from ssd1306 import SSD1306#引用 SS1306
from machine import Pin#引用引脚
from pyb import Accel,Timer,I2C,Pin, ExtInt,Switch

前置部件的学习

OLED的使用

OLED的使用可以调用SSD1306.py这个文件创建对象来实现。
下面是ssd1306.py的代码,可以添加和修改函数。可以之后再来看。

import pyb
import font

# Constants
DISPLAYOFF          = 0xAE
SETCONTRAST         = 0x81
DISPLAYALLON_RESUME = 0xA4
DISPLAYALLON        = 0xA5
NORMALDISPLAY       = 0xA6
INVERTDISPLAY       = 0xA7
DISPLAYON           = 0xAF
SETDISPLAYOFFSET    = 0xD3
SETCOMPINS          = 0xDA
SETVCOMDETECT       = 0xDB
SETDISPLAYCLOCKDIV  = 0xD5
SETPRECHARGE        = 0xD9
SETMULTIPLEX        = 0xA8
SETLOWCOLUMN        = 0x00
SETHIGHCOLUMN       = 0x10
SETSTARTLINE        = 0x40
MEMORYMODE          = 0x20
COLUMNADDR          = 0x21
PAGEADDR            = 0x22
COMSCANINC          = 0xC0
COMSCANDEC          = 0xC8
SEGREMAP            = 0xA0
CHARGEPUMP          = 0x8D
EXTERNALVCC         = 0x10
SWITCHCAPVCC        = 0x20
SETPAGEADDR         = 0xB0
SETCOLADDR_LOW      = 0x00
SETCOLADDR_HIGH     = 0x10
ACTIVATE_SCROLL                      = 0x2F
DEACTIVATE_SCROLL                    = 0x2E
SET_VERTICAL_SCROLL_AREA             = 0xA3
RIGHT_HORIZONTAL_SCROLL              = 0x26
LEFT_HORIZONTAL_SCROLL               = 0x27
VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29
VERTICAL_AND_LEFT_HORIZONTAL_SCROLL  = 0x2A

# I2C devices are accessed through a Device ID. This is a 7-bit
# value but is sometimes expressed left-shifted by 1 as an 8-bit value.
# A pin on SSD1306 allows it to respond to ID 0x3C or 0x3D. The board
# I bought from ebay used a 0-ohm resistor to select between "0x78"
# (0x3c << 1) or "0x7a" (0x3d << 1). The default was set to "0x78"
DEVID = 0x3c

# I2C communication here is either <DEVID> <CTL_CMD> <command byte>
# or <DEVID> <CTL_DAT> <display buffer bytes> <> <> <> <>...
# These two values encode the Co (Continuation) bit as b7 and the
# D/C# (Data/Command Selection) bit as b6.
CTL_CMD = 0x80
CTL_DAT = 0x40

class SSD1306(object):

  def __init__(self, pinout, height=32, external_vcc=True, i2c_devid=DEVID):
  # 设置基本参数
    self.external_vcc = external_vcc
    self.height       = 32 if height == 32 else 64
    self.pages        = int(self.height / 8)
    self.columns      = 128
    self.i2c = pyb.I2C(1)
    self.i2c.init(pyb.I2C.MASTER, baudrate=400000) # 400kHz
    self.devid = i2c_devid

    self.offset = 1
    self.cbuffer = bytearray(2)
    self.cbuffer[0] = CTL_CMD

  def clear(self):
    self.buffer = bytearray(self.offset + self.pages * self.columns)
    if self.offset == 1:
      self.buffer[0] = CTL_DAT

  def write_command(self, command_byte):
    self.cbuffer[1] = command_byte
    self.i2c.send(self.cbuffer, addr=self.devid, timeout=5000)

  def invert_display(self, invert):
    self.write_command(INVERTDISPLAY if invert else NORMALDISPLAY)

  def display(self):
    self.write_command(COLUMNADDR)
    self.write_command(0)
    self.write_command(self.columns - 1)
    self.write_command(PAGEADDR)
    self.write_command(0)
    self.write_command(self.pages - 1)
    if self.offset == 1:
      self.i2c.send(self.buffer, addr=self.devid, timeout=5000)
    else:
      self.dc.high()
      self.spi.send(self.buffer)

  def set_pixel(self, x, y, state):
    index = x + (int(y / 8) * self.columns)
    if state:
      self.buffer[self.offset + index] |= (1 << (y & 7))
    else:
      self.buffer[self.offset + index] &= ~(1 << (y & 7))

  def init_display(self):
    chargepump = 0x10 if self.external_vcc else 0x14
    precharge  = 0x22 if self.external_vcc else 0xf1
    multiplex  = 0x1f if self.height == 32 else 0x3f
    compins    = 0x02 if self.height == 32 else 0x12
    contrast   = 0xff # 0x8f if self.height == 32 else (0x9f if self.external_vcc else 0x9f)
    data = [DISPLAYOFF,
            SETDISPLAYCLOCKDIV, 0x80,
            SETMULTIPLEX, multiplex,
            SETDISPLAYOFFSET, 0x00,
            SETSTARTLINE | 0x00,
            CHARGEPUMP, chargepump,
            MEMORYMODE, 0x00,
            SEGREMAP | 0x10,
            COMSCANDEC,
            SETCOMPINS, compins,
            SETCONTRAST, contrast,
            SETPRECHARGE, precharge,
            SETVCOMDETECT, 0x40,
            DISPLAYALLON_RESUME,
            NORMALDISPLAY,
            DISPLAYON]
    for item in data:
      self.write_command(item)
    self.clear()
    self.display()

  def poweron(self):
    if self.offset == 1:
      pyb.delay(10)
    else:
      self.res.high()
      pyb.delay(1)
      self.res.low()
      pyb.delay(10)
      self.res.high()
      pyb.delay(10)

  def poweroff(self):
    self.write_command(DISPLAYOFF)

  def contrast(self, contrast):
    self.write_command(SETCONTRAST)
    self.write_command(contrast)

  def draw_text(self, x, y, string, size=1, space=1):
    def pixel_x(char_number, char_column, point_row):
      char_offset = x + char_number * size * font.cols + space * char_number
      pixel_offset = char_offset + char_column * size + point_row
      return self.columns - pixel_offset

    def pixel_y(char_row, point_column):
      char_offset = y + char_row * size
      return char_offset + point_column

    def pixel_mask(char, char_column, char_row):
      char_index_offset = ord(char) * font.cols #按照一行是一个
      return font.bytes[char_index_offset + char_column] >> char_row & 0x1

    pixels = (
      (pixel_x(char_number, char_column, point_row),
       pixel_y(char_row, point_column),
       pixel_mask(char, char_column, char_row))
      for char_number, char in enumerate(string)
      for char_column in range(font.cols)
      for char_row in range(font.rows)
      for point_column in range(size)
      for point_row in range(1, size + 1))

    for pixel in pixels:
      self.set_pixel(*pixel)

这个函数库实现了对SSD1306 OLED显示屏的控制,特别是通过I2C接口与显示屏进行通信的功能。SSD1306是一种用于小尺寸显示屏的常见驱动芯片,广泛用于嵌入式系统、微控制器项目和显示简单文本或图形的应用中。该库的核心功能包括初始化显示屏、清屏、设置像素、显示文本和图像,以及控制显示屏的对比度、开启/关闭电源和反转显示内容。

以下是该库的一些主要功能:

  1. 显示初始化 (init_display): 设置显示屏的参数,如对比度、预充电周期、复用率、扫描方向等,并清空屏幕后显示。
  2. 清除屏幕 (clear): 清除显示缓冲区内容,方便后续绘制新的内容。
  3. 设置像素 (set_pixel): 根据指定的坐标设置单个像素的亮灭状态,允许自定义绘制图像或其他内容。
  4. 显示内容 (display): 将缓冲区中的内容发送到OLED显示屏进行显示。
  5. 反转显示 (invert_display): 可以反转屏幕显示,使得亮的像素变暗,暗的像素变亮。
  6. 调节对比度 (contrast): 调整显示屏的亮度,对比度越高显示越亮。
  7. 绘制文本 (draw_text): 可以在指定的坐标位置显示文本,支持不同的字体大小和间距。
  8. 开关屏幕 (poweron/poweroff): 控制屏幕的电源状态,节省电量。
  9. 通过I2C发送命令或数据: 利用write_command方法发送命令控制显示屏的操作,或者发送缓冲区数据实现显示效果。

总结来说,这个库主要用于嵌入式系统中的SSD1306 OLED显示屏控制,适合显示文本、简单的图形、和执行基本的显示屏管理操作。

写字体的小demo

从实际的显示出发。一个OLED的尺寸是64*128.
从 display.draw_text(x,y,“text”)入手。
x[0,128]y[0,64]
实际上,一个字符的高度大概是8个单位长度。可以装下21个字符,长度的占比是6个单位长度。安装上下比较美观的,行分隔比较大的,可以选择12个单位的间距。

display = SSD1306(pinout={'sda': 'PB7', 'scl': 'PB6'}, height=64, external_vcc=False)

    display.poweron()#开启 OLED 电源
    display.init_display()#初始化显示
    display.draw_text(1,1,'YD-pyboard')#显示位置,显示内容,写入显存
    display.draw_text(1,13,'www.vcc-gnd.com') #显示位置,显示内容
    display.draw_text(1,25,'flyfish') #显示位置,显示内容
    display.draw_text(1,37,'1234567890-=,.<>/;') #显示位置,显示内容
    display.draw_text(1,49,'abcdefghijklmnopqrstu') #显示位置,显示内容
    display.draw_text(1,56, 'YD-pyboard')#y最低可显示的位置。

说明,因为底层的python解释器,设置,无法进行text[::-1]这样的字符反转操作。

这个功能可以设置屏幕的亮(白屏)和暗(黑屏)

display.invert_display(True)
 display.invert_display(False)  # 禁用反转显示(恢复正常显示)

我们要绘制坐标轴 ,还有预警这些,需要写一个画线的函数,和画原点的函数。
画线的函数我们分为画直线的轴,还有画曲线的。在实现这个之后,我们认为线应该可以移动,坐标 轴和曲线在buff内可以重绘。

命令控制语句

两个参数
CTL_CMD = 0x80
CTL_DAT = 0x40

这两个命令是SSD1306 OLED控制器芯片的命令集。

CTL_CMD(控制命令)和CTL_DAT(数据命令)是SSD1306
OLED控制器芯片的命令集。它们用于控制OLED的显示模式、设置时钟、调整对比度等操作。

CTL_CMD是一个字节,用于表示要发送的命令。CTL_DAT是一个字节,用于表示要发送的数据。在向SSD1306
OLED控制器发送命令时,需要先发送CTL_CMD,然后发送相应的数据。

例如,要设置SSD1306
OLED的显示模式,需要先发送CTL_CMD,然后发送一个表示显示模式的字节。不同的显示模式对应不同的字节值。

总之,CTL_CMDCTL_DAT是SSD1306
OLED控制器芯片的命令集,用于控制OLED的显示模式、设置时钟、调整对比度等操作。

先了解代码的类的设置

def __init__(self, pinout, height=32, external_vcc=True, i2c_devid=DEVID):
    self.external_vcc = external_vcc
    self.height       = 32 if height == 32 else 64
    self.pages        = int(self.height / 8)
    self.columns      = 128
    self.i2c = pyb.I2C(1)
    self.i2c.init(pyb.I2C.MASTER, baudrate=400000) # 400kHz
    self.devid = i2c_devid

    self.offset = 1#设置偏移量(offset)为1。偏移量用于指定缓冲区(cbuffer)中数据的起始位置。
    self.cbuffer = bytearray(2)#创建一个长度为2的bytearray对象(cbuffer),用于存储控制命令(CTL_CMD)和后续的数据。
    self.cbuffer[0] = CTL_CMD#将控制命令(CTL_CMD)写入cbuffer的第一个字节(索引为0)。

这段代码是用于初始化一个名为SSD1306的类,该类用于控制一个OLED(电容式显示屏)模块。以下是代码的详细解释:

  1. __init__方法:这是一个类的构造函数,用于初始化类的属性和执行一些必要的操作。

  2. pinout:一个表示OLED模块引脚配置的参数。

  3. height:一个表示OLED模块像素高度的参数,默认为32。

  4. external_vcc:一个表示是否使用外部VCC(电源)的参数,默认为True。

  5. i2c_devid:一个表示I2C设备ID的参数,默认为DEVID

  6. self.external_vcc:将external_vcc参数赋值给self.external_vcc属性。

  7. self.height:将height参数赋值给self.height属性。如果height不是32,则将其设置为64。

  8. self.pages:计算OLED模块的页数(即像素高度除以8)。

  9. self.columns:将columns参数赋值给self.columns属性。

  10. self.i2c:创建一个名为self.i2cpyb.I2C对象,用于与OLED模块进行I2C通信。

  11. self.i2c.init(pyb.I2C.MASTER, baudrate=400000):初始化self.i2c对象,设置为主模式,波特率为400kHz。

  12. self.devid:将i2c_devid参数赋值给self.devid属性。

  13. cbuff:是控制缓冲区的意思

总之,这段代码的主要目的是初始化一个SSD1306类的实例,该实例用于控制一个OLED模块。
使用bytearray作为显示缓冲区
每8行像素组成一个页(page),一页有8*128个像素。
显示数据按列优先方式组织。

清空缓冲区

  def clear(self):
    self.buffer = bytearray(self.offset + self.pages * self.columns)
    if self.offset == 1:
      self.buffer[0] = CTL_DAT#写数据标识

写命令的标识

  def write_command(self, command_byte):
    self.cbuffer[1] = command_byte
    self.i2c.send(self.cbuffer, addr=self.devid, timeout=5000)

向一个设备(可能是传感器或执行器)发送命令的函数。函数的实现原理是通过I2C(Inter-IntegratedCircuit)通信协议将命令字节写入设备。
函数的输入参数是一个命令字节(command_byte),用于指定要执行的命令。函数首先将命令字节写入一个内部缓冲区(cbuffer)的第二个字节(索引为1),然后使用i2c库的send方法将缓冲区数据发送给设备。
设备地址(devid)和发送超时时间(timeout)是函数的附加参数,用于指定要通信的设备地址和发送数据时的超时时间。

下面给出命令注释

#define DISPLAYOFF          0xAE  // 关闭显示器(Display Off)
#define SETCONTRAST         0x81  // 设置显示对比度(Set Contrast)
#define DISPLAYALLON_RESUME 0xA4  // 关闭像素关闭显示(显示内容恢复)(Display All On Resume)
#define DISPLAYALLON        0xA5  // 点亮所有像素(Display All On)
#define NORMALDISPLAY       0xA6  // 正常显示模式(Normal Display)
#define INVERTDISPLAY       0xA7  // 反转显示(Invert Display)
#define DISPLAYON           0xAF  // 打开显示器(Display On)
#define SETDISPLAYOFFSET    0xD3  // 设置显示偏移(Set Display Offset)
#define SETCOMPINS          0xDA  // 设置COM引脚硬件配置(Set COM Pins Hardware Configuration)
#define SETVCOMDETECT       0xDB  // 设置VCOMH电平(Set VCOMH Deselect Level)
#define SETDISPLAYCLOCKDIV  0xD5  // 设置显示时钟分频(Set Display Clock Divide Ratio)
#define SETPRECHARGE        0xD9  // 设置预充电周期(Set Precharge Period)
#define SETMULTIPLEX        0xA8  // 设置多路复用率(Set Multiplex Ratio)
#define SETLOWCOLUMN        0x00  // 设置低列地址(Set Low Column Address)
#define SETHIGHCOLUMN       0x10  // 设置高列地址(Set High Column Address)
#define SETSTARTLINE        0x40  // 设置显示起始行(Set Display Start Line)
#define MEMORYMODE          0x20  // 设置内存地址模式(Set Memory Addressing Mode)
#define COLUMNADDR          0x21  // 设置列地址范围(Set Column Address)
#define PAGEADDR            0x22  // 设置页地址范围(Set Page Address)
#define COMSCANINC          0xC0  // 正常扫描COM(Scan COM from 0 to N-1)
#define COMSCANDEC          0xC8  // 反向扫描COM(Scan COM from N-1 to 0)
#define SEGREMAP            0xA0  // 正常段重映射(Set Segment Re-map)
#define CHARGEPUMP          0x8D  // 设置电荷泵(Enable Charge Pump Regulator)
#define EXTERNALVCC         0x10  // 使用外部VCC电源(Use External VCC)
#define SWITCHCAPVCC        0x20  // 使用内部开关电容VCC电源(Use Internal Switch Cap VCC)
#define SETPAGEADDR         0xB0  // 设置页地址(Set Page Start Address)
#define SETCOLADDR_LOW      0x00  // 设置低列地址(Set Low Column Address)
#define SETCOLADDR_HIGH     0x10  // 设置高列地址(Set High Column Address)
#define ACTIVATE_SCROLL     0x2F  // 启动滚动(Activate Scroll)
#define DEACTIVATE_SCROLL   0x2E  // 停止滚动(Deactivate Scroll)
#define SET_VERTICAL_SCROLL_AREA 0xA3  // 设置垂直滚动区域(Set Vertical Scroll Area)
#define RIGHT_HORIZONTAL_SCROLL  0x26  // 向右水平滚动(Right Horizontal Scroll)
#define LEFT_HORIZONTAL_SCROLL   0x27  // 向左水平滚动(Left Horizontal Scroll)
#define VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29  // 垂直加右水平滚动(Vertical and Right Horizontal Scroll)
#define VERTICAL_AND_LEFT_HORIZONTAL_SCROLL  0x2A  // 垂直加左水平滚动(Vertical and Left Horizontal Scroll)

翻转和正常模式

def invert_display(self, invert):
    self.write_command(INVERTDISPLAY if invert else NORMALDISPLAY)

显示(重要功能)

  def display(self):
    self.write_command(COLUMNADDR)#设置列地址范围
    #接着,发送了两个参数,分别是0和self.columns - 1,用于设置列地址的范围。
    self.write_command(0)
    self.write_command(self.columns - 1)
    self.write_command(PAGEADDR)#设置页地址范围
    self.write_command(0)
    self.write_command(self.pages - 1)
    
    if self.offset == 1:
      self.i2c.send(self.buffer, addr=self.devid, timeout=5000)
    else:
      self.dc.high()
      self.spi.send(self.buffer)

更多OLED的使用

参考下面的文章OLED绘制文本

传感器的数据读取

传感器的初始化设置

import pyb, _thread,time,onewire,ds18x20,dht #引用 pyb,线程,时间,一线通讯,18b20
from pyb import Pin#引用引脚
from ssd1306 import SSD1306#引用 SS1306
from machine import Pin#引用引脚
display = SSD1306(pinout={'sda': 'PB7','scl': 'PB6'},height=64,external_vcc=False)#OLED 初始化
ow=onewire.OneWire(Pin('PB0'))#初始化 onewire
ds=ds18x20.DS18X20(ow)#初始化 18B20
dht = dht.DHT11(Pin("PB1"))#dht11 初始化

加速度传感器

记录加速度计

Accel的说明

from pyb import Accel
accel = Accel()
print(accel.x(), accel.y(), accel.z(),accel.tilt())
#经过过滤的输出,前四次样本的采样和。
accel.filtered_xyz()

设备倾斜角的算法

在 MicroPython 的 pyb.Accel 类中,tilt() 方法用于计算设备的倾斜角度,通常是相对于地面的倾斜程度。倾斜角度可以用来判断设备相对于重力的方向。

tilt() 方法的计算

tilt() 方法通常会根据加速度计的 x、y 和 z 分量计算出设备的倾斜角度。以下是一个基本的思路:

  1. 获取加速度值:首先从加速度计中读取 x、y 和 z 的值。

  2. 计算倾斜角度:可以使用反正切函数(atan2)来计算相对于水平面的倾斜角度。公式如下:

    • 计算横向和纵向倾斜角度:
      • 横向倾斜(Roll): roll = atan2(y, z)
      • 纵向倾斜(Pitch): pitch = atan2(x, z)
    • 根据需要将这些角度转换为度数(degrees)。

示例计算代码

下面是一个简单的示例代码,展示如何计算倾斜角度:

from pyb import Accel
import math

accel = Accel()

# 获取加速度值
x = accel.x()
y = accel.y()
z = accel.z()

# 计算倾斜角度
roll = math.atan2(y, z) * (180 / math.pi)  # 转换为度
pitch = math.atan2(x, z) * (180 / math.pi)  # 转换为度

print("Roll:", roll, "degrees")
print("Pitch:", pitch, "degrees")

上述是可能的计算方式之一。

def recordacc(sec):
    time.sleep(sec)
    # 打开文件以写入数据,无法写入文件,没有权限。
    with open('accel_data.txt', 'w') as file:
        for _ in range(100):  # 记录100次数据,可以根据需要调整
            x = accel.x()  # 获取 x 轴加速度
            y = accel.y()  # 获取 y 轴加速度
            z = accel.z()  # 获取 z 轴加速度
            tilt = accel.tilt()  # 获取倾斜角度
            print(accel.x(), accel.y(), accel.z(),accel.tilt())
            file.write("{x}, {y}, {z}, {tilt}/n")
            # 等待一段时间,以便获取新的数据
            time.sleep(0.1)  # 每0.1秒记录一次

因为,这个MicroPython可能无法访问文件系统,所以无法进行文件的读写。
取一些采集数据,观察一下静止的不同姿态的数据是否不同。

from pyb import Accel

accel = Accel()
for i in range(100):
	print(accel.x(), accel.y(), accel.z(),accel.tilt())
	#经过过滤的输出,前三次样本的采样和。
	if(i%4==0)
	print(accel.filtered_xyz()/4)
	time.sleep(0.1)  # 每0.1秒记录一次

我们直接运行不考虑过程的话。
平放在桌面上
1 -1 -21 26
(5, -5, -84)按照数据除以4来统计的话。就是(0,0,20)左右是平放的状态
在这里插入图片描述
直立起来的加速度传感器的数据状态。
-18 -3 -4 26
-20 -3 -4 26
-22 -3 0 26
-21 -2 -1 26
-20 -1 -2 26
-22 -3 -2 26

对于需要的那个状态的加速度传感器的数值是(0,20,0)左右设置检查的函数

abs(y - 20) < 4 

温度测量器材的区别

DHT11 和 DS18B20 都是常用的温度传感器,但它们在性能和精度方面有所区别:

  1. DHT11:

    • 温度测量范围:0°C 到 50°C。
    • 温度精度:±2°C。
    • 湿度测量:支持,测量范围为 20% 到 90% RH,精度为 ±5% RH。
    • 特点:DHT11 是一个低成本、低精度的传感器,适合一些对精度要求不高的环境监测应用。
  2. DS18B20:

    • 温度测量范围:-55°C 到 125°C。
    • 温度精度:±0.5°C 在 -10°C 到 +85°C 的范围内,其他范围可能稍有降低。
    • 湿度测量:不支持,仅用于温度测量。
    • 特点:DS18B20 具有更宽的测量范围和更高的精度,适用于对温度监测精度要求较高的场景。

区别:

  • 测量范围:DS18B20 的温度测量范围更宽,尤其适合低温和高温环境,而 DHT11 的测量范围相对有限。
  • 精度:DS18B20 的温度测量精度更高,适用于需要较高温度精确度的场景。
  • 功能:DHT11 可以同时测量温度和湿度,而 DS18B20 仅测量温度。

所以,如果你关注的是精度或需要监测极端温度,DS18B20 更合适;如果你还需要监测湿度且对温度精度要求不高,DHT11 是更经济的选择。

温湿度传感器(DHT11)

在这里插入图片描述

精度相对较低,响应时间较长,最小采样间隔为1s。受内部恢复时间的影响,不能频繁读取数据。
因此,下面采样的time.sleep时间是1s

dht = dht.DHT11(Pin("PB1"))
dht.measure()
dht.temperature()
dht.humidity()

#使用try except结构

对于DH11这个传感器

DS18X20检测:

类文件

from micropython import const

_CONVERT = const(0x44)
_RD_SCRATCH = const(0xbe)
_WR_SCRATCH = const(0x4e)

class DS18X20:
    def __init__(self, onewire):
        self.ow = onewire
        self.buf = bytearray(9)

    def scan(self):
        return [rom for rom in self.ow.scan() if rom[0] in (0x10, 0x22, 0x28)]

    def convert_temp(self):
        self.ow.reset(True)
        self.ow.writebyte(self.ow.SKIP_ROM)
        self.ow.writebyte(_CONVERT)

    def read_scratch(self, rom):
        self.ow.reset(True)
        self.ow.select_rom(rom)
        self.ow.writebyte(_RD_SCRATCH)
        self.ow.readinto(self.buf)
        #循环冗余码的检查
        if self.ow.crc8(self.buf):
            raise Exception('CRC error')
        return self.buf

    def write_scratch(self, rom, buf):
        self.ow.reset(True)
        self.ow.select_rom(rom)
        self.ow.writebyte(_WR_SCRATCH)
        self.ow.write(buf)

    def read_temp(self, rom):
        buf = self.read_scratch(rom)
        if rom[0] == 0x10:
            if buf[1]:
                t = buf[0] >> 1 | 0x80
                t = -((~t + 1) & 0xff)
            else:
                t = buf[0] >> 1
            return t - 0.25 + (buf[7] - buf[6]) / buf[7]
        else:
            t = buf[1] << 8 | buf[0]
            if t & 0x8000: # sign bit set
                t = -((t ^ 0xffff) + 1)
            return t / 16

说明:
单线通信(OneWire)协议与传感器进行通信。
在这里插入图片描述
链接的GPIO端口是PB0

查看一个GPIO端口的使用情况

from machine import Pin

# 创建 Pin 对象
pin = Pin('PB0', Pin.IN)  # 查询 GPIO0 的状态(输入模式)
# 读取引脚值
print(pin.value())  # 打印当前引脚的高低电平状态

工作流程

初始化 OneWire 和 DS18X20 设备:

from machine import Pin
import ds18x20
ow=onewire.OneWire(Pin('PB0'))
ds=ds18x20.DS18X20(ow)

扫描设备

# 扫描 OneWire 总线,找到所有支持的设备(ROM 码)
roms = ds.scan()
print("Found devices:", roms)

开始温度转换

# 开始温度转换
ds.convert_temp()

# 通常需要等待一段时间让转换完成
import time
time.sleep_ms(750)  # 通常 DS18B20 需要 750ms

读取温度数据

# 读取每个设备的温度
for rom in roms:
    temp = ds.read_temp(rom)
    print("Temperature:", temp, "°C")

查看测试的输出:

>>> ds=ds18x20.DS18X20(ow)# 18B20
>>> roms = ds.scan()
>>> print("Found devices:", roms)
Found devices: [bytearray(b'(\xcbpt\x1e \x01\x93')]

如果有多个设备的时候

Found devices: [bytearray(b'(\xcbpt\x1e \x01\x93'), bytearray(b'(\x8b\xd7\xf1\x04\x00\x03\x1a')]

上述的几个设备初始化,只有ds18x20是属于onewire传送数据,需要使用rom来访问。
在这里插入图片描述

Ds18x20的进一步学习

可自行搜索文档
一个文档
在这里插入图片描述
ds18B20核心是直接温度-数字测量。温度转换可由用户,自定义9,10,11,12,从0.5,0.25,0.125,0.0625的分辨率。上电默认是12位转换精度。上电后工作在低功耗闲置状态下,主设备必须向DS18B20发送温度转换命令[44h]才能开始温度转换。转换后,温度转换的值会保存在暂存存储器的温度寄存器中,并恢复闲置状态。
温度寄存器的格式:
在这里插入图片描述
温度/数据对应关系:
在这里插入图片描述
上电复位时是+85C.
接下来研究一下存储器:
在这里插入图片描述
TH Register (Threshold High):高位温度触发点。(只读)
TL Register (Threshold Low):低位温度触发点。(只读)
温度报警寄存器
Temperature TH和TL:温度的高字节还有低字节。
Configuration Register (Config Reg):配置寄存器,包含分辨率和其他配置信息。

Temperature LSB (T_LSB):温度的低字节。
Reserved:保留字节,通常为 0xFF。
Reserved:保留字节,通常为 0xFF。
Reserved:保留字节,通常为 0xFF。
CRC:循环冗余校验码,用于验证数据的完整性。

使用写暂存寄存器命令[4Eh]才能将数据写入Byte2,3,4中,必须从Byte2中的最低位开始。
可以通过[BEh](读取命令)来检查是否修改了。

配置寄存器修改分辨率

在这里插入图片描述
可以看到有效的数据转换间隔是750ms.如果我们希望减少数据转化的时间,加快访问效率,就需要设置位00.降低分辨率

roms = ds. Scan()#此处只有一个rom
rom = rom[0]
#因为可以写入三个数据,所以要构造三字节的数据
#可以设置新的TH和TL
#也可以设置为原来的
scratch = ds.read_scratch(rom)
TH = scratchpad[2]
TL = scratchpad[3]
# 设置分辨率和其他配置
TH = 0x50  # 高位温度触发点,例如40.0°C
TL = 0x05  # 低位温度触发点,例如2.5°C
RESOLUTION_12_BIT = 0b01111111  # 12位分辨率
RESOLUTION_9_BIT =0b00011111 #0x1f

# 构建要写入的字节序列
buf = bytearray([TH, TL, RESOLUTION_9_BIT ])
ds.write_scratch(rom,buf)

#同理,可以通过读取来确定是否修改成功了
config_register = scratchpad[4]
#resolution_bits = (config_register >> 5) & 0b1
#resolution_bits = (config_register >> 6) & 0b1

线程设置和切换

_thread 库是 Python 中的一个低级线程模块,用于创建和操作线程。它是 threading 模块的底层实现。虽然功能相对简单,但可以用来启动线程、执行并发操作等。

_thread 模块中的主要函数:

  1. _thread.start_new_thread(function, args[, kwargs])
    启动一个新的线程并执行指定的函数。

    • function:在线程中运行的函数。
    • args:传递给函数的参数,必须是元组。
    • kwargs:可选的关键字参数(在某些实现中不一定支持)。

    示例

    import _thread
    import time
    
    def print_numbers(thread_name, delay):
        for i in range(5):
            time.sleep(delay)
            print(f"{thread_name}: {i}")
    
    # 创建两个线程
    _thread.start_new_thread(print_numbers, ("Thread-1", 1))
    _thread.start_new_thread(print_numbers, ("Thread-2", 2))
    
    # 主线程需要延迟,否则子线程可能不会有机会运行
    time.sleep(10)
    
  2. _thread.exit()
    当前线程退出执行,不抛出异常。可以在任何地方调用来安全地退出线程。

    示例

    import _thread
    
    def some_task():
        print("Thread starting")
        _thread.exit()  # 退出线程
        print("This will not be printed")
    
    _thread.start_new_thread(some_task, ())
    
  3. _thread.allocate_lock()
    分配一个新的锁对象,用于线程同步。锁有两个状态:locked(锁定)和 unlocked(未锁定)。
    相关的锁对象方法:

    • lock.acquire(blocking=True):获取锁。如果 blockingTrue,线程会阻塞直到锁被释放。如果为 False,则尝试获取锁,若锁已经被占用,返回 False
    • lock.release():释放锁。

    示例

    import _thread
    import time
    
    lock = _thread.allocate_lock()
    
    def thread_task(name):
        with lock:  # 获取锁
            print(f"{name} acquired lock")
            time.sleep(2)
            print(f"{name} released lock")
    
    # 创建两个线程,它们会争夺同一个锁
    _thread.start_new_thread(thread_task, ("Thread-1",))
    _thread.start_new_thread(thread_task, ("Thread-2",))
    
    time.sleep(5)  # 等待子线程完成
    
  4. _thread.get_ident()
    返回当前线程的标识符(一个整数)。每个线程有一个唯一的标识符,可以用来区分线程。

    示例

    import _thread
    
    def print_thread_id():
        print(f"Thread ID: {_thread.get_ident()}")
    
    _thread.start_new_thread(print_thread_id, ())
    
  5. _thread.exit_thread()
    当前线程退出(和 _thread.exit() 功能相同)。这是一个较老的函数,现在通常推荐使用 _thread.exit() 代替。

  6. _thread.interrupt_main()
    中断主线程(发送一个 KeyboardInterrupt 到主线程)。这可以用来从子线程中中断主线程的执行。

    示例

    import _thread
    import time
    
    def interrupt_main_thread():
        time.sleep(2)
        print("Interrupting main thread")
        _thread.interrupt_main()
    
    _thread.start_new_thread(interrupt_main_thread, ())
    
    try:
        while True:
            print("Main thread running...")
            time.sleep(1)
    except KeyboardInterrupt:
        print("Main thread interrupted")
    
  7. _thread.stack_size([size])
    获取或设置新线程的栈大小(单位:字节)。如果传递 size,则设置新线程的栈大小,并返回旧的栈大小;如果不传递参数,则返回当前的栈大小。不同平台对栈大小有不同的限制。

    示例

    import _thread
    
    # 获取当前的线程栈大小
    current_stack_size = _thread.stack_size()
    print(f"Current stack size: {current_stack_size}")
    

总结

  • 创建线程_thread.start_new_thread()
  • 退出线程_thread.exit()exit_thread()
  • 同步锁_thread.allocate_lock()
  • 获取线程 ID_thread.get_ident()
  • 中断主线程_thread.interrupt_main()

尽管 _thread 提供了基本的线程管理功能,但在实际应用中,推荐使用 threading 模块,因为它提供了更高级的功能(如守护线程、线程池、条件变量等),并且更安全易用。

定时器 Timer

from pyb import Timer#引入定时器
tim = Timer(1, freq=1000)#使用定时器 1,频率是 1000
tim.counter() # 获取当前计数值
tim.freq(0.5) # 0.5 Hz
tim.callback(lambda t: pyb.LED(1).toggle())#设置中断函数

有定时器,可以达到数据的采集的统一,因为我们可以使用中断去取数据,来达到不同频率的仪器,同步采集。
使用全局的数据变量来记录。
另外设置显示函数。

第一个需求(DHT11检测温度值和湿度值):

OLED显示数值

from ssd1306 import SSD1306#引用 SSD1306
import _thread,time,dht#引用线程,时间,dht 库
from machine import Pin#引用 PIN 库

display = SSD1306(pinout={'sda': 'PB7','scl':'PB6'},height=64,external_vcc=False)#显示OLED 初始化
dht = dht.DHT11(Pin("PB1"))#dht11 初始化
def funcA(sec):#线程 A
	time.sleep(sec)#延时
	try:#异常处理
		display.poweron()#OLED 上电
		display.init_display()#OLED 初始化
		display.draw_text(1,1,'DHT11Tem')#OLED 显示内容 1
		display.draw_text(1,13,'DHT11Hum')#OLED 显示内容 2
		while True:
			try:
				dht.measure()#DHT 数据获取
				print("temperature:",dht.temperature())#向 REPL 打印温度
				print("humidity:",dht.humidity())#向 REPL 打印湿度
				display.draw_text(55,1,str(round(dht.temperature(),1)))#OLED 显示温度
				display.draw_text(55,13,str(round(dht.humidity(),1)))#OLED 显示湿度

			except Exception as ex:#异常处理
				print("DHT11 lose")#打印就是异常
			display.display()#OLED 显示
			time.sleep(1)#延时
	except Exception as ex:#异常处理
		print('Unexpected error: {0}'.format(ex))#异常处理函数
if __name__ == '__main__':#程序入口
	_thread.start_new_thread(funcA, (1,))#线程 A
	

第一次实现的代码,后期,会把代码和布局进行整合。

从DHT11读取数据绘制曲线的代码

# 从左下角开始了
def draw_curve_from_sensor( start_x=90, step=1):
    """
    从传感器数据中读取加速度值并绘制曲线。当 x 减少到 0 时,动态更新显示器。
    :param start_x: 曲线起始的 x 坐标,默认 90.给前面留下数值的空间
    :param step: 每次 y 坐标的增量
    """
    # dht = dht.DHT11(Pin("PB1"))#dht11 初始化
    # accel = Accel()  # 初始化加速度传感器
    
    x = start_x
    # prev_y = 32  # 初始 y 值,OLED 显示屏中间(假设屏幕高度为64)

    while x >= 0:
        # 从传感器读取 y 数据
        # accel_y = accel.y()
        try:
            dht.measure()#DHT 数据获取
            display.draw_axes()
            # 将加速度传感器的 y 值映射到显示屏的范围(0 到 height)
            # mapped_y = int((accel_y + 10) * (self.height / 20))  # 将加速度值范围 [-10, 10] 映射到显示屏高度

            # # 清除上一帧的像素
            # self.clear()
            tem1 = dht.temperature()
            hum  = dht.humidity()
            mapped_tem1 = 54-int((tem1 - 5) / (50) * 54)
            mapped_hum = 54-int((hum - 20) / (100-20) * 54)
            # 绘制当前传感器值的点
            display.set_pixel(x, mapped_tem1, 1)
            display.set_pixel(x, mapped_hum, 1)


            # 动态更新显示器
            display.display()

            # 控制绘制速度
            pyb.delay(10)

            # 更新 x 坐标和 y 值
            x -= step

            # 如果 x 坐标到达 0,重置为 98 以形成循环效果
            if x < 0:
                x = start_x
                display.clear()
                #在每次更新函数之后:重新设置坐标的内容
                display.draw_axes()
                display.draw_text(1,13,'Tem')#OLED 温度
                display.draw_text(1,25,'Hum')#OLED 湿度
                display.draw_text(1,37,'tem')#  精细的温度
            # prev_y = mapped_y
        except Exception as ex:#异常处理
                print("DHT11 lose")#打印就是异常
        display.display()

把前四个需求的处理部分进行类组织

我们可以把这个需求实现的东西,用一个类来进行组织。

先贴一个ai生成的搞笑的类图
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

AI画图太抽象了,按照功能来划分。

设置的全局变量

global update_flag_up  # 加更新按钮
global update_flag_down# 减更新按钮
global update_mode     # 模式切换
global mode_num        # 修改数据的模式
global tem1_d		   # 第一个传感器的温度限制
global tem2_d		   # 第二个传感器的温度限制
global hum_d		   # 第一个传感器的湿度限制
global display_mode    # 显示模式 

传感器的初始化

传感器的初始化和数据存储空间的初始化。

ow=onewire.OneWire(Pin('PB0'))#初始化 onewire
ds=ds18x20.DS18X20(ow)#初始化 18B20
dht = dht.DHT11(Pin("PB1"))#dht11 初始化
accel = Accel()

i2c = I2C(1, I2C.MASTER, baudrate=100000)
default=30
b_default=default.to_bytes(1, 'big')
i2c.mem_write(b_default, 80, 0x10)#b'\x1e' 两种输入初值的方式
#睡眠一段时间,使设备正常工作
time.sleep_ms(50)
i2c.mem_write(b_default, 80, 0x20)
time.sleep_ms(50)
i2c.mem_write(b_default, 80, 0x30)

#蜂鸣器的初始化
buzzer = Pin('PA1')
tim = Timer(2, freq=1000)
ch = tim.channel(2, Timer.PWM, pin=buzzer)
#ch.pulse_width_percent(0)

传感器限制的初始化

def init_threshold(default):
    global tem1_d
    global tem2_d
    global hum_d 
    if default != None:
        b_default=default.to_bytes(1, 'big')
        i2c.mem_write(b_default, 80, 0x10)#b'\x1e' 两种输入初值的方式
        #睡眠一段时间,使设备正常工作
        time.sleep_ms(50)
        i2c.mem_write(b_default, 80, 0x20)
        time.sleep_ms(50)
        i2c.mem_write(b_default, 80, 0x30)
    else:
        # 读取EEPROM数据
        data = i2c.mem_read(1, 80, 0x10)
        tem1_d = int.from_bytes(data, 'big')
        data = i2c.mem_read(1, 80, 0x20)
        tem2_d = int.from_bytes(data, 'big')
        data = i2c.mem_read(1, 80, 0x30)
        hum_d = int.from_bytes(data, 'big')
        print("tem1_d:", tem1_d)
        print("tem2_d:", tem2_d)
        print("hum_d:", hum_d)

架构设计

按照程序的功能和结构进行划分:

  • 数据读取函数:用于读取加速度传感器和其他三个传感器的数据。
  • 状态判断函数:用于判断是否切换显示模式,以及是否触发报警。
  • 显示和报警函数:用于显示和报警信息

数据读取函数

    def read_dht(self):
        try:
            self.dht.measure()
            temperature = self.dht.temperature()
            humidity = self.dht.humidity()
            return temperature, humidity
        except Exception as ex:
            print("DHT11 error:", ex)
            return None, None
     def read_ds18b20(self):
        try:
            roms = self.ds.scan()
            self.ds.convert_temp()
            for rom in roms:
                temperature = self.ds.read_temp(rom)
            return temperature
        except Exception as ex:
            print("DS18B20 error:", ex)
            return None
	 def read_accel(self):
	 
	      try:
	          x, y, z = self.accel.x(), self.accel.y(), self.accel.z()
	          tilt = self.accel.tilt()
	          return x, y, z, tilt
	      except Exception as ex:
	          print("Accel error:", ex)
	          return None, None, None, None

状态判断函数

这些函数判断是否需要切换显示模式和是否触发报警。

# 判断是否需要切换显示模式
def check_mode(x, y, z):
     #只对这个(0,20,0)进行判断
	 if abs(y - 20) < 4 :

# 判断是否触发报警
def check_alarm(tem1, tem2, hum):
    #global tem1_d
    #global tem2_d
    #global hum_d 
	#不需要修改,此处只是说明是全局的限制值
	# 初始化标志位
    flag = 0
    
    # 检查每个条件,将对应位置为 1
    if tem1 > tem1_d:
        flag |= 0b100  # 设置第一位为 1
    if tem2 > tem2_d:
        flag |= 0b010  # 设置第二位为 1
    if hum > hum_d:
        flag |= 0b001  # 设置第三位为 1
    return flag  # 返回二进制标志

显示和报警函数

负责显示模式和报警。

# 显示模式
def display_mode(mode, x, y, z, temperature, humidity, temp_ds18b20):
    if mode == "default":
        print(f"Displaying default mode: X={x}, Y={y}, Z={z}")
    else:
        print(f"Displaying alternate mode with additional info")
    # 其他显示内容,如温湿度、DS18B20 温度
    print(f"Temp: {temperature}, Humidity: {humidity}, DS18B20 Temp: {temp_ds18b20}")

def buzzer_alert(num):
	ch.pulse_width_percent(num)

# 报警
def process_flag(flag):
    # 根据 flag 的每一位来控制相应的 LED
    if flag & 0b100:  # 检查第一位,是否需要亮 LED1
        pyb.LED(1).on()
    if flag & 0b010:  # 检查第二位,是否需要亮 LED2
        pyb.LED(2).on()
    if flag & 0b001:  # 检查第三位,是否需要亮 LED3
        pyb.LED(3).on()
    # 统一延时2秒
    buzzer_alert(60)
    time.sleep(2)
    buzzer_alert(0)
    pyb.LED(1).off()
    pyb.LED(2).off()
    pyb.LED(3).off()

定时器回调函数

# 定时器回调函数
def timer_callback(timer):
    # 读取数据
    x, y, z = read_accel_data()
    temperature, humidity, temp_ds18b20 = read_other_sensors()

    # 判断模式和报警
    mode = check_mode(x, y, z)
    alarm_triggered = check_alarm(temperature, humidity, temp_ds18b20)

    # 显示模式
    display_mode(mode, x, y, z, temperature, humidity, temp_ds18b20)

    # 报警
    if alarm_triggered:
        alarm()

# 初始化定时器,设置为 0.5 Hz
tim = Timer(1, freq=0.5)
tim.callback(timer_callback)

在这里插入图片描述

进一步优化

在这里插入图片描述

# 回调 1:快速读取传感器数据并存储在全局变量中
def sensor_data_callback(timer):
    global sensor_data
    sensor_data = read_all_sensors()  # 尽量确保这个函数快速执行
    # 存储数据后立即返回

# 回调 2:状态检查和报警
def check_status_callback(timer):
    global sensor_data, mode
    x, y, z, temperature, humidity, temp_ds18b20 = sensor_data
    mode = check_mode(x, y, z)
    if check_alarm(temperature, humidity, temp_ds18b20):
        alarm()
    # 仅做判断逻辑,避免阻塞

# 主线程:显示模式处理(不在定时器中)
while True:
    x, y, z, temperature, humidity, temp_ds18b20 = sensor_data
    display_mode(mode, x, y, z, temperature, humidity, temp_ds18b20)
    time.sleep(1)  # 显示内容的更新周期可以比数据采集周期慢

第五个需求:按键控制阈值

有三个按键。
一个USR按键和两个KEY1,KEY2。
在这里插入图片描述
第三个电路图的原理
这里是引用

读取usr按键

from pyb import Switch
sw =Switch()
sw.value()

按下按键可以使用回调函数

from pyb import Switch
sw = Switch()
sw.callback(lambda: pyb.LED(1).toggle())
#里面就是一个自定义函数,修改LED1的电位

KEY1(S1),KEY2(S2)如何使用输入功能和中断输入功能
S1对应的是PB9,S2对应的是PB8

from pyb import Pin #引用 PIN 库函数
p_in = Pin('PB9', Pin.IN, Pin.PULL_UP)#设置输入上拉输入状态
p_in.value()#询问引脚的状态

Pin:这是一个类,用于表示微控制器上的一个引脚。
‘PB9’:这是引脚的名称,表示具体的引脚编号。不同的微控制器有不同的引脚命名规则,PB9可能表示B组的第9个引脚。
Pin.IN:这是引脚的一个属性,设置引脚为输入模式。
Pin.PULL_UP:这是引脚的另一个属性,启用内部上拉电阻。上拉电阻的作用是在引脚悬空时提供一个默认的高电平状态。

编写中断程序

from pyb import Pin, ExtInt#应引用 Pin 库,和外部中断库
callback = lambda e: print("PB9 is passed")#编写中断处理程序
ext = ExtInt(Pin('PB9'), ExtInt.IRQ_RISING, Pin.PULL_NONE, callback)#初始化输入

from pyb import Pin, ExtInt
def callback(line):
 print("PB9 is passed")
ext = ExtInt(Pin('PB9'), ExtInt.IRQ_RISING, Pin.PULL_NONE, callback)

Pin用于控制引脚,ExtInt用于处理外部中断。
ext = ExtInt(Pin(‘PB9’), ExtInt.IRQ_RISING, Pin.PULL_NONE, callback):
Pin(‘PB9’):指定使用PB9引脚。
ExtInt.IRQ_RISING:设置中断类型为上升沿触发,即当引脚从低电平变为高电平时触发中断。
Pin.PULL_NONE:不启用内部上拉或下拉电阻。
callback:指定中断触发时调用的回调函数。
也就是说点击之后触发。

eeprom存储

使用24C08来进行读写.
24c08 是一个非遗失 eeprom 存储器器件,采用的 IIC 总线技术。 24c08 在许多试验中都有出现。 24c02 的应用,主要在存储一些掉电后还要保存数据的场合,在上次运行时,保存的数据,在下一次运行时还能够调出。
PB6 连接 SCL,PB7 连接 SDA,这两个通讯线都实际进行了上拉电阻,大部分 I2C 都需要进行上拉处理。
在这里插入图片描述

import pyb
from pyb import Pin,Timer,I2C
from ssd1306 import SSD1306
import _thread,time
from machine import Pin

display = SSD1306(pinout={'sda': 'PB7','scl': 'PB6'},height=64,external_vcc=False)

def funcA(sec):
	time.sleep(sec)
	#这里1表示I2C总线的编号,I2C.MASTER表示设备作为主设备,baudrate=100000设置通信的波特率。
	i2c = I2C(1, I2C.MASTER, baudrate=100000)
	print("scan I2C",i2c.scan())#[60, 76, 80, 81, 82, 83],扫描到的设备
	time.sleep_ms(50)
	print("write 24C08 yd")
	i2c.mem_write('yd', 80, 0x10) # 写入数据,写入设备,写入起始地址
	time.sleep_ms(50)
	print("read 24C08",i2c.mem_read(2, 80, 0x10))#读取两个字节,读取设备
	time.sleep_ms(50)
	ONtimes=i2c.mem_read(1, 80, 0x14)#读取一个特定的地址(0x14),这里假设这个地址存储了一个计数器的值,并将其转换为整数
	x = int.from_bytes(ONtimes,'big')#'big'和'little'是大端和小端存储。有from_bytes,还有to_bytes,x.to_bytes(length, 'big').
	print("ONtimes",x)
	x=x+1
	#重新写入
	b = x.to_bytes(1,'big')
	i2c.mem_write( b, 80, 0x14)
	try:
		display.poweron()
		display.init_display()
		display.draw_text(1,1,'24C08 TEST')
		display.draw_text(1,13,'24C08 times')
		display.draw_text(105,13,str(x-1))

		while True:
			display.display()
			time.sleep_ms(30)
	except Exception as ex:
		print('Unexpected error: {0}'.format(ex))

if __name__ == '__main__':
	_thread.start_new_thread(funcA, (1,))

这些地址通常是7位的,但在某些情况下,它们可能被扩展为8位。在MicroPython中,地址通常以7位的形式表示,最左边的位是读/写位,用于指示接下来的操作是读操作还是写操作。

关于存储的说明:
24C08 是一款由 Microchip Technology 生产的 EEPROM(电可擦除可编程只读存储器),其存储容量为 8K bits,即 1024 字节。
存储容量:8Kbit(即1024字节)。
页大小:8字节。
地址范围:从0x00到0x3FF。
0x00-0x3ff有1024个地址,每位存一个字节。
页地址是整页的,0x00第一页,0x10第二页。
在这个代码中,我们访问非整页地址会受限。
ONtimes=i2c.mem_read(1, 80, 0x14)

说明:本来打算用中断来设置内存的数据,但是中断时,会有堆栈保存,修改内存,这种耗时和修改不被允许。
给出的方案是根据修改标志,设置循环的线程来检查标志,在里面执行修改操作.

from pyb import Accel,Timer,I2C,Switch
#记住在开头声明全局,并进行赋值
global update_flag_up
global update_flag_down
global update_mode
global mode_num 
update_flag_up=False
update_flag_down=False
update_mode=False
mode_num = 0#0,1,2
#三个值代表温度DH1,温度XD2,湿度
default=30
b_default=default.to_bytes(1, 'big')
i2c.mem_write(b_default, 80, 0x10)#b'\x1e' 两种输入初值的方式
i2c.mem_write(b_default, 80, 0x20)
i2c.mem_write(b_default, 80, 0x30)


def callback1(line):
    global update_flag_up
    update_flag_up = True
def callback2(line):
    global update_flag_down
    update_flag_down = True
def callback3(line):
	global update_mode
    update_mode = True 
def detect_sign3(sec):
	time.sleep(sec)
    global update_mode
    global mode_num
	while True:
		if update_mode:
		    # 重置标志
            update_mode = False
            mode_num = (mode_num + 1)%3
			print("mode:", mode_num)
def detect_sign1(sec):
  time.sleep(sec)
  global update_flag_up
  while True:
    if update_flag_up:
            # 重置标志
            update_flag_up = False
            time.sleep_ms(50)
            # 读取EEPROM中的数据
            if 0 ==mode_num: 
                addr=0x10
            if 1 ==mode_num: 
                addr=0x20
            if 2 ==mode_num: 
                addr=0x30
            data = i2c.mem_read(1, 80,addr)
            x = int.from_bytes(data, 'big')
            # 数据加一
            x = x + 1

            # 将新的数据写回EEPROM
            b = x.to_bytes(1, 'big')
            i2c.mem_write(b, 80, addr)
            time.sleep_ms(50)
            # 打印新的数据
            print("Updated value:", x)

        # 避免CPU占用过高,添加短暂的延时
    time.sleep_ms(10)
def detect_sign2(sec):
  time.sleep(sec)
  global update_flag_down
  while True:
    if update_flag_down:
            # 重置标志
            update_flag_down = False
            time.sleep_ms(50)
            # 读取EEPROM中的数据
            if 0 ==mode_num: 
                addr=0x10
            if 1 ==mode_num: 
                addr=0x20
            if 2 ==mode_num: 
                addr=0x30
            data = i2c.mem_read(1, 80,addr)
            x = int.from_bytes(data, 'big')
            x = x -1
            # 将新的数据写回EEPROM
            b = x.to_bytes(1, 'big')
            i2c.mem_write(b, 80, addr)
            time.sleep_ms(50)
            # 打印新的数据
            print("Updated value:", x)

        # 避免CPU占用过高,添加短暂的延时
    time.sleep_ms(10)

def detect_sign3(sec):
    time.sleep(sec)
    global update_mode
    global mode_num
    while True:
        if update_mode:
		    # 重置标志
            update_mode = False
            mode_num = (mode_num + 1)%3
            print("mode:", mode_num)
#主函数调用代码
ext = ExtInt(Pin('PB9'), ExtInt.IRQ_RISING, Pin.PULL_NONE, callback1)
ext = ExtInt(Pin('PB8'), ExtInt.IRQ_RISING, Pin.PULL_NONE, callback2)
sw = Switch()
sw.callback(callback3)
_thread.start_new_thread(detect_sign1, (1,))
_thread.start_new_thread(detect_sign2, (1,))
_thread.start_new_thread(detect_sign3, (1,))

上述的代码会有一些冗余
使用这个代码来进行替换detect_sign1和2函数

def detect_sign(sec):
    time.sleep(sec)
    global update_flag_up, update_flag_down
    global tem1_d, tem2_d, hum_d
    
    while True:
        if update_flag_up or update_flag_down:
            time.sleep_ms(200)
            
            # 确定当前模式对应的地址和变量
            mode_map = {
                0: (0x10, 'tem1_d'),
                1: (0x20, 'tem2_d'),
                2: (0x30, 'hum_d')
            }
            
            if mode_num in mode_map:
                addr, var_name = mode_map[mode_num]
                
                # 根据标志决定是加一还是减一
                delta = 1 if update_flag_up else -1
                
                # 更新对应的全局变量
                if var_name == 'tem1_d':
                    tem1_d += delta
                elif var_name == 'tem2_d':
                    tem2_d += delta
                else:  # var_name == 'hum_d'
                    hum_d += delta
                # 读取EEPROM数据
                data = i2c.mem_read(1, 80, addr)
                x = int.from_bytes(data, 'big') + delta

                # 写回EEPROM
                b = x.to_bytes(1, 'big')
                i2c.mem_write(b, 80, addr)
                
                print("Updated value:", x)
            # 重置标志
            update_flag_up = False
            update_flag_down = False
        time.sleep_ms(10)

蜂鸣器警报和LED

让三个LED,根据模式进行报警

from pyb import LED
#因为模式设置是0,1,2
#我们希望是2,3,4对应报警亮灯

led = LED()
while True:
	led.toggle()
	pyb.delay(500)
	led.toggle()
	pyb.delay(500)

#设置蜂鸣器
buzzer = Pin('PA1') # X1 has TIM2, CH1,
tim = Timer(2, freq=1000) #2 是代表是定时器 2,freq=1000 是设置为 1kHz
ch = tim.channel(2, Timer.PWM, pin=buzzer)#1 代表是通道 2,模式为 PWM,pin=p 引脚上述设置的语句
ch.pulse_width_percent(0)#占空比为 50%
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值