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.mzph.cn/pingmian/20498.shtml

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

相关文章

【算法】位运算算法——消失的两个数字(困难)

题解:消失的两个数字(位运算算法) 目录 1.题目2.题解3.示例代码如下4.总结 1.题目 题目链接:LINK 2.题解 本题要求时间复杂度O(N),空间复杂度O(1),分别否了我们 排序遍历 和 哈希数组 的想法。想要在规定时间/空间复杂度内完成本题,需要借…

数据结构05:树与二叉树 习题02[C++]

考研笔记整理,本篇作为二叉树的入门习题,供小伙伴们参考~🥝🥝 之前的博文链接在此:数据结构05:树与二叉树[C]-CSDN博客~🥝🥝 第1版:王道书的课后习题~🧩&am…

基于多源土地覆盖产品整合的高精度中国森林覆盖数据集

本数据集是结合多源土地覆盖产品的一致性和互补性,基于无云合成影像并结合随机森林分类模型和多级投票策略生产的一套全国范围的高精度森林覆盖数据产品。该数据产品可以提升我国森林资源监测、林业经营管理、森林碳汇计量评价等方面工作的准确性,为森林…

什么洗地机口碑最好?目前口碑最好的十大洗地机品牌推荐

如今人们的生活节奏越来越快,休闲时间越来越少,下班之后还要腾出时间和精力打扫卫生,委实是耗神的事儿。这个时候,高科技的智能清洁家电产品便起到了重要作用了。洗地机集合扫地、吸尘、洗地为一体的清洁机器,能够快速…

【5】MySQL数据库备份-XtraBackup 安装报错 zstd

XtraBackup 安装报错 zstd 前言解决方案 前言 在 Linux 系统上安装 XtraBackup 过程中,遇到如下的报错(… Requires: zstd …): --> Processing Dependency: zstd for package: percona-xtrabackup-80-8.0.35-30.1.el7.x86_…

pytorch-Normalization

目录 1. 为什么Normalization2. Normalization2.1 image Normalization2.2 Batch Normalization 3. Normalization pytorch实现3.1 Normalization标准公式3.2 2d normalization3.3 normalize test 4. 使用normalization的好处 1. 为什么Normalization 下图使用sigmoid激活函数…

在线考试教学系统平台系统源码/视频教学系统PHP源码/在线考试系统PHP源码

源码介绍 在线考试教学系统平台系统源码,视频教学系统PHP源码,在线考试系统PHP源码。 安装 1、部署好网站环境:php5.6mysql 2、将源码传至网站根目录,php源码用二进制上传,或者上传压缩包在空间解压 3、将“zztuk…

【PPT】修改新建文本框默认字体

【PPT】修改新建文本框默认字体

物联边缘网关有哪些功能?物联边缘网关在工业方向的应用-天拓四方

随着物联网技术的快速发展,越来越多的设备和系统正在接入到网络中,形成了一个庞大的智能生态系统。在这个系统中,物联边缘网关扮演着至关重要的角色,它不仅是连接设备和云端的桥梁,更是推动智能应用落地的关键。在当今…

【GD32】从零开始学GD32单片机高级篇——SDIO外设详解(GD32F470ZGT6)

目录 简介总线拓扑总线操作“无响应” 和 “无数据” 操作多块读写操作数据流读写操作 总线协议命令响应R1/R1b (普通命令响应)R2 (CID, CSD 寄存器)R3 (OCR 寄存器)R4 (Fast IO)R4b(Fast IO)R5 (中断请求)R5b(中断请求)R6 (发布的…

skywalking介绍及搭建

链路追踪框架比对: skywalking安装部署: 下载地址:Downloads | Apache SkyWalking 配置微服务与skywalking整合: copy agent/optional-plugins/apm-spring-cloud-getway-xx.jar到plugins,然后重启skywalking 监控界面…

会议管理系统(含源码+sql+视频导入教程)

👉文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 会议管理系统拥有两种角色 管理员:部门管理、员工管理、会议管理、会议室管理、预订会议、添加员工、注册员工审批等 用户:个人通知中心、预订会议、查看所有会议…

网络研究观-20240601

新战争时代的商业风险 美国人已经将战争视为遥远战场上发生的事件。然而,网络空间打破了这种看法,让全球战争的真正影响来到了美国家门口。 攻击不再局限于遥远的战场,而是在最意想不到的时间和地点发动袭击。 谁将主宰第五次工业革命&…

FreeSWITCH 1.10.10 简单图形化界面21-录音相关

FreeSWITCH 1.10.10 简单图形化界面21-录音相关 FreeSWITCH GUI界面预览00、安装FreeSWITCH GUI先看使用手册1、录音相关的应用11、record用法:举例:注意: 12、record_session用法:举例: 2、录音相关的变量3、单腿录音…

使用matplotlib绘制折线条形复合图

使用matplotlib绘制折线条形复合图 介绍效果代码 介绍 在数据可视化中,复合图形是一种非常有用的工具,可以同时显示多种数据类型的关系。在本篇博客中,我们将探讨如何使用 matplotlib 库来绘制包含折线图和条形图的复合图。 效果 代码 imp…

联合和枚举(自定义类型)

1.枚举(关键字:enum) 1.1枚举类型的声明 把可能的值一一列举 赋的值是可能取值 1.2枚举类型的优点 1)增加代码的可读性和可维护性 2)和#define定义的标识符比较枚举有类型检查,更加严谨 3)便于调试&a…

vue:实现丝滑上传进度条

一、效果展示 缓若江海凝清光 . 二、代码 const uploadProgress ref(); //上传进度//进度丝滑更新 //进度,时常 const ProgressChange (targetPercent: number, duration: number) > {//performance.now() 是浏览器提供的一个高性能时间 API,它返…

Linux系统编程(七)网络编程TCP、UDP

本文目录 一、基础知识点1. IP地址2. 端口3. 域名4. 网络协议类型5. IP协议类型6. 字节序7. socket套接字 二、常用API1. socket套接字描述符2. bind套接字绑定3. listen设置客户端连接个数4. accept接收客户端请求5. connect连接服务端 三、编程流程1.TCP编程 在学习本章之前&…

《mysql轻松学习·二》

1、创建数据表 contacts:数据表名 auto_increament:自动增长 primary key:主键 engineInnoDB default charsetutf8; 默认字符集utf8,不写就默认utf8 对数据表的操作: alter table 数据表名 add sex varchar(1); //添…

使用YOLOv10训练自己的数据集

1. yolov10源码下载 THU-MIG/yolov10: YOLOv10: Real-Time End-to-End Object Detection (github.com)https://github.com/THU-MIG/yolov10?tabreadme-ov-file 2. 环境配置 预先安装好ANACONDA、PyCharm或者VSCode等基本软件。参考以下博客: 史上最全最详细的An…