FreeRTOS基础(八):FreeRTOS 时间管理

      前面我们用了FreeRTOS中的延时函数,本篇博客就来探讨FreeRTOS中的延时函数,看看他们是如何发挥作用的。当我们在裸机开发中调用delay_ms()函数时,我们的处理器将不处理任何事,造成处理器资源的浪费。 为此,为了提高CPU的利用率,在FreeRTOS中提供了两个延时API函数。

目录

一、相对延时函数

1.1  函数介绍

1.2 函数实现过程

1.2.1. 任务进入阻塞状态

1.2.2. 计算延迟结束时间

1.2.3. 检查延迟数是否为0

1.2.4. 任务进入延迟列表

1.2.5. 调度任务

1.2.6. 时钟节拍中断处理

1.2.7. 任务唤醒

1.3 总结

二、绝对延时

2.1 函数介绍

2.2 函数实现过程

2.2.1 获取当前时间

2.2.2 计算下一个唤醒时间

2.2.3 检查是否需要延时

2.2.4 更新上一次唤醒时间

2.2.5 返回 pdPASS

2.3 总结 

2.4 举例理解

三、相对延时和绝对延时的对比

四、延时函数解析

五、使用延时函数

5.1 相对延时函数的使用

5.2 绝对延时函数的使用

六、实验验证

6.1 创建任务及实现任务函数

6.2 主函数调用入口函数,操作系统开始进行任务的切换和调度

6.3 实验结果


一、相对延时函数

1.1  函数介绍

        FreeRTOS 的相对延时函数 vTaskDelay 是一个常用的任务延时函数。相对延时:指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束。它使当前任务进入阻塞状态一段指定的时间,以节拍(ticks)为单位

函数原型:void vTaskDelay( const TickType_t xTicksToDelay );
参数:xTicksToDelay(将调⽤任务转换到就绪状态前保持在阻塞状态的滴答中断次数)

参数

  • xTicksToDelay: 指定要延迟的时钟节拍数。一个时钟节拍的时间长度取决于系统节拍率(通常通过 configTICK_RATE_HZ 定义),  我们通常使用的是1ms作为一个时钟节拍。

1.2 函数实现过程

1.2.1. 任务进入阻塞状态

        当调用 vTaskDelay 时,当前任务会立即进入阻塞状态。该函数首先计算出当前系统的时钟节拍计数(xTickCount)和需要延迟的节拍数(xTicksToDelay)。

1.2.2. 计算延迟结束时间

          任务延迟结束的时间点(即节拍计数)由当前节拍计数加上需要延迟的节拍数计算得出:

TickType_t xTimeToWake;
xTimeToWake = xTickCount + xTicksToDelay;

这里,xTickCount 是当前系统的节拍计数,xTicksToDelay 是要延迟的节拍数。

1.2.3. 检查延迟数是否为0

         如果 xTicksToDelay 为 0,函数直接返回,因为不需要延迟。

1.2.4. 任务进入延迟列表

        FreeRTOS 使用延迟列表(delay list)来管理处于延迟状态的任务。任务被添加到延迟列表中,并记录其唤醒时间(xTimeToWake)。

vListInsert( &xDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );

1.2.5. 调度任务

          任务进入阻塞状态后,调度器会选择下一个任务运行。任务切换是通过 taskYIELD 实现的:

taskYIELD();

1.2.6. 时钟节拍中断处理

         系统时钟节拍中断(tick interrupt)发生时,节拍计数器(xTickCount)递增。中断处理函数 vTaskIncrementTick 负责更新系统节拍计数器,并检查是否有任务需要从延迟列表中移到就绪列表。

void vTaskIncrementTick( void )
{
    if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
    {
        const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;
        xTickCount = xConstTickCount;

        if( xConstTickCount == ( TickType_t ) 0U )
        {
            // 检查溢出情况
        }

        // 检查是否有任务需要唤醒
        if( xConstTickCount >= xNextTaskUnblockTime )
        {
            prvCheckDelayedTasks();
        }
    }
}

1.2.7. 任务唤醒

        当当前的节拍计数达到或超过任务的唤醒时间(xTimeToWake)时,延迟任务将从延迟列表中移除,并加入就绪列表。这个过程在 prvCheckDelayedTasks 函数中完成:

void prvCheckDelayedTasks( void )
{
    TCB_t *pxTCB;

    while( listLIST_IS_EMPTY( pxDelayedTaskList ) == pdFALSE )
    {
        pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );

        if( xTickCount < listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) ) )
        {
            break;
        }

        ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
        vTaskSwitchContext();
    }
}

1.3 总结

    vTaskDelay 函数通过将任务放入延迟列表,计算任务的唤醒时间,并在时钟节拍中断时检查任务是否需要被唤醒,从而实现任务的延时功能。这个过程涉及到任务调度、时钟节拍计数的管理以及列表操作等机制,确保任务能够在指定的时间点后重新进入就绪状态,继续执行。

二、绝对延时

2.1 函数介绍

         xTaskDelayUntil() 是 FreeRTOS 中用于创建相对精确的周期性任务的函数。它可以确保任务以固定的时间间隔运行,而不受其它任务的影响。vTaskDelay() 不同,xTaskDelayUntil() 通过一个时间基准点来确保周期的准确性。绝对延时:指将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务。

函数原型:
BaseType_t xTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );

参数

  • pxPreviousWakeTime: 指向一个变量,这个变量保存上一次任务被唤醒的时间。该变量在第一次调用前需要被初始化为当前时间,通常使用 xTaskGetTickCount() 获取。

  • xTimeIncrement: 任务之间的时间间隔,即任务周期,单位是 tick 数。

 返回值

        返回 pdPASS 表示成功。理论上,它总是返回 pdPASS,因此可以忽略返回值。

2.2 函数实现过程

2.2.1 获取当前时间

      函数首先获取当前的 tick 计数,即系统运行到目前为止的时间。

TickType_t xTimeNow;
xTimeNow = xTaskGetTickCount();

2.2.2 计算下一个唤醒时间

       计算任务下一个唤醒时间 xNextWakeTime,这个时间是基于上一次唤醒时间 *pxPreviousWakeTime 加上时间增量 xTimeIncrement

TickType_t xNextWakeTime;
xNextWakeTime = *pxPreviousWakeTime + xTimeIncrement;

2.2.3 检查是否需要延时

       比较当前时间 xTimeNow 和下一个唤醒时间 xNextWakeTime,决定是否需要延时。如果当前时间已经超过下一个唤醒时间,说明任务已经错过了预定的周期,此时不会延时;否则,任务需要延时到下一个唤醒时间。

if( xNextWakeTime > xTimeNow ) {
    // 延时到下一个唤醒时间
    vTaskDelay( xNextWakeTime - xTimeNow );
}

2.2.4 更新上一次唤醒时间

       更新 *pxPreviousWakeTimexNextWakeTime,为下一次调用 xTaskDelayUntil 做准备。

*pxPreviousWakeTime = xNextWakeTime;

2.2.5 返回 pdPASS

      函数总是返回 pdPASS 表示成功,理论上可以忽略返回值。

以下是 xTaskDelayUntil() 的完整实现代码:

BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )
{
    TickType_t xTimeNow;
    BaseType_t xShouldDelay = pdFALSE;

    configASSERT( pxPreviousWakeTime );

    vTaskSuspendAll();
    {
        xTimeNow = xTaskGetTickCount();

        // 下一次唤醒时间
        const TickType_t xNextWakeTime = *pxPreviousWakeTime + xTimeIncrement;

        // 检查是否需要延时
        if( xNextWakeTime > xTimeNow ) {
            xShouldDelay = pdTRUE;
        }
    }
    ( void ) xTaskResumeAll();

    // 如果需要延时,则调用 vTaskDelay 进行延时
    if( xShouldDelay != pdFALSE ) {
        vTaskDelay( xNextWakeTime - xTimeNow );
    }

    // 更新上一次唤醒时间
    *pxPreviousWakeTime = xNextWakeTime;

    return pdPASS;
}

4. 解释

  • vTaskSuspendAll 和 xTaskResumeAll: 这两个函数用于确保在获取当前时间和计算延时时间的过程中,任务不会被调度器中断。这样可以避免在多任务环境中出现时间不一致的情况。

  • configASSERT: 这是一个断言,用于检查指针 pxPreviousWakeTime 是否为空,确保函数调用的正确性。

  • 时间计算和比较: 通过 xTimeNowxNextWakeTime 的比较来决定是否需要延时,如果当前时间小于下一个唤醒时间,则需要延时到下一个周期。

2.3 总结 

  xTaskDelayUntil() 函数通过维护一个上次唤醒时间和一个时间增量,确保任务可以在精确的周期时间内执行。它的实现通过获取当前时间、计算下一个唤醒时间、决定是否需要延时、更新上一次唤醒时间等步骤,确保任务在 FreeRTOS 中能够以固定的时间间隔运行,提高了系统的实时性和任务调度的精确性。

2.4 举例理解

例子 1: 定时闹钟

想象你是一个上班族,每天早上需要在 7:00 起床,你设置了一个闹钟来确保你准时起床。

1. 第一次设定闹钟:你第一次设定闹钟在 7:00。
2. 起床:第二天早上闹钟响了,你在 7:00 起床。
3. 重新设定闹钟:你再次设定闹钟在第二天的 7:00,确保你每天都在同一时间起床。

这里,7:00 就是你的基准时间(`pxPreviousWakeTime`),每天 24 小时的间隔就是时间增量(`xTimeIncrement`)。不管你前一天晚上几点睡觉,闹钟都会确保你第二天早上 7:00 起床。

 例子 2: 公交车的时间表

公交车按照固定的时间表运行,比如每隔 15 分钟发一班车。

1. 公交车发车:假设第一班车在早上 8:00 发车。
2. 下一班车时间:每隔 15 分钟一班,下一班车在 8:15,接着是 8:30,以此类推。

在这个例子中,早上 8:00 是基准时间(`pxPreviousWakeTime`),15 分钟是时间增量(`xTimeIncrement`)。公交车司机会按照这个固定的时间表发车,不受前一班车是否晚点的影响。

例子 3: 厨房定时器

你在厨房烘焙蛋糕,需要每隔 30 分钟检查一次烤箱。

1. 第一次检查:你在蛋糕入烤箱后的 30 分钟时进行第一次检查。
2. 设定定时器:检查完毕后,你再次设定定时器在接下来的 30 分钟。
3. 后续检查:每隔 30 分钟你都进行一次检查,确保蛋糕不会烤焦。

这里,第一次检查时间就是你的基准时间(`pxPreviousWakeTime`),30 分钟是时间增量(`xTimeIncrement`)。不管你在检查时花了多长时间,定时器会确保你每隔 30 分钟进行一次检查。

在 FreeRTOS 中的类比

       在 FreeRTOS 中,`xTaskDelayUntil()` 类似于上述例子中的定时机制,它保证了任务以固定的时间间隔运行,而不是简单地延迟一段时间(这可能会因为任务执行的时间不同而导致整体时间漂移)。

1. 基准时间:任务上一次唤醒的时间。
2. 时间增量:任务周期,即任务之间的固定时间间隔。

         每次任务运行完后,都会计算下次唤醒的时间点(基准时间 + 时间增量),然后延时到这个时间点。如果任务执行时间过长导致当前时间已经超过下次唤醒时间,任务会立即执行而不再延时。

         通过 `xTaskDelayUntil()`,你可以确保任务以固定的周期运行,类似于每天固定时间起床、公交车按时间表发车以及厨房定时器检查烤箱。

三、相对延时和绝对延时的对比

适用场景举例

  • 相对延时 适合场景:

    • 简单的 LED 闪烁程序。
    • 需要延时一段时间再执行某些操作,而不关心时间的绝对精度。
  • 绝对延时 适合场景:

    • 定期采集传感器数据,每隔固定时间采集一次。
    • 实时控制系统中,需要以固定时间间隔执行控制算法。

四、延时函数解析

五、使用延时函数

5.1 相对延时函数的使用

使用方式:vTaskDelay(pdMS_TO_TICKS(100));或者直接 vTaskDelay(毫秒数)

pdMS_TO_TICKS() 是 FreeRTOS 提供的一个宏,用于将毫秒数转换为 tick 数。它的定义如下:

#define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( ( ( TickType_t ) ( xTimeInMs ) * configTICK_RATE_HZ ) / 1000 ) )

其中 configTICK_RATE_HZ 是 FreeRTOS 的 tick 频率配置项,表示每秒的 tick 数。通过这个宏,可以方便地将时间转换为 tick 数,以便使用 vTaskDelay() 进行延时。

vTaskDelay() 的延时精度取决于系统的 tick 频率(configTICK_RATE_HZ)。例如,如果 tick 频率为 1000 Hz(即 1 ms 一个 tick),那么延时的精度就是 1 毫秒。如果 tick 频率较低,延时的精度也会降低。

5.2 绝对延时函数的使用

1、初始化基准时间

        在任务函数中,使用 xTaskGetTickCount() 获取当前的 tick 计数,并将其赋值给 xLastWakeTime,作为初始的基准时间。

TickType_t xLastWakeTime = xTaskGetTickCount(); //获取当前的tick数
TickType_t xFrequency = pdMS_TO_TICKS(1000);  //延时时间为1秒周期

2、进行绝对延时

  vTaskDelayUntil(&xLastWakeTime, xFrequency);

六、实验验证

    定义三个任务,任务1和任务2使用vTaskDelay()函数。任务3使用vTaskDelayUntil()函数。 任务3将以固定的频率进行执行。

6.1 创建任务及实现任务函数

#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;    //定义任务句柄(结构体指针)
void start_task(void* args);




/**********************TASK1任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define  TASK1_STACK_SIZE  128            //定义堆栈大小为128字(1字等于4字节)
#define  TASK1_PRIO         1             //定义任务优先级,0-31根据任务需求
TaskHandle_t   task1_handler;           //定义任务句柄(结构体指针)
void task1(void* args);




/**********************TASK2任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define  TASK2_STACK_SIZE  128            //定义堆栈大小为128字(1字等于4字节)
#define  TASK2_PRIO         1             //定义任务优先级,0-31根据任务需求
TaskHandle_t   task2_handler;           //定义任务句柄(结构体指针)
void task2(void* args);



/**********************TASK3任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define  TASK3_STACK_SIZE  128            //定义堆栈大小为128字(1字等于4字节)
#define  TASK3_PRIO         2            //定义任务优先级,0-31根据任务需求
TaskHandle_t   task3_handler;           //定义任务句柄(结构体指针)
void task3(void* args);






/*********开始任务用来创建其他三个任务,只创建一次,不能是死循环,同时创建完3个任务后删除任务1本身***********/
void start_task(void* args)
{
	taskENTER_CRITICAL();        /*进入临界区*/
	BaseType_t xReturn;        

	xTaskCreate( (TaskFunction_t)         task1,
                             (char *)     "task1",  
              ( configSTACK_DEPTH_TYPE)   TASK1_STACK_SIZE,
                            (void *)      NULL,
                            (UBaseType_t) TASK1_PRIO ,
                        (TaskHandle_t *)  &task1_handler );
	
    /*对任务1创建是否成功进行判断*/							
	if(xReturn == pdPASS)
   {
         printf("create task1 Success\n");
   }
   else
   {
         printf("create task1 error\n");
   }
	
   
	xTaskCreate( (TaskFunction_t)         task2,
                             (char *)     "task2",  
              ( configSTACK_DEPTH_TYPE)   TASK2_STACK_SIZE,
                            (void *)      NULL,
                            (UBaseType_t) TASK2_PRIO ,
                        (TaskHandle_t *)  &task2_handler );	
							
	/*对任务2创建是否成功进行判断*/						
	if(xReturn == pdPASS)
   {
         printf("create task2 Success\n");
   }
   else
   {
         printf("create task2 error\n");
   }

   
     xTaskCreate( (TaskFunction_t)         task3,
                             (char *)     "task3",  
              ( configSTACK_DEPTH_TYPE)   TASK3_STACK_SIZE,
                            (void *)      NULL,
                            (UBaseType_t) TASK3_PRIO ,
                        (TaskHandle_t *)  &task3_handler );	
	
	/*对任务3创建是否成功进行判断*/
	if(xReturn == pdPASS)
   {
         printf("create task3 Success\n");
   }
   else
   {
         printf("create task3 error\n");
   }
	
   
	vTaskDelete(NULL);    //删除开始任务自身,传参NULL
							
	taskEXIT_CRITICAL();   /*退出临界区*/
		

    //临界区内不会进行任务的调度切换,出了临界区才会进行任务调度,抢占式						
}




/********其余三个任务的任务函数,无返回值且是死循环***********/

/***任务1:打印字符串验证*******/
void task1(void* args)
{
	while(1)
	{
		 printf("任务1运行!\n");
         vTaskDelay(300);       //FreeRTOS自带的延时函数
	
	}
	
	
}

/***任务2:打印字符串验证*******/
void task2(void* args)
{
	while(1)
	{
		printf("任务2运行!\n");
        vTaskDelay(300);       //FreeRTOS自带的延时函数
	}
	
}


/***任务3:实现绝对延时*******/
void task3(void* args)
{
		TickType_t xLastWakeTime = xTaskGetTickCount(); //获取当前的tick数
        TickType_t xFrequency = pdMS_TO_TICKS(2000);  //延时时间为2秒周期
		while(1)
	    {
		     printf("任务3运行!\n");
		     vTaskDelayUntil(&xLastWakeTime, xFrequency);
	     }	  
	
}





//FreeRTO入口例程函数,无参数,无返回值
void freertos_demo(void)
{
	    xTaskCreate( (TaskFunction_t)     start_task,
                             (char *)     "start_task",  
              ( configSTACK_DEPTH_TYPE)   START_TASK_STACK_SIZE,
                            (void *)      NULL,
                            (UBaseType_t) START_TASK_PRIO ,
                        (TaskHandle_t *)  &start_task_handler );
							
							
	vTaskStartScheduler();  //开启任务调度器
	
}




6.2 主函数调用入口函数,操作系统开始进行任务的切换和调度

#include "stm32f4xx.h"                  // Device header
#include "stdio.h"
#include "myusart.h"

#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"



int main(void)
{
    //1、硬件初始化
     My_UsartInit();
     
    //2、进入入口函数
     freertos_demo();
	

  
}



6.3 实验结果

至此,已经讲解完毕!初次学习,循序渐进,一步步掌握即可!以上就是全部内容!请务必掌握,创作不易,欢迎大家点赞加关注评论,您的支持是我前进最大的动力!下期再见!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ChatTTS改良版 - 高度逼真的人类情感文本生成语音工具(TTS)本地一键整合包下

先介绍下ChatTTS 和之前发布的 Fish Speech 类似&#xff0c;都是免费开源的文本生成语音的AI软件&#xff0c;但不同的是&#xff0c;ChatTTS测试下来&#xff0c;对于人类情感语调的模仿&#xff0c;应该是目前开源项目做的最好的&#xff0c;是一款高度接近人类情感、音色、…

计算机工作原理(程序猿必备的计算机常识)

目录 一、计算机工作原理1.冯诺依曼体系2. CPU执行指令的过程 二、操作系统三、进程的概念四、进程的管理五、进程的调度 一、计算机工作原理 1.冯诺依曼体系 现在的计算机大多都遵循冯诺依曼体系结构 CPU&#xff1a; 中央处理器&#xff0c;进行算术运算和逻辑判断&#…

百度文心一言API批量多线程写文章软件-key免费无限写

百度文心大模型的两款主力模型ENIRE Speed、ENIRE Lite全面免费&#xff0c;即刻生效。 百度文心大模型的两款主力模型 这意味着&#xff0c;大模型已进入免费时代&#xff01; 据了解&#xff0c;这两款大模型发布于今年 3 月&#xff0c;支持 8K 和 128k 上下文长度。 ER…

赢销侠的秘密武器:如何提升客户满意度?

在竞争激烈的商业战场上&#xff0c;客户满意度是企业能否长盛不衰的关键。它如同一面镜子&#xff0c;映照出企业的服务质量和产品实力。那么&#xff0c;赢销侠们是如何运用秘密武器来提升客户满意度的呢&#xff1f;本文将深入探讨这一课题&#xff0c;并揭示背后的策略与智…

灾备方案中虚拟化平台元数据备份技术应用

首先需要介绍下元数据是什么&#xff1f; 元数据&#xff08;Metadata&#xff09;是一个重要的概念&#xff0c;它描述了数据的数据&#xff0c;也就是说&#xff0c;元数据提供了关于数据属性的信息。这些属性可能包括数据的存储位置、历史数据、资源查找、文件记录等。 元…

LabVIEW与欧陆温控表通讯的实现与应用:厂商软件与自主开发的优缺点

本文探讨了LabVIEW与欧陆温控表通讯的具体实现方法&#xff0c;并对比了使用厂商提供的软件与自行开发LabVIEW程序的优缺点。通过综合分析&#xff0c;帮助用户在实际应用中选择最适合的方案&#xff0c;实现高效、灵活的温控系统。 LabVIEW与欧陆温控表通讯的实现与应用&#…

基于Jenkins+Kubernetes+GitLab+Harbor构建CICD平台

1. 实验环境 1.1 k8s环境 1&#xff09;Kubernetes 集群版本是 1.20.6 2&#xff09;k8s控制节点&#xff1a; IP&#xff1a;192.168.140.130 主机名&#xff1a;k8s-master 配置&#xff1a;4C6G 3&#xff09;k8s工作节点 节点1&#xff1a; IP&#xff1a;192.1…

day-37 最大正方形

思路 动态规划&#xff0c;这题主要得弄明白状态转换方程&#xff0c;dp[i][j]表示以&#xff08;i,j&#xff09;为右下角的最大正方形 解题方法 1.首先将第一行和第一列初始化&#xff0c;当对应位置的matrix为’0’时&#xff0c;dp数组对应位置也为零&#xff0c;否则为1 …

上位机图像处理和嵌入式模块部署(f407 mcu中fatfs中间件使用)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们已经实现了spi norflash的驱动&#xff0c;理论上这已经可以实现数据的持久化保存了。为什么还需要一个文件系统呢&#xff1f;主要原因还…

HTML静态网页成品作业(HTML+CSS)——家乡常德介绍网页(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…

【Qt 学习笔记】Qt窗口 | 对话框 | Qt对话框的分类及介绍

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt窗口 | 对话框 | 模态对话框 文章编号&#xff1a;Qt 学习笔记 / 51…

API开发秘籍:揭秘Swagger与Spring REST Docs的文档自动化神技

在这个数字化时代&#xff0c;如何让你的业务像外卖一样快速送达顾客手中&#xff1f;本文将带你走进Spring Boot的世界&#xff0c;学习如何利用RESTful API构建一个高效、直观的“外卖帝国”。从基础的REST架构风格&#xff0c;到Spring MVC的魔力&#xff0c;再到Swagger和S…

解决kettle界面右上角的connect消失——且使用admin登录不上Kettle资源库

一、问题描述 1.1、Kettle界面右上角的connect消失了 当我们配置Kettle界面的资源库(Other Repositories)内容后,Kettle界面右上角的connect消失了;如下图所示: 1.2、使用默认的账户【admin】和密码【admin】登录不上kettle资源库 当我们切换到我们配置的数据库使用超管账…

排序-希尔排序

介绍 希尔排序属于那种没有了解过的直接看代码一脸懵逼的&#xff0c; 所以同学们尽量不要直接看代码&#xff0c;仔细阅读本篇博客内容。 插入排序本来算是一个低效排序&#xff0c; 一次只可以挪动一个数据&#xff0c; 但是&#xff0c;它的强来了&#xff01;&#xff01…

513.找树左下角的值

给定一个二叉树&#xff0c;在树的最后一行找到最左边的值。 示例 1: 示例 2: 思路&#xff1a; 深度最大的叶子结点一定是最后一行。 优先左边搜索&#xff0c;记录深度最大的叶子节点&#xff0c;此时就是树的最后一行最左边的值 代码&#xff1a; class Solution:def fi…

272 基于matlab的形态滤波和局域值分解(LMD)的齿轮故障诊断

基于matlab的形态滤波和局域值分解&#xff08;LMD&#xff09;的齿轮故障诊断&#xff0c;GUI交互界面。通过形态滤波对一维信号进行降噪处理&#xff0c;并通过LMD局部均值分解提取故障信号&#xff0c;最后提取处故障频率。程序已调通&#xff0c;可直接运行。 272 形态滤波…

红外听力教学考试系统-红外语音听力广播在大学英语四六级听力考试中应用

红外听力教学考试系统-红外语音听力广播在大学英语四六级听力考试中的应用 由北京海特伟业科技有限公司任洪卓发布于2024年6月1日 红外语音听力广播&#xff08;即红外听力教学考试系统&#xff09;在英语四六级听力考试的应用正日益凸显出其重要性和优越性。在当前的高等教育…

排序-插入排序与选择排序

插入排序 基本思想 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列 。 打扑克牌整理手牌用的就是插入排序的思想 代码实现 void InsertSort(int* a, int n) { assert(a); …

中间件模版引擎

文章目录 中间件1.自定义中间件1&#xff09;全局2&#xff09;局部中间件 2.内置中间件(静态资源目录&#xff09; Art-template1.模板语法1&#xff09;输出2&#xff09;原文输出3&#xff09;条件判断4&#xff09;循环5&#xff09;子模版6&#xff09;模版继承7&#xff…

2024四川三支一扶“考生信息表”照着填❗

2024四川三支一扶“考生信息表”照着填❗ ☑️四川三支一扶开始报名&#xff0c;大家要按照提示如实、准确、完整填写《高校毕业生“三支一扶”计划招募考生信息表》哦~ ☑️不知道怎么填写的宝子们&#xff0c;可以参考图1。 ☑️毕业证书编号如实填写&#xff0c;若是应届生&…