码已上传,有需自下。
链接: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、使用流程,按照四个界面流转。
(1)第一个界面,提示支持的功能。
(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(); //显示配置文件选择界面
}
}