物聯網系列②——使用ESP8266與STM32進行物聯網開發板設計
- 一、設計目標
- 二、電路設計
- 1、原理圖設計
- 2、電路板展示
- 三、目前實作的功能
- 四、代碼撰寫
- 1、DHT11驅動代碼
- 2、0.96寸OLED顯示代碼(包含驅動)
- 3、EEPROM驅動代碼
- 4、與ESP8266串口通信(包含串口通信驅動)
- 5、DS1302驅動代碼
- 6、讀寫FLASH
- 五、優化方向
一、設計目標
在ESP8266網路服務器的學習程序中,產生了制作一個ESP8266與STM32物聯網開發板的想法,
該開發板具備以下功能:
- 主控芯片:STM32F103RCT6 ,具備CAN,I2C等豐富的外設,同時擁有256KB FLASH,便于代碼的擴展,用作該電路的主控芯片,進行資料處理,實作對各種外設的控制,
- 物聯網芯片:ESP12-F,作為云端與STM32通信的節點,負責資料傳輸,
- LED指示燈:4*LED指示燈,進行系統不同作業狀態指示,如電源指示,系統開啟指示,通訊指示等等,
- 0.96寸OLED:使用4線SPI通信,與按鍵結合進行系統功能設定.通過2.54MM母排與STM32F103RCT6連接,
- 3按鍵:3個按鍵接入STM32,功能分別為:回傳鍵,向下鍵,OK鍵,3按鍵外接一個LED,模擬自帶LED的按鍵模塊效果,
- 溫濕度模塊:DHT11,進行溫濕度采集,資料于OLED和云端顯示
- 實時時鐘:DS1302,用于離線狀態的實時時鐘顯示,于OLED中顯示,使用CR1220為其供電,
- EEPROM:記錄系統運行資料,WIFI賬號密碼等,考慮記錄溫濕度資料,形成大資料
- 串口芯片:CH340,與電腦進行串口通信,同時也是STM32與ESP8266的程式下載埠,通過跳線帽選擇下載程式的目標,分別對其進行程式下載
- CAN通信:使用TJA1050T和RJ45網口進行CAN通信
- 供電:通過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、電路板展示



三、目前實作的功能
因平時時間有限,僅實作以下功能:
-
OLED驅動:
主界面:顯示年月日時分秒等時間資訊,顯示當前溫濕度
選單界面:通過向下鍵移動游標,通過OK鍵對無線賬號名稱密碼設定,時間設定,IO口電平控制,風扇控制等進行選擇,
無線設定界面
時間設定界面
IO口控制界面
風扇控制界面
界面切換演算法 -
DS1302驅動:時間的獲取和設定
-
DHT11驅動:溫濕度資料獲取
-
無線賬號密碼設定和時間設定演算法,通過按鍵操作和OLED顯示屏顯示完成此功能,此部分內容更多的是演算法層面的代碼撰寫
-
IO口電平控制:與4點演算法類似,通過按鍵設定IO口的電平狀態,并反饋于OLED界面
-
風扇控制:通過外接繼電器,通過一個IO口控制繼電器的開關,繼電器接風扇,從而實作IO口對風扇開關的控制,結合了DHT11溫濕度傳感器,可設定溫度高于多少度時風扇自動開啟,如溫度高于25°C時風扇自動開啟,低于25°C時風扇便關閉了,類似智能家居的聯動功能
-
EEPROM驅動:實作對EEPROM資料的寫入和讀取
-
CAN通信:實作兩設備間資料的發送和獲取
-
串口通信:實作與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元左右,若批量大了成本還會大幅度下降,相比某寶功能差不多的物聯網開發板上百元的價格還是有性價比的,但是缺乏了相關資源如教程的配套服務,要完善的功能有以下幾點:
- ESP8266程式下載電路,此前該電路在設計上存在很大問題
- 豐富ESP8266的功能,目前僅與STM32進行串口通信
- 豐富ESP8266與STM32相結合的應用,能發揮ESP8266作為通信節點,STM32作為主控芯片進行資料運算等特點的應用,將會是該開發板的經典應用
不足之處還望各位大佬不吝賜教!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/261036.html
標籤:其他
