STM32 工程移植 LVGL:一步一步完成

STM32 工程移植 LVGL:一步一步完成

LVGL,作为一款强大且灵活的开源图形库,专为嵌入式系统GUI设计而生,极大地简化了开发者在创建美观用户界面时的工作。作为一名初学者,小编正逐步深入探索LVGL的奥秘,并决定记录下这段学习旅程,以便与同道中人共享心得。本文旨在于引导读者如何将LVGL图形库成功集成至STM32微控制器项目中,从零开始,步步为营,让嵌入式界面开发之旅变得更加平易近人。

小编选择了源自淘宝“魔女开发板”店铺的一款STM32F407开发板作为实践工具,其配套的丰富资源和客服技术支持为学习之路铺设了坚实的基础。特别值得关注的是,该店铺提供了一套详尽且实用的入门系列文章——【快速入门 LVGL】,首篇聚焦于如何在STM32工程中高效移植LVGL,该文详述了从环境搭建到实际应用的每一步,成为了我此次学习的重要指引。文章链接如下:
【快速入门 LVGL】-- 1、STM32 工程移植 LVGL

介绍

LVGL(Light and Variables Graphics Library)是一个免费的开源图形库,提供了创建具有易于使用的图形元素、优美的视觉效果和低内存占用的嵌入式GUI所需的一切。它用C语言编写,以实现最大的兼容性(与C ++兼容),模拟器可在没有嵌入式硬件的PC上启动嵌入式GUI设计。

1. 准备工作

首先,你需要以下几样东西:

  • STM32CubeMX:用于配置 STM32 的硬件特性。
  • STM32 HAL 库:STM32 的硬件抽象层库,用于简化硬件操作。
  • LVGL 库:你可以在 LVGL 的 GitHub 页面下载最新版本。

STM32工程的要求

1️⃣设置堆栈大小:Heap、Stack,设置为:0x1000

2️⃣准备好你使用开发平台所用屏幕的驱动函数BSP(一般屏幕商家会提供)

  • 画点函数,用于后面注册LVGL的显示功能
  • 触摸检测函数 (返回:0-未按下、1-按下)、坐标获取函数,用于注册LVGL的触屏功能

2. 下载 LVGL

浏览器搜索 lvgl git
在这里插入图片描述

虽然LVGL已发布了v9.0、v9.1等,但v8.3版,是目前最广泛使用的版本。
它拥有众多的网上教程资源,使开发者能够轻松地学习和使用LVGL。而且多款主流的可视化设计工具(如SimVis Designer和Qt Design Studio)都支持LVGL的v8.3版本。v9.0、v9.1等版本由于其相对较新,相关资源和可视化设计工具的支持可能相对较少。

因此这里建议下载v8.3版

可以选择用git clone到本地或者直接下载zip
在这里插入图片描述

下载完成
在这里插入图片描述

3. LVGL源文件裁剪

我们所下载的LVGL源文件并不是所有文件都要加入到我们的工程中,我们首先要进行裁剪

我们只需要下面这五个文件
在这里插入图片描述

1️⃣将这五个文件复制一份作为模板,方便后面的移植
在这里插入图片描述

2️⃣打开模板中的examples文件夹,将porting文件夹以外的全部文件删除
在这里插入图片描述
删除后👇
在这里插入图片描述

3️⃣修改 porting 里面的文件名称

由于LVGL源代码中的头文件,使用了相对路径,如在 “lvgl.h” 中

#include "src/misc/lv_log.h"
#include "src/misc/lv_timer.h"
#include "src/misc/lv_math.h"
#include "src/misc/lv_mem.h"
#include "src/misc/lv_async.h"
#include "src/misc/lv_anim_timeline.h"
#include "src/misc/lv_printf.h"

所以我们在构造工程是也要使用相同目录结构

打开 “porting” 文件夹,修改里面的文件名,将 “_template” 删除
在这里插入图片描述

修改后如下
在这里插入图片描述

4️⃣ 修改 lv_conf.h 文件名

回到 “LVGL” 文件夹中,lv_conf_template.h,是LVGL配置参数的重要文件,同样将它修改为: lv_conf.h
在这里插入图片描述

修改完成后如下
在这里插入图片描述

4. 将LVGL 添加到STM32工程

1️⃣将我们创建的 lvgl 文件夹,粘贴到STM32工程目录下

在这里插入图片描述
2️⃣打开Keil5,在工程里,添加4个文件夹

文件夹名称文件类型
LVGL_myGui用户自己的界面代码文件、官方demo等
LVGL_confLVGL 的两个h文件
LVGL_portingLVGL 的接口文件, 如显示、触摸屏、键盘等
LVGL_srcLVGL 的所有底层c文件

操作如下👇

在这里插入图片描述

3️⃣为每一个文件夹组,添加需要的文件

文件夹 (Group)添加文件
LVGL_myGUI不用添加。
LVGL_conflv_conf.h, lvgl.h
LVGL_portinglv_port_disp.c, lv_port_disp.h, lv_port_indev.c, lv_port_indev.h
LVGL_src所有 .c 文件位于 lvhl/src 及其所有子文件夹下

🚨注意src文件夹下,会有多重的子文件夹,需要把每一个子文件夹的C文件全部添加进来
在这里插入图片描述
4️⃣打勾C99
在这里插入图片描述

5️⃣添加头文件路径

配置项具体内容
路径1LVGL 文件夹路径
路径2LVGL\src 文件夹路径
路径3LVGL\examples\porting 文件夹路径

添加方法如下
在这里插入图片描述

5. 注册输出设备

1️⃣打开 lv_conf.h,对第15行预编译#if 0进行修改👇,将0改为1,以启用此文件
在这里插入图片描述
同样的方法启用LVGL_porting下的 lv_port_disp.h,打开 lv_port_disp.h,进行如下修改

行号原内容修改后内容
7#if 0#if 1
22lvgl/lvgl.hlvgl.h

在这里插入图片描述
同样启用 lv_port_disp.c

行号原内容修改后内容
7#if 0#if 1
12"lv_port_disp_template.h""lv_port_disp.h"

在这里插入图片描述
2️⃣添加 LCD 驱动的头文件
同样在 lv_port_disp.c

  1. 插入LCD驱动文件

    • 位置: 第14行之后
    • 操作: 插入您的LCD驱动头文件
  2. 设置显示屏宽度和高度

    • 位置: 第20行 & 第25行(或根据实际代码结构调整)
    • 操作: 根据您的显示屏参数,替换现有的宽度和高度定义

在这里插入图片描述
插入LCD驱动头文件 ,确保您的项目可以访问LCD的绘图功能,特别是画点函数。

🚨留意LVGL的横屏默认设置 LVGL库默认以横向模式布局,这意味着宽度对应水平像素,高度对应垂直像素。

3️⃣选择创建缓存的方式
lv_port_disp.c文件中,您需要配置LVGL的显示缓冲区。面对提供的三种创建缓冲区的选项,您需要选择其中一种方式。通常推荐选择第一种方案,特别是对于大多数基本应用场景。

定位到相关代码段:首先,在文件中找到第86行至101行附近的内容。这部分代码涉及到显示缓冲区的配置。

选择第一种方法:这通常是创建单个缓冲区的方法,适用于多数场景。确认第86行附近的代码(第一种缓冲区创建方式)是未被注释的,形如:

    /* 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*/

这段代码初始化了一个缓冲区,名为buf_1,并分配了足够的内存空间来存储屏幕一行或多行的像素数据。

注释掉其他方法:为了明确选择第一种方式,需要将描述第二和第三种缓冲区创建方式的代码行(大约在第90行到101行之间)进行注释处理。注释后如下
在这里插入图片描述
4️⃣关联 画点函数
同样在lv_port_disp.c文件里

假设您的LCD驱动中画点函数的原型为

void My_LCD_DrawPixel(int x, int y, uint16_t color);

我们需要将其与LVGL的显示刷新回调函数168行的disp_flush()关联起来

替换画点函数disp_flush函数内部,LVGL通过调用一个画点函数来实际在屏幕上绘制每个像素。你需要将此部分代码替换为我们自己的的LCD驱动中的画点函数调用。

修改后如下
在这里插入图片描述

6. 注册触摸屏

1️⃣启用 “lv_port_indev.h
在lv_port_indev.h中修改下面两行

行号原始代码修改后代码
8#if 0#if 1
20"lvgl / lvgl.h""lvgl.h"

在这里插入图片描述

2️⃣启动 “lv_port_indev.c
打开"lv_port_indev.c", 修改以下内容

行号原内容修改后内容
7#if 0#if 1
12"lv_port_indev_template.h""lv_port_indev.h"
13"../../lvgl.h""lvgl.h"

在这里插入图片描述

3️⃣添加 触屏 的驱动头文件
同样在"lv_port_indev.c"文件下第14行,插入:#include “触摸屏的头文件”
在这里插入图片描述

4️⃣注释掉不需要的输入任务注册’

在文件 “lv_port_indev.c” 中,你还需要对输入设备初始化函数 lv_port_indev_init() 进行调整,以便仅启用触摸屏输入而禁用其他类型的输入设备注册。以下是具体的操作:

  1. 定位函数:首先,找到文件中的 lv_port_indev_init() 函数,它通常位于文件的中后部,大约在第70行左右。

  2. 识别并注释:在此函数内部,您会看到为不同输入设备(如触摸屏、鼠标、键盘、编码器和物理按键)注册处理任务的代码段。为了仅保留触摸屏输入,你需要:

    • 保留:触摸屏输入相关的注册代码,这部分是我们希望保持活跃的部分。

    • 注释掉:鼠标、键盘、编码器、物理按键等其它输入设备的注册代码。

示例修改:
在这里插入图片描述
5️⃣添加触摸检测函数

在"lv_port_indev.c"文件中,为了添加触屏检测功能,你需要对原有的触摸检测函数touchpad_is_pressed()进行如下👇修改,以使用特定的触屏检测函数。:

// 假设原先的 touchpad_is_pressed 函数位于约209行
bool touchpad_is_pressed(void) {// 第212行:插入魔女开发板提供的触屏检测函数// 注意:这里使用XPT2046_IsPressed()是魔女开发板提供的外部函数// 返回值已经是 0 代表未按下,1 代表已按下,符合LVGL要求return XPT2046_IsPressed();// 第213行:原有的 return false 应该被注释掉或直接删除// 注释如下:// // return false;
}// 其余代码...

如果你使用的是其他开发板或库(比如原子哥的STM32库),并且该库提供了不同的触屏检测接口,您只需将XPT2046_IsPressed()替换为对应的触屏检测函数名称,并确保该函数返回值逻辑与LVGL所需的逻辑相匹配(即0表示未按下,非0值表示按下)

例如,如果使用原子哥的库且触屏检测函数名为TP_IsPress(),并且它的返回值是1表示按下,0表示未按下,则代码应调整为:

bool touchpad_is_pressed(void) {// 使用原子哥STM32库的触屏检测函数return TP_IsPress();
}

🚨确保所做的修改符合您实际使用的硬件接口和函数逻辑。

6️⃣添加坐标获取函数
在"lv_port_indev.c"文件中,为了集成触屏坐标获取功能,你还需要修改坐标获取函数touchpad_get_xy(),确保LVGL能够正确读取到触摸点的坐标。

void touchpad_get_xy(lv_coord_t *x, lv_coord_t *y) {// 第221行:修改为调用魔女开发板提供的获取X坐标的函数(*x) = XPT2046_GetX(); // XPT2046_GetX()为魔女开发板中实现了X坐标获取的方法// 第222行:修改为调用获取Y坐标的函数(*y) = XPT2046_GetY(); // 同样地,XPT2046_GetY()用于获取Y坐标// 根据不同库或硬件,您可能需要调整这里的函数调用,确保它们返回的是当前触摸点的实际坐标值。
}

这段代码修改后,LVGL框架将能够通过调用touchpad_get_xy()来获取触屏上每次触摸事件的坐标信息。请注意,XPT2046_GetX()XPT2046_GetY()函数需要替换为是你硬件平台上正确的实现,用于读取触摸屏控制器坐标值。

7. 添加LVGL的文件引用

现在,我们已成功调整了LVGL显示和触摸功能相关的代码,接下来的步骤是将LVGL的功能实际整合到项目中,即在工程中“激活”LVGL,使其发挥作用。

1️⃣头文件包含
在主程序或相应的配置文件中,确保包含了必要的LVGL头文件。这通常涉及lvgl.h及其他之前配置的特定头文件,比如lv_port_disp.hlv_port_indev.h

#include "lvgl.h"
#include "lv_port_disp.h"
#include "lv_port_indev.h"

2️⃣ 初始化LVGL
在项目启动初期,调用LVGL的初始化函数,设置显示驱动和输入设备。这些在主循环开始前完成:

lv_init(); // 初始化LVGL库
lv_port_disp_init(); // 初始化显示驱动
lv_port_indev_init(); // 初始化输入设备

3️⃣初始化LCD、触摸屏
同样在main函数内、 while 循环之前,调用LCD初始化函数、触摸屏初始化函数

LCD_Init();                                         // 初始化 LCD
LCD_SetDir(1);                                      // 设置LCD的显示方向:横屏
XPT2046_Init(xLCD.width, xLCD.height,  xLCD.dir);   // 初始化触摸屏

4️⃣ 创建及管理UI元素
开始创建图形界面元素,如按钮、标签、滑块等。这通常涉及到使用LVGL的API来定义和控制界面组件:

static lv_obj_t *label = lv_label_create(lv_scr_act(), NULL); // 创建一个标签
lv_label_set_text(label, "Hello, LVGL!"); // 设置标签文本

5️⃣主循环集成LVGL任务处理
在您的主循环或任务调度中,定期调用LVGL的任务处理函数,以确保UI的实时响应和更新:

while(1) {// 处理其他任务...// 更新LVGL任务lv_task_handler(); // 或使用特定的调度函数,如lv_tick_inc()// 确保系统休眠或延时,避免CPU过载HAL_Delay(1 - 1);// 1ms延时
}

8. LVGL 心跳、任务刷新

LVGL图形库为了保证用户界面的流畅更新和响应,引入了心跳机制(Heartbeat)和任务刷新的概念。这些机制确保了LVGL能够及时处理图形渲染、输入事件和动画更新等任务。以下是关于如何在项目中正确实现LVGL心跳与任务刷新的基本指南:

心跳机制(Heartbeat)

LVGL的心跳机制主要通过周期性的调用来维持,它负责触发内部的任务调度,从而更新UI元素和处理后台任务。心跳频率影响着UI的流畅度和响应速度,一般推荐的频率为每秒大约60次(即大约每16毫秒一次)。

实现方法:

使用系统定时器:在嵌入式系统中,可以通过配置硬件定时器中断来定期触发心跳。定时器中断服务例程(ISR)中调用lv_tick_inc()函数来模拟心跳。

🚨但要注意在很多实时操作系统(RTOS)环境中,SysTick定时器通常被操作系统本身用于基本的时间管理,比如任务调度和时间片分配。因此,直接用SysTick来驱动LVGL心跳可能会与RTOS的核心功能冲突,或者限制系统的灵活性和可扩展性。

为了实现高精度的定时控制,建议利用TIM机制生成1毫秒的周期性中断,并将此中断配置为高级别优先级。通过在中断服务程序中触发LVGL心跳时钟,确保用户界面的实时更新。实现这一功能时,可灵活选择TIM外设及编程手段,涵盖直接操作寄存器、采用STM32标准库或是手动集成HAL库等不同策略。本文照仿【快速入门 LVGL】-- 1、STM32 工程移植 LVGL 将以CubeMX工具为例,配置TIM6以实现每1毫秒的精确中断调度。

1️⃣打开CubeMX并选择您的STM32型号

  • 启动STM32 CubeMX软件,选择或创建您的项目,指定目标STM32微控制器型号。

2️⃣配置TIM6

  1. 启用TIM6: 在“Pinout & Configuration”页面左侧的外设列表中找到TIM6,点击展开。

  2. 基本定时器配置:

    • 设置Prescaler(预分频器)以得到1ms的周期。例如,如果您的STM32运行在84MHz(这是很多STM32的默认频率),您需要设置预分频器为PSC = 84-1(因为 84000000 / 84- 1 = 100kHz,即每1us触发一次更新事件)。
    • 设置Counter Mode(计数模式)Up,意味着计数器从0递增到自动重载值(Autoreload)。
    • 设置Autoreload(自动重载)值为1000-1,配合上述预分频设置达到1ms中断周期。

    在这里插入图片描述

3️⃣设置中断优先级

转到Configuration -> NVIC Settings,在中断列表中找到TIM6,点击它并设置优先级为高。您可以根据系统中的其他中断需求调整具体的优先级数值。

确保Interrupt(更新中断)被勾选,这样每当计数器达到自动重载值时就会产生中断。
在这里插入图片描述

4️⃣生成代码

点击Project Manager,然后Generate Code,选择您偏好的IDE和工程类型,让CubeMX生成代码。

5️⃣编写中断服务例程

在生成的代码中,找到stm32f4xx_hal_tim.c文件,里面会有一个空的HAL_TIM_PeriodElapsedCallback函数,这是一个弱定义的函数,需要我们重写。

我们将这个函数实现,放到main.c的最后

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == TIM6)                     // 判断是哪个TIM产生的中断{// 在这里调用LVGL的心跳函数lv_tick_inc(1);                             // 给LVGL提供1ms的心跳时期}
}

6️⃣主函数中使能TIM6

确保在main.c或相应的主函数中,有代码初始化并使能TIM6

HAL_TIM_Base_Init(&htim6);
HAL_TIM_Base_Start_IT(&htim6); // 使用中断模式启动TIM6

🎊🎉🎉🎉🎉🎉🎉🎉🎉🎉🎊

恭喜你至此,关于时间精度的需求已成功解决,LVGL图形库的移植工作也圆满结束。激动人心的时刻来临,点击编译按键后,结果显示令人满意——0个错误!这意味着我们的移植不仅完成了,而且实现了无缝对接与优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/317209.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3.C++动态内存管理(超全)

目录 1 .C/C 内存分布 2. C语言中动态内存管理方式:malloc/calloc/realloc/free 3. C内存管理方式 3.1 new/delete操作内置类型 3.2 new和delete操作自定义类型 3.3 operator new函数 3.4 定位new表达式(placement-new) (了解) 4. 常…

java-springmvc 01 补充 javaweb 三大组件Servlet,Filter、Listener(源码都是tomcat8.5项目中的)

01.JavaWeb三大组件指的是:Servlet、Filter、Listener,三者提供不同的功能 这三个在springmvc 运用很多 Servlet 01.Servlet接口: public interface Servlet {/*** 初始化方法* 实例化servlet之后,该方法仅调用一次 * init方法必须执行完…

SpringCloud(微服务介绍,远程调用RestTemplate,注册中心Nacos,负载均衡Ribbon,环境隔离,进程和线程的区别)【详解】

目录 一、微服务介绍 1. 系统架构的演变 1 单体架构 2 分布式服务 3 微服务 2. SpringCloud介绍 SpringCloud简介 SpringCloud版本 3. 小结 二、远程调用RestTemplate【理解】 1. 服务拆分 1 服务拆分原则 2 服务拆分示例 1) 创建父工程 2) 准备用户服务 1. 用户…

Kubernetes TDengine 系列|安装 TDengine 的 Grafana 插件|Grafana监控TDengine数据

为了让Grafana 能够监控到TDengine 数据,快速集成搭建数据监测报警系统,所以直接安装TDengine 插件。 目录 一、安装 TDengine 的 Grafana 插件1、下载TDengine grafana插件2、解压到指定目录3、配置未签名插件 二、配置数据源,简单查询TDen…

python的输入输出(爽文,备忘,查询,友好)

Python中的输入输出主要涉及到输入函数和输出函数。 输出函数:print() print() 函数用于将信息输出到屏幕上。它可以输出字符串、变量的值,以及其他各种数据类型。 name "Alice" age 30 print("姓名:", name, "年龄:&quo…

气象数据nc数据矢量化处理解析及可视化

气象数据可视化是将气象学领域中复杂的数据集转化为图形或图像的过程,以直观展示天气现象、气候模式、趋势和预报结果。气象数据的可视化技术广泛应用于科学研究、气象预报、航空、航海、农业生产、灾害预警系统、城市规划、公众服务等领域。以下是一些关键的气象数…

mac虚拟机软件哪个好 mac虚拟机怎么安装Windows 苹果Mac电脑上受欢迎的主流虚拟机PK Parallels Desktop和VM

什么是苹果虚拟机? 苹果虚拟机是一种软件工具,它允许在非苹果硬件上运行苹果操作系统(如ios)。通过使用虚拟机,您可以在Windows PC或Linux上体验和使用苹果的操作系统,而无需购买苹果硬件。 如何使用苹果虚…

【智能算法】海象优化算法(WO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2024年,M Han等人受到海象群体自然行为启发,提出了海象优化算法(Walrus Optimizer, WO)。 2.算法原理 2.1算法思想 WO灵感来自海象通过接收关键信号…

设计模式-01 设计模式单例模式

设计模式-01 设计模式单例模式 目录 设计模式-01 设计模式单例模式 1定义 2.内涵 3.使用示例 4.具体代码使用实践 5.注意事项 6.最佳实践 7.总结 1 定义 单例模式是一种设计模式,它确保一个类只能被实例化一次。它通过在类内部创建类的唯一实例并提供一个全…

飞书API(6):使用 pandas 处理数据并写入 MySQL 数据库

一、引入 上一篇了解了飞书 28 种数据类型通过接口读取到的数据结构,本文开始探讨如何将这些数据写入 MySQL 数据库。这个工作流的起点是从 API 获取到的一个完整的数据,终点是写入 MySQL 数据表,表结构和维格表结构类似。在过程中可以有不同…

完美解决AttributeError: module ‘backend_interagg‘ has no attribute ‘FigureCanvas‘

遇到这种错误通常是因为matplotlib的后端配置问题。在某些环境中,尤其是在某些特定的IDE或Jupyter Notebook环境中,可能会因为后端配置不正确而导致错误。错误信息提示 module backend_interagg has no attribute FigureCanvas 意味着当前matplotlib的后…

首页最新 多IP浏览器防关联:如何配置多个独立且稳定的IP地址?

在互联网时代,IP地址的重要性不言而喻。然而,IP关联问题却成为一项令人担忧的隐私和安全挑战。针对这个问题,多IP浏览器是一种解决方案,可以帮助用户单独配置多个独立且稳定的IP地址,有效地防止IP关联。 一、IP关联是…

【Python小练】求斐波那契数列第n个数

题目 输出斐波那契数列第n个数。 分析 首先我们要知道,斐波那契数列,这个数列从第三位开始等于前两个数的和,要知道数列第n个数(n>2),就要知道其前两相的值,着就需要用到递归了。来看一下吧…

开源、轻量、易用的服务器实时监控工具:哪吒探针

本文首发于只抄博客,欢迎点击原文链接了解更多内容。 前言 哪吒探针是一个开源、轻量、易用的服务器监控、运维工具,它有以下几个特点: 一键安装:可以一键安装面板与 Agent,并且支持 Linux、Windows、MacOS、OpenWRT…

纯血鸿蒙APP实战开发——发布图片评论

介绍 本示例将通过发布图片评论场景,介绍如何使用startAbilityForResult接口拉起相机拍照,并获取相机返回的数据。 效果图预览 使用说明 通过startAbilityForResult接口拉起相机,拍照后获取图片地址。 实现思路 创建CommentData类&#…

VSCode 配置 CMake

VSCode 配置 C/C 环境的详细过程可参考:VSCode 配置 C/C 环境 1 配置C/C编译环境 方案一 如果是在Windows,需要安装 MingW,可以去官网(https://sourceforge.net/projects/mingw-w64/)下载安装包。 注意安装路径不要出现中文。 打开 windows…

虚析构与纯虚析构

这里的new Cat("Tom"&#xff09;是由于基类函数中的构造函数里面带有string变量 1. 法一:利用虚函数&#xff0c;虚化基类中的析构函数 virtual ~Animal() { cout << "动物的析构函数调用" << endl; } 2. 法二:利用纯…

VitePress 构建的博客如何部署到 Netlify 平台?

VitePress 构建的博客如何部署到 Netlify 平台&#xff1f; 前言 之前写了篇文章【使用 Vitepress 构建博客并部署到 github 平台】&#xff0c;有个老哥说 github page 访问太慢了&#xff0c;希望放到 Netlify 平台上面。 咱也没部署过&#xff0c;就试了一下&#xff0c;发…

低功耗数字IC后端设计实现典型案例| UPF Flow如何避免工具乱用Always On Buffer?

下图所示为咱们社区低功耗四核A7 Top Hierarchical Flow后端训练营中的一个案例&#xff0c;设计中存在若干个Power Domain&#xff0c;其中Power Domain2(简称PD2)为default Top Domain&#xff0c;Power Domain1&#xff08;简称PD1&#xff09;为一个需要power off的domain&…

【再探】设计模式—抽象工厂及建造者模式

抽象工厂模式和建造者模式都属于创建型模式。两者都能创建对应的对象&#xff0c;而创建者模式更侧重于创建复杂对象&#xff0c;将对象的创建过程封装起来&#xff0c;让客户端不需要知道对象的内部细节。 1 抽象工厂模式 需求&#xff1a; 在使用工厂方法模式时&#xff0…