物联网系列②——使用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;
/
#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
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
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
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);
}
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
}
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
}
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);
} //更新显示
}
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);
}
}
}
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);
}
}
}
uint32_t oled_pow(uint8_t m,uint8_t n)
{
uint32_t result=1;
while(n--)result*=m;
return result;
}
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');
}
}
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++;
}
}
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);
}
}
}
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);
OLED_Clear();
OLED_Set_Pos(0,0);
}
//系统开机界面
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"
#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
#define GPIO_OLED_PIN_Pin GPIO_Pin_8
#define GPIO_OLED_RES_Pin GPIO_Pin_9
#define GPIO_OLED_DC_Pin GPIO_Pin_10
#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)
#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)
#define DATAOUT(x) GPIO_Write(GPIOC,x); //输出
extern char FAN_TEMP_Enter[5];//风扇温度设置存储
extern u8 FAN_STATE_Flag;//风扇状态标志位
extern u8 FAN_TEMP_Control_Flag;//风扇温度控制标志位
void OLED_Clear(void);
void OLED_Display_On(void);
void OLED_ShowPosture(void);
void OLED_Display_Off(void);
void OLED_InitConfig(void);
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);
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
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;
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
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作为主控芯片进行数据运算等特点的应用,将会是该开发板的经典应用
不足之处还望各位大佬不吝赐教!