基于littlevgl的多窗口程序实现

基于littlevgl的多窗口程序实现工装下载

码已上传,有需自下。
链接:https://pan.baidu.com/s/1U6pCyZzG7Q9MFPxJNqCAhw
提取码:0q6q

前言

因为工作中要给产品下载程序,需要用一个下载器。产品型号很多,每个产品都有一个下载器,导致找下载器用半个小时,下载只需30s。思考了下载器的工作过程,两个步骤,(1)复制flash的内容到产品的flash,(2)给产品mcu下载boot程序。原先之所以用多个下载器是因为每个产品的boot程序不一样,如果要让一个下载器支持多个产品,就需要保存很多boot程序,可是下载器是单片机,容量不够啊。而且,下载器就只有四个按键,和用户交互时,怎么确定产品型号,再下载对应的boot程序呢。在现有的下载器平台上,这些问题不好解决。所以要换平台啊,哈哈哈,聪明不 秉着自己动手丰衣足食的优良传统,我要自己整一个支持所有产品的下载器,再也不想忍受找下载器的痛苦了。

实现思路

1、我在工位发现了一个开发板,GD的一个单片机开发板GD32F450Z-EVAL 2016-10 V1.1,MCU是GD32F450ZKT6。带触摸屏,带SDRAM。性能强悍,资源丰富。
2、首先肯定要是有图形界面的,因为开发板上按键也不多,如果用命令行,那还不如杀了我和其他同事。然后,要使用SD卡,因为要支持所有产品,那么需要保存所有的boot程序,如果后期有新产品,增加boot程序,不希望更改我的程序,而是直接从SD卡读取配置文件和boot程序,实现灵活的调整。最后,已经使用大容量的SD卡,而且文件内容挺多的,我就不想自己管理数据了,就再加一个文件系统吧。
3、综上,使用了图形界面和用户交互,文件系统管理众多的数据。经过筛选,图形界面选择Littlvgl,文件系统使用Fatfs。Littlevgl从没接触过,但是我看到正点原子有littevgl的教程,我也用的控件也不多,所以应该不是很难。Fatfs正点原子也有教程,我之前学过且看过实现代码,所以这个也不难。ok,问题解决。开干。
4、使用流程,按照四个界面流转。

Created with Raphaël 2.3.0 开始 显示功能列表 选择配置文件 确认? 显示下载进度 结束 yes no

(1)第一个界面,提示支持的功能。

  • 从母卡读取数据。这个功能是为了从已有的产品获取数据,省去用电脑下载数据的操作。母卡也是正常产品,只是因为从他这读取数据,他就叫母卡,不能白用人家不是!!
  • 给产品写数据。从母卡读回来的数据写入新的产品,这个产品可能刚做回来,没有程序。也可能数据有损坏,无法正常使用了。
  • 只给产品的flash写数据。有时只需要下载flash内容,满足这个需求。
  • 只给产品的MCU下载boot程序。满足只下载boot的需求。

(2)选择配置文件。从SD卡的配置文件目录读取文件,显示在该界面。
(3)确认是否选择该配置文件。因为可能误触摸,所以要有一个确认界面,如果确认,就开始下载程序。如果点否,返回第二个界面,重新选择配置文件。
(4)等待界面。下载程序时,干看着也不行,要让用户有事干,知道当前下载进度,此界面显示当前下载进度。当下载结束后,返回第一个界面,开始新的循环。

目前效果

以上功能都实现了,但是速率很不理想,需要继续优化。
视频在哔哩哔哩

程序实现

首先说界面实现吧

全程使用label控件,原因是,大小,位置,显示内容,子对象,回调函数,我需要的他都有,没必要使用更高级的控件了。
代码

void win_1_start(void)
{
	lv_obj_t *src;
	
	lv_obj_t  *label_title;		//title label的对象指针
	lv_obj_t  *label_Master;	//from Master label的对象指针
	lv_obj_t  *label_Target;	//to Target label的对象指针
	lv_obj_t  *label_Mcu;		//to Mcu boot label的对象指针
	lv_obj_t  *label_Flash;		//to Flash label的对象指针
	msg_attr_t  msg_attr;
	
	
	pcfg_opt.is_running = 0;
	pcfg_opt.program_bytes = 0;
	pcfg_opt.process_percent = 0;
	pcfg_opt.opt = NO_OPT;
	memset(pcfg_opt.pcfg_file_name, 0, _MAX_LFN);	//工装操作信息的初始值设置为0
	memset(pcfg_opt.pcfg_boot_name, 0, _MAX_LFN);	//boot.bin文件名
	
	
	src = lv_scr_act(); 
	desktop_1 = lv_obj_create(src, src);
	
	
	msg_attr.P_parent = desktop_1;
	msg_attr.P_obj = label_title;
	msg_attr.width = 320;
	msg_attr.y_mod = 0;
	msg_attr.msg_str = "Card:\0";
	msg_attr.callback_func = label_1_event_handler;
	win_1_box_show(&msg_attr);	//显示title label 

	msg_attr.P_obj = label_Master;
	msg_attr.width = 200;
	msg_attr.y_mod = 100;
	msg_attr.msg_str = "from Mater\0";
	msg_attr.opt_msg = FROM_MASTER_OPT;
	msg_attr.callback_func = label_Master_event_handler;
	win_1_box_show(&msg_attr);	//显示from Master label
	
	msg_attr.P_obj = label_Target;
	msg_attr.width = 200;
	msg_attr.y_mod = 150;
	msg_attr.msg_str = "to Target\0";
	msg_attr.opt_msg = TO_TARGET_OPT;
	msg_attr.callback_func = label_Master_event_handler;
	win_1_box_show(&msg_attr);	//显示to Mcu boot label

每个label显示时调用的win_1_bos_show是自己写的函数,因为显示显示属性相似,不想重复粘贴,而且那么写也不好看所以做成了函数。

static void win_1_box_show(msg_attr_t *msg_attr)
{
	
	lv_obj_t *desktop = msg_attr->P_parent;
	
	lv_obj_t *label_1 = msg_attr->P_obj;
	
    label_1 = lv_label_create(desktop, NULL);
	
	lv_obj_set_user_data(label_1, (void*)msg_attr->opt_msg);	//设置obj的显示出来的操作信息
	lv_label_set_long_mode(label_1, LV_LABEL_LONG_DOT);
	lv_obj_set_width(label_1, msg_attr->width);
	
	lv_label_set_align(label_1, LV_LABEL_ALIGN_CENTER);
//	lv_obj_set_pos(label_1, 10, 20);
	lv_label_set_body_draw(label_1, true);
	lv_style_plain.text.font = &lv_font_roboto_28; // LV_FONT_ROBOTO_22
	lv_label_set_style(label_1, LV_LABEL_STYLE_MAIN, &lv_style_plain_color);
	
	lv_obj_set_click(label_1, true);
	lv_obj_set_event_cb(label_1, msg_attr->callback_func);
	
	lv_label_set_text(label_1, msg_attr->msg_str);    
	
	lv_obj_align(label_1, desktop, LV_ALIGN_IN_TOP_MID, 0, msg_attr->y_mod);
}

然后是label的回调函数,这是第一个界面的label的回调函数。其中有界面切换的实现。首先lv_obj_del( )删除当前桌面对象,然后win_sel_pcfg(),就跳转到第二个界面了。
当然了,也可以像被注释的那句话lv_obj_set_hidden(),将当前窗口隐藏,但是隐藏的话,窗口的变量不会被释放,每次调用窗口的过程,都会有内存泄漏,我又不会处理这个情况,所以只好每次删掉,下次显示时,重新来过

void label_Master_event_handler(lv_obj_t *obj, lv_event_t event)
{
	static uint16_t cnt = 0;
	uint16_t	opt_msg = 0;
	lv_obj_t *desktop;
	
	switch(event)
	{
		case LV_EVENT_RELEASED://按下后松手了
		{
			opt_msg = (uint16_t)lv_obj_get_user_data(obj);	//获取按键显示的操作信息
			pcfg_opt.opt = opt_msg;				//将操作信息通知到全局变量,
			
			lv_obj_del(desktop_1);
//			desktop = lv_obj_get_parent(obj);
//			lv_obj_set_hidden(desktop, true);	//将当前窗口隐藏
			
			win_sel_pcfg();						//显示配置文件选择界面
			
		}	
	}

几个细节

  • 跳转新的窗口前,一定要把当前窗口删除,否则不会显示新的窗口。因为两个窗口都是可显示的,显示屏只有一个,那就矛盾了。
  • 每个界面都是先在屏幕对象上创建一个父label。然后在父label上创子label,这样删除窗口时,删除父label就可以。如果所有对象在屏幕对象上创建,跳转新的界面时,无法删除屏幕对象,也就无法显示新界面。
  • 在等待界面中,显示当前下载的进度的同时也在下载数据。在单线程中,实现思路是,每次只下载1KB的数据,下载完成后,给显示界面发送事件,通知当前进度。由于每次下载时间很短,界面显示进度就会流畅,且下载完成后,等待界面也会同步显示完成。

你可能感兴趣的:(物联网,C语言,入门,单片机,嵌入式硬件)