FreeRTOS基础(四):静态创建任务

      上一篇博客,我们讲解了FreeRTOS中如何动态创建任务,那么这一讲,我们从实战出发,规范我们在FreeRTOS下的编码风格,掌握静态创建任务的编码风格,达到实战应用!

目录

一、空闲任务和空闲任务钩子函数

1.0 为什么会有空闲任务?

1.1 空闲任务(Idle Task)

1.2 空闲任务钩子函数(Idle Task Hook)

二、静态创建任务的基本步骤

2.1 使能FreeRTOS的API函数

2.2 定义静态创建任务函数的入口参数

2.3 编写任务函数

2.4主函数进行调用

2.5补充

2.6任务执行顺序

四、静态创建任务和删除任务的API函数解析(选学)

五、静态创建任务和动态创建任务的区别

5.1 创建过程上比较

5.2 整体理解

 5.3 应用场合    

六、临界区保护

6.1 概念

6.2 临界区保护实现方法

6.3 使用示例

6.4 注意事项


一、空闲任务和空闲任务钩子函数

1.0 为什么会有空闲任务?

       思考⼀个问题:在我们的Free RTOS中可以将所有任务阻塞吗?

     不能。

①所有任务都阻塞而没有可运行的任务,会导致系统⽆法处理外部响应,这与实时操作系统的设计 理念相悖。

②可能会出现死锁或系统崩溃。 

        因此,必须始终至少有⼀个任务可以进⼊运行状态,这就是空闲任务。

1.1 空闲任务(Idle Task)

     在FreeRTOS中,空闲任务是在我们调用任务调度器函数的内部自动创建的,它是优先级最低的任务。它在系统中没有其他任务可以运行时执行。其主要作用包括:

  1. CPU利用率:空闲任务的运行表明系统没有其他更高优先级的任务需要运行,因此空闲任务的执行时间可以被用来计算CPU的空闲时间,从而得出CPU利用率。
  2. 内存管理:空闲任务负责清理被删除的任务堆栈内存。

注意:空闲任务不需要我们进行创建!!!

      注意:空闲任务它也是一个任务,只不过比较特殊而已,它是任务调度器自动帮我们进行创建的,在静态创建任务时,我们首先使能空闲任务的API函数,

// FreeRTOSConfig.h文件中
#define configUSE_IDLE_HOOK 1

然后自己实现空闲任务的内存分配函数(因为静态创建的时候,是由我们自己进行分配的,分配在bass段,通常为全局数组),


/************************空闲任务配置**************/
StaticTask_t idle_task_tcb;                              //空闲任务控制块
StackType_t  idle_task_stack[configMINIMAL_STACK_SIZE];  //空闲任务栈的大小/*空闲任务内存分配函数实现*/
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,StackType_t ** ppxIdleTaskStackBuffer,uint32_t * pulIdleTaskStackSize )
{*ppxIdleTaskTCBBuffer=&idle_task_tcb;*ppxIdleTaskStackBuffer=idle_task_stack;* pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;}

如果需要在空闲任务中执行特定操作,可以实现空闲任务钩子函数。

// 空闲任务钩子函数
void vApplicationIdleHook(void)
{// 在此处添加空闲时要执行的代码
}

      在动态创建任务时,我们不需要实现空闲任务的内存分配函数,因为这是由 FreeRTOS 自动的从 FreeRTOS 管理的堆中分配。即动态任务的分配不需要管空闲任务。

1.2 空闲任务钩子函数(Idle Task Hook)

      空闲任务钩子函数是一个用户定义的函数,它在每次空闲任务运行时被调用。它允许用户在系统空闲时执行一些特定的任务,比如低功耗模式的处理、监控等。

如何使用空闲任务钩子函数

step1、配置宏(使能空闲任务API函数)

step2、实现空闲任务的内存分配函数(注意:只有静态创建任务才需要!)

step3、自定义实现空闲任务钩子函数(根据自己的需求)

//1、FreeRTOSConfig.h文件中
#define configUSE_IDLE_HOOK 1/************************空闲任务配置**************/
StaticTask_t idle_task_tcb;                              //空闲任务控制块
StackType_t  idle_task_stack[configMINIMAL_STACK_SIZE];  //空闲任务栈的大小/*2、空闲任务内存分配函数实现*/       只有静态创建任务才需要
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,StackType_t ** ppxIdleTaskStackBuffer,uint32_t * pulIdleTaskStackSize )
{*ppxIdleTaskTCBBuffer=&idle_task_tcb;*ppxIdleTaskStackBuffer=idle_task_stack;* pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;}//3、空闲任务钩子函数具体实现(根据需求)
void vApplicationIdleHook(void)
{// 在此处添加空闲时要执行的代码
}

通过上述配置,空闲任务钩子函数将在系统空闲时被调用,执行用户定义的代码。

二、静态创建任务的基本步骤

2.1 使能FreeRTOS的API函数

        同样在使用FreeRTOS任务创建函数之前,我们需要在配置文件里(FreertosConfig.h)使能API函数

  1.  需将宏configSUPPORT_STATIC_ALLOCATION 配置为 1 ,此时便支持静态创建。利用Ctrl+F搜索即可。
  2. 使能空闲任务的API函数,在FreeRTOSConfig.h文件中#define configUSE_IDLE_HOOK 1

 

2.2 定义静态创建任务函数的入口参数

     通过前面的讲解,我们知道动态创建任务的API函数如下:

      与动态创建的任务相比,只是后面两个参数发生了变化,其实这在前面讲过了,这是因为:静态创建任务时,任务的任务控制块以及任务的栈空间所需的内存,需用户分配提供,通常在bass段申请一个足够大的全局数组即可!该函数返回该任务的任务句柄!

其实,我们需要定义的入口参数就是这个API函数的参数,提前定义好,然后传入参数,他就会自动的为我们创建好对应的任务,并且处于一种就绪态。   从上面我们可以看到:

1、任务函数指针:

       其实就是函数名,我们知道函数名就是函数的入口地址,就是一个函数指针

2、任务名字:

        其实也就是函数名对应的字符串,要用双引号括起来

3、任务堆栈大小:

        静态创建任务,任务的任务控制块以及任务的栈空间所需的内存,均需要我们自己进行内存分配,通常在bass段申请一个足够大的全局数组即可!我们需要定义好任务栈的大小,也就是数组的大小!使用宏:

#define     START_TASK_STACK_SIZE  128   //定义堆栈大小为128字(1字等于4字节)

4、传递给任务的参数:

       不需要传参,我们直接给NULL即可;

5、任务优先级:

        我们使用的是硬件的方式,因此,它要在0-31之间,使用宏定义即可:

#define        START_TASK_PRIO         1    //定义任务优先级,0-31根据任务需求

6、任务堆栈地址:

      这个参数就是我们自己申请的任务堆栈的地址,我们使用的是全局数组,使用宏定义即可,需要我们提前定义好,然后传入数组名即可,因为数组名就是地址。

StackType_t    start_task_stack[START_TASK_STACK_SIZE];  //申请的任务堆栈(全局数组)

7、任务控制块地址:

     动态创建任务,任务控制块也是由我们自己进行内存分配的,任务控制块就是一个结构体,因此需要我们提前定义好任务控制块,然后传入任务控制块的地址。使用宏即可:

StaticTask_t   start_task_tcb;   //创建任务控制块结构体

8、任务句柄:

     这个是因为:这个函数的返回值为任务句柄,我们后续对任务的删除等操作,都是通过该任务句柄进行操作,因此,我们需要提前定义好任务句柄,然后接收创建任务的返回值即可对该任务进行操作!使用宏即可:

TaskHandle_t   start_task_handler;    //定义任务句柄(结构体指针)

从上面我们可以知道:其实我们只需要提前利用宏定义好五个参数即可,其他的参数只要任务函数编写好,便可以确定。示例如下:

/**********************START_TASK任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/#define        START_TASK_STACK_SIZE  128   //定义堆栈大小为128字(1字等于4字节)
#define        START_TASK_PRIO         1    //定义任务优先级,0-31根据任务需求
StackType_t    start_task_stack[START_TASK_STACK_SIZE];  //申请的任务堆栈(全局数组)
StaticTask_t   start_task_tcb;           //申请的任务控制块(创建结构体)
TaskHandle_t   start_task_handler;      //定义任务句柄(结构体指针)
void start_task(void* args);

注意:

  1. 为了编码规范,我们使用的宏都是大写,虽然较长,但是通俗易懂;
  2. 使用API函数进行任务创建,里面的参数需要进行强制转换,以免报错。
  3. 为了任务执行的顺序是按照我们设定好的优先级执行的,我们可以在创建任务的任务中,使用临界段保护,那么在这个任务体中,可以屏蔽中断(中断优先级在5-15之内)比如切换任务的PendSV,此时,我们创建任务的过程中,不会进行任务的调度,然后我们创建任务结束后,在打开临界段保护,此时不会对所有中断进行屏蔽,也就是任务切换PendSV(中断)才会进行任务调度。如下代码所示,在创建任务开始之前和创建任务之后加入,后面详细讲解。
  4. 静态创建任务函数,有返回值,返回任务句柄,用提前定义好的任务句柄接收,后面便可以使用任务句柄操作任务。
#include "stm32f4xx.h"                  // Device header
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"/**********************START_TASK任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/#define        START_TASK_STACK_SIZE  128   //定义堆栈大小为128字(1字等于4字节)
#define        START_TASK_PRIO         1    //定义任务优先级,0-31根据任务需求
TaskHandle_t   start_task_handler;    //定义任务句柄(结构体指针)
StackType_t    start_task_stack[START_TASK_STACK_SIZE];
StaticTask_t   start_task_tcb;
void start_task(void* args);/**********************TASK1任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define  TASK1_STACK_SIZE  128            //定义堆栈大小为128字(1字等于4字节)
#define  TASK1_PRIO         2             //定义任务优先级,0-31根据任务需求
TaskHandle_t   task1_handler;           //定义任务句柄(结构体指针)
StackType_t    task1_stack[TASK1_STACK_SIZE];
StaticTask_t   task1_tcb;
void task1(void* args);/**********************TASK2任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define  TASK2_STACK_SIZE  128            //定义堆栈大小为128字(1字等于4字节)
#define  TASK2_PRIO         3             //定义任务优先级,0-31根据任务需求
TaskHandle_t   task2_handler;           //定义任务句柄(结构体指针)
StackType_t    task2_stack[TASK2_STACK_SIZE];
StaticTask_t   task2_tcb;
void task2(void* args);/**********************TASK3任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define  TASK3_STACK_SIZE  128            //定义堆栈大小为128字(1字等于4字节)
#define  TASK3_PRIO         4            //定义任务优先级,0-31根据任务需求
TaskHandle_t   task3_handler;           //定义任务句柄(结构体指针)
StackType_t    task3_stack[TASK3_STACK_SIZE];
StaticTask_t   task3_tcb;
void task3(void* args);
/*********开始任务用来创建其他三个任务,只创建一次,不能是死循环,同时创建完3个任务后删除任务1本身***********/
void start_task(void* args)
{taskENTER_CRITICAL();        /*进入临界区*/task1_handler = xTaskCreateStatic( (TaskFunction_t)        task1,( char * )    "task1", (uint32_t)    TASK1_STACK_SIZE,(void * )    NULL,(UBaseType_t)    TASK1_PRIO,(StackType_t * )    task1_stack,(StaticTask_t * )    &task1_tcb );task2_handler= xTaskCreateStatic( (TaskFunction_t)       task2,( char * )    "task2", (uint32_t)    TASK2_STACK_SIZE,(void * )    NULL,(UBaseType_t)    TASK2_PRIO,(StackType_t * )    task2_stack,(StaticTask_t * )    &task2_tcb );task3_handler= xTaskCreateStatic( (TaskFunction_t)       task3,( char * )    "task3", (uint32_t)    TASK3_STACK_SIZE,(void * )    NULL,(UBaseType_t)    TASK3_PRIO,(StackType_t * )    task3_stack,(StaticTask_t * )    &task3_tcb );							 vTaskDelete(start_task_handler);    //删除开始任务自身,传参NULL或者开始任务句柄taskEXIT_CRITICAL();   /*退出临界区*///临界区内不会进行任务的调度切换,出了临界区才会进行任务调度,抢占式						
}

此外,还要实现空闲任务的内存分配函数,至于空闲任务的钩子函数可根据自己需求是否实现,这里就不实现了。 

/************************空闲任务配置**************/
StaticTask_t idle_task_tcb;                              //空闲任务控制块
StackType_t  idle_task_stack[configMINIMAL_STACK_SIZE];  //空闲任务栈的大小/*空闲任务内存分配*/
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,StackType_t ** ppxIdleTaskStackBuffer,uint32_t * pulIdleTaskStackSize )
{*ppxIdleTaskTCBBuffer=&idle_task_tcb;*ppxIdleTaskStackBuffer=idle_task_stack;* pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;}

2.3 编写任务函数

对每个任务具体实现的功能进行函数的实现:需要注意,任务函数没有返回值并且是死循环的!

/********其余三个任务的任务函数,无返回值且是死循环***********//***任务1:实现LED0每500ms翻转一次*******/
void task1(void* args)
{while(1){printf("任务1正在运行!\n");GPIO_ToggleBits(GPIOF,GPIO_Pin_9 );vTaskDelay(500);       //FreeRTOS自带的延时函数}}/***任务2:实现LED1每500ms翻转一次*******/
void task2(void* args)
{while(1){printf("任务2正在运行!\n");GPIO_ToggleBits(GPIOF,GPIO_Pin_10 );vTaskDelay(500);       //FreeRTOS自带的延时函数}}/***任务3:判断按键KEY0,按下KEY0,任务1删除*******/
void task3(void* args)
{while(1){printf("任务3正在运行!\n");if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0)  //表示按键按下{if(task1_handler!=NULL)  //防止重复删除{printf("删除任务1!\n");vTaskDelete(task1_handler);    //删除任务1,传任务1的句柄task1_handler=NULL;}}	  vTaskDelay(10); }}

此外,我们再自定义一个入口函数,用来创建开始任务,然后将要创建的任务全部放在这个开始任务中,主函数只需调用这个入口函数,即可在这个开始任务中 , 创建其他的任务,这样做,规范代码,梳理代码逻辑,清晰易懂任务的运行顺序!如下所示:

//FreeRTO入口例程函数,无参数,无返回值,用来创建开始任务
void freertos_demo(void)
{//静态创建任务会返回任务句柄start_task_handler  =  xTaskCreateStatic( (TaskFunction_t)    start_task,( char * )    "start_task", (uint32_t)    START_TASK_STACK_SIZE,(void * )    NULL,(UBaseType_t)    START_TASK_PRIO,(StackType_t * )    start_task_stack,(StaticTask_t * )    &start_task_tcb );vTaskStartScheduler();  //开启任务调度器}

2.4主函数进行调用

      在完成上述的编写后,主函数内部只需要引入对应的头文件,然后在函数内部调用相应的函数对使用到的外设进行初始化,然后调用入口函数即可进行按照我们设定的优先级进行任务的调度,如下所示:

#include "stm32f4xx.h"                  // Device header
#include "stdio.h"
#include "myled.h"
#include "mykey.h"
#include "myusart.h"#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"    //可以用来单独存放任务函数的声明以及配置相关的宏定义,然后直接引入头文件使用extern TaskHandle_t Start_Handle;  
/*使用任务句柄可以对任务操作,如果没有添加上面的单独头文件存放,
那么使用其他文件的全局变量利用extern关键字引入即可。*/int main(void)
{//1、外设初始化My_UsartInit();LED_Init();KEY_Init();//2、调用入口函数freertos_demo();}

2.5补充

    为进行模块化的编程,我们可以将创建相应的头文件可以用来单独存放任务函数的声明以及任务配置相关的宏定义,然后在主函数直接引入头文件使用即可,这样工程结构清晰易懂!

2.6任务执行顺序

编写完程序后,一定要进行验证,验证程序是否按照我们设定的顺序及进行执行,类似于操作系统的线程同步问题!

     首先主函数调用入口函数,在入口函数内部创建开始任务函数,该开始任务进入就绪状态,启用任务调度器,调度器启动后,FreeRTOS 将接管系统控制,开始调度任务。此时CPU就会去执行开始任务,然后,在开始任务中创建三个任务,注意:由于使用了临界保护:taskENTER_CRITICAL();        /*进入临界区*/  它会对5-15优先级的中断进行屏蔽,即不会发生作用,其中PendSV是用来任务切换的内核中断,它的优先级是13,因此,会被屏蔽,也就是说,我在创建三个任务的过程中,不会进行其他任务的切换,保证我的开始任务创建其他的三个任务不会被打断!!!创建完三个任务后,它们都进入了就绪态,然后,再删除这个开始任务(因为每个任务只需要创建一次,多次创建占用堆栈内存,造成栈溢出!)此时,我在关闭临界区保护,taskEXIT_CRITICAL();   /*退出临界区*/,也就是打开所有中断,此时PendSV中断就会被打开,按照任务的优先级进行抢占式调度,分别执行任务3、任务2、任务1,在三个任务执行的过程中,加入适当的延时,他就会进行任务的切换,去就绪列表寻找优先级最高的任务去运行!

四、静态创建任务和删除任务的API函数解析(选学)

五、静态创建任务和动态创建任务的区别

5.1 创建过程上比较

       动态创建的任务,任务的任务控制块以及任务的栈空间所需的内存,均由 FreeRTOS 从 FreeRTOS 管理的堆中分配(真正需要我们定义的也就是任务句柄);

       静态创建的任务,任务的任务控制块以及任务的栈空间所需的内存,需用户分配提供。

需要我们额外定义这两个。

 此外,静态创建任务,需要我们实现空闲任务的内存分配函数。

5.2 整体理解

  1.  静态创建任务是在编译时为任务分配内存(全局数组),任务在运行之前已分配内存。这种方法不需要在运行时使用动态内存分配函数,因此更加可靠和节省内存。
  2.  动态创建任务是在运行时通过动态内存分配函数分配任务内存。这种分配方式可以更灵活地适应不同大小和数量的任务。然而,在动态方式下,程序需要在运行时使用动态内存分配函数,这可能会导致内存泄漏和堆碎片等问题。

 

 5.3 应用场合    

       1、在实际的应用中,动态方式创建任务是比较常用的,除非有特殊的需求,一般都会使用动态方式创建任务,动态创建相对简单,更为常用,静态创建:可将任务堆栈放置在特定的内存位置,并且无需关心对内存分配失败的处理。
      2、如果应用程序中的任务数量和大小已知,则可以使用静态方式分配内存,并且无需动态内存分配。 如果应用程序需要更多的灵活性,并且需要在运行时根据需要创建或删除任务,则应使用动态方式分配内存。

六、临界区保护

6.1 概念

      在FreeRTOS(实时操作系统)中,临界区保护是一个关键概念,用于确保共享资源在多任务环境中被安全地访问和修改。临界区保护防止多个任务在同一时间访问共享资源,从而避免数据竞争和不一致的情况。  它可以保护那些不想被打断的程序段,关闭freertos所管理的中断,中断无法打断,滴答中断和PendSV中断无法进行不能实现任务调度 。

6.2 临界区保护实现方法

在FreeRTOS中,临界区保护主要通过以下两种方法实现:

  1. 任务级临界区保护

    • vTaskSuspendAll() 和 xTaskResumeAll():这对函数用于挂起和恢复任务调度。当调用vTaskSuspendAll()时,FreeRTOS内核暂停任务切换,直到调用xTaskResumeAll()。在此期间,任务调度被禁止,确保当前任务独占地执行临界区内的代码。
    • 适用于临界区执行时间较长的场景,因为任务调度被完全禁止。
  2. 中断级临界区保护(常用方法)

    • taskENTER_CRITICAL() 和 taskEXIT_CRITICAL():这对宏用于进入和退出临界区。当调用taskENTER_CRITICAL()时,内核禁用所有中断,确保在临界区内的代码不会被任何中断打断。当调用taskEXIT_CRITICAL()时,恢复中断的状态。
    • 适用于临界区执行时间较短的场景,因为禁用中断的时间较短,能减少系统的中断响应延迟。

6.3 使用示例

任务级临界区保护示例:

void vTaskFunction(void *pvParameters)
{// 其他代码vTaskSuspendAll(); // 挂起任务调度// 临界区代码// 访问或修改共享资源// 例如:共享变量++sharedVariable++;xTaskResumeAll(); // 恢复任务调度// 其他代码
}

中断级临界区保护示例:

void vTaskFunction(void *pvParameters)
{// 其他代码taskENTER_CRITICAL(); // 进入临界区,禁用中断// 临界区代码// 访问或修改共享资源// 例如:共享变量++sharedVariable++;taskEXIT_CRITICAL(); // 退出临界区,恢复中断// 其他代码
}

6.4 注意事项

临界区保护的注意事项

  1. 使用范围临界区保护应该仅用于保护需要保护的代码,避免长时间禁用任务调度或中断,影响系统的实时性能。
  2. 开销:禁用任务调度和中断都有一定的开销,特别是禁用中断会影响系统的实时响应,需要慎重使用。
  3. 嵌套:在FreeRTOS中,taskENTER_CRITICAL()taskEXIT_CRITICAL()可以嵌套使用,但要确保成对出现,防止中断长时间被禁用。

       通过以上的介绍,相信你对静态创建任务的过程会有清晰的认识,其实步骤也是非常简单的,接下来去实践吧!熟练后就不难了,万事开头难! 至此,静态创建任务就已经讲解完毕!初次学习,循序渐进,一步步掌握即可!以上就是全部内容!请务必掌握,创作不易,欢迎大家点赞加关注评论,您的支持是我前进最大的动力!下期再见!

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

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

相关文章

MT8781安卓核心板_MTK联发科Helio G99核心板规格参数

MT8781安卓核心板采用先进的台积电6纳米级芯片生产工艺,配备高性能Arm Cortex-A76处理器和Arm Mali G57 GPU,加上LPDDR4X内存和UFS 2.2存储,在处理速度和数据访问速度上都有着出色的表现。 MT8781还支持120Hz显示器,无需额外的DSC…

TK防关联引流系统:全球TikTok多账号运营的神器

在TikTok的生态中,高效运营多个账号已成为品牌全球推广的必经之路。为此,TK防关联引流系统应运而生,它是一款专为TikTok设计的效率神器,助您迅速搭建并管理全球多账号矩阵。该系统由先进的“防关联智能终端”硬件和智能的“TK防关…

随身wifi网络卡顿怎么解决?随身WiFi哪个牌子的最好用?排名第一名的随身WiFi!

对于随身wifi靠不靠谱这个问题,网上一直存在争议。很多人的随身wifi网速不稳定,信号看着满格就是上不了网。关于随身wifi卡顿到底该怎么解决呢? 1.如果是设备网络在一个地方上网速度很快,换一个地方网络就不行了,很可能…

常用中间件各版本下载

常用中间件下载地址 前言分布式中间件负载均衡中间件缓存中间件数据库中间件其他中间件1、Maven下载地址2、Git下载地址2、JDK下载地址3、MySQL下载地址4、Redis下载地址5、Nacos下载地址6、Tomcat下载地址7、Nginx下载地址8、RocketMQ下载地址8、RabbitMQ下载地址8、Erlang下载…

新设立湖北投资管理公司流程和要求

在湖北投资管理企业进行注册时,需要准备一系列的材料并按照一定的流程进行办理。本文将从注册材料及注册流程两方面来介绍,帮助您了解注册投资管理企业的步骤和所需的具体材料。详情致电咨询我或者来公司面谈。 新注册材料要求: 企业名称申请书:要求提供…

Day 9:2829. k-avoiding 数组的最小总和

Leetcode 2829. k-avoiding 数组的最小总和 给你两个整数 n 和 k 。 对于一个由 不同 正整数组成的数组,如果其中不存在任何求和等于 k 的不同元素对,则称其为 k-avoiding 数组。 返回长度为 n 的 k-avoiding 数组的可能的最小总和。 n 个不同正整数的最…

树形结构-CRUD接口

先看一下效果:整体的效果 新增效果 --默认值是 default 修改效果 - 大致效果如上 --------------------------------------------------------------------------------------------------------------------------------- 下面讲解代码如何实现的 根据你使用…

Selenium+Java 环境搭建

selenium 介绍 Selenium 是 web 应用中基于 UI 的自动化测试框架,支持多平台、多浏览器、多语言。 早期的 selenium RC 已经被现在的 webDriver 所替代,可以简单的理解为selenium1.0webdriver 构成 现在的 Selenium2.0 。现在我们说起 selenium &#xf…

AI大模型探索之路-实战篇12: 构建互动式Agent智能数据分析平台:实现多轮对话控制

系列篇章💥 AI大模型探索之路-实战篇4:深入DB-GPT数据应用开发框架调研 AI大模型探索之路-实战篇5:探索Open Interpreter开放代码解释器调研 AI大模型探索之路-实战篇6:掌握Function Calling的详细流程 AI大模型探索之路-实战篇7…

【机器学习300问】106、Inception网络结构如何设计的?这么设计的目的是什么?

谷歌的Inception网络,也被称为GoogLeNet,是Google在2014年推出的一种深度卷积神经网络(CNN)模型,在这之前的AlexNet、VGG等结构都是通过增大网络的深度(层数)来获得更好的训练效果,但…

vue3简单快速实现主题切换功能

⛰️个人主页: 蒾酒 🔥系列专栏:《vue3实战》 目录 内容概要 实现步骤 1.定义不同主题的css样式变量 2.入口main.ts中引入这个样式文件 3.主题样式css变量引用 4.设置默认主题样式 5.实现点击按钮主题切换 总结 最近发现了一个巨牛的人工智…

JS-Lodash工具库

文档:Lodash Documentation orderBy函数:根据条件进行排序 注:第一个是要排序的数组,第二个是根据什么字段进行排序,第三个是排序的方式(desc倒序) 安装方式:Lodash npm i lodash…

python判断文件是否存在

import os test_path "/Users/yxk/Desktop/test/GrayScale.tif" if(os.path.exists(test_path)):print(文件存在!!!!) else:print("文件不存在!!!!")结果如下 …

ABAP 借助公司封装的钉钉URL,封装的RFC给钉钉发送消息

FUNCTION ZRFC_BC_SMSSEND_DINGTALK. *"---------------------------------------------------------------------- *"*"本地接口: *" IMPORTING *" VALUE(DESTUSRID) TYPE CHAR255 *" VALUE(CONTENT) TYPE CHAR255 *&quo…

Linux 的权限

目录 Linux 的用户 root 用户 和 普通用户 如何新建普通用户? 如何切换用户? 一开始是以 root 用户登录: 一开始以普通用户登录: 如何删除用户? Linux文件权限 什么是读权限( r )&#…

自然语言处理学习路线

学习目标 NLP 系统知识(从入门到入土) 学习内容 NLP的基本流程:(待更)文本预处理(标点符号处理、繁体转简体、分词Tokenizer):(待更)词袋模型(TF…

校园志愿者|基于SprinBoot+vue的校园志愿者管理系统(源码+数据库+文档)

校园志愿者管理系统 目录 基于SprinBootvue的校园志愿者管理系统 一、前言 二、系统设计 三、系统功能设计 1 系统功能模块 2管理员功能 3志愿者功能 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍&a…

关于FPGA 使用SPI FLASH固化时如何配置固化参数

关于FPGA 使用SPI FLASH固化时如何配置固化参数 EDA工具:Vivado 关于FPGA 使用SPI FLASH固化时如何配置固化参数一、引言二、如何设置固化参数:使用50M的速度 ,SPI为X4 ,以及bit压缩第一:点open implenment design第二…

万字长文,小白新手怎么开始做YOLO实验,从零开始教!整体思路在这里,科研指南针!

最近专栏来了很多的新手小白,对科研实验的过程感到困惑和无从下手,这篇文章就来讲解一下整体的科研流程,从选择数据集到发表论文的各个步骤,并针对大家在实验中常犯的错误进行解答。并且为大家提供通向我其他相关博客的指引&#…

14.微信小程序之地理定位功能

目录 1.地理定位介绍 1.1 申请开通 1.2 使用方法 2.拒绝授权后的解决方案 3.开通腾讯位置服务 4.LBS 逆地址解析 1.地理定位介绍 小程序地理定位是指通过小程序开发平台提供的 API,来获取用户的地理位置信息。用户在使用小程序时,可以授权小程序获…