Utilisez Proteus pour simuler STM32 afin de réaliser la détection de température et d'humidité DHT11

1. Introduction

Proteus est un célèbre outil EDA (logiciel de simulation) au Royaume-Uni. De la disposition schématique, du débogage de code à la co-simulation de MCU et de circuits périphériques, en passant par le passage en un clic à la conception de circuits imprimés, il réalise véritablement la conception complète du concept au produit. C'est la seule plate-forme de conception au monde qui intègre un logiciel de simulation de circuit, un logiciel de conception de PCB et un logiciel de simulation de modèle virtuel.Son modèle de processeur prend en charge 8051, HC11, PIC10/12/16/18/24/30/DSPIC33, AVR, ARM, 8086 et MSP430, etc. En 2010, les processeurs des séries Cortex et DSP ont été ajoutés, et d'autres séries de modèles de processeurs ont continué à être ajoutées. En termes de compilation, il prend également en charge divers compilateurs tels que IAR, Keil et MATLAB. L'article précédent a présenté le téléchargement, l'installation, l'établissement du projet et l'achèvement de l'opération de simulation de lampe à LED de Proteus. Cet article ajoute l'impression du port série et la détection de température et d'humidité DHT11 sur cette base.

2. Procédure de conception

Utilisez d'abord le logiciel keil pour concevoir le programme, puis générez le fichier HEX, attendez que le schéma de principe soit conçu, puis effectuez le test de simulation.

Remarque : La puce actuellement utilisée est STM32F103. La version de Proteus est la 8.9

image-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. Concevoir le schéma de circuit

3.1 Ajouter un appareil DHT11

Ouvrez Proteus et recherchez les composants DHT11.

image-20220524143937845

Sélectionnez une zone vide avec la souris, cliquez sur le bouton droit de la souris et placez l'alimentation et GND.

image-20220524144345503

L'effet recherché est le suivant :

image-20220524144418613

3.2 Ajouter un terminal de port série virtuel

Afin de visualiser facilement la sortie du port série du programme, ajoutez une boîte d'affichage de terminal de port série.

En mode instrument virtuel, sélectionnez virtual terminall'outil, puis cliquez sur la zone vide du diagramme schématique pour placer virtual terminall'outil.

image-20220524145332372

Lors du dessin du schéma de principe, on constate souvent que le câblage est compliqué ou que le câblage est très désordonné.Si les broches des composants ne conviennent pas pour être directement connectées au microcontrôleur MCU, vous pouvez utiliser la forme d'étiquettes ou de câblage de bus . Ici, le terminal du port série est utilisé pour la démonstration et le port IO est connecté par étiquetage.

Sélectionnez d'abord le mode terminal dans la barre de menu des coordonnées, puis cliquez sur la souris DEFAULT, puis cliquez sur le bouton gauche de la souris sur la zone vide du schéma, une ligne de connexion creuse apparaîtra, connectez simplement cette ligne de connexion au port IO du composant.

image-20220524150145275

image-20220524145810211

Après l'avoir placé, cliquez sur le terminal avec la souris - le cercle creux, et une boîte de dialogue apparaîtra pour définir le port IO connecté.

image-20220524150321956

Définissez ensuite le nom de l'étiquette sur les bornes PA9 et PA10 du MCU.

image-20220524151328570

Définissez le débit en bauds de l'affichage série virtuel : 115 200

image-20220524150744558

Si Virtual Terminal ne peut pas automatiquement ouvrir une fenêtre lors du débogage de la simulation, vous pouvez cliquer sur Déboguer dans la barre de menus et choisir de restaurer la fenêtre contextuelle.

image-20220524151628962

Réglez l'oscillateur à cristal de la puce STM32 sur : 71 MHZ

image-20220524155200053

3.3 Démarrer la simulation

image-20220524155229691

image-20220524155252228

image-20220524155723214

image-20230326205221523

Cet article participe au "Golden Stone Project"

Je suppose que tu aimes

Origine juejin.im/post/7214854106179928119
conseillé
Classement