物联网系列②——使用ESP8266与STM32进行物联网开发板设计

物联网系列②——使用ESP8266与STM32进行物联网开发板设计

  • 一、设计目标
  • 二、电路设计
    • 1、原理图设计
    • 2、电路板展示
  • 三、目前实现的功能
  • 四、代码编写
    • 1、DHT11驱动代码
    • 2、0.96寸OLED显示代码(包含驱动)
    • 3、EEPROM驱动代码
    • 4、与ESP8266串口通信(包含串口通信驱动)
    • 5、DS1302驱动代码
    • 6、读写FLASH
  • 五、优化方向

一、设计目标

      在ESP8266网络服务器的学习过程中,产生了制作一个ESP8266与STM32物联网开发板的想法。
      该开发板具备以下功能:

  1. 主控芯片:STM32F103RCT6 ,具备CAN,I2C等丰富的外设,同时拥有256KB FLASH,便于代码的扩展。用作该电路的主控芯片,进行数据处理,实现对各种外设的控制。
  2. 物联网芯片:ESP12-F,作为云端与STM32通信的节点,负责数据传输。
  3. LED指示灯:4*LED指示灯,进行系统不同工作状态指示,如电源指示,系统开启指示,通讯指示等等。
  4. 0.96寸OLED:使用4线SPI通信,与按键结合进行系统功能设置.通过2.54MM母排与STM32F103RCT6连接。
  5. 3按键:3个按键接入STM32,功能分别为:返回键,向下键,OK键。3按键外接一个LED,模拟自带LED的按键模块效果。
  6. 温湿度模块:DHT11,进行温湿度采集,数据于OLED和云端显示
  7. 实时时钟:DS1302,用于离线状态的实时时钟显示,于OLED中显示。使用CR1220为其供电。
  8. EEPROM:记录系统运行数据,WIFI账号密码等,考虑记录温湿度数据,形成大数据
  9. 串口芯片:CH340,与电脑进行串口通信,同时也是STM32与ESP8266的程序下载端口,通过跳线帽选择下载程序的目标,分别对其进行程序下载
  10. CAN通信:使用TJA1050T和RJ45网口进行CAN通信
  11. 供电:通过TPS54231将12V转至5V,通过LM1117 将5V转3.3V 。使用DC 5.5*2.5mm接口供电,同时可使用Mico USB供电

二、电路设计

1、原理图设计

(1)供电:12V转5V,5V转3.3V,STM32供电
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第1张图片
(2)STM32F103RCT6
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第2张图片
(3)CAN
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第3张图片
(4)DHT11
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第4张图片
(5)0.96 OLED
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第5张图片
(6)DS1302
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第6张图片
(7)EEPROM
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第7张图片
(8)程序下载接口
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第8张图片
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第9张图片
(9)3按键
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第10张图片
(10)LED
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第11张图片
(11)ESP12-F(红框处存在问题)
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第12张图片
(12)CH340
      这部分电路存在问题,电路图不在此处放出,仅介绍思路。要通过USB接口对STM32进行串口程序下载,需通过CH340芯片和三极管对boot0引脚进行电平操作。那是否可以通过ESP8266与STM32共用一个CH340芯片进行程序下载?通过排针和跳线帽选择要下载程序的芯片,这样成本不就降低了吗?
      电路板打板回来焊接测试后,发现可以使用这个方案对STM32进行程序下载,但是ESP8266的程序总是下载不成功。经过一段时间的排查发现,在下载电路的设计上存在一个很大的问题!在设计时对ESP8266下载电路缺乏足够的认识,也没去看该芯片的规格书,以为将TX,RX接入CH340芯片即可,完全忽略了在下载程序时应该将GPIO0引脚拉低,再将芯片RST才能进入下载模式,这也导致了CH340电路缺少了对GPIO0的电路设计,直接导致了ESP8266无法进行程序下载。在不修改电路的前提下,要对ESP8266进行程序下载只能是通过外接TTL串口下载电路,然后手动将GPIO0电平拉低,再手动按下RST按键,才能成功地进行程序下载。电路板上CH340电路在设计上是完全错误的,未实现设计目标。ESP8266正确的下载电路应如下图所示(未包含STM32部分):
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第13张图片

2、电路板展示

物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第14张图片
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第15张图片
物联网系列②——使用ESP8266与STM32进行物联网开发板设计_第16张图片

三、目前实现的功能

      因平时时间有限,仅实现以下功能:

  1. OLED驱动:
          主界面:显示年月日时分秒等时间信息,显示当前温湿度
          菜单界面:通过向下键移动光标,通过OK键对无线账号名称密码设置,时间设置,IO口电平控制,风扇控制等进行选择。
          无线设置界面
          时间设置界面
          IO口控制界面
          风扇控制界面
          界面切换算法

  2. DS1302驱动:时间的获取和设置

  3. DHT11驱动:温湿度数据获取

  4. 无线账号密码设置和时间设置算法,通过按键操作和OLED显示屏显示完成此功能,此部分内容更多的是算法层面的代码编写

  5. IO口电平控制:与4点算法类似,通过按键设置IO口的电平状态,并反馈于OLED界面

  6. 风扇控制:通过外接继电器,通过一个IO口控制继电器的开关,继电器接风扇,从而实现IO口对风扇开关的控制。结合了DHT11温湿度传感器,可设置温度高于多少度时风扇自动开启,如温度高于25°C时风扇自动开启,低于25°C时风扇便关闭了,类似智能家居的联动功能

  7. EEPROM驱动:实现对EEPROM数据的写入和读取

  8. CAN通信:实现两设备间数据的发送和获取

  9. 串口通信:实现与ESP8266的串口通信
    10.FLASH读写:实现对FLASH的读写,在非程序区域进行读写,实现类似EEPROM功能

四、代码编写

1、DHT11驱动代码

#include "stm32f10x.h"                   //STM32器件寄存器定义头文件,必须包含
#include "Delay.h"
#include "DHT11.h"


u8  HUM_DATA_H_TEST,HUM_DATA_L_TEST,TEMP_DATA_H_TEST,TEMP_DATA_L_TEST,CHECK_DATA_TEST;  //用于数据校验
u8  HUM_DATA_H,HUM_DATA_L,TEMP_DATA_H,TEMP_DATA_L,CHECK_DATA;

//***********************************************************************
//	DMT11数据引脚配置为输出
//***********************************************************************
void DHT11_GPIO_OUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = DHT11_Pin;                                 //使用GPIOC_12引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;             //配置为推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;            
	GPIO_Init(DHT11_Pin_Port, &GPIO_InitStructure);  						 //相关的GPIO口初始化
    GPIO_SetBits(DHT11_Pin_Port, DHT11_Pin);	
}
//***********************************************************************
//	DMT11数据引脚配置为输入
//***********************************************************************
void DHT11_GPIO_IN(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = DHT11_Pin;                             //使用GPIOC_12引脚           
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;                //配置为输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_Init(DHT11_Pin_Port, &GPIO_InitStructure);  				    //相关的GPIO口初始化 
}
//***********************************************************************
//	DMT11 8位数据读取函数
//***********************************************************************
u8 DHT11_DATA_8bit(void)
{	
    u32 count=5000;  //防呆
    u8 data=0,i;
	for(i=0;i<8;i++)
	{
		count =5000;
		while((!DHT11_DATA)&&(count>0)) {count--;}      //等待数据引脚拉高
		
        delay_us(30);                                                       //26-28us表示为0,70us表示为1
		
		if(DHT11_DATA)                                                  //如果此时依然读取到高电平则表示该值为1
		{
            data = (data<<1)+1;	                                    //左移一位	
		}
		else                                                                     //如果此时读到低电平
		{
			data<<=1;	                                                   //左移一位
		}
        count =5000;
      while((DHT11_DATA)&&(count>0)) count--;        //等待数据引脚拉低,即一位数据读取完毕				
	}
    return data;
}
//***********************************************************************
//	DMT11数据读取函数
//***********************************************************************
void DHT11_DATA_READ(void)
{               
	DHT11_GPIO_OUT();               //设置为输出
	DHT11_DATA_CLR;                 //拉低数据线,发出开始标志
    delay_mms(20);                  //总线拉低后必须至少18ms,确保DHT11能检测到起始信号
    DHT11_DATA_SET;                 //拉高并进行延时等待
    delay_us(20);                   //延迟20-40us,等待响应信号
	DHT11_GPIO_IN();                //切换为输入模式
	
	delay_us(180);
  //等待80us的响应时间结束
  //等待80us的拉高时间结束
  //开始接收数据
	HUM_DATA_H_TEST = DHT11_DATA_8bit();
    HUM_DATA_L_TEST = DHT11_DATA_8bit();
	TEMP_DATA_H_TEST = DHT11_DATA_8bit();
	TEMP_DATA_L_TEST = DHT11_DATA_8bit();
	CHECK_DATA_TEST = DHT11_DATA_8bit();
		
	DHT11_GPIO_OUT();
	
	//数据校验
	CHECK_DATA = (HUM_DATA_H_TEST + HUM_DATA_L_TEST + TEMP_DATA_H_TEST + TEMP_DATA_L_TEST);
	if(CHECK_DATA == CHECK_DATA_TEST)
	{
		HUM_DATA_H = HUM_DATA_H_TEST;
		HUM_DATA_L = HUM_DATA_L_TEST;
		TEMP_DATA_H = TEMP_DATA_H_TEST;
		TEMP_DATA_L = TEMP_DATA_L_TEST;
	}
}


#ifndef __DHT11_H
#define	__DHT11_H
#include "user_define.h"
#include "Delay.h"
/*温湿度传感器接口定义*/

#define DHT11_Pin               GPIO_Pin_11
#define DHT11_Pin_Port          GPIOB
#define RCC_DHT11               RCC_APB2Periph_GPIOB
#define DHT11_DATA_CLR	        GPIO_ResetBits(DHT11_Pin_Port, DHT11_Pin)        //DATA置低
#define DHT11_DATA_SET	        GPIO_SetBits(DHT11_Pin_Port, DHT11_Pin)           //DATA置高
#define DHT11_DATA              GPIO_ReadInputDataBit(DHT11_Pin_Port, DHT11_Pin) //DATA输入


extern u8 HUM_DATA_H;
extern u8 HUM_DATA_L;
extern u8 TEMP_DATA_H;
extern u8 TEMP_DATA_L;
extern u8 CHECK_DATA;

void DHT11_DATA_READ(void);
#endif

2、0.96寸OLED显示代码(包含驱动)

#include "oled.h"
#include "user_config.h"
#include "bmp.h"        //图库
#include "oledfont.h"  	//字库

char WIFI_NAME_Enter[10]={0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A};//输入密码存储,默认8个星号
char WIFI_KEY_Enter[10]={0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A};//输入密码存储,默认8个星号
char FAN_TEMP_Enter[5]={0x2A,0x2A,0x2A};//输入温度数据存储,默认3个星号
u8  time_set[13]={0x30,0x30,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,};   //秒十位,秒个位,分十位,分个位,时十位,时个位,日十位,日个位,月十位,月个位,周,年十位,年个位
u8  time_set1[7];//秒,分,时,日,月,周,年
u8 FAN_STATE_Flag=0;//风扇状态标志位
u8 FAN_TEMP_Control_Flag=0;//风扇温度控制标志位
char sec1,min1,hour1,year1,mon1,dat1;//上一次的时间数值
#if OLED_MODE==1
/**
  * @brief 向SSD1106写入一个字节
	* @param dat:要写入的数据/命令  cmd:数据/命令标志 0,表示命令;1,表示数据
	* @retval None
  */
void OLED_WR_Byte(uint8_t dat,uint8_t cmd)
{
	DATAOUT(dat);	    
	if(cmd)
	  OLED_DC_Set();
	else 
		OLED_DC_Clr();		   
	OLED_CS_Clr();
	OLED_WR_Clr();	 
	OLED_WR_Set();
	OLED_CS_Set();	  
	OLED_DC_Set();	 
} 	    	    
#else

/**
  * @brief 向SSD1106写入一个字节
	* @param dat:要写入的数据/命令 cmd:数据/命令标志 0,表示命令;1,表示数据;
	* @retval None
  */
void OLED_WR_Byte(uint8_t dat,uint8_t cmd)
{	
	uint8_t i;			  
	if(cmd)
	  OLED_DC_Set();
	else 
	  OLED_DC_Clr();		  
	for(i=0;i<8;i++)
	{			  
		OLED_SCLK_Clr();
		if(dat&0x80)
		   OLED_SDIN_Set();
		else 
		   OLED_SDIN_Clr();
		OLED_SCLK_Set();
		dat<<=1;   
	}				 		  
	OLED_DC_Set();   	  
} 
#endif

/**
  * @brief 清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样
	* @param None
	* @retval None
  */
void OLED_Set_Pos(unsigned char x, unsigned char y) 
{ 
	OLED_WR_Byte(0xb0+y,OLED_CMD);
	OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
	OLED_WR_Byte((x&0x0f)|0x01,OLED_CMD); 
}   

/**
  * @brief 开启OLED显示
	* @param None
	* @retval None
  */ 
void OLED_Display_On(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
	OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON
	OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
}

/**
  * @brief 关闭OLED显示    
	* @param None
	* @retval None
  */   
void OLED_Display_Off(void)
{
	OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
	OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF
	OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
}		

/**
  * @brief 清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样
	* @param None
	* @retval None
  */
void OLED_Clear(void)  
{  
	uint8_t i,n;		    
	for(i=0;i<8;i++)  
	{  
		OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)
		OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址
		OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址   
		for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA); 
	}																		 //更新显示
}

/**
  * @brief 在指定位置显示一个字符,包括部分字符
	* @param x:0~127   y:0~63   mode:0,反白显示  1,正常显示	 size:选择字体 16/12 
	* @retval None
  */
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr)
{      	
	unsigned char c=0,i=0;	
	c=chr-' ';														//得到偏移后的值			
	if(x>Max_Column-1)
	{
		x=x;
		y=y+2;
	}
	if(SIZE==16)
	{
		OLED_Set_Pos(x,y);	
		for(i=0;i<8;i++)
		OLED_WR_Byte(D8X16[c*16+i],OLED_DATA);
		OLED_Set_Pos(x,y+1);
		for(i=0;i<8;i++)
		OLED_WR_Byte(D8X16[c*16+i+8],OLED_DATA);
	}
	else if(SIZE==12)
	{	
		OLED_Set_Pos(x,y);	
		for(i=0;i<6;i++)
		OLED_WR_Byte(D12X12[c][i],OLED_DATA);
		OLED_Set_Pos(x,y+1);
		for(i=0;i<6;i++)
		OLED_WR_Byte(D12X12[c][i+6],OLED_DATA);
	}
	else if(SIZE==24)
	{	
		OLED_Set_Pos(x,y);	
		for(i=0;i<12;i++)
		OLED_WR_Byte(D24X24[c][i],OLED_DATA);
		OLED_Set_Pos(x,y+1);
		for(i=0;i<12;i++)
		OLED_WR_Byte(D24X24[c][i+12],OLED_DATA);
	}
	else if(SIZE==8)
	{
		
		OLED_Set_Pos(x,y);
		for(i=0;i<8;i++)
		{
			OLED_WR_Byte(D8X8[c][i],OLED_DATA);
		}
	}
	else if(SIZE==6)
	{
		OLED_Set_Pos(x,y+1);
		for(i=0;i<6;i++)
		{
			OLED_WR_Byte(D6X8[c][i],OLED_DATA);
		}
	}
}
/**
  * @brief 在指定位置显示一个字符,可调节字符大小
	* @param x:0~127   y:0~63   mode:0,反白显示  1,正常显示	 size:选择字体 16/12 
	* @retval None
  */
void OLED_ShowChar_Adjust(uint8_t size,uint8_t x,uint8_t y,uint8_t chr)
{      	
	unsigned char c=0,i=0;	
	c=chr-' ';														//得到偏移后的值			
	if(x>Max_Column-1)
	{
		x=x-127;
		y=y+2;
	}
	if(size==24)
	{	
		OLED_Set_Pos(x,y);	
		for(i=0;i<12;i++)
		OLED_WR_Byte(D24X24[c][i],OLED_DATA);
		OLED_Set_Pos(x,y+1);
		for(i=0;i<12;i++)
		OLED_WR_Byte(D24X24[c][i+12],OLED_DATA);
	}
	else if(size==16)
	{
		OLED_Set_Pos(x,y);	
		for(i=0;i<8;i++)
		OLED_WR_Byte(D8X16[c*16+i],OLED_DATA);
		OLED_Set_Pos(x,y+1);
		for(i=0;i<8;i++)
		OLED_WR_Byte(D8X16[c*16+i+8],OLED_DATA);
	}
	else if(size==12)
	{	
		OLED_Set_Pos(x,y);	
		for(i=0;i<6;i++)
		OLED_WR_Byte(D12X12[c][i],OLED_DATA);
		OLED_Set_Pos(x,y+1);
		for(i=0;i<6;i++)
		OLED_WR_Byte(D12X12[c][i+6],OLED_DATA);
	}
	else if(size==8)
	{
		
		OLED_Set_Pos(x,y);
		for(i=0;i<8;i++)
		{
			OLED_WR_Byte(D8X8[c][i],OLED_DATA);
		}
	}
	else if(size==6)
	{
		OLED_Set_Pos(x,y+1);
		for(i=0;i<6;i++)
		{
			OLED_WR_Byte(D6X8[c][i],OLED_DATA);
		}
	}
}
/**
  * @brief m^n函数
	* @param None
	* @retval None
  */
uint32_t oled_pow(uint8_t m,uint8_t n)
{
	uint32_t result=1;	 
	while(n--)result*=m;    
	return result;
}		

/**
  * @brief 显示2个数字
	* @param x,y :起点坐标
	*				 len :数字的位数,即显示几位有效数字
  *				 size:字体大小
  *				 mode:模式	0,填充模式;1,叠加模式
  *        num:数值(0~4294967295);
	* @retval None
  */ 		  
void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size)
{         	
	uint8_t t,temp;
	uint8_t enshow=0;						   
	for(t=0;t120){x=0;y+=2;}
			j++;
	}
}

/**
  * @brief 显示显示BMP图片
	* @param 显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7
	* @retval None
  */
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
{ 	
 unsigned int j=0;
 unsigned char x,y;
  
  if(y1%8==0) y=y1/8;      
  else y=y1/8+1;
	for(y=y0;y");
	}
	else if(shift%3==2)
	{
		OLED_ShowString(0, 6, "->");
	}
	else if(shift%3==0)
	{
		OLED_ShowString(0, 2, "->");
	}
}
//无线名称显示
void OLED_WIFI_Name(u16 *Snum)
{
	 u8 i=0;
	 u8 length=0;
   u32 temp_data =0;
	OLED_ShowString(27, 0, "WIFI NAME");
	if(WIFI_Name_Length*8<128)//如果未超出1行显示空间
	{
		length=(128-WIFI_Name_Length*8)/2;//居中显示
	   for(i=0;i");
	}
	else if((shift+1)%2==0)
	{
		OLED_ShowString(10, 4, "->");
	}
}
//用户创建一个无线界面
void OLED_Cread_WIFI(uint8_t shift)
{
	OLED_ShowString(24, 0, "Cread WIFI");

	OLED_ShowString(30, 2, "WIFI Name");
	OLED_ShowString(30, 4, "WIFI Key");
	OLED_ShowString(30, 6, "Send Data");
	if((shift+1)%3==1)
	{
			OLED_ShowString(10, 2, "->");
	}
	else if((shift+1)%3==0)
	{
		OLED_ShowString(10, 6, "->");
	}
	else if((shift+1)%3==2)
	{
		OLED_ShowString(10, 4, "->");
	}
}
//用户设置无线名称界面
void OLED_Cread_WIFI_NAME(uint8_t shift1,uint8_t shift2)//shift1向下,shift2确认后选择数字
{
	char temp[1];
	//u8 LAST=0;//之前shift2的数值
	OLED_ShowString(28,0, " WIFI NAME");
	OLED_ShowString(32, 4, WIFI_NAME_Enter);
	OLED_ShowString(32+shift1*8, 4, "_");
	if(shift2!=0)
	{
		if(shift2==10)
		{
			temp[0]=0x30;
			OLED_ShowString(32+shift1*8, 4, "0");
		}
		else
		{
			temp[0]=shift2+0x30;
		  OLED_ShowString(32+shift1*8, 4, temp);
		}
		if(Enter_Over_Flag==1)//如果确认输入
		{
			WIFI_NAME_Enter[shift1]=temp[0];//存储数据
			Enter_Over_Flag=0;//清空标志位
		}
	}
}
//用户设置无线密码界面
void OLED_Cread_WIFI_Password(uint8_t shift1,uint8_t shift2)//shift1向下,shift2确认后选择数字
{
	char temp[1];
	//u8 LAST=0;//之前shift2的数值
	OLED_ShowString(28,0, " WIFI KEY");
	OLED_ShowString(32, 4, WIFI_KEY_Enter);

	OLED_ShowString(32+shift1*8, 4, "_");
	if(shift2!=0)
	{
		if(shift2==10)
		{
			temp[0]=0x30;
			OLED_ShowString(32+shift1*8, 4, "0");
		}
		else
		{
			temp[0]=shift2+0x30;
		  OLED_ShowString(32+shift1*8, 4, temp);
		}
		if(Enter_Over_Flag==1)//如果确认输入
		{
			WIFI_KEY_Enter[shift1]=temp[0];//存储数据
			Enter_Over_Flag=0;//清空标志位
		}
	}
}
void OLED_Send_WIFI_DATA(uint8_t shift)//发送无线命令显示界面
{
	u8 i=0,j=0,h=0;
	for(i=0;i<8;i++)
	{
		if(WIFI_NAME_Enter[i]!=0x2A)
		{
			j++;
		}
		if(WIFI_KEY_Enter[i]!=0x2A)
		{
			h++;
		}
	}
	if(j==8&&h==8)
	{
		//ESP8266_SendData(Send_WIFI_Data,0x00,0x00);//发送无线数据
		//OLED_ShowString(28, 0, "WIFI DATA");
		OLED_ShowString(20, 0, "SEND DATA?");
		OLED_ShowString(28, 4, "YSE");
		OLED_ShowString(76, 4, "NO");
		if((shift+1)%2==1)
	  {
		 	OLED_ShowString(12, 4, "->");
	  }
	  else if((shift+1)%2==0)
	  {
		  OLED_ShowString(60, 4, "->");
	  }
	}
	else
	{
		OLED_ShowString(28, 0, "WIFI DATA");
		OLED_ShowString(28, 4, "DATA Error");
	}
	
}
void OLED_Send_DATA(uint8_t shift)//发送命令显示界面
{
	u8 i;
	OLED_Clear();
	OLED_DrawBMP(40,2,88,8,logo);
	if(shift==2)
	{
		OLED_ShowString(8, 0, "DATA Sending.");
		for(i=0;i<8;i++)
		{
			ESP8266_SendData(Send_WIFI_Name,0x00,WIFI_NAME_Enter[i]);//发送无线名称
			ESP8266_SendData(Send_WIFI_Key,0x00,WIFI_KEY_Enter[i]);//发送无线密码
		}
	}
	else if(shift==1)
	{
		OLED_ShowString(8, 0, "DATA Sending..");
	}
  else if(shift==0)
	{
		OLED_ShowString(8, 0, "DATA Sending...");
	}
}
//主界面显示
void OLED_DESK(void)
{
	OLED_ShowString(48, 0,"DESK");
	OLED_time();//时间
	OLED_Temp_Hum();//温度,湿度
}
//时钟显示
void OLED_time(void)
{
	char sec,min,hour,year,mon,dat;
	
	char data1,data2;//十位,个位
	hour=time[2];//小时
	min=time[1];//分
	sec=time[0];//秒
	year=time[6];//年
	mon=time[4];//月
	dat=time[3];//日
	if((hour1>=10&&hour<10)||(min1>=10&&min<10)||(sec1>=10&&sec<10)||(mon1>=10&&mon<10)||(dat1>=10&&dat<10))
	{
		OLED_Clear();//屏幕刷新
	}
	hour1=hour;
	min1=min;
	sec1=sec;
	year1=year;
	mon1=mon;
	dat1=dat;
	if(hour<10)//个位数
	{
		data1=hour+0x30;
		OLED_ShowString(32, 2,"0");
		OLED_ShowString(40, 2,&data1);
	}
	else
	{
		data1=hour/10;//十位
		data1=data1+0x30;
		OLED_ShowString(32, 2,&data1);
		data2=hour%10;//个位
		data2=data2+0x30;
		OLED_ShowString(40, 2,&data2);
	}
	if(min<10)//个位数
	{
		data1=min+0x30;
	    OLED_ShowString(56, 2,"0");
		OLED_ShowString(64, 2,&data1);
	}
	else
	{
		data1=min/10;//十位
		data1=data1+0x30;
		OLED_ShowString(56, 2,&data1);
		data2=min%10;//个位
		data2=data2+0x30;
		OLED_ShowString(64, 2,&data2);
	}
	if(sec<10)//个位数
	{
		data1=sec+0x30;
		OLED_ShowString(80, 2,"0");
		OLED_ShowString(88, 2,&data1);
	}
	else
	{
		data1=sec/10;//十位
		data1=data1+0x30;
		OLED_ShowString(80, 2,&data1);
		data2=sec%10;//个位
		data2=data2+0x30;
		OLED_ShowString(88, 2,&data2);
	}
	OLED_ShowString(48, 2,":");
	OLED_ShowString(72, 2,":");
	//年月日显示
	data2=year/10+0x30;//十位
	data1=year%10+0x30;//个位
	OLED_ShowString(24, 4,"20");
	OLED_ShowString(40, 4,&data2);
	OLED_ShowString(48, 4,&data1);
	OLED_ShowString(56, 4,".");
	
	data2=mon/10+0x30;//十位
	data1=mon%10+0x30;//个位
    OLED_ShowString(64, 4,&data2);
	OLED_ShowString(72, 4,&data1);
	OLED_ShowString(80, 4,".");
	
	data2=dat/10+0x30;//十位
	data1=dat%10+0x30;//个位
    OLED_ShowString(88, 4,&data2);
	OLED_ShowString(96, 4,&data1);
}
//显示温度,湿度
void OLED_Temp_Hum(void)
{
	u8 i;
	char temp_char[5]={0},Hum_char[3]={0};
	char temp[10]={0x00,0x00,0x0C,0x12,0x12,0x0C,0x00,0x00};//“°”
    u16 temp_t= TEMP_DATA_H%10;
	u16 hum_t = HUM_DATA_H%10;
   temp_char[0]=TEMP_DATA_H/10+0x30;
   temp_char[1]=temp_t+0x30;
   temp_char[2]='.';
   temp_char[3]=TEMP_DATA_L+0x30;
  OLED_ShowString(10,6,temp_char);
	//"°"
	OLED_Set_Pos(42,6);
		for(i=0;i<8;i++)
		{
			OLED_WR_Byte(temp[i],OLED_DATA);
		}
		
	OLED_ShowString(48,6,"C");
    Hum_char[0] = HUM_DATA_H/10+0x30;
    Hum_char[1] = hum_t+0x30;
    OLED_ShowString(100,6,Hum_char);
	OLED_ShowString(116,6,"%");
}
//菜单界面
void OLED_MENU(uint8_t shift)
{
	OLED_ShowString(48, 0, "MENU");
    if((shift+1)%4<=3&&(shift+1)%4>0)
    {
    	OLED_ShowString(30, 2, "WIFI SET");//设置无线
    	OLED_ShowString(30, 4, "Time SET");//设置时间
    	OLED_ShowString(30, 6, "IO Contron");//引脚控制
    	if((shift+1)%4==1)
     	{
	   		OLED_ShowString(10, 2, "->");
	    }
	   else if((shift+1)%4==3)
	   {
	    	OLED_ShowString(10, 6, "->");
	   }
	  else if((shift+1)%4==2)
	  {
	  	OLED_ShowString(10, 4, "->");
	  }
  }
	else
	{
		OLED_ShowString(30, 2, "FAN CONTROL");//设置风扇
		if((shift+1)%4==0)
  	{
	 		OLED_ShowString(10, 2, "->");
	  }
	}
}
//时间设置界面
void OLED_Time_Set(uint8_t shift1,uint8_t shift2)//shift1向下,shift2确认后选择数字
{
   char temp[1];

   char data1,data2;//十位,个位
	OLED_ShowString(32, 0,"Time Set");
	OLED_ShowString(62, 6,"SET");
    data1=time_set[4];data2=time_set[5];OLED_ShowString(32, 2,&data1);OLED_ShowString(40, 2,&data2);//hour
	data1=time_set[2];data2=time_set[3];OLED_ShowString(56, 2,&data1);OLED_ShowString(64, 2,&data2);//min
    data1=time_set[0];data2=time_set[1];OLED_ShowString(80, 2,&data1);OLED_ShowString(88, 2,&data2);//sec

	OLED_ShowString(48, 2,":");
	OLED_ShowString(72, 2,":");
	//年月日显示
	OLED_ShowString(24, 4,"20");
	data1=time_set[11];data2=time_set[12];OLED_ShowString(40, 4,&data1);OLED_ShowString(48, 4,&data2);//year
	OLED_ShowString(56, 4,".");
	data1=time_set[8];data2=time_set[9];OLED_ShowString(64, 4,&data1);OLED_ShowString(72, 4,&data2);//mon
	OLED_ShowString(80, 4,".");
	data1=time_set[6];data2=time_set[7];OLED_ShowString(88, 4,&data1);OLED_ShowString(96, 4,&data2);//day
    data1=time_set[10];OLED_ShowString(116, 4,&data1);//week

  if(shift1==0)      OLED_ShowString(32, 2, "_");
	else if(shift1==1) OLED_ShowString(40, 2, "_");
	else if(shift1==2) OLED_ShowString(56, 2, "_");
	else if(shift1==3) OLED_ShowString(64, 2, "_");
	else if(shift1==4) OLED_ShowString(40, 4, "_");
	else if(shift1==5) OLED_ShowString(48, 4, "_");
	else if(shift1==6) OLED_ShowString(64, 4, "_");
	else if(shift1==7) OLED_ShowString(72, 4, "_");
	else if(shift1==8) OLED_ShowString(88, 4, "_");
	else if(shift1==9) OLED_ShowString(96, 4, "_");
	else if(shift1==10) OLED_ShowString(116, 4, "_");
	else if(shift1==11) OLED_ShowString(46, 6, "->");
	
	if(shift2!=0)
	{
		if(shift2==10)
		{
			temp[0]=0x30;
			if(shift1==0)      OLED_ShowString(32, 2, "0");
	       else if(shift1==1) OLED_ShowString(40, 2, "0");
	       else if(shift1==2) OLED_ShowString(56, 2, "0");
	       else if(shift1==3) OLED_ShowString(64, 2, "0");
	       else if(shift1==4) OLED_ShowString(40, 4, "0");
	       else if(shift1==5) OLED_ShowString(48, 4, "0");
	       else if(shift1==6) OLED_ShowString(64, 4, "0");
	       else if(shift1==7) OLED_ShowString(72, 4, "0");
	       else if(shift1==8) OLED_ShowString(88, 4, "0");
	       else if(shift1==9) OLED_ShowString(96, 4, "0");
	       else if(shift1==10) OLED_ShowString(116, 4, "0");
		}
		else
		{
			temp[0]=shift2+0x30;
			if(shift1==0)      OLED_ShowString(32, 2, temp);
	       else if(shift1==1) OLED_ShowString(40, 2, temp);
	       else if(shift1==2) OLED_ShowString(56, 2, temp);
	       else if(shift1==3) OLED_ShowString(64, 2, temp);
	       else if(shift1==4) OLED_ShowString(40, 4, temp);
	       else if(shift1==5) OLED_ShowString(48, 4, temp);
	       else if(shift1==6) OLED_ShowString(64, 4, temp);
	       else if(shift1==7) OLED_ShowString(72, 4, temp);
	       else if(shift1==8) OLED_ShowString(88, 4, temp);
	       else if(shift1==9) OLED_ShowString(96, 4, temp);
	       else if(shift1==10) OLED_ShowString(116, 4, temp);
		}
		if(Enter_Over_Flag==1)//如果确认输入
		{
			if(shift1==0)      time_set[4]=temp[0];//存储数据
	       else if(shift1==1) time_set[5]=temp[0];//存储数据
	       else if(shift1==2) time_set[2]=temp[0];//存储数据
	       else if(shift1==3) time_set[3]=temp[0];//存储数据
	       else if(shift1==4) time_set[11]=temp[0];//存储数据
	       else if(shift1==5) time_set[12]=temp[0];//存储数据
	       else if(shift1==6) time_set[8]=temp[0];//存储数据
	       else if(shift1==7) time_set[9]=temp[0];//存储数据
	       else if(shift1==8) time_set[6]=temp[0];//存储数据
	       else if(shift1==9) time_set[7]=temp[0];//存储数据
	       else if(shift1==10) time_set[10]=temp[0];//存储数据
			
			Enter_Over_Flag=0;//清空标志位
		}
	}
}
//时钟设置确认界面
void OLED_Time_Set_Check(void)
{
	u8 i,j=0;
	u8 Addr=0x80;
	for(i=0;i<13;i++)
	{
		if(time_set[i]!=0x2A)
		{
			j++;
		}
	}
	if(j==13)//所有数据均已设置好
	{
		time_set1[0]=0x00;//秒
		time_set1[1]=(time_set[2]-0x30)*16+(time_set[3]-0x30);//分
		time_set1[2]=(time_set[4]-0x30)*16+(time_set[5]-0x30);//时
		time_set1[3]=(time_set[6]-0x30)*16+(time_set[7]-0x30);//日
		time_set1[4]=(time_set[8]-0x30)*16+(time_set[9]-0x30);//月
		time_set1[5]=(time_set[10]-0x30)*16;//周
		time_set1[6]=(time_set[11]-0x30)*16+(time_set[12]-0x30);//年
		
		
		Write_Data(0x00,0x8e);      //关闭写保护
        for(i = 0;i<7;i++)
      {
        Write_Data(time_set1[i],Addr);  
        Addr+=2;
      }
      Write_Data(0x80,0x8e);       //开启写保护
	  OLED_ShowString(20, 2, "SET Succeed");
	}
	else
	{
		OLED_ShowString(28, 2, "DATA Error");
	}
}
//IO口设置列表
void IO_Control_Menu(uint8_t shift)
{
	OLED_ShowString(24, 0, "IO Control");

	OLED_ShowString(30, 2, "PC13");//设置PC13
	OLED_ShowString(30, 4, "PC12");//设置PC12
	OLED_ShowString(30, 6, "PB15");//设置PB15
	if((shift+1)%3==1)
	{
			OLED_ShowString(10, 2, "->");
	}
	else if((shift+1)%3==0)
	{
		OLED_ShowString(10, 6, "->");
	}
	else if((shift+1)%3==2)
	{
		OLED_ShowString(10, 4, "->");
	}
	
	 if(GPIO_ReadOutputDataBit(CONTROL_Port1,CONTROL_PinC13)==0)
	{
		OLED_ShowString(84, 2, "OFF");
	}
	else
	{
		OLED_ShowString(84, 2, "ON");
	}
	if(GPIO_ReadOutputDataBit(CONTROL_Port1,CONTROL_PinC12)==0)
	{
		OLED_ShowString(84, 4, "OFF");
	}
	else
	{
		OLED_ShowString(84, 4, "ON");
	}
		 if(GPIO_ReadOutputDataBit(CONTROL_Port2,CONTROL_PinB15)==0)
	{
		OLED_ShowString(84, 6, "OFF");
	}
	else
	{
		OLED_ShowString(90, 6, "ON");
	}
}
//IO口设置
void IO_Control(uint8_t shift1,uint8_t shift2)
{
	OLED_ShowString(24, 0, "IO Control");
	OLED_ShowString(28, 4, "ON");
	OLED_ShowString(76, 4, "OFF");
	if((shift1+1)%3==1)
	{
		OLED_ShowString(48, 2, "PC13");
	}
	else if((shift1+1)%3==0)
	{
		OLED_ShowString(48, 2, "PB15");
	}
	else if((shift1+1)%3==2)
	{
		OLED_ShowString(48, 2, "PC12");
	}

	if((shift2+1)%2==1)
	 {
		 OLED_ShowString(12, 4, "->");
	 }
	 else if((shift2+1)%2==0)
	 {
		 OLED_ShowString(60, 4, "->");
	 }
}
void IO_Control_Data_Set(uint8_t shift1,uint8_t shift2)
{
	OLED_ShowString(20, 2, "SET Succeed");
	if((shift2+1)%2==1)//ON
	{
		 if((shift1+1)%3==1)
	   {
		   CONTROL_PinC13_ON;
	   }
	   else if((shift1+1)%3==0)
	   {
		   CONTROL_PinB15_ON;
	   }
	   else if((shift1+1)%3==2)
	   {
		   CONTROL_PinC12_ON;
	   }
	}
	else if((shift2+1)%2==0)//OFF
	{
		 if((shift1+1)%3==1)
	   {
		   CONTROL_PinC13_OFF;
	   }
	   else if((shift1+1)%3==0)
	   {
		    CONTROL_PinB15_OFF;
	   }
	   else if((shift1+1)%3==2)
	   {
		    CONTROL_PinC12_OFF;
	   }
	}
}
//风扇设置
void Fan_Control(uint8_t shift)
{
	OLED_ShowString(20, 0, "FAN Control");
	OLED_ShowString(30, 2, "ON/OFF");
	OLED_ShowString(30, 4, "Temp");

	if((shift+1)%2==1)
	 {
		 OLED_ShowString(12, 2, "->");
	 }
	 else if((shift+1)%2==0)
	 {
		 OLED_ShowString(12, 4, "->");
	 }
}
void Fan_Control_ON_OFF(uint8_t shift)
{
	OLED_ShowString(20, 0, "FAN Control");
	OLED_ShowString(28, 4, "ON");
	OLED_ShowString(76, 4, "OFF");
	
	if((shift+1)%2==1)
	 {
		 OLED_ShowString(12, 4, "->");
	 }
	 else if((shift+1)%2==0)
	 {
		 OLED_ShowString(60, 4, "->");
	 }
}

void Fan_Control_ON_OFF_Set(uint8_t shift)
{
	OLED_ShowString(20, 2, "SET Succeed");
	if((shift+1)%2==1)//ON
	{
		 FAN_CONTROL_Pin_ON;
		 FAN_STATE_Flag=1;//风扇状态为1表示为开启
	}
	else if((shift+1)%2==0)//OFF
	{
		FAN_CONTROL_Pin_OFF;
		FAN_STATE_Flag=0;//风扇状态为0表示为关闭
	} 
}
void Fan_Control_Temp(uint8_t shift1,uint8_t shift2)
{
	u8 i;
	char temp1[1];
	char temp[10]={0x00,0x00,0x0C,0x12,0x12,0x0C,0x00,0x00};//“°”
    char data1,data2,data3;//十位,个位,分位
	 FAN_TEMP_Control_Flag=0;//风扇温度控制标志位置0,防止设置过程中风扇开启
    OLED_ShowString(20, 0, "FAN Control");
	OLED_ShowString(56, 4, ".");
	//"°"
	OLED_Set_Pos(75,4);	
	for(i=0;i<8;i++)
		{
			OLED_WR_Byte(temp[i],OLED_DATA);
		}
	OLED_ShowString(83,4,"C");
	OLED_ShowString(52,6,"SET");
	
	data1=FAN_TEMP_Enter[0];OLED_ShowString(40, 4, &data1);
	data2=FAN_TEMP_Enter[1];OLED_ShowString(48, 4, &data2);
	data3=FAN_TEMP_Enter[2];OLED_ShowString(64, 4, &data3);
	
	if(shift1==0)      OLED_ShowString(40, 4, "_");
	else if(shift1==1) OLED_ShowString(48, 4, "_");
	else if(shift1==2) OLED_ShowString(64, 4, "_");
	else if(shift1==3) OLED_ShowString(36, 6, "->");
	if(shift2!=0)
	{
		if(shift2==10)
		{
			temp1[0]=0+0x30;
			if(shift1==0)      OLED_ShowString(40, 4, "0");
	       else if(shift1==1) OLED_ShowString(48, 4, "0");
	       else if(shift1==2) OLED_ShowString(64, 4, "0");
		}
		else
		{
			temp1[0]=shift2+0x30;
		    if(shift1==0)      OLED_ShowString(40, 4, temp1);
	       else if(shift1==1) OLED_ShowString(48, 4, temp1);
	       else if(shift1==2) OLED_ShowString(64, 4, temp1);
		}
		if(Enter_Over_Flag==1)//如果确认输入
		{
			FAN_TEMP_Enter[shift1]=temp1[0];//存储数据
			Enter_Over_Flag=0;//清空标志位
		}
	}
}
void Fan_Control_Temp_Set(void)
{
	u8 i,j=0;
	for(i=0;i<3;i++)
	{
		if(FAN_TEMP_Enter[i]!=0x2A)
		{
			j++;
		}
	}
	if(j==3)//所有数据均已设置好
	{
        FAN_TEMP_Control_Flag=1;//风扇温度控制标志位置1
		OLED_ShowString(20, 2, "SET Succeed");
	}
	else
	{
		OLED_ShowString(28, 2, "DATA Error");
	}
}


#ifndef __OLED_H
#define __OLED_H

#include "stdlib.h" 	 
#include "DELAY.h"
#include 
#include  
#include "delay.h"



/**
  * OLED模式设置
	* 0:4线串行模式
	* 1:并行8080模式
  */
#define OLED_MODE 				0
#define SIZE 					  16
#define XLevelL						0x00
#define XLevelH						0x10
#define Max_Column				128
#define Max_Row						64
#define	Brightness				0xFF 
#define X_WIDTH 					128
#define Y_WIDTH 					64	    

#define OLED_CMD  0				//写命令
#define OLED_DATA 1				//写数据

/* 端口引脚宏定义,方便程序移植 */
#define  GPIO_OLED_CLK  				RCC_APB2Periph_GPIOC
#define  GPIO_OLED_SCLK_Pin     GPIO_Pin_7							/* D0 */
#define  GPIO_OLED_PIN_Pin     	GPIO_Pin_8							/* D1 */
#define  GPIO_OLED_RES_Pin     	GPIO_Pin_9							/* RES */
#define  GPIO_OLED_DC_Pin     	GPIO_Pin_10							/* DC */

/* 引脚电平设置 */
/** 
  * 注意:需要配置的有RES、DC、CLK、PIN四个引脚,接线CS可不接,当选模式0的时候要接CS
	*/
/*
#define OLED_CS_Clr()  GPIO_ResetBits(GPIOA,GPIO_Pin_8)	//CS 片选 => 置零或悬空 当选模式0的时候要连
#define OLED_CS_Set()  GPIO_SetBits(GPIOA,GPIO_Pin_8)
*/

#define OLED_RST_Clr() GPIO_ResetBits(GPIOC,GPIO_Pin_9)	//RES RES => 接RES引脚
#define OLED_RST_Set() GPIO_SetBits(GPIOC,GPIO_Pin_9)

#define OLED_DC_Clr() GPIO_ResetBits(GPIOC,GPIO_Pin_10)	//DC  DC  => 接DC引脚 
#define OLED_DC_Set() GPIO_SetBits(GPIOC,GPIO_Pin_10)

/* 使用4线串行接口时使用 */ 
#define OLED_SCLK_Clr() GPIO_ResetBits(GPIOC,GPIO_Pin_7)//CLK D0  => 接D0引脚
#define OLED_SCLK_Set() GPIO_SetBits(GPIOC,GPIO_Pin_7)

#define OLED_SDIN_Clr() GPIO_ResetBits(GPIOC,GPIO_Pin_8)//PIN D1  => 接D1引脚
#define OLED_SDIN_Set() GPIO_SetBits(GPIOC,GPIO_Pin_8)
  
/* PC0~7,作为数据线 */
#define DATAOUT(x) GPIO_Write(GPIOC,x);									//输出 


extern char FAN_TEMP_Enter[5];//风扇温度设置存储
extern u8 FAN_STATE_Flag;//风扇状态标志位
extern u8 FAN_TEMP_Control_Flag;//风扇温度控制标志位

/* OLED控制用函数 */
void OLED_Clear(void);																							  /* OLED清屏 */
void OLED_Display_On(void);                                                                                  /* OLED开 */
void OLED_ShowPosture(void);																				 /* 提示信息 */
void OLED_Display_Off(void);                                                                                 /* OLED关 */
void OLED_InitConfig(void);                                                                                    /* OLED初始化 */
void OLED_WR_Byte(uint8_t dat,uint8_t cmd);	                                                     /* 写字节 */
void OLED_Set_Pos(unsigned char x, unsigned char y);                                        /* 设置坐标 */
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr);                                           /* 显示字符 */
void OLED_ShowChar_Adjust(uint8_t size,uint8_t x,uint8_t y,uint8_t chr);             //可自己设置大小的字符显示
void OLED_ShowString(uint8_t x,uint8_t y, char *p);	                                              /* 显示字符串 */
void OLED_ShowNum(uint8_t x,uint8_t y,u32 num,uint8_t len,uint8_t size);           /* 显示数字 */
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[]);/* 显示图片 */
void Board_MPU_Angle_Show(void);
void Platform_MPU_Angle_Show(void);
void Lipo_Voltage_Display(void);
void DHT11_TemHum_Display(void);
void USB_ON_Dispaly(void);
void USB_OFF_Dispaly(void);

/* OLED显示函数 */
void OLED_StartDesk(void);         //开机画面
void OLED_WIFI(uint8_t shift);     //无线信息获取界面
void OLED_WIFI_Name(u16 *Snum);    //无线名称显示
void OLED_WIFI_Password(u16 *Snum);//无线密码显示
void OLED_WIFI_IP(u16 *Snum);      //无线IP显示
void OLED_AskDesk(uint8_t shift);  //用户询问函数
void OLED_Cread_WIFI(uint8_t shift);//用户创建无线选择界面
void OLED_Cread_WIFI_NAME(uint8_t shift1,uint8_t shift2);//用户设置无线名称界面
void OLED_Cread_WIFI_Password(uint8_t shift1,uint8_t shift2);//shift1向下,shift2确认后选择数字
void OLED_Send_WIFI_DATA(uint8_t shift);//发送无线命令显示界面
void OLED_Send_DATA(uint8_t shift);//发送命令显示界面
void OLED_DESK(void);//主界面显示
void OLED_time(void);//时钟显示
void OLED_Temp_Hum(void);//显示温湿度
void OLED_MENU(uint8_t shift);//菜单界面显示
void OLED_Time_Set(uint8_t shift1,uint8_t shift2);//时钟设置界面
void OLED_Time_Set_Check(void);//时钟设置确认界面
void IO_Control_Menu(uint8_t shift);//IO口设置列表
void IO_Control(uint8_t shift1,uint8_t shift2);//IO口设置开关选择
void IO_Control_Data_Set(uint8_t shift1,uint8_t shift2);//IO口设置数据设置成功界面
void Fan_Control(uint8_t shift);//风扇设置界面
void Fan_Control_ON_OFF(uint8_t shift);//风扇开关设置界面
void Fan_Control_ON_OFF_Set(uint8_t shift);//风扇开关设置成功画面
void Fan_Control_Temp(uint8_t shift1,uint8_t shift2);//风扇温度设置界面
void Fan_Control_Temp_Set(void);风扇温度设置确认界面
#endif  /* __OLED_H */

3、EEPROM驱动代码

#include "24cxx.h" 
#include "DELAY.h"

//初始化IIC接口
void AT24CXX_Init(void)
{
	IIC_Init();
}
//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址  
//返回值  :读到的数据
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{				  
	u8 temp=0;		  	    																 
    IIC_Start();  
	if(EE_TYPE>AT24C16)
	{
		IIC_Send_Byte(0XA0);	   //发送写命令
		IIC_Wait_Ack();
		IIC_Send_Byte(ReadAddr>>8);//发送高地址
		IIC_Wait_Ack();		 
	}else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据 	 

	IIC_Wait_Ack(); 
    IIC_Send_Byte(ReadAddr%256);   //发送低地址
	IIC_Wait_Ack();	    
	IIC_Start();  	 	   
	IIC_Send_Byte(0XA1);           //进入接收模式			   
	IIC_Wait_Ack();	 
    temp=IIC_Read_Byte(0);		   
    IIC_Stop();//产生一个停止条件	    
	return temp;
}
//在AT24CXX指定地址写入一个数据
//WriteAddr  :写入数据的目的地址    
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{				   	  	    																 
    IIC_Start();  
	if(EE_TYPE>AT24C16)
	{
		IIC_Send_Byte(0XA0);	    //发送写命令
		IIC_Wait_Ack();
		IIC_Send_Byte(WriteAddr>>8);//发送高地址
 	}else
	{
		IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据 
	}	 
	IIC_Wait_Ack();	   
    IIC_Send_Byte(WriteAddr%256);   //发送低地址
	IIC_Wait_Ack(); 	 										  		   
	IIC_Send_Byte(DataToWrite);     //发送字节							   
	IIC_Wait_Ack();  		    	   
    IIC_Stop();//产生一个停止条件 
	delay_mms(10);	 
}
//在AT24CXX里面的指定地址开始写入长度为Len的数据
//该函数用于写入16bit或者32bit的数据.
//WriteAddr  :开始写入的地址  
//DataToWrite:数据数组首地址
//Len        :要写入数据的长度2,4
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
{  	
	u8 t;
	for(t=0;t>(8*t))&0xff);
	}												    
}

//在AT24CXX里面的指定地址开始读出长度为Len的数据
//该函数用于读出16bit或者32bit的数据.
//ReadAddr   :开始读出的地址 
//返回值     :数据
//Len        :要读出数据的长度2,4
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)
{  	
	u8 t;
	u32 temp=0;
	for(t=0;t
#ifndef __24CXX_H
#define __24CXX_H
#include "iic.h"   

#define AT24C01		127
#define AT24C02		255
#define AT24C04		511
#define AT24C08		1023
#define AT24C16		2047
#define AT24C32		4095
#define AT24C64	  8191
#define AT24C128	16383
#define AT24C256	32767  

#define EE_TYPE AT24C02
					  
u8 AT24CXX_ReadOneByte(u16 ReadAddr);							//指定地址读取一个字节
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite);		//指定地址写入一个字节
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);//指定地址开始写入指定长度的数据
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len);					//指定地址开始读取指定长度数据
void AT24CXX_Write(u16 WriteAddr,u16 NumToWrite,u8 *pBuffer);	//从指定地址开始写入指定长度的数据
void AT24CXX_Read(u16 ReadAddr,u16 NumToRead,u8 *pBuffer);   	//从指定地址开始读出指定长度的数据

u8 AT24CXX_Check(void);  //检查器件
void AT24CXX_Init(void); //初始化IIC
#endif

4、与ESP8266串口通信(包含串口通信驱动)

#include "usart1.h"
#include 

u8 USART1_Rx_Buff[80];//串口数据存储
u8 USART1_Rx_Length[10];//串口数据存储长度,最多存储9组数据,从USART1_Rx_Length[1]开始记录,USART1_Rx_Length[0]=0
u8 USART1_Rx_Num=0;    //串口数据存储组数
u8 USART_Count=0;//数据存储计数
u8 ESP8266_Data_Flag=0;//串口数据处理标志位

int fputc(int ch, FILE *f)
{
	while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);		
	USART_SendData(USART1, (unsigned char)ch);				
	return ch;
}

void USART1_Config(void)
{
	USART_InitTypeDef USART_InitStructure;

	/* USART1 工作模式配置 */
	USART_InitStructure.USART_BaudRate = 115200;	//波特率设置:115200
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;	//数据位数设置:8位
	USART_InitStructure.USART_StopBits = USART_StopBits_1; 	//停止位设置: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(USART1, &USART_InitStructure);  //初始化USART1
	USART_Cmd(USART1, ENABLE);// USART1使能
	//配置串口中断
	NVIC_InitTypeDef NVIC_InitStrue;
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	// 配置中断优先级
	NVIC_InitStrue.NVIC_IRQChannel=USART1_IRQn;
	NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=0;//优先级0
	NVIC_InitStrue.NVIC_IRQChannelSubPriority=0;//子优先级0
	NVIC_Init(&NVIC_InitStrue);


}

 /*发送一个字节数据*/
 void UART1SendByte(unsigned char SendData)
{	   
        USART_SendData(USART1,SendData);
        while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	    
}  

/*接收一个字节数据*/
unsigned char UART1GetByte(unsigned char* GetData)
{   	   
        if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET)
        {  return 0;//没有收到数据 
		}
        *GetData = USART_ReceiveData(USART1); 
        return 1;//收到数据
}


void USART1_IRQHandler(void)
{
	u8 i;//计数,用于减去之前所有的数据数量总和,算出当前组的数据
  if(USART_GetITStatus(USART1,USART_IT_RXNE))//中断标志
  {
    USART1_Rx_Buff[USART_Count] = USART_ReceiveData(USART1); 
	  if(USART1_Rx_Buff[USART_Count-1]==0x0D)//倒数第二个结束位
	  {
		  if(USART1_Rx_Buff[USART_Count]==0x0A)//倒数第一个结束位
		  {
		  	USART1_Rx_Num++;//数据存储组数+1
		   	i=USART1_Rx_Num-1;
				USART1_Rx_Length[USART1_Rx_Num]=USART_Count;
				while(i!=0)
				{
					USART1_Rx_Length[USART1_Rx_Num]=USART1_Rx_Length[USART1_Rx_Num]-USART1_Rx_Length[i];//减去之前所有的长度
					i--;
				}
				//USART1_Rx_Length[USART1_Rx_Num]=USART_Count-USART1_Rx_Length[USART1_Rx_Num-1];//从0开始计算的个数
		  	ESP8266_Data_Flag=1;//数据需要处理标志位置1
		  	LED3_Toggle();
	  	}
  	}
  	USART_Count++; 
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除中断
	}
}
#ifndef __USART1_H
#define	__USART1_H

#include "stm32f10x.h"
#include 
#include 

#define USART1_Port        GPIOA                   
#define USART1_TX          GPIO_Pin_9               //TX
#define USART1_RX          GPIO_Pin_10				      //RX

extern u8 ESP8266_Data_Flag ;//串口数据处理标志位
extern u8 USART1_Rx_Buff[80];//串口数据存储
extern u8 USART1_Rx_Length[10];//串口数据存储长度,最多存储9组数据,从USART1_Rx_Length[1]开始记录,USART1_Rx_Length[0]=0
extern u8 USART1_Rx_Num;     //串口数据存储组数
extern u8 USART_Count;//数据存储计数

void USART1_Config(void);
void UART1SendByte(unsigned char SendData);
unsigned char UART1GetByte(unsigned char* GetData);
#endif /* __USART1_H */

5、DS1302驱动代码

#include "DS1302.h"
char time[8];//存储时钟信息
u8 rsec,rmin,rhour,rdate,rmonth,rday,ryear;
u8 init_time[7]={0x00,0x22,0x18,0x04,0x10,0x07,0x20};   

void CLK_SDA_IN(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;                        						//定义一个GPIO口初始化结构体
	//RCC_APB2PeriphClockCmd(DS1302_PORT, ENABLE);  				 //GPIOA端口RCC时钟使能,已在别处使能
	GPIO_InitStructure.GPIO_Pin = CLK_SDA_PINS;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;						 //配置成浮空输入模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;           	//传输速率配置成50HZ
	GPIO_Init(DS1302_PORT, &GPIO_InitStructure);                      		//调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO 	
}

void CLK_SDA_OUT(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;                        						//定义一个GPIO口初始化结构体
	//RCC_APB2PeriphClockCmd(DS1302_PORT, ENABLE);  				 //GPIOA端口RCC时钟使能
	GPIO_InitStructure.GPIO_Pin = CLK_SDA_PINS;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;					//配置成推挽输出模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;           	//传输速率配置成50HZ
	GPIO_Init(DS1302_PORT, &GPIO_InitStructure);                      		//调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO 	
}

void Write_Byte(u8 byte_1)
{
  u8 i = 0;
  CLK_SDA_OUT();//SDA配置为输出
  for (i=0;i<8;i++) 
  {
   if((byte_1 & 0x01)) 
   {
     CLK_SDA_SET;
   }
    else
    {
      CLK_SDA_RESET;
    }
   CLK_SCK_SET;  //时钟信号
   CLK_SCK_RESET;
	 byte_1 >>= 1;
  }
}

u8 Read_Byte(void)
{
  u8 i = 0;
  u8 val;
  CLK_SDA_IN();//SDA配置为输入
  for (i=0;i<8;i++) 
  {
    val >>= 1;
    if(GPIO_ReadInputDataBit(DS1302_PORT,CLK_SDA_PINS) == SET)
    {
      val = val| 0x80; 
    }
       
    CLK_SCK_SET;//时钟信号
    CLK_SCK_RESET;  
  }
  return val;
}


void Write_Data (u8 WData,u8 Addr)
{
  CLK_CE_SET; //拉高片选     
  
  Write_Byte(Addr);   //写入地址
  Write_Byte(WData);  //写入数据
  
  //写完后
  CLK_CE_RESET;    //拉低CE 
  CLK_SDA_RESET;   //拉低SDA
}

u8 Read_Data(u8 Addr)
{
  u8 Data =0;

  CLK_CE_SET;       //拉高片选
  
  Write_Byte(Addr);    //写入地址
  Data = Read_Byte();  //读取数据
  
  CLK_CE_RESET;      
  CLK_SDA_RESET;     

  return Data;
}

 
u8 SEC_Read(void)  
{
  rsec = Read_Data(AddrSeconds+1);
  return rsec;
}
u8 MIN_Read(void)  
{
  rmin = Read_Data(AddrMinutes+1);
  return rmin;
}

u8 HOUR_Read(void)  
{
  rhour = Read_Data(AddrHour+1);
  return rhour;
}

u8 DAY_Read(void)  
{
  rdate = Read_Data(AddrDate+1);
  return rdate;
}

u8 MONTH_Read(void)   
{
  rmonth = Read_Data(AddrMonth+1);
  return rmonth;
}

u8 WEEK_Read(void)  
{
  rday = Read_Data(AddrDay+1);
  return rday;
}

u8 YEAR_Read(void)  
{
  ryear = Read_Data(AddrYear+1);
  return ryear;
}


unsigned char BCD(unsigned char bcd)
 {
      unsigned char res;
      res = bcd/16 *10 +bcd%16;
      return res;
 }
 

void init_DS1302(void)
{
    u8 dat;
    u8 i;
    u8 Addr=0x80;
    dat = Read_Data(AddrSeconds+1); 
    if((dat & 0x80)) 
   {
    	Write_Data(0x00,0x8e);     
      for(i = 0;i<7;i++)
      {
        Write_Data(init_time[i],Addr);  
        Addr+=2;
       }
      Write_Data(0x80,0x8e);      
     }
     
}
//获取时间数据
void DS1302_DATA_GET(void)
{
	time[0] = BCD(SEC_Read());
  time[1] = BCD(MIN_Read());
  time[2] = BCD(HOUR_Read());
  time[3] = BCD(DAY_Read());
  time[4] = BCD(MONTH_Read());
  time[5] = BCD(WEEK_Read());
  time[6] = BCD(YEAR_Read());
}

#ifndef __DS1302__H
#define __DS1302__H
#include "DELAY.h"

#define		AddrSeconds		0x80
#define		AddrMinutes		0x82
#define		AddrHour		0x84
#define		AddrDate		0x86
#define		AddrMonth		0x88
#define		AddrDay			0x8A
#define		AddrYear		0x8C
#define DS1302_PORT  (GPIOA)
#define CLK_CE_PORT  (GPIOA)
#define CLK_CE_PINS  (GPIO_Pin_3)
#define CLK_SDA_PORT  (GPIOA)
#define CLK_SDA_PINS  (GPIO_Pin_2)
#define CLK_SCK_PORT  (GPIOA)
#define CLK_SCK_PINS  (GPIO_Pin_1)

#define CLK_SCK_RESET   GPIO_ResetBits(DS1302_PORT, CLK_SCK_PINS)
#define CLK_SCK_SET     GPIO_SetBits(DS1302_PORT, CLK_SCK_PINS)
#define CLK_SDA_RESET   GPIO_ResetBits(DS1302_PORT, CLK_SDA_PINS)
#define CLK_SDA_SET     GPIO_SetBits(DS1302_PORT, CLK_SDA_PINS)
#define CLK_CE_RESET    GPIO_ResetBits(DS1302_PORT, CLK_CE_PINS)
#define CLK_CE_SET      GPIO_SetBits(DS1302_PORT, CLK_CE_PINS)


extern char time[8];//存储时钟信息

void CLK_SDA_IN(void);
void CLK_SDA_OUT(void);
void init_DS1302(void);
void Write_Byte(u8 Wdata);
u8 Read_Byte(void);
void Write_Data (u8 WData,u8 Addr);
u8 Read_Data(u8 Addr);
void Set_Time(u8 Year, u8 Month, u8 Date, u8 Hour, u8 Minutes, u8 Seconds, u8 Day);
u8 SEC_Read(void);
u8 MIN_Read(void);
u8 HOUR_Read(void);
u8 DAY_Read(void);
u8 MONTH_Read(void);
u8 WEEK_Read(void);
u8 YEAR_Read(void);
unsigned char BCD(unsigned char bcd);
void DS1302_DATA_GET(void);
#endif

6、读写FLASH

#include "STM_FLASH.h"
#include "stm32f10x_flash.h"
//#include "Delay.h"

//解锁Flash
void STMFLASH_Unlock(void)
{
  FLASH->KEYR=FLASH_KEY1;
  FLASH->KEYR=FLASH_KEY2;
}

//flash上锁
void STMFLASH_Lock(void)
{
  FLASH->CR|=1<<7;//上锁
}

//得到Flash的状态
u8 STMFLASH_GetStatus(void)
{    
  u32 res;         
  res=FLASH->SR;
  if(res&(1<<0))return 1;        //忙
  else if(res&(1<<2))return 2;  //编程错误
  else if(res&(1<<4))return 3;  //写保护
  return 0; //操作完成
}
//等待操作完成
//time:延时时间
//返回值:状态
u8 STMFLASH_WaitDone(u16 time)
{
	u8 res;
  do
  {
     res=STMFLASH_GetStatus();
     if(res!=1)break;//非忙,无需等待,直接退出
     delay_us(1); time--;             
  }while(time);
  if(time==0)res=0xff;//TIMEOUT
  return res;
}
//擦除页
//paddr:页地址
//返回值:执行情况
u8 STMFLASH_ErasePage(u32 paddr)
{
  u8 res=0;
  res=STMFLASH_WaitDone(0X5FFF);//等待上次操作结束,>20ms
  if(res==0)
  {
    FLASH->CR|=1<<1;//页擦除
    FLASH->AR=paddr;//设置页地址
    FLASH->CR|=1<<6;//开始擦除
    res=STMFLASH_WaitDone(0X5FFF);//等待操作结束,>20ms
    if(res!=1)//非忙
    {
      FLASH->CR&=~(1<<1);//清除页擦除标志
    }
   }
   return res;
}
//在FLASH指定地址写入半字
//faddr:指定地址(此地址必须为2的倍数)
//dat:要写入的数据
//返回值:写入的情况
u8 STMFLASH_WriteHalfWord(u32 faddr, u16 dat)
{
  u8 res;
  res=STMFLASH_WaitDone(0XFF);
  if(res==0)//OK
  {
    FLASH->CR|=1<<0;//编程使能
    *(vu16*)faddr=dat;//写入数据
    res=STMFLASH_WaitDone(0XFF);//等待操作完成
    if(res!=1)//操作成功
    {
      FLASH->CR&=~(1<<0);//清除PG位
    }
   }
  return res;
}
//读取指定地址的半字(16位数据)
//faddr:读地址
//返回值:对应数据
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
  return *(vu16*)faddr;
}


#if STM32_FLASH_WREN  //如果使能了写
//不检查写入
//WriteAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)个数
void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
  u16 i;
  for(i=0;i=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
  STMFLASH_Unlock(); //解锁
  offaddr=WriteAddr-STM32_FLASH_BASE;  //实际偏移地址
  secpos=offaddr/STM_SECTOR_SIZE;      //扇区地址  0~127 for STM32F103RBT6
  secoff=(offaddr%STM_SECTOR_SIZE)/2;  //在扇区内偏移(2个字节为基本单位)
  secremain=STM_SECTOR_SIZE/2-secoff;  //扇区剩余空间大小
  if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围
  while(1)
  {
    STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,
    STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
    for(i=0;i(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
       else secremain=NumToWrite;//下一个扇区可以写完
    }
  };
  STMFLASH_Lock();//上锁
}
#endif

//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)个数
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
{
  u16 i;
  for(i=0;i
#ifndef __STM_FLASH_H__
#define __STM_FLASH_H__
#include "user_Config.h"

//用户根据自己的需求设置容量
#define STM32_FLASH_SIZE    512        //所选STM32的FLASH容量大小
#define STM32_FLASH_WREN     1         //使能FLASH写入(0,不使能;1,使能)

//FLASH起始地址
#define STM32_FLASH_BASE 0x08000000     //STM32 FLASH起始地址

//FLASH解锁键值
#define FLASH_KEY1               0X45670123
#define FLASH_KEY2               0XCDEF89AB

//要写入STM32 FLASH的数组
//#define SIZE sizeof(FLASH_Buffer)                  //数组长度
//#define FLASH_SAVE_ADDR  0X08070000               //设置FLASH 保护地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0x8000000)
#define FLASH_SAVE_ADDR  0X0803FF5C               //设置FLASH 保护地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0x8000000)

void STMFLASH_Unlock(void);                        //FLASH解锁
void STMFLASH_Lock(void);                          //FLASH上锁
u8 STMFLASH_GetStatus(void);                       //获得状态
u8 STMFLASH_WaitDone(u16 time);                    //等待操作结束
u8 STMFLASH_ErasePage(u32 paddr);                  //擦除页
u8 STMFLASH_WriteHalfWord(u32 faddr, u16 dat);     //写入半字
u16 STMFLASH_ReadHalfWord(u32 faddr);              //读出半字 
void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len); //指定地址开始写入指定长度的数据
u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len);                    //指定地址开始读取指定长度的数据
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite);    //从指定地址开始写入指定长度的数据
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead);       //从指定地址开始读出指定长度的数据
#endif

      以上为本开发板一些比较重要的代码,主要是提供一些外设的驱动代码汇总和OLED显示的一些思路。驱动代码都是经过测试的,有需求的可对附加功能进行删减之后直接使用,整个系统完整代码只用于本人私下测试使用,暂不放出。

五、优化方向

      这个电路板成本价在50元左右,若批量大了成本还会大幅度下降,相比某宝功能差不多的物联网开发板上百元的价格还是有性价比的,但是缺乏了相关资源如教程的配套服务。要完善的功能有以下几点:

  1. ESP8266程序下载电路,此前该电路在设计上存在很大问题
  2. 丰富ESP8266的功能,目前仅与STM32进行串口通信
  3. 丰富ESP8266与STM32相结合的应用,能发挥ESP8266作为通信节点,STM32作为主控芯片进行数据运算等特点的应用,将会是该开发板的经典应用

       不足之处还望各位大佬不吝赐教!

你可能感兴趣的:(嵌入式,物联网,单片机,嵌入式,物联网,stm32)