IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

news2025/6/12 19:32:47

文章目录

  • 概述
  • HelloWorld 工程
    • C/C++配置
    • 编译器主配置
    • Makefile脚本
    • 烧录器主配置
    • 运行结果
    • 程序调用栈
  • 任务管理实验
    • 实验结果
    • osal 系统适配层
    • osal_task_create
  • 其他实验
    • 实验源码
    • 内存管理实验
    • 互斥锁实验
    • 信号量实验
  • CMISIS接口实验
    • 还是得JlINK
    • CMSIS 简介
    • LiteOS->CMSIS
    • 任务间消息交互
    • 执行结果
  • 其他

概述

本实验基于LiteOS Studio 工具进行物联网终端的开发,使用LiteOS操作系统进行物联网开发板的控制。实验主要目的:

  • 掌握LiteOS Studio的使用
  • 掌握LiteOS操作系统任务的使用
  • 掌握LiteOS操作系统内存管理的使用
  • 掌握LiteOS操作系统互斥量和信号量使用
  • 熟悉LCD屏幕的使用 (在实验4中)
  • 熟悉开发板的LED和按键使用 (在实验4中)
  • OSAL接口使用
  • CMSIS接口使用(CMSIS任务和消息队列接口使用等)

@History
在进行本实验前,请先阅读 #<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 LiteOS Studio + GCC + JLink>#,文中较详细的介绍了LiteOS Studio的搭建和使用方法,文中我们也提及了LiteOS工程(LiteOS_Lab_HCIP),但没有使用它,而是直接使用了BearPi-IoT_Std_LiteOS 源码。为了贴合实验指导书的步骤,我们在这里选用 LiteOS_Lab_HCIP源码。

在写本文前,我已经尝试了不同形式的物联网开发IDE,分别如下几篇文章中:
#<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 LiteOS Studio + GCC + JLink>#
#<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 VSCode + IoT Link 插件>#
#<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 VSCode + GitBash + GCC工具>#

如果主要目标是为了完成HCIP-IOT实验,我是建议选用指导书中指定的LiteOS Studio开发环境,不仅是因为贴合指导书中的步骤,减少不必要的麻烦。华为云IoTDA中提及的VSCode + IoT Link 模式,由于要使用低版本的VSCode,让人很不爽,配置和调试操作都体验不咋地。相比较而言,LiteOS Studio 虽然是基于VSCode的,但其与你已经安装的VSCode不会产生环境变量或软件安装层次的冲突,算是优势吧。

HelloWorld 工程

请参见 #<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 LiteOS Studio + GCC + JLink># 完成此部分实验。
在这里插入图片描述

C/C++配置

c_cpp_properties.json 是 VSCode 中 C/C++ 扩展的核心配置文件,主要用于配置代码分析和开发环境,确保 IntelliSense(智能代码补全、错误检查等)能够准确理解项目结构和编译器行,需要作出如下修改,
在这里插入图片描述

编译器主配置

关于目标板卡配置、组件配置、编译器,参见前文提到的 #<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 LiteOS Studio + GCC + JLink>#,不再赘述。编译器配置下的Makefile脚本选择H:\HuaWeiYun\LiteOS_Lab_HCIP\targets\STM32L431_BearPi\GCC下的Makefile文件。
在这里插入图片描述

Makefile脚本

这块在HCIP-IoT实验手册中并没有提及。Makefile(脚本)最终编译哪个示例工程,是由LiteOS_Lab_HCIP\targets\STM32L431_BearPi.config文件决定的。
在这里插入图片描述
最简单的改变该文件的方法是,从\Demos各示例程序文件夹下拷贝defaults.sdkconfig全部内容,当然,也可以借助menuconfig和genconfig生成,那就稍微复杂些了,可以参考本专栏下其他文章。

烧录器主配置

烧录器、调试器配置,参见前文提到的 #<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 VSCode + IoT Link 插件>#,采用ST-Link+OpenOCD模式。烧录文件要编译成功后才有哦。但还是建议使用JLink模式。
在这里插入图片描述
但还是建议使用JLink模式。这一节使用ST-Link+OpenOCD,体验很差劲。
在这里插入图片描述

运行结果

小熊派开发板的这个串口,感觉不太顶用,我就拔插过两三次,感觉就有接触不良的情况偶发啦。
在这里插入图片描述

在这里插入图片描述

程序调用栈

LiteOS_Lab_HCIP\targets\STM32L431_BearPi\Src\main.c

int main(void)
{
    UINT32 uwRet = LOS_OK;
    HardWare_Init();
    uwRet = LOS_KernelInit();
    if (uwRet != LOS_OK)  {
        return LOS_NOK;
    }

    extern void shell_uart_init(int baud);
    shell_uart_init(115200);

    link_test();  //第一步

    (void)LOS_Start();
    return 0;
}

static int link_test() {
    int ret = -1;
    UINT32 uwRet = LOS_OK;
    UINT32  handle;
    TSK_INIT_PARAM_S task_init_param;

    memset (&task_init_param, 0, sizeof (TSK_INIT_PARAM_S));
    task_init_param.uwArg = (unsigned int)NULL;
    task_init_param.usTaskPrio = 2;
    task_init_param.pcName =(char *) "link_main";
    task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)link_main; //第二步
    task_init_param.uwStackSize = 0x1000;
    uwRet = LOS_TaskCreate(&handle, &task_init_param);
    if(LOS_OK == uwRet){
        ret = 0;
    }
    return ret;
}

LiteOS_Lab_HCIP\iot_link\link_main.c

int link_main(void *args) {
	...
#ifdef CONFIG_LINKDEMO_ENABLE
    extern int standard_app_demo_main(void);
    (void) standard_app_demo_main();   //第三步
#endif
    ...
}

LiteOS_Lab_HCIP\targets\STM32L431_BearPi\Demos\hello_world_demo\hello_world_demo.c

//任务入口函数
static int app_hello_world_entry()   {
    while (1)    {
        printf("Hello World! This is BearPi!\r\n");
        osal_task_sleep(4*1000);
    }
}

//第四步
int standard_app_demo_main()  {
    osal_task_create("helloworld",app_hello_world_entry,NULL,0x400,NULL,2);
    return 0;
}

任务管理实验

新添加任务入口函数并创建任务

void *task1 = NULL, *task2 = NULL; int num = 0;
//添加任务2
static int hcip_iot_task(void) {
    while (1)    {
        printf("This is task2!\r\n");
        #if 1
        if (num == 3) {
            osal_task_kill(task1);
        }
        #endif
        osal_task_sleep(4*1000);
    }
}

//宏控制的本项目下的任务创建接口/每个示例下该函数声明相同
int standard_app_demo_main() {
    task1 = osal_task_create("helloworld",app_hello_world_entry,NULL,0x400,NULL,2);
    //新创建任务
    task2 = osal_task_create("task2",hcip_iot_task,NULL,0x400,NULL,2);
    return 0;
}

实验结果

任务添加测试, 两个任务同时运行,
在这里插入图片描述
如上,任务1和任务2同时执行,同时打印输出。

任务删除测试,
在这里插入图片描述
如上,任务1被关闭后,周任务2在执行。

几个注意事项:
1、在编译或重新编译前前,要先关闭串口,否则可能使得IDE卡主,不能执行编译过程,必须得重启软件。
2、在烧录器配置烧录.hex文件时,遇到过显示烧录成功(且复位过)但是不生效的情况,可以多尝试几次或配置烧录.bin文件。

osal 系统适配层

在这里我们简单看看osal适配层的实现方法,后期我们会单独讲解。
在这里插入图片描述
个人觉得osal与cmsis有些类似,在上述os目录下,osal和linux/ucos_ii等系统目录平级,cmsis目录在liteos之下,如下图,
在这里插入图片描述
OSAL定义统一的系统调用接口(如线程管理、通信),使应用无需关注底层内核(如LiteOS与Linux的差异)。例如,鸿蒙通过OSAL层兼容Linux与LiteOS内核。 CMSIS 为Cortex处理器提供标准外设访问API(如寄存器映射)及RTOS接口(CMSIS-RTOS2),确保代码可跨RTOS(如FreeRTOS、RTX5)复用。

osal_task_create

//osal 操作系统适配层函数
void* osal_task_create(const char *name,int (*task_entry)(void *args), void *args,int stack_size,void *stack,int prior) {
    void *ret = NULL;
    if((NULL != s_os_cb) &&(NULL != s_os_cb->ops) &&(NULL != s_os_cb->ops->task_create)) {
        ret = s_os_cb->ops->task_create(name, task_entry,args,stack_size,stack,prior);
    }
    return ret;
}

osal_task_create 函数调用堆栈分析,
在这里插入图片描述

其他实验

互斥锁、内存管理、信号量实验,统一参见如下代码。

实验源码

#define SWITCH_TEST_HELLO  0
#define SWITCH_TEST_TASK   0
#define SWITCH_TEST_MUTEX  1
#define SWITCH_TEST_MEM    0
#define SWITCH_TEST_SEMP   0

//任务句柄
void *task1 = NULL, *task2 = NULL; int num = 0;

#if SWITCH_TEST_MUTEX
//需要保护的公共资源
uint32_t public_value = 0;
//互斥锁
osal_mutex_t public_value_mutex;
#endif
//信号量实验
osal_semp_t sync_semp = NULL;

#if SWITCH_TEST_HELLO
static int app_hello_world_entry()
{
    while (1)
    {
        printf("Hello World! This is BearPi!\r\n");
        osal_task_sleep(4*1000);
    }
}
#endif

#if SWITCH_TEST_TASK
static int hcip_iot_task(void) {
    while (1)    {
        printf("This is task2, num:%d \r\n", ++num);
        #if 1
        if (num == 3) {
            osal_task_kill(task1);
        }
        #endif
        osal_task_sleep(4*1000);
    }
}
#endif

#if SWITCH_TEST_MUTEX
//互斥锁/任务1入口
static int mutex_task1_entry() {
    while (1) {
        if (true == osal_mutex_lock(public_value_mutex)) {
            printf("task1: lock a mutex.\r\n");
            public_value += 10;
            printf("task1: public_value = %ld.\r\n", public_value);
            printf("task1: sleep...\r\n");
            osal_task_sleep(10); //ms
            printf("task1: continue...\r\n");
            printf("task1: unlock a mutex.\r\n");
            osal_mutex_unlock(public_value_mutex);
            if (public_value > 60) break; 
        } //if
    } //while
    return 0;
}

//互斥锁/任务2入口
static int mutex_task2_entry() {
    while (1) {
        if (true == osal_mutex_lock(public_value_mutex)) {
            printf("task2: lock a mutex.\r\n");
            public_value += 5;
            printf("task2: public_value = %ld.\r\n", public_value);
            printf("task2: unlock a mutex.\r\n");
            osal_mutex_unlock(public_value_mutex);
            if (public_value > 50) break; 
            #if 0  //task2 not sleep
            osal_task_sleep(10); //ms
            #endif
        } //if
    } //while
    return 0;
}
#endif

#if SWITCH_TEST_MEM
//内存管理实验/任务1
static int mem_access_task_entry() {
    uint32_t i = 0;          //for look
    size_t mem_size = 0;     //
    uint8_t *mem_ptr = NULL; //内存块指针
    //loop
    while (1) {
        //每次循环申请的块大小扩一倍
        mem_size = 1 << i++;
        //执行申请操作
        mem_ptr = osal_malloc(mem_size);
        //success
        if (NULL != mem_ptr) {
            printf("access %d bytes memory success!\r\n", mem_size);
            osal_free(mem_ptr);
            mem_ptr = NULL;
            printf("free memory success!\r\n");
        }
        else {
            printf("access %d bytes memory failed!\r\n", mem_size);
            return 0;
        }
    }
    
    return 0;
}
#endif

//信号量实验
#if SWITCH_TEST_SEMP
//信号量实验/任务1
static int semp_task1_entry() {
    printf("task1: post a semp.\r\n");
    osal_semp_post(sync_semp);
    printf("task1: end.\r\n");
}

//信号量实验/任务1
static int semp_task2_entry() {
    printf("task2: watting for a semp...\r\n");
    osal_semp_pend(sync_semp);
    printf("task2: access a semp.\r\n");
}
#endif

//示例初始化函数
int standard_app_demo_main() {
//原HelloWorld
#if SWITCH_TEST_HELLO
    osal_task_create("helloworld",app_hello_world_entry,NULL,0x400,NULL,2);
#endif

//互斥锁实验
#if SWITCH_TEST_MUTEX
    //创建互斥锁
    osal_mutex_create(&public_value_mutex);
    //创建任务 //const char *name,int (*task_entry)(void *args), void *args,int stack_size,void *stack,int prior
    task1 = osal_task_create("mutex_task1", mutex_task1_entry, NULL, 0x400, NULL, 12);
    //创建任务 //const char *name,int (*task_entry)(void *args), void *args,int stack_size,void *stack,int prior
    task2 = osal_task_create("mutex_task2", mutex_task2_entry, NULL, 0x400, NULL, 11);
#endif

//内存实验
#if SWITCH_TEST_MEM
    //创建任务
    task2 = osal_task_create("mem_task", mem_access_task_entry, NULL, 0x400, NULL, 11);
#endif

//信号量实验
#if SWITCH_TEST_SEMP
    //创建信号量 /数量1初始值0
    osal_semp_create(&sync_semp, 1, 0);
    //任务1优先级低,负责释放信号量
    task1 = osal_task_create("semp_task1", semp_task1_entry, NULL, 0x400, NULL, 12);
    //任务2优先级高,先进入等待/申请信号量的状态
    task2 = osal_task_create("semp_task2", semp_task2_entry, NULL, 0x400, NULL, 11);
#endif

    return 0;
}

内存管理实验

在这里插入图片描述

互斥锁实验

在这里插入图片描述
如上实验结果,如果高优先级的任务不睡眠,则低优先级任务必要要等到高优先级任务退出后才有机会执行。

信号量实验

在这里插入图片描述
这里,semp_task2_entry优先级高,会先执行,进入pend等待信号量状态,函数会使得当前任务进入阻塞状态,从而让出 CPU 资源给其他任务使用,如这里优先级稍低的semp_task1_entry任务。待task1释放信号量后,task2被唤醒继续执行。

CMISIS接口实验

与FreeRTOS一样,LiteOS也支持CMSIS,这简直是福利。在以下目录 LiteOS_Lab_HCIP\iot_link\os\liteos\cmsis
在这里插入图片描述

还是得JlINK

前面几个小实验,纯粹是为了体验OpenOCD模式,但真的很难用啊。在进行CMSIS实验时,哈哈也不知道是咋鼓捣的,基于STLink+OpenOCD的调试环境,它之间罢工了。在配置文件、OpenOCD版本等方向尝试修复无果。
在这里插入图片描述
于是乎,我又乖乖的将板载的STLink刷成了JLink,该过程参考 #<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 LiteOS Studio + GCC + JLink># 文章。这个二次烧录Jlink固件的过程,也很崎岖…
1、在 #<IDE/IoT/搭建物联网(LiteOS)集成开发环境,基于 VSCode + IoT Link 插件>#文中,提到了使用 STLinkReflash 将JLink 刷回 STLink 也不顺利。
2、在第一步中,通过STM32 ST-LINK Utility升级了调试器固件。
3、在上述两步基础上,再尝试使用 STLinkReflash 将STLink刷成JLink是失败的。后来,我重新安装了 STlink驱动、更换了usb接口、重启过电脑,等一系列组合拳下来,竟然成功啦。哈哈,不想再尝试了。就按照Studio官方建议,这么用吧。
4、J-Link 固件内置 J-Link GDB Server,可直接与调试工具(如 LiteOS Studio 的 GDB 客户端)通信,无需中间层协议转换。这种直接集成减少了调试链路的复杂性,提高运行效率。刷写后的 ST-Link(J-Link OB)可实现高达 1.8MHz 的下载速率,显著快于 OpenOCD + ST-Link 的组合。ST-Link没有内置GDB服务,因此要借助外部的openocd.exe做GDB服务器。

CMSIS 简介

随着 32 位处理器在嵌入式市场需求量逐渐增多,各家芯片公司推出新型芯片,伴随而
来的是开发工具、软件兼容以及代码移植等问题。在这种情况下,各个硬件平台的供应商都
寻求易于使用且高效的解决方案,其中,ARM 与 Atmel、IAR、KEIL、SEGGER 和 ST 等诸
多芯片和软件工具厂商合作,发布了一套 CMSIS 标准。
CMSIS(Cortex Microcontroller Software Interface Standard),即 ARM Cortex 微控制器软
件接口标准。CMSIS 标准提供了内核和外围设备、实时操作系统和中间组件之间的通用 API
接口,从而简化了软件的重复使用,缩短了微控制器开发人员的学习时间,并缩短了新设备
的上市时间。下图是 ARM 公司的 CMSIS 标准结构框图:
在这里插入图片描述
其中,CMSIS-CORE 层定义了 Cortex-M 以及 Cortex-A 处理器(Cortex-A5/A7/A9)内核
和外围设备的标准化 API。CMSIS-Pack 层包含了 CMSIS-Driver 驱动框架、CMSIS-DSP 相关
库、CMSIS-RTOS 操作系统 API、中间件 API 和 Peripheral HAL 层 API 等。根据 CMSIS 的标准,ARM 公司整合并提供了 CMSIS 软件包模板。基于 ARM 提供的 CMSIS 软件包模板,ST 官方结合自己芯片的差异进行了修改,并将其整合到了 STM32Cube 固件包中的 CMSIS 文件夹里。

LiteOS->CMSIS

除了ST的HAL支持外,LiteOS也要提供支持,以osThreadNew为例,

//cmsis_liteos2.c /定义在cmsis_os2.h中
osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr) {
	...
	uwRet = LOS_TaskCreate(&uwTid, &stTskInitParam);
	...
}

上述 LOS_TaskCreate 实现在 LiteOS_Lab_HCIP\iot_link\os\liteos\base\core 内核中。在osal层的任务创建函数 osal_task_create,其最后也要调用上述 LOS_TaskCreate 内核实现。 该函数的实现,我们不再深入。

任务间消息交互

#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <osal.h>
#include <cmsis_os.h> //CMSIS_OS_VER==2

#define SWITCH_TEST_HELLO  0
#define SWITCH_TEST_TASK   0
#define SWITCH_TEST_MUTEX  0
#define SWITCH_TEST_MEM    0
#define SWITCH_TEST_SEMP   0
#define SWITCH_TEST_CMSIS  1

//cmsis接口
#if SWITCH_TEST_CMSIS
//消息队列句柄 /void*
osMessageQueueId_t cmsis_queue;
//消息队列消息
typedef struct cmsis_msg {
    int a;
    int b;
} TMsg;

///typedef void (*osThreadFunc_t) (void *argument);
//任务1 /发送消息 
static void cmsis_task1_entry(void *argument) {
    TMsg tMsg = {0, 0};
    while (1) {
        //
        tMsg.a += 1;
        tMsg.b += 2;
        //(osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout)
        osMessageQueuePut(cmsis_queue, &tMsg, 0, 10);
        //打印发送的消息
        printf("Send Msg a:%d b:%d\r\n", tMsg.a, tMsg.b);
        //睡眠
        osal_task_sleep(1*1000);
        //任务退出
        if (tMsg.a > 100) break;
    }
}

///typedef void (*osThreadFunc_t) (void *argument);
//任务2 /接收消息
static void cmsis_task2_entry(void *argument) {
    TMsg tMsg; uint8_t msg_prio = 0;
    while (1) {
        //osStatus_t osMessageQueueGet (osMessageQueueId_t mq_id, void *msg_ptr, uint8_t *msg_prio, uint32_t timeout)
        osMessageQueueGet(cmsis_queue, (void*)&tMsg, &msg_prio, osWaitForever);
        //打印收到的消息
        printf("Recv Msg a:%d b:%d\r\n", tMsg.a, tMsg.b);
    }
}
#endif

int standard_app_demo_main() {
//cmsis接口
#if SWITCH_TEST_CMSIS
    //创建消息队列/osMessageQueueId_t osMessageQueueNew (uint32_t msg_count, uint32_t msg_size, const osMessageQueueAttr_t *attr);
    cmsis_queue = osMessageQueueNew (5, sizeof(TMsg), NULL);
    const osThreadAttr_t thread_attr1 = {
    .name = "MyThread1",         // 线程名称(调试用)
    .stack_size = 1024,         // 栈大小(字节)
    .priority = osPriorityAboveNormal5  
    };
    const osThreadAttr_t thread_attr2 = {
        .name = "MyThread2",         // 线程名称(调试用)
        .stack_size = 1024,         // 栈大小(字节)
        .priority = osPriorityAboveNormal5
    };
    //任务1/发送消息 /osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr)
    osThreadId_t thread1 = osThreadNew (cmsis_task1_entry, NULL, &thread_attr1); 
    //任务2接收消息 /osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr)
    osThreadId_t thread2 = osThreadNew (cmsis_task2_entry, NULL, &thread_attr2);
    //
    printf("Hello cmsis!\r\n");
#endif

    return 0;
}

执行结果

在这里插入图片描述

其他

怎么说呢?挺不顺的,一定要有耐心。

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

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

相关文章

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…