Use Proteus para simular STM32 para realizar la detección de temperatura y humedad DHT11

1. Introducción

Proteus es una herramienta EDA (software de simulación) famosa en el Reino Unido. Desde el diseño esquemático, la depuración de código hasta la cosimulación de MCU y circuitos periféricos, el cambio con un solo clic al diseño de PCB, realmente realiza el diseño completo desde el concepto hasta el producto. Es la única plataforma de diseño en el mundo que integra software de simulación de circuitos, software de diseño de PCB y software de simulación de modelo virtual.Su modelo de procesador admite 8051, HC11, PIC10/12/16/18/24/30/DSPIC33, AVR, ARM, 8086 y MSP430, etc. En 2010, se agregaron los procesadores de las series Cortex y DSP, y se continuaron agregando otras series de modelos de procesadores. En términos de compilación, también admite varios compiladores como IAR, Keil y MATLAB. El artículo anterior presentó la descarga, la instalación, el establecimiento del proyecto y la finalización de la operación de simulación de la lámpara LED de Proteus. Este artículo agrega impresión de puerto serie y detección de temperatura y humedad DHT11 sobre esta base.

2. Procedimiento de diseño

Primero use el software keil para diseñar el programa, luego genere el archivo HEX, espere a que se diseñe el diagrama esquemático y luego realice la prueba de simulación.

Nota: El chip utilizado actualmente es STM32F103. La versión de Proteus es 8.9

imagen-20220524143658130

 #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. Diseñar el esquema del circuito

3.1 Agregar dispositivo DHT11

Abra Proteus y busque los componentes DHT11.

imagen-20220524143937845

Seleccione un área en blanco con el mouse, haga clic con el botón derecho del mouse y coloque la fuente de alimentación y GND.

imagen-20220524144345503

El efecto diseñado es el siguiente:

imagen-20220524144418613

3.2 Añadir un terminal de puerto serie virtual

Para ver la salida del puerto serie del programa convenientemente, agregue un cuadro de visualización de terminal de puerto serie.

En el modo de instrumento virtual, seleccione virtual terminalla herramienta y luego haga clic en el área en blanco del diagrama esquemático para colocar virtual terminalla herramienta.

imagen-20220524145332372

Al dibujar el diagrama esquemático, a menudo se encuentra que el cableado es complicado o está muy desordenado.Si los pines de los componentes no son convenientes para conectarse directamente al microcontrolador MCU, puede usar la forma de etiquetas o cableado de bus. . Aquí, el terminal del puerto serie se usa para la demostración y el puerto IO se conecta mediante el etiquetado.

Primero seleccione el modo de terminal en la barra de menú de coordenadas, luego haga clic con el mouse DEFAULTy luego haga clic con el botón izquierdo del mouse en el área en blanco del diagrama esquemático, aparecerá una línea de conexión hueca, simplemente conecte esta línea de conexión al puerto IO del componente

imagen-20220524150145275

imagen-20220524145810211

Después de colocarlo, haga clic en el terminal con el mouse: el círculo hueco y aparecerá un cuadro de diálogo para configurar el puerto IO conectado.

imagen-20220524150321956

Luego configure el nombre de la etiqueta en los terminales PA9 y PA10 de la MCU.

imagen-20220524151328570

Establezca la velocidad en baudios de la pantalla serial virtual: 115200

imagen-20220524150744558

Si Virtual Terminal no puede abrir automáticamente una ventana al depurar la simulación, puede hacer clic en Depurar en la barra de menú y elegir restaurar la ventana emergente.

imagen-20220524151628962

Ajuste el oscilador de cristal del chip STM32 a: 71MHZ

imagen-20220524155200053

3.3 Iniciar la simulación

imagen-20220524155229691

imagen-20220524155252228

imagen-20220524155723214

imagen-20230326205221523

Este artículo está participando en el "Proyecto Piedra Dorada"

Supongo que te gusta

Origin juejin.im/post/7214854106179928119
Recomendado
Clasificación