FreeRTOS_任务相关API函数

news2024/11/27 19:46:01

目录

1. 任务创建和删除 API 函数

1.1 函数 xTaskCreate()

1.2 函数 xTaskCreateStatic()

1.3 函数 xTaskCreateRestricted()

1.4 函数 vTaskDelete()

2. 任务创建和删除实验(动态方法)

2.1 实验程序与分析

3. 任务创建和删除实验(静态方法)

3.1 完整实验代码

4. 任务挂起和恢复 API 函数

4.1 函数 vTaskSuspend()

4.2 函数 vTaskResume()

4.3 函数 xTaskResumeFromISR()

5. 任务挂起与恢复实验

5.1 实验程序

5.1.1 main.c

5.1.2 EXTI.c

5.1.3 EXTI.h

5.1.4 实验结果


1. 任务创建和删除 API 函数

        FreeRTOS 最基本的功能就是任务管理,而任务管理最基本的操作就是创建和删除任务

FreeRTOS 的任务创建和删除 API 函数如下所示:

        xTaskCreate()                                                 使用动态的方法创建一个任务。

        xTaskCreateStatic()                                       使用静态的方法创建一个任务。

        xTaskCreateRestricted()                               创建一个使用 MPU 进行限制的任务,相关内存使用动态内存分配。

        vTaskDelete()                                                 删除一个任务。

1.1 函数 xTaskCreate()

        此函数用来创建一个任务,任务需要 RAM 来保存与任务有关的状态信息(任务控制块),任务也需要一定的 RAM 来作为任务堆栈

        如果使用函数 xTaskCreate() 来创建任务的话那么这些所需的 RAM 就会自动的从 FreeRTOS 的堆中分配,因此必须提供内存管理文件,默认我们使用 heap_4.c 这个内存管理文件,而且宏 configSUPPORT_DYNAMIC_ALLOCATION 必须为 1。

        如果使用函数 xTaskCreateStatic() 创建的话这些 RAM 就需要用户来提供了。

        新创建的任务默认就是就绪态的,如果当前没有比它更高优先级的任务运行,那么此任务就会立即进入运行态开始运行,不管在任务调度器启动前还是启动后,都可以创建任务。

BaseType_t xTaskCreate(TaskFunction_t          pxTaskCode,     //任务函数
                       const char *const       pcName,         //任务名字
                       const uint16_t          usStackDepth,   //任务堆栈大小
                       void *const             pvParameters,   //传递给任务函数的参数 
                       UBaseType_t             uxPriority,     //任务优先级
                       TaskHandle_t *const     pxCreatedTask   //任务句柄
                       )

参数:

pxTaskCode:              任务函数。

pcName:                     任务名字,一般用于追踪和调试,任务名字长度不能超过 configMAX_TASK_NAME_LEN。

usStackDepth:           任务堆栈大小,注意实际申请到的堆栈是 usStackDepth 的 4 倍。其中空闲任务的任务堆栈大小为 configMINIMAL_STACK_SIZE。

pvParameters:           传递给任务函数的参数。

uxPriority:                  任务优先级,范围是 0 ~ configMAX_PRIORITIES - 1。

pxCreatedTask:         任务句柄,任务创建成功以后会返回此任务的任务句柄,这个句柄其实就是任务的任务堆栈。此参数就用来保存这个任务句柄。其他 API 函数可能会使用到这个句柄。

返回值:

pdPASS:                                                                                              任务创建成功。

errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY:                    任务创建失败,因为堆内存不足!

1.2 函数 xTaskCreateStatic()

        此函数和 xTaskCreate() 的功能相同,也是用来创建任务的,但是使用此函数创建的任务所需的 RAM 需要由用户来提供。如果要使用此函数的话需要将宏 configSUPPORT_STATIC_ALLOCATION 定义为 1。

BaseType_t xTaskCreate(TaskFunction_t          pxTaskCode,     //任务函数
                       const char *const       pcName,         //任务名字
                       const uint16_t          usStackDepth,   //任务堆栈大小
                       void *const             pvParameters,   //传递给任务函数的参数 
                       UBaseType_t             uxPriority,     //任务优先级
                       StackType_t *const      puxStackBuffer, //任务堆栈
                       StaticTask_t *const     pxTaskBuffer    //任务控制块
                       )

参数:

pxTaskCode:              任务函数。

pcName:                     任务名字,一般用于追踪和调试,任务名字长度不能超过 configMAX_TASK_NAME_LEN。

usStackDepth:           任务堆栈大小,由于本函数是静态方法创建任务,所以任务堆栈由用户给出,一般是个数组,此参数就是这个数组的大小

pvParameters:           传递给任务函数的参数。

uxPriority:                  任务优先级,范围是 0 ~ configMAX_PRIORITIES - 1。

puxStackBuffer:        任务堆栈,一般为数组,数组类型要为 StackType_t 类型。

pxTaskBuffer:            任务控制块。

返回值:

NULL:                         任务创建失败,puxStackBuffer 或 pxTaskBuffer 为 NULL 的时候会导致这个错误发生。

其他值:                        任务创建成功,返回任务的任务句柄。

1.3 函数 xTaskCreateRestricted()

        此函数也是用来创建任务的,只不过此函数要求所使用的 MCU 有 MPU(内存保护单元),用此函数创建的任务会受到 MPU 的保护。其他的功能和函数 xTaskCreate() 一样。

BaseType_t xTaskCreateRestricted(const TaskParameters_t *const    pxTaskDefinition,
                                 TaskHandle_t*                    pxCreatedTask)

参数:

pxTaskDefinition:                指向一个结构体 TaskParameters_t,这个结构体描述了任务的任务函数、堆栈大小、优先级等。此结构体在文件 task.h 中有定义。

pxCreatedTask:                   任务句柄。

返回值:

pdPASS:                               任务创建成功。

其他值:                                  任务未创建成功,很有可能是因为 FreeRTOS 的堆太小了。

1.4 函数 vTaskDelete()

        删除一个用函数 xTaskCreate() 或者 xTaskCreateStatic() 创建的任务,被删除了的任务不再存在,也就是说再也不会进入运行态。任务被删除以后就不能再使用此任务的句柄!

        如果此任务是使用动态方法创建的,也就是使用函数 xTaskCreate() 创建的,那么在此任务被删除以后此任务之前申请的堆栈和控制块内存会在空闲任务中被释放掉,因此当调用函数 vTaskDelete() 删除任务以后必须给空闲任务一定的运行时间。

        只有那些由内核分配给任务的内存才会在任务被删除以后自动的释放掉,用户分配给任务的内存需要用户自行释放掉,比如某个任务中用户调用函数 pvPortMalloc() 分配了 500 字节的内存,那么在此任务被删除以后用户也必须调用函数 vPortFree() 将这 500 字节的内存释放掉,否则会导致内存泄露。

vTaskDelete(TaskHandle_t  xTaskToDelete)

参数:

xTaskToDelete:                要删除的任务的任务句柄。

返回值:

2. 任务创建和删除实验(动态方法)

        经过上面的学习,我们已经了解了 FreeRTOS 的任务创建和删除的 API 函数,紧接着我们就来学习如何去使用这些 API 函数;

        首先先学习 xTaskCreate() 和 vTaskDelete() 这两个函数的使用:

在本实验中设计了三个任务:start_task、task1_task 和 task2_task,这三个任务的任务功能如下:

        start_task:用来创建其他两个任务。

        task1_task:当此任务运行 5 次以后就会调用函数 vTaskDelete() 删除任务 task2_task,此任务也会控制 LED0 的闪烁,并且周期性的刷新 LCD 指定区域的背景颜色。

        task2_task:此任务为普通的应用任务,此任务也会控制 LED1 的闪烁,并且周期性的刷新 LCD 指定区域的背景颜色。

2.1 实验程序与分析

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "LED.h"
#include "FreeRTOS.h"
#include "task.h"
#include "lcd.h"


//任务优先级
#define START_TASK_PRIO     1
//任务堆栈大小
#define START_STK_SIZE      128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define TASK1_TASK_PRIO     2
//任务堆栈大小
#define TASK1_STK_SIZE      128
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);

//任务优先级
#define TASK2_TASK_PRIO     3
//任务堆栈大小
#define TASK2_STK_SIZE      128
//任务句柄
TaskHandle_t Task2Task_Handler;
//任务函数
void task2_task(void *pvParameters);

//LCD刷屏时使用的颜色
int LCD_Discolor[14]={WHITE, BLACK, BLUE,  BRED,
                      GRED,  GBLUE, RED,   MAGENTA,
                      GREEN, CYAN,  YELLOW,BROWN,
                      BRRED, GRAY};

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);   //首先设置系统中断优先级分组4
    delay_init(168);
    uart_init(115200);
    LED_Init();
    LCD_Init();
    
    POINT_COLOR=RED;
    LCD_ShowString(30,10,200,16,16,"ATK STM32F407");
    LCD_ShowString(30,30,200,16,16,"FreeRTOS Experiment");
    LCD_ShowString(30,50,200,16,16,"Task Create and Delete");
    LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
    LCD_ShowString(30,90,200,16,16,"2023/06/06");   
    
    //创建开始任务
    xTaskCreate((TaskFunction_t)start_task,             //任务函数
                (const char*   )"start_task",           //任务名称
                (uint16_t      )START_STK_SIZE,         //任务堆栈大小    
                (void*         )NULL,                  //传递给任务函数的参数
                (UBaseType_t   )START_TASK_PRIO,        //任务优先级
                (TaskHandle_t* )&StartTask_Handler);    //任务句柄
    //调用函数 xTaskCreate() 创建 start_task 任务,函数中的各个参数就是上面的任务设置中定义的,其他任务的创建也用这种方法。            
    vTaskStartScheduler();    //开启任务调度
    //调用函数 vTaskStartScheduler() 开启 FreeRTOS 的任务调度器,FreeRTOS 开始运行。            
}

//开始任务函数,用于创建其他两个任务
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();   //进入临界区
    //创建TASK1任务
    xTaskCreate((TaskFunction_t)task1_task,     //任务函数
                (const char*   )"task1_task",   //任务名称
                (uint16_t      )TASK1_STK_SIZE, //任务堆栈大小
                (void*         )NULL,           //传递给任务函数的参数
                (UBaseType_t   )TASK1_TASK_PRIO,//任务优先级
                (TaskHandle_t* )&Task1Task_Handler);
    //创建TASK2任务
    xTaskCreate((TaskFunction_t)task2_task,     //任务函数
                (const char*   )"task2_task",   //任务名称
                (uint16_t      )TASK2_STK_SIZE, //任务堆栈大小
                (void*         )NULL,           //传递给任务函数的参数
                (UBaseType_t   )TASK2_TASK_PRIO,//任务优先级
                (TaskHandle_t* )&Task2Task_Handler);
    vTaskDelete(StartTask_Handler);  //删除开始任务,参数为开始任务的句柄
    //开始任务的任务函数,在此任务函数中我们创建了另外两个任务 task1_task 和 task2_task。
    //start_task 任务的职责就是用来创建其他的任务或者信号量、消息队列等。
    //当创建完成以后就可以删除掉 start_task 任务。                
    taskEXIT_CRITICAL();    //离开临界区                     
}

//task1任务函数
//当此任务运行 5 次以后就会调用函数 vTaskDelete() 删除任务 task2_task,此任务也会控制 LED0 的闪烁,并且周期性的刷新 LCD 指定区域的背景颜色。
void task1_task(void *pvParameters)
{
    u8 task1_num=0; //定义一个局部变量来保存该函数运行的次数
    
    POINT_COLOR=BLACK;
    LCD_DrawRectangle(5,110,115,314); //(x1,y1),(x2,y2):矩形的对角坐标
    LCD_DrawLine(5,130,115,130); x1,y1:起点坐标  x2,y2:终点坐标 
    
    POINT_COLOR=BLUE;
    LCD_ShowString(6,111,110,16,16,"Task1 Run:000");
    
    while(1) //任务内部的程序都是无限的循环,也可以使用for(;;)
    {
        task1_num++;   //注意task1_num加到255的时候会清零!!  
        //while循环延时了1000ms,也就是1s,所以每隔 1 秒钟 task1_num 加 1 并且 LED0翻转。
        LED0=!LED0;
        printf("任务 1 已经执行:%d 次\r\n",task1_num);
        if(task1_num==5)
        {
            vTaskDelete(Task2Task_Handler); //当此任务运行 5 次以后就会调用函数 vTaskDelete() 删除任务 task2_task
            printf("任务 1 删除了任务 2 !\r\n");
        }
        LCD_Fill(6,131,114,313,LCD_Discolor[task1_num%14]); //填充区域  
        //前四个参数是填充的x1,y1;x2,y2坐标,最后一个参数是要填充的颜色 
        //task1_num数组大小为14,所以对14取余,task1_task函数运行,task1_num++;始终得到的是0~13的循环,也就是LCD_Discolor中颜色依次变化
        LCD_ShowxNum(86,111,task1_num,3,16,0x80);
        //x,y:起点坐标
        //num:数值(0~999999999);	 
        //len:长度(即要显示的位数)
        //size:字体大小
        //mode:
        //[7]:0,不填充;1,填充0.
        //[6:1]:保留
        //[0]:0,非叠加显示;1,叠加显示.
        vTaskDelay(1000);    //延时1s,也就是1000个时钟节拍
    }
}

//task2任务函数
//此任务为普通的应用任务,此任务也会控制 LED1 的闪烁,并且周期性的刷新 LCD 指定区域的背景颜色
void task2_task(void *pvParameters)
{
    u8 task2_num=0;
    
    POINT_COLOR=BLACK;
    LCD_DrawRectangle(125,110,234,314); //画一个矩形	
	LCD_DrawLine(125,130,234,130);		//画线
	POINT_COLOR = BLUE;
	LCD_ShowString(126,111,110,16,16,"Task2 Run:000");
    
    while(1)
    {
        task2_num++;	//任务2执行次数加1 
        LED1=!LED1;
		printf("任务 2 已经执行:%d 次\r\n",task2_num); 
		LCD_ShowxNum(206,111,task2_num,3,16,0x80);  //显示任务2执行次数
		LCD_Fill(126,131,233,313,LCD_Discolor[13-task2_num%14]); //填充区域  13-task2_num%14 该颜色是LCD_Discolor数组逆序显示颜色
        vTaskDelay(1000);                           //延时1s,也就是1000个时钟节拍
    }
}

        实验中串口的现象如上所示:一开始任务1和任务2是同时开始运行的,由于任务2的优先级比任务1的优先级高,所以任务2先输出信息。当任务1运行了5次以后任务1就删除了任务2,最后只剩下任务1在运行了;注意task1_num加到255的时候会清零!!

总结:

        这是我学习 FreeRTOS 以来写的第一个程序,很多习惯后面的学习中都是要用到的。比如说使用任务宏定义任务优先级,堆栈大小等,一般有关一个任务的东西我们放到一起,比如说任务堆栈、任务句柄、任务函数声明等,这样方便修改。要是工程比较大的话最好用一个专用的头文件来管理。

        在 main 函数中一开始肯定是初始化各种外设硬件,初始化完外设以后调用函数 xTaskCreate() 创建一个开始任务,注意创建开始任务是在调用函数 vTaskStartScheduler() 开启任务调度器之前,这样当后面开启任务调度器以后就会直接运行开始任务了。其他任务的创建就放在开始任务的任务函数中,由于开始任务的职责就是创建其他应用任务和信号量、队列等这些内核对象的,所以它只需要执行一次,当这些东西创建完成以后就可以删除掉开始任务了。

3. 任务创建和删除实验(静态方法)

        本次实验我们使用函数 xTaskCreateStatic() 来创建任务,也就是静态的方法实现任务的创建和删除,任务的堆栈、任务控制块就需要由用户来指定了

注意:

        使用静态方法创建任务的时候需要将 宏configSUPPORT_STATIC_ALLOCATION 设置为 1 ,在文件 FreeRTOSConfig.h 中设置,如下所示:

#define configSUPPORT_STATIC_ALLOCATION        1    //静态内存
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
	extern void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );
#endif
#if( configSUPPORT_STATIC_ALLOCATION == 1 )

	/* 如果支持静态分配,那么应用程序必须提供以下回调函数-这使得应用程序可以选择性地提供将被计时器任务用作任务堆栈和TCB的内存。*/
	extern void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize );

#endif

        如果默认设置 #define configSUPPORT_STATIC_ALLOCATION        1 ,那么就需要在主函数中定义 vApplicationGetIdleTaskMemory 和 vApplicationGetTimerTaskMemory ,否则程序就会报错!!!

//空闲任务任务堆栈
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];  //#define configMINIMAL_STACK_SIZE		( ( unsigned short ) 130 )
//空闲任务控制块
static StaticTask_t IdleTaskTCB;

//定时器服务任务堆栈
static StackType_t TimerTaskStack[configTIMER_TASK_STACK_DEPTH]; //#define configTIMER_TASK_STACK_DEPTH	( configMINIMAL_STACK_SIZE * 2 )
//定时器服务任务控制块
static StaticTask_t TimerTaskTCB;

//获取空闲任务的任务堆栈和任务控制块内存,本次使用的是静态内存,
//因此空闲任务的任务堆栈和任务控制块的内存就应该由用户来提供,                      
//FreeRTOS提供了接口函数 vApplicationGetIdleTaskMemory() 实现此函数即可
//ppxIdleTaskTCBBuffer:任务控制块内存
//ppxIdleTaskStackBuffer:任务堆栈内存
//pulIdleTaskStackSize:任务堆栈大小
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
                                   StackType_t **ppxIdleTaskStackBuffer,
                                   uint32_t *pulIdleTaskStackSize)
{
    *ppxIdleTaskTCBBuffer=&IdleTaskTCB;
    *ppxIdleTaskStackBuffer=IdleTaskStack; //数组名代表首元素地址
    *pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;
}

//获取定时器服务任务的任务堆栈和任务控制块内存
//ppxTimerTaskTCBBuffer:任务控制块内存
//ppxTimerTaskStackBuffer:任务堆栈内存
//pulTimerTaskStackSize:任务堆栈大小
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,       
                                    StackType_t **ppxTimerTaskStackBuffer,
                                    uint32_t *pulTimerTaskStackSize)
{
    *ppxTimerTaskTCBBuffer=&TimerTaskTCB;
    *ppxTimerTaskStackBuffer=TimerTaskStack;
    *pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;
}

        如果使用静态方法的话需要用户实现两个函数 :vApplicationGetIdleTaskMemory() 和 vApplicationGetTimerTaskMemory()。通过这两个函数来给空闲任务和定时器服务任务的任务堆栈和任务控制块分配内存。

        首先,用户自己定义静态的任务堆栈和任务控制块内存,然后将这些内存传递给函数参数。最后创建空闲任务和定时器服务任务的 API 函数会调用 vApplicationGetIdleTaskMemory() 和 vApplicationGetTimerTaskMemory() 来获取这些内存。

//任务优先级
#define START_TASK_PRIO     1
//任务堆栈大小
#define START_STK_SIZE      128
//任务堆栈
StackType_t StartTaskStack[START_STK_SIZE];
//任务控制块
StaticTask_t StartTaskTCB;
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define TASK1_TASK_PRIO     2
//任务堆栈大小
#define TASK1_STK_SIZE      128
//任务堆栈
StackType_t Task1TaskStack[TASK1_STK_SIZE];
//任务控制块
StaticTask_t Task1TaskTCB;
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);

//任务优先级
#define TASK2_TASK_PRIO     3
//任务堆栈大小
#define TASK2_STK_SIZE      128
//任务堆栈
StackType_t Task2TaskStack[TASK2_STK_SIZE];
//任务控制块
StaticTask_t Task2TaskTCB;
//任务句柄
TaskHandle_t Task2Task_Handler;
//任务函数
void task2_task(void *pvParameters);

        1. 静态创建任务需要用户提供任务堆栈,这里定义一个数组作为任务堆栈,堆栈数组为 StackType_t 类型。

        2. 定义任务控制块,注意任务控制块的类型要用 StaticTask_t,切记!!!

3.1 完整实验代码

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "LED.h"
#include "FreeRTOS.h"
#include "task.h"
#include "lcd.h"

//空闲任务任务堆栈
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];  //#define configMINIMAL_STACK_SIZE		( ( unsigned short ) 130 )
//空闲任务控制块
static StaticTask_t IdleTaskTCB;

//定时器服务任务堆栈
static StackType_t TimerTaskStack[configTIMER_TASK_STACK_DEPTH]; //#define configTIMER_TASK_STACK_DEPTH	( configMINIMAL_STACK_SIZE * 2 )
//定时器服务任务控制块
static StaticTask_t TimerTaskTCB;

//任务优先级
#define START_TASK_PRIO     1
//任务堆栈大小
#define START_STK_SIZE      128
//任务堆栈
StackType_t StartTaskStack[START_STK_SIZE];
//任务控制块
StaticTask_t StartTaskTCB;
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define TASK1_TASK_PRIO     2
//任务堆栈大小
#define TASK1_STK_SIZE      128
//任务堆栈
StackType_t Task1TaskStack[TASK1_STK_SIZE];
//任务控制块
StaticTask_t Task1TaskTCB;
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);

//任务优先级
#define TASK2_TASK_PRIO     3
//任务堆栈大小
#define TASK2_STK_SIZE      128
//任务堆栈
StackType_t Task2TaskStack[TASK2_STK_SIZE];
//任务控制块
StaticTask_t Task2TaskTCB;
//任务句柄
TaskHandle_t Task2Task_Handler;
//任务函数
void task2_task(void *pvParameters);

//LCD刷屏时使用的颜色
int LCD_Discolor[14]={WHITE, BLACK, BLUE,  BRED,
                      GRED,  GBLUE, RED,   MAGENTA,
                      GREEN, CYAN,  YELLOW,BROWN,
                      BRRED, GRAY};

//获取空闲任务的任务堆栈和任务控制块内存,本次使用的是静态内存,
//因此空闲任务的任务堆栈和任务控制块的内存就应该由用户来提供,                      
//FreeRTOS提供了接口函数 vApplicationGetIdleTaskMemory() 实现此函数即可
//ppxIdleTaskTCBBuffer:任务控制块内存
//ppxIdleTaskStackBuffer:任务堆栈内存
//pulIdleTaskStackSize:任务堆栈大小
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
                                   StackType_t **ppxIdleTaskStackBuffer,
                                   uint32_t *pulIdleTaskStackSize)
{
    *ppxIdleTaskTCBBuffer=&IdleTaskTCB;
    *ppxIdleTaskStackBuffer=IdleTaskStack;
    *pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;
}

//获取定时器服务任务的任务堆栈和任务控制块内存
//ppxTimerTaskTCBBuffer:任务控制块内存
//ppxTimerTaskStackBuffer:任务堆栈内存
//pulTimerTaskStackSize:任务堆栈大小
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,       
                                    StackType_t **ppxTimerTaskStackBuffer,
                                    uint32_t *pulTimerTaskStackSize)
{
    *ppxTimerTaskTCBBuffer=&TimerTaskTCB;
    *ppxTimerTaskStackBuffer=TimerTaskStack;
    *pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;
}

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);   //首先设置系统中断优先级分组4
    delay_init(168);
    uart_init(115200);
    LED_Init();
    LCD_Init();
    
    POINT_COLOR=RED;
    LCD_ShowString(30,10,200,16,16,"ATK STM32F407");
    LCD_ShowString(30,30,200,16,16,"FreeRTOS Experiment");
    LCD_ShowString(30,50,200,16,16,"Task Static Create and Delete");
    LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
    LCD_ShowString(30,90,200,16,16,"2023/06/06");   
    
    //创建开始任务
    StartTask_Handler=xTaskCreateStatic((TaskFunction_t)start_task,             //任务函数
                                        (const char*   )"start_task",           //任务名称
                                        (uint32_t      )START_STK_SIZE,         //任务堆栈大小    
                                        (void*         )NULL,                   //传递给任务函数的参数
                                        (UBaseType_t   )START_TASK_PRIO,        //任务优先级
                                        (StackType_t*  )StartTaskStack,         //任务堆栈
                                        (StaticTask_t* )&StartTaskTCB);         //任务控制块
    //调用函数 xTaskCreate() 创建 start_task 任务,函数中的各个参数就是上面的任务设置中定义的,其他任务的创建也用这种方法。            
    vTaskStartScheduler();    //开启任务调度
    //调用函数 vTaskStartScheduler() 开启 FreeRTOS 的任务调度器,FreeRTOS 开始运行。            
}

//开始任务函数,用于创建其他两个任务
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();   //进入临界区
    //创建TASK1任务
    Task1Task_Handler=xTaskCreateStatic((TaskFunction_t)task1_task,     //任务函数
                                        (const char*   )"task1_task",   //任务名称
                                        (uint32_t      )TASK1_STK_SIZE, //任务堆栈大小
                                        (void*         )NULL,           //传递给任务函数的参数
                                        (UBaseType_t   )TASK1_TASK_PRIO,//任务优先级
                                        (StackType_t*  )Task1TaskStack, //任务堆栈
                                        (StaticTask_t* )&Task1TaskTCB); //任务控制块
    //创建TASK2任务
    Task2Task_Handler=xTaskCreateStatic((TaskFunction_t)task2_task,     //任务函数
                                        (const char*   )"task2_task",   //任务名称
                                        (uint32_t      )TASK2_STK_SIZE, //任务堆栈大小
                                        (void*         )NULL,           //传递给任务函数的参数
                                        (UBaseType_t   )TASK2_TASK_PRIO,//任务优先级
                                        (StackType_t*  )Task2TaskStack, //任务堆栈
                                        (StaticTask_t* )&Task2TaskTCB); //任务控制块
    vTaskDelete(StartTask_Handler);  //删除开始任务,参数为开始任务的句柄
    //开始任务的任务函数,在此任务函数中我们创建了另外两个任务 task1_task 和 task2_task。
    //start_task 任务的职责就是用来创建其他的任务或者信号量、消息队列等。
    //当创建完成以后就可以删除掉 start_task 任务。                
    taskEXIT_CRITICAL();    //离开临界区                     
}

//task1任务函数
//当此任务运行 5 次以后就会调用函数 vTaskDelete() 删除任务 task2_task,此任务也会控制 LED0 的闪烁,并且周期性的刷新 LCD 指定区域的背景颜色。
void task1_task(void *pvParameters)
{
    u8 task1_num=0; //定义一个局部变量来保存该函数运行的次数
    
    POINT_COLOR=BLACK;
    LCD_DrawRectangle(5,110,115,314); //(x1,y1),(x2,y2):矩形的对角坐标
    LCD_DrawLine(5,130,115,130); x1,y1:起点坐标  x2,y2:终点坐标 
    
    POINT_COLOR=BLUE;
    LCD_ShowString(6,111,110,16,16,"Task1 Run:000");
    
    while(1) //任务内部的程序都是无限的循环,也可以使用for(;;)
    {
        task1_num++;   //注意task1_num加到255的时候会清零!!  
        //while循环延时了1000ms,也就是1s,所以每隔 1 秒钟 task1_num 加 1 并且 LED0翻转。
        LED0=!LED0;
        printf("任务 1 已经执行:%d 次\r\n",task1_num);
        if(task1_num==5)
        {
            vTaskDelete(Task2Task_Handler); //当此任务运行 5 次以后就会调用函数 vTaskDelete() 删除任务 task2_task
            printf("任务 1 删除了任务 2 !\r\n");
        }
        LCD_Fill(6,131,114,313,LCD_Discolor[task1_num%14]); //填充区域  
        //前四个参数是填充的x1,y1;x2,y2坐标,最后一个参数是要填充的颜色 
        //task1_num数组大小为14,所以对14取余,task1_task函数运行,task1_num++;始终得到的是0~13的循环,也就是LCD_Discolor中颜色依次变化
        LCD_ShowxNum(86,111,task1_num,3,16,0x80);
        //x,y:起点坐标
        //num:数值(0~999999999);	 
        //len:长度(即要显示的位数)
        //size:字体大小
        //mode:
        //[7]:0,不填充;1,填充0.
        //[6:1]:保留
        //[0]:0,非叠加显示;1,叠加显示.
        vTaskDelay(1000);    //延时1s,也就是1000个时钟节拍
    }
}

//task2任务函数
//此任务为普通的应用任务,此任务也会控制 LED1 的闪烁,并且周期性的刷新 LCD 指定区域的背景颜色
void task2_task(void *pvParameters)
{
    u8 task2_num=0;
    
    POINT_COLOR=BLACK;
    LCD_DrawRectangle(125,110,234,314); //画一个矩形	
	LCD_DrawLine(125,130,234,130);		//画线
	POINT_COLOR = BLUE;
	LCD_ShowString(126,111,110,16,16,"Task2 Run:000");
    
    while(1)
    {
        task2_num++;	//任务2执行次数加1 
        LED1=!LED1;
		printf("任务 2 已经执行:%d 次\r\n",task2_num); 
		LCD_ShowxNum(206,111,task2_num,3,16,0x80);  //显示任务2执行次数
		LCD_Fill(126,131,233,313,LCD_Discolor[13-task2_num%14]); //填充区域  13-task2_num%14 该颜色是LCD_Discolor数组逆序显示颜色
        vTaskDelay(1000);                           //延时1s,也就是1000个时钟节拍
    }
}


        静态创建和删除任务和动态创建和删除任务的串口输出结果一致,本质区别在于需要我们手动提供任务控制块和任务栈区的内存

4. 任务挂起和恢复 API 函数

        有时候我们需要暂停某任务的运行,过一段时间以后再重新运行。这个时候要是使用了删除和重建的方法那么任务中变量保存的值肯定丢失了!FreeRTOS 给我们提供了解决这种问题的方法,那就是任务挂起和恢复当某个任务要停止运行一段时间的话就将这个任务挂起,当要重新运行这个任务的话就恢复这个任务的运行

FreeRTOS 的任务挂起和恢复 API 函数如下所示:

        vTaskSuspend()                                        挂起一个任务

        vTaskResume()                                         恢复一个任务的运行

        xTaskResumeFromISR()                          中断服务函数中恢复一个函数的运行

4.1 函数 vTaskSuspend()

        此函数用于将某个任务设置为挂起态,进入挂起态的任务永远都不会进入运行态退出挂起态的唯一方法就是调用任务恢复函数 vTaskResume() 或 xTaskResumeFromISR()

函数原型如下:

void vTaskSuspend(TaskHandle_t xTaskToSuspend)

参数:

xTaskToSuspend:        要挂起的任务的任务句柄,创建任务的时候会为每个任务分配一个任务句柄。

                                         如果使用函数 xTaskCreate() 创建任务的话那么函数的参数 pxCreatedTask 就是此任务的任务句柄,

                                         如果使用函数 xTaskCreateStatic() 创建任务的话那么函数的返回值就是此任务的任务句柄。

                                         也可以通过函数 xTaskGetHandler() 来根据任务名字来获取某个任务的任务句柄。

注意:

        如果参数为 NULL 的话表示挂起任务自己!!!

返回值:

4.2 函数 vTaskResume()

        将一个任务从挂起态恢复到就绪态,任务必须处于挂起态才能调用该函数,只有通过函数 vTaskSuspend() 设置为挂起态的任务才可以使用 vTaskResume() 恢复!

函数原型如下:

void vTaskResume(TaskHandle_t xTaskToResume)

参数:

xTaskToResume:                        要恢复的任务的任务句柄

返回值:

4.3 函数 xTaskResumeFromISR()

        此函数是 vTaskResume() 的中断版本,用于在中断服务函数中恢复一个任务。

函数原型如下:

BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume)

参数:

xTaskToResume:                        要恢复任务的任务句柄

返回值:

pdTRUE:                                      恢复运行的任务的任务优先级等于或者高于正在运行的任务(被中断打断的任务),这意味着在退出中断服务函数以后必须进行一次上下文切换。

pdFLASE:                                    恢复运行的任务的任务优先级低于正在运行的任务(被中断打断的任务),这意味着在退出中断服务函数以后不需要进行一次上下文切换。

5. 任务挂起与恢复实验

        本次实验学习使用 FreeRTOS 的任务挂起和恢复相关的 API 函数,包括 vTaskSuspend()、vTaskResume() 和 xTaskResumeFromISR()。

本实验设计 4 个任务:start_task、key_task、task1_task 和 task2_task,这四个任务的任务功能如下:

        start_task:用来创建其他三个任务

        key_task:按键服务任务,检测按键按下的结果,根据不同的按键结果执行不同的操作

        task1_task:应用任务 1

        task2_task:应用任务 2

本实验需要四个按键,KEY0、KEY1、KEY2 和 KEY_UP,这四个按键的功能如下:

        KEY0:此按键为中断模式,在中断服务函数中恢复任务 2 的运行

        KEY1:此按键为输入模式,用于恢复任务 1 的运行

        KEY2:此按键为输入模式,用于挂起任务 2 的运行

        KEY_UP:此按键为输入模式,用于挂起任务 1 的运行

5.1 实验程序

5.1.1 main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "LED.h"
#include "FreeRTOS.h"
#include "task.h"
#include "lcd.h"
#include "key.h"
#include "EXTI.h"

//任务优先级
#define START_TASK_PRIO     1
//任务堆栈大小
#define START_STK_SIZE      168
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define KEY_TASK_PRIO       2
//任务堆栈大小
#define KEY_STK_SIZE        168
//任务句柄
TaskHandle_t KeyTask_Handler;
//任务函数
void key_task(void *pvParameters);

//任务优先级
#define TASK1_TASK_PRIO     3
//任务堆栈大小
#define TASK1_STK_SIZE      168
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);

//任务优先级
#define TASK2_TASK_PRIO     4
//任务堆栈大小
#define TASK2_STK_SIZE      168
//任务句柄
TaskHandle_t Task2Task_Handler;
//任务函数
void task2_task(void *pvParameters);

//LCD刷屏时使用的颜色
int LCD_discolor[14]={WHITE, BLACK, BLUE,  BRED,      
                      GRED,  GBLUE, RED,   MAGENTA,       	 
                      GREEN, CYAN,  YELLOW,BROWN, 			
                      BRRED, GRAY };

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);  //系统中断优先级分组 4
    delay_init(168);
    uart_init(115200);
    KEY_Init();
    LED_Init();
    LCD_Init();
    EXTIX_Init();
    
    POINT_COLOR=RED;
    LCD_ShowString(30,10,200,16,16,"ATK STM32F407");
    LCD_ShowString(30,30,200,16,16,"FreeRTOS Experiment");
    LCD_ShowString(30,50,200,16,16,"Task Suspend and Resume");
    LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK");
    LCD_ShowString(30,90,200,16,16,"2023/06/07");
    
    //创建开始任务
    xTaskCreate((TaskFunction_t)start_task,             //任务函数
                (const char*   )"start_task",           //任务名称
                (uint16_t      )START_STK_SIZE,         //任务堆栈大小
                (void*         )NULL,                   //传递给任务函数的参数
                (UBaseType_t   )START_TASK_PRIO,        //任务优先级
                (TaskHandle_t* )&StartTask_Handler);    //任务句柄
    vTaskStartScheduler();    //开启任务调度
}

//开始任务函数
void start_task(void* pvParameters)
{
    taskENTER_CRITICAL();   //进入临界区
    //创建KEY函数
    xTaskCreate((TaskFunction_t)key_task,             //任务函数
                (const char*   )"key_task",           //任务名称
                (uint16_t      )KEY_STK_SIZE,         //任务堆栈大小
                (void*         )NULL,                   //传递给任务函数的参数
                (UBaseType_t   )KEY_TASK_PRIO,        //任务优先级
                (TaskHandle_t* )&KeyTask_Handler);    //任务句柄
    //创建TASK1函数
    xTaskCreate((TaskFunction_t)task1_task,             //任务函数
                (const char*   )"task1_task",           //任务名称
                (uint16_t      )TASK1_STK_SIZE,         //任务堆栈大小
                (void*         )NULL,                   //传递给任务函数的参数
                (UBaseType_t   )TASK1_TASK_PRIO,        //任务优先级
                (TaskHandle_t* )&Task1Task_Handler);    //任务句柄
    //创建TASK2函数
    xTaskCreate((TaskFunction_t)task2_task,             //任务函数
                (const char*   )"task2_task",           //任务名称
                (uint16_t      )TASK2_STK_SIZE,         //任务堆栈大小
                (void*         )NULL,                   //传递给任务函数的参数
                (UBaseType_t   )TASK2_TASK_PRIO,        //任务优先级
                (TaskHandle_t* )&Task2Task_Handler);    //任务句柄
    vTaskDelete(StartTask_Handler);  //删除开始任务
    taskEXIT_CRITICAL();    //退入临界区
}

//key任务函数
void key_task(void *pvParameters)
{
    u8 key;
    while(1)
    {
        key=KEY_Scan(0);
        switch(key)
        //KEY0      中断恢复任务2
        //KEY1      恢复任务1
        //KEY2      挂起任务2
        //KEY_UP    挂起任务1
        {
            case 4: //Key.h中定义:key=4 KEY_UP按键按下
                vTaskSuspend(Task1Task_Handler); //挂起任务1
                printf("挂起任务1的运行!\r\n");
                break;
            case 2: //Key.h中定义:key=2 KEY1按键按下
                vTaskResume(Task1Task_Handler); //恢复任务1
                printf("恢复任务1的运行!\r\n");
                break;
            case 3: //Key.h中定义:key=3 KEY2按键按下
                vTaskSuspend(Task2Task_Handler); //挂起任务2
                printf("挂起任务2的运行!\r\n");
                break;
        }
        vTaskDelay(10);   //延时10ms
    }
}

//task1任务函数
void task1_task(void *pvParameters)
{
    u8 task1_num=0;
    
    POINT_COLOR=BLACK;
    
    LCD_DrawRectangle(5,110,115,314); //画一个矩形
    LCD_DrawLine(5,130,115,130);    //画线
    
    POINT_COLOR=BLUE;
    LCD_ShowString(6,111,110,16,16,"Task1 Run:000");
    while(1)
    {
        task1_num++; //任务执1行次数加1 注意task1_num加到255的时候会清零!!
        LED0=!LED0;
        printf("任务1已经执行:%d次\r\n",task1_num);
        LCD_Fill(6,131,114,313,LCD_discolor[task1_num%14]); //要填充区域的颜色,设置颜色数组依次循环
        LCD_ShowxNum(86,111,task1_num,3,16,0x80);  //显示任务执行次数
        vTaskDelay(1000);   //延时1s,也就是1000个时钟节拍
    }
}

//task2任务函数
void task2_task(void *pvParameters)
{
    u8 task2_num=0;
	
	POINT_COLOR = BLACK;

	LCD_DrawRectangle(125,110,234,314); 	//画一个矩形	
	LCD_DrawLine(125,130,234,130);		//画线
	POINT_COLOR = BLUE;
	LCD_ShowString(126,111,110,16,16,"Task2 Run:000");
	while(1)
	{
		task2_num++;	//任务2执1行次数加1 
		LED1=!LED1;
		printf("任务2已经执行:%d次\r\n",task2_num);
		LCD_Fill(126,131,233,313,LCD_discolor[13-task2_num%14]); //填充区域
		LCD_ShowxNum(206,111,task2_num,3,16,0x80);	//显示任务执行次数
        vTaskDelay(1000);                           //延时1s,也就是1000个时钟节拍	
	}
}


5.1.2 EXTI.c

#include "stm32f4xx.h"            
#include "EXTI.h"
#include "delay.h"
#include "key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "usart.h"

//初始化外部中断
//初始化PE4引脚,PE4引脚对应于KEY0按键,KEY0按键低电平触发按键,所以设置该引脚为下降沿触发
//PE4引脚对应外部中断线4
void EXTIX_Init(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    
    KEY_Init();
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);  //使用外部中断需要使能SYSCFG时钟
    
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);  //PE4 连接到中断线 4
    
    EXTI_InitStructure.EXTI_Line=EXTI_Line4; //外部中断线4
    EXTI_InitStructure.EXTI_LineCmd=ENABLE; //使能
    EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; //中断事件
    EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发,因为KEY0按键按下就表示引脚输入下降沿
    EXTI_Init(&EXTI_InitStructure);
    
    NVIC_InitStructure.NVIC_IRQChannel=EXTI4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0x06;  //抢占优先级 6 
                                                                 //如果中断服务函数要使用 FreeRTOS 的 API 函数的话,那么中断优先级一定要低于
                                                                 //configMAX_SYSCALL_INTERRUPT_PRIORITY! 这里设置为6
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x00;
    NVIC_Init(&NVIC_InitStructure);
}

//任务句柄
extern TaskHandle_t Task2Task_Handler;

//外部中断线4服务程序
void EXTI4_IRQHandler(void)
{
    //该程序的意思就是设置一个变量
    BaseType_t YieldRequired; //typedef long BaseType_t;
    
    delay_xms(20);  //消抖
    if(KEY0==0) //KEY0按键按下
    {
        YieldRequired=xTaskResumeFromISR(Task2Task_Handler);  //中断服务函数中恢复任务2的运行
        printf("恢复任务2的运行!\r\n");
        if(YieldRequired==pdTRUE) //设置变量接收中断服务函数中恢复任务2的返回值
        //如果函数xTaskResumeFromISR()返回值为pdTRUE,那么说明要恢复的这个
		//任务的任务优先级等于或者高于正在运行的任务(被中断打断的任务),所以在
		//退出中断的时候一定要进行上下文切换!
        {
            portYIELD_FROM_ISR(YieldRequired); //调用该函数进行上下文切换
        }
    }
    EXTI_ClearITPendingBit(EXTI_Line4);  //清除外部中断线4上的中断标志位
}



5.1.3 EXTI.h

#ifndef _EXTI__H_
#define _EXTI__H_
#include "sys.h"


void EXTIX_Init(void);

#endif

5.1.4 实验结果

        从上图中可以看到,一开始任务1和任务2都正常运行,当挂起任务1或者任务2以后,任务1或者任务2就会停止运行,直到下一次重新恢复任务1或者任务2的运行。

        重点是:保存任务运行次数的变量都没有发生数据丢失,如果用任务删除和重建的方法这些数据必然会丢失掉的!

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

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

相关文章

ZC-CLS381RGB颜色识别——配置寄存器组(上)

文章目录 前言一、ZC-CLS381RGB简介二、配置寄存器组1.主控寄存器2.检测速率寄存器2.增益寄存器2.颜色数据寄存器 三、状态转移图和信号波形图绘制总结 前言 在现代工业生产中,颜色识别技术已经成为了一个非常重要的技术。颜色识别可以用于产品质量检测、物料分类、…

特瑞仕|常见电子元器件的故障现象及原因详解

​电子元器件是现代电子设备中不可或缺的组成部分,但在长时间的使用过程中,它们也可能会出现各种故障现象。本文将详细介绍一些常见电子元器件的故障现象及原因,以帮助读者更好地理解和处理这些问题。 一、电阻器 故障现象:电阻值…

湖南人的商业策略:用“副产品免费”的模式,推动主产品消费

湖南人的商业策略:用“副产品免费”的模式,推动主产品消费 什么是副产品免费模式?(主产品要钱,副产品不要钱) 免费商业模型设计的核心就是通过延长产业链,以此来达到利润链条的延伸,在这个过程中衍生和挖掘…

1.8 掌握Scala函数

一、声明函数 (一)显式声明函数 案例演示 (1)加法函数 package net.huawei.day08import scala.io.StdIn/*** 功能:显式声明函数* 作者:* 日期:2023年03月20日*/ object Example01 {def add1…

测试用例设计方法之因果图详解

一、因果图概述 因果图是从需求中找出因(输入条件)和果(输出或程序状态的改变),通过分析输入条件之间的关系(组合关系、约束关系等)及输入和输出之间的关系绘制出因果图,再转化成判…

composer-创建自己的依赖库

1.环境 码云账号(或者GitHub)码云地址composer 官方仓库账号 Packagist composer官方仓库安装composer 2.步骤 2.1 发行composer的依赖包是需要从git 或者svn里拉取的,所以得先在码云里创建一个仓库 2.2 依赖包中必须有composer.json配置标明名字依赖等信息,配置大概如下,配…

Vue基础第五篇

一、动态组件 1.基本使用 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>动态组件</title><script src"https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></sc…

华为安全专家带你入门安全多方计算

安全多方计算 从0到1 6月8日&#xff08;本周四&#xff09; 19:00—21:00&#xff0c;华为安全专家带你入门安全多方计算&#xff0c;欢迎参加&#xff01; 考虑以下应用场景&#xff1a; Alice认为她可能患有某种遗传病&#xff0c;Bob有一个包含DNA模式与各类疾病的数据库。…

Hive执行计划之hive依赖及权限查询和常见使用场景

文章目录 概述1.explain dependency的查询与使用2.借助explain dependency解决一些常见问题2.1.识别看似等价的SQL代码实际上是不等价的&#xff1a;2.2 通过explain dependency验证将过滤条件在不同位置的查询区别 3.查看SQL操作涉及到的相关权限信息 概述 Hive查看执行计划的…

RocketMQ-Request-Reply特性

源码版本号:版本号:4.9.4 使用场景 随着服务规模的扩大&#xff0c;单机服务无法满足性能和容量的要求&#xff0c;此时需要将服务拆分为更小粒度的服务或者部署多个服务实例构成集群来提供服务。在分布式场景下&#xff0c;RPC是最常用的联机调用的方式。 在构建分布式应用…

高考季,17岁VS人工智能,谁的作文更胜一筹?

又到一年高考日。想起十二年前我也曾和众多莘莘学子一样,在这场人生的史诗里挣扎奋斗。 那时的我满怀着期待和焦虑,站在人生的岔口,茫然纠结该循哪条道路。十二年光阴荏苒,岁月如梭, 如今我已不复当年学子的面容,更无法回首当时的迷茫与彷徨。 时过境迁,我如今以另一种身份再…

flask+scrapy

管道数据库 class SpiderBookPipeline:def __init__(self):host localhostuser rootpassword hdp020820db 警察大学信息检索self.conn pymysql.connect(hosthost, useruser, passwordpassword, dbdb)self.cursor self.conn.cursor()def process_item(self, item, spider…

【Python】Python系列教程-- Python3 元组(十三)

文章目录 前言访问元组修改元组删除元组元组运算符元组索引&#xff0c;截取元组内置函数关于元组是不可变的 前言 往期回顾&#xff1a; Python系列教程–Python3介绍&#xff08;一&#xff09;Python系列教程–Python3 环境搭建&#xff08;二&#xff09;Python系列教程–…

项目中的Echarts图表统计

数据可视化 一、Echarts二、前端&#xff08;VueEcharts&#xff09;HomeView.vue&#xff08;完整代码&#xff09; 三、后端&#xff08;SpringBootMyBatis&#xff09;BorrowController.javaIBorrowService.javaBorrowService.javadatetimeToDateStr()函数countList()函数 B…

同样是产品经理 段位差别大

同样是产品经理&#xff0c;段位差别大 趣讲大白话&#xff1a;做人的差距大 【趣讲信息科技189期】 **************************** 市场越内卷 对产品的要求越来越高 不管叫不叫产品经理这个头衔 产品开发的重要性不会降低 《人人都是产品经理》总结的段位 姑且一看&#xff…

java线程多线程并发

文章目录 对java线程的认识wait&#xff08;&#xff09;和sleep&#xff08;&#xff09;区别&#xff1f;wait&#xff0c;notify为什么要放在同步代码块中&#xff1f; 多线程**什么时候使用多线程**&#xff1a;**多线程的优缺点**&#xff1a;**线程安全问题**&#xff1a…

MATLAB应用

目录 网站 智能图像色彩缩减和量化 网站 https://yarpiz.com/ 智能图像色彩缩减和量化 使用智能聚类方法&#xff1a;&#xff08;a&#xff09;k均值算法&#xff0c;&#xff08;b&#xff09;模糊c均值聚类&#xff08;FCM&#xff09;和&#xff08;c&#xff09;自组织神…

Mysql—存储过程

简介 存储过程就是类似于MySQL触发器&#xff0c;经过事先编写好相应语句&#xff0c;通过编译后存储在数据库上。触发器不需要手动调用即可实现相应SQL功能。MySQL存储过程&#xff0c;需要自己去调用得到相应的结果。 语法 创建存储过程 CREATE DEFINER CURRENT_USER PR…

git---->团队开发神器,一篇文章直接掌握

git---->团队开发神器&#xff0c;一篇文章直接掌握 一 学习git的原因概念版本的概念1 版本控制软件的基础功能2 集中式版本控制软件3 分布式版本控制 二 git的安装三 GitHub Desktop的使用四 团队操作五 中央服务器--github从github上下载文件到本地仓库传输文件 六 国内中…

chatgpt赋能python:Python如何实现自增

Python如何实现自增 在Python编程中&#xff0c;自增是一种非常常用的操作&#xff0c;它可以让我们在循环、计数等场景中更加方便地进行操作。实际上&#xff0c;在Python中&#xff0c;实现自增非常简单&#xff0c;本文将介绍Python中常用的自增操作&#xff0c;并分享自增…