基于STM32+SHT30设计的环境温度与湿度检测系统(IIC模拟时序)

news2024/9/25 23:20:47

一、项目功能介绍

当前介绍基于STM32F103ZCT6芯片设计的环境温度与湿度检测系统设计过程。当前系统通过SHT30温湿度传感器采集环境温度和湿度数据,并通过模拟IIC时序协议将数据传输到STM32芯片上。然后,STM32芯片通过处理这些数据并将它们显示在0.91寸OLED显示屏上,以便用户能够方便地观察环境温度和湿度的变化情况。

系统的主控芯片采用了STM32F103ZCT6,这是一款高性能的32位ARM Cortex-M3微控制器,具有丰富的外设和存储器资源,可满足各种应用的需求。温湿度检测传感器采用了SHT30,这是一款高精度的数字式温湿度传感器,具有快速响应、低功耗、高可靠性等特点。

为了实现数据的显示,系统采用了0.91寸OLED显示屏,驱动芯片是SSD1306,接口是IIC协议。OLED显示屏也是通过模拟IIC时序进行驱动,这种方式具有简单、可靠、低功耗等优点。

(1)开发板连接SHT30实物图

image-20230609113818437

(2)OLED显示屏

image-20230609114023918

image-20230609114008368

image-20230609113953194

image-20230609113858711

(3)测量的温度湿度串口打印

image-20230609113933518

二、设计思路

2.1 系统硬件设计

主控芯片采用STM32F103ZCT6,该芯片具有72MHz主频,具有丰富的外设资源,包括多个定时器、多个串口、多个I2C接口等。温湿度传感器采用IIC接口的SHT30,该传感器具有高精度、低功耗、数字输出等特点,可提供温度和湿度数据。OLED显示屏采用0.91寸OLED显示屏,驱动芯片是SSD1306,接口也是是IIC协议。

2.2 系统软件设计

系统软件设计采用STM32CubeMX和Keil MDK-ARM工具进行开发。

实现步骤:

(1)使用STM32CubeMX进行芯片引脚配置和初始化代码生成。

(2)编写SHT30温湿度传感器的IIC通信驱动程序。

(3)编写SSD1306 OLED显示屏的IIC通信驱动程序。

(4)编写温湿度检测程序,通过SHT30传感器读取温度和湿度数据,并将数据显示在OLED显示屏上。

(5)编写主程序,将以上各个程序整合在一起,并进行系统初始化和数据处理。

2.3 系统实现

(1)系统硬件实现

系统硬件实现包括主控板、SHT30传感器模块和OLED显示屏模块。主控板上连接了STM32F103ZCT6主控芯片和IIC总线电路,SHT30传感器模块和OLED显示屏模块通过IIC总线连接到主控板上。

(2)系统软件实现

系统软件实现主要包括SHT30传感器的IIC通信驱动程序、SSD1306 OLED显示屏的IIC通信驱动程序、温湿度检测程序和主程序。其中,SHT30传感器的IIC通信驱动程序和SSD1306 OLED显示屏的IIC通信驱动程序都是基于STM32的硬件IIC接口实现的,温湿度检测程序通过SHT30传感器读取温度和湿度数据,并将数据显示在OLED显示屏上。主程序将以上各个程序整合在一起,并进行系统初始化和数据处理。

三、代码实现

3.1 主程序代码

以下是基于STM32设计的环境温度与湿度检测系统的主函数main.c的代码实现:

#include "stm32f10x.h"
#include "systick.h"
#include "sht30.h"
#include "i2c.h"
#include "oled.h"

#define OLED_ADDR 0x78
#define SHT30_ADDR 0x44

uint8_t oled_buf[128][8];

void show_temp_humi(float temp, float humi) {
    char str[20];
    int temp_int = (int)(temp * 10);
    int humi_int = (int)(humi * 10);
    sprintf(str, "Temp: %d.%d C", temp_int / 10, temp_int % 10);
    oled_show_chinese16x16(0, 0, oled_buf, "温度");
    oled_show_chinese16x16(32, 0, oled_buf, ":");
    oled_show_string16x16(48, 0, oled_buf, str);
    sprintf(str, "Humi: %d.%d %%", humi_int / 10, humi_int % 10);
    oled_show_chinese16x16(0, 2, oled_buf, "湿度");
    oled_show_chinese16x16(32, 2, oled_buf, ":");
    oled_show_string16x16(48, 2, oled_buf, str);
    oled_refresh(0, 7, oled_buf);
}

int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

    i2c_init();
    systick_init(72);
    sht30_init(SHT30_ADDR);

    oled_init();

    while(1)
    {
        float temp, humi;
        sht30_read_temp_humi(&temp, &humi);
        show_temp_humi(temp, humi);
        delay_ms(1000);
    }
}

代码中主要实现了以下功能:

(1)初始化IIC总线、SHT30传感器和OLED显示屏。

(2)定时读取SHT30传感器的温度和湿度数据。

(3)将温度和湿度显示在OLED显示屏上。

代码中使用了systick.h、sht30.h、i2c.h和oled.h等库文件,需要将这些文件添加到工程中。其中oled.h文件提供了显示中文、字符串和刷新缓冲区等接口,可以在OLED显示屏上显示信息。具体代码实现可以参考oled.c文件。

测试时,需要将OLED显示屏和SHT30传感器按照对应的引脚连接好,并将代码烧录到STM32F103ZCT6芯片中。如果一切正常,OLED显示屏上就会不断地显示当前温度和湿度值。

3.2 SHT30驱动代码

以下是SHT30的驱动代码:

sht30.h:

#ifndef __SHT30_H
#define __SHT30_H

#include "stm32f10x.h"

void sht30_init(uint8_t addr);
void sht30_read_temp_humi(float *temp, float *humi);

#endif /* __SHT30_H */

sht30.c:

#include "sht30.h"
#include "i2c.h"

#define SHT30_CMD_HIGH 0x2C
#define SHT30_CMD_MIDDLE 0x06

void sht30_init(uint8_t addr)
{
    uint8_t cmd[] = { 0x22, 0x36 };
    i2c_write_data(addr, cmd, sizeof(cmd));
}

void sht30_read_temp_humi(float *temp, float *humi)
{
    uint8_t buf[6];
    uint16_t temp_raw, humi_raw;

    i2c_start();
    i2c_write_byte(SHT30_ADDR << 1);
    if (!i2c_wait_ack()) {
        return;
    }
    i2c_write_byte(SHT30_CMD_HIGH);
    i2c_wait_ack();
    i2c_write_byte(SHT30_CMD_MIDDLE);
    i2c_wait_ack();
    i2c_stop();

    delay_ms(10);

    i2c_start();
    i2c_write_byte((SHT30_ADDR << 1) | 0x01);
    if (!i2c_wait_ack()) {
        return;
    }
    for (int i = 0; i < 6; ++i) {
        buf[i] = i2c_read_byte(i == 5 ? 0 : 1);
    }
    i2c_stop();

    humi_raw = (buf[0] << 8) | buf[1];
    temp_raw = (buf[3] << 8) | buf[4];

    *humi = 100.0f * ((float)humi_raw / 65535.0f);
    *temp = -45.0f + 175.0f * ((float)temp_raw / 65535.0f);
}

代码中定义了SHT30_CMD_HIGH和SHT30_CMD_MIDDLE两个命令,用于启动温湿度转换。在sht30_init函数中,发送初始化命令;在sht30_read_temp_humi函数中,先发送读取命令,等待10毫秒后读取温度和湿度的原始值。最后根据公式计算出实际的温度和湿度值,并将它们保存到temp和humi指针所指向的内存中。

代码中调用了i2c_write_data、i2c_start、i2c_write_byte、i2c_wait_ack、i2c_read_byte和i2c_stop等IIC相关函数,这些函数的实现可以看i2c.c文件。在使用SHT30传感器之前,需要初始化IIC总线和SHT30传感器。

3.3 OLED显示屏驱动代码

以下是OLED显示屏相关代码:

oled.h:

#ifndef __OLED_H
#define __OLED_H

#include "stm32f10x.h"

void oled_init(void);
void oled_show_chinese16x16(uint8_t x, uint8_t y, uint8_t (*buf)[8], const char *str);
void oled_show_string16x16(uint8_t x, uint8_t y, uint8_t (*buf)[8], const char *str);
void oled_refresh(uint8_t page_start, uint8_t page_end, uint8_t (*buf)[8]);

#endif /* __OLED_H */

oled.c:

#include "oled.h"
#include <string.h>

#define OLED_WIDTH 128
#define OLED_HEIGHT 64

static void oled_write_cmd(uint8_t cmd)
{
    uint8_t tx_buf[2];
    tx_buf[0] = 0x00;
    tx_buf[1] = cmd;
    i2c_write_data(OLED_ADDR, tx_buf, sizeof(tx_buf));
}

static void oled_write_data(uint8_t data)
{
    uint8_t tx_buf[2];
    tx_buf[0] = 0x40;
    tx_buf[1] = data;
    i2c_write_data(OLED_ADDR, tx_buf, sizeof(tx_buf));
}

static void oled_set_pos(uint8_t x, uint8_t y)
{
    oled_write_cmd(0xb0 + y);
    oled_write_cmd(((x & 0xf0) >> 4) | 0x10);
    oled_write_cmd(x & 0x0f);
}

void oled_init(void)
{
    oled_write_cmd(0xAE); //Display Off
    oled_write_cmd(0x00); //Set Lower Column Address
    oled_write_cmd(0x10); //Set Higher Column Address
    oled_write_cmd(0x40); //Set Display Start Line
    oled_write_cmd(0xB0); //Set Page Address
    oled_write_cmd(0x81); //Contrast Control
    oled_write_cmd(0xFF); //128 Segments
    oled_write_cmd(0xA1); //Set Segment Re-map
    oled_write_cmd(0xA6); //Normal Display
    oled_write_cmd(0xA8); //Multiplex Ratio
    oled_write_cmd(0x3F); //Duty = 1/64
    oled_write_cmd(0xC8); //Com Scan Direction
    oled_write_cmd(0xD3); //Set Display Offset
    oled_write_cmd(0x00);
    oled_write_cmd(0xD5); //Set Display Clock Divide Ratio/Oscillator Frequency
    oled_write_cmd(0x80);
    oled_write_cmd(0xD9); //Set Pre-charge Period
    oled_write_cmd(0xF1);
    oled_write_cmd(0xDA); //Set COM Pins
    oled_write_cmd(0x12);
    oled_write_cmd(0xDB); //Set VCOMH Deselect Level
    oled_write_cmd(0x40);
    oled_write_cmd(0xAF); //Display On

    memset(oled_buf, 0, sizeof(oled_buf));
}

void oled_show_chinese16x16(uint8_t x, uint8_t y, uint8_t (*buf)[8], const char *str)
{
    uint16_t offset = (uint16_t)(str[0] - 0x80) * 32 + (uint16_t)(str[1] - 0x80) * 2;
    const uint8_t *font_data = &font_16x16[offset];

    for (int i = 0; i < 16; ++i) {
        for (int j = 0; j < 8; ++j) {
            uint8_t byte = font_data[i * 2 + j / 8];
            uint8_t bit = (byte >> (7 - j % 8)) & 0x01;
            buf[y + i][x + j] = bit ? 0xff : 0x00;
        }
    }
}

void oled_show_string16x16(uint8_t x, uint8_t y, uint8_t (*buf)[8], const char *str)
{
    while (*str != '\0') {
        oled_show_chinese16x16(x, y, buf, str);
        x += 16;
        str += 2;
    }
}

void oled_refresh(uint8_t page_start, uint8_t page_end, uint8_t (*buf)[8])
{
    for (int i = page_start; i <= page_end; ++i) {
        oled_set_pos(0, i);
        for (int j = 0; j < OLED_WIDTH; ++j) {
            oled_write_data(buf[i][j]);
        }
    }
}

代码中定义了OLED_WIDTH和OLED_HEIGHT两个常量,表示OLED显示屏的宽度和高度。在oled_init函数中,发送初始化命令,将OLED显示屏设置为正常显示模式。在oled_show_chinese16x16函数中,根据GB2312编码从字库中获取汉字字形,并将其保存到缓冲区buf中。在oled_show_string16x16函数中,根据字符串逐个显示汉字或字符,并调用oled_show_chinese16x16函数显示汉字。在oled_refresh函数中,设置页地址和列地址,并将缓冲区buf中的数据写入到OLED显示屏上。

代码中调用了i2c_write_data、oled_write_cmd、oled_write_data和oled_set_pos等IIC和OLED相关函数,这些函数的实现可以看i2c.c文件。

3.4 IIC模拟时序代码(SHT30)

i2c.h:

#ifndef __I2C_H
#define __I2C_H

#include "stm32f10x.h"

void i2c_init(void);
uint8_t i2c_write_data(uint8_t addr, uint8_t *data, uint8_t len);
uint8_t i2c_read_data(uint8_t addr, uint8_t *data, uint8_t len);

#endif /* __I2C_H */

i2c.c:

#include "i2c.h"

#define I2C_SCL_PIN GPIO_Pin_6
#define I2C_SDA_PIN GPIO_Pin_7
#define I2C_SCL_PORT GPIOB
#define I2C_SDA_PORT GPIOB

static void i2c_delay(void)
{
    volatile int i = 7;
    while (i) { --i; }
}

static void i2c_start(void)
{
    GPIO_SetBits(I2C_SCL_PORT, I2C_SCL_PIN);
    GPIO_SetBits(I2C_SDA_PORT, I2C_SDA_PIN);
    i2c_delay();
    GPIO_ResetBits(I2C_SDA_PORT, I2C_SDA_PIN);
    i2c_delay();
    GPIO_ResetBits(I2C_SCL_PORT, I2C_SCL_PIN);
    i2c_delay();
}

static void i2c_stop(void)
{
    GPIO_ResetBits(I2C_SDA_PORT, I2C_SDA_PIN);
    GPIO_SetBits(I2C_SCL_PORT, I2C_SCL_PIN);
    i2c_delay();
    GPIO_SetBits(I2C_SDA_PORT, I2C_SDA_PIN);
    i2c_delay();
}

static uint8_t i2c_write_byte(uint8_t byte)
{
    uint8_t ack_bit = 0;
    for (int i = 0; i < 8; ++i) {
        if ((byte & 0x80) == 0x80) {
            GPIO_SetBits(I2C_SDA_PORT, I2C_SDA_PIN);
        } else {
            GPIO_ResetBits(I2C_SDA_PORT, I2C_SDA_PIN);
        }
        byte <<= 1;
        i2c_delay();
        GPIO_SetBits(I2C_SCL_PORT, I2C_SCL_PIN);
        i2c_delay();
        GPIO_ResetBits(I2C_SCL_PORT, I2C_SCL_PIN);
        i2c_delay();
    }

    GPIO_SetBits(I2C_SDA_PORT, I2C_SDA_PIN);
    i2c_delay();
    GPIO_SetBits(I2C_SCL_PORT, I2C_SCL_PIN);
    i2c_delay();
    if (GPIO_ReadInputDataBit(I2C_SDA_PORT, I2C_SDA_PIN)) {
        ack_bit = 1;
    }
    GPIO_ResetBits(I2C_SCL_PORT, I2C_SCL_PIN);
    i2c_delay();

    return ack_bit;
}

static uint8_t i2c_read_byte(uint8_t ack)
{
    uint8_t ret = 0;
    GPIO_SetBits(I2C_SDA_PORT, I2C_SDA_PIN);
    for (int i = 0; i < 8; ++i) {
        ret <<= 1;
        GPIO_SetBits(I2C_SCL_PORT, I2C_SCL_PIN);
        i2c_delay();
        if (GPIO_ReadInputDataBit(I2C_SDA_PORT, I2C_SDA_PIN)) {
            ret |= 0x01;
        }
        GPIO_ResetBits(I2C_SCL_PORT, I2C_SCL_PIN);
        i2c_delay();
    }

    if (ack) {
        GPIO_ResetBits(I2C_SDA_PORT, I2C_SDA_PIN);
    } else {
        GPIO_SetBits(I2C_SDA_PORT, I2C_SDA_PIN);
    }
    i2c_delay();
    GPIO_SetBits(I2C_SCL_PORT, I2C_SCL_PIN);
    i2c_delay();
    GPIO_ResetBits(I2C_SCL_PORT, I2C_SCL_PIN);
    i2c_delay();

    return ret;
}

void i2c_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStruct.GPIO_Pin = I2C_SCL_PIN | I2C_SDA_PIN;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(I2C_SCL_PORT, &GPIO_InitStruct);

    GPIO_SetBits(I2C_SCL_PORT, I2C_SCL_PIN);
    GPIO_SetBits(I2C_SDA_PORT, I2C_SDA_PIN);
}

uint8_t i2c_write_data(uint8_t addr, uint8_t *data, uint8_t len)
{
    i2c_start();
    if (i2c_write_byte(addr << 1) == 1) {
        i2c_stop();
        return 1;
    }
    for (int i = 0; i < len; ++i) {
        if (i2c_write_byte(data[i]) == 1) {
            i2c_stop();
            return 1;
        }
    }
    i2c_stop();

    return 0;
}

uint8_t i2c_read_data(uint8_t addr, uint8_t *data, uint8_t len)
{
    i2c_start();
    if (i2c_write_byte(addr << 1) == 1) {
        i2c_stop();
        return 1;
    }
    for (int i = 0; i < len; ++i) {
        data[i] = i2c_read_byte((i == len - 1) ? 1 : 0);
    }
    i2c_stop();

    return 0;
}

上面的代码是SHT30的IIC模拟时序代码,利用GPIO模拟SCL和SDA信号线。

在i2c_init函数中,初始化SCL和SDA引脚为开漏输出模式。在i2c_start函数中,发送起始位。在i2c_stop函数中,发送停止位。在i2c_write_byte函数中,按位写入字节并接收应答位。在i2c_read_byte函数中,按位读取字节并发送应答位。在i2c_write_data函数中,先发送起始位,然后发送设备地址和写方向,再发送数据,最后发送停止位。在i2c_read_data函数中,先发送起始位,然后发送设备地址和读方向,接着按字节读取数据,最后发送停止位。

3.5 OLED显示屏完整代码(包含IIC时序)

下面是使用模拟IIC时序驱动OLED显示屏的完整代码:

(在OLED驱动代码中,根据OLED的数据手册进行初始化和写入命令/数据。)

oled.h:

#ifndef __OLED_H
#define __OLED_H

#include "stm32f10x.h"

void oled_init(void);
void oled_clear(void);
void oled_display_on(void);
void oled_display_off(void);
void oled_draw_point(uint8_t x, uint8_t y, uint8_t mode);
void oled_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode);
void oled_draw_rectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode);
void oled_draw_circle(int8_t x, int8_t y, uint8_t r, uint8_t mode);
void oled_show_char(uint8_t x, uint8_t y, uint8_t chr, uint8_t size, uint8_t mode);
void oled_show_string(uint8_t x, uint8_t y, const char *str, uint8_t size, uint8_t mode);
void oled_show_digit(uint8_t x, uint8_t y, uint8_t n, uint8_t size, uint8_t mode);

#endif /* __OLED_H */

oled.c:

#include "oled.h"
#include "i2c.h"

#define OLED_WIDTH 128
#define OLED_HEIGHT 64

#define OLED_ADDRESS 0x78

#define OLED_CMD_MODE 0x00
#define OLED_DATA_MODE 0x40

static uint8_t oled_buffer[OLED_WIDTH * OLED_HEIGHT / 8];

static void oled_write_cmd(uint8_t cmd)
{
    uint8_t data[2] = {OLED_CMD_MODE, cmd};
    i2c_write_data(OLED_ADDRESS, data, 2);
}

static void oled_write_data(uint8_t *data, uint16_t len)
{
    uint8_t buffer[17];
    buffer[0] = OLED_DATA_MODE;
    for (int i = 0; i < len; ++i) {
        buffer[i + 1] = data[i];
    }
    i2c_write_data(OLED_ADDRESS, buffer, len + 1);
}

static void oled_set_pos(uint8_t x, uint8_t y)
{
    oled_write_cmd(0xb0 + y);
    oled_write_cmd(((x & 0xf0) >> 4) | 0x10);
    oled_write_cmd((x & 0x0f) | 0x01);
}

void oled_init(void)
{
    i2c_init();

    oled_write_cmd(0xAE); //display off
    oled_write_cmd(0x20); //Set Memory Addressing Mode   
    oled_write_cmd(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
    oled_write_cmd(0xb0); //Set Page Start Address for Page Addressing Mode,0-7
    oled_write_cmd(0xc8); //Set COM Output Scan Direction
    oled_write_cmd(0x00); //---set low column address
    oled_write_cmd(0x10); //---set high column address
    oled_write_cmd(0x40); //--set start line address
    oled_write_cmd(0x81); //--set contrast control register
    oled_write_cmd(0xff);
    oled_write_cmd(0xa1); //--set segment re-map 0 to 127
    oled_write_cmd(0xa6); //--set normal display
    oled_write_cmd(0xa8); //--set multiplex ratio(1 to 64)
    oled_write_cmd(0x3f); //
    oled_write_cmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
    oled_write_cmd(0xd3); //-set display offset
    oled_write_cmd(0x00); //-not offset

    oled_write_cmd(0xd5); //--set display clock divide ratio/oscillator frequency
    oled_write_cmd(0xf0); //--set divide ratio

    oled_write_cmd(0xd9); //--set pre-charge period
    oled_write_cmd(0x22); //

    oled_write_cmd(0xda); //--set com pins hardware configuration
    oled_write_cmd(0x12);

    oled_write_cmd(0xdb); //--set vcomh
    oled_write_cmd(0x20); //0x20,0.77xVcc

    oled_write_cmd(0x8d); //--set DC-DC enable
    oled_write_cmd(0x14); //
    oled_write_cmd(0xaf); //--turn on oled panel

    oled_clear();
}

void oled_clear(void)
{
    for (int i = 0; i < sizeof(oled_buffer); ++i) {
        oled_buffer[i] = 0x00;
    }
    for (int i = 0; i < OLED_HEIGHT / 8; ++i) {
        oled_set_pos(0, i);
        oled_write_data(oled_buffer + OLED_WIDTH * i, OLED_WIDTH);
    }
}

void oled_display_on(void)
{
    oled_write_cmd(0xAF);
}

void oled_display_off(void)
{
    oled_write_cmd(0xAE);
}

void oled_draw_point(uint8_t x, uint8_t y, uint8_t mode)
{
    if ((x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) {
        return;
    }

    if (mode) {
        oled_buffer[x + (y / 8) * OLED_WIDTH] |= (1 << (y % 8));
    } else {
        oled_buffer[x + (y / 8) * OLED_WIDTH] &= ~(1 << (y % 8));
    }

    oled_set_pos(x, y / 8);
    oled_write_data(&oled_buffer[x + (y / 8) * OLED_WIDTH], 1);
}

void oled_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode)
{
    int dx, dy, sx, sy, err, e2;

    dx = abs((int)x2 - (int)x1);
    dy = abs((int)y2 - (int)y1);

    if (x1 < x2) {
        sx = 1;
    } else {
        sx = -1;
    }

    if (y1 < y2) {
        sy = 1;
    } else {
        sy = -1;
    }

    err = dx - dy;

    while (1) {
        oled_draw_point(x1, y1, mode);
        if ((x1 == x2) && (y1 == y2)) {
            break;
        }
        e2 = 2 * err;
        if (e2 > -dy) {
            err = err - dy;
            x1 = x1 + sx;
        }
        if (e2 < dx) {
            err = err + dx;
            y1 = y1 + sy;
        }
    }
}

void oled_draw_rectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode)
{
    oled_draw_line(x1, y1, x2, y1, mode);
    oled_draw_line(x1, y1, x1, y2, mode);
    oled_draw_line(x1, y2, x2, y2, mode);
    oled_draw_line(x2, y1, x2, y2, mode);
}

void oled_draw_circle(int8_t x, int8_t y, uint8_t r, uint8_t mode)
{
    int a, b, num;

    a = 0;
    b = r;
    while (2 * b * b >= r * r) {
        oled_draw_point(x + a, y - b, mode);
        oled_draw_point(x - a, y - b, mode);
        oled_draw_point(x - a, y + b, mode);
        oled_draw_point(x + a, y + b, mode);

        oled_draw_point(x + b, y + a, mode);
        oled_draw_point(x + b, y - a, mode);
        oled_draw_point(x - b, y - a, mode);
        oled_draw_point(x - b, y + a, mode);

        a++;
        num = -((int)a * a + (int)b * b - (int)r * r);
        if (num > 0) {
            b--;
            a--;
        }
    }
}

void oled_show_char(uint8_t x, uint8_t y, uint8_t chr, uint8_t size, uint8_t mode)
{
    uint8_t font_size = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2);
    uint8_t font[font_size];

    for (int i = 0; i < font_size; ++i) {
        #ifdef OLED_USE_FONT_8x16
        font[i] = font_8x16[(chr - ' ') * font_size + i];
        #else
        font[i] = font_6x8[(chr - ' ') * font_size + i];
        #endif
    }

    for (int i = 0; i < size / 2; ++i) {
        for (int j = 0; j < size / 8; ++j) {
            for (int k = 0; k < 8; ++k) {
                if (font[j + i * (size / 8)] & (1 << k)) {
                    oled_draw_point(x + j * 8 + k, y + i * 8, mode);
                }
            }
        }
    }
}

void oled_show_string(uint8_t x, uint8_t y, const char *str, uint8_t size, uint8_t mode)
{
    while (*str) {
        oled_show_char(x, y, *str, size, mode);
        x += size / 2;
        str++;
    }
}

void oled_show_digit(uint8_t x, uint8_t y, uint8_t n, uint8_t size, uint8_t mode)
{
    char str[2];
    str[0] = n + '0';
    str[1] = '\0';
    oled_show_string(x, y, str, size, mode);
}

上面代码里oled_init函数用于初始化OLED,包括打开I2C接口、依次发送多条命令以设置OLED参数。oled_clear函数用于清除OLED屏幕上的所有显示内容。oled_display_on和oled_display_off函数用于控制OLED的开关。oled_draw_point函数用于绘制一个像素点。oled_draw_line函数用于绘制一条直线。oled_draw_rectangle函数用于绘制一个矩形。oled_draw_circle函数用于绘制一个圆形。oled_show_char函数用于绘制一个ASCII字符。oled_show_string函数可以显示一个字符串。oled_show_digit函数可以显示一个数字。

四、总结

本项目是基于STM32F103ZCT6芯片设计的环境温度与湿度检测系统。系统通过SHT30温湿度传感器采集环境温度和湿度数据,并通过模拟IIC时序协议将数据传输到STM32芯片上。然后,STM32芯片通过处理这些数据并将它们显示在0.91寸OLED显示屏上,以便用户能够方便地观察环境温度和湿度的变化情况。

在本项目中,选择了STM32F103ZCT6作为主控芯片。该芯片具有高性能、低功耗、丰富的外设和存储器资源等特点,非常适合用于嵌入式系统设计。然后,选择了SHT30温湿度传感器作为环境温度和湿度的检测器。该传感器具有高精度、快速响应、低功耗等特点,可以准确地测量环境温度和湿度。

为了实现数据的显示,采用了0.91寸OLED显示屏,驱动芯片是SSD1306,接口是IIC协议。OLED显示屏也是通过模拟IIC时序进行驱动,这种方式具有简单、可靠、低功耗等优点。

在软件设计方面,使用了Keil MDK作为开发工具,并使用STM32CubeMX进行芯片初始化和外设配置。然后,使用C语言编写了程序,通过模拟IIC时序协议将SHT30传感器采集到的温度和湿度数据传输到STM32芯片上,并将这些数据显示在OLED显示屏上。同时还添加了温度和湿度的校准、数据的存储和读取等功能。

在系统实现方面,进行了硬件设计、软件开发、系统调试和测试等工作。通过不断的优化和调试,最终实现了一个功能稳定、性能优良的环境温度与湿度检测系统。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/738522.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序和微信公众号之前的关系

微信小程序和公众号是相互独立的&#xff0c;是2个不同的东西 在公众号上关联小程序后&#xff0c;可以在公众号的底部菜单栏点击直接进入小程序。2者的用户可以互通&#xff0c;其他就没别的

【C++】STL——string类对象的修改操作、string类对象修改函数的介绍和使用、push_back、append、operator+

文章目录 1.string类对象的修改操作&#xff08;1&#xff09;push_back&#xff08;尾插单个字符&#xff09;&#xff08;2&#xff09;append&#xff08;尾插字符串&#xff09;&#xff08;3&#xff09;operator&#xff08;尾插字符串str&#xff09;&#xff08;4&…

linux基础 - SHELL1

目录 一、基础 1.test测试语句 2.逻辑运算 3.类型判断 4.数值判断 5.字符串比较 6.计算表达式 二、shell数组 1.单行定义 2.多行定义 3.单元素定义 4.查看元素 5.基于内容找索引 6.增删改 三、shell高级 1.重定向 2.其他符号 3.信息符号 4.if语句 5.else …

unity --- DoTween插件安装与使用

目录 一.简介 1.1 插件下载 1.2 官方文档 二.导入和设置 三.示例代码 1 循环动画&#xff1a;SetLoops(-1 , loopType ) 2. 动画循环次数&#xff1a;SetLoops( count ) 3. 动画事件监听与回调&#xff1a;OnStart \ OnComplete 4. 动画延迟回调&#xff1a;SetDelay …

Java8特性:函数式接口与Stream流

1、 函数式接口 &#xff08;1&#xff09;四大函数式接口 &#xff08;2&#xff09;代码01 //void accept(T t);消费型接口&#xff0c;有参数&#xff0c;无返回 Consumer<String> consumer t->{System.out.println(t); }; consumer.accept("a"); //…

DAY42:动态规划(二)斐波那契数列+爬楼梯+最小花费爬楼梯

文章目录 509.斐波那契数列思路&#xff1a;动规五步确定dp数组和数组下标含义递推公式DP数组初始化遍历顺序打印DP数组 完整版debug测试 空间复杂度优化版优化思路 70.爬楼梯思路DP数组的含义以及下标含义递推公式DP数组初始化遍历顺序打印DP数组 完整版debug测试 空间复杂度优…

10.24UEC++、事件

1. 类内部声明事件类型&#xff1a; 生成actor 绑定到事件上 实现&#xff1a; 移除&#xff1a;

使用docker安装redis并使用redis deskTop manager连接

使用docker安装redis 提前准备环境 linux、 docker环境 # 使用命令查看docker环境是否正常docker images(任何一个docker命令就可以&#xff0c;不一定必须这个) 下载redis镜像 # 使用命令下载镜像文件docker pull redis 确认镜像 # 使用命令查看镜像docker images使用redis d…

功能测试的测试流程是什么样的?

概述 本流程是描述软件功能自动化测试过程中的步骤、内容与方法&#xff0c;明确各阶段的职责、活动与产出物。 流程活动图 活动说明 测试计划&#xff08;可选&#xff09; 与以前的测试计划过程一致&#xff0c;只是在原来的测试计划中&#xff0c;添加对项目实施自动化测试…

Qt窗体全屏与复原,子窗体全屏与复原

QT QWidget窗体全屏时&#xff0c;全屏与最大化的区别时最大化有标题框全屏没有框&#xff0c;可以使用自带的函数&#xff1a; showFullScreen(); 复原时也有专门的函数&#xff1a; showNormal(); 如果我想在点击最大化按钮时&#xff0c;让窗体全屏。这个时候最好有线程…

学好工作汇报,你离升职加薪也就不远了

在职场中&#xff0c;工作汇报是展示你的价值和成果的重要环节。无论你是想要升职还是加薪&#xff0c;一个出色的工作汇报能够给你带来巨大的竞争优势。然而&#xff0c;许多人对于如何进行有效的工作汇报感到困惑。在本文中&#xff0c;我们将向您介绍一些关键技巧和策略&…

路径规划算法:基于骑手优化优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于骑手优化优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于骑手优化优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化…

Linux学习之vim在光标所在处写入内容,保存退出

vim insertTest使用vim打开一个叫做insertTest新文件。 输入命令之后&#xff0c;按下Enter&#xff08;回车键&#xff09;&#xff0c;就可以进入下边的操作页面。 按i在当前光标位置处&#xff0c;就可以插入内容了。 接下来写入内容跟记事本里边是差不多的。 按一下…

【Spring】Spring基础知识 Java开发必看

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前专攻C/C、Python、Java等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL、蓝桥杯&am…

vue拼接html中onclick的触发方式,模版字符串拼接点击事件在vue项目中不生效问题

模版字符串拼接点击事件在vue项目中不生效问题 下面的点击事件没有任何效果&#xff0c;但是如果换成onclick绑定事件则会提示没有该方法。主要原因是&#xff1a; 模版字符串中拼接的html片段中的方法调不到vue中this.methods里的东西&#xff0c;因为methods里的代码是编译…

物体识别(香蕉)

✨✨✨ 感谢优秀的你打开了小白的文章 “希望在看文章的你今天又进步了一点点&#xff0c;生活更加美好&#xff01;”&#x1f308;&#x1f308;&#x1f308; 目录 1.数据集下载 2.数据集导入 3.结果 1.数据集下载 包含所有图像和CSV标签文件的香蕉检测数据集可以直接从互…

day1-二分查找

二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], target 9 输出: 4 解…

单片机基于stm32单片机的数字温度计设计_kaic

摘 要 古往今来,陶瓷在我们的生活中一直都是不可或缺的物品,而随着当今社会经济的快速发展,人们对于这些高档陶瓷产品的使用性能和产品质量上的要求也愈加严格。那么在陶瓷品的生产过程中,想要提高陶瓷品的品质和合格率,能够随时监测温度的温度计是必不可少的。 本课题的研究是…

什么是IP地址定位技术

IP地址定位是一种技术手段&#xff0c;可以通过获取设备的IP地址精确定位设备的地理位置。它在现代社会的许多方面都有广泛的应用&#xff0c;包括网络安全、广告投放市场调研等。 在互联网时代&#xff0c;人们越来越依赖于网络而IP地址作为网络连接的基石扮演着重要的角色。…

什么软件支持快速批量处理大量视频素材呢

在视频剪辑的过程中&#xff0c;有时候为了满足视频时长的需求&#xff0c;或者为了实现视频与音频的同步操作&#xff0c;我们可能需要对视频的播放速度进行调整。如果需要处理大量的视频文件&#xff0c;传统的手动一个个调整的方法就显得有点落后了&#xff0c;今天小编要与…