<think>我们已有一个主程序框架,现在需要完善各个模块。由于问题中要求使用DHT11和TFT显示屏,并且使用STM32F103RC,我们将分别实现这些模块。注意:由于代码量较大,我们只给出关键部分的实现,并假设您已经配置好了STM32CubeMX生成的基本外设初始化代码(如GPIO、SPI、定时器等)。步骤:1.首先,完成DHT11的驱动代码(dht11.c和dht11.h)。2.完成TFT显示屏的驱动代码(tft_gfx.c,tft_gfx.h,tft_fonts.c,tft_fonts.h)。3.在主程序(main.c)中整合这些模块,并实现功能。由于TFT显示屏的驱动通常依赖于具体的屏幕型号(例如ILI9341),我们以ILI9341为例。下面分别给出各个文件的关键代码:一、dht11.h```c#ifndef__DHT11_H#define__DHT11_H#include"stm32f1xx_hal.h"typedefenum{DHT11_OK,DHT11_ERROR,DHT11_TIMEOUT}DHT11_Status;voidDHT11_Init(void);DHT11_StatusDHT11_Read(uint8_t*temperature,uint8_t*humidity);#endif```二、dht11.c注意:DHT11需要精确的时序,这里我们使用定时器来产生微秒延时。假设我们使用TIM2,并且已经配置为1us计数。```c#include"dht11.h"//定义DHT11的数据引脚#defineDHT11_PORTGPIOA#defineDHT11_PINGPIO_PIN_4//使用TIM2进行微秒延时externTIM_HandleTypeDefhtim2;voidDHT11_Init(void){//设置引脚为输出模式,并初始化为高电平GPIO_InitTypeDefGPIO_InitStruct={0};GPIO_InitStruct.Pin=DHT11_PIN;GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(DHT11_PORT,&GPIO_InitStruct);HAL_GPIO_WritePin(DHT11_PORT,DHT11_PIN,GPIO_PIN_SET);}//微秒延时函数voiddelay_us(uint16_tus){__HAL_TIM_SET_COUNTER(&htim2,0);HAL_TIM_Base_Start(&htim2);while(__HAL_TIM_GET_COUNTER(&htim2)<us);HAL_TIM_Base_Stop(&htim2);}DHT11_StatusDHT11_Read(uint8_t*temperature,uint8_t*humidity){uint8_tdata[5]={0};uint8_ti,j;//主机发送开始信号HAL_GPIO_WritePin(DHT11_PORT,DHT11_PIN,GPIO_PIN_RESET);delay_us(18000);//至少18msHAL_GPIO_WritePin(DHT11_PORT,DHT11_PIN,GPIO_PIN_SET);delay_us(40);//等待20-40us//设置引脚为输入模式GPIO_InitTypeDefGPIO_InitStruct={0};GPIO_InitStruct.Pin=DHT11_PIN;GPIO_InitStruct.Mode=GPIO_MODE_INPUT;GPIO_InitStruct.Pull=GPIO_PULLUP;HAL_GPIO_Init(DHT11_PORT,&GPIO_InitStruct);//等待DHT11响应delay_us(40);if(HAL_GPIO_ReadPin(DHT11_PORT,DHT11_PIN)!=GPIO_PIN_RESET){returnDHT11_TIMEOUT;}delay_us(80);if(HAL_GPIO_ReadPin(DHT11_PORT,DHT11_PIN)!=GPIO_PIN_SET){returnDHT11_TIMEOUT;}//开始接收数据for(i=0;i<5;i++){for(j=0;j<8;j++){//等待低电平结束(每个数据位前有一个50us的低电平)while(HAL_GPIO_ReadPin(DHT11_PORT,DHT11_PIN)==GPIO_PIN_RESET);//高电平持续时间决定数据位是0还是1delay_us(40);if(HAL_GPIO_ReadPin(DHT11_PORT,DHT11_PIN)==GPIO_PIN_SET){data[i]|=(1<<(7-j));//等待高电平结束while(HAL_GPIO_ReadPin(DHT11_PORT,DHT11_PIN)==GPIO_PIN_SET);}}}//检查校验和if(data[4]==(data[0]+data[1]+data[2]+data[3])){*humidity=data[0];*temperature=data[2];returnDHT11_OK;}returnDHT11_ERROR;}```三、tft_fonts.h这里我们定义一种简单的字体(例如8x16)的字符点阵数据。由于点阵数据较长,这里只给出部分示例。```c#ifndef__TFT_FONTS_H#define__TFT_FONTS_H#include<stdint.h>typedefstruct{constuint8_twidth;constuint8_theight;constuint16_t*data;}FontDef;externFontDefFont_8x16;#endif```四、tft_fonts.c```c#include"tft_fonts.h"//这里只给出部分字符(例如数字和字母)的点阵数据constuint16_tFont8x16_Data[]={//每个字符16行,每行8位(实际按16位存储,高8位不用)//字符'0'0x00,0x00,0x3E,0x7F,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x7F,0x3E,0x00,0x00,//...其他字符};FontDefFont_8x16={.width=8,.height=16,.data=Font8x16_Data};```五、tft_gfx.h```c#ifndef__TFT_GFX_H#define__TFT_GFX_H#include"stm32f1xx_hal.h"#include"tft_fonts.h"//TFT屏幕尺寸#defineTFT_WIDTH240#defineTFT_HEIGHT320//常用颜色定义(RGB565)#defineBLACK0x0000#defineBLUE0x001F#defineRED0xF800#defineGREEN0x07E0#defineCYAN0x07FF#defineMAGENTA0xF81F#defineYELLOW0xFFE0#defineWHITE0xFFFFvoidTFT_Init(void);voidTFT_SetRotation(uint8_trotation);voidTFT_FillScreen(uint16_tcolor);voidTFT_DrawPixel(uint16_tx,uint16_ty,uint16_tcolor);voidTFT_DrawLine(uint16_tx0,uint16_ty0,uint16_tx1,uint16_ty1,uint16_tcolor);voidTFT_DrawRect(uint16_tx,uint16_ty,uint16_tw,uint16_th,uint16_tcolor);voidTFT_FillRect(uint16_tx,uint16_ty,uint16_tw,uint16_th,uint16_tcolor);voidTFT_DrawCircle(uint16_tx0,uint16_ty0,uint16_tr,uint16_tcolor);voidTFT_FillCircle(uint16_tx0,uint16_ty0,uint16_tr,uint16_tcolor);voidTFT_SetTextColor(uint16_tcolor,uint16_tbgcolor);voidTFT_SetTextSize(uint8_tsize);voidTFT_SetCursor(uint16_tx,uint16_ty);voidTFT_PrintChar(charc);voidTFT_Print(char*str);#endif```六、tft_gfx.c这里我们只实现部分关键函数,具体实现依赖于SPI发送数据。假设我们使用SPI1,并且已经配置好,同时有CS(片选)、DC(数据/命令)、RST(复位)引脚。```c#include"tft_gfx.h"#include"spi.h"#include<string.h>//定义TFT的控制引脚#defineTFT_CS_PORTGPIOA#defineTFT_CS_PINGPIO_PIN_15#defineTFT_DC_PORTGPIOB#defineTFT_DC_PINGPIO_PIN_5#defineTFT_RST_PORTGPIOB#defineTFT_RST_PINGPIO_PIN_6//全局变量staticuint16_ttextColor=WHITE;staticuint16_ttextBgColor=BLACK;staticuint8_ttextSize=1;staticuint16_tcursorX=0;staticuint16_tcursorY=0;//发送命令staticvoidTFT_SendCommand(uint8_tcmd){HAL_GPIO_WritePin(TFT_DC_PORT,TFT_DC_PIN,GPIO_PIN_RESET);//命令模式HAL_GPIO_WritePin(TFT_CS_PORT,TFT_CS_PIN,GPIO_PIN_RESET);//片选HAL_SPI_Transmit(&hspi1,&cmd,1,HAL_MAX_DELAY);HAL_GPIO_WritePin(TFT_CS_PORT,TFT_CS_PIN,GPIO_PIN_SET);//取消片选}//发送数据staticvoidTFT_SendData(uint8_tdata){HAL_GPIO_WritePin(TFT_DC_PORT,TFT_DC_PIN,GPIO_PIN_SET);//数据模式HAL_GPIO_WritePin(TFT_CS_PORT,TFT_CS_PIN,GPIO_PIN_RESET);HAL_SPI_Transmit(&hspi1,&data,1,HAL_MAX_DELAY);HAL_GPIO_WritePin(TFT_CS_PORT,TFT_CS_PIN,GPIO_PIN_SET);}//发送16位数据(用于颜色)staticvoidTFT_SendData16(uint16_tdata){uint8_tbuf[2]={data>>8,data&0xFF};HAL_GPIO_WritePin(TFT_DC_PORT,TFT_DC_PIN,GPIO_PIN_SET);HAL_GPIO_WritePin(TFT_CS_PORT,TFT_CS_PIN,GPIO_PIN_RESET);HAL_SPI_Transmit(&hspi1,buf,2,HAL_MAX_DELAY);HAL_GPIO_WritePin(TFT_CS_PORT,TFT_CS_PIN,GPIO_PIN_SET);}//初始化TFTvoidTFT_Init(void){//复位HAL_GPIO_WritePin(TFT_RST_PORT,TFT_RST_PIN,GPIO_PIN_RESET);HAL_Delay(100);HAL_GPIO_WritePin(TFT_RST_PORT,TFT_RST_PIN,GPIO_PIN_SET);HAL_Delay(100);//初始化命令序列(以ILI9341为例)TFT_SendCommand(0x01);//软件复位HAL_Delay(120);TFT_SendCommand(0xCF);//电源控制BTFT_SendData(0x00);TFT_SendData(0xC1);TFT_SendData(0x30);//...其他初始化命令TFT_SendCommand(0x3A);//颜色格式TFT_SendData(0x55);//16位TFT_SendCommand(0x29);//开启显示HAL_Delay(100);//设置方向(旋转)TFT_SetRotation(0);}//设置旋转方向voidTFT_SetRotation(uint8_trotation){TFT_SendCommand(0x36);switch(rotation){case0://竖屏TFT_SendData(0x48);break;case1://横屏TFT_SendData(0x28);break;//...其他方向}}//填充屏幕voidTFT_FillScreen(uint16_tcolor){TFT_FillRect(0,0,TFT_WIDTH,TFT_HEIGHT,color);}//画点voidTFT_DrawPixel(uint16_tx,uint16_ty,uint16_tcolor){if(x>=TFT_WIDTH||y>=TFT_HEIGHT)return;TFT_SendCommand(0x2A);//列地址设置TFT_SendData16(x);TFT_SendData16(x);TFT_SendCommand(0x2B);//行地址设置TFT_SendData16(y);TFT_SendData16(y);TFT_SendCommand(0x2C);//写入RAMTFT_SendData16(color);}//填充矩形voidTFT_FillRect(uint16_tx,uint16_ty,uint16_tw,uint16_th,uint16_tcolor){if(x>=TFT_WIDTH||y>=TFT_HEIGHT)return;if(x+w>TFT_WIDTH)w=TFT_WIDTH-x;if(y+h>TFT_HEIGHT)h=TFT_HEIGHT-y;TFT_SendCommand(0x2A);TFT_SendData16(x);TFT_SendData16(x+w-1);TFT_SendCommand(0x2B);TFT_SendData16(y);TFT_SendData16(y+h-1);TFT_SendCommand(0x2C);for(uint32_ti=0;i<(uint32_t)w*h;i++){TFT_SendData16(color);}}//设置文本颜色voidTFT_SetTextColor(uint16_tcolor,uint16_tbgcolor){textColor=color;textBgColor=bgcolor;}//设置文本大小voidTFT_SetTextSize(uint8_tsize){textSize=size;}//设置光标位置voidTFT_SetCursor(uint16_tx,uint16_ty){cursorX=x;cursorY=y;}//打印一个字符voidTFT_PrintChar(charc){//这里使用8x16字体if(c=='\n'){cursorY+=Font_8x16.height*textSize;cursorX=0;return;}//计算字符在点阵数据中的位置uint8_tindex=c-'';constuint16_t*charData=&Font_8x16.data[index*Font_8x16.height];for(uint8_ti=0;i<Font_8x16.height;i++){uint8_tline=charData[i];for(uint8_tj=0;j<Font_8x16.width;j++){if(line&(1<<(7-j))){TFT_FillRect(cursorX+j*textSize,cursorY+i*textSize,textSize,textSize,textColor);}elseif(textBgColor!=textColor){TFT_FillRect(cursorX+j*textSize,cursorY+i*textSize,textSize,textSize,textBgColor);}}}cursorX+=Font_8x16.width*textSize;}//打印字符串voidTFT_Print(char*str){while(*str){TFT_PrintChar(*str++);}}```七、main.c这里使用之前已经给出的主程序框架,但需要根据实际硬件调整引脚定义。注意:主程序中需要初始化外设(在`MX_GPIO_Init`,`MX_SPI1_Init`,`MX_TIM2_Init`中配置),并调用各模块的初始化函数。由于代码较长,这里不再重复。请参考之前的主程序框架,结合上述模块完成。八、注意事项1.DHT11的时序要求严格,如果读取失败,请检查延时精度和中断干扰。2.TFT驱动需要根据具体型号调整初始化命令。3.字体数据较大,可以根据需要裁剪。九、相关函数说明1.DHT11_Read:读取温湿度,返回状态,并将温湿度值通过指针参数返回。2.TFT_Init:初始化TFT屏幕。3.TFT_FillScreen:填充屏幕颜色。4.TFT_SetTextColor:设置文本颜色和背景色。5.TFT_SetTextSize:设置文本大小(倍数)。6.TFT_SetCursor:设置文本光标位置。7.TFT_Print:在屏幕上打印字符串。十、主循环流程1.每2秒读取一次DHT11。2.根据按键状态控制风扇。3.更新TFT显示。4.检查温湿度阈值,触发报警。由于完整代码量非常大,以上只提供了关键部分。请根据实际硬件连接修改引脚定义,并确保外设初始化正确。§§相关问题§§:1.如何优化DHT11的读取精度和稳定性?2.在TFT屏幕上显示中文字符有哪些方法?3.如何实现温湿度数据的存储和回放?4.怎样通过PWM控制风扇转速?</think>以下是基于STM32F103RC系列微控制器的完整解决方案,使用DHT11温湿度传感器和RGB TFT显示屏,在Keil环境中实现温湿度监测、风扇控制和报警功能。代码分为四个主要文件:`main.c`、`dht11.c/h`、`tft_gfx.c/h` 和 `tft_fonts.c/h`。
### 完整代码实现
#### 1. `main.c` - 主程序
```c
#include "stm32f1xx_hal.h"
#include "dht11.h"
#include "tft_gfx.h"
#include "tft_fonts.h"
#include <stdio.h>
/* 硬件配置 */
#define FAN_ENABLE_PIN GPIO_PIN_0
#define FAN_ENABLE_PORT GPIOA
#define FAN_DIR1_PIN GPIO_PIN_8
#define FAN_DIR2_PIN GPIO_PIN_9
#define FAN_DIR_PORT GPIOC
#define BUZZER_PIN GPIO_PIN_10
#define BUZZER_PORT GPIOC
#define BUTTON_DIR1_PIN GPIO_PIN_8
#define BUTTON_DIR2_PIN GPIO_PIN_9
#define BUTTON_PORT GPIOC
/* 阈值设置 */
#define TEMP_THRESHOLD 30 // 温度报警阈值(℃)
#define HUMID_THRESHOLD 80 // 湿度报警阈值(%)
/* 全局变量 */
uint8_t fanState = 0; // 0=关闭, 1=开启
uint8_t fanDirection = 0; // 0=正转, 1=反转
uint8_t tempValue = 0;
uint8_t humidValue = 0;
uint8_t alertActive = 0;
char displayBuffer[40];
/* 函数原型 */
void SystemClock_Config(void);
void Error_Handler(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
static void MX_TIM2_Init(void);
void updateTFTDisplay(void);
void checkThresholds(void);
void drawFanIcon(uint16_t x, uint16_t y, uint16_t color);
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
MX_TIM2_Init();
/* 外设初始化 */
DHT11_Init(GPIOA, GPIO_PIN_4); // DHT11 on PA4
TFT_Init(); // 初始化TFT
TFT_FillScreen(BLACK); // 清屏
/* 初始显示 */
TFT_SetTextColor(WHITE, BLACK);
TFT_SetTextSize(2);
TFT_SetCursor(30, 10);
TFT_Print("温湿度监控系统");
HAL_Delay(1000);
/* 主循环 */
while (1) {
/* 读取温湿度 */
if (DHT11_Read(&tempValue, &humidValue) == DHT11_OK) {
updateTFTDisplay();
checkThresholds();
} else {
TFT_SetTextColor(RED, BLACK);
TFT_SetCursor(50, 200);
TFT_Print("传感器错误!");
}
HAL_Delay(2000); // 每2秒读取一次
/* 按键扫描 */
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
HAL_Delay(50); // 消抖
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
fanState = !fanState;
updateTFTDisplay();
}
while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
}
if (HAL_GPIO_ReadPin(BUTTON_PORT, BUTTON_DIR1_PIN) == GPIO_PIN_RESET) {
HAL_Delay(50);
if (HAL_GPIO_ReadPin(BUTTON_PORT, BUTTON_DIR1_PIN) == GPIO_PIN_RESET) {
fanDirection = 0;
updateTFTDisplay();
}
while (HAL_GPIO_ReadPin(BUTTON_PORT, BUTTON_DIR1_PIN) == GPIO_PIN_RESET);
}
if (HAL_GPIO_ReadPin(BUTTON_PORT, BUTTON_DIR2_PIN) == GPIO_PIN_RESET) {
HAL_Delay(50);
if (HAL_GPIO_ReadPin(BUTTON_PORT, BUTTON_DIR2_PIN) == GPIO_PIN_RESET) {
fanDirection = 1;
updateTFTDisplay();
}
while (HAL_GPIO_ReadPin(BUTTON_PORT, BUTTON_DIR2_PIN) == GPIO_PIN_RESET);
}
/* 控制风扇 */
if (fanState) {
HAL_GPIO_WritePin(FAN_ENABLE_PORT, FAN_ENABLE_PIN, GPIO_PIN_SET);
if (fanDirection == 0) {
HAL_GPIO_WritePin(FAN_DIR_PORT, FAN_DIR1_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(FAN_DIR_PORT, FAN_DIR2_PIN, GPIO_PIN_RESET);
} else {
HAL_GPIO_WritePin(FAN_DIR_PORT, FAN_DIR1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(FAN_DIR_PORT, FAN_DIR2_PIN, GPIO_PIN_SET);
}
} else {
HAL_GPIO_WritePin(FAN_ENABLE_PORT, FAN_ENABLE_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(FAN_DIR_PORT, FAN_DIR1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(FAN_DIR_PORT, FAN_DIR2_PIN, GPIO_PIN_RESET);
}
/* 报警处理 */
if (alertActive) {
HAL_GPIO_TogglePin(BUZZER_PORT, BUZZER_PIN);
HAL_Delay(200);
} else {
HAL_GPIO_WritePin(BUZZER_PORT, BUZZER_PIN, GPIO_PIN_RESET);
}
}
}
/* 更新TFT显示 */
void updateTFTDisplay(void) {
// 清空显示区域
TFT_FillRect(0, 50, 240, 190, BLACK);
// 显示温度
TFT_SetTextColor(YELLOW, BLACK);
TFT_SetTextSize(2);
TFT_SetCursor(20, 60);
TFT_Print("温度:");
sprintf(displayBuffer, "%2d C", tempValue);
TFT_SetCursor(100, 60);
TFT_Print(displayBuffer);
// 显示湿度
TFT_SetTextColor(CYAN, BLACK);
TFT_SetCursor(20, 90);
TFT_Print("湿度:");
sprintf(displayBuffer, "%2d %%", humidValue);
TFT_SetCursor(100, 90);
TFT_Print(displayBuffer);
// 显示风扇状态
TFT_SetTextColor(GREEN, BLACK);
TFT_SetCursor(20, 120);
TFT_Print("风扇:");
TFT_SetCursor(100, 120);
TFT_Print(fanState ? "开启" : "关闭");
// 显示风扇方向
TFT_SetCursor(20, 150);
TFT_Print("方向:");
TFT_SetCursor(100, 150);
TFT_Print(fanDirection ? "反转" : "正转");
// 绘制风扇图标
drawFanIcon(180, 120, fanState ? GREEN : RED);
// 报警状态显示
if (alertActive) {
TFT_SetTextColor(RED, BLACK);
TFT_SetCursor(20, 180);
if (tempValue > TEMP_THRESHOLD && humidValue > HUMID_THRESHOLD) {
TFT_Print("警告: 温湿度过高!");
} else if (tempValue > TEMP_THRESHOLD) {
TFT_Print("警告: 温度过高!");
} else {
TFT_Print("警告: 湿度过高!");
}
}
}
/* 绘制风扇图标 */
void drawFanIcon(uint16_t x, uint16_t y, uint16_t color) {
TFT_FillCircle(x, y, 15, color);
TFT_DrawCircle(x, y, 15, WHITE);
TFT_DrawLine(x, y, x+15, y, WHITE);
TFT_DrawLine(x, y, x, y-15, WHITE);
TFT_DrawLine(x, y, x-10, y+10, WHITE);
TFT_DrawLine(x, y, x+10, y+10, WHITE);
}
/* 检查阈值 */
void checkThresholds(void) {
alertActive = (tempValue > TEMP_THRESHOLD || humidValue > HUMID_THRESHOLD) ? 1 : 0;
}
/* GPIO初始化 */
static void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// 风扇控制
GPIO_InitStruct.Pin = FAN_ENABLE_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(FAN_ENABLE_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = FAN_DIR1_PIN | FAN_DIR2_PIN;
HAL_GPIO_Init(FAN_DIR_PORT, &GPIO_InitStruct);
// 蜂鸣器
GPIO_InitStruct.Pin = BUZZER_PIN;
HAL_GPIO_Init(BUZZER_PORT, &GPIO_InitStruct);
// 按键 (PA0, PC8, PC9)
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = BUTTON_DIR1_PIN | BUTTON_DIR2_PIN;
HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct);
}
// 其他初始化函数(SystemClock_Config, MX_SPI1_Init, MX_TIM2_Init)需根据具体硬件配置
```
#### 2. `dht11.h` - DHT11头文件
```c
#ifndef __DHT11_H
#define __DHT11_H
#include "stm32f1xx_hal.h"
typedef enum {
DHT11_OK,
DHT11_ERROR,
DHT11_TIMEOUT
} DHT11_Status;
void DHT11_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
DHT11_Status DHT11_Read(uint8_t *temperature, uint8_t *humidity);
#endif
```
#### 3. `dht11.c` - DHT11驱动
```c
#include "dht11.h"
#include "main.h"
static GPIO_TypeDef* DHT11_GPIO;
static uint16_t DHT11_PIN;
static TIM_HandleTypeDef* DHT11_TIMER = &htim2; // 使用TIM2
void DHT11_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) {
DHT11_GPIO = GPIOx;
DHT11_PIN = GPIO_Pin;
// 初始化GPIO
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DHT11_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DHT11_GPIO, &GPIO_InitStruct);
HAL_GPIO_WritePin(DHT11_GPIO, DHT11_PIN, GPIO_PIN_SET);
}
static void delay_us(uint16_t us) {
__HAL_TIM_SET_COUNTER(DHT11_TIMER, 0);
HAL_TIM_Base_Start(DHT11_TIMER);
while (__HAL_TIM_GET_COUNTER(DHT11_TIMER) < us);
HAL_TIM_Base_Stop(DHT11_TIMER);
}
DHT11_Status DHT11_Read(uint8_t *temperature, uint8_t *humidity) {
uint8_t data[5] = {0};
uint8_t i, j;
// 主机发送开始信号
HAL_GPIO_WritePin(DHT11_GPIO, DHT11_PIN, GPIO_PIN_RESET);
delay_us(18000); // 18ms
HAL_GPIO_WritePin(DHT11_GPIO, DHT11_PIN, GPIO_PIN_SET);
delay_us(30); // 30us
// 设置为输入模式
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DHT11_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(DHT11_GPIO, &GPIO_InitStruct);
// 等待响应
delay_us(40);
if (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) != GPIO_PIN_RESET)
return DHT11_TIMEOUT;
delay_us(80);
if (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) != GPIO_PIN_SET)
return DHT11_TIMEOUT;
// 读取40位数据
for (i = 0; i < 5; i++) {
for (j = 0; j < 8; j++) {
while (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_RESET);
delay_us(40);
if (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_SET) {
data[i] |= (1 << (7 - j));
while (HAL_GPIO_ReadPin(DHT11_GPIO, DHT11_PIN) == GPIO_PIN_SET);
}
}
}
// 校验和检查
if (data[4] == (data[0] + data[1] + data[2] + data[3])) {
*humidity = data[0];
*temperature = data[2];
return DHT11_OK;
}
return DHT11_ERROR;
}
```
#### 4. `tft_gfx.h` - TFT图形库头文件
```c
#ifndef __TFT_GFX_H
#define __TFT_GFX_H
#include "stm32f1xx_hal.h"
#include "tft_fonts.h"
#define TFT_WIDTH 240
#define TFT_HEIGHT 320
// RGB565颜色定义
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
// 函数声明
void TFT_Init(void);
void TFT_SetRotation(uint8_t rotation);
void TFT_FillScreen(uint16_t color);
void TFT_DrawPixel(int16_t x, int16_t y, uint16_t color);
void TFT_DrawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
void TFT_DrawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void TFT_FillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void TFT_DrawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void TFT_FillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void TFT_SetTextColor(uint16_t color, uint16_t bgcolor);
void TFT_SetTextSize(uint8_t size);
void TFT_SetCursor(int16_t x, int16_t y);
void TFT_PrintChar(char c);
void TFT_Print(char *str);
#endif
```
#### 5. `tft_gfx.c` - TFT图形库实现
```c
#include "tft_gfx.h"
#include "spi.h"
// 硬件配置
#define TFT_CS_PORT GPIOA
#define TFT_CS_PIN GPIO_PIN_15
#define TFT_DC_PORT GPIOB
#define TFT_DC_PIN GPIO_PIN_5
#define TFT_RST_PORT GPIOB
#define TFT_RST_PIN GPIO_PIN_6
// 全局变量
static uint16_t _textColor = WHITE;
static uint16_t _textBgColor = BLACK;
static uint8_t _textSize = 1;
static int16_t _cursorX = 0;
static int16_t _cursorY = 0;
static uint8_t _rotation = 0;
// 发送命令
static void TFT_SendCommand(uint8_t cmd) {
HAL_GPIO_WritePin(TFT_DC_PORT, TFT_DC_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY);
HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_SET);
}
// 发送数据
static void TFT_SendData(uint8_t data) {
HAL_GPIO_WritePin(TFT_DC_PORT, TFT_DC_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, &data, 1, HAL_MAX_DELAY);
HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_SET);
}
static void TFT_SendData16(uint16_t data) {
uint8_t buf[2] = {data >> 8, data & 0xFF};
HAL_GPIO_WritePin(TFT_DC_PORT, TFT_DC_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, buf, 2, HAL_MAX_DELAY);
HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_SET);
}
// TFT初始化
void TFT_Init(void) {
// 硬件复位
HAL_GPIO_WritePin(TFT_RST_PORT, TFT_RST_PIN, GPIO_PIN_RESET);
HAL_Delay(100);
HAL_GPIO_WritePin(TFT_RST_PORT, TFT_RST_PIN, GPIO_PIN_SET);
HAL_Delay(120);
// 初始化序列 (ILI9341)
TFT_SendCommand(0x01); // 软件复位
HAL_Delay(150);
TFT_SendCommand(0xCF); // 电源控制B
TFT_SendData(0x00);
TFT_SendData(0xC1);
TFT_SendData(0x30);
TFT_SendCommand(0xED); // 电源开启序列
TFT_SendData(0x64);
TFT_SendData(0x03);
TFT_SendData(0x12);
TFT_SendData(0x81);
TFT_SendCommand(0xE8); // 驱动时序控制A
TFT_SendData(0x85);
TFT_SendData(0x00);
TFT_SendData(0x78);
TFT_SendCommand(0xCB); // 电源控制A
TFT_SendData(0x39);
TFT_SendData(0x2C);
TFT_SendData(0x00);
TFT_SendData(0x34);
TFT_SendData(0x02);
TFT_SendCommand(0xF7); // 泵比控制
TFT_SendData(0x20);
TFT_SendCommand(0xEA); // 驱动时序控制B
TFT_SendData(0x00);
TFT_SendData(0x00);
TFT_SendCommand(0xC0); // 电源控制1
TFT_SendData(0x23);
TFT_SendCommand(0xC1); // 电源控制2
TFT_SendData(0x10);
TFT_SendCommand(0xC5); // VCOM控制1
TFT_SendData(0x3E);
TFT_SendData(0x28);
TFT_SendCommand(0xC7); // VCOM控制2
TFT_SendData(0x86);
TFT_SendCommand(0x36); // 内存访问控制
TFT_SendData(0x48); // 竖屏模式
TFT_SendCommand(0x3A); // 像素格式
TFT_SendData(0x55); // 16位像素
TFT_SendCommand(0xB1); // 帧率控制
TFT_SendData(0x00);
TFT_SendData(0x18);
TFT_SendCommand(0xB6); // 显示功能控制
TFT_SendData(0x08);
TFT_SendData(0x82);
TFT_SendData(0x27);
TFT_SendCommand(0xF2); // 3G控制
TFT_SendData(0x00);
TFT_SendCommand(0x26); // Gamma设置
TFT_SendData(0x01);
TFT_SendCommand(0xE0); // 正伽马校正
TFT_SendData(0x0F);
TFT_SendData(0x31);
TFT_SendData(0x2B);
TFT_SendData(0x0C);
TFT_SendData(0x0E);
TFT_SendData(0x08);
TFT_SendData(0x4E);
TFT_SendData(0xF1);
TFT_SendData(0x37);
TFT_SendData(0x07);
TFT_SendData(0x10);
TFT_SendData(0x03);
TFT_SendData(0x0E);
TFT_SendData(0x09);
TFT_SendData(0x00);
TFT_SendCommand(0xE1); // 负伽马校正
TFT_SendData(0x00);
TFT_SendData(0x0E);
TFT_SendData(0x14);
TFT_SendData(0x03);
TFT_SendData(0x11);
TFT_SendData(0x07);
TFT_SendData(0x31);
TFT_SendData(0xC1);
TFT_SendData(0x48);
TFT_SendData(0x08);
TFT_SendData(0x0F);
TFT_SendData(0x0C);
TFT_SendData(0x31);
TFT_SendData(0x36);
TFT_SendData(0x0F);
TFT_SendCommand(0x11); // 退出睡眠
HAL_Delay(120);
TFT_SendCommand(0x29); // 开启显示
}
void TFT_SetRotation(uint8_t m) {
_rotation = m % 4;
TFT_SendCommand(0x36);
switch (_rotation) {
case 0: // 竖屏
TFT_SendData(0x48);
break;
case 1: // 横屏
TFT_SendData(0x28);
break;
case 2: // 倒竖屏
TFT_SendData(0x88);
break;
case 3: // 倒横屏
TFT_SendData(0xE8);
break;
}
}
void TFT_FillScreen(uint16_t color) {
TFT_FillRect(0, 0, TFT_WIDTH, TFT_HEIGHT, color);
}
void TFT_FillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
if (x >= TFT_WIDTH || y >= TFT_HEIGHT) return;
if (x + w > TFT_WIDTH) w = TFT_WIDTH - x;
if (y + h > TFT_HEIGHT) h = TFT_HEIGHT - y;
TFT_SendCommand(0x2A); // 列地址设置
TFT_SendData16(x);
TFT_SendData16(x + w - 1);
TFT_SendCommand(0x2B); // 行地址设置
TFT_SendData16(y);
TFT_SendData16(y + h - 1);
TFT_SendCommand(0x2C); // 写入RAM
HAL_GPIO_WritePin(TFT_DC_PORT, TFT_DC_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_RESET);
for (uint32_t i = 0; i < (uint32_t)w * h; i++) {
uint8_t buf[2] = {color >> 8, color & 0xFF};
HAL_SPI_Transmit(&hspi1, buf, 2, HAL_MAX_DELAY);
}
HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_SET);
}
// 其他图形函数(TFT_DrawPixel, TFT_DrawLine等)根据需求实现
void TFT_SetTextColor(uint16_t color, uint16_t bgcolor) {
_textColor = color;
_textBgColor = bgcolor;
}
void TFT_SetTextSize(uint8_t size) {
_textSize = (size > 0) ? size : 1;
}
void TFT_SetCursor(int16_t x, int16_t y) {
_cursorX = x;
_cursorY = y;
}
void TFT_PrintChar(char c) {
if (c == '\n') {
_cursorY += Font_8x16.height * _textSize;
_cursorX = 0;
return;
}
uint8_t charIndex = c - ' ';
const uint16_t *charData = &Font_8x16.data[charIndex * Font_8x16.height];
for (uint8_t i = 0; i < Font_8x16.height; i++) {
uint8_t line = charData[i];
for (uint8_t j = 0; j < Font_8x16.width; j++) {
if (line & (1 << (7 - j))) {
TFT_FillRect(_cursorX + j * _textSize,
_cursorY + i * _textSize,
_textSize, _textSize, _textColor);
} else if (_textBgColor != _textColor) {
TFT_FillRect(_cursorX + j * _textSize,
_cursorY + i * _textSize,
_textSize, _textSize, _textBgColor);
}
}
}
_cursorX += Font_8x16.width * _textSize;
}
void TFT_Print(char *str) {
while (*str) {
TFT_PrintChar(*str++);
}
}
```
#### 6. `tft_fonts.h` - 字体定义
```c
#ifndef __TFT_FONTS_H
#define __TFT_FONTS_H
#include <stdint.h>
typedef struct {
const uint8_t width;
const uint8_t height;
const uint16_t *data;
} FontDef;
extern FontDef Font_8x16;
#endif
```
#### 7. `tft_fonts.c` - 字体数据
```c
#include "tft_fonts.h"
// 8x16 ASCII 字体
const uint16_t Font8x16_Data[] = {
// 空格 (ASCII 32)
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
// 数字0-9和字母等
// 这里只展示部分,完整字体需自行添加
// '0'
0x0000, 0x3E00, 0x7F00, 0x6300, 0x6300, 0x6300, 0x6300, 0x6300,
0x6300, 0x6300, 0x6300, 0x6300, 0x7F00, 0x3E00, 0x0000, 0x0000,
// '1'
0x0000, 0x0C00, 0x1C00, 0x3C00, 0x0C00, 0x0C00, 0x0C00, 0x0C00,
0x0C00, 0x0C00, 0x0C00, 0x0C00, 0x3F00, 0x3F00, 0x0000, 0x0000,
// ... 其他字符
};
FontDef Font_8x16 = {
.width = 8,
.height = 16,
.data = Font8x16_Data
};
```
### 代码解释
#### 1. 系统架构
- **主程序(main.c)**:协调整个系统,处理传感器数据、用户输入和设备控制
- **DHT11驱动(dht11.c/h)**:实现温湿度传感器的读取协议
- **TFT图形库(tft_gfx.c/h)**:提供TFT显示屏的基本绘图功能
- **字体数据(tft_fonts.c/h)**:包含字符点阵数据用于文本显示
#### 2. 关键功能实现
- **温湿度监测**:
- 每2秒读取一次DHT11传感器
- 使用TIM2定时器提供精确的微秒级延时
- **TFT显示**:
- 显示温度、湿度数值
- 实时显示风扇状态和方向
- 超阈值时显示红色警告信息
- 绘制风扇状态图标
- **风扇控制**:
- PA0按键切换风扇开关
- PC8按键设置正转
- PC9按键设置反转
- **报警功能**:
- 温度>30℃或湿度>80%时触发蜂鸣器
- 蜂鸣器以200ms间隔闪烁报警
- TFT显示红色警告信息
#### 3. 硬件连接
```plaintext
DHT11:
VCC: 3.3V
GND: GND
DATA: PA4
TFT (SPI ILI9341):
CS: PA15
DC: PB5
RST: PB6
MOSI: PA7 (SPI1_MOSI)
SCK: PA5 (SPI1_SCK)
风扇控制:
ENABLE: PA0
IN1: PC8
IN2: PC9
蜂鸣器: PC10
按键:
FAN_SW: PA0 (与风扇使能共用)
DIR_FWD: PC8
DIR_REV: PC9
```
### Keil项目配置
1. **创建项目**:
- 选择STM32F103RC设备
- 启用HAL库
- 配置系统时钟为72MHz
2. **外设配置**:
- SPI1: 全双工主机模式,时钟分频32
- TIM2: 基本定时器,预分频71,计数范围50000(1MHz时钟)
- GPIO: 配置所有使用的引脚
3. **项目结构**:
```
Project/
├── Core/
│ ├── Inc/
│ │ ├── dht11.h
│ │ ├── tft_fonts.h
│ │ └── tft_gfx.h
│ ├── Src/
│ │ ├── main.c
│ │ ├── dht11.c
│ │ ├── tft_fonts.c
│ │ └── tft_gfx.c
│ └── ...
├── Drivers/
│ ├── CMSIS/
│ └── STM32F1xx_HAL_Driver/
└── ...
```
### 优化建议
1. **DHT11稳定性**:
- 添加CRC校验
- 实现多次读取取平均值
- 增加读取失败重试机制
2. **显示性能**:
- 实现部分区域刷新
- 使用双缓冲技术
- 优化SPI传输速度
3. **系统健壮性**:
- 添加看门狗定时器
- 实现按键长按检测
- 添加温度补偿算法