主頁 >  其他 > 物聯網系列②——使用ESP8266與STM32進行物聯網開發板設計

物聯網系列②——使用ESP8266與STM32進行物聯網開發板設計

2021-02-19 16:24:31 其他

物聯網系列②——使用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供電

(2)STM32F103RCT6

(3)CAN

(4)DHT11

(5)0.96 OLED

(6)DS1302

(7)EEPROM

(8)程式下載介面


(9)3按鍵

(10)LED

(11)ESP12-F(紅框處存在問題)

(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部分):

2、電路板展示



三、目前實作的功能

因平時時間有限,僅實作以下功能:

  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;t<len;t++)
	{
		temp=(num/oled_pow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				OLED_ShowChar(x+(size/2)*t,y,' ');
				continue;
			}else enshow=1; 
		 	 
		}
	 	OLED_ShowChar(x+(size/2)*t,y,temp+'0'); 
	}
} 

/**
  * @brief 顯示一個字符號串
	* @param 
	* @retval None
  */
void OLED_ShowString(uint8_t x,uint8_t y,char *chr)
{
	unsigned char j=0;
	while(chr[j]!='\0')
	{		OLED_ShowChar(x,y,chr[j]);
			x+=8;
		if(x>120){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<y1;y++)
	{
		OLED_Set_Pos(x0,y);
    for(x=x0;x<x1;x++)
	    {      
	    	OLED_WR_Byte(BMP[j++],OLED_DATA);	    	
	    }
	}
} 

/**
  * @brief 初始化SSD1306
	* @param None
	* @retval None
  */				    
void OLED_InitConfig(void)
{ 	
  OLED_RST_Set();
	delay_mms(100);
	OLED_RST_Clr();
	delay_mms(100);
	OLED_RST_Set(); 
					  
	OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
	OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
	OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
	OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
	OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness
	OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
	OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	OLED_WR_Byte(0x00,OLED_CMD);//-not offset
	OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
	OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
	OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
	OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
	OLED_WR_Byte(0x12,OLED_CMD);
	OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
	OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
	OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
	OLED_WR_Byte(0x02,OLED_CMD);//
	OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
	OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
	OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
	OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) 
	OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
	
	OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/ 
	OLED_Clear();
	OLED_Set_Pos(0,0); 	
}  

/*****************************************************/
/*        以下為IOT OLED顯示演算法部分                 */
/*****************************************************/
//系統開機界面
void OLED_StartDesk(void)
{
	OLED_Clear();
	OLED_ShowString(16, 0, "IOT Loading.");
	OLED_DrawBMP(40,2,88,8,logo);
	delay_s(3);
	OLED_ShowString(16, 0, "IOT Loading..");
	delay_s(3);
  OLED_ShowString(16, 0, "IOT Loading...");
	delay_s(2);
	OLED_Clear();
}
//無線資訊獲取界面
void OLED_WIFI(uint8_t shift)
{
	OLED_ShowString(19, 0, "Information");

	OLED_ShowString(20, 2, "WIFI Name");
	OLED_ShowString(20, 4, "WIFI Password");
	OLED_ShowString(20, 6, "WIFI IP");
	if(shift%3==1)
	{
			OLED_ShowString(0, 4, "->");
	}
	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<WIFI_Name_Length;i++)//顯示無線名稱
     {        
           temp_data =Snum[i];        
			OLED_ShowChar(length+i*8,4,temp_data);
     }
	}
  else//超出1行顯示區域
	{
		for(i=0;i<WIFI_Name_Length;i++)//顯示無線名稱
    {        
           temp_data =Snum[i];        
			OLED_ShowChar(i*8,4,temp_data);
    }
	}
}
//無線密碼顯示
void OLED_WIFI_Password(u16 *Snum)
{
	u8 i=0;
	u8 length=0;
    u32 temp_data =0;
	OLED_ShowString(27, 0, "WIFI Key");
	if(WIFI_Password_Length*8<128)//如果未超出1行顯示空間
	{
		length=(128-WIFI_Password_Length*8)/2;//居中顯示
	   for(i=0;i<WIFI_Password_Length;i++)//顯示無線名稱
     {        
            temp_data =Snum[i];        
			OLED_ShowChar(length+i*8,4,temp_data);
     }
	}
  else//超出1行顯示區域
	{
		for(i=0;i<WIFI_Password_Length;i++)//顯示無線名稱
     {        
            temp_data =Snum[i];        
	 		OLED_ShowChar(i*8,4,temp_data);
     }
	}
}
//無線IP顯示
void OLED_WIFI_IP(u16 *Snum)
{
	u8 i=0;
	u8 length=0;
    u32 temp_data =0;
	OLED_ShowString(27, 0, "WIFI IP");
	if(WIFI_IP_Length*8<128)//如果未超出1行顯示空間
	{
		length=(128-WIFI_IP_Length*8)/2;//居中顯示
	   for(i=0;i<WIFI_IP_Length;i++)//顯示無線名稱
     {        
           temp_data =Snum[i];        
			OLED_ShowChar(length+i*8,4,temp_data);
     }
	}
  else//超出1行顯示區域
	{
		for(i=0;i<WIFI_Password_Length;i++)//顯示無線名稱
     {        
            temp_data =Snum[i];        
			OLED_ShowChar(i*8,4,temp_data);
     }
	}
}
//用戶設定界面
void OLED_AskDesk(uint8_t shift)
{
	OLED_ShowString(36, 0, "Setting");

	OLED_ShowString(30, 2, "Cread WIFI");
	OLED_ShowString(30, 4, "Connect WIFI");
	//OLED_ShowString(30, 6, "Send Data");
	if((shift+1)%2==1)
	{
			OLED_ShowString(10, 2, "->");
	}
	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 <string.h>
#include <stdio.h> 
#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<Len;t++)
	{
		AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(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<Len;t++)
	{
		temp<<=8;
		temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1); 	 				   
	}
	return temp;												    
}
//檢查AT24CXX是否正常
//這里用了24XX的最后一個地址(255)來存盤標志字.
//如果用其他24C系列,這個地址要修改
//回傳1:檢測失敗
//回傳0:檢測成功
u8 AT24CXX_Check(void)
{
	u8 temp;
	temp=AT24CXX_ReadOneByte(255);//避免每次開機都寫AT24CXX			   
	if(temp==0X55)return 0;		   
	else//排除第一次初始化的情況
	{
		AT24CXX_WriteOneByte(255,0X55);
	    temp=AT24CXX_ReadOneByte(255);	  
		if(temp==0X55)return 0;
	}
	return 1;											  
}

//在AT24CXX里面的指定地址開始讀出指定個數的資料
//ReadAddr :開始讀出的地址 對24c02為0~255
//pBuffer  :資料陣列首地址
//NumToRead:要讀出資料的個數
void AT24CXX_Read(u16 ReadAddr,u16 NumToRead,u8 *pBuffer)
{
	while(NumToRead)
	{
		*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);	
		NumToRead--;
	}
}  
//在AT24CXX里面的指定地址開始寫入指定個數的資料
//WriteAddr :開始寫入的地址 對24c02為0~255
//pBuffer   :資料陣列首地址
//NumToWrite:要寫入資料的個數
void AT24CXX_Write(u16 WriteAddr,u16 NumToWrite,u8 *pBuffer)
{
	while(NumToWrite--)
	{
		AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
		WriteAddr++;
		pBuffer++;
	}
}
#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 <stdarg.h>

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 <stdio.h>
#include <user_config.h>

#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<NumToWrite;i++)
  {
    STMFLASH_WriteHalfWord(WriteAddr,pBuffer[i]);
    WriteAddr+=2;//地址增加2
  }
}

//從指定地址開始寫入指定長度的資料
//WriteAddr:起始地址(此地址必須為2的倍數)
//pBuffer:資料指標
//NumToWrite:半字(16位)個數

#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024 //位元組
#else
#define STM_SECTOR_SIZE      2048
#endif 
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K位元組

void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
  u32 secpos;        //扇區地址
  u16 secoff;        //扇區內偏移地址(16位計算)
  u16 secremain;     //扇區內剩余地址(16位字計算)
  u16 i;
  u32 offaddr;       //去掉0x08000000后的地址
  if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(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<secremain;i++)//校驗資料
    {
       if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除
    }
    if(i<secremain)//需要擦除
    {
      STMFLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除整個扇區
      for(i=0;i<secremain;i++) STMFLASH_BUF[i+secoff]=pBuffer[i];//
			STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);
//寫入整個扇區
    }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);
//寫已經擦除了的,直接寫入扇區剩余區間
    if(NumToWrite==secremain)break;//寫入結束了
    else//寫入未結束
    {
       secpos++; //扇區地址增1
       secoff=0; //偏移位置為0
       pBuffer+=secremain;    //指標偏移
       WriteAddr+=secremain; //寫地址偏移
       NumToWrite-=secremain;      //位元組(16位)個數遞減
       if(NumToWrite>(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<NumToRead;i++)
  {
    pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//讀取2個位元組
    ReadAddr+=2;//偏移2個位元組
  }
}
#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作為主控芯片進行資料運算等特點的應用,將會是該開發板的經典應用

不足之處還望各位大佬不吝賜教!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/261036.html

標籤:其他

上一篇:使用java 獲取tomcat服務器埠及應用服務器IP地址

下一篇:信號處理(二)——每位元6dB的信噪比是怎么來的

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more