FreeRTOS学习笔记(二)

一、时间片调度

1、同等优先级任务轮流地享有相同的 CPU 时间(可设置), 叫时间片,在FreeRTOS中,一个时间片就等于SysTick 中断周期

/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
    uint32_t task1_num = 0;
    while(1)
    {
        taskENTER_CRITICAL();               /* 进入临界区 */
        printf("task1运行次数:%d\r\n",++task1_num);
        taskEXIT_CRITICAL();                /* 退出临界区 */
        delay_ms(10);
    }
}

/* 任务二,列表项的插入和删除实验 */
void task2( void * pvParameters )
{
    uint32_t task2_num = 0;
    while(1)
    {
        taskENTER_CRITICAL();               /* 进入临界区 */
        printf("task2运行次数:%d\r\n",++task2_num);
        taskEXIT_CRITICAL();                /* 退出临界区 */
        delay_ms(10);
    }
}

二、FreeRTOS任务相关API函数介绍

在这里插入图片描述
获取任务运行时间函数代码

char task_buff[500];
/* 任务二,实现任务运行时间统计API函数的使用 */
void task2( void * pvParameters )
{
    uint8_t key = 0;
    while(1)
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            vTaskGetRunTimeStats(task_buff);
            printf("%s\r\n",task_buff);
        }
        vTaskDelay(10);
    }
}

任务查询函数使用例程

char task_buff[500];
/* 任务二,实现任务状态查询API函数使用 */
void task2( void * pvParameters )
{
    UBaseType_t priority_num = 0;
    UBaseType_t task_num = 0;
    UBaseType_t task_num2 = 0;
    TaskStatus_t * status_array = 0;
    TaskStatus_t * status_array2 = 0;
    TaskHandle_t task_handle = 0;
    UBaseType_t task_stack_min = 0;
    eTaskState state = 0;
    uint8_t i = 0;
    
    vTaskPrioritySet( task2_handler,4 );
    priority_num = uxTaskPriorityGet( NULL );
    printf("task2任务优先级为%ld\r\n",priority_num);
    
    task_num = uxTaskGetNumberOfTasks();
    printf("任务数量:%ld\r\n",task_num);
    
    status_array = mymalloc(SRAMIN,(sizeof(TaskStatus_t) * task_num));
    task_num2 = uxTaskGetSystemState( status_array,task_num,NULL);
    printf("任务名\t\t任务优先级\t任务编号\r\n");
    for(i = 0; i < task_num2; i++)
    {
        printf("%s\t\t%ld\t%ld\r\n",
                status_array[i].pcTaskName,
                status_array[i].uxCurrentPriority,
                status_array[i].xTaskNumber);
    }
    
    status_array2 = mymalloc(SRAMIN,sizeof(TaskStatus_t));
    vTaskGetInfo( task2_handler,status_array2,pdTRUE,eInvalid);
    printf("任务名:%s\r\n",status_array2->pcTaskName);
    printf("任务优先级:%ld\r\n",status_array2->uxCurrentPriority);
    printf("任务编号:%ld\r\n",status_array2->xTaskNumber);
    printf("任务状态:%d\r\n",status_array2->eCurrentState);
    
    task_handle = xTaskGetHandle( "task1" );
    printf("任务句柄:%#x\r\n",(int)task_handle);
    printf("task1的任务句柄:%#x\r\n",(int)task1_handler);
    
    state = eTaskGetState( task2_handler );
    printf("当前task2的任务状态为:%d\r\n",state);
    
    vTaskList( task_buff );
    printf("%s\r\n",task_buff);
    while(1)
    {
//        task_stack_min = uxTaskGetStackHighWaterMark( task2_handler );
//        printf("task2历史剩余最小堆栈为%ld\r\n",task_stack_min);
        vTaskDelay(1000);
    }
}

三、FreeRTOS时间管理

在这里插入图片描述
相对延时:指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束
绝对延时:指将整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务

/* 任务一,演示相对延时函数 */
void task1( void * pvParameters )
{
    while(1)
    {
        LED0_TOGGLE();      /* PB1 */
        delay_ms(20);
        vTaskDelay(500);
    }
}

/* 任务二,演示绝对延时函数 */
void task2( void * pvParameters )
{
    TickType_t xLastWakeTime;
    xLastWakeTime = xTaskGetTickCount();
    while(1)
    {
        LED1_TOGGLE();      /* PB0 */
        delay_ms(20);
        vTaskDelayUntil(&xLastWakeTime,500);
    }
}

四、FreeRTOS消息队列

1、队列是任务到任务、任务到中断、中断到任务数据交流的一种机制(消息传递)
类似全局变量?假设有一个全局变量a = 0,现有两个任务都在写这个变量a
在这里插入图片描述
全局变量的弊端:数据无保护,导致数据不安全,当多个任务同时对该变量操作时,数据易受损

问题:
当多个任务写入消息给一个“满队列”时,这些任务都会进入阻塞状态,也就是说有多个任务 在等待同一 个队列的空间。那当队列中有空间时,哪个任务会进入就绪态?
答:
1、优先级最高的任务
2、如果大家的优先级相同,那等待时间最久的任务会进入就绪态

队列相关API函数介绍
在这里插入图片描述
在这里插入图片描述

#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

#define START_TASK_PRIO         1
#define START_TASK_STACK_SIZE   128
TaskHandle_t    start_task_handler;
void start_task( void * pvParameters );

#define TASK1_PRIO         2
#define TASK1_STACK_SIZE   128
TaskHandle_t    task1_handler;
void task1( void * pvParameters );

#define TASK2_PRIO         3
#define TASK2_STACK_SIZE   128
TaskHandle_t    task2_handler;
void task2( void * pvParameters );

#define TASK3_PRIO         4
#define TASK3_STACK_SIZE   128
TaskHandle_t    task3_handler;
void task3( void * pvParameters );

QueueHandle_t key_queue;        /* 小数据句柄 */
QueueHandle_t big_date_queue;   /* 大数据句柄 */
char buff[100] = {"我是一个大数组,大大的数组 124214 uhsidhaksjhdklsadhsaklj"};

void freertos_demo(void)
{    
    /* 队列的创建 */
    key_queue = xQueueCreate( 2, sizeof(uint8_t) );
    if(key_queue != NULL)
    {
        printf("key_queue队列创建成功!!\r\n");
    }else printf("key_queue队列创建失败!!\r\n");
    
    big_date_queue = xQueueCreate( 1, sizeof(char *) );
    if(big_date_queue != NULL)
    {
        printf("big_date_queue队列创建成功!!\r\n");
    }else printf("big_date_queue队列创建失败!!\r\n");
    
    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();
}

void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
               
    xTaskCreate((TaskFunction_t         )   task3,
                (char *                 )   "task3",
                (configSTACK_DEPTH_TYPE )   TASK3_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK3_PRIO,
                (TaskHandle_t *         )   &task3_handler );                
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,实现入队 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    char * buf;
    BaseType_t   err = 0;
    buf = &buff[0]; /* buf = &buff[0] */
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES || key == KEY1_PRES)
        {
            err = xQueueSend( key_queue, &key, portMAX_DELAY );
            if(err != pdTRUE)
                printf("key_queue队列发送失败\r\n");
        }else if(key == WKUP_PRES)
        {
            err = xQueueSend( big_date_queue, &buf, portMAX_DELAY );
            if(err != pdTRUE)
                printf("key_queue队列发送失败\r\n");
        }
        vTaskDelay(10);
    }
}

/* 任务二,小数据出队 */
void task2( void * pvParameters )
{
    uint8_t key = 0;
    BaseType_t err = 0;
    while(1)
    {
        err = xQueueReceive( key_queue,&key,portMAX_DELAY);
        if(err != pdTRUE)
            printf("key_queue队列读取失败\r\n");
        else 
            printf("key_queue读取队列成功,数据:%d\r\n",key);
    }
}

/* 任务三,大数据出队 */
void task3( void * pvParameters )
{
    char * buf;
    BaseType_t err = 0;
    while(1)
    {
        err = xQueueReceive( big_date_queue,&buf,portMAX_DELAY);
        if(err != pdTRUE)
        {
            printf("big_date_queue队列读取失败\r\n");
        }else 
        {
            printf("数据:%s\r\n",buf);
        }
    }
}

五、信号量

队列与信号量的对比
在这里插入图片描述

5.1、二值信号量

二值信号量通常用于互斥访问或任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,所以二值信号量更适合用于同步!

二值信号量相关API函数
在这里插入图片描述

二值信号量实验

QueueHandle_t semphore_handle;

void freertos_demo(void)
{    
    semphore_handle = xSemaphoreCreateBinary();
    if(semphore_handle != NULL)
    {
        printf("二值信号量创建成功!!!\r\n");
    }
    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();
}

void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                         
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,释放二值信号量 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    BaseType_t err;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            if(semphore_handle != NULL)
            {
                err = xSemaphoreGive(semphore_handle);
                if(err == pdPASS)
                {
                    printf("信号量释放成功!!\r\n");
                }else printf("信号量释放失败!!\r\n");
            }
        }
        vTaskDelay(10);
    }
}

/* 任务二,获取二值信号量 */
void task2( void * pvParameters )
{
    uint32_t i = 0;
    BaseType_t err;
    while(1)
    {
        err = xSemaphoreTake(semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
        if(err == pdTRUE)
        {
            printf("获取信号量成功\r\n");
        }else printf("已超时%d\r\n",++i);
    }
}

5.2、计数型信号量

计数型信号量相关API函数
在这里插入图片描述

QueueHandle_t count_semphore_handle;

void freertos_demo(void)
{    
    count_semphore_handle = xSemaphoreCreateCounting(100 , 0);  /* 创建计数型信号量 */
    if(count_semphore_handle != NULL)
    {
        printf("计数型信号量创建成功!!!\r\n");
    }
    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();
}


void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                             
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,释放计数型信号量 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            if(count_semphore_handle != NULL)
            {
                xSemaphoreGive(count_semphore_handle);      /* 释放信号量 */
            }
        }
        vTaskDelay(10);
    }
}

/* 任务二,获取计数型信号量 */
void task2( void * pvParameters )
{
    BaseType_t err = 0;
    while(1)
    {
        err = xSemaphoreTake(count_semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
        if(err == pdTRUE)
        {
            printf("信号量的计数值为:%d\r\n",(int)uxSemaphoreGetCount(count_semphore_handle));
        }
        vTaskDelay(1000);
    }
}

优先级反转问题
在这里插入图片描述
高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上看,就像是中优先级的任务比高优先级任务具有更高的优先权(即优先级翻转)

优先级反转示例代码

/* 任务一,低优先级任务 */
void low_task( void * pvParameters )
{
    while(1) 
    {
        printf("low_task获取信号量\r\n");
        xSemaphoreTake(semphore_handle,portMAX_DELAY);
        printf("low_task正在运行!!!\r\n");
        delay_ms(3000);
        printf("low_task释放信号量\r\n");
        xSemaphoreGive(semphore_handle); 
        vTaskDelay(1000);
    }
}

/* 任务二,中优先级任务 */
void middle_task( void * pvParameters )
{
    while(1)
    {
        printf("middle_task正在运行!!!\r\n");
        vTaskDelay(1000);
    }
}

/* 任务三,高优先级任务 */
void high_task( void * pvParameters )
{
    while(1)
    {
        printf("high_task获取信号量\r\n");
        xSemaphoreTake(semphore_handle,portMAX_DELAY);
        printf("high_task正在运行!!!\r\n");
        delay_ms(1000);
        printf("high_task释放信号量\r\n");
        xSemaphoreGive(semphore_handle); 
        vTaskDelay(1000);
    }
}

5.3、互斥信号量

互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中!
优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会
被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。

优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响
注意:互斥信号量不能用于中断服务函数中,原因如下:
(1) 互斥信号量有任务优先级继承的机制, 但是中断不是任务,没有任务优先级, 所以互斥信号量只能用与任务中,不能用于中断服务函数。
(2) 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。

/* 任务一,低优先级任务 */
void low_task( void * pvParameters )
{
    while(1) 
    {
        printf("low_task获取信号量\r\n");
        xSemaphoreTake(mutex_semphore_handle,portMAX_DELAY);
        printf("low_task正在运行!!!\r\n");
        delay_ms(3000);
        printf("low_task释放信号量\r\n");
        xSemaphoreGive(mutex_semphore_handle); 
        vTaskDelay(1000);
    }
}

/* 任务二,中优先级任务 */
void middle_task( void * pvParameters )
{
    while(1)
    {
        printf("middle_task正在运行!!!\r\n");
        vTaskDelay(1000);
    }
}

/* 任务三,高优先级任务 */
void high_task( void * pvParameters )
{
    while(1)
    {
        printf("high_task获取信号量\r\n");
        xSemaphoreTake(mutex_semphore_handle,portMAX_DELAY);
        printf("high_task正在运行!!!\r\n");
        delay_ms(1000);
        printf("high_task释放信号量\r\n");
        xSemaphoreGive(mutex_semphore_handle); 
        vTaskDelay(1000);
    }
}

六、队列集

一个队列只允许任务间传递的消息为同一种数据类型,如果需要在任务间传递不同数据类型的消息时,那么就可以使用队列集 !
作用:用于对多个队列或信号量进行“监听”,其中不管哪一个消息到来,都可让任务退出阻塞状态。

队列集相关API函数介绍
在这里插入图片描述

void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();                               /* 进入临界区 */
    queueset_handle = xQueueCreateSet( 2 );             /* 创建队列集,可以存放2个队列 */
    if(queueset_handle != NULL)
    {
        printf("队列集创建成功!!\r\n");
    }
    
    queue_handle = xQueueCreate( 1, sizeof(uint8_t) );  /* 创建队列 */ 
    semphr_handle = xSemaphoreCreateBinary();           /* 创建二值信号量 */
    
    xQueueAddToSet( queue_handle,queueset_handle);
    xQueueAddToSet( semphr_handle,queueset_handle);
    
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                             
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,实现队列发送以及信号量释放 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    BaseType_t err = 0;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            err = xQueueSend( queue_handle, &key, portMAX_DELAY );
            if(err == pdPASS)
            {
                printf("往队列queue_handle写入数据成功!!\r\n");
            }
        }else if(key == KEY1_PRES)
        {
            err = xSemaphoreGive(semphr_handle);
            if(err == pdPASS)
            {
                printf("释放信号量成功!!\r\n");
            }
        }
        vTaskDelay(10);
    }
}

/* 任务二,获取队列集的消息 */
void task2( void * pvParameters )
{
    QueueSetMemberHandle_t member_handle;
    uint8_t key;
    while(1)
    {
        member_handle = xQueueSelectFromSet( queueset_handle,portMAX_DELAY);
        if(member_handle == queue_handle)
        {
            xQueueReceive( member_handle,&key,portMAX_DELAY);
            printf("获取到的队列数据为:%d\r\n",key);
        }else if(member_handle == semphr_handle)
        {
            xSemaphoreTake( member_handle, portMAX_DELAY );
            printf("获取信号量成功!!\r\n");
        }
    }
}

队列集使用流程
在这里插入图片描述

七、事件标志组

它的每一个位表示一个事件(高8位不算)
每一位事件的含义,由用户自己决定,如:bit0表示按键是否按下,bit1表示是否接受到消息 …
这些位的值为1:表示事件发生了;值为0:表示事件未发生
任意任务或中断都可以读写这些位
可以等待某一位成立,或者等待多位同时成立

虽然使用了 32 位无符号的数据类型变量来存储事件标志, 但其中的高8位用作存储事件标志组的控制信息,低24位用作存储事件标志 ,所以说一个事件组最多可以存储 24 个事件标志!
在这里插入图片描述
事件标志组与队列、信号量的区别?
在这里插入图片描述
事件标志组相关API函数介绍
在这里插入图片描述
事件标志组使用代码:

void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    eventgroup_handle = xEventGroupCreate();
    if(eventgroup_handle != NULL)
    {
        printf("事件标志组创建成功!!\r\n");
    }
    
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                             
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,释放二值信号量 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            xEventGroupSetBits( eventgroup_handle, EVENTBIT_0); /* 将事件标志组的bit0位置1 */
        }else if(key == KEY1_PRES)
        {
            xEventGroupSetBits( eventgroup_handle, EVENTBIT_1); /* 将事件标志组的bit1位置1 */
        }
        vTaskDelay(10);
    }
}

/* 任务二,获取二值信号量 */
void task2( void * pvParameters )
{
    EventBits_t event_bit = 0;
    while(1)
    {
        event_bit = xEventGroupWaitBits( eventgroup_handle,         /* 事件标志组句柄 */
                                         EVENTBIT_0 | EVENTBIT_1,   /* 等待事件标志组的bit0和bit1位 */
                                         pdTRUE,                    /* 成功等待到事件标志位后,清除事件标志组中的bit0和bit1*/
                                         pdTRUE,                    /* 等待事件标志组的bit0和bit1位都置1,就成立 */
                                         portMAX_DELAY );           /* 死等 */
        printf("等待到的事件标志位值为:%#x\r\n",event_bit);
    }
}

八、任务通知

在这里插入图片描述
只要合理,灵活的利用任务通知的特点,可以在一些场合中替代队列、信号量、事件标志组!
在这里插入图片描述
在这里插入图片描述

8.1、任务通知模拟信号量实验

FreeRTOS任务通知模拟计数型信号量实验

void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                             
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,发送任务通知值 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            printf("任务通知模拟计数型信号量释放!\r\n");
            xTaskNotifyGive(task2_handler);
        }
        vTaskDelay(10);
    }
}

/* 任务二,接收任务通知值 */
void task2( void * pvParameters )
{
    uint32_t rev = 0;
    while(1)
    {
        rev = ulTaskNotifyTake(pdFALSE , portMAX_DELAY);
        if(rev != 0)
        {
            printf("rev:%d\r\n",rev);
        }
        vTaskDelay(1000);
    }
}

FreeRTOS任务通知模拟二值信号量实验

/* 任务一,发送任务通知值 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            printf("任务通知模拟二值信号量释放!\r\n");
            xTaskNotifyGive(task2_handler);
        }
        vTaskDelay(10);
    }
}

/* 任务二,接收任务通知值 */
void task2( void * pvParameters )
{
    uint32_t rev = 0;
    while(1)
    {
        rev = ulTaskNotifyTake(pdTRUE , portMAX_DELAY);
        if(rev != 0)
        {
            printf("接收任务通知成功,模拟获取二值信号量!\r\n");
        }
    }
}

FreeRTOS任务通知模拟消息邮箱实验

/* 任务一,发送任务通知值 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    
    while(1) 
    {
        key = key_scan(0);
        if((key != 0) && (task2_handler != NULL))
        {
            printf("任务通知模拟消息邮箱发送,发送的键值为:%d\r\n",key);
            xTaskNotify( task2_handler, key, eSetValueWithOverwrite );
        }
        vTaskDelay(10);
    }
}

/* 任务二,接收任务通知值 */
void task2( void * pvParameters )
{
    uint32_t noyify_val = 0;
    while(1)
    {
        xTaskNotifyWait( 0, 0xFFFFFFFF, &noyify_val, portMAX_DELAY );
        switch(noyify_val)
        {
            case KEY0_PRES:
            {
                printf("接收到的通知值为:%d\r\n",noyify_val);
                LED0_TOGGLE();
                break;
            }
            case KEY1_PRES:
            {
                printf("接收到的通知值为:%d\r\n",noyify_val);
                LED1_TOGGLE();
                break;
            }
            default : break;
        }
    }
}

FreeRTOS任务通知模拟事件标志组实验

/* 任务一,发送任务通知值*/
void task1( void * pvParameters )
{
    uint8_t key = 0;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            printf("将bit0位置1\r\n");
            xTaskNotify( task2_handler, EVENTBIT_0, eSetBits );
        }else if(key == KEY1_PRES)
        {
            printf("将bit1位置1\r\n");
            xTaskNotify( task2_handler, EVENTBIT_1, eSetBits );
        }
        vTaskDelay(10);
    }
}

/* 任务二,接收任务通知值 */
void task2( void * pvParameters )
{
    uint32_t notify_val = 0,event_bit = 0;
    while(1)
    {
        xTaskNotifyWait( 0, 0xFFFFFFFF, &notify_val, portMAX_DELAY );
        if(notify_val & EVENTBIT_0)
        {
            event_bit |= EVENTBIT_0;
        }
        if(notify_val & EVENTBIT_1)
        {
            event_bit |= EVENTBIT_1;
        }
        if(event_bit == (EVENTBIT_0|EVENTBIT_1))
        {
            printf("任务通知模拟事件标志组接收成功!!\r\n");
            event_bit = 0;
        }
    }
}

九、软件定时器

软件定时器的相关配置
在这里插入图片描述
在这里插入图片描述
FreeRTOS软件定时器实验

TimerHandle_t timer1_handle = 0;    /* 单次定时器 */
TimerHandle_t timer2_handle = 0;    /* 周期定时器 */

void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    
    /* 单次定时器 */
    timer1_handle = xTimerCreate( "timer1", 
                                    500,
                                    pdFALSE,
                                    (void *)1,
                                    timer1_callback );
                            
    /* 周期定时器 */
    timer2_handle = xTimerCreate( "timer2", 
                                    2000,
                                    pdTRUE,
                                    (void *)2,
                                    timer2_callback );
                                    
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                                             
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,按键扫描并控制软件定时器 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            xTimerStart(timer1_handle,portMAX_DELAY);
            xTimerStart(timer2_handle,portMAX_DELAY);
        }else if(key == KEY1_PRES)
        {
            xTimerStop(timer1_handle,portMAX_DELAY);
            xTimerStop(timer2_handle,portMAX_DELAY);
        }
        vTaskDelay(10);
    }
}

/* timer1的超时回调函数 */
void timer1_callback( TimerHandle_t pxTimer )
{
    static uint32_t timer = 0;
    printf("timer1的运行次数:%d\r\n",++timer);
}

/* timer2的超时回调函数 */
void timer2_callback( TimerHandle_t pxTimer )
{
    static uint32_t timer = 0;
    printf("timer2的运行次数:%d\r\n",++timer);
}


十、低功耗模式

在这里插入图片描述
FreeRTOS低功耗Tickless模式实验

QueueHandle_t semphore_handle;
/* 进入低功耗前所需要执行的操作 */
void PRE_SLEEP_PROCESSING(void)
{
    __HAL_RCC_GPIOA_CLK_DISABLE();
    __HAL_RCC_GPIOB_CLK_DISABLE();
    __HAL_RCC_GPIOC_CLK_DISABLE();
    __HAL_RCC_GPIOD_CLK_DISABLE();
    __HAL_RCC_GPIOE_CLK_DISABLE();
    __HAL_RCC_GPIOF_CLK_DISABLE();
    __HAL_RCC_GPIOG_CLK_DISABLE();
}
/* 退出低功耗后所需要执行的操作 */
void POST_SLEEP_PROCESSING(void)
{
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOF_CLK_ENABLE();
    __HAL_RCC_GPIOG_CLK_ENABLE();
}
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    semphore_handle = xSemaphoreCreateBinary();
    if(semphore_handle != NULL)
    {
        printf("二值信号量创建成功!!!\r\n");
    }
    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();
}


void start_task( void * pvParameters )
{
    taskENTER_CRITICAL();               /* 进入临界区 */
    xTaskCreate((TaskFunction_t         )   task1,
                (char *                 )   "task1",
                (configSTACK_DEPTH_TYPE )   TASK1_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK1_PRIO,
                (TaskHandle_t *         )   &task1_handler );
                
    xTaskCreate((TaskFunction_t         )   task2,
                (char *                 )   "task2",
                (configSTACK_DEPTH_TYPE )   TASK2_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   TASK2_PRIO,
                (TaskHandle_t *         )   &task2_handler );
                             
    vTaskDelete(NULL);
    taskEXIT_CRITICAL();                /* 退出临界区 */
}

/* 任务一,释放二值信号量 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    BaseType_t err;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            if(semphore_handle != NULL)
            {
                err = xSemaphoreGive(semphore_handle);
                if(err == pdPASS)
                {
                    printf("信号量释放成功!!\r\n");
                }else printf("信号量释放失败!!\r\n");
            }
            
        }
        vTaskDelay(10);
    }
}

/* 任务二,获取二值信号量 */
void task2( void * pvParameters )
{
    uint32_t i = 0;
    BaseType_t err;
    while(1)
    {
        err = xSemaphoreTake(semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
        if(err == pdTRUE)
        {
            printf("获取信号量成功\r\n");
        }else printf("已超时%d\r\n",++i);
        
    }
}

十一、内存管理

/* 任务一,申请内存以及释放内存,并显示空闲内存大小 */
void task1( void * pvParameters )
{
    uint8_t key = 0, t = 0;
    uint8_t * buf = NULL;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            buf = pvPortMalloc(30);                 /* 申请内存 */
            if(buf != NULL)
            {
                printf("申请内存成功!\r\n");
            }else printf("申请内存失败\r\n");
        }else if(key == KEY1_PRES)
        {
            if(buf != NULL)
            {
                vPortFree(buf);                     /* 释放内存 */
                printf("释放内存!!\r\n");
            }                
        }
        if(t++ > 50)
        {
            t = 0;
            printf("剩余的空闲内存大小为:%d\r\n",xPortGetFreeHeapSize());
        }
        vTaskDelay(10);
    }
}

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

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

相关文章

Java算法(六):模拟评委打分案例 方法封装抽离实现 程序的节流处理

Java算法&#xff08;六&#xff09; 评委打分 需求&#xff1a; 在编程竞赛中&#xff0c;有 6 个评委为参赛选手打分&#xff0c;分数为 0 - 100 的整数分。 选手的最后得分为&#xff1a;去掉一个最高分和一个最低分后 的 4个评委的平均值。 注意程序的节流 package c…

Spring-循环依赖简述

什么是循环依赖 // A依赖了B class A {public B b; } ​ // B依赖了A class B {public A a; } ​ // 循环依赖 A a new A(); B b new B(); a.b b; b.a a; 对象之间的相互依赖很正常&#xff0c;但是在Spring中由于对象创建要经过Bean的生命周期&#xff0c;所以就有了循环…

【广州华锐互动】气象卫星监测AR互动教学软件为气象学习带来更多乐趣

由VR制作公司广州华锐互动开发的气象卫星监测AR互动教学软件是一款结合了增强现实(AR)技术与气象监测技术的教育软件。它通过直观、互动的方式&#xff0c;帮助学生更好地理解和掌握气象监测的基本知识和技能。本文将从气象卫星监测AR互动教学软件的应用场景、优势分析、实际意…

c#如何把字符串中的指定字符删除

可以使用以下四种方法&#xff1a; 一、使用关键字&#xff1a;Replace public string Replace(char oldChar,char newChar); 在对象中寻找oldChar&#xff0c;如果寻找到&#xff0c;就用newChar将oldChar替换掉。 1、实例代码&#xff1a; 2、执行结果&#xff1a; 二、Rem…

【CesiumJS入门】(11)加载LAS点云数据

前言 最近有两次投递简历以及面试都被问到了是否有三维点云数据处理相关的经验。然而我的岗位都没有和点云相关的工作任务&#xff0c;所以还是得自己加把劲呀。 本篇将从数据获取到加载来简易地介绍一个LAS点云数据的加载。 加载数据 首先&#xff0c;你得有一份LAS格式的…

node插件MongoDB(三)—— 库mongoose 的使用和数据类型

前言 提示&#xff1a;使用mongoose 的前提是你安装了node和 MongoDB。 mongoose 官网文档&#xff1a;http://mongoosejs.net/docs/index.html 文章目录 前言一、安装二、基本使用1. 打开bin目录的mongod.exe文件2. 基本使用的代码&#xff08;连接mongodb 服务&#xff09;3.…

虚拟机复制后,无法ping通问题解决

虚拟机复制后&#xff0c;无法ping通问题解决 可能出现的现象 ssh工具连接不上虚拟机&#xff1b;虚拟机ping不通外网或者ping不通内网其它虚拟机&#xff1b; 原因 原虚拟机和新复制出来的虚拟机的ip地址重复&#xff1b;原虚拟机和新复制出来的虚拟机的MAC地址重复&#…

【wp】2023鹏城杯初赛 Web web1(反序列化漏洞)

考点&#xff1a; 常规的PHP反序列化漏洞双写绕过waf 签到题 源码&#xff1a; <?php show_source(__FILE__); error_reporting(0); class Hacker{private $exp;private $cmd;public function __toString(){call_user_func(system, "cat /flag");} }class A {p…

基于ssm的校园办公室报修管理系统

基于ssm的校园办公室报修管理系统 摘要 基于SSM的校园办公室报修管理系统是一个现代化的、高效的报修平台&#xff0c;它能够帮助校园内的教职工和学生更方便、更快捷地提交和处理报修请求。该系统基于Spring、SpringMVC和MyBatis&#xff08;简称SSM&#xff09;开发&#xff…

计算机组成原理之指令

引言 关于riscv操作数 32个寄存器 | X0~X31|快速定位数据。在riscv中&#xff0c;只对寄存器中的数据执行算术运算 2^61个存储字 | 只能被数据传输指令访问。riscv体系采用的是字节寻址。 一个寄存器是8bytes&#xff0c;64位&#xff08;double word&#xff09; 每次取的…

K8S用户管理体系介绍

1 K8S账户体系介绍 在k8s中&#xff0c;有两类用户&#xff0c;service account和user&#xff0c;我们可以通过创建role或clusterrole&#xff0c;再将账户和role或clusterrole进行绑定来给账号赋予权限&#xff0c;实现权限控制&#xff0c;两类账户的作用如下。 server ac…

webpack的简单使用

什么是webpack&#xff08;去官网看详细的API&#xff09; 本质上&#xff0c;webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时&#xff0c;它会在内部从一个或多个入口点构建一个 依赖图(dependency graph)&#xff0c;然后将你项…

谷歌提出 AGI 完整路线图:目前 ChatGPT 只处于 AGI 的第一阶段

本心、输入输出、结果 文章目录 谷歌提出 AGI 完整路线图:目前 ChatGPT 只处于 AGI 的第一阶段前言谷歌 DeepMind 发布 AGI 分级框架发展 AGI 必须遵循6个基本原则什么是AGI图灵测试详解六大原则AGI 的五大发展过程阶段原文参考弘扬爱国精神谷歌提出 AGI 完整路线图:目前 Cha…

SpringBoot整合定时任务遇到的多实例问题

唠嗑部分 是这样&#xff0c;前几日完善了定时任务的日志记录&#xff0c;今日切换了服务器&#xff0c;多部署了一个节点&#xff0c;使用nginx负载均衡&#xff0c;但是查看日志却发现了如下情况 那糟糕了&#xff0c;传说中的多实例问题出现了&#xff0c;今天我们就来聊聊…

浙大计算机学院2024届推免直博生名单

名单&#xff1a; 分析&#xff1a; 浙大计算机学院共录取推免直博生158人&#xff0c;其中计算机科学与技术专业73人&#xff0c;人工智能专业7人&#xff0c;软件工程专业21人&#xff0c;网络空间安全专业19人&#xff0c;电子信息专业31人&#xff0c;设计专业7人 欢迎关…

图文解析 Nacos 配置中心的实现

目录 一、什么是 Nacos 二、配置中心的架构 三、Nacos 使用示例 &#xff08;一&#xff09;官方代码示例 &#xff08;二&#xff09;Properties 解读 &#xff08;三&#xff09;配置项的层级设计 &#xff08;四&#xff09;获取配置 &#xff08;五&#xff09;注册…

Java快速排序算法、三路快排(Java算法和数据结构总结笔记)[7/20]

一、什么是快速排序算法 快速排序的基本思想是选择一个基准元素&#xff08;通常选择最后一个元素&#xff09;将数组分割为两部分&#xff0c;一部分小于基准元素&#xff0c;一部分大于基准元素。 然后递归地对两部分进行排序&#xff0c;直到整个数组有序。这个过程通过 par…

如何从存档服务器上完全删除PDM用户

当创建新用户时使用“PDM 登录”类型&#xff08;如下图&#xff09;&#xff0c;PDM用户名和密码会存储于存档服务器的注册表中。 存档服务器的注册表位置如下&#xff1a; HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks\Applications\PDMWorks Enterprise\ArchiveServer\ConisioU…

响应式艺术作品展示前端html网站模板源码

响应式艺术作品展示网站模板是一款适合各种艺术作品在线展示的响应式网站模板下载。提示&#xff1a;本模板调用到谷歌字体库&#xff0c;可能会出现页面打开比较缓慢。 转载自 https://www.qnziyw.cn/wysc/qdmb/23778.html

C/C++轻量级并发TCP服务器框架Zinx-游戏服务器开发003:架构搭建-需求分析及TCP通信方式的实现

文章目录 1 项目总体架构2 项目需求2.1 服务器职责2.2 消息的格式和定义 3 基于Tcp连接的通信方式3.1 通道层实现GameChannel类3.1.1 TcpChannel类3.1.2 Tcp工厂类3.1.3 创建主函数&#xff0c;添加Tcp的监听套接字3.1.4 代码测试 3.2 协议层与消息类3.2.1 消息的定义3.2.2 消息…