【开源项目自学笔记】STM32与ESP8266通讯

STM32通过串口与esp8266通讯收发DTH11的温湿度数据


文章目录

  • 系列文章目录
  • 前言
  • 一、pandas是什么?
  • 二、使用步骤
  • 1.引入库
  • 2.读入数据
  • 总结

  • 前言

    这个项目学习了这几块内容:stm32的串口中断,定时器中断,时间片轮询机制,esp8266,DTH11温湿度传感器的使用。对串口通信收发数据的处理。

    设计知识点:串口的格式化打印。DWT内核精确延时。sysTick延时。


    一、DTH11介绍

    1. DTH11是串行接口,单线双向。一次一次完整的数据传输为40bit,高位先出。
    2. 数据格式:8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据 +8bit校验。数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位。
    3. 通讯过程:总线空闲状态一直处于高电平。dth11处于低功耗状态。当主机开始发信号时,要先拉低总线18~30ms,再将总线拉高20~40us,再将总线切换为输入模式,此时总线如果为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉 高80us,准备发送数据,每一bit数据都以50us低电平时隙开始,高电平的长短定了数据位是0还是1。如果读取响应信号为高电平,则DHT11没有 响应,请检查线路是否连接正常.当最后一bit数据传送完毕后,DHT11拉低总线 50us,随后总线由上拉电阻拉高进入空闲状态。
    4. 数字0信号
    5. 数字1信号

    二、DTH11代码

    引脚配置

    static void DHT11_GPIO_Config ( void )
    {		
    	/*定义一个GPIO_InitTypeDef类型的结构体*/
    	GPIO_InitTypeDef GPIO_InitStructure;
    
    	
    	/*开启macDHT11_Dout_GPIO_PORT的外设时钟*/
      macDHT11_Dout_SCK_APBxClock_FUN ( macDHT11_Dout_GPIO_CLK, ENABLE );	
    
    	/*选择要控制的macDHT11_Dout_GPIO_PORT引脚*/															   
      	GPIO_InitStructure.GPIO_Pin = macDHT11_Dout_GPIO_PIN;	
    
    	/*设置引脚模式为通用推挽输出*/
      	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   
    
    	/*设置引脚速率为50MHz */   
      	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
    
    	/*调用库函数,初始化macDHT11_Dout_GPIO_PORT*/
      	GPIO_Init ( macDHT11_Dout_GPIO_PORT, &GPIO_InitStructure );		  
    	
    }

    温湿度读取函数

    通过读取温度度,通过在头文件中定义了一个存储温湿度数据的结构体,这个结构体是局部变量保存在栈中,函数结束会被自动回收。

    typedef struct
    {
        uint8_t  humi_int;        //湿度的整数部分
        uint8_t  humi_deci;         //湿度的小数部分
        uint8_t  temp_int;         //温度的整数部分
        uint8_t  temp_deci;         //温度的小数部分
        uint8_t  check_sum;         //校验和
                             
    } DHT11_Data_TypeDef;

    驱动代码中用到的延时是比较时间戳来进行记时。

    #define DHT11_DELAY_US(us)  CPU_TS_Tmr_Delay_US(us)
    #define DHT11_DELAY_MS(ms)  CPU_TS_Tmr_Delay_MS(ms)
    
    void CPU_TS_TmrInit(void)
    {
        /* 使能DWT外设 */
        DEM_CR |= (uint32_t)DEM_CR_TRCENA;                
    
        /* DWT CYCCNT寄存器计数清0 */
        DWT_CYCCNT = (uint32_t)0u;
    
        /* 使能Cortex-M DWT CYCCNT寄存器 */
        DWT_CR |= (uint32_t)DWT_CR_CYCCNTENA;
    }
    uint32_t CPU_TS_TmrRd(void)
    {        
      return ((uint32_t)DWT_CYCCNT);
    }
    void CPU_TS_Tmr_Delay_US(__IO uint32_t us)
    {
      uint32_t ticks;
      uint32_t told,tnow,tcnt=0;
    
      /* 在函数内部初始化时间戳寄存器, */  
    #if (CPU_TS_INIT_IN_DELAY_FUNCTION)  
      /* 初始化时间戳并清零 */
      CPU_TS_TmrInit();
    #endif
      
      ticks = us * (GET_CPU_ClkFreq() / 1000000);  /* 需要的节拍数 */      
      tcnt = 0;
      told = (uint32_t)CPU_TS_TmrRd();         /* 刚进入时的计数器值 */
    
      while(1)
      {
        tnow = (uint32_t)CPU_TS_TmrRd();  
        if(tnow != told)
        { 
            /* 32位计数器是递增计数器 */    
          if(tnow > told)
          {
            tcnt += tnow - told;  
          }
          /* 重新装载 */
          else 
          {
            tcnt += UINT32_MAX - told + tnow; 
          } 
          
          told = tnow;
    
          /*时间超过/等于要延迟的时间,则退出 */
          if(tcnt >= ticks)break;
        }
      }
    }
    static void DHT11_Mode_IPU(void)
    {
     	  GPIO_InitTypeDef GPIO_InitStructure;
    
    	  	/*选择要控制的macDHT11_Dout_GPIO_PORT引脚*/	
    	  GPIO_InitStructure.GPIO_Pin = macDHT11_Dout_GPIO_PIN;
    
    	   /*设置引脚模式为浮空输入模式*/ 
    	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ; 
    
    	  /*调用库函数,初始化macDHT11_Dout_GPIO_PORT*/
    	  GPIO_Init(macDHT11_Dout_GPIO_PORT, &GPIO_InitStructure);	 
    	
    }
    
    static void DHT11_Mode_Out_PP(void)
    {
     	GPIO_InitTypeDef GPIO_InitStructure;
    
    	 	/*选择要控制的macDHT11_Dout_GPIO_PORT引脚*/															   
      	GPIO_InitStructure.GPIO_Pin = macDHT11_Dout_GPIO_PIN;	
    
    	/*设置引脚模式为通用推挽输出*/
      	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   
    
    	/*设置引脚速率为50MHz */   
      	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    
    	/*调用库函数,初始化macDHT11_Dout_GPIO_PORT*/
      	GPIO_Init(macDHT11_Dout_GPIO_PORT, &GPIO_InitStructure);	 	 
    	
    }
    
    static uint8_t DHT11_ReadByte ( void )
    {
    	uint8_t i, temp=0;
    	
    
    	for(i=0;i<8;i++)    
    	{	 
    		/*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/  
    		while(macDHT11_Dout_IN()==Bit_RESET);
    
    		/*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”,
    		 *通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时 
    		 */
    		DHT11_DELAY_US(40); //延时x us 这个延时需要大于数据0持续的时间即可	   	  
    
    		if(macDHT11_Dout_IN()==Bit_SET)/* x us后仍为高电平表示数据“1” */
    		{
    			/* 等待数据1的高电平结束 */
    			while(macDHT11_Dout_IN()==Bit_SET);
    
    			temp|=(uint8_t)(0x01<<(7-i));  //把第7-i位置1,MSB先行 
    		}
    		else	 // x us后为低电平表示数据“0”
    		{			   
    			temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行
    		}
    	}
    	
    	return temp;
    	
    }
    
    uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
    {  
    	/*输出模式*/
    	DHT11_Mode_Out_PP();
    	/*主机拉低*/
    	macDHT11_Dout_0;
    	/*延时18ms*/
    	DHT11_DELAY_MS(18);
    
    	/*总线拉高 主机延时30us*/
    	macDHT11_Dout_1; 
    
    	DHT11_DELAY_US(30);   //延时30us
    
    	/*主机设为输入 判断从机响应信号*/ 
    	DHT11_Mode_IPU();
    
    	/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/   
    	if(macDHT11_Dout_IN()==Bit_RESET)     
    	{
    		/*轮询直到从机发出 的80us 低电平 响应信号结束*/  
    		while(macDHT11_Dout_IN()==Bit_RESET);
    
    		/*轮询直到从机发出的 80us 高电平 标置信号结束*/
    		while(macDHT11_Dout_IN()==Bit_SET);
    
    		/*开始接收数据*/   
    		DHT11_Data->humi_int= DHT11_ReadByte();
    
    		DHT11_Data->humi_deci= DHT11_ReadByte();
    
    		DHT11_Data->temp_int= DHT11_ReadByte();
    
    		DHT11_Data->temp_deci= DHT11_ReadByte();
    
    		DHT11_Data->check_sum= DHT11_ReadByte();
    
    
    		/*读取结束,引脚改为输出模式*/
    		DHT11_Mode_Out_PP();
    		/*主机拉高*/
    		macDHT11_Dout_1;
    
    		/*检查读取的数据是否正确*/
    		if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci)
    			return SUCCESS;
    		else 
    			return ERROR;
    	}
    	
    	else
    		return ERROR;
    	
    }

     三、esp8266

     1.GPIO初始化,USART初始化,NVIC中断

    static void ESP8266_GPIO_Config ( void )
    {
    	/*定义一个GPIO_InitTypeDef类型的结构体*/
    	GPIO_InitTypeDef GPIO_InitStructure;
    	/* 配置 CH_PD 引脚*/
    	macESP8266_CH_PD_APBxClock_FUN ( macESP8266_CH_PD_CLK, ENABLE ); 
    	GPIO_InitStructure.GPIO_Pin = macESP8266_CH_PD_PIN;	
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
    	GPIO_Init ( macESP8266_CH_PD_PORT, & GPIO_InitStructure );	 
    	/* 配置 RST 引脚*/
    	macESP8266_RST_APBxClock_FUN ( macESP8266_RST_CLK, ENABLE ); 				   
    	GPIO_InitStructure.GPIO_Pin = macESP8266_RST_PIN;	
    	GPIO_Init ( macESP8266_RST_PORT, & GPIO_InitStructure );	 
    }
    static void ESP8266_USART_Config ( void )
    {
    	GPIO_InitTypeDef GPIO_InitStructure;
    	USART_InitTypeDef USART_InitStructure;
    	/* config USART clock */
    	macESP8266_USART_APBxClock_FUN ( macESP8266_USART_CLK, ENABLE );
    	macESP8266_USART_GPIO_APBxClock_FUN ( macESP8266_USART_GPIO_CLK, ENABLE );
    	/* USART GPIO config */
    	/* Configure USART Tx as alternate function push-pull */
    	GPIO_InitStructure.GPIO_Pin =  macESP8266_USART_TX_PIN;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(macESP8266_USART_TX_PORT, &GPIO_InitStructure);  
    	/* Configure USART Rx as input floating */
    	GPIO_InitStructure.GPIO_Pin = macESP8266_USART_RX_PIN;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    	GPIO_Init(macESP8266_USART_RX_PORT, &GPIO_InitStructure);
    	/* USART1 mode config */
    	USART_InitStructure.USART_BaudRate = macESP8266_USART_BAUD_RATE;
    	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    	USART_InitStructure.USART_StopBits = USART_StopBits_1;
    	USART_InitStructure.USART_Parity = USART_Parity_No ;
    	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    	USART_Init(macESP8266_USARTx, &USART_InitStructure);
    	/* 中断配置 */
    	USART_ITConfig ( macESP8266_USARTx, USART_IT_RXNE, ENABLE ); //使能串口接收中断 
    	USART_ITConfig ( macESP8266_USARTx, USART_IT_IDLE, ENABLE ); //使能串口总线空闲中断 	
    	ESP8266_USART_NVIC_Configuration ();
    	USART_Cmd(macESP8266_USARTx, ENABLE);
    }
    static void ESP8266_USART_NVIC_Configuration ( void )
    {
    	NVIC_InitTypeDef NVIC_InitStructure; 
    	
    	
    	/* Configure the NVIC Preemption Priority Bits */  
    	NVIC_PriorityGroupConfig ( macNVIC_PriorityGroup_x );
    
    	/* Enable the USART2 Interrupt */
    	NVIC_InitStructure.NVIC_IRQChannel = macESP8266_USART_IRQ;	 
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_Init(&NVIC_InitStructure);
    
    }

    2.AT命令

    串口收发结构体

    #define RX_BUF_MAX_LEN     1024                                     //最大接收缓存字节数
    
    extern struct  STRUCT_USARTx_Fram                                  //串口数据帧的处理结构体
    {
    	char  Data_RX_BUF [ RX_BUF_MAX_LEN ];
    	
      union {
        __IO u16 InfAll;
        struct {
    		  __IO u16 FramLength       :15;                               // 14:0 
    		  __IO u16 FramFinishFlag   :1;                                // 15 
    	  } InfBit;
      }; 
    	
    } strEsp8266_Fram_Record;
    Data_RX_BUF: 字符数组,用于存储接收的数据帧内容,长度为 RX_BUF_MAX_LEN,即 1024 字节。
    InfAll: 16 位无符号整数,用于访问结构体中所有位字段。
    InfBit: 位字段联合,包含两个成员:
    FramLength: 15 位,表示数据帧的长度,范围为 0-32767。
    FramFinishFlag: 1 位,表示数据帧接收完毕的标志。
    结构体定义后,通过 strEsp8266_Fram_Record 声明一个全局变量,该变量的类型是 STRUCT_USARTx_Fram 结构体,用于在程序中存储和处理串口数据帧。
    
    可以使用该结构体和其成员变量来进行串口数据帧的接收、处理和访问。例如,可以通过 strEsp8266_Fram_Record.Data_RX_BUF 来访问接收到的数据帧内容,通过 strEsp8266_Fram_Record.InfBit.FramLength 来获取数据帧的长度,通过 strEsp8266_Fram_Record.InfBit.FramFinishFlag 来判断数据帧是否接收完毕。

     ESP8266_Cmd是PC端用串口给ESP8266发送数据。发送AT命令并且打印esp8266接收到的数据到PC串口。reply1,reply2为AT指令。

    bool ESP8266_Cmd ( char * cmd, char * reply1, char * reply2, u32 waittime )
    {    
    	strEsp8266_Fram_Record .InfBit .FramLength = 0;               //从新开始接收新的数据包
    
    	macESP8266_Usart ( "%s\r\n", cmd );
    
    	if ( ( reply1 == 0 ) && ( reply2 == 0 ) )                      //不需要接收数据
    		return true;
    	
    	Delay_ms ( waittime );                 //延时
    	
    	strEsp8266_Fram_Record .Data_RX_BUF [ strEsp8266_Fram_Record .InfBit .FramLength ]  = '\0';
    
    	macPC_Usart ( "%s", strEsp8266_Fram_Record .Data_RX_BUF );
      strEsp8266_Fram_Record .InfBit .FramLength = 0;                             //清除接收标志
    	strEsp8266_Fram_Record.InfBit.FramFinishFlag = 0;                             
    	if ( ( reply1 != 0 ) && ( reply2 != 0 ) )
    		return ( ( bool ) strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply1 ) || 
    						 ( bool ) strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply2 ) ); 
     	
    	else if ( reply1 != 0 )
    		return ( ( bool ) strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply1 ) );
    	
    	else
    		return ( ( bool ) strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply2 ) );
    	
    }

     USART_printf是通过调用USART_SendData。

    void USART_printf ( USART_TypeDef * USARTx, char * Data, ... )
    {
    	const char *s;
    	int d;   
    	char buf[16];
    
    	
    	va_list ap;
    	va_start(ap, Data);
    
    	while ( * Data != 0 )     // 判断是否到达字符串结束符
    	{				                          
    		if ( * Data == 0x5c )  //'\'
    		{									  
    			switch ( *++Data )
    			{
    				case 'r':							          //回车符
    				USART_SendData(USARTx, 0x0d);
    				Data ++;
    				break;
    
    				case 'n':							          //换行符
    				USART_SendData(USARTx, 0x0a);	
    				Data ++;
    				break;
    
    				default:
    				Data ++;
    				break;
    			}			 
    		}
    		
    		else if ( * Data == '%')
    		{									  //
    			switch ( *++Data )
    			{				
    				case 's':										  //字符串
    				s = va_arg(ap, const char *);
    				
    				for ( ; *s; s++) 
    				{
    					USART_SendData(USARTx,*s);
    					while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
    				}
    				
    				Data++;
    				
    				break;
    
    				case 'd':			
    					//十进制
    				d = va_arg(ap, int);
    				
    				itoa(d, buf, 10);
    				
    				for (s = buf; *s; s++) 
    				{
    					USART_SendData(USARTx,*s);
    					while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
    				}
    				
    				Data++;
    				
    				break;
    				
    				default:
    				Data++;
    				
    				break;
    				
    			}		 
    		}
    		
    		else USART_SendData(USARTx, *Data++);
    		
    		while ( USART_GetFlagStatus ( USARTx, USART_FLAG_TXE ) == RESET );
    		
    	}
    }

    中断串口服务函数是从ESP8266接收响应。接收到的数据存放到strEsp8266_Fram_Record结构体中。

    extern struct  STRUCT_USARTx_Fram                                  //串口数据帧的处理结构体
    {
    	char  Data_RX_BUF [ RX_BUF_MAX_LEN ];
    	
      union {
        __IO u16 InfAll;
        struct {
    		  __IO u16 FramLength       :15;                               // 14:0 
    		  __IO u16 FramFinishFlag   :1;                                // 15 
    	  } InfBit;
      }; 
    	
    } strEsp8266_Fram_Record;
    
    void macESP8266_USART_INT_FUN ( void )
    {	
    	uint8_t ucCh;
    	
    	if ( USART_GetITStatus ( macESP8266_USARTx, USART_IT_RXNE ) != RESET )
    	{
    		ucCh  = USART_ReceiveData( macESP8266_USARTx );
    		
    		if ( strEsp8266_Fram_Record .InfBit .FramLength < ( RX_BUF_MAX_LEN - 1 ) )                       //预留1个字节写结束符
    			strEsp8266_Fram_Record .Data_RX_BUF [ strEsp8266_Fram_Record .InfBit .FramLength ++ ]  = ucCh;
    
    	}
    	 	 
    	if ( USART_GetITStatus( macESP8266_USARTx, USART_IT_IDLE ) == SET )                                         //数据帧接收完毕
    	{
        strEsp8266_Fram_Record .InfBit .FramFinishFlag = 1;
    		
    		ucCh = USART_ReceiveData( macESP8266_USARTx );                                                              //由软件序列清除中断标志位(先读USART_SR,然后读USART_DR)
    	
    		ucTcpClosedFlag = strstr ( strEsp8266_Fram_Record .Data_RX_BUF, "CLOSED\r\n" ) ? 1 : 0;                   //获取连接状态
    		
      }	
    
    }


    总结

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【开源项目自学笔记】STM32与ESP8266通讯

    发表评论