Melis4.0[D1s]:2.启动流程(GUI桌面加载部分)跟踪笔记

news2024/11/26 14:55:23

文章目录

  • 0. 控制台输出信息等级设置
    • 0.1 设置log level = 4 无法正常启动
  • 1.宏观启动流程
    • 1.1 控制台入口函数finsh_thread_entry()执行《startup.sh》
    • 1.2 《startup.sh》启动桌面GUI模块
      • 1.2.1 《startup.sh》加载 desktop.mod
      • 1.2.2 desktop.mod加载 init.axf
      • 1.2.3 init.axf 介绍
        • 1.2.3.1 INIT_MOpen()调用application_init_process()启动消息死循环
        • 1.2.3.2 application_init_process()调用gscene_bgd_init()设置背景图片
        • 1.2.3.3 application_init_process()调用_process_init()
  • 2. GUI桌面app加载
    • 2.1 app_root.axf 介绍
    • 2.2 主函数 app_root_start()调用app_root_wincreate()创建根窗口
    • 2.2 主函数 _home_on_create()调用init_multi_screen_res()初始化图片、文字资源
    • 2.3 dsk_theme_open() 通过ID获取图片数据
  • 3.绘制主界面
    • 3.1 draw_multi_screen_icon_coor_rect()函数调用表格

在阅读Melis4.0GUI加载流程,顺手做笔记,供自己查阅。

0. 控制台输出信息等级设置

为了方便跟踪代码执行流程,可以参考控制台的输出信息。控制台输出调试信息有6个等级,分别为0-5:

#define OPTION_LOG_LEVEL_CLOSE             0
#define OPTION_LOG_LEVEL_LOG               1
#define OPTION_LOG_LEVEL_ERROR             2
#define OPTION_LOG_LEVEL_WARNING           3
#define OPTION_LOG_LEVEL_INFO              4
#define OPTION_LOG_LEVEL_MESSAGE           5

在GUI源码中,有很多INFO级别的调试信息,可以打开观察学习:
在这里插入图片描述

0.1 设置log level = 4 无法正常启动

设置log level = 4,测试了一下,显示开机界面后就死机了,一直打印下面的信息,等了几分钟都没有反应:

[INF]: [riscv_cpu_handle_exception:0271]:                   ecall SYSCALL_FROM_S

只好设置为log level = 3 。可以正常使用,但是界面切换比 log level = 2会慢一点。

1.宏观启动流程

Melis启动顺序的介绍在《Melis4.0 RTOS_方案快速开发指南》,这里先上一张该文档的截图:
在这里插入图片描述

1.1 控制台入口函数finsh_thread_entry()执行《startup.sh》

这个函数在文件 《ekernel\subsys\finsh_cli\shell_entry.c》 中,在进入cli(command line interface)之前,会执行《startup.sh》文件。
在这里插入图片描述

1.2 《startup.sh》启动桌面GUI模块

1.2.1 《startup.sh》加载 desktop.mod

该文件的路径为 《projects\d1s-mq\data\UDISK\startup.sh》 ,内容如下:

echo "Execute startup script begin.............."

#insmod d:\mod\display.mod
insmod d:\mod\orange.mod
insmod d:\apps\desktop.mod
echo "...............Execute startup script end"

desktop.mod就是桌面GUI的入口app。它主要由《D1s-Melis\livedesk\beetles\mod_desktop\》目录下的文件编译链接产生。该目录下的Makefile文件:

ccflags-y           +=  -DEPDK_DEBUG_LEVEL=EPDK_DEBUG_LEVEL_LOG_ALL         \
                        -DUSED_BY_DESKTOP                                   \
                        $(SOLUTION_INCLUDE)

usrlibs-y           +=  -L$(srctree)/${elibrary-libs}/          \
                        --start-group                           \
                        -lsyscall    -lminic                    \
                        -llzma       -lcharset                  \
                        -lmediainfo                             \
                        --end-group

#-lmediainfo
MOD_NAME            := desktop
SUF_NAME            := mod
PRJ_PATH            := $(MELIS_BASE)/projects/${TARGET_BOARD}/data/UDISK/apps/
TEMP_PATH           := $(MELIS_BASE)/emodules/bin

$(MOD_NAME)-objs    += desktop_api.o
$(MOD_NAME)-objs    += mod_desktop.o
$(MOD_NAME)-objs    += magic.o
$(MOD_NAME)-objs    += built-in.o

$(MOD_NAME)-objs    += engine/
$(MOD_NAME)-objs    += framework/
$(MOD_NAME)-objs    += functions/
$(MOD_NAME)-objs    += msg_srv/
$(MOD_NAME)-objs    += util/

include $(MELIS_BASE)/scripts/Makefile.mods

1.2.2 desktop.mod加载 init.axf

mod_desktop.c 中加载GUI的函数 DESKTOP_MOpen()代码,其中的 init.axf 就是完成GUI准备工作可执行文件 :

__mp *DESKTOP_MOpen(__u32 mid, __u32 mod)
{
    __log("DESKTOP_MOpen.");
    int ret = -1, value = 0;
    desktp_data.mid  = mid;
	
	ret = esCFG_GetKeyValue("card_product", "card_product_used", (int32_t *)&value, 1);
	if (ret == -1 || value == 0)
	{
		desktp_data.init_id = esMODS_MInstall(BEETLES_APP_ROOT"apps\\init.axf", 0);
	} else if (value) {
		desktp_data.init_id = esMODS_MInstall(BEETLES_APP_ROOT"apps\\init_sd_product.axf", 0);
	}
    desktp_data.init_mp = esMODS_MOpen(desktp_data.init_id, 0);
    return (__mp *)&desktp_data;
}

1.2.3 init.axf 介绍

请注意,这里有2个C文件名很相似:init_mod.c 和 mod_init.c 。
init.axf《livedesk\beetles\init》 目录下的源码编译链接产生。看看该目录下的Makefile部分内容:
在这里插入图片描述
主要函数INIT_MOpen()在文件 《livedesk\beetles\init\mod_init.c》 中:

__mp *INIT_MOpen(__u32 mid, __u32 mod)
{
    __inf("----------------------INIT_MOpen --------------------------\n");
    msg_emit_init();

    init_data.mid      = mid;
    init_data.init_tid = esKRNL_TCreate(application_init_process, NULL, 0x10000, KRNL_priolevel3);
    esKRNL_TaskNameSet(init_data.init_tid, "initprocess");
    if (init_data.init_tid == 0)
    {
        __err(" application_init_process main thread create error\n");
        return NULL;
    }

    return (__mp *)&init_data;
}

1.2.3.1 INIT_MOpen()调用application_init_process()启动消息死循环

application_init_process()在文件 《livedesk\beetles\init\init_mod.c》 中:

void application_init_process(void *arg)
{
	.....
	__log("%s %d before init_mainwin_create", __FUNCTION__, __LINE__);
    init_mainwin        = init_mainwin_create();
    .....
    _process_init();    
    __log("%s %d before GUI_GetMessageEx", __FUNCTION__, __LINE__);
    /* message loop*/
    while (GUI_GetMessageEx(&msg, init_mainwin))    // 消息循环
    {
#ifdef __MSG
        if (msg.id != GUI_MSG_TIMER)
        {
            __msg("msg.h_deswin=%x, msg.id=%d, msg.dwAddData1=%d,msg.dwAddData2=%d", msg.h_deswin, msg.id, msg.dwAddData1, msg.dwAddData2);
        }
#endif
        ret = GUI_DispatchMessage(&msg);        // 分发消息到回调

        if (msg.p_arg)                          // 同步消息回应
        {
            GUI_SetSyncMsgRetVal(&msg, ret);
            GUI_PostSyncSem(&msg);
        }
    }
    __log("%s %d want to shutdown_logo_show", __FUNCTION__, __LINE__);//	add by hwd00001
    .....
}

上面的代码一直停留在消息循环中,正常情况不会退出循环。我在消息循环后面加了打印log信息,一直没有看到log输出。

1.2.3.2 application_init_process()调用gscene_bgd_init()设置背景图片

逐级调用函数所在文件
application_init_process()livedesk\beetles\init\init_mod.c
gscene_bgd_init()livedesk\beetles\init\background\gscene_backgrd.c
gscene_bgd_set_status_show()livedesk\beetles\init\background\gscene_backgrd.c
gscene_bgd_update_filename()livedesk\beetles\init\background\gscene_backgrd.c
gscene_bgd_get_default_file()livedesk\beetles\init\background\gscene_backgrd.c
get_logo_mode()livedesk\beetles\init\background\fb_lib\backlayer_lib.c

这里重点分析一下get_logo_mode()

uint32_t get_logo_mode(void)
{
    if (esCFG_GetKeyValue("backlayer", "backlayer_mode", &bk_mode, 1) != EPDK_OK)
    {
        bk_mode = 0;
        __err("get_logo_mode err!");
    }
    return bk_mode;
}

esCFG_GetKeyValue()函数是从 sys_config_nor.fex 中读取键值,这里是 backlayer_mode=0:
在这里插入图片描述
gscene_bgd_get_default_file()于是获取的图片名称为 res\bg_default0.jpg ,对应的SDK目录下的 《projects\d1s-mq\data\UDISK\res\bg_default0.jpg》 ,只要更换这个图片,使用相同的名称,就可以改变背景图片。
sys_config.fex 的读写可参考官方文档《Melis4.0_RTOS_配置(sys_config)_开发指南.pdf》
gscene_bgd_get_default_file()部分源码:

static int32_t gscene_bgd_get_default_file(char *filename)
{
    int32_t     index;
    char        path[BG_MAX_CHAR_LEN] = {0};

    index   = gscene_bgd_get_default_bg_index();
    if (get_logo_mode() == JPG_MODE)
    {
        eLIBs_sprintf(path, "%sbg_default%d.jpg", BG_DEFAULT_PATH, index);
    }
    else
    {
        eLIBs_sprintf(path, "%sbg_default%d.bgd", BG_DEFAULT_PATH, index);
    }
    ......
}

1.2.3.3 application_init_process()调用_process_init()

application_init_process()_process_init()位于同一个C文件。

static void _process_init(void)
{
    H_WIN                   scene_adjust;
    int32_t flag            = 2;
    char load_para[1024]    = {0};

    _framework_init();

    #if CONFIG_SUPPORT_TOUCHPANEL
    open("/dev/tpadc_rtp", O_WRONLY);
    #endif
    
    eLIBs_memcpy(load_para, &flag, 4);
    __msg("********_process_init***********");
    activity_set_load_para("root", "", load_para, sizeof(load_para));
    activity_load_app("application://app_root");
}

到此处,加载 app_root.axf,启动GUI桌面程序。

2. GUI桌面app加载

2.1 app_root.axf 介绍

app_root.axf《livedesk\beetles\sun20iw1_app\apps》 目录下的源码编译链接产生。看看该文件下的Makefile部分内容:
在这里插入图片描述

2.2 主函数 app_root_start()调用app_root_wincreate()创建根窗口

app_root_start()在文件 《livedesk\beetles\sun20iw1_app\apps\app_root\app_root.c》 中:

/**********************************************************************************************************************
插件接口实现
**********************************************************************************************************************/
static int32_t app_root_start(Activity *activity)
{
    __inf("**************app_root plugin start!**************** \n");
    __inf("****************************\n");
    
    app_root_init_res();

    root_activity       = activity;
    happ_root_manwin    = app_root_wincreate(activity);

    if (NULL == happ_root_manwin)
    {
        __inf("	 app_root_wincreate fail! \n");
        return EPDK_FAIL;
    }
    else
    {
        GUI_WinSetFocusChild(happ_root_manwin);
        __inf("	 app_root_wincreate success! \n");
        return EPDK_OK;
    }
}

E:\C_code\D1s-Melis\livedesk\beetles\sun20iw1_app\apps\app_root\app_root_scene.c

逐级调用函数所在文件
app_root_start()livedesk\beetles\sun20iw1_app\apps\app_root\app_root_scene.c
app_root_wincreate()livedesk\beetles\sun20iw1_app\apps\app_root\app_root_scene.c
app_root_win_proc()livedesk\beetles\sun20iw1_app\apps\app_root\app_root_scene.c
app_home_create()livedesk\beetles\sun20iw1_app\apps\multi_screen_home\app_multi_screen_home.c
_home_on_create()livedesk\beetles\sun20iw1_app\apps\multi_screen_home\desktop_scene\desktop_scene.c
desktop_scene_create()livedesk\beetles\sun20iw1_app\apps\multi_screen_home\desktop_scene\desktop_scene.c

2.2 主函数 _home_on_create()调用init_multi_screen_res()初始化图片、文字资源

跟踪到这里,虽然还不知道如何显示界面的图片,不过已经明白如何替换图片了。
init_multi_screen_res()初始化主界面的8个图片(每个图片分焦点和非焦点2种),8个字符串。

static const __s32  multi_screen_uf_icon[HOME_UNFOCUS_ICON_NUM] =
{
    //固定应用
    ID_HOME_NEW_EXPLORER_UF_BMP,
    ID_HOME_NEW_MOVIE_UF_BMP,
    ID_HOME_NEW_MUSIC_UF_BMP,
    ID_HOME_NEW_PICTURE_UF_BMP,
    ID_HOME_NEW_EBOOK_UF_BMP,
    ID_HOME_NEW_FM_UF_BMP,
    ID_HOME_NEW_RECORD_UF_BMP,
    ID_HOME_NEW_SETTING_UF_BMP,
};
static const __s32  multi_screen_f_icon[HOME_FOCUS_ICON_NUM] =
{
    //固定应用选中
    ID_HOME_NEW_EXPLORER_FC_BMP,
    ID_HOME_NEW_MOVIE_FC_BMP,
    ID_HOME_NEW_MUSIC_FC_BMP,
    ID_HOME_NEW_PICTURE_FC_BMP,
    ID_HOME_NEW_EBOOK_FC_BMP,
    ID_HOME_NEW_FM_FC_BMP,
    ID_HOME_NEW_RECORD_FC_BMP,
    ID_HOME_NEW_SETTING_FC_BMP
};

static const int32_t  multi_screen_string[MULTI_SCREEN_STRING_MAX] =
{
    STRING_HOME_EXPLORER,
    STRING_HOME_MOVIE,
    STRING_HOME_MUSIC,
    STRING_HOME_PHOTO,
    STRING_HOME_EBOOK,
    STRING_HOME_FM,
    STRING_HOME_RECORD,
    STRING_HOME_SETTING
};
int32_t init_multi_screen_res(pmulti_screen_ui_t pui, __s16 current_focus)
{
    uint32_t   i;
    //初始化unfocus ICON 资源
    for (i = HOME_UNFOCUS_ICON_START; i < HOME_UNFOCUS_ICON_NUM; i++)
    {
        if (multi_screen_uf_icon[i])
        {
            pui->bmp_uf[i] = dsk_theme_open(multi_screen_uf_icon[i]);

            if (pui->bmp_uf[i] == NULL)
            {
                __wrn("init_multi_screen_res() index:%d open fail\n", i);
            }
        }
        else
        {
            pui->bmp_uf[i] = NULL;
        }
    }

    /*focus icon*/
    pui->bmp_f[current_focus] = dsk_theme_open(multi_screen_f_icon[current_focus]);

    //初始化String 资源
    for (i = 0; i < MULTI_SCREEN_STRING_MAX; i++)
    {
        dsk_langres_get_menu_text(multi_screen_string[i], pui->lang[i], GUI_NAME_MAX);
    }

    return EPDK_OK;
}

在这里插入图片描述
以图中文件管理图标为例,说说我的理解。
文件管理的非焦点图标ID为 ID_HOME_NEW_EXPLORER_UF_BMP ,焦点图标为 ID_HOME_NEW_EXPLORER_FC_BMP,对应的资源文件:
在这里插入图片描述
详细的可以阅读《livedesk\beetles\sun20iw1_app\res\theme》目录下的文件,我现在只是一知半解。
在这里插入图片描述
《facebuildercmd》生成 theme.htheme.bin,h文件供源码使用,bin文件则嵌入到image中,在运行中使用。

2.3 dsk_theme_open() 通过ID获取图片数据

dsk_theme_open()部分源码:

int32_t init_multi_screen_res(pmulti_screen_ui_t pui, __s16 current_focus)
{
......
            pui->bmp_uf[i] = dsk_theme_open(multi_screen_uf_icon[i]);
......

    /*focus icon*/
    pui->bmp_f[current_focus] = dsk_theme_open(multi_screen_f_icon[current_focus]);
......
}    

dsk_theme_open()函数就是根据 theme.h 范围内的ID,到 theme.bin找到相应的图片数据。

3.绘制主界面

3.1 draw_multi_screen_icon_coor_rect()函数调用表格

逐级调用函数所在文件
desktop_scene_create()livedesk\beetles\sun20iw1_app\apps\multi_screen_home\desktop_scene\desktop_scene.c
desktop_scene_frm_manager_proc()livedesk\beetles\sun20iw1_app\apps\multi_screen_home\desktop_scene\desktop_scene.c
desktop_scene_view_show_all()livedesk\beetles\sun20iw1_app\apps\multi_screen_home\desktop_scene\desktop_scene.c
draw_multi_screen_icon_coor_rect()livedesk\beetles\sun20iw1_app\apps\multi_screen_home\ui\multi_screen_ui.c

draw_multi_screen_icon_coor_rect()用来绘制界面图标,其中 ==uf_rect_800_480[8]==是指定图标的区域:

draw_multi_screen_icon_coor_rect(self->pui, HOME_UNFOCUS_ICON_START + i, 
								EPDK_FALSE, &uf_rect_800_480[i]);

《livedesk\beetles\sun20iw1_app\apps\multi_screen_home\desktop_scene\desktop_scene.h》部分内容:

static GUI_RECT  string_rect_800_480[MULTI_SCREEN_STRING_MAX] =
{
    {16 + 45,                                   80 + 100,   16 + 180 - 45,                                      80 + 142},
    {16 + 180 + 16 + 45,                        80 + 100,   16 + 180 + 16 + 180 - 45,                           80 + 142},
    {16 + 180 + 16 + 180 + 16 + 45,             80 + 100,   16 + 180 + 16 + 180 + 16 + 180 - 45,                80 + 142},
    {16 + 180 + 16 + 180 + 16 + 180 + 16 + 45,  80 + 100,   16 + 180 + 16 + 180 + 16 + 180 + 16 + 180 - 45,     80 + 142},

    {16 + 45,                                   80 + 50 + 100 + 142,    16 + 180 - 45,                                      80 + 142 + 50 + 142},
    {16 + 180 + 16 + 45,                        80 + 50 + 100 + 142,    16 + 180 + 16 + 180 - 45,                           80 + 142 + 50 + 142},
    {16 + 180 + 16 + 180 + 16 + 45,             80 + 50 + 100 + 142,    16 + 180 + 16 + 180 + 16 + 180 - 45,                80 + 142 + 50 + 142},
    {16 + 180 + 16 + 180 + 16 + 180 + 16 + 45,  80 + 50 + 100 + 142,    16 + 180 + 16 + 180 + 16 + 180 + 16 + 180 - 45,     80 + 142 + 50 + 142},
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#与三菱PLC MC协议通信,Java与三菱PLC MC协议通信

三菱PLC的MC协议是一种常用的通信协议&#xff0c;用于实现三菱PLC与其他设备之间的通信。以下是一些关于MC协议的基本信息&#xff1a;协议格式MC协议的通信数据格式如下&#xff1a;数据头网络编号PC编号目标模块IO编号目标模块站号本机模块IO编号本机模块站号请求数据长度请…

Linud SSH与SCP的配置

目录 配置SSH协议 配置服务器通过密钥进行认证 配置SCP完成文件传输 ssh协议讲解 SSH协议理论讲解_静下心来敲木鱼的博客-CSDN博客https://blog.csdn.net/m0_49864110/article/details/128500490?ops_request_misc%257B%2522request%255Fid%2522%253A%2522167704203816800…

不加大资金投入,仅凭智能名片如何解决企业营销难题的?

中国90%以上的中小企业想要竞争和发展&#xff0c;就必须推广自己的品牌&#xff0c;提高自己的知名度。在小程序之前&#xff0c;APP是主流&#xff0c;但大多数中小企业负担不起APP的开发和昂贵的营销成本。 进入微信互联网时代后&#xff0c;为了帮助企业以更低的成本获得…

浙大MEM现场小组复试经验分享

作为2019年上岸浙大MEM项目的学姐一枚&#xff0c;很高兴收到杭州达立易考教育老师的邀请&#xff0c;给大家分享下现场面试的经历。先来看下复试流程是怎么样的。1、体检所有考生须参加。体检需在复试前完成&#xff08;未体检考生不得参加复试&#xff09;。 2、资格审查&…

历时半年!从外包到现在阿里网易25K,分享一下自己的涨薪经验

前言 首先自我介绍一下&#xff0c;本人普通一本毕业&#xff0c;年初被老东家裁员干掉了&#xff0c;之后一直住在朋友那混吃等死&#xff0c;转折是今年年后&#xff0c;二月初的时候和大佬吃了个饭&#xff0c;觉得自己不能这样下去了&#xff0c;拿着某大佬给我的面试资料…

你知道IT运维的本质是什么吗?

大家好&#xff0c;我是技福的小咖老师。 之前看到个文章&#xff0c;说运维的本质是“可视化”&#xff0c;甚至还有人说是DevOps。不可否认&#xff0c;“可视化”是运维过程中非常重要的一个环节&#xff1b;DevOps则是开发运维一体化非常重要的工具。 究其根本&#xff0…

【09-JVM面试专题-实例化过程详细讲讲?对象的基本结构你知道吗?TLAB堆上内存分配是怎么样的?你了解这个TLAB吗?】

实例化过程详细讲讲&#xff1f;对象的基本结构你知道吗&#xff1f;TLAB堆上内存分配是怎么样的&#xff1f;你了解这个TLAB吗&#xff1f; 实例化过程详细讲讲&#xff1f;对象的基本结构你知道吗&#xff1f;TLAB堆上内存分配是怎么样的&#xff1f;你了解这个TLAB吗&#x…

EMR Studio Workspace 访问 Github ( 公网Git仓库 )

EMR Studio Workspace访问公网Git仓库 会遇到很多问题,由于EMR Studio不能给出任何有用的错误信息,导致排查起来非常麻烦。下面总结了若干项注意事项,可以避免踩坑。如果你遇到了同样的问题,请根据以下部分或全部建议去修正你的环境,问题即可解决。本文地址:https://laur…

pc端集成企业微信的扫码登录及遇到的问题

集成步骤&#xff1a; 1、在企业微信后台中添加应用 2、记录下应用的相关信息&#xff0c;在后文要用到 3、引入企业微信js 旧版&#xff1a;http://rescdn.qqmail.com/node/ww/wwopenmng/js/sso/wwLogin-1.0.0.js 新版&#xff08;20210803更新&#xff09;&#xff1a;http…

kotlin学习教程

kotlin的方法 可以直接调用 不用 new? 2.kotlin关于字符串 用 $拼接变量 3.kotlin 类 方法 变量 可以同级的&#xff0c;同级的 方法 和 变量(常量) 是 生成了 一个新的 xxxKt.class &#xff0c;并且都是 static的&#xff0c; 4.kotlin的类&#xff0c;方法&#xff0c;默认…

JVM调优方式

对JVM内存的系统级的调优主要的目的是减少GC的频率和Full GC的次数。 1.Full GC 会对整个堆进行整理&#xff0c;包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收&#xff0c;所以比较慢&#xff0c;因此应该尽可能减少Full GC的次数。 2.导致Full GC的原因 1)年老…

消息中间件

为什么要使用消息中间件同步通信&#xff1a;耗时长&#xff0c;受网络波动影响&#xff0c;不能保证高成功率&#xff0c;耦合性高。1.同步方式&#xff08;耗时长&#xff09;&#xff1a;同步方式的问题&#xff1a;当一个用户提交订单到成功需要300ms300ms300ms20ms 920ms…

民锋国际期货:2023,既艰难又充满希望,既纷乱又有无数机会。

不管是官方还是民间&#xff0c;各种信号都表明&#xff0c;2023年是一个拼经济的年份。 通货膨胀带来的需求量的增加&#xff0c;与中国经济高速发展带来的供给量增加&#xff0c;二者共同构成了我们的物价。 做一个长期主义者&#xff0c;做一个坚定看好中国未来的人&#…

SpringBoot(powernode)(内含教学视频+源代码)

SpringBoot&#xff08;powernode&#xff09;&#xff08;内含教学视频源代码&#xff09; 教学视频源代码下载链接地址&#xff1a;https://download.csdn.net/download/weixin_46411355/87484637 目录SpringBoot&#xff08;powernode&#xff09;&#xff08;内含教学视频…

AcWing3490.小平方——学习笔记

目录 题目 代码 AC结果 思路 题目 3490. 小平方 - AcWing题库https://www.acwing.com/problem/content/3493/ 代码 import java.util.Scanner;public class Main {public static void main(String[] args){Scanner input new Scanner(System.in);int target input.nextI…

Python 自动化测试必会技能板块—unittest框架

说到 Python 的单元测试框架&#xff0c;想必接触过 Python 的朋友脑袋里第一个想到的就是 unittest。的确&#xff0c;作为 Python 的标准库&#xff0c;它很优秀&#xff0c;并被广泛应用于各个项目。但其实在 Python 众多项目中&#xff0c;主流的单元测试框架远不止这一个。…

谷歌seo关键词怎么做?Google如何优化关键词

本文主要分享谷歌seo关键词怎么去操盘才能更好的提升排名&#xff0c;我们立刻来学习一下。 本文由光算创作&#xff0c;有可能会被剽窃和修改&#xff0c;我们佛系对待这种行为吧。 谷歌seo关键词如何优化&#xff1f;这里我们提供一个谷歌seo优化的公式 答案是&#xff1a…

Vue基础15之消息订阅与发布、TodoList消息订阅与发布、TodoList编辑功能

Vue基础15消息订阅与发布安装pubsub-js库使用main.jsSchool.vueStudent.vue总结&#xff1a;消息订阅与发布&#xff08;pubsub&#xff09;TodoList-消息的订阅与发布将Item的deleteTodo使用消息订阅与发布App.vueMyItem.vueMyList.vueTodoList-编辑App.vueMyItem.vue完善编辑…

【极海APM32替代笔记】HAL库ADC测量精度提高方案(利用内部参考电压VREFINT计算VDDA来提高精度)

【极海APM32替代笔记】HAL库ADC测量精度提高方案&#xff08;利用内部参考电压VREFINT计算VDDA来提高精度&#xff09; 多数STM32的MCU 都没有内部基准电压 如L496系列 但在外接VDDA时&#xff08;一般与VCC 3.3V连接&#xff09; 有可能VCC不稳定 导致参考电压不确定 从而使A…

【并发编程十八】线程局部存储(TLS)

【并发编程十八】线程局部存储(TLS&#xff09;一、定义二、线程局部存储的实现1、windows系统2、linux系统3、c11三、windows系统1、线程局部存储是分块的&#xff08;TLS_MINIMUM_AVAILABLE&#xff09;2、获得索引3、通过索引&#xff1a;存储数据、取出数据4、释放索引和内…