当前位置: 首页 > news >正文

【LVGL】学习笔记--(1)Keil中嵌入式系统移植LVGL

一 LVGL简介

最近emwin用的比较烦躁,同时被LVGL酷炫的界面吸引到了,所以准备换用LVGL试试水。

LVGL(轻量级和通用图形库)是一个免费和开源的图形库,它提供了创建嵌入式GUI所需的一切,具有易于使用的图形元素,美丽的视觉效果和低内存占用。

【1】主要特性

  1. 丰富且强大的模块化图形组件:按钮 (buttons)、图表 (charts)、列表 (lists)、滑动条 (sliders)、图片 (images) 等

  1. 高级的图形引擎:动画、抗锯齿、透明度、平滑滚动、图层混合等效果

  1. 支持多种输入设备:触摸屏、 键盘、编码器、按键等

  1. 支持多显示设备

  1. 不依赖特定的硬件平台,可以在任何显示屏上运行

  1. 配置可裁剪(最低资源占用:64 kB Flash,16 kB RAM)

  1. 基于UTF-8的多语种支持,例如中文、日文、韩文、阿拉伯文等

  1. 可以通过类CSS的方式来设计、布局图形界面(例如:Flexbox、Grid)

  1. 支持操作系统、外置内存、以及硬件加速(LVGL已内建支持STM32 DMA2D、NXP PXP和VGLite)

  1. 即便仅有单缓冲区(frame buffer)的情况下,也可保证渲染如丝般顺滑

  1. 全部由C编写完成,并支持C++调用

  1. 支持Micropython编程,参见:LVGL API in Micropython

  1. 支持模拟器仿真,可以无硬件依托进行开发

  1. 丰富详实的例程

  1. 详尽的文档以及API参考手册,可线上查阅或可下载为PDF格式

  1. 在 MIT 许可下免费和开源

【2】配置要求

【3】与其他显示工具的对比

二 LVGL移植前准备

【1】参考资料

  1. LVGL官网:https://lvgl.io/

  1. LVGL源码:https://gitee.com/my_lvgl/lvgl

  1. 百问网LVGL中文开发手册:http://lvgl.100ask.net/8.2/index.html

  1. 一些关于移植的经验资料:

LITTLEVGL (LVGL)入门之移植到STM32芯片

LVGL学习笔记 | 02 - 移植LVGL 8.2到STM32F407开发板(MCU屏)

STM32移植LVGL(LittleVGL)

在STM32F4上移植LVGL8.2

【2】硬件资源

硬件资源包括:

  1. 嵌入式平台(我用的MCU是华大的HC32F460芯片)

  1. 实现了屏幕驱动的液晶屏(我用的是3.2寸320*240的8080并口屏,屏幕驱动可以移步【嵌入式】MCU(HC32F460)+并口LCD液晶屏ILI9341 移植emWin记录)

  1. 输入设备(非必要,包括触摸屏、鼠标、键盘、编码器、按键,我自己使用的是磁控旋钮编码器)

【3】其他

推荐一款不错的LVGL设计工具GUI Guider,是由恩智浦开发的,通过NXP官网(https://www.nxp.com.cn/design/software/development-software/gui-guider:GUI-GUIDER)即可下载,具体使用后面有机会专门写一篇文章介绍。

准备好了以上的内容,接下来就可以进行移植了。

三 LVGL移植

【1】源码下载

首先下载LVGL源码,链接见上一章的参考资料。有很多版本,我这边没有选择最新的版本,而是用了V1.5.0-GA版本GUI Guider支持的V8.3.2版本:

下载完解压后是这样的:

【2】源码添加工程

  1. 将上面解压完成的lvgl-v8.3.2文件夹中的下列文件复制到自己keil工程自建文件夹GUI_LVGL中:

  1. 复制进来后,删除proting文件夹以及lv_conf_template文件中_template后缀:

  1. 在keil中建立管理目录:

其中:

  • GUI/LVGL用来存放src及其子目录中所有.c文件。这一步比较考验耐心和细致,每一个子目录下都有不少源文件。(要是不想自己keil工程中有许多看起来凌乱的源文件,可以参考STM32/keil把多个c文件编译为静态库lib中的方法,预先将这许许多多的.c文件编译为lib库,那么GUI/LVGL中放一个lib文件即可。但是这种方法要注意创建lib的工程和使用lib的工程芯片型号要一致才可以)。我是使用的lib库的形式。

  • GUI/LVGL/PORT用来存放porting下的三个源文件,分别是lv_port_disp.c(显示接口文件)、lv_port_indev(输入设备接口文件)、lv_port_fs(文件系统接口文件)

  • GUI/LVGL用来存放GUI Guider生成的图形界面文件,这边暂时还没有用到,可以不用管。

  1. 包含头文件路径:

  1. 最后还有两个重要的小操作,一个是要选用C99 Mode:

另一个是加大栈空间,方法是打开.s启动文件,修改其中的Stack_Size值,我这边配置的是8KB(根据上面LVGL运行配置要求的推荐值确定)

至此,可以编译一下,由于还没有使用显示接口等,所以这边应该是编译无误的。

【3】添加心跳接口

在timer.c中包含头文件"lvgl.h",并在1ms定时器中调用lv_tick_inc(1)启用心跳:

/**************************************************************************
* 函数名称: TimeraUnit2_IrqCallback
* 功能描述: 定时器A 单元2初始化
* 输入参数: 
* 输出参数: 
* 返 回 值: 
* 其它说明: 1ms定时器中断
**************************************************************************/
static void TimeraUnit2_IrqCallback(void)
{lv_tick_inc(1);TIMERA_ClearFlag(TIMERA_UNIT2, TimeraFlagOverflow);
}

当然还有其他方式让LVGL开始心跳,这个可以自行选用,控制心跳周期为1ms即可。

【4】显示接口对接屏幕驱动

  1. 修改lv_conf.h头文件中的#if 0为#if 1,同时根据适配的显示设备设置颜色格式:

补充,如果想屏幕刷新快一点,可以将FPS做如下调整(30ms-->10ms,即FPS由30-->100):

  1. 把lv_port_disp.c、lv_port_disp.h、lv_port_indev.c、lv_port_indev.h四个文件的#if 0 都改成#if 1 ,这四个文件包含的头文件名字还需根据编译报错信息修改。:

注意,若头文件#include "lvgl/lvgl.h"包含报错,可以添加宏定义LV_CONF_INCLUDE_SIMPLE

  1. 修改lv_port_disp.c中适配底层屏幕驱动分辨率(我用的就是320*240):

/**********************      DEFINES*********************/
#ifndef MY_DISP_HOR_RES#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen width, default value 320 is used for now.#define MY_DISP_HOR_RES    320
#endif#ifndef MY_DISP_VER_RES#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen height, default value 240 is used for now.#define MY_DISP_VER_RES    240
#endif
  1. 修改lv_port_disp.c中lv_port_disp_init函数,该函数包含两个部分,一个是初始化disp_init(),一个是绘制缓冲区

其中初始化函数disp_init()中加入自己的屏幕初始化程序:

void LCD_AllInit(void)
{LCD_InitGPIO();  //初始化几个GPIO口,包括CS\RS\WR\RD\RES\DB0-15LCD_HardwareReset();  //LCD复位LCD_ConfigureREG();   //RGB配置初始化LCD_Clear(WHITE);  //清屏白色LCD_DisplayDir(data_inter.showDirect);  //默认为横屏
}static void disp_init(void)
{/*You code here*/LCD_AllInit();
}
  1. 修改lv_port_disp.c中lv_port_disp_init函数绘制缓冲区的部分,删除不用的Example for 2/3,只留Example for 1即可:

void lv_port_disp_init(void)
{/*-------------------------* Initialize your display* -----------------------*/disp_init();/*-----------------------------* Create a buffer for drawing*----------------------------*//*** LVGL requires a buffer where it internally draws the widgets.* Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.* The buffer has to be greater than 1 display row** There are 3 buffering configurations:* 1. Create ONE buffer:*      LVGL will draw the display's content here and writes it to your display** 2. Create TWO buffer:*      LVGL will draw the display's content to a buffer and writes it your display.*      You should use DMA to write the buffer's content to the display.*      It will enable LVGL to draw the next part of the screen to the other buffer while*      the data is being sent form the first buffer. It makes rendering and flushing parallel.** 3. Double buffering*      Set 2 screens sized buffers and set disp_drv.full_refresh = 1.*      This way LVGL will always provide the whole rendered screen in `flush_cb`*      and you only need to change the frame buffer's address.*//* Example for 1) */static lv_disp_draw_buf_t draw_buf_dsc_1;static lv_color_t buf_1[MY_DISP_HOR_RES * 10];                          /*A buffer for 10 rows*/lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10);   /*Initialize the display buffer*//*-----------------------------------* Register the display in LVGL*----------------------------------*/static lv_disp_drv_t disp_drv;                         /*Descriptor of a display driver*/lv_disp_drv_init(&disp_drv);                    /*Basic initialization*//*Set up the functions to access to your display*//*Set the resolution of the display*/disp_drv.hor_res = MY_DISP_HOR_RES;disp_drv.ver_res = MY_DISP_VER_RES;/*Used to copy the buffer's content to the display*/disp_drv.flush_cb = disp_flush;/*Set a display buffer*/disp_drv.draw_buf = &draw_buf_dsc_1;/*Required for Example 3)*///disp_drv.full_refresh = 1;/* Fill a memory array with a color if you have GPU.* Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.* But if you have a different GPU you can use with this callback.*///disp_drv.gpu_fill_cb = gpu_fill;/*Finally register the driver*/lv_disp_drv_register(&disp_drv);
}

补充,如果想屏幕刷新快一点,在SRAM足够的条件下,可以修改默认的buf_1[MY_DISP_HOR_RES * 10]为buf_1[MY_DISP_HOR_RES * MY_DISP_VER_RES / 4]:

static lv_disp_draw_buf_t draw_buf_dsc_1;static lv_color_t buf_1[MY_DISP_HOR_RES * MY_DISP_VER_RES / 4];                          /*A buffer for 10 rows*/lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * MY_DISP_VER_RES / 4);   /*Initialize the display buffer*/

  1. 修改lv_port_disp.c中的刷屏函数disp_flush,默认的刷屏用打点的方式速度太慢,改用刷块的方式:

void LCD_Fill_LVGL(U16 sx,U16 sy,U16 ex,U16 ey,U16 *color)
{uint16_t height, width;uint16_t i, j;width = ex - sx + 1;            /* 得到填充的宽度 */height = ey - sy + 1;           /* 高度 */for (i = 0; i < height; i++){LCD_SetCursor(sx, sy + i); /* 设置光标位置 */LCD_WriteRAM_Prepare();    /* 开始写入GRAM */for (j = 0; j < width; j++)LCD_WriteRAM(color[i * width + j]); /* 写入数据 */}
} /*Flush the content of the internal buffer the specific area on the display*You can use DMA or any hardware acceleration to do this operation in the background but*'lv_disp_flush_ready()' has to be called when finished.*/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{LCD_Fill_LVGL(area->x1,area->y1,area->x2,area->y2,(U16 *)color_p);/*IMPORTANT!!!*Inform the graphics library that you are ready with the flushing*/lv_disp_flush_ready(disp_drv);
}

至此,显示接口对接屏幕驱动完成。

【5】输入设备接口对接编码器

如果没有使用到诸如触摸屏、鼠标、键盘、编码器、按键这几类的外部设备,只要显示界面不用操作的话,可以跳过这一节,直接到LVGL测试章节继续。

我这边用到的是磁控旋钮编码器外设

  1. 修改lv_port_indev.c中lv_port_indev_init函数,包含两个部分,一个是初始化encoder_init(),一个是注册编码器外部设备

其中初始化函数encoder_init()中加入自己的屏幕初始化程序:

/*Initialize your keypad*/
extern STRU_KNOB knob_old;
extern char knob_state_now;
static void encoder_init(void)
{/*Your code comes here*/MLX_InitGPIO();MLX_InitSPI();knob_old = knobPosInit();  //旋钮位置初始化
}
  1. 删除不用的外部设备,只留下编码器作为唯一外部输入:

void lv_port_indev_init(void)
{/*** Here you will find example implementation of input devices supported by LittelvGL:*  - Touchpad*  - Mouse (with cursor support)*  - Keypad (supports GUI usage only with key)*  - Encoder (supports GUI usage only with: left, right, push)*  - Button (external buttons to press points on the screen)**  The `..._read()` function are only examples.*  You should shape them according to your hardware*/static lv_indev_drv_t indev_drv;/*------------------* Encoder* -----------------*//*Initialize your encoder if you have*/encoder_init();/*Register a encoder input device*/lv_indev_drv_init(&indev_drv);indev_drv.type = LV_INDEV_TYPE_ENCODER;indev_drv.read_cb = encoder_read;indev_encoder = lv_indev_drv_register(&indev_drv);/*Later you should create group(s) with `lv_group_t * group = lv_group_create()`,*add objects to the group with `lv_group_add_obj(group, obj)`*and assign this input device to group to navigate in it:*`lv_indev_set_group(indev_encoder, group);`*/
}
  1. 注册外部设备,修改encoder_read函数,encoder_handler可以不用管,encoder_diff的加减表示左旋和右旋,encoder_state赋值LV_INDEV_STATE_PR或者LV_INDEV_STATE_REL表示按下或松开

/*Will be called by the library to read the encoder*/
static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{if(knob_state_now == 9 || knob_state_now == 15)  //按下动作和按下状态都算按下encoder_state = LV_INDEV_STATE_PR;   //按下elseencoder_state = LV_INDEV_STATE_REL;  //松开if(knob_state_now == 19)encoder_diff ++;else if(knob_state_now == 17)encoder_diff --;data->enc_diff = encoder_diff;data->state = encoder_state;encoder_diff = 0;
}

knob_state_now表示当前旋钮的状态,其状态的获得在主循环中获取,将其映射到encoder_diff和encoder_state这两个变量

#define KNOB_ESCAPE         8   //旋钮抬起状态,xy不变
#define KNOB_UP             17  //z不变,正向旋转
#define KNOB_DOWN           19  //z不变,反向旋转
#define KNOB_ENTER_BEF      15  //按下动作

四 LVGL测试

至此便完成了LVGL的显示接口以及输入设备接口的移植。

在主任务中添加上面的初始化,这边简单做了一个小界面方便展示:

#include "lvgl.h"
#include "lv_port_disp.h"
#include "lv_port_indev.h"
static void lv_ex_label_1(void)
{lv_obj_t * label2 = lv_label_create(lv_scr_act());lv_label_set_recolor(label2, true);lv_label_set_long_mode(label2, LV_LABEL_LONG_SCROLL_CIRCULAR); /*Circular scroll*/lv_obj_set_width(label2, 120);// Hello world ! Trisuborn.lv_label_set_text(label2, "#ff0000 Hello# #00ff00 world ! Trisuborn.#");lv_obj_align(label2, LV_ALIGN_CENTER, 0, 0);
}void GUI_Task(void)
{//LVGL初始化lv_init();//显示器初始化lv_port_disp_init();//外部输入初始化lv_port_indev_init();//界面生成lv_ex_label_1();while(1){knob_now = knobPosNow();  //确定旋钮当前位置knob_state_now = Knob_State_Entry(&knob_old, &knob_now, knob_state_old);  //确定旋钮当前状态if(KNOB_ESCAPE == knob_state_now){knob_state_old = knob_state_now;}else{knob_old = knob_now;knob_state_old = knob_state_now;}lv_task_handler();os_dly_wait(5);}
}

效果如下:

相关文章:

【LVGL】学习笔记--(1)Keil中嵌入式系统移植LVGL

一 LVGL简介最近emwin用的比较烦躁&#xff0c;同时被LVGL酷炫的界面吸引到了&#xff0c;所以准备换用LVGL试试水。LVGL(轻量级和通用图形库)是一个免费和开源的图形库&#xff0c;它提供了创建嵌入式GUI所需的一切&#xff0c;具有易于使用的图形元素&#xff0c;美丽的视觉效…...

Transformer学习笔记

Transformer学习笔记1. 参考2. 模型图3.encoder部分3.1 Positional Encoding3.2 Muti-Head Attention3.3 ADD--残差连接3.4 Norm标准化3.5 单个Transformer Encoder流程图4.decoder部分4.1 mask Muti-Head Attention4.2 Muti-Head Attention5 多个Transformer Encoder和多个Tra…...

vue-cli引入wangEditor、Element,封装可上传附件的富文本编辑器组件(附源代码直接应用,菜单可调整)

关于Element安装引入&#xff0c;请参考我的另一篇文章&#xff1a;vue-cli引入Element Plus&#xff08;element-ui&#xff09;&#xff0c;修改主题变量&#xff0c;定义全局样式_shawxlee的博客-CSDN博客_chalk variables 1、安装wangeditor npm i wangeditor --savewangE…...

移动办公时代,数智化平台如何赋能企业管理升级?

在传统的办公模式下&#xff0c;企业组织办公不仅时效低&#xff0c;周期长、成本高&#xff0c;且各办公系统相互独立。随着社会经济的发展&#xff0c;人们的工作生活变得多样化&#xff0c;对于办公的需求也越来越多&#xff0c;存在明显弊端的传统办公模式已不能满足企业对…...

2023“拼夕夕”为什么可以凭借简单的拼团做这么大?

2023“拼夕夕”为什么可以凭借简单的拼团做这么大&#xff1f; 2023-02-24 梦龙 大家好&#xff0c;我是你们熟悉而又陌生的好朋友梦龙&#xff0c;一个创业期的年轻人 大家都知道&#xff0c;拼夕夕背后的商业模式是拼团&#xff0c;但是大家知道为什么简单的拼团可以让拼夕…...

sqlmap工具

sqlmap Sqlmap是一个开源的渗透测试工具&#xff0c;可以用来自动化的检测&#xff0c;利用SQL注入漏洞&#xff0c;获取数据库服务器的权限。目前支持的数据库有MySQL、Oracle、PostgreSQL、Microsoft SQL Server、Microsoft Access等大多数据库 Sqlmap采用了以下5种独特的SQ…...

高/低压供配电系统设计——安科瑞变电站电力监控系统的应用

摘 要&#xff1a;在电力系统的运行过程中&#xff0c;变电站作为整个电力系统的核心&#xff0c;在保证电力系统可靠的运行方面起着至关重要的作用&#xff0c;基于此需对变电站监控系统的特点进行分析&#xff0c;结合变电站监控系统的功能需求&#xff0c;对变电站电力监控系…...

Tapdata 和 Databend 数仓数据同步实战

作者&#xff1a;韩山杰https://github.com/hantmacDatabend Cloud 研发工程师基础架构在云计算时代也发生着翻天地覆的变化&#xff0c;对于业务的支持变成了如何能利用好云资源实现降本增效&#xff0c;同时更好的支撑业务也成为新时代技术人员的挑战。 本篇文章通过&#xf…...

单核CPU, 1G内存,也能做JVM调优吗?

最近&#xff0c;笔者的技术群里有人问了一个有趣的技术话题&#xff1a;单核CPU, 1G内存的超低配机器&#xff0c;怎么做JVM调优&#xff1f;这实际上是两个问题。单核CPU的超低配机器&#xff0c;怎么充分利用CPU&#xff1f;单核CPU, 1G内存的超低配机器&#xff0c;怎么做J…...

《计算机应用研究》投稿经历和时间节点

记录四川计算机研究院《计算机应用研究》期刊投稿经历和时间节点。 日期状态周期2022.11.09上传稿件当天显示编辑部已接收稿件&#xff0c;开始初审2022.11.09 – 2022.11.15初审6天2022.11.15 – 2022.12.21外审36天2022.12.21收到退修意见&#xff08;邮件形式&#xff09;编…...

mars3d获取视窗的范围

期望效果 :1.我现在想获取到当前视窗的地图范围&#xff0c;请问有什么⽅法可以拿到吗 2.⽐如当前视窗地图范围的边界点&#xff0c;每个边界点的经纬度 回复&#xff1a;1.mars3d的API⽂档中有相关的⽅法 2.具体使⽤可以参考⽂档地址&#xff1a;http://mars3d.cn/api/Map.htm…...

《高性能MySQL》读书笔记(上)

目录 MySQL的架构 MySQL中的锁 MySQL中的事务 事务特性 隔离级别 事务日志 多版本并发控制MVCC 影响MySQL性能的物理因素 InnoDB缓冲池 MySQL常用的数据类型以及优化 字符串类型 日期和时间类型 数据标识符 MySQL的架构 默认情况下&#xff0c;每个客户端连接都…...

05-代理模式

代理模式 代理模式使用代理对象来代替真实对象的访问&#xff0c;在不修改原有对象的前提下&#xff0c;提供额外的操作&#xff0c;扩展目标对象的功能。代理模式分为静态代理和动态代理。 静态代理 手动为目标对象中的方法进行增强&#xff0c;通过实现相同接口重写方法进…...

RocketMQ源码分析之消费队列、Index索引文件存储结构与存储机制-上篇

RocketMQ 存储基础回顾&#xff1a; 源码分析RocketMQ之CommitLog消息存储机制 本文主要从源码的角度分析 Rocketmq 消费队列 ConsumeQueue 物理文件的构建与存储结构&#xff0c;同时分析 RocketMQ 索引文件IndexFile 文件的存储原理、存储格式以及检索方式。RocketMQ 的存储…...

基于Java的浏览器的设计与实现毕业设计

技术&#xff1a;Java等摘要&#xff1a;当今世界是一个以计算机网络为核心的信息时代&#xff0c;互联网为人们快速获取、发布和传递信息提供了便捷&#xff0c;而浏览器作为互联网上查找信息的重要工具&#xff0c;给人们提供了巨大而又宝贵的信息财富&#xff0c;受到了大家…...

手把手教你使用vite打包自己的js代码包并推送到npm

准备 要有npm账号&#xff0c;没有的铁子去npm官网注册一个&#xff0c;又不要钱。 使用vite创建项目 一行代码搞定 npm create vite viet-demo框架选择Others 模板选择library 选择ts 这样项目就创建完了 这个项目默认有一个函数&#xff0c;用来记录按钮的点击次数并…...

Tomcat源码分析-关于tomcat热加载的一些思考

在前面的文章中&#xff0c;我们分析了 tomcat 类加载器的相关源码&#xff0c;也了解了 tomcat 支持类的热加载&#xff0c;意味着 tomcat 要涉及类的重复卸装/装载过程&#xff0c;这个过程是很敏感的&#xff0c;一旦处理不当&#xff0c;可能会引起内存泄露 卸载类 我们知…...

DataWhale 大数据处理技术组队学习task4

五、分布式并行编程模型MapReduce 1. 概述 1.1 分布式并行编程 背景&#xff1a;摩尔定律已经开始逐渐失效&#xff0c;提升数据处理计算能力刻不容缓。传统的程序开发与分布式并行编程 传统的程序开发&#xff1a;以单指令、单数据流的方式顺序执行&#xff0c;虽然这种方式…...

Oracle 12C以上统计信息收集CDB、PDB执行时间不一致问题

文章目录前言一、统计信息窗口期调查二、时区调查三、查询alert记录四、why Database Statistic Collection Job is running two times inside a Maintenance Window?五、Default Scheduler Timezone Value In PDB$SEED Different Than CDB六、总结前言 在实际工作中发现一个…...

用Python获取弹幕的两种方式(一种简单但量少,另一量大管饱)

前言 弹幕可以给观众一种“实时互动”的错觉&#xff0c;虽然不同弹幕的发送时间有所区别&#xff0c;但是其只会在视频中特定的一个时间点出现&#xff0c;因此在相同时刻发送的弹幕基本上也具有相同的主题&#xff0c;在参与评论时就会有与其他观众同时评论的错觉。 在国内…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...

深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向

在人工智能技术呈指数级发展的当下&#xff0c;大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性&#xff0c;吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型&#xff0c;成为释放其巨大潜力的关键所在&…...

微服务通信安全:深入解析mTLS的原理与实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言&#xff1a;微服务时代的通信安全挑战 随着云原生和微服务架构的普及&#xff0c;服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...