利用Proteus仿真STM32实现DHT11温湿度检测
wptr33 2025-06-09 00:40 5 浏览
1. 前言
Proteus是英国著名的EDA工具(仿真软件),从原理图布图、代码调试到单片机与外围电路协同仿真,一 键切换到PCB设计,真正实现了从概念到产品的完整设计。是世界上唯一将电路仿真软件、PCB设计软 件和虚拟模型仿真软件三合一的设计平台,其处理器模型支持8051、HC11、
PIC10/12/16/18/24/30/DSPIC33、AVR、ARM、8086和MSP430等,2010年又增加了Cortex和DSP系 列处理器,并持续增加其他系列处理器模型。在编译方面,它也支持IAR、Keil和MATLAB等多种编译 器。 前面文章介绍了Proteus的下载,安装,建立工程,完成LED灯仿真运行。这篇文章在这基础上增加串口打印,DHT11温湿度检测。
2. 设计程序
先使用keil软件就将程序设计设计好,然后生成HEX文件,等待设计好原理图后进行仿真测试。
注意: 当前使用的芯片是STM32F103。Proteus的版本是8.9
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "dht11.h"
/*
(3)温湿度传感器: DHT11
VCC--VCC
GND---GND
DAT---PA5
*/
#include "stm32f10x.h"
#include <stdio.h>
#include <stdarg.h>
#include "sys.h"
#include <string.h>
#define USART1_RX_LENGTH 1024
extern u8 USART1_RX_BUFFER[USART1_RX_LENGTH]; //保存接收数据的缓冲区
extern u32 USART1_RX_CNT; //当前接收到的数据长度
extern u8 USART1_RX_FLAG; //1表示数据接收完毕 0表示没有接收完毕
#define USART2_RX_LENGTH 1024
extern u8 USART2_RX_BUFFER[USART2_RX_LENGTH]; //保存接收数据的缓冲区
extern u32 USART2_RX_CNT; //当前接收到的数据长度
extern u8 USART2_RX_FLAG; //1表示数据接收完毕 0表示没有接收完毕
#define USART3_RX_LENGTH 1024
extern u8 USART3_RX_BUFFER[USART3_RX_LENGTH]; //保存接收数据的缓冲区
extern u32 USART3_RX_CNT; //当前接收到的数据长度
extern u8 USART3_RX_FLAG; //1表示数据接收完毕 0表示没有接收完毕
void USART1_Init(u32 baud);
void USART2_Init(u32 baud);
void USART3_Init(u32 baud);
void USARTx_StringSend(USART_TypeDef *USARTx,char *str);
void USARTx_DataSend(USART_TypeDef *USARTx,u8 *data,u32 len);
//定义按键IO口
#define KEY_S3 PAin(1)
//函数声明
void KEY_Init(void);
u8 KEY_Scan(u8 mode);
//LED定义
#define LED1 PBout(6)
#define LED2 PBout(7)
#define LED3 PBout(8)
#define LED4 PBout(9)
//蜂鸣器IO口定义
#define BEEP PAout(6)
//函数声明
void LED_Init(void);
void BEEP_Init(void);
//IO方向设置
#define DHT11_IO_IN() {GPIOA->CRL&=0XFF0FFFFF;GPIOA->CRL|=0x00800000;}
#define DHT11_IO_OUT() {GPIOA->CRL&=0XFF0FFFFF;GPIOA->CRL|=0x00300000;}
////IO操作函数
#define DHT11_DQ_OUT PAout(5) //数据端口 PA5
#define DHT11_DQ_IN PAin(5) //数据端口 PA5
u8 DHT11_Init(void); //初始化DHT11
u8 DHT11_Read_Data(u8 *temp,u8 *humi);//读取温湿度
u8 DHT11_Read_Byte(void); //读出一个字节
u8 DHT11_Read_Bit(void); //读出一个位
u8 DHT11_Check(void); //检测是否存在DHT11
void DHT11_Rst(void); //复位DHT11
//复位DHT11
void DHT11_Rst(void)
{
DHT11_IO_OUT(); //SET OUTPUT
DHT11_DQ_OUT=0; //拉低DQ
DelayMs(20); //拉低至少18ms
DHT11_DQ_OUT=1; //DQ=1
DelayUs(30); //主机拉高20~40us
}
//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void)
{
u8 retry=0;
DHT11_IO_IN();//SET INPUT
while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
{
retry++;
DelayUs(1);
};
if(retry>=100)return 1;
else retry=0;
while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
{
retry++;
DelayUs(1);
};
if(retry>=100)return 1;
return 0;
}
//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void)
{
u8 retry=0;
while(DHT11_DQ_IN&&retry<100)//等待变为低电平
{
retry++;
DelayUs(1);
}
retry=0;
while(!DHT11_DQ_IN&&retry<100)//等待变高电平
{
retry++;
DelayUs(1);
}
DelayUs(40);//等待40us
if(DHT11_DQ_IN)return 1;
else return 0;
}
//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)
{
u8 i,dat;
dat=0;
for (i=0;i<8;i++)
{
dat<<=1;
dat|=DHT11_Read_Bit();
}
return dat;
}
//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u8 DHT11_Read_Data(u8 *temp,u8 *humi)
{
u8 buf[5];
u8 i;
DHT11_Rst();
//printf("------------------------\r\n");
if(DHT11_Check()==0)
{
for(i=0;i<5;i++)//读取40位数据
{
buf[i]=DHT11_Read_Byte();
}
if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
{
*humi=buf[0];
*temp=buf[2];
}
}else return 1;
return 0;
}
//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在
u8 DHT11_Init(void)
{
RCC->APB2ENR|=1<<2; //使能PORTA口时钟
GPIOA->CRL&=0XFF0FFFFF;//PORTA.5 推挽输出
GPIOA->CRL|=0X00300000;
GPIOA->ODR|=1<<5; //输出1
DHT11_Rst();
return DHT11_Check();
}
/*
函数功能:按键初始化
硬件连接:PA1
特性: 按下为低电平---没按下高电平
*/
void KEY_Init(void)
{
//开时钟
RCC->APB2ENR|=1<<2;
//配置模式
GPIOA->CRL&=0xFFFFFF0F;
GPIOA->CRL|=0x00000080;
//上拉
GPIOA->ODR|=1<<1;
}
/*
函数功能:函数扫描函数
函数参数: mode=1表示使用连续模式 mode=0使用单击模式
返回值: 2 3 4 5 表示具体的按钮 0表示没有按下
*/
u8 KEY_Scan(u8 mode)
{
static u8 flag=1; //记录上一次按下的状态
if(mode)flag=1;
if(flag&&(KEY_S3==0))
{
flag=0;
delay_ms(20);
if(KEY_S3==0)return 3;
}
else if(KEY_S3)
{
flag=1;
}
return 0;
}
/*
函数功能: LED初始化
硬件连接: PB6 PB7 PB8 PB9
特性: 低电平点亮
*/
void LED_Init(void)
{
//开时钟
RCC->APB2ENR|=1<<3;
//配置GPIO口
GPIOB->CRL&=0x00FFFFFF;
GPIOB->CRL|=0x22000000;
GPIOB->CRH&=0xFFFFFF00;
GPIOB->CRH|=0x00000022;
//上拉
GPIOB->ODR|=1<<6;
GPIOB->ODR|=1<<7;
GPIOB->ODR|=1<<8;
GPIOB->ODR|=1<<9;
}
/*
函数功能: 蜂鸣器初始化
硬件连接: PA6
特性: 高电平响
*/
void BEEP_Init(void)
{
RCC->APB2ENR|=1<<2;
GPIOA->CRL&=0xF0FFFFFF;
GPIOA->CRL|=0x02000000;
}
/*
函数功能: 串口1的初始化
硬件连接: PA9(TX) 和 PA10(RX)
*/
void USART1_Init(u32 baud)
{
/*1. 开时钟*/
RCC->APB2ENR|=1<<14; //USART1时钟
RCC->APB2ENR|=1<<2; //PA
RCC->APB2RSTR|=1<<14; //开启复位时钟
RCC->APB2RSTR&=~(1<<14);//停止复位
/*2. 配置GPIO口的模式*/
GPIOA->CRH&=0xFFFFF00F;
GPIOA->CRH|=0x000008B0;
/*3. 配置波特率*/
USART1->BRR=72000000/baud;
/*4. 配置核心寄存器*/
USART1->CR1|=1<<5; //开启接收中断
STM32_SetPriority(USART1_IRQn,1,1); //设置中断优先级
USART1->CR1|=1<<2; //开启接收
USART1->CR1|=1<<3; //开启发送
USART1->CR1|=1<<13;//开启串口功能
}
/*
函数功能: 串口2的初始化
硬件连接: PA2(TX) 和 PA3(RX)
*/
void USART2_Init(u32 baud)
{
/*1. 开时钟*/
RCC->APB1ENR|=1<<17; //USART2时钟
RCC->APB2ENR|=1<<2; //PA
RCC->APB1RSTR|=1<<17; //开启复位时钟
RCC->APB1RSTR&=~(1<<17);//停止复位
/*2. 配置GPIO口的模式*/
GPIOA->CRL&=0xFFFF00FF;
GPIOA->CRL|=0x00008B00;
/*3. 配置波特率*/
USART2->BRR=36000000/baud;
/*4. 配置核心寄存器*/
USART2->CR1|=1<<5; //开启接收中断
STM32_SetPriority(USART2_IRQn,1,1); //设置中断优先级
USART2->CR1|=1<<2; //开启接收
USART2->CR1|=1<<3; //开启发送
USART2->CR1|=1<<13;//开启串口功能
}
/*
函数功能: 串口3的初始化
硬件连接: PB10(TX) 和 PB11(RX)
*/
void USART3_Init(u32 baud)
{
/*1. 开时钟*/
RCC->APB1ENR|=1<<18; //USART3时钟
RCC->APB2ENR|=1<<3; //PB
RCC->APB1RSTR|=1<<18; //开启复位时钟
RCC->APB1RSTR&=~(1<<18);//停止复位
/*2. 配置GPIO口的模式*/
GPIOB->CRH&=0xFFFF00FF;
GPIOB->CRH|=0x00008B00;
/*3. 配置波特率*/
USART3->BRR=36000000/baud;
/*4. 配置核心寄存器*/
USART3->CR1|=1<<5; //开启接收中断
STM32_SetPriority(USART3_IRQn,1,1); //设置中断优先级
USART3->CR1|=1<<2; //开启接收
USART3->CR1|=1<<3; //开启发送
USART3->CR1|=1<<13;//开启串口功能
}
u8 USART1_RX_BUFFER[USART1_RX_LENGTH]; //保存接收数据的缓冲区
u32 USART1_RX_CNT=0; //当前接收到的数据长度
u8 USART1_RX_FLAG=0; //1表示数据接收完毕 0表示没有接收完毕
//串口1的中断服务函数
void USART1_IRQHandler(void)
{
u8 data;
//接收中断
if(USART1->SR&1<<5)
{
TIM1->CNT=0; //清除计数器
TIM1->CR1|=1<<0; //开启定时器1
data=USART1->DR; //读取串口数据
// if(USART1_RX_FLAG==0) //判断上一次的数据是否已经处理完毕
{
//判断是否可以继续接收
if(USART1_RX_CNT<USART1_RX_LENGTH)
{
USART1_RX_BUFFER[USART1_RX_CNT++]=data;
}
else //不能接收,超出存储范围,强制表示接收完毕
{
USART1_RX_FLAG=1;
}
}
}
}
u8 USART2_RX_BUFFER[USART2_RX_LENGTH]; //保存接收数据的缓冲区
u32 USART2_RX_CNT=0; //当前接收到的数据长度
u8 USART2_RX_FLAG=0; //1表示数据接收完毕 0表示没有接收完毕
//串口2的中断服务函数
void USART2_IRQHandler(void)
{
u8 data;
//接收中断
if(USART2->SR&1<<5)
{
TIM2->CNT=0; //清除计数器
TIM2->CR1|=1<<0; //开启定时器2
data=USART2->DR; //读取串口数据
// if(USART2_RX_FLAG==0) //判断上一次的数据是否已经处理完毕
{
//判断是否可以继续接收
if(USART2_RX_CNT<USART2_RX_LENGTH)
{
USART2_RX_BUFFER[USART2_RX_CNT++]=data;
}
else //不能接收,超出存储范围,强制表示接收完毕
{
USART2_RX_FLAG=1;
}
}
}
}
u8 USART3_RX_BUFFER[USART3_RX_LENGTH]; //保存接收数据的缓冲区
u32 USART3_RX_CNT=0; //当前接收到的数据长度
u8 USART3_RX_FLAG=0; //1表示数据接收完毕 0表示没有接收完毕
//串口3的中断服务函数
void USART3_IRQHandler(void)
{
u8 data;
//接收中断
if(USART3->SR&1<<5)
{
TIM3->CNT=0; //清除计数器
TIM3->CR1|=1<<0; //开启定时器3
data=USART3->DR; //读取串口数据
// if(USART3_RX_FLAG==0) //判断上一次的数据是否已经处理完毕
{
//判断是否可以继续接收
if(USART3_RX_CNT<USART3_RX_LENGTH)
{
USART3_RX_BUFFER[USART3_RX_CNT++]=data;
}
else //不能接收,超出存储范围,强制表示接收完毕
{
USART3_RX_FLAG=1;
}
}
}
}
/*
函数功能: 字符串发送
*/
void USARTx_StringSend(USART_TypeDef *USARTx,char *str)
{
while(*str!='\0')
{
USARTx->DR=*str++;
while(!(USARTx->SR&1<<7)){}
}
}
/*
函数功能: 数据发送
*/
void USARTx_DataSend(USART_TypeDef *USARTx,u8 *data,u32 len)
{
u32 i;
for(i=0;i<len;i++)
{
USARTx->DR=*data++;
while(!(USARTx->SR&1<<7)){}
}
}
//printf函数底层函数接口
int fputc(int c, FILE* stream)
{
USART1->DR=c;
while(!(USART1->SR&1<<7)){}
return c;
}
u8 dht11_temp;
u8 dht11_humidity;
int main()
{
u8 key_val;
u32 time=0;
LED_Init();
BEEP_Init();
KEY_Init();
USART1_Init(115200); //串口1初始化-打印调试信息
//初始化DHT11
DHT11_Init();
while(1)
{
key_val=KEY_Scan(0); //PA1
if(key_val)
{
BEEP=!BEEP;
LED1=!LED1; //PB6
}
delay_ms(5);
time++;
if(time>=10)
{
time=0;
LED2=!LED2; //PB7
//读取温湿度
if(DHT11_Read_Data(&dht11_temp,&dht11_humidity))
{
printf("温度读取失败.\r\n");
}
printf("T:%d,H:%d\r\n",dht11_temp,dht11_humidity);
//湿度大于80以上就关闭插座
if(dht11_humidity>80)
{
LED1=1;
}
}
}
}
3. 设计电路图
3.1 添加DHT11器件
打开Proteus,搜索DHT11元器件。
鼠标选择空白区域,点击鼠标右键,放置电源和GND。
设计好的效果如下:
3.2 添加虚拟串口终端
为了方便查看程序的串口输出,添加一个串口终端显示框。
在虚拟仪表模式下,选择virtual terminal工具,然后在原理图空白区域点击一下就可以放virtual terminal工具。
在绘制原理图的经常遇到连线复杂,或者布线很乱,如果元器件的引脚不方便直接与MCU单片机连接,可以采用标签的形式或者总线方式布线。这里以串口终端演示,采用标签方式连接IO口。
首先在坐标的菜单栏里选择终端模式,然后鼠标点击DEFAULT,然后在原理图的空白区域,点击一下鼠标左键,会出现一个空心的连接线条,将这个连接线条连接到元器件的IO口上就行。
放置好之后,鼠标点击这个接线端子--空心圆圈,弹出对话框,设置连接的IO口。
然后MCU的PA9和PA10的端子上也设置好标签名称。
设置虚拟串口显示器的波特率为:115200
如果在调试仿真时, Virtual Terminal无法自动弹出窗口,可以点击菜单栏的调试,选择恢复弹出窗口。
设置STM32芯片的晶振为:71MHZ
3.3 开始仿真
本文正在参加「金石计划」
相关推荐
- 开发者必看的八大Material Design开源项目
-
MaterialDesign是介于拟物和扁平之间的一种设计风格,自从它发布以来,便引起了很多开发者的关注,在这里小编介绍在Android开发者当中里最受青睐的八个MaterialDesign开源项...
- 另类插这么可爱,一定是…(另类t恤)
-
IT之家(www.ithome.com):另类插图:这么可爱,一定是…OSXMavericks和Yosemite打破了苹果对Mac操作系统传统的命名方式,使用加州的某些标志性景点来替换猫...
- Android常用ADB命令(安卓adb工具是什么)
-
杀死应用①根据包名获取APP的PIDadbshellps|grep应用包名②执行kill命令...
- 微软Mac版PowerPoint测试Reading Order Pane功能
-
IT之家5月20日消息,微软公司昨日(5月19日)发布博文,邀请Microsoft365Insiders成员,测试macOS新版PowerPoint演示文稿应用,重点引入...
- Visual Studio跨平台开发实战(4):Xamarin Android控制项介绍
-
前言不同于iOS,Xamarin在VisualStudio中针对Android,可以直接设计使用者界面.在本篇教学文章中,笔者会针对Android的专案目录结构以及基本控制项进行介绍,包...
- 用云存储30分钟快速搭建APP,你信吗?
-
背景不管你承认与否,移动互联的时代已经到来,这是一个移动互联的时代,手机已经是当今世界上引领潮流的趋势,大型的全球化企业和中小企业都把APP程序开发纳入到他们的企业发展策略当中。但随着手机APP上传的...
- 谷歌P图神器来了!不用学不用教,输入一句话,分分钟给结果
-
Pine发自凹非寺量子位|公众号QbitAI当你拍照片时,“模特不好好配合”怎么办?...
- iOS文本编辑控件UITextField和UITextVie
-
记录一个菜鸟的IOS学习之旅,如能帮助正在学习的你,亦枫不胜荣幸;如路过的大神如指教几句,亦枫感激涕淋!细心的朋友可能已经注意到了,IOS学习之旅系列教程在本篇公众号的文章中,封面已经换成美女图片了,...
- Android入门图文教程集锦(android 入门教程)
-
Android入门视频教程集锦AndroidStudio错误gradientandroid:endXattributenotfound...
- 如何使用Android自定义复合视图(如何使用android自定义复合视图)
-
在最近的一个客户应用中,我遇到了一个需求,根据选定的值来生成指定数量的编辑框字段,这样用户可以输入人物信息。最初我的想法是把这些逻辑放到Fragment中,只是根据选中值的变化来向线性布局容器中增加编...
- 原生安卓开发app的框架frida常用关键代码定位
-
前言有时候可能会对APP进行字符串加密等操作,这样的话你的变量名等一些都被混淆了,看代码就可能无从下手...
- 教程10 | 三分钟搞定一个智能输入法程序
-
一案例描述1、考核知识点网格布局线性布局样式和主题Toast2、练习目标掌握网格布局的使用掌握Toast的使用掌握线性布局的使用...
- (Android 8.1) 功能与新特性(android的功能)
-
和你一起终身学习,这里是程序员AndroidAndroid8.1(API级别27)为用户和开发人员引入了各种新特性和功能。本文档重点介绍了开发人员的新功能。通过本章阅读,您将获取到以下内容:Andr...
- 怎样设置EditText内部文字被锁定不可删除和修改
-
在做项目的时候,我曾经遇到过这样的要求,就是跟百度贴吧客户端上的一样,在回复帖子的时候,在EditText中显示回复人的名字,而且这个名字不可以修改和删除,说白了就是不可操作,只能在后面输入内容。在E...
- 如何阻止 Android 活动启动时 EditText 获得焦点
-
技术背景在Android开发中,当活动启动时,EditText有时会自动获得焦点并弹出虚拟键盘,这可能不是用户期望的行为。为了提升用户体验,我们需要阻止...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
-
- 开发者必看的八大Material Design开源项目
- 另类插这么可爱,一定是…(另类t恤)
- Android常用ADB命令(安卓adb工具是什么)
- 微软Mac版PowerPoint测试Reading Order Pane功能
- Visual Studio跨平台开发实战(4):Xamarin Android控制项介绍
- 用云存储30分钟快速搭建APP,你信吗?
- 谷歌P图神器来了!不用学不用教,输入一句话,分分钟给结果
- iOS文本编辑控件UITextField和UITextVie
- Android入门图文教程集锦(android 入门教程)
- 如何使用Android自定义复合视图(如何使用android自定义复合视图)
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)