FreeRtos学习笔记

一 、配置文件介绍

 置1:RTOS使用抢占式调度器    置0:RTOS使用协作式调度器(时间片)

#define configUSE_PREEMPTION			1

  置1:使能时间片调度(默认使能)  当系统为抢占时,相同优先级任务发生时间片流转,0时反之

#define configUSE_TIME_SLICING 1

 1:开启低功耗   (暂时未了解)

#ifndef configUSE_TICKLESS_IDLE 0

 CPU指令执行频率,统称为Fclk

系统时钟,使用的是F407系列,系统频率是168MHZ

1/Fclk就是cpu时钟周期

#define configCPU_CLOCK_HZ				( SystemCoreClock )

 RTOS系统节拍中断频率,即1秒中断次数,这里是1ms,每次中断会进行任务调度

#define configTICK_RATE_HZ				( ( TickType_t ) 1000 )


//可使用的最大优先级
#define configMAX_PRIORITIES					  (32)

//空闲任务使用的堆栈大小
#define configMINIMAL_STACK_SIZE				((unsigned short)128)
  
//任务名字字符串长度
#define configMAX_TASK_NAME_LEN					(16)

 //系统节拍计数器变量数据类型,1表示为16位无符号整形,0表示为32位无符号整形
#define configUSE_16_BIT_TICKS					0                      

//空闲任务放弃CPU使用权给其他同优先级的用户任务
#define configIDLE_SHOULD_YIELD					1           

//启用队列
#define configUSE_QUEUE_SETS					  0    

//开启任务通知功能,默认开启
#define configUSE_TASK_NOTIFICATIONS    1   

//使用互斥信号量
#define configUSE_MUTEXES						    0    

//使用递归互斥信号量                                            
#define configUSE_RECURSIVE_MUTEXES			0   

//为1时使用计数信号量
#define configUSE_COUNTING_SEMAPHORES		0

/* 设置可以注册的信号量和消息队列个数 */
#define configQUEUE_REGISTRY_SIZE				10                                 
                                                                       
#define configUSE_APPLICATION_TASK_TAG		  0                       
                      

/*****************************************************************
              FreeRTOS与内存申请有关配置选项                                               
*****************************************************************/
//支持动态内存申请
#define configSUPPORT_DYNAMIC_ALLOCATION        1    
//支持静态内存
#define configSUPPORT_STATIC_ALLOCATION					0					
//系统所有总的堆大小
#define configTOTAL_HEAP_SIZE					((size_t)(36*1024))    


/***************************************************************
             FreeRTOS与钩子函数有关的配置选项                                            
**************************************************************/
/* 置1:使用空闲钩子(Idle Hook类似于回调函数);置0:忽略空闲钩子
 * 
 * 空闲任务钩子是一个函数,这个函数由用户来实现,
 * FreeRTOS规定了函数的名字和参数:void vApplicationIdleHook(void ),
 * 这个函数在每个空闲任务周期都会被调用
 * 对于已经删除的RTOS任务,空闲任务可以释放分配给它们的堆栈内存。
 * 因此必须保证空闲任务可以被CPU执行
 * 使用空闲钩子函数设置CPU进入省电模式是很常见的
 * 不可以调用会引起空闲任务阻塞的API函数
 */
#define configUSE_IDLE_HOOK						0      

/* 置1:使用时间片钩子(Tick Hook);置0:忽略时间片钩子
 * 
 * 
 * 时间片钩子是一个函数,这个函数由用户来实现,
 * FreeRTOS规定了函数的名字和参数:void vApplicationTickHook(void )
 * 时间片中断可以周期性的调用
 * 函数必须非常短小,不能大量使用堆栈,
 * 不能调用以”FromISR" 或 "FROM_ISR”结尾的API函数
 */
 /*xTaskIncrementTick函数是在xPortSysTickHandler中断函数中被调用的。因此,vApplicationTickHook()函数执行的时间必须很短才行*/
#define configUSE_TICK_HOOK						0           

//使用内存申请失败钩子函数
#define configUSE_MALLOC_FAILED_HOOK			0 

/*
 * 大于0时启用堆栈溢出检测功能,如果使用此功能 
 * 用户必须提供一个栈溢出钩子函数,如果使用的话
 * 此值可以为1或者2,因为有两种栈溢出检测方法 */
#define configCHECK_FOR_STACK_OVERFLOW			0   


/********************************************************************
          FreeRTOS与运行时间和任务状态收集有关的配置选项   
**********************************************************************/
//启用运行时间统计功能
#define configGENERATE_RUN_TIME_STATS	        0             
 //启用可视化跟踪调试
#define configUSE_TRACE_FACILITY				      0    
/* 与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数
 * prvWriteNameToBuffer()
 * vTaskList(),
 * vTaskGetRunTimeStats()
*/
#define configUSE_STATS_FORMATTING_FUNCTIONS	1                       
                                                                        
                                                                        
/********************************************************************
                FreeRTOS与协程有关的配置选项                                                
*********************************************************************/
//启用协程,启用协程以后必须添加文件croutine.c
#define configUSE_CO_ROUTINES 			          0                 
//协程的有效优先级数目
#define configMAX_CO_ROUTINE_PRIORITIES       ( 2 )                   


/***********************************************************************
                FreeRTOS与软件定时器有关的配置选项      
**********************************************************************/
 //启用软件定时器
#define configUSE_TIMERS				            0                              
//软件定时器优先级
#define configTIMER_TASK_PRIORITY		        (configMAX_PRIORITIES-1)        
//软件定时器队列长度
#define configTIMER_QUEUE_LENGTH		        10                               
//软件定时器任务堆栈大小
#define configTIMER_TASK_STACK_DEPTH	      (configMINIMAL_STACK_SIZE*2)    

/************************************************************
            FreeRTOS可选函数配置选项                                                     
************************************************************/
#define INCLUDE_xTaskGetSchedulerState       1                       
#define INCLUDE_vTaskPrioritySet		         1
#define INCLUDE_uxTaskPriorityGet		         1
#define INCLUDE_vTaskDelete				           1
#define INCLUDE_vTaskCleanUpResources	       1
#define INCLUDE_vTaskSuspend			           1
#define INCLUDE_vTaskDelayUntil			         1
#define INCLUDE_vTaskDelay				           1
#define INCLUDE_eTaskGetState			           1
#define INCLUDE_xTimerPendFunctionCall	     0
//#define INCLUDE_xTaskGetCurrentTaskHandle       1
//#define INCLUDE_uxTaskGetStackHighWaterMark     0
//#define INCLUDE_xTaskGetIdleTaskHandle          0


/******************************************************************
            FreeRTOS与中断有关的配置选项                                                 
******************************************************************/
#ifdef __NVIC_PRIO_BITS
	#define configPRIO_BITS       		__NVIC_PRIO_BITS
#else
	#define configPRIO_BITS       		4                  
#endif
//中断最低优先级
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			15     

//系统可管理的最高中断优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY	5 

#define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )	/* 240 */

#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )


/****************************************************************
            FreeRTOS与中断服务函数有关的配置选项                         
****************************************************************/
#define xPortPendSVHandler 	PendSV_Handler
#define vPortSVCHandler 	SVC_Handler


/* 以下为使用Percepio Tracealyzer需要的东西,不需要时将 configUSE_TRACE_FACILITY 定义为 0 */
#if ( configUSE_TRACE_FACILITY == 1 )
#include "trcRecorder.h"
#define INCLUDE_xTaskGetCurrentTaskHandle               1   // 启用一个可选函数(该函数被 Trace源码使用,默认该值为0 表示不用)
#endif


#endif /* FREERTOS_CONFIG_H */

二、任务创建

1 任务的实现过程

①定义任务

单位是字;所以128×44=512字节的栈空间大小

#define TASK1_STACK_SIZE 128
StackType_t Task1Stack[TASK1_STACK_SIZE ];
#define TASK2_STACK_SIZE 128
StackType_t Task2Stack[TASK2_STACK_SIZE ];

②定义任务函数

//LED0任务函数 
void led0_task(void *pvParameters)
{
    while(1)
    {
        LED0=~LED0;
        vTaskDelay(500);
    }
}   

//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

③定义任务控制块

typedef struct tskTaskControlBlock
{
	volatile StackType_t	*pxTopOfStack;	/*栈顶*/
	

	ListItem_t			xStateListItem;	    /*任务节点*/
	ListItem_t			xEventListItem;	
	UBaseType_t			uxPriority;			
	StackType_t			*pxStack;		/*任务栈起始地址*/

/*任务名称,字符串形式*/
	char				pcTaskName[ configMAX_TASK_NAME_LEN ];

} tskTCB;

typedef tskTCB TCB_t;

④列表和列表项

有列表、列表项、迷你列表项结构体

列表初始化函数
void vListInitialise( List_t * const pxList )
{
	pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );			

	pxList->xListEnd.xItemValue = portMAX_DELAY;

	pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );	
	pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );

	pxList->uxNumberOfItems = ( UBaseType_t ) 0U;

	listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
	listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
列表项初始化
void vListInitialiseItem( ListItem_t * const pxItem )
{
	/* Make sure the list item is not recorded as being on a list. */
	pxItem->pvContainer = NULL;

	/* 初始化用于完整性检查的变量,如果开启了这个功能的话 */
	listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
	listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
列表项插入函数
/*pxList:列表项要插入的列表
  pxNewListItem 要插入的列表项*/
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
列表项尾插法函数
/*pxList:列表项要插入的列表
  pxNewListItem 要插入的列表项*/

void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
列表项删除函数

返回值:列表项剩余数目

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
 列表的遍历
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )			

2 任务创建函数

静态创建任务函数xTaskCreateStatic( )

静态创建任务函数,必须将宏定义打开(比较少用)

#if( configSUPPORT_STATIC_ALLOCATION == 1 )

TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode,
									const char * const pcName,
									const uint32_t ulStackDepth,
									void * const pvParameters,
									UBaseType_t uxPriority,
									StackType_t * const puxStackBuffer,
									StaticTask_t * const pxTaskBuffer ) 
	{
实现调度器void vTaskStartScheduler( void )
动态创建任务函数xTaskCreate( )
 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);   //任务句柄   

3 任务管理

任务状态通常分为以下四种:就绪、运行、阻塞、挂起态

4 常用任务API函数

①任务挂起函数

作用:挂起单个任务

参数:任务句柄,参数为NULL的话表示挂起任务本身

获取参数方法 :

        如果是静态创建任务,xTaskCreateStatic()的返回值就是任务句柄

        也可以通过xTaskGetHandle()函数来获取任务句柄

void vTaskSuspend(TaskHandle_t xTaskToSuspend)

 作用:挂起所有任务,必须调用xTaskResumeALL()恢复所有任务,是配套的。

void vTaskSuspendAll( void ) 
{
    ++uxSchedulerSuspended;
}

②任务恢复函数

作用:恢复单个任务

参数:任务句柄

注意:不能在中断里使用该函数

void vTaskResume(TaskHandle_t xTaskToResume)

也是恢复单个任务,凡是结尾带FromISR的API都可在中断运行

返回值:

pdTRUE:恢复任务等于或高于正在执行的任务(被中断打断),中断结束时,需要进行上下文切换pdFALSE:恢复任务低于正在执行的任务(被中断打断),中断结束时,不需要进行上下文切换

BaseType_t vTaskResumeFromISR(TaskHandle_t xTaskToResume)

③任务删除函数

作用:删除单个任务

参数:任务句柄,参数为NULL的话表示删除任务本身

void vTaskDelete( TaskHandle_t xTaskToDelete)

④任务延时函数

//调用该函数阻塞运行,适用于对时间精度要求不是很高
void vTaskDelay( const TickType_t xTicksToDelay );


/*
     适用于比较精准的延时
*/
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )

5 任务设计要点

需要考虑 中断服务函数、普通任务、空闲任务、任务执行时间

在中断函数中不能出现任何阻塞函数!!!

三、消息队列

1 消息队列简介

 队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任务间传递信息,实现了任务接收来自其他任务或中断的不固定长度的消息。
 FreeRTOS 中使用队列数据结构实现任务异步通信工作,具有如下特性:

●消息支持先进先出方式排队,支持异步读写工作方式。
●读写队列均支持超时机制。
●消息支持后进先出方式排队,往队首发送消息( LIFO )。
●可以允许不同长度(不超过队列节点最大值)的任意类型消息。
●一个任务能够从任意一个消息队列接收和发送消息。

多个任务能够从同一个消息队列接收和发送消息。

通常队列采用FIFO的存储缓冲机制,也可以使用LIFO的存储缓冲,也就是后进先出

出队阻塞(可设置不阻塞,一直阻塞、阻塞时间)
当任务尝试从一个队列中读取消息的时候可以指定一个阻塞时间,这个阻塞时间就是当任务从队列中读取消息无效的时候任务阻塞的时间。
入队阻塞(队列满了会阻塞)
入队说的是向队列中发送消息,将消息加入到队列中。和出队阻塞一样,当一个任务向队列发送消息的话也可以设置阻塞时间。

2 消息队列控制块

typedef struct QueueDefinition
{
    ....
}xQUEUE;
typedef xQUEUE Queue_t;

3 消息队列API函数

①创建函数

作用:用于创建一个新的消息队列

参数:

        uxQueueLength:    队列长度

         uxItemSize: 消息单元大小,以字节为单位

返回值:

        成功 : 返回一个队列句柄

        失败 : 返回NULL,原因可能是RAM无法分配成功

QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength,
                            UBaseType_t uxItemSize);

作用:用于创建一个新的消息队列(静态创建)

参数:

        uxQueueLength:    队列长度

         uxItemSize: 消息单元大小,以字节为单位

 QueueHandle_t xQueueCreateStatic(
							  UBaseType_t uxQueueLength,
							  UBaseType_t uxItemSize,
							  uint8_t *pucQueueStorageBuffer,
							  StaticQueue_t *pxQueueBuffer
						  );

②删除函数

作用:用于删除一个新的消息队列

参数:消息队列句柄

void vQueueDelete(QueueHandle_t xQueue);

③发送消息函数

作用:用于向队尾发送一个队列消息

参数:xQueue:消息队列句柄、

         pvItemToQueue:指针(消息指针)

        xTicksToWait :队列满时等待的时间,为0表示不阻塞,portMAX_DELAY为阻塞

返回值:

        pdTRUE:成功

        errQUEUE_FULL:失败

 BaseType_t xQueueSend(
							  QueueHandle_t xQueue,
							  const void * pvItemToQueue,
							  TickType_t xTicksToWait
						 );


xQueueSendToBack();

作用:在中断服务函数中用于向队尾发送一个队列消息

参数:xQueue:消息队列句柄、

         pvItemToQueue:指针(消息指针)

        pxHigherPriorityTaskWoken:当入队导致一个任务解锁、并且优先级高于当前被中断的任务,则将pxHigherPriorityTaskWoken设置为pdTRUE,在中断退出前进行上下文切换,去执行被唤醒的优先级更高的任务。从V7.30起,这个作为可选参数可以设置为NULL

返回值:

        pdTRUE:成功

        errQUEUE_FULL:失败

 BaseType_t xQueueSendFromISR(
									 QueueHandle_t xQueue,
									 const void *pvItemToQueue,
									 BaseType_t *pxHigherPriorityTaskWoken
								);

作用:用于向队首发送一个队列消息

参数:xQueue:消息队列句柄、

         pvItemToQueue:指针(消息指针)

        xTicksToWait :队列满时等待的时间,为0表示不阻塞,portMAX_DELAY为阻塞

返回值:

        pdTRUE:成功

        errQUEUE_FULL:失败

 BaseType_t xQueueSendToToFront(
								   QueueHandle_t	xQueue,
								   const void		*pvItemToQueue,
								   TickType_t		xTicksToWait
							   );

作用:用于向队首发送一个队列消息

参数:xQueue:消息队列句柄、

         pvItemToQueue:指针(消息指针)

        xTicksToWait :队列满时等待的时间,为0表示不阻塞,1为阻塞

        xCopyPosition:确定队尾还是队首插入

返回值:

        pdTRUE:成功

        errQUEUE_FULL:失败

 BaseType_t xQueueGenericSend(
									QueueHandle_t xQueue,
									const void * pvItemToQueue,
									TickType_t xTicksToWait
									BaseType_t xCopyPosition
								);

④读取消息函数

作用:用于接收队列消息,并把接收到的消息从队列中删除

参数:xQueue:消息队列句柄、

         pvBuffer:指针(要保存的数据指针)

        xTicksToWait :队列满时等待的时间,为0表示不阻塞,portMAX_DELAY为阻塞

        xCopyPosition:确定队尾还是队首插入

返回值:

        成功:pdTRUE

        失败:pdFALSE

 BaseType_t xQueueReceive(
								 QueueHandle_t xQueue,
								 void *pvBuffer,
								 TickType_t xTicksToWait
							);</pre>

作用:用于接收队列消息,不会删除队列消息

参数:xQueue:消息队列句柄、

         pvBuffer:指针(要保存的数据指针)

        xTicksToWait :队列满时等待的时间,为0表示不阻塞,1为阻塞

        xCopyPosition:确定队尾还是队首插入

返回值:

        成功:pdTRUE

        失败:pdFALSE

 BaseType_t xQueuePeek(
							 QueueHandle_t xQueue,
							 void *pvBuffer,
							 TickType_t xTicksToWait
						 );</pre>

4 使用消息队列的注意事项

1.使用 xQueueSend ()、 xQueueSendFromISR ()、 xQueueReceive ()等这些函数之前应先创建需消息队列,并根据队列句柄进行操作。
2.队列读取采用的是先进先出( FIFO )模式,会先读取先存储在队列中的数据。当然也 FreeRTOS 也支持后进先出( LIFO )模式,那么读取的时候就会读取到后进队列的数据。
3.在获取队列中的消息时候,我们必须要定义一个存储读取数据的地方,并且该数据区域大小不小于消息大小,否则,很可能引发地址非法的错误。
4.无论是发送或者是接收消息都是以拷贝的方式进行,如果消息过于庞大,可以将消息的地址作为消息进行发送、接收。
5.队列是具有自己独立权限的内核对象,并不属于任何任务。所有任务都可以向同一队列写入和读出。一个队列由多任务或中断写入是经常的事,但由多个任务读出倒是用的比较少。、

四、信号量

1 信号量简介

        信号量( Semaphore )是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问,常用于协助一组相互竞争的任务来访问临界资源。在多任务系统中,各任务之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。

①二值信号量

二值信号量既可以用于临界资源访问也可以用于同步功能。

②计数信号量

二进制信号量可以被认为是长度为1的队列,而计数信号量则可以被认为长度大于1的队列,信号量使用者依然不必关心存储在队列中的消息,只需关心队列是否有消息即可。

③互斥信号量

互斥信号量其实是特殊的二值信号量,由于其特有的优先级继承机制从而,使它更适用于简单互锁,也就是保护临界资源。

④递归信号量(比较少用)

2 信号量API函数

①创建函数

1 创建二值信号量 

//这个宏定义要设置为1
configSUPPORT_DYNAMIC_ALLOCATION == 1

xSemaphoreCreateBinary();

2 创建技术信号量

xSemaphoreCreateCounting()

②删除函数

vSemaphoreDelete();

③释放信号量

xSemaphoreGive();

带中断保护,在中断释放 

xSemaphoreGiveFromISR();

④获取信号量

xSemaphoreTake()

xSemaphoreTakeFromISR()

五、互斥量

1 互斥量简介

        互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源。

1.1互斥量的优先级继承机制

 我的理解:就是将低优先级的任务暂时设置成高优先级的任务,这么做是为了避免被其他任务打断,增加释放信号的时间。

1.2互斥量的使用场景

互斥量:可能会引起优先级翻转的情况

递归互斥量:任务可能多次获取互斥量的情况下,避免同一任务多次递归持有造成死锁。

2 互斥量常用API函数

①创建函数

xQueueCreateMutex( queueQUEUE_TYPE_MUTEX);

②递归互斥量创建函数

xSemaphoreCreateRecursiveMutex();

③删除函数

vSemaphoreDelete();

④获取互斥量函数

xSemaphoreTake();

⑤递归互斥量获取函数

xSemaphoreTakeRecursive();

⑥互斥量释放函数 & 递归互斥量释放函数

xSemaphoreGive();
xSemaphoreGiveRecursive();

六、事件

1 事件简介

事件是一种实现任务间通信的机制,主要用于实现多任务间的同步,但事件通信只能是事件类型的通信,无数据传输。与信号量不同的是,它可以实现一对多,多对多的同步。
 configUSE _16_ BIT _ TICKS 定义为0,那么 uxEventBits 是32位的,有24个位用来实现事件标志组。
一对多同步模型:一个任务等待多个事件的触发,这种情况是比较常见的;多对多同步模型:多个任务等待多个事件的触发。 

2 事件API函数

①创建函数

xEventGroupCreate()

②事件删除函数

vEventGroupDelete()

③事件组置位函数

/*置位事件组中指定的位*/
xEventGroupSetBits();
xEventGroupSetBitsFromISR();

④等待事件函数

xEventGroupWaitBits();

⑤清除Bit标志位

/*如果在阻塞函数那没有指定读取后删除标志位,那么就手动使用这个函数进行删除*/
xEventGroupClearBits();
xEventGroupClearBitsFromISR();

七、软件定时器

1 软件定时器简介

定时时间到触发回调函数

可裁剪,通过宏定义关闭

定时器任务默认是最大优先级

2 常用API函数

①创建函数

xTimerCreate()

②启动函数

xTimerStart();
xTimerStartFromISR();

③停止函数

xTimerStop()
xTimerStopFromISR()

④删除函数

xTimerDelete()

八、任务通知

1 任务通知简介

使用前将 configUSE_TASK_NOTIFICATIONS 设置为         1

FreeRTOS 提供以下几种方式发送通知给任务 :
        1 发送通知给任务,如果有通知未读,不覆盖通知值。
        2 发送通知给任务,直接覆盖通知值。
        3 发送通知给任务,设置通知值的一个或者多个位,可以当做事件组来使用发送通知给任务,递增通知值,可以当做计数信号量使用。


当然,凡是都有利弊,不然的话 FreeRTOS 还要内核的 IPC 通信机制干嘛消息通知虽然处理更快,RAM 开销更小,但也有以下限制 :
        1 只能有一个任务接收通知消息,因为必须指定接收通知的任务。
        2 只有等待通知的任务可以被阻塞,发送通知的任务,在任何情况下都不会因为发送失败而进入阻塞态。

2 常用API函数

①发送任务通知函数

xTaskGenericNotify()   xTaskGenericNotifyFromISR()

 用于任务中向指定任务发送任务通知,并更新对方的任务通知值(加1操作)

xTaskNotifyGive()    xTaskNotifyGiveFromISR()

 向指定任务发送一个任务通知,带有通知值并且用户可以指定通知值的发送方式

xTaskNotify()        xTaskNotifyFromISR()

 在中断向指定任务发送一个任务通知,并返回对象任务上一个通知值

xTaskNotifyAndQueryFromISR()

②获取任务通知

用于获取任务通知、获取二值信号量、计数信号量类型的任务通知

ulTaskNotifyTake()

 用于等待一个任务通知,并带有超时等待

ulTaskNotifyWait()

3 代替消息队列

向指定任务发送,eSetValueWithOverwrite 会覆盖当前通知

发送任务函数
//Send任务函数
void Send_task(void *pvParameters)
{
	BaseType_t xReturn = pdPASS;
	u8 key=0;
	uint32_t send1 = 1;
	uint32_t send2 = 2;
	
	while(1)
	{
		key=KEY_Scan(0);
		if(key==KEY_UP_PRESS)
		{
			xReturn = xTaskNotify(Receive1Task_Handler, /*任务句柄*/

						    send1, /* 发送的数据,最大为4字节 */

								eSetValueWithOverwrite );/*覆盖当前通知*/
			if( xReturn == pdPASS )
				printf("Receive1_Task_Handle 任务通知消息发送成功!\r\n");
		}
		else if(key==KEY1_PRESS)
		{
			xReturn = xTaskNotify(Receive2Task_Handler, /*任务句柄*/

								send2, /* 发送的数据,最大为4字节 */

								eSetValueWithOverwrite );/*覆盖当前通知*/
			if( xReturn == pdPASS )
				printf("Receive2_Task_Handle 任务通知消息发送成功!\r\n");
		}
		vTaskDelay(20);
	}
}

接收函数

//Receive1任务函数
void Receive1_task(void *pvParameters)
{
    BaseType_t xReturn = pdTRUE;
  uint32_t r_num;

	
	while(1)
    {
		//获取任务通知 ,没获取到则一直等待
		xReturn=xTaskNotifyWait(0x0,			//进入函数的时候不清除任务bit
								ULONG_MAX,	  //退出函数的时候清除所有的bit
								&r_num,		  //保存任务通知值
                       
								portMAX_DELAY);	//阻塞时间
		if( pdTRUE == xReturn )
			printf("Receive1_Task 任务通知消息为 %d \n",r_num);                      

		
	}
}

//Receive2任务函数
void Receive2_task(void *pvParameters)
{
	BaseType_t xReturn = pdTRUE;
  uint32_t r_num;

	while(1)
    {
		//获取任务通知 ,没获取到则一直等待
		xReturn=xTaskNotifyWait(0x0,			//进入函数的时候不清除任务bit
								ULONG_MAX,	  //退出函数的时候清除所有的bit

								&r_num,		  //保存任务通知值
                  
								portMAX_DELAY);	//阻塞时间
		if( pdTRUE == xReturn )
                  

			printf("Receive2_Task 任务通知消息为 %d \n",r_num);                      

		
	}
}

3 代替二值信号量

4 代替计数型信号量

5 代替事件组

九、内存管理

FreeRTOS 中内存堆为ucHeap[],大小为configTOTAL_HEAP_SIZE

标准 C 库中的也可以实现动态内存管理,但是如下和 free0malloc原因限制了其使用:
1 小型式系统中并不总是可用的,这些函数在小型嵌入小型嵌入式设备中的RAM 不足。
2占据了相当大的一块代码空间。它们的实现可能非常的大,
3 他们几乎都不是安全的
4 每次调用这些函数执行的时间可能都不一样。它们并不是确定的,
5 它们有可能产生碎片
6 这两个函数会使得链妾器配置得复杂

FreeRTOS为我们提供了5种内存管理算法,分别是head_1.c~head_5.c

1 malloc()代替

prPortMalloc()

2 free()代替

vPortFree()

3常用API函数

void *pvPortMalloc( size t xSize ); //内存申请函数
void vPortEree( void *pv ); //内存释放函数
void vPortInitialiseBlocks( void ); //初始化内存堆函数
size t xPortGetEreeHeapsize( void ); //获取当前未分配的内存堆大小
size_t xPortGetMinimumEverEreeHeapSize( void ); //获取未分配的内存堆历史最小值

十、中断管理

1 临界段保护

        就是一段执行的程序不能被中断打断的代码段

taskENTER_CRITICAL();           //进入临界区

 taskEXIT_CRITICAL();            //退出临界区

2 中断管理简介

        用户可以自定义配置系统可管里的最高中断优先级的宏定义configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY它是用于配置内核中的置为某个值的时候,NVIC 不会响应比该basepri 寄存器的,当 basepri优先级低的中断,而优无级比之更高的中断则不受影响。就是说当这个宏定值在 0、1、2、3、4 的这些中断是不受义配置为 5 的时候,中断优先级FreeRTOS 屏蔽的,也就是说即使车系统进入临界段的时候,这些中断也能被才被触发,当然,这些中断服务函数中也触发而不是等到退出临界段的时候不能调用 FreeRTOS 提供的 API的数接口,而中断优先级在 5 到 15 的这些中断是可以被屏蔽的,也能安全调用 FreeRTOS 提供的 API 函数接口。

十一、CPU录用率

        用户想要使用 CPU 利用率统计的话,需要自定义配置一下,首先在FreeRTOSConfig.h 配置与系统运行时间和任务状态收集有关的配置选项,并且实现portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 与DortGET_RUN_TIME_COUNTER_VALUE()这两个宏定义

API函数

vTaskGetRunTimeStats()

vTaskList()

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值