Verwenden Sie Proteus, um STM32 zu simulieren, um die DHT11-Temperatur- und Feuchtigkeitserkennung zu realisieren

1. Einleitung

Proteus ist ein bekanntes EDA-Tool (Simulationssoftware) in Großbritannien.Vom schematischen Layout, Code-Debugging bis zur Co-Simulation von MCU und Peripherieschaltungen, Umschalten mit einem Klick zum PCB-Design, es realisiert wirklich das komplette Design vom Konzept bis zum Produkt. Es ist die einzige Designplattform der Welt, die Schaltungssimulationssoftware, PCB-Designsoftware und Simulationssoftware für virtuelle Modelle integriert.Sein Prozessormodell unterstützt 8051, HC11, PIC10/12/16/18/24/30/DSPIC33, AVR, ARM. 8086 und MSP430 usw. Im Jahr 2010 wurden Prozessoren der Cortex- und DSP-Serie hinzugefügt, und es wurden weitere Serien von Prozessormodellen hinzugefügt. Bei der Kompilierung unterstützt es auch verschiedene Compiler wie IAR, Keil und MATLAB. Im vorherigen Artikel wurden der Download, die Installation, die Projekteinrichtung und der Abschluss der LED-Lampensimulation von Proteus vorgestellt. Dieser Artikel fügt auf dieser Basis das Drucken über serielle Anschlüsse und die DHT11-Temperatur- und Feuchtigkeitserkennung hinzu.

2. Entwurfsverfahren

Erst mit der keil-Software das Programm designen, dann HEX-Datei generieren, warten, bis der Schaltplan entworfen ist und dann den Simulationstest durchführen.

Hinweis: Der derzeit verwendete Chip ist STM32F103. Die Proteus-Version ist 8.9

Bild-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. Entwerfen Sie den Schaltplan

3.1 DHT11-Gerät hinzufügen

Öffnen Sie Proteus und suchen Sie nach DHT11-Komponenten.

Bild-20220524143937845

Wählen Sie mit der Maus einen leeren Bereich aus, klicken Sie mit der rechten Maustaste und platzieren Sie die Stromversorgung und GND.

Bild-20220524144345503

Der entworfene Effekt ist wie folgt:

Bild-20220524144418613

3.2 Fügen Sie ein virtuelles serielles Port-Terminal hinzu

Um die Ausgabe des seriellen Anschlusses des Programms bequem anzuzeigen, fügen Sie ein Terminal-Anzeigefeld für den seriellen Anschluss hinzu.

Wählen Sie im virtuellen Instrumentenmodus virtual terminaldas Werkzeug aus und klicken Sie dann auf den leeren Bereich des schematischen Diagramms, um virtual terminaldas Werkzeug zu platzieren.

Bild-20220524145332372

Beim Zeichnen des Schaltplans wird häufig festgestellt, dass die Verdrahtung kompliziert oder sehr chaotisch ist.Wenn die Pins der Komponenten nicht geeignet sind, direkt mit dem MCU-Mikrocontroller verbunden zu werden, können Sie die Form von Etiketten oder Busverdrahtung verwenden . Hier wird der Anschluss des seriellen Ports zur Demonstration verwendet und der IO-Port wird durch Beschriftung verbunden.

Wählen Sie zuerst den Terminalmodus in der Koordinatenmenüleiste, klicken Sie dann mit der Maus DEFAULTund klicken Sie dann mit der linken Maustaste auf den leeren Bereich des schematischen Diagramms. Eine leere Verbindungslinie wird angezeigt. Verbinden Sie diese Verbindungslinie einfach mit dem IO-Port des Bauteils.

Bild-20220524150145275

Bild-20220524145810211

Nachdem Sie es platziert haben, klicken Sie mit der Maus auf das Terminal – den leeren Kreis, und ein Dialogfeld wird angezeigt, um den verbundenen IO-Port festzulegen.

Bild-20220524150321956

Stellen Sie dann den Labelnamen an den Klemmen PA9 und PA10 der MCU ein.

Bild-20220524151328570

Stellen Sie die Baudrate der virtuellen seriellen Anzeige ein: 115200

Bild-20220524150744558

Wenn Virtual Terminal beim Debuggen der Simulation nicht automatisch ein Fenster öffnen kann, können Sie in der Menüleiste auf Debuggen klicken und das Popup-Fenster wiederherstellen.

Bild-20220524151628962

Stellen Sie den Kristalloszillator des STM32-Chips auf: 71 MHz

Bild-20220524155200053

3.3 Starten Sie die Simulation

Bild-20220524155229691

Bild-20220524155252228

Bild-20220524155723214

Bild-20230326205221523

Dieser Artikel nimmt am "Golden Stone Project" teil

Ich denke du magst

Origin juejin.im/post/7214854106179928119
Empfohlen
Rangfolge