STM32 + SHT20 温湿度测试 TFT 显示方案

一、系统架构设计

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    STM32 + SHT20 + TFT 系统                │
├─────────────────────────────────────────────────────────────┤
│  SHT20传感器  │  STM32F103  │  TFT显示屏   │  用户界面    │
│              │              │              │              │
│  • 温度测量  │  • I2C通信   │  • 320x240   │  • 数值显示  │
│  • 湿度测量  │  • 数据处理  │  • 16位色深  │  • 曲线图表  │
│  • 精度±0.3℃ │  • 算法滤波  │  • SPI接口   │  • 状态指示  │
│  • 精度±2%RH │  • 定时采集  │  • 触摸控制  │  • 数据记录  │
└─────────────────────────────────────────────────────────────┘

二、硬件连接设计

2.1 硬件连接图

复制代码
STM32F103C8T6          SHT20传感器          2.4寸 TFT LCD
─────────────────────────────────────────────────────────────
PB6 (I2C1_SCL) ──────> SCL (第4脚)          VCC ──────────> 3.3V
PB7 (I2C1_SDA) ──────> SDA (第3脚)          GND ──────────> GND
3.3V ───────────────> VDD (第1脚)          CS  ──────────> PB12
GND ────────────────> GND (第2脚)          RST ──────────> PB13
                                  SCK ──────────> PB14
                                  MISO ─────────> PB15 (可选)
                                  MOSI ─────────> PB15
                                  DC  ──────────> PB11
                                  BL  ──────────> PB10 (背光)

2.2 电源设计

复制代码
电源方案:
• SHT20: 3.3V ±0.1V,电流约1.5mA
• TFT: 3.3V,背光电流约80mA
• 总电流: < 100mA,可用USB供电
• 建议在VDD和GND间加100nF去耦电容

三、完整源码实现

3.1 SHT20 驱动头文件 (sht20.h)

c 复制代码
#ifndef __SHT20_H
#define __SHT20_H

#include "stm32f10x.h"
#include <stdint.h>
#include <stdbool.h>
#include <math.h>

// SHT20 I2C地址
#define SHT20_ADDR_WRITE    0x80    // 10000000
#define SHT20_ADDR_READ     0x81    // 10000001

// SHT20 命令定义
#define SHT20_CMD_TRIGGER_TEMP_HOLD   0xE3  // 温度测量,保持主机
#define SHT20_CMD_TRIGGER_HUMI_HOLD   0xE5  // 湿度测量,保持主机
#define SHT20_CMD_TRIGGER_TEMP_NOHOLD 0xF3  // 温度测量,不保持主机
#define SHT20_CMD_TRIGGER_HUMI_NOHOLD 0xF5  // 湿度测量,不保持主机
#define SHT20_CMD_WRITE_USER_REG       0xE6  // 写用户寄存器
#define SHT20_CMD_READ_USER_REG        0xE7  // 读用户寄存器
#define SHT20_CMD_SOFT_RESET           0xFE  // 软件复位

// 用户寄存器位定义
#define SHT20_USER_REG_RESOLUTION_12_14 0x00  // 温度12位,湿度14位
#define SHT20_USER_REG_RESOLUTION_8_12  0x01  // 温度8位,湿度12位
#define SHT20_USER_REG_RESOLUTION_10_13 0x80  // 温度10位,湿度13位
#define SHT20_USER_REG_RESOLUTION_11_11 0x81  // 温度11位,湿度11位
#define SHT20_USER_REG_BATTERY_STATUS   0x40  // 电池状态位
#define SHT20_USER_REG_HEATER_ON        0x04  // 加热器开启
#define SHT20_USER_REG_HEATER_OFF       0x00  // 加热器关闭

// 测量分辨率
typedef enum {
    RESOLUTION_12_14 = 0,  // 温度12位(0.04°C),湿度14位(0.01%RH)
    RESOLUTION_8_12,       // 温度8位(0.5°C),湿度12位(0.04%RH)
    RESOLUTION_10_13,      // 温度10位(0.1°C),湿度13位(0.02%RH)
    RESOLUTION_11_11       // 温度11位(0.2°C),湿度11位(0.1%RH)
} Resolution_t;

// 温湿度数据结构体
typedef struct {
    float temperature;      // 温度值 (°C)
    float humidity;        // 湿度值 (%RH)
    uint8_t battery_low;   // 电池低电压标志
    uint32_t timestamp;    // 时间戳
    bool valid;            // 数据有效性
} SHT20_Data_t;

// 校准参数结构体
typedef struct {
    float temp_offset;     // 温度偏移校准
    float humi_offset;     // 湿度偏移校准
    float temp_scale;      // 温度比例校准
    float humi_scale;      // 湿度比例校准
} SHT20_Calibration_t;

// 函数声明
bool SHT20_Init(void);
bool SHT20_SoftReset(void);
bool SHT20_ReadUserReg(uint8_t *reg_value);
bool SHT20_WriteUserReg(uint8_t reg_value);
bool SHT20_SetResolution(Resolution_t resolution);
bool SHT20_ReadTemperature(float *temperature);
bool SHT20_ReadHumidity(float *humidity);
bool SHT20_ReadTempAndHumi(SHT20_Data_t *data);
void SHT20_SetCalibration(SHT20_Calibration_t *cal);
float SHT20_GetCompensatedTemp(float raw_temp);
float SHT20_GetCompensatedHumi(float raw_humi);

#endif /* __SHT20_H */

3.2 SHT20 驱动核心 (sht20.c)

c 复制代码
#include "sht20.h"
#include "i2c.h"
#include "delay.h"

static SHT20_Calibration_t sht20_cal = {0};

// I2C底层函数
static bool I2C_WriteByte(uint8_t addr, uint8_t data) {
    return I2C_WriteByte_ToSlave(addr, data);
}

static bool I2C_ReadByte(uint8_t addr, uint8_t *data) {
    return I2C_ReadByte_FromSlave(addr, data);
}

static bool I2C_ReadBytes(uint8_t addr, uint8_t *data, uint8_t len) {
    return I2C_ReadBytes_FromSlave(addr, data, len);
}

// 初始化SHT20
bool SHT20_Init(void) {
    uint8_t user_reg;
    
    // 软件复位
    if (!SHT20_SoftReset()) {
        return false;
    }
    
    Delay_ms(50);  // 等待复位完成
    
    // 读取用户寄存器
    if (!SHT20_ReadUserReg(&user_reg)) {
        return false;
    }
    
    // 设置分辨率为12位温度,14位湿度
    if (!SHT20_SetResolution(RESOLUTION_12_14)) {
        return false;
    }
    
    // 关闭加热器
    user_reg &= ~SHT20_USER_REG_HEATER_ON;
    if (!SHT20_WriteUserReg(user_reg)) {
        return false;
    }
    
    return true;
}

// 软件复位
bool SHT20_SoftReset(void) {
    return I2C_WriteByte(SHT20_ADDR_WRITE, SHT20_CMD_SOFT_RESET);
}

// 读取用户寄存器
bool SHT20_ReadUserReg(uint8_t *reg_value) {
    if (!I2C_WriteByte(SHT20_ADDR_WRITE, SHT20_CMD_READ_USER_REG)) {
        return false;
    }
    
    return I2C_ReadByte(SHT20_ADDR_READ, reg_value);
}

// 写用户寄存器
bool SHT20_WriteUserReg(uint8_t reg_value) {
    uint8_t data[2] = {SHT20_CMD_WRITE_USER_REG, reg_value};
    
    return I2C_WriteByte(SHT20_ADDR_WRITE, data[0]) &&
           I2C_WriteByte(SHT20_ADDR_WRITE, data[1]);
}

// 设置测量分辨率
bool SHT20_SetResolution(Resolution_t resolution) {
    uint8_t user_reg;
    
    if (!SHT20_ReadUserReg(&user_reg)) {
        return false;
    }
    
    // 清除分辨率位
    user_reg &= ~0x81;
    
    // 设置新的分辨率
    switch(resolution) {
        case RESOLUTION_12_14:
            user_reg |= SHT20_USER_REG_RESOLUTION_12_14;
            break;
        case RESOLUTION_8_12:
            user_reg |= SHT20_USER_REG_RESOLUTION_8_12;
            break;
        case RESOLUTION_10_13:
            user_reg |= SHT20_USER_REG_RESOLUTION_10_13;
            break;
        case RESOLUTION_11_11:
            user_reg |= SHT20_USER_REG_RESOLUTION_11_11;
            break;
    }
    
    return SHT20_WriteUserReg(user_reg);
}

// 读取温度
bool SHT20_ReadTemperature(float *temperature) {
    uint8_t data[3];
    
    // 发送温度测量命令
    if (!I2C_WriteByte(SHT20_ADDR_WRITE, SHT20_CMD_TRIGGER_TEMP_NOHOLD)) {
        return false;
    }
    
    // 等待测量完成(最大85ms)
    Delay_ms(100);
    
    // 读取温度数据
    if (!I2C_ReadBytes(SHT20_ADDR_READ, data, 3)) {
        return false;
    }
    
    // 检查CRC(可选)
    // if (!CheckCRC(data, 2, data[2])) return false;
    
    // 转换温度值
    uint16_t temp_raw = ((uint16_t)data[0] << 8) | data[1];
    temp_raw &= ~0x0003;  // 清除状态位
    
    *temperature = -46.85 + 175.72 * (float)temp_raw / 65536.0;
    
    return true;
}

// 读取湿度
bool SHT20_ReadHumidity(float *humidity) {
    uint8_t data[3];
    
    // 发送湿度测量命令
    if (!I2C_WriteByte(SHT20_ADDR_WRITE, SHT20_CMD_TRIGGER_HUMI_NOHOLD)) {
        return false;
    }
    
    // 等待测量完成(最大29ms)
    Delay_ms(50);
    
    // 读取湿度数据
    if (!I2C_ReadBytes(SHT20_ADDR_READ, data, 3)) {
        return false;
    }
    
    // 转换湿度值
    uint16_t humi_raw = ((uint16_t)data[0] << 8) | data[1];
    humi_raw &= ~0x0003;  // 清除状态位
    
    *humidity = -6.0 + 125.0 * (float)humi_raw / 65536.0;
    
    // 限制湿度范围
    if (*humidity < 0.0) *humidity = 0.0;
    if (*humidity > 100.0) *humidity = 100.0;
    
    return true;
}

// 同时读取温度和湿度
bool SHT20_ReadTempAndHumi(SHT20_Data_t *data) {
    if (data == NULL) return false;
    
    // 读取温度
    if (!SHT20_ReadTemperature(&data->temperature)) {
        data->valid = false;
        return false;
    }
    
    // 读取湿度
    if (!SHT20_ReadHumidity(&data->humidity)) {
        data->valid = false;
        return false;
    }
    
    // 应用校准参数
    data->temperature = SHT20_GetCompensatedTemp(data->temperature);
    data->humidity = SHT20_GetCompensatedHumi(data->humidity);
    
    // 读取电池状态
    uint8_t user_reg;
    if (SHT20_ReadUserReg(&user_reg)) {
        data->battery_low = (user_reg & SHT20_USER_REG_BATTERY_STATUS) ? 1 : 0;
    }
    
    data->timestamp = Get_SystemTime();
    data->valid = true;
    
    return true;
}

// 设置校准参数
void SHT20_SetCalibration(SHT20_Calibration_t *cal) {
    if (cal != NULL) {
        sht20_cal = *cal;
    }
}

// 温度补偿
float SHT20_GetCompensatedTemp(float raw_temp) {
    return (raw_temp + sht20_cal.temp_offset) * sht20_cal.temp_scale;
}

// 湿度补偿
float SHT20_GetCompensatedHumi(float raw_humi) {
    return (raw_humi + sht20_cal.humi_offset) * sht20_cal.humi_scale;
}

3.3 TFT 显示屏驱动 (tft_lcd.c)

c 复制代码
#include "tft_lcd.h"
#include "spi.h"
#include "delay.h"

// TFT引脚定义
#define TFT_CS_PIN      GPIO_Pin_12
#define TFT_RST_PIN     GPIO_Pin_13
#define TFT_DC_PIN      GPIO_Pin_11
#define TFT_BL_PIN      GPIO_Pin_10
#define TFT_PORT        GPIOB

// 颜色定义
uint16_t Color_Table[16] = {
    BLACK,   NAVY,    DARKGREEN, DARKCYAN, MAROON, PURPLE,  OLIVE,   LIGHTGREY,
    DARKGREY,YELLOW,  GREEN,     CYAN,     RED,    MAGENTA, BLUE,    WHITE
};

// TFT命令
#define TFT_CMD_SWRESET   0x01
#define TFT_CMD_SLPOUT    0x11
#define TFT_CMD_DISPOFF   0x28
#define TFT_CMD_DISPON    0x29
#define TFT_CMD_CASET     0x2A
#define TFT_CMD_PASET     0x2B
#define TFT_CMD_RAMWR     0x2C
#define TFT_CMD_MADCTL    0x36
#define TFT_CMD_PIXFMT    0x3A

// GPIO控制
static inline void TFT_CS_LOW(void) { GPIO_ResetBits(TFT_PORT, TFT_CS_PIN); }
static inline void TFT_CS_HIGH(void) { GPIO_SetBits(TFT_PORT, TFT_CS_PIN); }
static inline void TFT_DC_LOW(void) { GPIO_ResetBits(TFT_PORT, TFT_DC_PIN); }
static inline void TFT_DC_HIGH(void) { GPIO_SetBits(TFT_PORT, TFT_DC_PIN); }
static inline void TFT_RST_LOW(void) { GPIO_ResetBits(TFT_PORT, TFT_RST_PIN); }
static inline void TFT_RST_HIGH(void) { GPIO_SetBits(TFT_PORT, TFT_RST_PIN); }
static inline void TFT_BL_ON(void) { GPIO_SetBits(TFT_PORT, TFT_BL_PIN); }
static inline void TFT_BL_OFF(void) { GPIO_ResetBits(TFT_PORT, TFT_BL_PIN); }

// 写命令
static void TFT_WriteCmd(uint8_t cmd) {
    TFT_DC_LOW();
    TFT_CS_LOW();
    SPI_WriteByte(cmd);
    TFT_CS_HIGH();
}

// 写数据
static void TFT_WriteData(uint8_t data) {
    TFT_DC_HIGH();
    TFT_CS_LOW();
    SPI_WriteByte(data);
    TFT_CS_HIGH();
}

static void TFT_WriteData16(uint16_t data) {
    TFT_DC_HIGH();
    TFT_CS_LOW();
    SPI_WriteByte(data >> 8);
    SPI_WriteByte(data & 0xFF);
    TFT_CS_HIGH();
}

// 设置窗口
static void TFT_SetWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
    TFT_WriteCmd(TFT_CMD_CASET);
    TFT_WriteData16(x0);
    TFT_WriteData16(x1);
    
    TFT_WriteCmd(TFT_CMD_PASET);
    TFT_WriteData16(y0);
    TFT_WriteData16(y1);
    
    TFT_WriteCmd(TFT_CMD_RAMWR);
}

// 画点
void TFT_DrawPoint(uint16_t x, uint16_t y, uint16_t color) {
    TFT_SetWindow(x, y, x, y);
    TFT_WriteData16(color);
}

// 填充矩形
void TFT_FillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) {
    uint32_t i;
    
    TFT_SetWindow(x, y, x + w - 1, y + h - 1);
    
    TFT_DC_HIGH();
    TFT_CS_LOW();
    
    for (i = 0; i < (uint32_t)w * h; i++) {
        SPI_WriteByte(color >> 8);
        SPI_WriteByte(color & 0xFF);
    }
    
    TFT_CS_HIGH();
}

// 初始化TFT
void TFT_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能GPIOB时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    // 配置GPIO
    GPIO_InitStructure.GPIO_Pin = TFT_CS_PIN | TFT_RST_PIN | TFT_DC_PIN | TFT_BL_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(TFT_PORT, &GPIO_InitStructure);
    
    // 初始化SPI
    SPI_Init();
    
    // 复位TFT
    TFT_RST_LOW();
    Delay_ms(100);
    TFT_RST_HIGH();
    Delay_ms(120);
    
    // 初始化序列
    TFT_WriteCmd(TFT_CMD_SWRESET);
    Delay_ms(120);
    
    TFT_WriteCmd(TFT_CMD_SLPOUT);
    Delay_ms(120);
    
    TFT_WriteCmd(TFT_CMD_PIXFMT);
    TFT_WriteData(0x55);  // 16位色
    
    TFT_WriteCmd(TFT_CMD_MADCTL);
    TFT_WriteData(0x08);  // 竖屏
    
    TFT_WriteCmd(TFT_CMD_DISPON);
    Delay_ms(120);
    
    // 开启背光
    TFT_BL_ON();
    
    // 清屏
    TFT_FillRect(0, 0, TFT_WIDTH, TFT_HEIGHT, BLACK);
}

// 显示字符
void TFT_ShowChar(uint16_t x, uint16_t y, uint8_t chr, uint16_t color, uint16_t bg_color) {
    uint8_t temp, t1;
    uint16_t y0 = y;
    
    chr = chr - ' ';  // 得到偏移后的值
    
    for (t1 = 0; t1 < 12; t1++) {
        temp = asc2_1206[chr][t1];
        for (uint8_t t = 0; t < 6; t++) {
            if (temp & 0x01) {
                TFT_DrawPoint(x + t, y, color);
            } else {
                TFT_DrawPoint(x + t, y, bg_color);
            }
            temp >>= 1;
        }
        y++;
        if ((y - y0) == 12) {
            y = y0;
            x += 6;
        }
    }
}

// 显示字符串
void TFT_ShowString(uint16_t x, uint16_t y, char *str, uint16_t color, uint16_t bg_color) {
    while (*str) {
        TFT_ShowChar(x, y, *str, color, bg_color);
        x += 6;
        str++;
    }
}

// 显示数字
void TFT_ShowNum(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint16_t color, uint16_t bg_color) {
    uint8_t t, temp;
    uint8_t enshow = 0;
    
    for (t = 0; t < len; t++) {
        temp = (num / (uint32_t)pow(10, len - t - 1)) % 10;
        if (enshow == 0 && t < (len - 1)) {
            if (temp == 0) {
                TFT_ShowChar(x + t * 6, y, ' ', color, bg_color);
                continue;
            } else {
                enshow = 1;
            }
        }
        TFT_ShowChar(x + t * 6, y, temp + '0', color, bg_color);
    }
}

// 显示浮点数
void TFT_ShowFloat(uint16_t x, uint16_t y, float num, uint8_t precision, uint16_t color, uint16_t bg_color) {
    char buffer[20];
    sprintf(buffer, "%.*f", precision, num);
    TFT_ShowString(x, y, buffer, color, bg_color);
}

// 画圆
void TFT_DrawCircle(uint16_t x0, uint16_t y0, uint8_t r, uint16_t color) {
    int16_t a, b;
    int16_t di;
    
    a = 0;
    b = r;
    di = 3 - (r << 1);
    
    while (a <= b) {
        TFT_DrawPoint(x0 + a, y0 - b, color);
        TFT_DrawPoint(x0 + b, y0 - a, color);
        TFT_DrawPoint(x0 + b, y0 + a, color);
        TFT_DrawPoint(x0 + a, y0 + b, color);
        TFT_DrawPoint(x0 - a, y0 + b, color);
        TFT_DrawPoint(x0 - b, y0 + a, color);
        TFT_DrawPoint(x0 - b, y0 - a, color);
        TFT_DrawPoint(x0 - a, y0 - b, color);
        
        if (di < 0) {
            di += (a << 2) + 6;
        } else {
            di += ((a - b) << 2) + 10;
            b--;
        }
        a++;
    }
}

3.4 主程序 (main.c)

c 复制代码
#include "stm32f10x.h"
#include "sht20.h"
#include "tft_lcd.h"
#include "usart.h"
#include "delay.h"
#include "led.h"

// 系统参数
#define UPDATE_INTERVAL  2000    // 更新间隔 (ms)
#define TEMP_HISTORY_LEN 60      // 温度历史长度

// 全局变量
SHT20_Data_t env_data;
float temp_history[TEMP_HISTORY_LEN];
uint8_t history_index = 0;

// UI界面元素
typedef struct {
    uint16_t x, y, w, h;
    uint16_t bg_color;
    uint16_t border_color;
    char title[32];
} UI_Box_t;

UI_Box_t temp_box = {10, 30, 140, 80, DARKBLUE, WHITE, "Temperature"};
UI_Box_t humi_box = {170, 30, 140, 80, DARKGREEN, WHITE, "Humidity"};
UI_Box_t status_box = {10, 130, 300, 60, BLACK, WHITE, "System Status"};

// 初始化系统
void System_Init(void) {
    SystemClock_Init();
    Delay_Init();
    USART_Init(115200);
    LED_Init();
    
    printf("STM32 + SHT20 Temperature & Humidity Monitor\r\n");
    printf("Initializing...\r\n");
    
    // 初始化TFT
    TFT_Init();
    printf("TFT LCD Initialized\r\n");
    
    // 初始化SHT20
    if (SHT20_Init()) {
        printf("SHT20 Sensor Initialized\r\n");
        LED_On(LED0);
    } else {
        printf("SHT20 Sensor Init Failed!\r\n");
        TFT_ShowString(10, 200, "SHT20 Error!", RED, BLACK);
        while(1);
    }
    
    // 设置校准参数(根据实际环境调整)
    SHT20_Calibration_t cal = {
        .temp_offset = -0.5f,   // 温度偏移-0.5°C
        .humi_offset = 2.0f,    // 湿度偏移+2%RH
        .temp_scale = 1.0f,     // 温度比例系数
        .humi_scale = 1.0f      // 湿度比例系数
    };
    SHT20_SetCalibration(&cal);
    
    // 初始化历史数据
    for (uint8_t i = 0; i < TEMP_HISTORY_LEN; i++) {
        temp_history[i] = 25.0f;  // 初始化为室温
    }
    
    printf("System Ready!\r\n");
}

// 绘制UI界面
void Draw_UI(void) {
    // 清屏
    TFT_FillRect(0, 0, TFT_WIDTH, TFT_HEIGHT, BLACK);
    
    // 标题
    TFT_ShowString(80, 5, "ENVIRONMENT MONITOR", WHITE, BLACK);
    TFT_DrawLine(0, 25, TFT_WIDTH, 25, WHITE);
    
    // 温度显示框
    TFT_FillRect(temp_box.x, temp_box.y, temp_box.w, temp_box.h, temp_box.bg_color);
    TFT_DrawRect(temp_box.x, temp_box.y, temp_box.w, temp_box.h, temp_box.border_color);
    TFT_ShowString(temp_box.x + 10, temp_box.y + 5, temp_box.title, WHITE, temp_box.bg_color);
    
    // 湿度显示框
    TFT_FillRect(humi_box.x, humi_box.y, humi_box.w, humi_box.h, humi_box.bg_color);
    TFT_DrawRect(humi_box.x, humi_box.y, humi_box.w, humi_box.h, humi_box.border_color);
    TFT_ShowString(humi_box.x + 10, humi_box.y + 5, humi_box.title, WHITE, humi_box.bg_color);
    
    // 状态显示框
    TFT_FillRect(status_box.x, status_box.y, status_box.w, status_box.h, status_box.bg_color);
    TFT_DrawRect(status_box.x, status_box.y, status_box.w, status_box.h, status_box.border_color);
    TFT_ShowString(status_box.x + 10, status_box.y + 5, status_box.title, WHITE, status_box.bg_color);
}

// 更新数据显示
void Update_Display(void) {
    char buffer[32];
    
    // 更新温度显示
    TFT_FillRect(temp_box.x + 10, temp_box.y + 25, 120, 30, temp_box.bg_color);
    sprintf(buffer, "%.1f C", env_data.temperature);
    TFT_ShowString(temp_box.x + 10, temp_box.y + 25, buffer, WHITE, temp_box.bg_color);
    
    // 温度图标
    TFT_DrawCircle(temp_box.x + 120, temp_box.y + 40, 8, RED);
    TFT_FillCircle(temp_box.x + 120, temp_box.y + 40, 6, RED);
    
    // 更新湿度显示
    TFT_FillRect(humi_box.x + 10, humi_box.y + 25, 120, 30, humi_box.bg_color);
    sprintf(buffer, "%.1f %%", env_data.humidity);
    TFT_ShowString(humi_box.x + 10, humi_box.y + 25, buffer, WHITE, humi_box.bg_color);
    
    // 湿度图标(水滴形状)
    TFT_DrawCircle(humi_box.x + 120, humi_box.y + 40, 8, BLUE);
    TFT_FillCircle(humi_box.x + 120, humi_box.y + 40, 6, BLUE);
    
    // 更新状态信息
    TFT_FillRect(status_box.x + 10, status_box.y + 25, 280, 25, status_box.bg_color);
    sprintf(buffer, "Battery: %s | Time: %lu s", 
            env_data.battery_low ? "LOW" : "OK", 
            env_data.timestamp / 1000);
    TFT_ShowString(status_box.x + 10, status_box.y + 25, buffer, GREEN, status_box.bg_color);
    
    // 绘制温度历史曲线
    Draw_TemperatureGraph();
}

// 绘制温度历史曲线
void Draw_TemperatureGraph(void) {
    uint16_t graph_x = 10;
    uint16_t graph_y = 210;
    uint16_t graph_w = 300;
    uint16_t graph_h = 50;
    
    // 绘制坐标轴
    TFT_DrawRect(graph_x, graph_y, graph_w, graph_h, WHITE);
    TFT_ShowString(graph_x, graph_y - 15, "Temperature History (60s)", WHITE, BLACK);
    
    // 绘制网格
    for (uint8_t i = 1; i < 6; i++) {
        uint16_t x = graph_x + (graph_w * i / 6);
        TFT_DrawLine(x, graph_y, x, graph_y + graph_h, DARKGREY);
    }
    
    // 绘制温度曲线
    uint16_t prev_x = graph_x;
    uint16_t prev_y = graph_y + graph_h - (uint16_t)((temp_history[0] - 20) * graph_h / 30);
    
    for (uint8_t i = 1; i < TEMP_HISTORY_LEN; i++) {
        uint16_t curr_x = graph_x + (graph_w * i / (TEMP_HISTORY_LEN - 1));
        uint16_t curr_y = graph_y + graph_h - (uint16_t)((temp_history[i] - 20) * graph_h / 30);
        
        TFT_DrawLine(prev_x, prev_y, curr_x, curr_y, RED);
        
        prev_x = curr_x;
        prev_y = curr_y;
    }
}

// 更新温度历史
void Update_TemperatureHistory(float temperature) {
    temp_history[history_index] = temperature;
    history_index = (history_index + 1) % TEMP_HISTORY_LEN;
}

// 主循环
int main(void) {
    uint32_t last_update = 0;
    uint8_t update_count = 0;
    
    // 系统初始化
    System_Init();
    
    // 绘制UI
    Draw_UI();
    
    printf("Entering Main Loop...\r\n");
    
    while(1) {
        uint32_t current_time = Get_SystemTime();
        
        // 定时更新数据
        if (current_time - last_update >= UPDATE_INTERVAL) {
            // 读取温湿度
            if (SHT20_ReadTempAndHumi(&env_data)) {
                printf("Temp: %.2f°C, Humi: %.2f%%RH, Battery: %s\r\n",
                       env_data.temperature, env_data.humidity,
                       env_data.battery_low ? "LOW" : "OK");
                
                // 更新显示
                Update_Display();
                
                // 更新历史数据
                Update_TemperatureHistory(env_data.temperature);
                
                // LED闪烁指示
                if (++update_count % 2) {
                    LED_On(LED1);
                } else {
                    LED_Off(LED1);
                }
            } else {
                printf("Failed to read SHT20 data!\r\n");
                TFT_ShowString(10, 250, "Sensor Read Error!", RED, BLACK);
            }
            
            last_update = current_time;
        }
        
        // 其他任务处理
        // ...
        
        Delay_ms(10);
    }
}

四、ASCII字体库 (font.h)

c 复制代码
#ifndef __FONT_H
#define __FONT_H

// 6x12 ASCII字体
const unsigned char asc2_1206[95][12] = {
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 空格
    {0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // !
    {0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00}, // "
    // ... 其他字符省略
};

#endif /* __FONT_H */

参考代码 STM32+SHT20测试温度输出和湿度测试TFT显示 www.youwenfan.com/contentcsu/60560.html

五、编译与部署

5.1 工程配置

复制代码
Project Structure:
├── User/
│   ├── main.c
│   ├── sht20.c/h
│   ├── tft_lcd.c/h
│   └── font.h
├── Hardware/
│   ├── i2c.c/h
│   ├── spi.c/h
│   ├── usart.c/h
│   └── delay.c/h
├── System/
│   └── sys.c/h
└── Libraries/
    └── STM32F10x_StdPeriph_Driver/

5.2 测试步骤

  1. 硬件检查: 确认所有连接正确,电源电压3.3V稳定
  2. I2C测试: 使用逻辑分析仪检查I2C通信是否正常
  3. SHT20测试: 读取传感器ID,验证通信
  4. TFT测试: 显示基本图形和文字
  5. 系统集成: 运行完整程序,验证温湿度显示

5.3 常见问题解决

问题 原因 解决方案
SHT20无响应 I2C地址错误 确认地址为0x80/0x81
温度读数异常 校准参数不当 调整temp_offset和temp_scale
TFT显示花屏 SPI时序问题 降低SPI时钟频率
湿度漂移 传感器老化 定期校准或更换传感器
数据跳变 电源干扰 增加去耦电容,改善布线
相关推荐
别了,李亚普诺夫2 小时前
MAX30102模块原理及代码实现
单片机·嵌入式
星夜夏空992 小时前
STM32单片机学习(3)——前置知识学习
stm32·单片机·学习
渣渣灰95873 小时前
基于STM32F03ZET6移植FreeRTOS
数据库·stm32·嵌入式硬件
magic_now3 小时前
FAT文件系统:嵌入式设备的极简选择
笔记·嵌入式硬件
星夜夏空993 小时前
STM32单片机学习(5) —— STM32的一些名词解释
stm32·单片机·学习
yuan199974 小时前
STM32 速度控制器:PWM + PID 无级调速实现
stm32·单片机·嵌入式硬件
czwxkn4 小时前
pcb设计-器件:稳压二极管
单片机·嵌入式硬件
刻BITTER4 小时前
W25Q32 SPI Flash 芯片读写速度测试 - 对比全片擦除和扇区擦除
嵌入式硬件