0~最大优先级数-1,
优先级0最小,优先级随数字依次递增
多个任务可以使用同一个优先级(configUSE_TIME_SLICING 为 1 时)
存储任务的属性,FREERTOS把这些属性结合到一起用一个结构体表示,这个结构体就叫:TCB_t,会存储任务优先级,任务堆栈起始地址等,多数成员和功能裁剪有关,哪些功能不适用,控制块的大小也随之减小。
a. 动态创建方法 xTaskCreate()
使用的内存是根据任务的使用情况申请的,需要传入一个内存指针
I. 函数原型
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
II. 参数:
pxTaskCode:要执行的任务的函数
const pcName:任务的名字,是一串字符串,长度不能超过最大名字长度
usStackDepth:任务堆栈大小,申请到为参数的四倍大小,注:空闲任务的堆栈大小为configMINIMAL_STACK_SIZE
pvParameters: 要传递的参数,可以为字符串
uxPriority: 任务优先级,范围在0~最高优先级-1
pxCreatedTask: 任务句柄,其他API函数会使用到这个任务句柄
动态方法在参数中返回任务句柄,而静态方法在函数返回值中返回任务句柄(参数中无任务句柄)
III. 返回值:
pdPASS: 如果任务成功创建
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY: 由于堆内存不足,任务创建失败。
@return pdPASS if the task was successfully created and added to a ready
list, otherwise an error code defined in the file projdefs.h
任务创建函数功能:
prvInitialiseNewTask()功能
pxPortInitialiseStack()的作用
栈底第一个位置存放portINITIAL_XPSR
接下来存放任务函数指针
prvTaskExitError入栈
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void pvParameters )
{
/ Simulate the stack frame as it would be created by a context switch
interrupt. */
/* Offset added to account for the way the MCU uses the stack on entry/exit
of interrupts, and to ensure alignment. */
pxTopOfStack--;
*pxTopOfStack = portINITIAL_XPSR; /* xPSR */
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
pxTopOfStack--;
*pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */
/* Save code space by skipping register initialisation. */
pxTopOfStack -= 5; /* R12, R3, R2 and R1. */
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
/* A save method is being used that requires each task to maintain its
own exec return value. */
pxTopOfStack--;
*pxTopOfStack = portINITIAL_EXEC_RETURN;
pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pxTopOfStack;//最后返回真正的栈顶地址
}
*prvAddNewTaskToReadyList( TCB_t pxNewTCB )的作用
prvAddTaskToReadyList( pxNewTCB ) 的作用
b. 静态创建方法 xTaskCreateStatic()
要先在 FreeRTOSConfig.h中
#define configSUPPORT_STATIC_ALLOCATION 1
然后定义vApplicationGetIdleTaskMemory,vApplicationGetTimerTaskMemory这两个函数,因为一旦使用了静态分配模式,就要用户自行实现这两个函数
不需要传入控制块的指针,要设置任务的堆栈大小
注:堆栈大小单位为UINT类型,所以设置堆栈大小50的话,实际大小为200字节
I. 函数原型
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 )
II. 参数:
pxTaskCode:要执行的任务的函数
const pcName:任务的名字,是一串字符串,长度不能超过最大名字长度
usStackDepth:任务堆栈大小,申请到为参数的四倍大小,注:空闲任务的堆栈大小为configMINIMAL_STACK_SIZE
pvParameters: 要传递的参数,可以为字符串
uxPriority: 任务优先级,范围在0~最高优先级-1
puxStackBuffer: 任务堆栈,一般为一个数组,数组类型要是StackType_t类型,当为动态创建时,这个栈空间由函数自行申请。
pxTaskBuffer: 任务控制块,必须要是StaticTask_t类型,当为动态创建时,这个任务控制块由函数自行申请。
动态方法在参数中返回任务句柄,而静态方法在函数返回值中返回任务句柄(参数中无任务句柄)
III. 返回值:
NULL:任务创建失败,puxStackBuffer 和 pxTaskBuffer 为 NULL会导致这个错误的发生
其他值:任务创建成功,返回任务的任务句柄
@return If neither pxStackBuffer or pxTaskBuffer are NULL, then the task will
be created and pdPASS is returned. If either pxStackBuffer or pxTaskBuffer
are NULL then the task will not be created and errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned.
例子:
// Structure that will hold the TCB of the task being created.
StaticTask_t xTaskBuffer;
// Buffer that the task being created will use as its stack. Note this is
// an array of StackType_t variables. The size of StackType_t is dependent on
// the RTOS port.
StackType_t xStack[ STACK_SIZE ];
// Function that implements the task being created.
void vTaskCode( void * pvParameters )
{
// The parameter value is expected to be 1 as 1 is passed in the
// pvParameters value in the call to xTaskCreateStatic().
configASSERT( ( uint32_t ) pvParameters == 1UL );
for( ;; )
{
// Task code goes here.
}
}
// Function that creates a task.
void vOtherFunction( void )
{
TaskHandle_t xHandle = NULL;
// Create the task without using any dynamic memory allocation.
xHandle = xTaskCreateStatic(
vTaskCode, // Function that implements the task.
"NAME", // Text name for the task.
STACK_SIZE, // Stack size in words, not bytes.
( void * ) 1, // Parameter passed into the task.
tskIDLE_PRIORITY,// Priority at which the task is created.
xStack, // Array to use as the task's stack.
&xTaskBuffer ); // Variable to hold the task's data structure.
// puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
// been created, and xHandle will be the task's handle. Use the handle
// to suspend the task.
vTaskSuspend( xHandle );
}
c. 创建一个使用MPU进行限制的任务
xTaskCreateRestricted()
相关内存使用动态内存分配
a. vTaskDelete()
删除一个用以上方法创建的任务,任务删除后不会再进入运行态,所以再也不能使用这个任务的句柄,
I. 函数原型
void vTaskDelete( TaskHandle_t xTaskToDelete )
II. 参数
xTaskToDelete: 要删除的任务的任务句柄
III. 返回值
无
IV. 任务删除函数功能:
a. vTaskSuspend()
挂起一个任务
I. 函数原型
void vTaskSuspend ( TaskHandle_t xTaskToSuspend )
II. 参数
xTaskToSuspend: 要挂起的任务的任务句柄,传入NULL的话,会将调用vTaskSuspend函数的任务挂起
III. 返回值
无
IV. 任务挂起函数功能:
b. vTaskResume()
恢复一个任务的运行,无论一个任务调用了多少次vTaskSuspend,恢复任务值用调用一次vTaskResume
注: INCLUDE_vTaskSuspend must be defined as 1 for this function to be available.
I. 函数原型
void vTaskResume ( TaskHandle_t xTaskToResume )
II. 参数
xTaskToResume: 要恢复的任务的句柄
III. 返回值
无
IV. 任务恢复函数功能:
c. xTaskResumeFromISR()
在中断服务函数中恢复一个任务的运行,与vTaskResume()相同,无论一个任务调用了多少次vTaskSuspend,恢复任务值用调用一次xTaskResumeFromISR()
注:INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be available
I. 函数原型
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume )
II. 参数
xTaskToResume: 要恢复的任务的句柄
III. 返回值
pdTRUE:恢复的任务优先级比当前运行的任务更高,所以要进行任务切换
pdFALSE:恢复的任务优先级比当前任务低,所以不用进行任务切换
例如:
extern TaskHandle_t Task2Task_Handler;
void EXTI3_IRQHandler(void)
{
BaseType_t YieldRequired;
delay_xms(50); //消抖
if(KEY0==0)
{
YieldRequired=xTaskResumeFromISR(Task2Task_Handler);//恢复任务2
printf("恢复任务2的运行!\r\n");
if(YieldRequired==pdTRUE)
{
/*如果函数xTaskResumeFromISR()返回值为pdTRUE,那么说明要恢复的这个
任务的任务优先级等于或者高于正在运行的任务(被中断打断的任务),所以在
退出中断的时候一定要进行上下文切换!*/
portYIELD_FROM_ISR(YieldRequired);
}
}
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3); //清除中断标志位
}
列表和列表项有完整性检查的成员,原理是在结构体的起始成员和末尾成员,设置两个固定值的变量,一旦有数据越界,必定会改变头尾数据的值,每次插入前检查一次值,就可以确认成员值是否被更改,以下为代码
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
#define listTEST_LIST_ITEM_INTEGRITY( pxItem ) configASSERT( ( ( pxItem )->xListItemIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxItem )->xListItemIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
#define listTEST_LIST_INTEGRITY( pxList ) configASSERT( ( ( pxList )->xListIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxList )->xListIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
a. 列表
/*
* Definition of the type of queue used by the scheduler.
*/
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;
注:最后一个成员变量为 Mini列表项类型
b. 列表项
/*
* Definition of the only type of object that a list can contain.
*/
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
void * configLIST_VOLATILE pvContainer; /*< Pointer to the list in which this list item is placed (if any). */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */
c. 迷你列表项
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
a. vListInitialise()
列表初始化
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
void vListInitialise( List_t * const pxList )
{
/* The list structure contains a list item which is used to mark the
end of the list. To initialise the list, the list end is inserted
as the only list entry. 遍历时使用*/
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
/* The list end value is the highest possible value in the list to
ensure it remains at the end of the list. 确保listEnd处于最尾端*/
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* The list end next and previous pointers point to itself so we know
when the list is empty. */
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
pxList->uxNumberOfItems = ( UBaseType_t ) 0U; //当前列表项为0
/* Write known values into the list if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */完整性检查
//16_Bit_Mcu use 0x5a5a, and 32_Bit one use 0x5a5a5a5aUL
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTE GRITY_CHECK_2_VALU E( pxList );
}
b. vListInitialiseItem()
列表项 初始化,在xTaskCreate(任务创建)的时候,函数会对其初始化
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* Make sure the list item is not recorded as being on a list. */
pxItem->pvContainer = NULL;
/* Write known values into the list item if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
c. vListInsert
列表项插入,由于列表是双向的,所以结构为环状
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
/* Only effective when configASSERT() is also defined, these tests may catch
the list data structures being overwritten in memory. They will not catch
data errors caused by incorrect configuration or use of FreeRTOS. */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* Insert the new list item into the list, sorted in xItemValue order.
If the list already contains a list item with the same item value then the
new list item should be placed after it. This ensures that TCB's which are
stored in ready lists (all of which have the same xItemValue value) get a
share of the CPU. However, if the xItemValue is the same as the back marker
the iteration loop below will not end. Therefore the value is checked
first, and the algorithm slightly modified if necessary. */
if( xValueOfInsertion == portMAX_DELAY )//如果插入的列表项是最大值,直接插入到列表最尾
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else//若不是最尾项,则找到要插入的位置,以下是按xItemValue 来寻找插入的位置
{
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
{
/* There is nothing to do here, just iterating to the wanted
insertion position. */
}
}
//找到位置后,做双向指针的插入
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
/* Remember which list the item is in. This allows fast removal of the
item later. */
pxNewListItem->pvContainer = ( void * ) pxList; //将列表项插入到列表中
( pxList->uxNumberOfItems )++; //列表的成员数加1
}
d. vListInsertEnd
列表项末尾插入 ,这里并不是指插入到列表尾,而是插入到pxIndex所指列表项的前面,因为pxIndex指向的是列表头,所以pxIndex所指项的前一个就是尾巴
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;
/* Only effective when configASSERT() is also defined, these tests may catch
the list data structures being overwritten in memory. They will not catch
data errors caused by incorrect configuration or use of FreeRTOS. */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );//此处为列表和列表项的完整性检查
/* Insert a new list item into pxList, but rather than sort the list,
makes the new list item the last item to be removed by a call to
listGET_OWNER_OF_NEXT_ENTRY(). */
//以下同样是在双向链表中插入元素的操作,只不过是在pxIndex所指项的前面插入,pxIndex所指的项是任意的,pxIndex所指的列表项就代表列表头
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* Remember which list the item is in. */
pxNewListItem->pvContainer = ( void * ) pxList; //将列表项插入到列表中
( pxList->uxNumberOfItems )++; //列表的成员数加1
}
f. uxListRemove
列表项删除
返回值:删除列表项后,列表剩余的列表项的数量
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in. Obtain the list from the list
item. */确定这个列表项是哪个列表的
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
//双向链表中,删除这个节点的操作
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* Only used during decision coverage testing. */用来输出调试信息
mtCOVERAGE_TEST_DELAY();
/* Make sure the index is left pointing to a valid item. */
if( pxList->pxIndex == pxItemToRemove )//如果删掉的是pxIndex所指的节点的话,则把pxIndex指向删除的节点的前一个节点
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();//用来输出调试信息
}
pxItemToRemove->pvContainer = NULL; //被删除的列表项要清除 所归属的列表
( pxList->uxNumberOfItems )--; //该列表的项目数减少
return pxList->uxNumberOfItems; //返回这个列表当前列表项数目
}
g. listGET_OWNER_OF_NEXT_ENTRY
列表的遍历
此函数用来用于从多个优先级的就绪任务中查找下一个要运行的任务
FREE_RTOS提供了一个宏定义形式的函数
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
{ \
List_t * const pxConstList = ( pxList ); \
/* Increment the index to the next item and return the item, ensuring */ \
/* we don't return the marker used at the end of the list. */ \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; //pxIndex指向下一个列表项 \
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
{ //如果pxIndex指向了列表的END项时,则代表到了列表的末尾,到了末尾的话,就跳过END项,指向列表头的列表项 \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
} \
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; //将指向的列表项的owner赋给TCB \
}
1. UBaseType_t uxTaskPriorityGet ( TaskHandle_t xTask )
功能: 获取对应任务的任务优先级
参数: 任务的句柄
返回值: 该任务的优先级
#if ( INCLUDE_uxTaskPriorityGet == 1 )
UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask )
{
TCB_t *pxTCB;
UBaseType_t uxReturn;
taskENTER_CRITICAL();
{
/* If null is passed in here then it is the priority of the that
called uxTaskPriorityGet() that is being queried. */
pxTCB = prvGetTCBFromHandle( xTask );
uxReturn = pxTCB->uxPriority;
}
taskEXIT_CRITICAL();
return uxReturn;
}
2. void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
功能: 将任务设定为对应的优先级
参数:
a. xTask:任务的句柄
b. uxNewPriority :新的任务优先级
3. UBaseType_t uxTaskGetSystemState ( TaskStatus_t * const pxTaskStatusArray,
const UBaseType_t uxArraySize,
uint32_t * const pulTotalRunTime )
功能: 获取系统中所有任务的任务状态
参数:
a. pxTaskStatusArray:存储任务状态的数组
typedef struct xTASK_STATUS
{
TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */
const char *pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
UBaseType_t xTaskNumber; /* A number unique to the task. */
eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */
UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */
UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */
uint32_t ulRunTimeCounter; /* 任务总共运行时间 The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */
StackType_t *pxStackBase; /* 指向任务堆栈 Points to the lowest address of the task's stack area. */
uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */栈空间历史剩余最小的容量
} TaskStatus_t;
b. uxArraySize:保存任务状态组的数组的大小
c. pulTotalRunTime :若configGENERATE_RUN_TIME_STATS为1 时,此参数用来保存系统的总的运行时间
返回值: 统计到的任务状态的个数
第一个创建的任务为vTaskStartScheduler()开始之前起创建的任务,在vTaskStartScheduler()中,会创建IDLE任务,以及定时器服务任务,所以任务的编号如下:
UBaseType_t uxTaskGetNumberOfTasks( void )
{
/* A critical section is not required because the variables are of type
BaseType_t. */
return uxCurrentNumberOfTasks;
}
/* Task states returned by eTaskGetState. */
typedef enum
{
eRunning = 0, /* A task is querying the state of itself, so must be running. */
eReady, /* The task being queried is in a read or pending ready list. */
eBlocked, /* The task being queried is in the Blocked state. */
eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */
eInvalid /* Used as an 'invalid state' value. */
} eTaskState;
6. TaskHookFunction_t xTaskGetApplicationTaskTag ( TaskHandle_t xTask )
功能: 获取任务的Tag值,任务句柄中有个pxTaskTag变量来保存任务标签值的,标签的功能由用户自己决定,这个函数获取的是任务的标签值的(系统内核不会使用该标签值),如果要使用下面的宏定义要开启
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
TaskHookFunction_t pxTaskTag;
#endif
参数: xTask获取的任务的任务句柄,若为NULL就获取正在运行的任务标签值
返回值: 任务的标签值
7. TaskHandle_t xTaskGetHandle( const char *pcNameToQuery )
功能: 获取当前任务的任务句柄,使用的话,下述的宏定义要打开
参数: 无
返回值: 当前任务的任务句柄
#if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) )
TaskHandle_t xTaskGetCurrentTaskHandle( void )
{
TaskHandle_t xReturn;
/* A critical section is not required as this is not called from
an interrupt and the current TCB will always be the same for any
individual execution thread. */
xReturn = pxCurrentTCB;
return xReturn;
}
#endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */
8. TaskHandle_t xTaskGetHandle( const char *pcNameToQuery )
功能: 根据任务名称获取任务句柄
参数: 任务名,字符串
返回值: 返回任务句柄,若为NULL,则说明没有这个任务
9. TaskHandle_t xTaskGetIdleTaskHandle ( void )
功能: 获取空闲任务的任务句柄,使用时宏定义要打开
参数: 无
返回值: 返回空闲任务的任务句柄
#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 )
TaskHandle_t xTaskGetIdleTaskHandle( void )
{
/* If xTaskGetIdleTaskHandle() is called before the scheduler has been
started, then xIdleTaskHandle will be NULL. */
configASSERT( ( xIdleTaskHandle != NULL ) );
return xIdleTaskHandle;
}
#endif /* INCLUDE_xTaskGetIdleTaskHandle */
10. UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask )
功能: 检查任务创建完成到现在栈空间历史剩余最小值,值越小溢出的可能越大,可以用于代码调试阶段,出货时不用
参数: 要查询的任务的任务句柄,使用NULL的话,则查询调用此函数的任务本身
返回值: 返回剩余量最小值
#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 )
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask )
{
TCB_t *pxTCB;
uint8_t *pucEndOfStack;
UBaseType_t uxReturn;
pxTCB = prvGetTCBFromHandle( xTask );
#if portSTACK_GROWTH < 0
{
pucEndOfStack = ( uint8_t * ) pxTCB->pxStack;
}
#else
{
pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack;
}
#endif
uxReturn = ( UBaseType_t ) prvTaskCheckFreeStackSpace( pucEndOfStack );
return uxReturn;
#endif /* INCLUDE_uxTaskGetStackHighWaterMark */
11. eTaskState eTaskGetState( TaskHandle_t xTask )
功能: 查询任务的运行状态
参数: 要查询的任务的任务句柄
返回值: 返回为eTaskState类型,如下
typedef enum
{
eRunning = 0, /* A task is querying the state of itself, so must be running. */
eReady, /* The task being queried is in a read or pending ready list. */
eBlocked, /* The task being queried is in the Blocked state. */
eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */
eInvalid /* Used as an 'invalid state' value. */
} eTaskState;
12. char *pcTaskGetName( TaskHandle_t xTaskToQuery )
功能: 查询对应任务的任务名
参数: 要查询的任务的任务句柄,NULL的话表示查询调用此函数的任务的名字
返回值: 返回对应任务的任务名
13. TickType_t xTaskGetTickCount( void )
功能: 查询任务调度器启动到现在,计数器xTickCount的值。xTickCount是系统的时钟节拍,每次定时器中断,xTickCount就会加1,定时器一秒中断几次取决于宏定义 configTICK_RATE_HZ。
参数: 无
返回值: 时间计数器xTickCount的值
14. void vTaskGetRunTimeStats( char *pcWriteBuffer )
功能: 提供一个表格,记录了每个任务运行的时间(不是秒,是由定时器的周期决定的变量,比如一个中断周期是50us,那么5就是 5 x 50us) 和 所占总时间的比例,
要使用的话,要打开一下几个宏定义:
configGENERATE_RUN_TIME_STATS,configUSE_STATS_FORMATTING_FUNCTIONS,
并且要实现一下几个宏定义:
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(),此宏定义要初始化一个定时器(中断频率要比系统时钟高,为系统时钟的10~20倍),比如:
void ConfigureTimeForRunTimeStats(void)
{
//定时器3初始化,定时器时钟为108M,分频系数为108-1,所以定时器3的频率
//为108M/108=1M,自动重装载为50-1,那么定时器周期就是50us
FreeRTOSRunTimeTicks=0;
TIM3_Init(50-1,108-1); //初始化TIM3
}
以及
portGET_RUN_TIME_COUNTER_VALUE()
#define portGET_RUN_TIME_COUNTER_VALUE() Runtime
或者portALT_GET_RUN_TIME_COUNTER_VALUE(Time)
这两个宏定义用来获取当前时基的时间值
参数:
pcWriteBuffer:用来保存任务时间信息的buffer
返回值: 无
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define portENTER_CRITICAL() vPortEnterCritical()
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS(); //除能5~15级的中断
uxCriticalNesting++; //使用该全局变量,是因为防止有多层临界区嵌套的时候,一旦调用退出临界区,就打乱了其他临界段的保护,只有全部临界段代码退出后才打开中断
/* This is not the interrupt safe version of the enter critical function so
assert() if it is being called from an interrupt context. Only API
functions that end in "FromISR" can be used in an interrupt. Only assert if
the critical nesting count is 1 to protect against recursive calls if the
assert function also uses a critical section. */
if( uxCriticalNesting == 1 )
{
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); //断言函数
}
}
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS(); //使能5~15级的中断
}
}
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
mrs ulReturn, basepri //先读出BASEPRI的值,保存到ulReturn中
cpsid i
msr basepri, ulNewBASEPRI //将屏蔽的优先级数写到BASEPRI中,比如写入4的话,0~3优先级的中断会执行,而4~15级的中断将被屏蔽
dsb
isb
cpsie i
}
return ulReturn; //返回之前的优先级,退出时将使用到这个值
}
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
#define portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) vPortSetBASEPRI( x )
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
/* Barrier instructions are not used as this function is only used to
lower the BASEPRI value. */
msr basepri, ulBASEPRI //将进入临界区时保存的值,重新写到BASEPRI寄存器中,恢复进入之前的状态
}
}
ISR中使用的临界区函数示例:
void TIMCallBack()
{
uint32_t uiState;
uiState = taskENTER_CRITICAL_FROM_ISR();
/********要执行的函数********/
taskEXIT_CRITICAL_FROM_ISR(uiState);
}
#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS()
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
cpsid i
msr basepri, ulNewBASEPRI
dsb
isb
cpsie i
}
}
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) //写0即打开所有中断
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
/* Barrier instructions are not used as this function is only used to
lower the BASEPRI value. */
msr basepri, ulBASEPRI
}
}
#if ( INCLUDE_vTaskDelay == 1 )
void vTaskDelay( const TickType_t xTicksToDelay )// 延时多少个时间节拍
{
BaseType_t xAlreadyYielded = pdFALSE;
/* A delay time of zero just forces a reschedule. */
if( xTicksToDelay > ( TickType_t ) 0U )
{
configASSERT( uxSchedulerSuspended == 0 );
vTaskSuspendAll();
{
traceTASK_DELAY();
/* A task that is removed from the event list while the
scheduler is suspended will not get placed in the ready
list or removed from the blocked list until the scheduler
is resumed.
This task cannot be in an event list as it is the currently
executing task. */
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); //此时就绪列表发生了变化
}
xAlreadyYielded = xTaskResumeAll();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Force a reschedule if xTaskResumeAll has not already done so, we may
have put ourselves to sleep. */
if( xAlreadyYielded == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* INCLUDE_vTaskDelay */
相关函数:
if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/* The current task must be in a ready list, so there is no need to
check, and the port reset macro can be called directly. */
portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );//重置任务的优先级
}
SysTick为倒计数类型的计数器
a. 先初始化SysTick
//初始化延迟函数
//当使用ucos的时候,此函数会初始化ucos的时钟节拍
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
u32 reload;
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
fac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用
reload=SYSCLK; //每秒钟的计数次数 单位为K
reload*=1000000/configTICK_RATE_HZ; //根据delay_ostickspersec设定溢出时间,周期50ms转换为频率为20HZ(1秒20个周期),被除数为一秒钟的计数,这个值不能大于77ms
//reload为24位寄存器,最大值:16777216,在216M下,约合77.7ms左右
fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延时的最少单位,OS的系统节拍
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
SysTick->LOAD=reload; //每1/OS_TICKS_PER_SEC秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
}
b. 注册中断服务函数
//systick中断服务函数,使用OS时用到
void xPortSysTickHandler( void )
{
/* The SysTick runs at the lowest interrupt priority, so when this interrupt
executes all interrupts must be unmasked. There is therefore no need to
save and then restore the interrupt mask value as its value is already
known - therefore the slightly faster vPortRaiseBASEPRI() function is used
in place of portSET_INTERRUPT_MASK_FROM_ISR(). */
vPortRaiseBASEPRI();
{
/* Increment the RTOS tick. */
if( xTaskIncrementTick() != pdFALSE )
{
/* A context switch is required. Context switching is performed in
the PendSV interrupt. Pend the PendSV interrupt. */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}
vPortClearBASEPRIFromISR();
}
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
HAL_IncTick();
}
c. xTaskIncrementTick()的功能
I. 若任务调度器未被挂起
xTickCount系统节拍加1
若系统节拍计数溢出,则交换两个延时列表(overflow和正常的列表)
若系统节拍计数大于 延时列表中最近要被唤醒的任务的节拍数,判断各个任务是不是到了退出阻塞态,就绪态(State)的时候,判断任务是否要退出各个event(队列,信号量),到了就退出对应的状态,并且把这个任务从对应的列表移除出来,并把合适的任务放到readylist中。
II. 若任务调度器被挂起
++uxPendedTicks
在xTaskResumeAll中,会调用这个变量次数的xTaskIncrementTick(),这样xTickCount就会恢复,并且取消任务的阻塞
用于任务和任务,任务和中断之间的传递消息,
队列长度:数据条数,创建时要指定大小和队列的长度
发送数据到队列是通过拷贝
出队阻塞: 任务从队列中读取消息可指定一个阻塞时间,阻塞时间设定为0的话,读不到数据,就马上返回执行其他程序;设定为0~portMAX_DELAY的话,任务没获取到消息就等待阻塞时间,若设置为portMAX_DELAY的话,就会一直等待,直到收到数据
入队阻塞: 当队列满时,等待有没位置,与出队阻塞类似
队列操作过程:
typedef struct QueueDefinition
{
int8_t *pcHead; /*< Points to the beginning of the queue storage area. */
int8_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */
int8_t *pcWriteTo; /*< Points to the free next place in the storage area. 空闲的块 */
union /* Use of a union is an exception to the coding standard to ensure two mutually exclusive structure members don't appear simultaneously (wasting RAM). */
{
int8_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */
UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */
} u;
List_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. 存储处于因队列满而无法发送消息进来的任务*/
List_t xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. 存储处于因队空而无法接收到消息进来的任务*/
volatile UBaseType_t uxMessagesWaiting;/*< The number of items currently in the queue. 目前队列中有多少消息*/
UBaseType_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. 队列的长度*/
UBaseType_t uxItemSize; /*< The size of each items that the queue will hold. 每个消息的大小*/
volatile int8_t cRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
volatile int8_t cTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
队列的创建:
QueueHandle_t xQueueGenericCreate ( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
功能: 创建队列的动态方法,内核自动分配内存
参数:
a. uxQueueLength: 最大的消息数量
b. uxItemSize: 每条消息的字节数
返回值: 队列的句柄,0为失败
#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U )
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
#endif
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
{
Queue_t *pxNewQueue;
size_t xQueueSizeInBytes;
uint8_t *pucQueueStorage;
configASSERT( uxQueueLength > ( UBaseType_t ) 0 );
if( uxItemSize == ( UBaseType_t ) 0 )
{
/* There is not going to be a queue storage area. */
xQueueSizeInBytes = ( size_t ) 0;
}
else
{
/* Allocate enough space to hold the maximum number of items that
can be in the queue at any time. */
xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
}
pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
if( pxNewQueue != NULL )
{
/* Jump past the queue structure to find the location of the queue
storage area. */
pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t );
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
/* Queues can be created either statically or dynamically, so
note this task was created dynamically in case it is later
deleted. */
pxNewQueue->ucStaticallyAllocated = pdFALSE;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
}
return pxNewQueue;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
创建队列的静态方法
QueueHandle_t xQueueGenericCreateStatic ( const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
uint8_t * pucQueueStorage,
StaticQueue_t *pxStaticQueue,
const uint8_t ucQueueType )
入队函数:
0~最大优先级数-1,
优先级0最小,优先级随数字依次递增
多个任务可以使用同一个优先级(configUSE_TIME_SLICING 为 1 时)
存储任务的属性,FREERTOS把这些属性结合到一起用一个结构体表示,这个结构体就叫:TCB_t,会存储任务优先级,任务堆栈起始地址等,多数成员和功能裁剪有关,哪些功能不适用,控制块的大小也随之减小。
a. 动态创建方法 xTaskCreate()
使用的内存是根据任务的使用情况申请的,需要传入一个内存指针
I. 函数原型
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
II. 参数:
pxTaskCode:要执行的任务的函数
const pcName:任务的名字,是一串字符串,长度不能超过最大名字长度
usStackDepth:任务堆栈大小,申请到为参数的四倍大小,注:空闲任务的堆栈大小为configMINIMAL_STACK_SIZE
pvParameters: 要传递的参数,可以为字符串
uxPriority: 任务优先级,范围在0~最高优先级-1
pxCreatedTask: 任务句柄,其他API函数会使用到这个任务句柄
动态方法在参数中返回任务句柄,而静态方法在函数返回值中返回任务句柄(参数中无任务句柄)
III. 返回值:
pdPASS: 如果任务成功创建
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY: 由于堆内存不足,任务创建失败。
@return pdPASS if the task was successfully created and added to a ready
list, otherwise an error code defined in the file projdefs.h
b. 静态创建方法 xTaskCreateStatic()
要先在 FreeRTOSConfig.h中
#define configSUPPORT_STATIC_ALLOCATION 1
然后定义vApplicationGetIdleTaskMemory,vApplicationGetTimerTaskMemory这两个函数,因为一旦使用了静态分配模式,就要用户自行实现这两个函数
不需要传入控制块的指针,要设置任务的堆栈大小
注:堆栈大小单位为UINT类型,所以设置堆栈大小50的话,实际大小为200字节
I. 函数原型
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 )
II. 参数:
pxTaskCode:要执行的任务的函数
const pcName:任务的名字,是一串字符串,长度不能超过最大名字长度
usStackDepth:任务堆栈大小,申请到为参数的四倍大小,注:空闲任务的堆栈大小为configMINIMAL_STACK_SIZE
pvParameters: 要传递的参数,可以为字符串
uxPriority: 任务优先级,范围在0~最高优先级-1
puxStackBuffer: 任务堆栈,一般为一个数组,数组类型要是StackType_t类型,当为动态创建时,这个栈空间由函数自行申请。
pxTaskBuffer: 任务控制块,必须要是StaticTask_t类型,当为动态创建时,这个任务控制块由函数自行申请。
动态方法在参数中返回任务句柄,而静态方法在函数返回值中返回任务句柄(参数中无任务句柄)
III. 返回值:
NULL:任务创建失败,puxStackBuffer 和 pxTaskBuffer 为 NULL会导致这个错误的发生
其他值:任务创建成功,返回任务的任务句柄
@return If neither pxStackBuffer or pxTaskBuffer are NULL, then the task will
be created and pdPASS is returned. If either pxStackBuffer or pxTaskBuffer
are NULL then the task will not be created and errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned.
例子:
// Structure that will hold the TCB of the task being created.
StaticTask_t xTaskBuffer;
// Buffer that the task being created will use as its stack. Note this is
// an array of StackType_t variables. The size of StackType_t is dependent on
// the RTOS port.
StackType_t xStack[ STACK_SIZE ];
// Function that implements the task being created.
void vTaskCode( void * pvParameters )
{
// The parameter value is expected to be 1 as 1 is passed in the
// pvParameters value in the call to xTaskCreateStatic().
configASSERT( ( uint32_t ) pvParameters == 1UL );
for( ;; )
{
// Task code goes here.
}
}
// Function that creates a task.
void vOtherFunction( void )
{
TaskHandle_t xHandle = NULL;
// Create the task without using any dynamic memory allocation.
xHandle = xTaskCreateStatic(
vTaskCode, // Function that implements the task.
"NAME", // Text name for the task.
STACK_SIZE, // Stack size in words, not bytes.
( void * ) 1, // Parameter passed into the task.
tskIDLE_PRIORITY,// Priority at which the task is created.
xStack, // Array to use as the task's stack.
&xTaskBuffer ); // Variable to hold the task's data structure.
// puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
// been created, and xHandle will be the task's handle. Use the handle
// to suspend the task.
vTaskSuspend( xHandle );
}
c. 创建一个使用MPU进行限制的任务
xTaskCreateRestricted()
相关内存使用动态内存分配
a. vTaskDelete()
删除一个用以上方法创建的任务,任务删除后不会再进入运行态,所以再也不能使用这个任务的句柄,
I. 函数原型
void vTaskDelete( TaskHandle_t xTaskToDelete )
II. 参数
xTaskToDelete: 要删除的任务的任务句柄
III. 返回值
无
a. vTaskSuspend()
挂起一个任务
I. 函数原型
void vTaskSuspend ( TaskHandle_t xTaskToSuspend )
II. 参数
xTaskToSuspend: 要挂起的任务的任务句柄,传入NULL的话,会将调用vTaskSuspend函数的任务挂起
III. 返回值
无
b. vTaskResume()
恢复一个任务的运行,无论一个任务调用了多少次vTaskSuspend,恢复任务值用调用一次vTaskResume
注: INCLUDE_vTaskSuspend must be defined as 1 for this function to be available.
I. 函数原型
void vTaskResume ( TaskHandle_t xTaskToResume )
II. 参数
xTaskToResume: 要恢复的任务的句柄
III. 返回值
无
c. xTaskResumeFromISR()
在中断服务函数中恢复一个任务的运行,与vTaskResume()相同,无论一个任务调用了多少次vTaskSuspend,恢复任务值用调用一次xTaskResumeFromISR()
注:INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be available
I. 函数原型
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume )
II. 参数
xTaskToResume: 要恢复的任务的句柄
III. 返回值
pdTRUE:恢复的任务优先级比当前运行的任务更高,所以要进行任务切换
pdFALSE:恢复的任务优先级比当前任务低,所以不用进行任务切换
例如:
extern TaskHandle_t Task2Task_Handler;
void EXTI3_IRQHandler(void)
{
BaseType_t YieldRequired;
delay_xms(50); //消抖
if(KEY0==0)
{
YieldRequired=xTaskResumeFromISR(Task2Task_Handler);//恢复任务2
printf("恢复任务2的运行!\r\n");
if(YieldRequired==pdTRUE)
{
/*如果函数xTaskResumeFromISR()返回值为pdTRUE,那么说明要恢复的这个
任务的任务优先级等于或者高于正在运行的任务(被中断打断的任务),所以在
退出中断的时候一定要进行上下文切换!*/
portYIELD_FROM_ISR(YieldRequired);
}
}
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3); //清除中断标志位
}
列表和列表项有完整性检查的成员,原理是在结构体的起始成员和末尾成员,设置两个固定值的变量,一旦有数据越界,必定会改变头尾数据的值,每次插入前检查一次值,就可以确认成员值是否被更改,以下为代码
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
#define listTEST_LIST_ITEM_INTEGRITY( pxItem ) configASSERT( ( ( pxItem )->xListItemIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxItem )->xListItemIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
#define listTEST_LIST_INTEGRITY( pxList ) configASSERT( ( ( pxList )->xListIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxList )->xListIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
a. 列表
/*
* Definition of the type of queue used by the scheduler.
*/
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;
注:最后一个成员变量为 Mini列表项类型
b. 列表项
/*
* Definition of the only type of object that a list can contain.
*/
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
void * configLIST_VOLATILE pvContainer; /*< Pointer to the list in which this list item is placed (if any). */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */
c. 迷你列表项
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
a. vListInitialise()
列表初始化
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
void vListInitialise( List_t * const pxList )
{
/* The list structure contains a list item which is used to mark the
end of the list. To initialise the list, the list end is inserted
as the only list entry. 遍历时使用*/
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
/* The list end value is the highest possible value in the list to
ensure it remains at the end of the list. 确保listEnd处于最尾端*/
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* The list end next and previous pointers point to itself so we know
when the list is empty. */
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
pxList->uxNumberOfItems = ( UBaseType_t ) 0U; //当前列表项为0
/* Write known values into the list if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */完整性检查
//16_Bit_Mcu use 0x5a5a, and 32_Bit one use 0x5a5a5a5aUL
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTE GRITY_CHECK_2_VALU E( pxList );
}
b. vListInitialiseItem()
列表项 初始化,在xTaskCreate(任务创建)的时候,函数会对其初始化
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* Make sure the list item is not recorded as being on a list. */
pxItem->pvContainer = NULL;
/* Write known values into the list item if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
c. vListInsert
列表项插入,由于列表是双向的,所以结构为环状
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
/* Only effective when configASSERT() is also defined, these tests may catch
the list data structures being overwritten in memory. They will not catch
data errors caused by incorrect configuration or use of FreeRTOS. */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* Insert the new list item into the list, sorted in xItemValue order.
If the list already contains a list item with the same item value then the
new list item should be placed after it. This ensures that TCB's which are
stored in ready lists (all of which have the same xItemValue value) get a
share of the CPU. However, if the xItemValue is the same as the back marker
the iteration loop below will not end. Therefore the value is checked
first, and the algorithm slightly modified if necessary. */
if( xValueOfInsertion == portMAX_DELAY )//如果插入的列表项是最大值,直接插入到列表最尾
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else//若不是最尾项,则找到要插入的位置,以下是按xItemValue 来寻找插入的位置
{
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
{
/* There is nothing to do here, just iterating to the wanted
insertion position. */
}
}
//找到位置后,做双向指针的插入
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
/* Remember which list the item is in. This allows fast removal of the
item later. */
pxNewListItem->pvContainer = ( void * ) pxList; //将列表项插入到列表中
( pxList->uxNumberOfItems )++; //列表的成员数加1
}
d. vListInsertEnd
列表项末尾插入 ,这里并不是指插入到列表尾,而是插入到pxIndex所指列表项的前面,因为pxIndex指向的是列表头,所以pxIndex所指项的前一个就是尾巴
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;
/* Only effective when configASSERT() is also defined, these tests may catch
the list data structures being overwritten in memory. They will not catch
data errors caused by incorrect configuration or use of FreeRTOS. */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );//此处为列表和列表项的完整性检查
/* Insert a new list item into pxList, but rather than sort the list,
makes the new list item the last item to be removed by a call to
listGET_OWNER_OF_NEXT_ENTRY(). */
//以下同样是在双向链表中插入元素的操作,只不过是在pxIndex所指项的前面插入,pxIndex所指的项是任意的,pxIndex所指的列表项就代表列表头
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* Remember which list the item is in. */
pxNewListItem->pvContainer = ( void * ) pxList; //将列表项插入到列表中
( pxList->uxNumberOfItems )++; //列表的成员数加1
}
f. uxListRemove
列表项删除
返回值:删除列表项后,列表剩余的列表项的数量
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in. Obtain the list from the list
item. */确定这个列表项是哪个列表的
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
//双向链表中,删除这个节点的操作
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* Only used during decision coverage testing. */用来输出调试信息
mtCOVERAGE_TEST_DELAY();
/* Make sure the index is left pointing to a valid item. */
if( pxList->pxIndex == pxItemToRemove )//如果删掉的是pxIndex所指的节点的话,则把pxIndex指向删除的节点的前一个节点
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();//用来输出调试信息
}
pxItemToRemove->pvContainer = NULL; //被删除的列表项要清除 所归属的列表
( pxList->uxNumberOfItems )--; //该列表的项目数减少
return pxList->uxNumberOfItems; //返回这个列表当前列表项数目
}
g. listGET_OWNER_OF_NEXT_ENTRY
列表的遍历
此函数用来用于从多个优先级的就绪任务中查找下一个要运行的任务
FREE_RTOS提供了一个宏定义形式的函数
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
{ \
List_t * const pxConstList = ( pxList ); \
/* Increment the index to the next item and return the item, ensuring */ \
/* we don't return the marker used at the end of the list. */ \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; //pxIndex指向下一个列表项 \
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
{ //如果pxIndex指向了列表的END项时,则代表到了列表的末尾,到了末尾的话,就跳过END项,指向列表头的列表项 \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
} \
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; //将指向的列表项的owner赋给TCB \
}
1. UBaseType_t uxTaskPriorityGet ( TaskHandle_t xTask )
功能: 获取对应任务的任务优先级
参数: 任务的句柄
返回值: 该任务的优先级
#if ( INCLUDE_uxTaskPriorityGet == 1 )
UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask )
{
TCB_t *pxTCB;
UBaseType_t uxReturn;
taskENTER_CRITICAL();
{
/* If null is passed in here then it is the priority of the that
called uxTaskPriorityGet() that is being queried. */
pxTCB = prvGetTCBFromHandle( xTask );
uxReturn = pxTCB->uxPriority;
}
taskEXIT_CRITICAL();
return uxReturn;
}
2. void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
功能: 将任务设定为对应的优先级
参数:
a. xTask:任务的句柄
b. uxNewPriority :新的任务优先级
3. UBaseType_t uxTaskGetSystemState ( TaskStatus_t * const pxTaskStatusArray,
const UBaseType_t uxArraySize,
uint32_t * const pulTotalRunTime )
功能: 获取系统中所有任务的任务状态
参数:
a. pxTaskStatusArray:存储任务状态的数组
typedef struct xTASK_STATUS
{
TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */
const char *pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
UBaseType_t xTaskNumber; /* A number unique to the task. */
eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */
UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */
UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */
uint32_t ulRunTimeCounter; /* 任务总共运行时间 The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */
StackType_t *pxStackBase; /* 指向任务堆栈 Points to the lowest address of the task's stack area. */
uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */栈空间历史剩余最小的容量
} TaskStatus_t;
b. uxArraySize:保存任务状态组的数组的大小
c. pulTotalRunTime :若configGENERATE_RUN_TIME_STATS为1 时,此参数用来保存系统的总的运行时间
返回值: 统计到的任务状态的个数
第一个创建的任务为vTaskStartScheduler()开始之前起创建的任务,在vTaskStartScheduler()中,会创建IDLE任务,以及定时器服务任务,所以任务的编号如下:
UBaseType_t uxTaskGetNumberOfTasks( void )
{
/* A critical section is not required because the variables are of type
BaseType_t. */
return uxCurrentNumberOfTasks;
}
/* Task states returned by eTaskGetState. */
typedef enum
{
eRunning = 0, /* A task is querying the state of itself, so must be running. */
eReady, /* The task being queried is in a read or pending ready list. */
eBlocked, /* The task being queried is in the Blocked state. */
eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */
eInvalid /* Used as an 'invalid state' value. */
} eTaskState;
6. TaskHookFunction_t xTaskGetApplicationTaskTag ( TaskHandle_t xTask )
功能: 获取任务的Tag值,任务句柄中有个pxTaskTag变量来保存任务标签值的,标签的功能由用户自己决定,这个函数获取的是任务的标签值的(系统内核不会使用该标签值),如果要使用下面的宏定义要开启
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
TaskHookFunction_t pxTaskTag;
#endif
参数: xTask获取的任务的任务句柄,若为NULL就获取正在运行的任务标签值
返回值: 任务的标签值
7. TaskHandle_t xTaskGetHandle( const char *pcNameToQuery )
功能: 获取当前任务的任务句柄,使用的话,下述的宏定义要打开
参数: 无
返回值: 当前任务的任务句柄
#if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) )
TaskHandle_t xTaskGetCurrentTaskHandle( void )
{
TaskHandle_t xReturn;
/* A critical section is not required as this is not called from
an interrupt and the current TCB will always be the same for any
individual execution thread. */
xReturn = pxCurrentTCB;
return xReturn;
}
#endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */
8. TaskHandle_t xTaskGetHandle( const char *pcNameToQuery )
功能: 根据任务名称获取任务句柄
参数: 任务名,字符串
返回值: 返回任务句柄,若为NULL,则说明没有这个任务
9. TaskHandle_t xTaskGetIdleTaskHandle ( void )
功能: 获取空闲任务的任务句柄,使用时宏定义要打开
参数: 无
返回值: 返回空闲任务的任务句柄
#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 )
TaskHandle_t xTaskGetIdleTaskHandle( void )
{
/* If xTaskGetIdleTaskHandle() is called before the scheduler has been
started, then xIdleTaskHandle will be NULL. */
configASSERT( ( xIdleTaskHandle != NULL ) );
return xIdleTaskHandle;
}
#endif /* INCLUDE_xTaskGetIdleTaskHandle */
10. UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask )
功能: 检查任务创建完成到现在栈空间历史剩余最小值,值越小溢出的可能越大,可以用于代码调试阶段,出货时不用
参数: 要查询的任务的任务句柄,使用NULL的话,则查询调用此函数的任务本身
返回值: 返回剩余量最小值
#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 )
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask )
{
TCB_t *pxTCB;
uint8_t *pucEndOfStack;
UBaseType_t uxReturn;
pxTCB = prvGetTCBFromHandle( xTask );
#if portSTACK_GROWTH < 0
{
pucEndOfStack = ( uint8_t * ) pxTCB->pxStack;
}
#else
{
pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack;
}
#endif
uxReturn = ( UBaseType_t ) prvTaskCheckFreeStackSpace( pucEndOfStack );
return uxReturn;
#endif /* INCLUDE_uxTaskGetStackHighWaterMark */
11. eTaskState eTaskGetState( TaskHandle_t xTask )
功能: 查询任务的运行状态
参数: 要查询的任务的任务句柄
返回值: 返回为eTaskState类型,如下
typedef enum
{
eRunning = 0, /* A task is querying the state of itself, so must be running. */
eReady, /* The task being queried is in a read or pending ready list. */
eBlocked, /* The task being queried is in the Blocked state. */
eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */
eInvalid /* Used as an 'invalid state' value. */
} eTaskState;
12. char *pcTaskGetName( TaskHandle_t xTaskToQuery )
功能: 查询对应任务的任务名
参数: 要查询的任务的任务句柄,NULL的话表示查询调用此函数的任务的名字
返回值: 返回对应任务的任务名
13. TickType_t xTaskGetTickCount( void )
功能: 查询任务调度器启动到现在,计数器xTickCount的值。xTickCount是系统的时钟节拍,每次定时器中断,xTickCount就会加1,定时器一秒中断几次取决于宏定义 configTICK_RATE_HZ。
参数: 无
返回值: 时间计数器xTickCount的值
14. void vTaskGetRunTimeStats( char *pcWriteBuffer )
功能: 提供一个表格,记录了每个任务运行的时间(不是秒,是由定时器的周期决定的变量,比如一个中断周期是50us,那么5就是 5 x 50us) 和 所占总时间的比例,
要使用的话,要打开一下几个宏定义:
configGENERATE_RUN_TIME_STATS,configUSE_STATS_FORMATTING_FUNCTIONS,
并且要实现一下几个宏定义:
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(),此宏定义要初始化一个定时器(中断频率要比系统时钟高,为系统时钟的10~20倍),比如:
void ConfigureTimeForRunTimeStats(void)
{
//定时器3初始化,定时器时钟为108M,分频系数为108-1,所以定时器3的频率
//为108M/108=1M,自动重装载为50-1,那么定时器周期就是50us
FreeRTOSRunTimeTicks=0;
TIM3_Init(50-1,108-1); //初始化TIM3
}
以及
portGET_RUN_TIME_COUNTER_VALUE()
#define portGET_RUN_TIME_COUNTER_VALUE() Runtime
或者portALT_GET_RUN_TIME_COUNTER_VALUE(Time)
这两个宏定义用来获取当前时基的时间值
参数:
pcWriteBuffer:用来保存任务时间信息的buffer
返回值: 无
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define portENTER_CRITICAL() vPortEnterCritical()
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS(); //除能5~15级的中断
uxCriticalNesting++; //使用该全局变量,是因为防止有多层临界区嵌套的时候,一旦调用退出临界区,就打乱了其他临界段的保护,只有全部临界段代码退出后才打开中断
/* This is not the interrupt safe version of the enter critical function so
assert() if it is being called from an interrupt context. Only API
functions that end in "FromISR" can be used in an interrupt. Only assert if
the critical nesting count is 1 to protect against recursive calls if the
assert function also uses a critical section. */
if( uxCriticalNesting == 1 )
{
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); //断言函数
}
}
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS(); //使能5~15级的中断
}
}
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
mrs ulReturn, basepri //先读出BASEPRI的值,保存到ulReturn中
cpsid i
msr basepri, ulNewBASEPRI //将屏蔽的优先级数写到BASEPRI中,比如写入4的话,0~3优先级的中断会执行,而4~15级的中断将被屏蔽
dsb
isb
cpsie i
}
return ulReturn; //返回之前的优先级,退出时将使用到这个值
}
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
#define portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) vPortSetBASEPRI( x )
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
/* Barrier instructions are not used as this function is only used to
lower the BASEPRI value. */
msr basepri, ulBASEPRI //将进入临界区时保存的值,重新写到BASEPRI寄存器中,恢复进入之前的状态
}
}
ISR中使用的临界区函数示例:
void TIMCallBack()
{
uint32_t uiState;
uiState = taskENTER_CRITICAL_FROM_ISR();
/********要执行的函数********/
taskEXIT_CRITICAL_FROM_ISR(uiState);
}
#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS()
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
cpsid i
msr basepri, ulNewBASEPRI
dsb
isb
cpsie i
}
}
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) //写0即打开所有中断
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
/* Barrier instructions are not used as this function is only used to
lower the BASEPRI value. */
msr basepri, ulBASEPRI
}
}
#if ( INCLUDE_vTaskDelay == 1 )
void vTaskDelay( const TickType_t xTicksToDelay )// 延时多少个时间节拍
{
BaseType_t xAlreadyYielded = pdFALSE;
/* A delay time of zero just forces a reschedule. */
if( xTicksToDelay > ( TickType_t ) 0U )
{
configASSERT( uxSchedulerSuspended == 0 );
vTaskSuspendAll();
{
traceTASK_DELAY();
/* A task that is removed from the event list while the
scheduler is suspended will not get placed in the ready
list or removed from the blocked list until the scheduler
is resumed.
This task cannot be in an event list as it is the currently
executing task. */
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); //此时就绪列表发生了变化
}
xAlreadyYielded = xTaskResumeAll();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Force a reschedule if xTaskResumeAll has not already done so, we may
have put ourselves to sleep. */
if( xAlreadyYielded == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* INCLUDE_vTaskDelay */
相关函数:
if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/* The current task must be in a ready list, so there is no need to
check, and the port reset macro can be called directly. */
portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );//重置任务的优先级
}
SysTick为倒计数类型的计数器
a. 先初始化SysTick
//初始化延迟函数
//当使用ucos的时候,此函数会初始化ucos的时钟节拍
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
u32 reload;
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
fac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用
reload=SYSCLK; //每秒钟的计数次数 单位为K
reload*=1000000/configTICK_RATE_HZ; //根据delay_ostickspersec设定溢出时间,周期50ms转换为频率为20HZ(1秒20个周期),被除数为一秒钟的计数,这个值不能大于77ms
//reload为24位寄存器,最大值:16777216,在216M下,约合77.7ms左右
fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延时的最少单位,OS的系统节拍
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
SysTick->LOAD=reload; //每1/OS_TICKS_PER_SEC秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
}
b. 注册中断服务函数
//systick中断服务函数,使用OS时用到
void xPortSysTickHandler( void )
{
/* The SysTick runs at the lowest interrupt priority, so when this interrupt
executes all interrupts must be unmasked. There is therefore no need to
save and then restore the interrupt mask value as its value is already
known - therefore the slightly faster vPortRaiseBASEPRI() function is used
in place of portSET_INTERRUPT_MASK_FROM_ISR(). */
vPortRaiseBASEPRI();
{
/* Increment the RTOS tick. */
if( xTaskIncrementTick() != pdFALSE )
{
/* A context switch is required. Context switching is performed in
the PendSV interrupt. Pend the PendSV interrupt. */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}
vPortClearBASEPRIFromISR();
}
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
HAL_IncTick();
}
c. xTaskIncrementTick()的功能
I. 若任务调度器未被挂起
xTickCount系统节拍加1
若系统节拍计数溢出,则交换两个延时列表(overflow和正常的列表)
若系统节拍计数大于 延时列表中最近要被唤醒的任务的节拍数,判断各个任务是不是到了退出阻塞态,就绪态(State)的时候,判断任务是否要退出各个event(队列,信号量),到了就退出对应的状态,并且把这个任务从对应的列表移除出来,并把合适的任务放到readylist中。
II. 若任务调度器被挂起
++uxPendedTicks
在xTaskResumeAll中,会调用这个变量次数的xTaskIncrementTick(),这样xTickCount就会恢复,并且取消任务的阻塞
用于任务和任务,任务和中断之间的传递消息,
队列长度:数据条数,创建时要指定大小和队列的长度
发送数据到队列是通过拷贝
出队阻塞: 任务从队列中读取消息可指定一个阻塞时间,阻塞时间设定为0的话,读不到数据,就马上返回执行其他程序;设定为0~portMAX_DELAY的话,任务没获取到消息就等待阻塞时间,若设置为portMAX_DELAY的话,就会一直等待,直到收到数据
入队阻塞: 当队列满时,等待有没位置,与出队阻塞类似
队列操作过程:
typedef struct QueueDefinition
{
int8_t *pcHead; /*< Points to the beginning of the queue storage area. */
int8_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */
int8_t *pcWriteTo; /*< Points to the free next place in the storage area. 空闲的块 */
union /* Use of a union is an exception to the coding standard to ensure two mutually exclusive structure members don't appear simultaneously (wasting RAM). */
{
int8_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */
UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */
} u;
List_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. 存储处于因队列满而无法发送消息进来的任务*/
List_t xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. 存储处于因队空而无法接收到消息进来的任务*/
volatile UBaseType_t uxMessagesWaiting;/*< The number of items currently in the queue. 目前队列中有多少消息*/
UBaseType_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. 队列的长度*/
UBaseType_t uxItemSize; /*< The size of each items that the queue will hold. 每个消息的大小*/
volatile int8_t cRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
volatile int8_t cTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
队列的创建:
QueueHandle_t xQueueGenericCreate ( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
功能: 创建队列的动态方法,内核自动分配内存
参数:
a. uxQueueLength: 最大的消息数量
b. uxItemSize: 每条消息的字节数
返回值: 队列的句柄,0为失败
#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U )
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
#endif
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
{
Queue_t *pxNewQueue;
size_t xQueueSizeInBytes;
uint8_t *pucQueueStorage;
configASSERT( uxQueueLength > ( UBaseType_t ) 0 );
if( uxItemSize == ( UBaseType_t ) 0 )
{
/* There is not going to be a queue storage area. */
xQueueSizeInBytes = ( size_t ) 0;
}
else
{
/* Allocate enough space to hold the maximum number of items that
can be in the queue at any time. */
xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
}
pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
if( pxNewQueue != NULL )
{
/* Jump past the queue structure to find the location of the queue
storage area. */
pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t );
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
/* Queues can be created either statically or dynamically, so
note this task was created dynamically in case it is later
deleted. */
pxNewQueue->ucStaticallyAllocated = pdFALSE;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
}
return pxNewQueue;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
创建队列的静态方法
QueueHandle_t xQueueGenericCreateStatic ( const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
uint8_t * pucQueueStorage,
StaticQueue_t *pxStaticQueue,
const uint8_t ucQueueType )
入队函数:
0~最大优先级数-1,
优先级0最小,优先级随数字依次递增
多个任务可以使用同一个优先级(configUSE_TIME_SLICING 为 1 时)
存储任务的属性,FREERTOS把这些属性结合到一起用一个结构体表示,这个结构体就叫:TCB_t,会存储任务优先级,任务堆栈起始地址等,多数成员和功能裁剪有关,哪些功能不适用,控制块的大小也随之减小。
a. 动态创建方法 xTaskCreate()
使用的内存是根据任务的使用情况申请的,需要传入一个内存指针
I. 函数原型
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
II. 参数:
pxTaskCode:要执行的任务的函数
const pcName:任务的名字,是一串字符串,长度不能超过最大名字长度
usStackDepth:任务堆栈大小,申请到为参数的四倍大小,注:空闲任务的堆栈大小为configMINIMAL_STACK_SIZE
pvParameters: 要传递的参数,可以为字符串
uxPriority: 任务优先级,范围在0~最高优先级-1
pxCreatedTask: 任务句柄,其他API函数会使用到这个任务句柄
动态方法在参数中返回任务句柄,而静态方法在函数返回值中返回任务句柄(参数中无任务句柄)
III. 返回值:
pdPASS: 如果任务成功创建
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY: 由于堆内存不足,任务创建失败。
@return pdPASS if the task was successfully created and added to a ready
list, otherwise an error code defined in the file projdefs.h
b. 静态创建方法 xTaskCreateStatic()
要先在 FreeRTOSConfig.h中
#define configSUPPORT_STATIC_ALLOCATION 1
然后定义vApplicationGetIdleTaskMemory,vApplicationGetTimerTaskMemory这两个函数,因为一旦使用了静态分配模式,就要用户自行实现这两个函数
不需要传入控制块的指针,要设置任务的堆栈大小
注:堆栈大小单位为UINT类型,所以设置堆栈大小50的话,实际大小为200字节
I. 函数原型
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 )
II. 参数:
pxTaskCode:要执行的任务的函数
const pcName:任务的名字,是一串字符串,长度不能超过最大名字长度
usStackDepth:任务堆栈大小,申请到为参数的四倍大小,注:空闲任务的堆栈大小为configMINIMAL_STACK_SIZE
pvParameters: 要传递的参数,可以为字符串
uxPriority: 任务优先级,范围在0~最高优先级-1
puxStackBuffer: 任务堆栈,一般为一个数组,数组类型要是StackType_t类型,当为动态创建时,这个栈空间由函数自行申请。
pxTaskBuffer: 任务控制块,必须要是StaticTask_t类型,当为动态创建时,这个任务控制块由函数自行申请。
动态方法在参数中返回任务句柄,而静态方法在函数返回值中返回任务句柄(参数中无任务句柄)
III. 返回值:
NULL:任务创建失败,puxStackBuffer 和 pxTaskBuffer 为 NULL会导致这个错误的发生
其他值:任务创建成功,返回任务的任务句柄
@return If neither pxStackBuffer or pxTaskBuffer are NULL, then the task will
be created and pdPASS is returned. If either pxStackBuffer or pxTaskBuffer
are NULL then the task will not be created and errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned.
例子:
// Structure that will hold the TCB of the task being created.
StaticTask_t xTaskBuffer;
// Buffer that the task being created will use as its stack. Note this is
// an array of StackType_t variables. The size of StackType_t is dependent on
// the RTOS port.
StackType_t xStack[ STACK_SIZE ];
// Function that implements the task being created.
void vTaskCode( void * pvParameters )
{
// The parameter value is expected to be 1 as 1 is passed in the
// pvParameters value in the call to xTaskCreateStatic().
configASSERT( ( uint32_t ) pvParameters == 1UL );
for( ;; )
{
// Task code goes here.
}
}
// Function that creates a task.
void vOtherFunction( void )
{
TaskHandle_t xHandle = NULL;
// Create the task without using any dynamic memory allocation.
xHandle = xTaskCreateStatic(
vTaskCode, // Function that implements the task.
"NAME", // Text name for the task.
STACK_SIZE, // Stack size in words, not bytes.
( void * ) 1, // Parameter passed into the task.
tskIDLE_PRIORITY,// Priority at which the task is created.
xStack, // Array to use as the task's stack.
&xTaskBuffer ); // Variable to hold the task's data structure.
// puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
// been created, and xHandle will be the task's handle. Use the handle
// to suspend the task.
vTaskSuspend( xHandle );
}
c. 创建一个使用MPU进行限制的任务
xTaskCreateRestricted()
相关内存使用动态内存分配
a. vTaskDelete()
删除一个用以上方法创建的任务,任务删除后不会再进入运行态,所以再也不能使用这个任务的句柄,
I. 函数原型
void vTaskDelete( TaskHandle_t xTaskToDelete )
II. 参数
xTaskToDelete: 要删除的任务的任务句柄
III. 返回值
无
a. vTaskSuspend()
挂起一个任务
I. 函数原型
void vTaskSuspend ( TaskHandle_t xTaskToSuspend )
II. 参数
xTaskToSuspend: 要挂起的任务的任务句柄,传入NULL的话,会将调用vTaskSuspend函数的任务挂起
III. 返回值
无
b. vTaskResume()
恢复一个任务的运行,无论一个任务调用了多少次vTaskSuspend,恢复任务值用调用一次vTaskResume
注: INCLUDE_vTaskSuspend must be defined as 1 for this function to be available.
I. 函数原型
void vTaskResume ( TaskHandle_t xTaskToResume )
II. 参数
xTaskToResume: 要恢复的任务的句柄
III. 返回值
无
c. xTaskResumeFromISR()
在中断服务函数中恢复一个任务的运行,与vTaskResume()相同,无论一个任务调用了多少次vTaskSuspend,恢复任务值用调用一次xTaskResumeFromISR()
注:INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be available
I. 函数原型
BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume )
II. 参数
xTaskToResume: 要恢复的任务的句柄
III. 返回值
pdTRUE:恢复的任务优先级比当前运行的任务更高,所以要进行任务切换
pdFALSE:恢复的任务优先级比当前任务低,所以不用进行任务切换
例如:
extern TaskHandle_t Task2Task_Handler;
void EXTI3_IRQHandler(void)
{
BaseType_t YieldRequired;
delay_xms(50); //消抖
if(KEY0==0)
{
YieldRequired=xTaskResumeFromISR(Task2Task_Handler);//恢复任务2
printf("恢复任务2的运行!\r\n");
if(YieldRequired==pdTRUE)
{
/*如果函数xTaskResumeFromISR()返回值为pdTRUE,那么说明要恢复的这个
任务的任务优先级等于或者高于正在运行的任务(被中断打断的任务),所以在
退出中断的时候一定要进行上下文切换!*/
portYIELD_FROM_ISR(YieldRequired);
}
}
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3); //清除中断标志位
}
列表和列表项有完整性检查的成员,原理是在结构体的起始成员和末尾成员,设置两个固定值的变量,一旦有数据越界,必定会改变头尾数据的值,每次插入前检查一次值,就可以确认成员值是否被更改,以下为代码
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
#define listTEST_LIST_ITEM_INTEGRITY( pxItem ) configASSERT( ( ( pxItem )->xListItemIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxItem )->xListItemIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
#define listTEST_LIST_INTEGRITY( pxList ) configASSERT( ( ( pxList )->xListIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxList )->xListIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
a. 列表
/*
* Definition of the type of queue used by the scheduler.
*/
typedef struct xLIST
{
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE UBaseType_t uxNumberOfItems;
ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
} List_t;
注:最后一个成员变量为 Mini列表项类型
b. 列表项
/*
* Definition of the only type of object that a list can contain.
*/
struct xLIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
void * configLIST_VOLATILE pvContainer; /*< Pointer to the list in which this list item is placed (if any). */
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
};
typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */
c. 迷你列表项
struct xMINI_LIST_ITEM
{
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
configLIST_VOLATILE TickType_t xItemValue;
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;
a. vListInitialise()
列表初始化
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
void vListInitialise( List_t * const pxList )
{
/* The list structure contains a list item which is used to mark the
end of the list. To initialise the list, the list end is inserted
as the only list entry. 遍历时使用*/
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
/* The list end value is the highest possible value in the list to
ensure it remains at the end of the list. 确保listEnd处于最尾端*/
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* The list end next and previous pointers point to itself so we know
when the list is empty. */
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
pxList->uxNumberOfItems = ( UBaseType_t ) 0U; //当前列表项为0
/* Write known values into the list if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */完整性检查
//16_Bit_Mcu use 0x5a5a, and 32_Bit one use 0x5a5a5a5aUL
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTE GRITY_CHECK_2_VALU E( pxList );
}
b. vListInitialiseItem()
列表项 初始化,在xTaskCreate(任务创建)的时候,函数会对其初始化
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* Make sure the list item is not recorded as being on a list. */
pxItem->pvContainer = NULL;
/* Write known values into the list item if
configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
c. vListInsert
列表项插入,由于列表是双向的,所以结构为环状
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
/* Only effective when configASSERT() is also defined, these tests may catch
the list data structures being overwritten in memory. They will not catch
data errors caused by incorrect configuration or use of FreeRTOS. */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
/* Insert the new list item into the list, sorted in xItemValue order.
If the list already contains a list item with the same item value then the
new list item should be placed after it. This ensures that TCB's which are
stored in ready lists (all of which have the same xItemValue value) get a
share of the CPU. However, if the xItemValue is the same as the back marker
the iteration loop below will not end. Therefore the value is checked
first, and the algorithm slightly modified if necessary. */
if( xValueOfInsertion == portMAX_DELAY )//如果插入的列表项是最大值,直接插入到列表最尾
{
pxIterator = pxList->xListEnd.pxPrevious;
}
else//若不是最尾项,则找到要插入的位置,以下是按xItemValue 来寻找插入的位置
{
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */
{
/* There is nothing to do here, just iterating to the wanted
insertion position. */
}
}
//找到位置后,做双向指针的插入
pxNewListItem->pxNext = pxIterator->pxNext;
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
/* Remember which list the item is in. This allows fast removal of the
item later. */
pxNewListItem->pvContainer = ( void * ) pxList; //将列表项插入到列表中
( pxList->uxNumberOfItems )++; //列表的成员数加1
}
d. vListInsertEnd
列表项末尾插入 ,这里并不是指插入到列表尾,而是插入到pxIndex所指列表项的前面,因为pxIndex指向的是列表头,所以pxIndex所指项的前一个就是尾巴
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;
/* Only effective when configASSERT() is also defined, these tests may catch
the list data structures being overwritten in memory. They will not catch
data errors caused by incorrect configuration or use of FreeRTOS. */
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );//此处为列表和列表项的完整性检查
/* Insert a new list item into pxList, but rather than sort the list,
makes the new list item the last item to be removed by a call to
listGET_OWNER_OF_NEXT_ENTRY(). */
//以下同样是在双向链表中插入元素的操作,只不过是在pxIndex所指项的前面插入,pxIndex所指的项是任意的,pxIndex所指的列表项就代表列表头
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
/* Only used during decision coverage testing. */
mtCOVERAGE_TEST_DELAY();
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* Remember which list the item is in. */
pxNewListItem->pvContainer = ( void * ) pxList; //将列表项插入到列表中
( pxList->uxNumberOfItems )++; //列表的成员数加1
}
f. uxListRemove
列表项删除
返回值:删除列表项后,列表剩余的列表项的数量
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/* The list item knows which list it is in. Obtain the list from the list
item. */确定这个列表项是哪个列表的
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
//双向链表中,删除这个节点的操作
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* Only used during decision coverage testing. */用来输出调试信息
mtCOVERAGE_TEST_DELAY();
/* Make sure the index is left pointing to a valid item. */
if( pxList->pxIndex == pxItemToRemove )//如果删掉的是pxIndex所指的节点的话,则把pxIndex指向删除的节点的前一个节点
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();//用来输出调试信息
}
pxItemToRemove->pvContainer = NULL; //被删除的列表项要清除 所归属的列表
( pxList->uxNumberOfItems )--; //该列表的项目数减少
return pxList->uxNumberOfItems; //返回这个列表当前列表项数目
}
g. listGET_OWNER_OF_NEXT_ENTRY
列表的遍历
此函数用来用于从多个优先级的就绪任务中查找下一个要运行的任务
FREE_RTOS提供了一个宏定义形式的函数
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
{ \
List_t * const pxConstList = ( pxList ); \
/* Increment the index to the next item and return the item, ensuring */ \
/* we don't return the marker used at the end of the list. */ \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; //pxIndex指向下一个列表项 \
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
{ //如果pxIndex指向了列表的END项时,则代表到了列表的末尾,到了末尾的话,就跳过END项,指向列表头的列表项 \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
} \
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; //将指向的列表项的owner赋给TCB \
}
1. UBaseType_t uxTaskPriorityGet ( TaskHandle_t xTask )
功能: 获取对应任务的任务优先级
参数: 任务的句柄
返回值: 该任务的优先级
#if ( INCLUDE_uxTaskPriorityGet == 1 )
UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask )
{
TCB_t *pxTCB;
UBaseType_t uxReturn;
taskENTER_CRITICAL();
{
/* If null is passed in here then it is the priority of the that
called uxTaskPriorityGet() that is being queried. */
pxTCB = prvGetTCBFromHandle( xTask );
uxReturn = pxTCB->uxPriority;
}
taskEXIT_CRITICAL();
return uxReturn;
}
2. void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
功能: 将任务设定为对应的优先级
参数:
a. xTask:任务的句柄
b. uxNewPriority :新的任务优先级
3. UBaseType_t uxTaskGetSystemState ( TaskStatus_t * const pxTaskStatusArray,
const UBaseType_t uxArraySize,
uint32_t * const pulTotalRunTime )
功能: 获取系统中所有任务的任务状态
参数:
a. pxTaskStatusArray:存储任务状态的数组
typedef struct xTASK_STATUS
{
TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */
const char *pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
UBaseType_t xTaskNumber; /* A number unique to the task. */
eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */
UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */
UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */
uint32_t ulRunTimeCounter; /* 任务总共运行时间 The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */
StackType_t *pxStackBase; /* 指向任务堆栈 Points to the lowest address of the task's stack area. */
uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */栈空间历史剩余最小的容量
} TaskStatus_t;
b. uxArraySize:保存任务状态组的数组的大小
c. pulTotalRunTime :若configGENERATE_RUN_TIME_STATS为1 时,此参数用来保存系统的总的运行时间
返回值: 统计到的任务状态的个数
第一个创建的任务为vTaskStartScheduler()开始之前起创建的任务,在vTaskStartScheduler()中,会创建IDLE任务,以及定时器服务任务,所以任务的编号如下:
UBaseType_t uxTaskGetNumberOfTasks( void )
{
/* A critical section is not required because the variables are of type
BaseType_t. */
return uxCurrentNumberOfTasks;
}
/* Task states returned by eTaskGetState. */
typedef enum
{
eRunning = 0, /* A task is querying the state of itself, so must be running. */
eReady, /* The task being queried is in a read or pending ready list. */
eBlocked, /* The task being queried is in the Blocked state. */
eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */
eInvalid /* Used as an 'invalid state' value. */
} eTaskState;
6. TaskHookFunction_t xTaskGetApplicationTaskTag ( TaskHandle_t xTask )
功能: 获取任务的Tag值,任务句柄中有个pxTaskTag变量来保存任务标签值的,标签的功能由用户自己决定,这个函数获取的是任务的标签值的(系统内核不会使用该标签值),如果要使用下面的宏定义要开启
#if ( configUSE_APPLICATION_TASK_TAG == 1 )
TaskHookFunction_t pxTaskTag;
#endif
参数: xTask获取的任务的任务句柄,若为NULL就获取正在运行的任务标签值
返回值: 任务的标签值
7. TaskHandle_t xTaskGetHandle( const char *pcNameToQuery )
功能: 获取当前任务的任务句柄,使用的话,下述的宏定义要打开
参数: 无
返回值: 当前任务的任务句柄
#if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) )
TaskHandle_t xTaskGetCurrentTaskHandle( void )
{
TaskHandle_t xReturn;
/* A critical section is not required as this is not called from
an interrupt and the current TCB will always be the same for any
individual execution thread. */
xReturn = pxCurrentTCB;
return xReturn;
}
#endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */
8. TaskHandle_t xTaskGetHandle( const char *pcNameToQuery )
功能: 根据任务名称获取任务句柄
参数: 任务名,字符串
返回值: 返回任务句柄,若为NULL,则说明没有这个任务
9. TaskHandle_t xTaskGetIdleTaskHandle ( void )
功能: 获取空闲任务的任务句柄,使用时宏定义要打开
参数: 无
返回值: 返回空闲任务的任务句柄
#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 )
TaskHandle_t xTaskGetIdleTaskHandle( void )
{
/* If xTaskGetIdleTaskHandle() is called before the scheduler has been
started, then xIdleTaskHandle will be NULL. */
configASSERT( ( xIdleTaskHandle != NULL ) );
return xIdleTaskHandle;
}
#endif /* INCLUDE_xTaskGetIdleTaskHandle */
10. UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask )
功能: 检查任务创建完成到现在栈空间历史剩余最小值,值越小溢出的可能越大,可以用于代码调试阶段,出货时不用
参数: 要查询的任务的任务句柄,使用NULL的话,则查询调用此函数的任务本身
返回值: 返回剩余量最小值
#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 )
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask )
{
TCB_t *pxTCB;
uint8_t *pucEndOfStack;
UBaseType_t uxReturn;
pxTCB = prvGetTCBFromHandle( xTask );
#if portSTACK_GROWTH < 0
{
pucEndOfStack = ( uint8_t * ) pxTCB->pxStack;
}
#else
{
pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack;
}
#endif
uxReturn = ( UBaseType_t ) prvTaskCheckFreeStackSpace( pucEndOfStack );
return uxReturn;
#endif /* INCLUDE_uxTaskGetStackHighWaterMark */
11. eTaskState eTaskGetState( TaskHandle_t xTask )
功能: 查询任务的运行状态
参数: 要查询的任务的任务句柄
返回值: 返回为eTaskState类型,如下
typedef enum
{
eRunning = 0, /* A task is querying the state of itself, so must be running. */
eReady, /* The task being queried is in a read or pending ready list. */
eBlocked, /* The task being queried is in the Blocked state. */
eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */
eInvalid /* Used as an 'invalid state' value. */
} eTaskState;
12. char *pcTaskGetName( TaskHandle_t xTaskToQuery )
功能: 查询对应任务的任务名
参数: 要查询的任务的任务句柄,NULL的话表示查询调用此函数的任务的名字
返回值: 返回对应任务的任务名
13. TickType_t xTaskGetTickCount( void )
功能: 查询任务调度器启动到现在,计数器xTickCount的值。xTickCount是系统的时钟节拍,每次定时器中断,xTickCount就会加1,定时器一秒中断几次取决于宏定义 configTICK_RATE_HZ。
参数: 无
返回值: 时间计数器xTickCount的值
14. void vTaskGetRunTimeStats( char *pcWriteBuffer )
功能: 提供一个表格,记录了每个任务运行的时间(不是秒,是由定时器的周期决定的变量,比如一个中断周期是50us,那么5就是 5 x 50us) 和 所占总时间的比例,
要使用的话,要打开一下几个宏定义:
configGENERATE_RUN_TIME_STATS,configUSE_STATS_FORMATTING_FUNCTIONS,
并且要实现一下几个宏定义:
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(),此宏定义要初始化一个定时器(中断频率要比系统时钟高,为系统时钟的10~20倍),比如:
void ConfigureTimeForRunTimeStats(void)
{
//定时器3初始化,定时器时钟为108M,分频系数为108-1,所以定时器3的频率
//为108M/108=1M,自动重装载为50-1,那么定时器周期就是50us
FreeRTOSRunTimeTicks=0;
TIM3_Init(50-1,108-1); //初始化TIM3
}
以及
portGET_RUN_TIME_COUNTER_VALUE()
#define portGET_RUN_TIME_COUNTER_VALUE() Runtime
或者portALT_GET_RUN_TIME_COUNTER_VALUE(Time)
这两个宏定义用来获取当前时基的时间值
参数:
pcWriteBuffer:用来保存任务时间信息的buffer
返回值: 无
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define portENTER_CRITICAL() vPortEnterCritical()
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS(); //除能5~15级的中断
uxCriticalNesting++; //使用该全局变量,是因为防止有多层临界区嵌套的时候,一旦调用退出临界区,就打乱了其他临界段的保护,只有全部临界段代码退出后才打开中断
/* This is not the interrupt safe version of the enter critical function so
assert() if it is being called from an interrupt context. Only API
functions that end in "FromISR" can be used in an interrupt. Only assert if
the critical nesting count is 1 to protect against recursive calls if the
assert function also uses a critical section. */
if( uxCriticalNesting == 1 )
{
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); //断言函数
}
}
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS(); //使能5~15级的中断
}
}
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
mrs ulReturn, basepri //先读出BASEPRI的值,保存到ulReturn中
cpsid i
msr basepri, ulNewBASEPRI //将屏蔽的优先级数写到BASEPRI中,比如写入4的话,0~3优先级的中断会执行,而4~15级的中断将被屏蔽
dsb
isb
cpsie i
}
return ulReturn; //返回之前的优先级,退出时将使用到这个值
}
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )
#define portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) vPortSetBASEPRI( x )
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
/* Barrier instructions are not used as this function is only used to
lower the BASEPRI value. */
msr basepri, ulBASEPRI //将进入临界区时保存的值,重新写到BASEPRI寄存器中,恢复进入之前的状态
}
}
ISR中使用的临界区函数示例:
void TIMCallBack()
{
uint32_t uiState;
uiState = taskENTER_CRITICAL_FROM_ISR();
/********要执行的函数********/
taskEXIT_CRITICAL_FROM_ISR(uiState);
}
#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS()
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
cpsid i
msr basepri, ulNewBASEPRI
dsb
isb
cpsie i
}
}
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) //写0即打开所有中断
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
/* Barrier instructions are not used as this function is only used to
lower the BASEPRI value. */
msr basepri, ulBASEPRI
}
}
#if ( INCLUDE_vTaskDelay == 1 )
void vTaskDelay( const TickType_t xTicksToDelay )// 延时多少个时间节拍
{
BaseType_t xAlreadyYielded = pdFALSE;
/* A delay time of zero just forces a reschedule. */
if( xTicksToDelay > ( TickType_t ) 0U )
{
configASSERT( uxSchedulerSuspended == 0 );
vTaskSuspendAll();
{
traceTASK_DELAY();
/* A task that is removed from the event list while the
scheduler is suspended will not get placed in the ready
list or removed from the blocked list until the scheduler
is resumed.
This task cannot be in an event list as it is the currently
executing task. */
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); //此时就绪列表发生了变化
}
xAlreadyYielded = xTaskResumeAll();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Force a reschedule if xTaskResumeAll has not already done so, we may
have put ourselves to sleep. */
if( xAlreadyYielded == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* INCLUDE_vTaskDelay */
相关函数:
if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/* The current task must be in a ready list, so there is no need to
check, and the port reset macro can be called directly. */
portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );//重置任务的优先级
}
SysTick为倒计数类型的计数器
a. 先初始化SysTick
//初始化延迟函数
//当使用ucos的时候,此函数会初始化ucos的时钟节拍
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
u32 reload;
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
fac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用
reload=SYSCLK; //每秒钟的计数次数 单位为K
reload*=1000000/configTICK_RATE_HZ; //根据delay_ostickspersec设定溢出时间,周期50ms转换为频率为20HZ(1秒20个周期),被除数为一秒钟的计数,这个值不能大于77ms
//reload为24位寄存器,最大值:16777216,在216M下,约合77.7ms左右
fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延时的最少单位,OS的系统节拍
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
SysTick->LOAD=reload; //每1/OS_TICKS_PER_SEC秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
}
b. 注册中断服务函数
//systick中断服务函数,使用OS时用到
void xPortSysTickHandler( void )
{
/* The SysTick runs at the lowest interrupt priority, so when this interrupt
executes all interrupts must be unmasked. There is therefore no need to
save and then restore the interrupt mask value as its value is already
known - therefore the slightly faster vPortRaiseBASEPRI() function is used
in place of portSET_INTERRUPT_MASK_FROM_ISR(). */
vPortRaiseBASEPRI();
{
/* Increment the RTOS tick. */
if( xTaskIncrementTick() != pdFALSE )
{
/* A context switch is required. Context switching is performed in
the PendSV interrupt. Pend the PendSV interrupt. */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}
vPortClearBASEPRIFromISR();
}
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
HAL_IncTick();
}
c. xTaskIncrementTick()的功能
I. 若任务调度器未被挂起
xTickCount系统节拍加1
若系统节拍计数溢出,则交换两个延时列表(overflow和正常的列表)
若系统节拍计数大于 延时列表中最近要被唤醒的任务的节拍数,判断各个任务是不是到了退出阻塞态,就绪态(State)的时候,判断任务是否要退出各个event(队列,信号量),到了就退出对应的状态,并且把这个任务从对应的列表移除出来,并把合适的任务放到readylist中。
II. 若任务调度器被挂起
++uxPendedTicks
在xTaskResumeAll中,会调用这个变量次数的xTaskIncrementTick(),这样xTickCount就会恢复,并且取消任务的阻塞
用于任务和任务,任务和中断之间的传递消息,
队列长度:数据条数,创建时要指定大小和队列的长度
发送数据到队列是通过拷贝
出队阻塞: 任务从队列中读取消息可指定一个阻塞时间,阻塞时间设定为0的话,读不到数据,就马上返回执行其他程序;设定为0~portMAX_DELAY的话,任务没获取到消息就等待阻塞时间,若设置为portMAX_DELAY的话,就会一直等待,直到收到数据
入队阻塞: 当队列满时,等待有没位置,与出队阻塞类似
队列操作过程:
typedef struct QueueDefinition
{
int8_t *pcHead; /*< Points to the beginning of the queue storage area. */
int8_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */
int8_t *pcWriteTo; /*< Points to the free next place in the storage area. 空闲的块 */
union /* Use of a union is an exception to the coding standard to ensure two mutually exclusive structure members don't appear simultaneously (wasting RAM). */
{
int8_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. 做出队列项的地址,作为队列时,这个成员是下个读取得地址*/
UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. 作为递归互斥信号量时,用作统计调用的次数*/
} u;
List_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. 存储处于因队列满而无法发送消息进来的任务*/
List_t xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. 存储处于因队空而无法接收到消息进来的任务*/
volatile UBaseType_t uxMessagesWaiting;/*< The number of items currently in the queue. 目前队列中有多少消息*/
UBaseType_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. 队列的长度,能存多少消息*/
UBaseType_t uxItemSize; /*< The size of each items that the queue will hold. 每个消息的大小,单位字节*/
volatile int8_t cRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
volatile int8_t cTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
(2)创建队列的静态方法
QueueHandle_t xQueueGenericCreateStatic ( const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
uint8_t * pucQueueStorage,
StaticQueue_t *pxStaticQueue,
const uint8_t ucQueueType )
QueueHandle_t xQueueGenericCreate ( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
功能: 创建队列的动态方法,内核自动分配内存
参数:
a. uxQueueLength: 最大的消息数量,队列的项目数
b. uxItemSize: 每条消息的字节数
c . ucQueueType 表明创建队列的用途
返回值: 队列的句柄,0为失败
#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U )
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
#endif
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
{
Queue_t *pxNewQueue;
size_t xQueueSizeInBytes;
uint8_t *pucQueueStorage;
configASSERT( uxQueueLength > ( UBaseType_t ) 0 );
if( uxItemSize == ( UBaseType_t ) 0 ) //用作信号量时,因为只关心有多少条消息
{
/* There is not going to be a queue storage area. */
xQueueSizeInBytes = ( size_t ) 0;
}
else//对应队列
{
/* Allocate enough space to hold the maximum number of items that
can be in the queue at any time. */
xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. 判断需要多大的内存*/
}
pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );//申请存储消息的内存
if( pxNewQueue != NULL )//判断是否申请成功
{
/* Jump past the queue structure to find the location of the queue
storage area. */
pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t );//得出数据存储区的地址
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
/* Queues can be created either statically or dynamically, so
note this task was created dynamically in case it is later
deleted. */
pxNewQueue->ucStaticallyAllocated = pdFALSE;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );//初始化新队列
}
return pxNewQueue;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
队列创建相关函数:
prvInitialiseNewQueue()
函数功能:
(1)xQueueSend( xQueue, pvItemToQueue, xTicksToWait )
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
(2)xQueueSendToBack ( xQueue, pvItemToQueue, xTicksToWait )
#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
(3)xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait )
#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT )
(4)xQueueOverwrite()
#define xQueueOverwrite( xQueue, pvItemToQueue ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE )
(5)最终调用的xQueueGenericSend()
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
函数功能:
当给出互斥型信号量时,优先级的继承处理
1.
中断入队函数:
(1)xQueueSendFromISR()
#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
(2)xQueueSendToBackFromISR()
#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
(3)xQueueSendToFrontFromISR()
#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT )
xQueueSendFromISR(QueueKey,USART_RX_BUF,&xHigherPriorityTaskWoken);//向队列中发送数据
USART_RX_STA=0;
memset(USART_RX_BUF,0,USART_REC_LEN);//清除数据接收缓冲区USART_RX_BUF,用于下一次数据接收
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换
(4)xQueueOverwriteFromISR()
#define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE )
(5)xQueueGenericSendFromISR()
函数功能:
(1)xQueueReceive()
收到的数据是一个字节(大小看队列的元素类型,long型的为两个字节)
(3)xQueueGenericReceive()
函数功能:
(4)xQueueReceiveFromISR()
函数功能:
上锁:
cRxLock: 获取Take消息(出队)上锁
cTxLock: 给出Give消息(入队)上锁
#define prvLockQueue( pxQueue ) \
taskENTER_CRITICAL(); \
{ \
if( ( pxQueue )->cRxLock == queueUNLOCKED ) \
{ \
( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED; \
} \
if( ( pxQueue )->cTxLock == queueUNLOCKED ) \
{ \
( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED; \
} \
} \
taskEXIT_CRITICAL()
解锁:
prvUnlockQueue()
1. 二值信号量:
Give没有阻塞时间
Take可以设定阻塞时间
相当于一个只有一个队列的队列项
任务与任务的同步
中断与任务的同步
a. 二值信号量创建函数
(1)xSemaphoreCreateBinary()
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
#endif
旧版本函数功能:
当给出互斥型信号量时,优先级的继承处理
(2)xSemaphoreGiveFromISR()
函数功能:
函数功能:
当获取互斥型信号量时,优先级的继承处理
(2)xSemaphoreCreateCountingStatic()
uxSemaphoreGetCount( xSemaphore )
3. 互斥型
互斥型信号量有优先级继承的特性,当一个互斥信号量被低优先级的任务使用,这时有个高优先级的任务也要获取这个信号量,然而会进入阻塞态,不过,这个高优先级的任务会将低优先级的任务提到和自己一样的优先级
互斥量不能用于中断服务函数中,因为互斥信号量的优先级继承机制,只在任务中继承,中断无法继承;并且中断服务函数不能因为等待互斥信号量而设置阻塞时间(中断不能进入阻塞态)
当低优先级的任务(GIVE)交出信号量后,其优先级会恢复到原来的优先级,并且不能在中断服务函数中使用 互斥 和 递归互斥信号量
a. 创建信号量
(1)xSemaphoreCreateMutex()
信号量创建成功后,默认成员为1,所以不用像二值信号量一样,重新赋初值
函数功能:
4. 递归互斥信号量
要使用的话,宏定义要打开,递归互斥信号量的给出和获取都有自己的函数,与互斥、二值和计数信号量获取和发送的时候调用的不同
a. 创建信号量
(1)xSemaphoreCreateRecursiveMutex()
函数功能:
#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )
(2)xSemaphoreCreateRecursiveMutexStatic()
(3)xSemaphoreGiveRecursive()
给出递归信号量,调用给出函数的第一次,会将信号量入队,以后再调用只是增加一个变量的值
函数功能:
定时器服务函数中,不能使用有阻塞的函数,例如vTaskDelay
要使用的话,要将宏定义设为1
任务的堆栈要根据回调函数来设置
(2)xTimerCreateStatic()
![在这里插入图片描述](https://img-blog.csdnimg.cn/20181111130350670.png)
事件位(事件标志)
事件标志组
EventBits_t的数据类型由以下宏定义决定
数据位为32位,一共能存储24位数据(32-8)
数据位为16位,一共能存储16位数据(16-8)
#if( configUSE_16_BIT_TICKS == 1 )
typedef uint16_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffff
#else
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
/* 32-bit tick type on a 32-bit architecture, so reads of the tick count do
not need to be guarded with a critical section. */
#define portTICK_TYPE_IS_ATOMIC 1
#endif
typedef TickType_t EventBits_t;
a. 事件标志组的创建
(1)xEventGroupCreate()
(2)xEventGroupCreateStatic()
(2)xEventGroupClearBitsFromISR()
c. 获取事件位
(1)xEventGroupGetBits()
(2)xEventGroupGetBitsFromISR()
d. 等待指定的事件位
xEventGroupWaitBits()
要使用的话,要打开宏定义,任务中有个32位的ulNotifiedValue变量
队列
事件标志组
模拟计数变量(实现不了资源管理,因为是单个任务对单个任务的)
简单的单个任务对单个任务的功能
速度更快
接收任务可以进入阻塞态,发送不会进入阻塞
(1)xTaskNotify()
模拟队列用,如果使用不覆写通知值的形式,假如通知值还未被处理,则会返回pdFAIL
(3)xTaskNotifyGive()
用于模拟计数型信号量 或者 二值信号量
(4)vTaskNotifyGiveFromISR()
用于模拟计数型信号量 或者 二值信号量
(5)xTaskNotifyAndQuery()
模拟队列用,用来传数值
(6)xTaskNotifyAndQueryFromISR()
模拟队列用,用来传数值
(7)xTaskGenericNotify
(8)xTaskGenericNotifyFromISR()
(1)ulTaskNotifyTake()
只有在任务的通知值不为0的情况下,进入阻塞态,为非0值的话,对通知值减1或者清零
函数功能:
运用处理空闲任务的时候,就进入低功耗模式,需要处理应用层代码,再唤醒
一般在空闲任务的钩子函数中执行低功耗的处理,比如进入处理器的低功耗模式,关闭外设时钟,降低系统主频等,
因为操作系统的心跳节拍会频繁触发中断,所以进入低功耗的时候,也要关闭时钟,但关闭时钟对任务的时间又造成影响
低功耗定时器来补偿关闭的时候,对任务的影响
空闲任务的优先级最低(为1),当任务要删除自己时,就需要在空闲任务中释放任务的资源
空闲任务中不能调用任何阻塞的函数,
可以使用通用的低功耗模式
使用pvPortMalloc() 和 vPortFree()
vTaskStartScheduler
__asm void prvStartFirstTask( void )
{
PRESERVE8
/* Use the NVIC offset register to locate the stack. */获取MSP指针
ldr r0, =0xE000ED08 :VTOR向量表偏移寄存器的地址,这个寄存器现在指向0x80000000(向量表的地址)
ldr r0, [r0] :读取完后,r0 = 0x80000000
ldr r0, [r0] :获取0x8000000(MSP)的地址的数据,即主栈指针MSP的初始值
/* Set the msp back to the start of the stack. */
msr msp, r0 :将r0赋值给MSP
/* Globally enable interrupts. */
cpsie i :开启中断,写0是除能(即打开中断,写1为 屏蔽 不包括fault和NMI的中断)
cpsie f :开启异常,写0是开启异常,写1位屏蔽比-1级数更低的中断优先级(硬FAULT也被屏蔽)
dsb :数据同步隔离
isb :指令同步屏障
/* Call SVC to start the first task. */
svc 0 :运行SVC产生SVC中断,调用该代号的服务函数,写SVC 0就调用 0号系统服务,
只用第一次运行调用SVC,以后都使用PendSv,运行这个指令后,启动第一个任务
nop
nop
}
以下为SVC 0指令调用的函数
__asm void vPortSVCHandler( void )
{
PRESERVE8
/* Get the location of the current TCB. */
ldr r3, =pxCurrentTCB :获取第一个任务的栈,pxCurrentTCB的第一个成员为栈顶指针
ldr r1, [r3] :这样R1就为任务控制块的地址
ldr r0, [r1] :将任务控制块第一个字段pxTopOfStack所指位置的数据(现场的值)
/* Pop the core registers. */
ldmia r0!, {r4-r11, r14} :为多加载指令,从r0的地址读取多个字,每读取一个字后,地址增加一个字,
这个指令将R0地址的数据赋给了R4到R11以及R14寄存器,
加载完后(数据出栈的过程),此时栈顶指针r0指向新的栈顶
(R0~R3等自动恢复),这些恢复的内容,应该是从堆内存中模拟的栈存储结构
msr psp, r0 :将新的栈顶指针赋值给PSP(进程栈指针)
isb :指令同步屏障
mov r0, #0 :设置寄存器R0为0
msr basepri, r0 :打开中断
bx r14 :自动恢复寄存器R0~R3,R12,LR,PC和xPSR的值,进程栈使用PSP,执行PC中保存的任务函数,然后FREERTOS的任务开始运行
}
R14设为0xfffffffd的原因,返回线程模式,并使用线程堆栈 SP = PSP
R14 为 EXC_RETURN = 0xfffffffd,意为出栈后使用PSP(MSP为中断服务函数使用,当STM32没有使用操作系统时,只有使用MSP)
上下文切换的场合
xPortPendSVHandler( void )
vTaskSwitchContext()
硬件方法:
用portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );取得uxTopPriority最高优先级是第几级,clz为硬件计算前导0个数,比如现在最高优先级的任务是29级,但是获取前导0个数为2,所以需要31-2得出真正的优先级
#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) )
确认对应优先级的就绪列表中有任务(每个优先级都有一个就绪列表)
使用listGET_OWNER_OF_NEXT_ENTRY()获取该列表的下个成员(下个运行的任务),取得下个任务的TCB,将其保存到pxCurrentTCB中
通用方法:
获取最高优先级的任务,直接读取uxTopReadyPriority(每次任务调度都会更新这个变量)
使用listGET_OWNER_OF_NEXT_ENTRY()获取该列表的下个成员(下个运行的任务),取得下个任务的TCB,将其保存到pxCurrentTCB中
#define taskSELECT_HIGHEST_PRIORITY_TASK()
{
UBaseType_t uxTopPriority = uxTopReadyPriority;
/* Find the highest priority queue that contains ready tasks. /
while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) )
{
configASSERT( uxTopPriority );
–uxTopPriority;
}
/ listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of
the same priority get an equal share of the processor time. /
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );
uxTopReadyPriority = uxTopPriority;
} / taskSELECT_HIGHEST_PRIORITY_TASK */
heap1.c
内存申请后,就不再释放,所以没有实现pvFree()
a. 内存申请
pvMalloc():
heap2.c
若任务每次分配和释放的内存不是一定的,不要使用这个
a. 内存初始化
prvHeapInit()
b. 内存申请
pvMalloc():
static void prvHeapInit( void )
{
BlockLink_t *pxFirstFreeBlock;
uint8_t *pucAlignedHeap;
/* Ensure the heap starts on a correctly aligned boundary. */
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
/* xStart is used to hold a pointer to the first item in the list of free
blocks. The void cast is used to prevent compiler warnings. */
xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
xStart.xBlockSize = ( size_t ) 0;
/* xEnd is used to mark the end of the list of free blocks. */
xEnd.xBlockSize = configADJUSTED_HEAP_SIZE;
xEnd.pxNextFreeBlock = NULL;
/* To start with there is a single free block that is sized to take up the
entire heap space. */
pxFirstFreeBlock = ( void * ) pucAlignedHeap;
pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE;
pxFirstFreeBlock->pxNextFreeBlock = &xEnd;
}
c. 空闲内存块回收函数:
prvInsertBlockIntoFreeList()
d. 内存释放:
vPortFree()
heap3.c
要根据 .s 文件中的HEAP_Size设定堆的大小
a. 内存申请
pvPortMalloc()
b. 内存释放
pvPortFree()
heap4.c
多了处理内存碎片的功能
a. 内存初始化
prvHeapInit()
b. 空闲内存块插入函数:
prvInsertBlockIntoFreeList()
c. 内存申请函数:
pvPortMalloc()
d. 内存释放函数:
pvPortFree()