一、原理图
1. STM32F103RET6
原理图及参考手册:https://pan.baidu.com/s/1O9LNB4pi_CoMYQlFxnAu-w?pwd=xwud 提取码: xwud

2. 温湿度SHT20

二、传感器数据手册
https://pan.baidu.com/s/1X9wXWY5LKW6fhXTOVIz4YA?pwd=9g9g 提取码: 9g9g --来自百度网盘超级会员v3的分享
三、硬件配置
1. 配置GPIO


2. 配置I2C2

3. 开启外部时钟

4. 配置UART1

5. 时钟配置

6. 工程配置


四、实现代码
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
// SHT20 从机地址(7位地址为0x40)
uint8_t SHT20_ADDR = 0x40;
// 测量命令(保持主机模式,即主机控制SCL线等待测量完成)
uint8_t SHT20_CMD_TEMP = 0xE3; // 温度测量命令
uint8_t SHT20_CMD_HUMI = 0xE5; // 湿度测量命令
uint8_t data_buf[3] = {0}; // 存储读取的3字节数据(16位数据+8位CRC)
uint16_t raw_temp = 0; // 温度原始值
uint16_t raw_humi = 0; // 湿度原始值
float temp = 0.0f; // 实际温度(℃)
float humi = 0.0f; // 实际湿度(%RH)
void SystemClock_Config(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C2_Init();
MX_USART1_UART_Init();
while (1)
{
// 读取温度
// 1. 发送温度测量命令
HAL_I2C_Master_Transmit(&hi2c2, (SHT20_ADDR << 1), &SHT20_CMD_TEMP, 1, 1000);
HAL_Delay(90); // SHT20温度测量最长需85ms,预留余量
// 2. 读取3字节数据(16位温度+8位CRC)
HAL_I2C_Master_Receive(&hi2c2, (SHT20_ADDR << 1) | 1, data_buf, 3, 1000);
// 3. 解析温度原始值(屏蔽低2位状态位)
raw_temp = (data_buf[0] << 8) | data_buf[1];
raw_temp &= 0xFFFC; // SHT20数据格式要求:低2位为状态位,需清除
// 4. 转换为实际温度(公式来自SHT20数据手册)
temp = -46.85f + (175.72f * raw_temp) / 65536.0f;
// 读取湿度
// 1. 发送湿度测量命令
HAL_I2C_Master_Transmit(&hi2c2, (SHT20_ADDR << 1), &SHT20_CMD_HUMI, 1, 1000);
HAL_Delay(30); // SHT20湿度测量最长需29ms,预留余量
// 2. 读取3字节数据(16位湿度+8位CRC)
HAL_I2C_Master_Receive(&hi2c2, (SHT20_ADDR << 1) | 1, data_buf, 3, 1000);
// 3. 解析湿度原始值(屏蔽低2位状态位)
raw_humi = (data_buf[0] << 8) | data_buf[1];
raw_humi &= 0xFFFC; // 清除低2位状态位
// 4. 转换为实际湿度(公式来自SHT20数据手册)
humi = -6.0f + (125.0f * raw_humi) / 65536.0f;
// 打印结果
printf("Temp:%.2f ℃\n\r", temp);
printf("Humi:%.2f %%RH\n\r", humi);
HAL_Delay(2000); // 2秒刷新一次
}
}
//printf重定向到UART1
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 500);
return ch;
}
五、实验效果


六、多文件编译——轮询
1. sht20.h
#ifndef SHT20_H
#define SHT20_H
#include "stdint.h"
#include "stm32f1xx_hal.h"
// SHT20 设备参数
#define SHT20_ADDR 0x40 // 7位I2C地址
#define SHT20_CMD_RESET 0xFE // 软复位命令
// 测量命令(声明为全局变量,用于取地址)
extern uint8_t SHT20_CMD_TEMP; // 温度测量命令(0xE3)
extern uint8_t SHT20_CMD_HUMI; // 湿度测量命令(0xE5)
extern uint16_t raw_temp; // 温度原始数据
extern uint16_t raw_humi; // 湿度原始数据
extern float temp; // 温度值
extern float humi; // 湿度值
// 函数声明
void SHT20_Reset(I2C_HandleTypeDef *hi2c); // 软复位
float SHT20_ReadTemperature(I2C_HandleTypeDef *hi2c); // 读取温度
float SHT20_ReadHumidity(I2C_HandleTypeDef *hi2c); // 读取湿度
#endif // SHT20_H
2. sht20.c
#include "sht20.h"
#include "stm32f1xx_hal.h"
#include "stdio.h" // 用于调试打印
// 测量命令定义(全局变量,分配内存)
uint8_t SHT20_CMD_TEMP = 0xE3; // 温度测量命令
uint8_t SHT20_CMD_HUMI = 0xE5; // 湿度测量命令
uint16_t raw_temp = 0;
uint16_t raw_humi = 0;
// 内部数据缓冲区(仅本文件使用)
static uint8_t data_buf[3] = {0};
/**
* @brief SHT20软复位
* @param hi2c: I2C句柄指针
*/
void SHT20_Reset(I2C_HandleTypeDef *hi2c) {
uint8_t reset_cmd = SHT20_CMD_RESET;
if (HAL_I2C_Master_Transmit(hi2c, (SHT20_ADDR << 1), &reset_cmd, 1, 1000) != HAL_OK) {
printf("SHT20 reset failed!\n\r");
} else {
printf("SHT20 reset success!\n\r");
}
HAL_Delay(15); // 复位后需等待15ms
}
/**
* @brief 读取温度
* @param hi2c: I2C句柄指针
* @return 温度值(℃),失败返回-1000.0f
*/
float SHT20_ReadTemperature(I2C_HandleTypeDef *hi2c) {
// 发送温度测量命令
if (HAL_I2C_Master_Transmit(hi2c, (SHT20_ADDR << 1), &SHT20_CMD_TEMP, 1, 1000) != HAL_OK) {
printf("Temp transmit failed!\n\r");
return -1000.0f;
}
HAL_Delay(100); // 等待测量完成(最长85ms,留余量)
// 读取3字节数据(16位数据+8位CRC)
if (HAL_I2C_Master_Receive(hi2c, (SHT20_ADDR << 1) | 1, data_buf, 3, 1000) != HAL_OK) {
printf("Temp receive failed!\n\r");
return -1000.0f;
}
// 解析数据(清除低2位状态位)
raw_temp = (data_buf[0] << 8) | data_buf[1];
raw_temp &= 0xFFFC;
// 转换为实际温度(公式来自数据手册)
return -46.85f + (175.72f * raw_temp) / 65536.0f;
}
/**
* @brief 读取湿度
* @param hi2c: I2C句柄指针
* @return 湿度值(%RH),失败返回-1.0f
*/
float SHT20_ReadHumidity(I2C_HandleTypeDef *hi2c) {
// 发送湿度测量命令
if (HAL_I2C_Master_Transmit(hi2c, (SHT20_ADDR << 1), &SHT20_CMD_HUMI, 1, 1000) != HAL_OK) {
printf("Humi transmit failed!\n\r");
return -1.0f;
}
HAL_Delay(40); // 等待测量完成(最长29ms,留余量)
// 读取3字节数据
if (HAL_I2C_Master_Receive(hi2c, (SHT20_ADDR << 1) | 1, data_buf, 3, 1000) != HAL_OK) {
printf("Humi receive failed!\n\r");
return -1.0f;
}
// 解析数据
raw_humi = (data_buf[0] << 8) | data_buf[1];
raw_humi &= 0xFFFC;
// 转换为实际湿度
return -6.0f + (125.0f * raw_humi) / 65536.0f;
}
3. main.c
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include "sht20.h" // 引入SHT20驱动
void SystemClock_Config(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C2_Init();
MX_USART1_UART_Init();
float temp = 0.0f; // 温度值
float humi = 0.0f; // 湿度值
printf("System initialized!\n\r");
SHT20_Reset(&hi2c2); // 初始化时复位SHT20
while (1)
{
// 读取温湿度
temp = SHT20_ReadTemperature(&hi2c2);
humi = SHT20_ReadHumidity(&hi2c2);
// 打印结果
if (temp != -1000.0f && humi != -1.0f) {
printf("Temp:%.2f ℃\n\r", temp);
printf("Humi:%.2f %%RH\n\r", humi);
} else {
printf("I2C communication error!\n\r");
}
HAL_Delay(2000); // 2秒刷新一次
}
}
/* 重定向fputc函数,该函数会在使用printf等输出函数时被调用 */
int fputc(int ch, FILE *f)
{
uint8_t temp[1] = {ch}; /* 将字符转换为uint8_t类型 */
HAL_UART_Transmit(&huart1, temp, 1, 2); /* 通过UART1发送字符,超时时间为2ms */
return ch; /* 返回写入的字符 */
}
方法一: 解决按复位按键,串口不输出温湿度数据问题

方法二:半主机模式,解决按复位按键串口不输出温湿度数据问题
/* 引入特殊编译选项,告知编译器不要使用标准主机环境的函数和功能 */
#pragma import(__use_no_semihosting)
/* 自定义FILE结构体,替代标准库中的版本 */
struct __FILE
{
int handle; /* 文件句柄,用于标识不同的文件或设备 */
};
/* 定义标准输出文件对象 */
FILE __stdout;
/* 系统退出函数,在嵌入式系统中可能不需要真正退出,所以这里只是空操作 */
void _sys_exit(int x)
{
x = x; /* 防止未使用参数的警告 */
}
/* 重定向fputc函数,该函数会在使用printf等输出函数时被调用 */
int fputc(int ch, FILE *f)
{
uint8_t temp[1] = {ch}; /* 将字符转换为uint8_t类型 */
HAL_UART_Transmit(&huart1, temp, 1, 2); /* 通过UART1发送字符,超时时间为2ms */
return ch; /* 返回写入的字符 */
}