1. 前言
近年来,随着电子产品的发展,数字时间表的应用在人们的工作和生活中发挥着越来越重要的作用。时间对人们来说总是那么宝贵,忙碌和复杂的工作很容易让人们忘记当前的时间,忘记他们想做什么,当事情不是很重要时,这种遗忘是无害的。然而,遇到重要的事情,暂时的延迟可能会导致一场灾难。
因此,从人们的日常生活到公司办公,从台式电脑到便携式智能手机,都需要标准的时间表。人们要求随时随地快速准确地提醒当前事务,并要求时间表更直观、更可靠、更便宜。这一要求催生了新的时间表。此外,由于更多的社会责任,人们要求设计的产品产生尽可能少的垃圾,消耗尽可能少的能量。因此,人们对时间表有体积小、功耗低的要求。
2. 功能需求
2.1 硬件部分
整个项目在正点原子stm32f103mini在开发板环境下进行。开发板的主控制是stm32f103rct6。
(1)2.8寸tft触摸屏负责显示和交互;
(2)pcf8563t用作时钟计时,并在屏幕上显示日期和时间。
(3)24小时显示时间,上位机支持设备端RTC更新日期和时间信息.
(4)DHT11温湿度传感器检测环境信息,并在液晶屏上显示信息;
(5)使用esp8266作WiFi模块与手机app进行通信;
(6)单片机存储接收到的内容w25Q64内,同时可以在屏幕上指定位置将内容显示出来。内容包括具体日程的文字内容以及日程开始、结束的时间;
(7)使用蜂鸣器并通过syn6288语音芯片合成的语音信息通过喇叭播放实现提醒功能。蜂鸣器和喇叭在议程开始和结束前五分钟提前发出语音提示(可以修改此提醒时间)。
(8)显示屏(横屏显示)应包括: 基本的日期、时间、温度和湿度信息显示,屏幕主体部分通过列表表显示app端接收到的时间表内容;在屏幕上设置一个触摸屏按钮,可以跳过当前或未开始的下一个时间表。
因为2.8英寸显示屏的空间有限,因此在同一屏幕范围内只显示一到两个时间表的具体内容。需要显示时间表的文本内容、开始和结束时间。当时间表即将开始和结束时触发语音提示;时间表结束后,从当前显示位置删除w25Q存储在64上的信息。同时显示下一个待开始的时间表。
显示的文本部分需要能够支持显示16和24个大小的所有字符,包括中文字符果不兼容,可以做成24个大小)。从手机上发送的中文文本信息应存储在屏幕上w25q64内。单片机终端应至少支持15个日程内容的存储。单片机应能够识别具体时间表的时间信息,并根据时间排序判断时间表是否过时,并直接删除过时时间表。删除和跳过时间表不需要触发提示。
(9)语音提示内容: 1:如果您需要开始时间表,请注意时间表。(如果可以阅读手机输入的时间表内容(主要是汉字),则此句改为:下一个时间表:XXX请注意时间。(XXX日程内容))
2:当前日程即将结束
3:连接成功(成功连接)app时播报)
4:连接失败
5:日程已设置(单片机接收手机发送的日程内容)。
(10)每句提示播报前蜂鸣器响一声,响完后停顿一秒再播报。
2.2 软件部分
软件部分主要是控制手机app,手机的app包括可以选择添加时间表的按钮,可以输入信息的文本框,可以将文本框中的内容发送到单片机的按钮。能同时在app查看单片机终端输入的时间表内容,并添加可删除输入内容的按钮。文本框分为三部分,一部分输入文本内容(大约两到八个汉字字符的长度)。)输入开始时间的文本框,输入结束时间的文本框。
2.3 功能总结
(1)STM32采用正点原子mini板。正原子2.8寸tft触摸屏
(2)pcf8563t用作时钟计时,并在屏幕上显示日期和时间
(3)DHT11温湿度传感器检测环境信息,并在液晶屏上显示信息
(4)使用esp8266作WiFi模块与手机app进行通信;
(5)w25Q64 烧录字库,存储字库,存储日程提醒信息 实现思路: 将W25Q64安装FATFS文件系统便于数据存储和读取,读写日程信息和字库信息。
(6)syn6288语音芯片合成的语音信息通过喇叭播放提醒功能
(7)开发手机APP输入提醒日程,单片机接收手机发送的日程内容。 单片机存储接收到的内容w25Q在64内,内容可以在屏幕上指定的位置显示。内容包括具体时间表的文本内容和时间表的开始和结束时间;时间表信息以文件的形式存储,修改、阅读和写作为文件操作。
3. 软件运行效果
打开软件后,输入设备端IP功能操作可在连接成功后进行地址和端口。
查看软件上的页面(即主页)、日志页面、新的日程提醒页面。
软件分为windows桌面版本和Android手机版,以下演示截图windows以桌面版为例。
软件采用QT设计,Qt Creator是跨平台的 Qt IDE, Qt Creator 是 Qt 被 Nokia 收购后推出的新型轻量级集成开发环境(IDE)。此 IDE 可跨平台运行,支持系统包括 Linux(32 位及 64 位)、Mac OS X 以及 Windows。
Qt Creator官网下载地址:Download Qt | Embedded System | Real Time Embedded Systems | Qt
QT所有版本下载地址:Index of /archive/qt
QT环境建设、入门开发专栏: https://blog.csdn.net/xiaolong1126626497/category_11400392.html
(
提醒事件的内容可以填写在此页面上,输入提醒的开始时间和结束时间。提前提醒时,输入后单击新的提醒事件
按钮可以将数据发送到设备端,并在主页上添加数据显示。
4. 通信协议
设备端与软件上位机之间的数据交互协议:
(1) 软件上位机对STM32发送: #update 让STM32发送当前存储的所有日程数据 STM32向上位机返回的数据格式: $update,提前提醒开始时间、结束时间、事件内容(0~59分钟) $update,2022/02/22 13:15,2022/02/23 12:17,吃饭,5
(2)
给STM32发送一条日程数据过去
格式:$add,起始时间,结束时间,事件内容,提前提醒时间(0~59分钟)
$add,2022/02/22 13:15,2022/02/23 12:17,吃饭,5
(3)
给STM32发送校准时间 *20220222131338
(4)
删除STM32上存储的日程数据
$del,起始时间,结束时间,事件内容,提前提醒时间(0~59分钟)
$del,2022/02/22 13:15,2022/02/23 12:17,吃饭,5
5. 测试流程总结
设备端采用ESP8266与上位机进行通信,ESP8266上电初始化为AP+TCP服务器模式,设置固定端口号。
采用电脑或者手机运行APP测试之前,先搜索ESP8266创建的WIFI热点连接上,然后打开软件,在软件里输入ESP8266服务器的IP地址和端口号点击连接,连接成功之后就可以与设备端进行交互。
如果没有设备端,也可以采用网络调试助手与上位机之前交互,测试功能。
6. 硬件部分
6.1 硬件实物
板子的串口正常提示:
6.2 外设硬件连线
(1) ESP8266 WIFI
PB10--->ESP8266-RX
PB11--->ESP8266-TX
3.3v--->VCC
GND---->GND
(2) SYN6628
PA2(TX)---SYN6628-RX
PA3(RX)---SYN6628-TX
3.3v---->VCC
GND----->GND
(3) DHT11 温湿度传感器
PA5 ---->DHT11-OUT
3.3v---->VCC
GND----->GND
剩下的用的硬件是开发板本身自带--正点原子STM32F1战舰V3开发板,硬件连接详情看原理图接口。
6.3 字库创建
6.4 SD卡上存放的字库文件
7. 设备端核心代码及实现思路
如果需要整个工程直接使用,可以去这里获取: https://download.csdn.net/download/xiaolong1126626497/85892788
7.1 字库读取
目前设备端LCD屏字库存放在SD卡上,通过fatfs文件系统读取字模进行显示,这样做的优点: 更换字库方便,直接把SD卡拔出来放在电脑上拷贝字库即可。
void NT35310_DisplayGBKData(u32 x,u32 y,u32 size,u8 *p,u16 c1,u16 c2)
{
FIL fp;
UINT br;
u8 L,H;
u32 Addr;
u16 font_size=size/8*size; //字体占用的点阵码字节大小
u8 *buff=NULL;
H=*p;
L=*(p+1);
if(L<0x7f)L=L-0x40;
else L=L-0x41;
H=H-0x81;
Addr=(190*H+L)*font_size; //中文在字库里的偏移量
buff=malloc(font_size); //使用的堆空间
if(buff==NULL)return;
switch(size)
{
case 16:
if(f_open(&fp,"0:SYSTEM/FONT/GBK16-H.DZK",FA_READ)!=FR_OK)
{
printf("f_open error.\r\n");
}
f_lseek(&fp,Addr);
f_read(&fp,buff,font_size,&br);
f_close(&fp);
break;
case 24:
f_open(&fp,"0:SYSTEM/FONT/GBK24-H.DZK",FA_READ);
f_lseek(&fp,Addr);
f_read(&fp,buff,font_size,&br);
f_close(&fp);
break;
case 32:
break;
}
//显示中文
NT35310_DisplayData(x,y,size,size,buff,c1,c2);
//释放空间
free(buff);
}
void NT35310_DisplayData(u32 x,u32 y,u32 w,u32 h,u8 *p,u16 c1,u16 c2)
{
u16 i,j,x0=x;
u8 data;
u16 colortemp=POINT_COLOR;
for(i=0;i<w/8*h;i++) //取出的模型总字节数
{
data=p[i]; //取出数组里一个字节的数据
for(j=0;j<8;j++)
{
if(data&0x80)Draw_Point(x0,y,c1); //字体颜色
else
{
Draw_Point(x0,y,c2); //背景颜色
}
data<<=1; //继续判断下一位
x0++; //继续画下一个点
}
if(x0-x==w) //判断是否需要换行
{
x0=x;//横坐标归位
y++; //纵坐标自增
}
}
POINT_COLOR=colortemp;
}
7.2 解析ESP8266数据
主函数里通过轮询方式检测,ESP8266是否收到上位机的命令,收到之后进行解析处理
//ESP8266 WIFI 返回的数据
if(USART3_RX_FLAG)
{
USART3_RX_BUFFER[USART3_RX_CNT]='\0';
printf("%s",USART3_RX_BUFFER);
//解析WIFI返回的数据
//如果是校准RTC时间 +IPD,0,15:*20220304220552
if(strstr((char*)USART3_RX_BUFFER,":*"))
{
printf("校准时间.\r\n");
rtc_time_update((char*)USART3_RX_BUFFER);
}
//如果是请求更新提醒
else if(strstr((char*)USART3_RX_BUFFER,"#update"))
{
printf("请求更新事件.\r\n");
update_enev();
}
//如果是新增提醒
//+IPD,0,49:$add,2022/03/04 21:56,2022/03/04 21:56,
else if(strstr((char*)USART3_RX_BUFFER,"$add"))
{
printf("新增提醒事件.\r\n");
add_enev((char*)USART3_RX_BUFFER);
}
//如果是删除某个提醒
//+IPD,0,49:$del,2022/03/04 21:56,2022/03/04 21:56,水水水水,0
else if(strstr((char*)USART3_RX_BUFFER,"$del"))
{
printf("删除某个提醒.\r\n");
del_enev((char*)USART3_RX_BUFFER);
}
USART3_RX_CNT=0;
USART3_RX_FLAG=0;
}
7.3 向SD卡存放事件信息
事件提醒都是存放在SD卡上,以文件的形式存放,上面封装的几个函数里,主要是就是读写文件。
/* 函数功能:从buf里面得到第cnt个逗号所在的位置 返 回 值:0~254,代表逗号所在位置的偏移. 255,代表不存在第cnt个逗号 */ u8 GetCommaOffset(char *buf,u8 cnt) { char *p=buf; while(cnt) { if(*buf==',')cnt--; buf++; } return buf-p; //计算偏移量 } /* 写文件 */ void FATFS_Write(const TCHAR *FileName,const char *WriteBuff) { FIL fp; FRESULT res; UINT cnt; //存放写入成功的数量 /*1. 创建文件*/ res=f_open(&fp,FileName, FA_WRITE | FA_CREATE_ALWAYS); if(res!=0) { printf("%s文件创建失败!\n",FileName); return; } /*2. 写入数据*/ res=f_write(&fp,WriteBuff,strlen(WriteBuff),&cnt); if(res!=0) { printf("%s文件写入失败!\n",FileName); return; } /*3. 关闭文件*/ f_close(&fp); printf("%s文件创建成功,成功写入:%d\n",FileName,cnt); } //提取指定逗号位置的数据 void GetCommaOffsetBuff(char *buf_in,char *buf_out,u8 cnt) { while (cnt) { if (*buf_in == ',')cnt--; if (*buf_in == '\0')break; buf_in++; } while (*buf_in != ',' && *buf_in != '\0') { *buf_out=*buf_in; buf_in++; buf_out++; } *buf_out = '\0'; } //字符串替换 //sub1替换前 sub2替换后字符 void StringSubstitution(char *p,char sub1,char sub2) { while (*p!='\0') { if (*p == sub1) { *p = sub2; } p++; } } //新增提醒 //+IPD,0,49:$add,2022/03/04 21:56,2022/03/04 21:56, void add_enev(char *p) { char buf_out[20]; u8 offset = 0; char *p2; offset = GetCommaOffset(p, 3); p2 = p + offset; printf("%d,%s\r\n", offset, p2); //提取提醒的起始时间 例如:2022/03/04 21:56 GetCommaOffsetBuff(p,buf_out,3); printf("buf_out1=%s\r\n",buf_out); StringSubstitution(buf_out,'/','-'); StringSubstitution(buf_out, ' ','-'); StringSubstitution(buf_out, ':','-'); printf("buf_out2=%s\r\n", buf_out); strcat(buf_out,".ev"); printf("buf_out3=%s\r\n", buf_out); FATFS_Write(buf_out, p2); //更新事件列表 update_event_list(); } //删除某个提醒 //+IPD,0,49:$del,2022/03/04 21:56,2022/03/04 21:56,水水水水,0 void del_enev(char *p) { char buf_out[20]; char dir_file_path[50]; //提取提醒的起始时间 例如:2022/03/04 21:56 GetCommaOffsetBuff(p, buf_out, 3); printf("buf_out1=%s\n", buf_out); StringSubstitution(buf_out, '/', '-'); StringSubstitution(buf_out, ' ', '-'); StringSubstitution(buf_out, ':', '-'); printf("buf_out2=%s\n", buf_out); strcat(buf_out, ".ev"); printf("buf_out3=%s\n", buf_out); //拼接目录名称 sprintf(dir_file_path,"0:/%s",buf_out); //删除文件 if(f_unlink(dir_file_path)==FR_OK) { printf("%s\r\n删除成功.",dir_file_path); } else { printf("%s\r\n删除失败.",dir_file_path); } //更新事件列表 update_event_list(); } //向APP终端更新提醒 //+IPD,0,7:#update /* STM32向上位机返回的数据格式: $update,起始时间,结束时间,事件内容,提前提醒时间(0~59分钟) $update,2022/02/22 13:15,2022/02/23 12:17,吃饭,5 */ u8 update_enev(void) { // u8 buff[]="$update,2022/02/22 13:15,2022/02/23 12:17,吃饭,5"; // ESP8266_ServerSendData(0,buff,strlen((char*)buff)); char *path="0:/"; //目录位置 DIR dir; FIL file; //文件指针 FRESULT res; FILINFO fno; //存放读取的文件信息 char *abs_path=NULL; char cmd[]="$update,"; //请求的命令头 char out_buff[100]; UINT cnt; strcpy(out_buff,cmd); /*1. 打开目录*/ res=f_opendir(&dir,path); if(res!=FR_OK)return res; /*2. 循环读取目录*/ while(1) { res=f_readdir(&dir,&fno); if(fno.fname[0] == 0 || res!=0)break; //printf("文件名称: %s,文件大小: %ld 字节\r\n",fno.fname,fno.fsize); /*过滤目录*/ if(strstr(fno.fname,".ev")) { //申请存放文件名称的长度 abs_path=malloc(strlen(path)+strlen(fno.fname)+1); if(abs_path==NULL)break; strcpy(abs_path,path); strcat(abs_path,"/"); strcat(abs_path,fno.fname); //读取文件数据 f_open(&file,abs_path,FA_READ); f_read(&file,out_buff+strlen(cmd),100,&cnt); free(abs_path); printf("abs_path=%s,读取:%d\r\n",abs_path,cnt); out_buff[cnt+strlen(cmd)]='\0'; //发送给上位机 ESP8266_ServerSendData(0,(u8*)out_buff,strlen((char*)out_buff)); printf("发送:%s\r\n",out_buff); delay_ms(100); } } /*3. 关闭目录*/ f_closedir(&dir); return 0; } //提取数据,存放到全局事件结构体里 //参数: i 索引值 buf_out 源数据内容 void ExtractData(int i,char *buf_out) { //std_to_sec(u16 syear, u8 smon, u8 sday, u8 hour, u8 min) char buf_num[50]; //格式化处理 标签:
传感器归位300pa2pa传感器nt1传感器w25传感器ld11传感器