FreeRTOS初始化中断需要哪些c语言结构体初始化?

没有更多推荐了,
不良信息举报
举报内容:
FreeRTOS内核详解—-Queue
举报原因:
原文地址:
原因补充:
最多只允许输入30个字
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!没有更多推荐了,
不良信息举报
举报内容:
学习FreeRTOS(2):初始化阶段的中断状态
举报原因:
原文地址:
原因补充:
最多只允许输入30个字
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!freeRTOS最吸引我的地方,就是他的任务间通信、任务间同步所用的概念,全部都建立在“队列”的基础之上。
只要抓住队列的实现,对其他的就比较清晰了。
对任务状态的管理,建立在“列表”的基础之上。
Queue_tqueue.h步骤:xQueueCreate( uxQueueLength, uxItemSize ) -&
xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
prvInitialiseNewQueue
xQueueGenericReset|+-- vListInitialise( &( pxQueue-&xTasksWaitingToSend ) );+-- vListInitialise( &( pxQueue-&xTasksWaitingToReceive ) );
本节只有创建队列、初始化队列两部分。参考地址:http://blog.csdn.net/zhzht/article/details/
typedef struct QueueDefinition
int8_t *pcH
/* 指向队列存储区起始位置,即第一个队列项 */
int8_t *pcT
/* 指向队列存储区结束后的下一个字节 */
int8_t *pcWriteTo;
/* 指向下队列存储区的下一个空闲位置 */
/* 使用联合体用来确保两个互斥的结构体成员不会同时出现 */
int8_t *pcReadF
/* 当结构体用于队列时,这个字段指向出队项目中的最后一个. */
UBaseType_t uxRecursiveCallC/* 当结构体用于互斥量时,用作计数器,保存递归互斥量被"获取"的次数. */
List_t xTasksWaitingToS
/* 因为等待入队而阻塞的任务列表,按照优先级顺序存储 */
List_t xTasksWaitingToR
/* 因为等待队列项而阻塞的任务列表,按照优先级顺序存储 */
volatile UBaseType_t uxMessagesW/*& 当前队列的队列项数目 */
UBaseType_t uxL
/* 队列项的数目 */
UBaseType_t uxItemS
/* 每个队列项的大小 */
volatile BaseType_t xRxL
/* 队列上锁后,存储从队列收到的列表项数目,如果队列没有上锁,设置为queueUNLOCKED */
volatile BaseType_t xTxL
/* 队列上锁后,存储发送到队列的列表项数目,如果队列没有上锁,设置为queueUNLOCKED */
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetC
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueN
uint8_t ucQueueT
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
uint8_t ucStaticAllocationF
typedef xQUEUE Queue_t;
* Type by which queues are referenced. 【queue被引用的句柄】 For example, a call to xQueueCreate()
* returns an QueueHandle_t variable that can then be used as a parameter to
* xQueueSend(), xQueueReceive(), etc.
typedef void * QueueHandle_t;
/* For internal use only. */
queueSEND_TO_BACK
( ( BaseType_t ) 0 )
queueSEND_TO_FRONT
( ( BaseType_t ) 1 )
queueOVERWRITE
( ( BaseType_t ) 2 )
/* For internal use only.
These definitions *must* match those in queue.c. */
#define queueQUEUE_TYPE_BASE
( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_SET
( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_MUTEX
( ( uint8_t ) 1U )
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE
( ( uint8_t ) 2U )
#define queueQUEUE_TYPE_BINARY_SEMAPHORE
( ( uint8_t ) 3U )
#define queueQUEUE_TYPE_RECURSIVE_MUTEX
( ( uint8_t ) 4U )
QueueHandle_t xQueueCreate(
UBaseType_t uxQueueLength,
UBaseType_t uxItemSize
* Creates a new queue instance, and returns a handle by which the new queue
* can be referenced.
* Internally, within the FreeRTOS implementation, queues use two blocks of
The first block is used to hold the queue's data structures.
* second block is used to hold items placed into the queue.
If a queue is
* created using xQueueCreate() then both blocks of memory are automatically
* dynamically allocated inside the xQueueCreate() function.
If a queue is created using
* xQueueCreateStatic() then the application writer must provide the memory that
* will get used by the queue.
xQueueCreateStatic() therefore allows a queue to
* be created without using any dynamic memory allocation.
* @param uxQueueLength The maximum number of items that the queue can contain.
* @param uxItemSize The number of bytes each item in the queue will require.
* Items are queued by copy, not by reference, so this is the number of bytes
* that will be copied for each posted item.
Each item on the queue must be
* the same size.
* @return If the queue is successfully create then a handle to the newly
* created queue is returned.
If the queue cannot be created then 0 is
* returned.
* Example usage:
struct AMessage
char ucMessageID;
char ucData[ 20 ];
void vATask( void *pvParameters )
QueueHandle_t xQueue1, xQueue2;
// Create a queue capable of containing 10 uint32_t values.
xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
if( xQueue1 == 0 )
// Queue was not created and must not be used.
// Create a queue capable of containing 10 pointers to AMessage structures.
// These should be passed by pointer as they contain a lot of data.
xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
if( xQueue2 == 0 )
// Queue was not created and must not be used.
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xQueueCreate( uxQueueLength, uxItemSize )
xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
QueueHandle_t xQueueCreateStatic(
UBaseType_t uxQueueLength,
UBaseType_t uxItemSize,
uint8_t *pucQueueStorageBuffer,
StaticQueue_t *pxQueueBuffer
* @param pucQueueStorageBuffer 【保存队列存储区】
If uxItemSize is not zero then
* pucQueueStorageBuffer must point to a uint8_t array that is at least large
* enough to hold the maximum number of items that can be in the queue at any
* one time - which is ( uxQueueLength * uxItemsSize ) bytes.
If uxItemSize is
* zero then pucQueueStorageBuffer can be NULL.
& &* @param pxQueueBuffer Must point to a variable of type StaticQueue_t, which& &* will be used to hold the queue's data structure.【保存队列结构体】
* Example usage:
struct AMessage
char ucMessageID;
char ucData[ 20 ];
#define QUEUE_LENGTH 10
#define ITEM_SIZE sizeof( uint32_t )
// xQueueBuffer will hold the queue structure.
StaticQueue_t xQueueBuffer;
// ucQueueStorage will hold the items posted to the queue.
Must be at least
// [(queue length) * ( queue item size)] bytes long.
uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ];
void vATask( void *pvParameters )
QueueHandle_t xQueue1;
// Create a queue capable of containing 10 uint32_t values.
xQueue1 = xQueueCreate( QUEUE_LENGTH, // The number of items the queue can hold.
// The size of each item in the queue
&( ucQueueStorage[ 0 ] ), // The buffer that will hold the items in the queue.
&xQueueBuffer ); // The buffer that will hold the queue structure.
// The queue is guaranteed to be created successfully as no dynamic memory
// allocation is used.
Therefore xQueue1 is now a handle to a valid queue.
// ... Rest of task code.
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
#define xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer )
xQueueGenericCreateStatic( ( uxQueueLength ), ( uxItemSize ), ( pucQueueStorage ), ( pxQueueBuffer ), ( queueQUEUE_TYPE_BASE ) )
#endif /* configSUPPORT_STATIC_ALLOCATION */
&分析动态创建函数&&xQueueGenericCreate()&
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType )
Queue_t *pxNewQ
size_t xQueueSizeInB
uint8_t *pucQueueS
configASSERT( uxQueueLength & ( UBaseType_t ) <span style="color: # );
if( uxItemSize == ( UBaseType_t ) <span style="color: # )
/* There is not going to be a queue storage area. */
xQueueSizeInBytes = ( size_t ) <span style="color: #;
/* 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 */
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. 【pdFALSE 标记他是动态创建的】*/
pxNewQueue-&ucStaticallyAllocated = pdFALSE;
#endif /* configSUPPORT_STATIC_ALLOCATION */
prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
return pxNewQ
freeRTOSconfig.h
#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
&继续看prvInitaliseNewQueue
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue )
/* Remove compiler warnings about unused parameters should
configUSE_TRACE_FACILITY not be set to 1. */
( void ) ucQueueT
if( uxItemSize == ( UBaseType_t ) <span style="color: # )
/* No RAM was allocated for the queue storage area, but PC head cannot
be set to NULL because NULL is used as a key to say the queue is used as
Therefore just set pcHead to point to the queue as a benign(good)
value that is known to be within the memory map. */
pxNewQueue-&pcHead = ( int8_t * ) pxNewQ
【做个标记而已,没有实际存储区】
/* Set the head to the start of the queue storage area. */
pxNewQueue-&pcHead = ( int8_t * ) pucQueueS
/* Initialise the queue members as described where the queue type is
defined. */
pxNewQueue-&uxLength = uxQueueL
pxNewQueue-&uxItemSize = uxItemS
( void ) xQueueGenericReset( pxNewQueue, pdTRUE );
//&-- 复位队列
#if ( configUSE_TRACE_FACILITY == 1 )【略】
pxNewQueue-&ucQueueType = ucQueueT
#endif /* configUSE_TRACE_FACILITY */
#if( configUSE_QUEUE_SETS == 1 )【略】
pxNewQueue-&pxQueueSetContainer = NULL;
#endif /* configUSE_QUEUE_SETS */
traceQUEUE_CREATE( pxNewQueue );
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
Queue_t * const pxQueue = ( Queue_t * ) xQ
configASSERT( pxQueue );
taskENTER_CRITICAL();
pxQueue-&pcTail = pxQueue-&pcHead + ( pxQueue-&uxLength * pxQueue-&uxItemSize );
pxQueue-&uxMessagesWaiting = ( UBaseType_t ) <span style="color: #U;
pxQueue-&pcWriteTo = pxQueue-&pcH
pxQueue-&u.pcReadFrom = pxQueue-&pcHead + ( ( pxQueue-&uxLength - ( UBaseType_t ) <span style="color: #U ) * pxQueue-&uxItemSize );
pxQueue-&cRxLock = queueUNLOCKED;
pxQueue-&cTxLock = queueUNLOCKED;
if( xNewQueue == pdFALSE )
【不是新创建的队列】
/* If there are tasks blocked waiting to read from the queue, then
the tasks will remain blocked as after this function exits the queue
will still be empty.
If there are tasks blocked waiting to write to
the queue, then one should be unblocked as after this function exits
it will be possible to write to it. */
if( listLIST_IS_EMPTY( &( pxQueue-&xTasksWaitingToSend ) ) == pdFALSE )
如果有任务等待写入队列,这个任务可以被Unblock
if( xTaskRemoveFromEventList( &( pxQueue-&xTasksWaitingToSend ) ) != pdFALSE )
queueYIELD_IF_USING_PREEMPTION();
mtCOVERAGE_TEST_MARKER();
mtCOVERAGE_TEST_MARKER();
【是新创建的队列】
/* Ensure the event queues start in the correct state. */
vListInitialise( &( pxQueue-&xTasksWaitingToSend ) );
vListInitialise( &( pxQueue-&xTasksWaitingToReceive ) );
taskEXIT_CRITICAL();
/* A value is returned for calling semantic consistency with previous
versions. */
return pdPASS;
##&&xTaskRemoveFromEventList
BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList )
TCB_t *pxUnblockedTCB;
BaseType_t xR
/* THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION.
It can also be
called from a critical section within an ISR. */
/* The event list is sorted in priority order, so the first in the list can
be removed as it is known to be the highest priority.
Remove the TCB from
the delayed list, and add it to the ready list.
If an event is for a queue that is locked then this function will never
get called - the lock count on the queue will get modified instead.
means exclusive access to the event list is guaranteed here.
This function assumes that a check has already been made to ensure that
pxEventList is not empty. */
pxUnblockedTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList );
configASSERT( pxUnblockedTCB );
( void ) uxListRemove( &( pxUnblockedTCB-&xEventListItem ) );
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
如果调度器没有关闭,将任务放入就绪任务列表
( void ) uxListRemove( &( pxUnblockedTCB-&xStateListItem ) );
prvAddTaskToReadyList( pxUnblockedTCB );
调度器关闭,将任务放入PendingReadyList,不排序的插入
/* The delayed and ready lists cannot be accessed, so hold this task
pending until the scheduler is resumed. */
vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB-&xEventListItem ) );
if( pxUnblockedTCB-&uxPriority & pxCurrentTCB-&uxPriority )
Unblock的任务优先级比当前的任务高
/* Return true if the task removed from the event list has a higher
priority than the calling task.
This allows the calling task to know if
it should force a context switch now. */
xReturn = pdTRUE;
/* Mark that a yield is pending in case the user is not using the
"xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */
xYieldPending = pdTRUE;
xReturn = pdFALSE;
#if( configUSE_TICKLESS_IDLE != 0 )
/* If a task is blocked on a kernel object then xNextTaskUnblockTime
might be set to the blocked task's time out time.
If the task is
unblocked for a reason other than a timeout xNextTaskUnblockTime is
normally left unchanged, because it is automatically reset to a new
value when the tick count equals xNextTaskUnblockTime.
However if
tickless idling is used it might be more important to enter sleep mode
at the earliest possible time - so reset xNextTaskUnblockTime here to
ensure it is updated at the earliest possible time. */
prvResetNextTaskUnblockTime();
阅读(...) 评论() &【FreeRTOS操作系统教程】第11章&&FreeRTOS任务栈大小确定及其溢出检测(上)
第11章&&&&
FreeRTOS任务栈大小确定及其溢出检测(上)
本章节为大家讲解FreeRTOS任务栈大小的确定方法以及栈溢出检测方法。给任务分配多大的栈空间,一直是初学者比较头疼的问题,本章就主要为大家讲解如何解决此问题。
本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及F429。
任务栈大小的确定
什么是栈溢出
FreeRTOS的栈溢出检测机制
实验例程说明(任务栈溢出检测方式一)
实验例程说明(任务栈溢出检测方式二)
11.1&任务栈大小的确定
在基于RTOS的应用设计中,每个任务都需要自己的栈空间,应用不同,每个任务需要的栈大小也是不同的。将如下的几个选项简单的累加就可以得到一个粗略的栈大小:
函数的嵌套调用,针对每一级函数用到栈空间的有如下四项:
函数局部变量。
函数形参,一般情况下函数的形参是直接使用的CPU寄存器,不需要使用栈空间,但是这个函数中如果还嵌套了一个函数的话,这个存储了函数形参的CPU寄存器内容是要入栈的。所以建议大家也把这部分算在栈大小中。
函数返回地址,针对M3和M4内核的MCU,一般函数的返回地址是专门保存到LR(Link
Register)寄存器里面的,如果这个函数里面还调用了一个函数的话,这个存储了函数返回地址的LR寄存器内容是要入栈的。所以建议大家也把这部分算在栈大小中。
函数内部的状态保存操作也需要额外的栈空间。
任务切换,任务切换时所有的寄存器都需要入栈,对于带FPU浮点处理单元的M4内核MCU来说,FPU寄存器也是需要入栈的。
针对M3内核和M4内核的MCU来说,在任务执行过程中,如果发生中断:
M3内核的MCU有8个寄存器是自动入栈的,这个栈是任务栈,进入中断以后其余寄存器入栈以及发生中断嵌套都是用的系统栈。
M4内核的MCU有8个通用寄存器和18个浮点寄存器是自动入栈的,这个栈是任务栈,进入中断以后其余通用寄存器和浮点寄存器入栈以及发生中断嵌套都是用的系统栈。
进入中断以后使用的局部变量以及可能发生的中断嵌套都是用的系统栈,这点要注意。
实际应用中将这些都加起来是一件非常麻烦的工作,上面这些栈空间加起来的总和只是栈的最小需求,实际分配的栈大小可以在最小栈需求的基础上乘以一个安全系数,一般取1.5-2。上面的计算是我们用户可以确定的栈大小,项目应用中还存在无法确定的栈大小,比如调用printf函数就很难确定实际的栈消耗。又比如通过函数指针实现函数的间接调用,因为函数指针不是固定的指向一个函数进行调用,而是根据不同的程序设计可以指向不同的函数,使得栈大小的计算变得比较麻烦。
另外还要注意一点,建议不要编写递归代码,因为我们不知道递归的层数,栈的大小也是不好确定的。
一般来说,用户可以事先给任务分配一个大的栈空间,然后通过第8章介绍的调试方法打印任务栈的使用情况,运行一段时间就会有个大概的范围了。这种方法比较简单且实用些。
函数栈大小确定
函数的栈大小计算起来是比较麻烦的,那么有没有简单的办法来计算呢?有的,一般IDE开发环境都有这样的功能,比如MDK会生成一个htm文件,通过这个文件用户可以知道每个被调用函数的最大栈需求以及各个函数之间的调用关系。但是MDK无法确定通过函数指针实现函数调用时的栈需求。另外,发生中断或中断嵌套时的现场保护需要的栈空间也不会统计。
关于MDK生成的map和htm文件的使用,我们安富莱电子有出过一期视频教程,可以在这里查看:
11.2&什么是栈溢出
前面为大家讲解了如何确定任务栈的大小,那什么又是栈溢出呢?简单的说就是用户分配的栈空间不够用了,溢出了。下面我们举一个简单的实例,栈生长方向从高地址向低地址生长(M4和M3是这种方式)。
上图标识1的位置是RTOS的某个任务调用了函数test()前的SP栈指针位置。
test (void);
int array[10];
上图标识2的位置是调用了函数test需要保存返回地址到栈空间。这一步不是必须的,对于M3和M4内核是先将其保存到LR寄存器中,如果LR寄存器中有保存上一级函数的返回地址,需要将LR寄存器中的内容先入栈。
上图标识3的位置是局部变量int
array[10]占用的栈空间,但申请了栈空间后已经越界了。这个就是所谓的栈溢出了。如果用户在函数test中通过数组array修改了这部分越界区的数据且这部分越界的栈空间暂时没有用到或者数据不是很重要,情况还不算严重,但是如果存储的是关键数据,会直接导致系统崩溃。
上图标识4的位置是局部变量申请了栈空间后,栈指针向下偏移(返回地址+变量i+10个数组元素)*4=48个字节。
上图标识5的位置可能是其它任务的栈空间,也可能是全局变量或者其它用途的存储区,如果test函数在使用中还有用到栈的地方就会从这里申请,这部分越界的空间暂时没有用到或者数据不是很重要,情况还不算严重,但是如果存储的是关键数据,会直接导致系统崩溃。
11.3FreeRTOS的栈溢出检测机制
FreeRTOS提供了两种栈溢出检测机制,这两种检测都是在任务切换时才会进行:
在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。
vApplicationStackOverflowHook( TaskHandle_t xTask,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&signed
char *pcTaskName );
用户可以在钩子函数里面做一些处理。这种方法不能保证所有的栈溢出都能检测到。比如任务在执行的过程中出现过栈溢出。任务切换前栈指针又恢复到了正常水平,这种情况在任务切换的时候是检测不到的。又比如任务栈溢出后,把这部分栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还好,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
使用方法一需要用户在FreeRTOSConfig.h文件中配置如下宏定义:
configCHECK_FOR_STACK_OVERFLOW&&
任务创建的时候将任务栈所有数据初始化为0xa5,任务切换时进行任务栈检测的时候会检测末尾的16个字节是否都是0xa5,通过这种方式来检测任务栈是否溢出了。相比方法一,这种方法的速度稍慢些,但是这样就有效地避免了方法一里面的部分情况。不过依然不能保证所有的栈溢出都能检测到,比如任务栈末尾的16个字节没有用到,即没有被修改,但是任务栈已经溢出了,这种情况是检测不到的。另外任务栈溢出后,任务栈末尾的16个字节没有修改,但是溢出部分的栈区数据被修改了,这部分栈区的数据不重要或者暂时没有用到还好,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
使用方法二需要用户在FreeRTOSConfig.h文件中配置如下宏定义:
configCHECK_FOR_STACK_OVERFLOW&&
栈溢出检测方法
除了FreeRTOS提供的这两种栈溢出检测机制,还有其它的栈溢出检测机制,大家可以在Mircrium官方发布的如下这个博文中学习:
钩子函数的主要作用就是对原有函数的功能进行扩展,用户可以根据自己的需要往里面添加相关的测试代码,大家可以在FreeRTOS工程中检索这个钩子函数vApplicationStackOverflowHook所在的位置。
11.4实验例程说明(任务栈溢出检测方式一)
11.4.1STM32F103开发板实验
配套例子:
V4-305_FreeRTOS实验_任务栈溢出检测方法一(模拟栈溢出)
实验目的:
学习FreeRTOS的任务栈溢出检测方法一(模拟栈溢出)。
FreeRTOS的任务栈溢出检测方法一说明:
FreeRTOSConfig.h文件中配置宏定义:
configCHECK_FOR_STACK_OVERFLOW&&
在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。
void vApplicationStackOverflowHook(
TaskHandle_t xTask,
signed char *pcTaskName );
用户可以在钩子函数里面做一些处理。本实验是在钩子函数中打印出现栈溢出的任务。
这种方法不能保证所有的栈溢出都能检测到。比如任务在执行的过程中发送过栈溢出。任务切换前栈指针又恢复到了正常水平,这种情况在任务切换的时候是检测不到的。又比如任务栈溢出后,把这部分栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还不会有什么问题,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
本实验就是简单的在任务vTaskUserIF中申请过大的栈空间,模拟出一种栈溢出的情况,溢出后触发钩子函数,因为我们将溢出部分的数据修改了,进而造成进入硬件异常。
实验内容:
K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
K2按键按下,模拟栈溢出。
各个任务实现的功能如下:
&&&&&&&&&&&&&
vTaskUserIF任务
&&:按键消息处理。
&&&&&&&&&&&&&
vTaskLED任务&&&
&:LED闪烁。
&&&&&&&&&&&&&
vTaskMsgPro任务
:消息处理,这里是用作LED闪烁。
&&&&&&&&&&&&&
vTaskStart任务&
&&:启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
FreeRTOSConfig.h文件中的配置如下:
defined(__ICCARM__) || defined(__CC_ARM) ||
defined(__GNUC__)
volatile uint32_t ulHighFrequencyTimerTicks;
configUSE_PREEMPTION&&&&&&&&
configUSE_IDLE_HOOK&&&&&&&&&
configUSE_TICK_HOOK&&&&&&&&&
configCPU_CLOCK_HZ&&&&&&&&&&
( ( unsigned long )
configTICK_RATE_HZ&&&&&&&&&&
( ( TickType_t ) 1000 )
configMAX_PRIORITIES&&&&&&&&
configMINIMAL_STACK_SIZE&
&& ( ( unsigned short ) 128
configTOTAL_HEAP_SIZE&&&&&&&
( ( size_t ) ( 17 * 1024 ) )
configMAX_TASK_NAME_LEN&&&&&
configUSE_TRACE_FACILITY
configUSE_16_BIT_TICKS&&&&&&
configIDLE_SHOULD_YIELD&&&&&
configCHECK_FOR_STACK_OVERFLOW&
configGENERATE_RUN_TIME_STATS&&&&&&&&&&&&&&&
configUSE_STATS_FORMATTING_FUNCTIONS&&&&&&&&
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()&&&&
(ulHighFrequencyTimerTicks = 0ul)
portGET_RUN_TIME_COUNTER_VALUE()&&&&&&&&&&&&
ulHighFrequencyTimerTicks
portALT_GET_RUN_TIME_COUNTER_VALUE&&&&&&&&&&
configUSE_CO_ROUTINES
configMAX_CO_ROUTINE_PRIORITIES ( 2 )
INCLUDE_vTaskPrioritySet&&&&
INCLUDE_uxTaskPriorityGet&&&&&&&&
INCLUDE_vTaskDelete&&&&&&&&&&&&&&
INCLUDE_vTaskCleanUpResources
INCLUDE_vTaskSuspend&&&&&&&&&&&&&
INCLUDE_vTaskDelayUntil&&&&&&&&&&
INCLUDE_vTaskDelay&&&&&&&&&&&&&&&
__NVIC_PRIO_BITS
configPRIO_BITS&&&&&&
__NVIC_PRIO_BITS
configPRIO_BITS&&&&&&
configLIBRARY_LOWEST_INTERRUPT_PRIORITY&&&&&&&&&&&&&
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY&&&
几个重要选项说明:
configUSE_PREEMPTION&&&&&&&
使能抢占式调度器
configCPU_CLOCK_HZ&&&&
( ( unsigned long )
系统主频72MHz。
configTICK_RATE_HZ&&&&&&&&&&&&&
( ( TickType_t ) 1000 )
系统时钟节拍1KHz,即1ms。
configMAX_PRIORITIES&&&&&&&&&
定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
configTOTAL_HEAP_SIZE&&&&&&&
( ( size_t ) ( 17 * 1024 ) )
定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
configCHECK_FOR_STACK_OVERFLOW&&
任务栈检测采用方式一。
INCLUDE_vTaskSuspend&&&&&&&&&&&&&&
使用FreeRTOS的任务挂起函数vTaskSuspend和任务恢复函数vTaskResume必须配置此宏定义为1。
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY&&&&&&&&&
定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4
16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为0到15,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY为0x01表示用户可以在抢占式优先级为1到15的中断里面调用FreeRTOS的API函数,抢占式优先级为0的中断里面是不允许调用的。
更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
上面截图中打印出来的任务状态字母B,
R, D, S对应如下含义:
tskBLOCKED_CHAR&&&&&&&&&
( 'B' )& 任务阻塞
tskREADY_CHAR&&&&&&
)& 任务就绪
tskDELETED_CHAR&&&&&&&&&&
( 'D' )& 任务删除
tskSUSPENDED_CHAR& &( 'S'
)& 任务挂起
栈溢出串口打印效果(按K2按键,多次打印溢出任务后最终进入硬件异常):
程序设计:
任务栈大小分配:
vTaskUserIF任务
&&:2048字节
vTaskLED任务&&&
&:2048字节
vTaskMsgPro任务 :2048字节
vTaskStart任务&
&&:2048字节
任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
configTOTAL_HEAP_SIZE&&&&&&&
( ( size_t ) ( 17 * 1024 ) )
系统栈大小分配:
FreeROTS初始化:
main(void)
__set_PRIMASK(1);&
bsp_Init();
vSetupSysInfoTest();
AppTaskCreate();
vTaskStartScheduler();
硬件外设初始化
硬件外设的初始化是在bsp.c文件实现:
bsp_Init(void)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
bsp_InitUart(); &&
bsp_InitLed();
bsp_InitKey();&&&&
FreeRTOS任务创建:
void AppTaskCreate (void)
xTaskCreate(
vTaskTaskUserIF,&&
&&&&&&&&&&&&&&&&
"vTaskUserIF",&&&&
&&&&&&&&&&&&
&&&&512,&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&
NULL,&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&
1,&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&
&xHandleTaskUserIF );& /*
任务句柄& */
xTaskCreate(
vTaskLED,&&&
&&&&&&&&&&&
&&&&&"vTaskLED",&
&&&&&&&&&&&&&&&&
512,&&&&&&&&
&&&&&&&&&&&&&&&&
NULL,&&&&&&&
&&&&&&&&&&&&&&&&
2,&&&&&&&&&&
&&&&&&&&&&&&&&&&
&xHandleTaskLED );
xTaskCreate(
vTaskMsgPro,&&&&
&&&&&&&&&&&&&&&&
"vTaskMsgPro",&&
&&&&&&&&&&&&&&&&
512,&&&&&&&&&&&&
&&&&&&&&&&&&&&&&
NULL,&&&&&&&&&&
&&&&&&&&&&&&&&&&
3,&&&&&&&&&&&&&&
&&&&&&&&&&&xHandleTaskMsgPro
任务句柄& */
xTaskCreate(
vTaskStart,&&&&
&&&&&&&&&&&&&&&&
"vTaskStart",&&
&&&&&&&&&&&&&&&&
512,&&&&&&&&&&&
&&&&&&&&&&&&&&&&
NULL,&&&&&&&&&&
&&&&&&&&&&&&&&&&
4,&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&
&xHandleTaskStart );& &
四个FreeRTOS任务的实现:
void vTaskTaskUserIF(void *pvParameters)
uint8_t ucKeyC
uint8_t pcWriteBuffer[500];
ucKeyCode = bsp_GetKey();
if (ucKeyCode != KEY_NONE)
&&&&&&&&&&&&&
switch (ucKeyCode)
&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
KEY_DOWN_K1:&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&
printf("=================================================\r\n");
&&&&&&&&&&&&&&&&&&&&&&
printf("任务名&&&&&
任务状态 优先级&& 剩余栈
任务序号\r\n");
&&&&&&&&&&&&&&&&&&&&&&
vTaskList((char *)&pcWriteBuffer);
&&&&&&&&&&&&&&&&&&&&&&
printf("%s\r\n", pcWriteBuffer);
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&
printf("\r\n任务名&&&&&&
运行计数&&&&&&&&
使用率\r\n");
&&&&&&&&&&&&&&&&&&&&&&
vTaskGetRunTimeStats((char *)&pcWriteBuffer);
&&&&&&&&&&&&&&&&&&&&&&
printf("%s\r\n", pcWriteBuffer);
&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
KEY_DOWN_K2:&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&
printf("K2键按下,模拟任务栈溢出检测\r\n");
&&&&&&&&&&&&&&&&&&&&&&
StackOverflowTest();
&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
default:&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&
vTaskDelay(20);
void vTaskLED(void *pvParameters)
bsp_LedToggle(2);
vTaskDelay(200);
void vTaskMsgPro(void *pvParameters)
bsp_LedToggle(3);
vTaskDelay(300);
void vTaskStart(void *pvParameters)
bsp_KeyScan();
vTaskDelay(10);
栈溢出测试函数:
栈溢出检测函数是通过用户按下按键K2后,通过任务vTaskTaskUserIF调用此函数。
void StackOverflowTest(void)
uint8_t buf[2048];
for(i = 2047; i &= 0; i--)
buf[i] = 0x55;
vTaskDelay(1);
栈溢出钩子函数:
vApplicationStackOverflowHook( TaskHandle_t xTask, signed char
*pcTaskName )
printf("任务:%s
发现栈溢出\r\n", pcTaskName);
11.4.2STM32F407开发板实验
配套例子:
V5-305_FreeRTOS实验_任务栈溢出检测方法一(模拟栈溢出)
实验目的:
学习FreeRTOS的任务栈溢出检测方法一(模拟栈溢出)。
FreeRTOS的任务栈溢出检测方法一说明:
FreeRTOSConfig.h文件中配置宏定义:
configCHECK_FOR_STACK_OVERFLOW&&
在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。
void vApplicationStackOverflowHook(
TaskHandle_t xTask,
signed char *pcTaskName );
用户可以在钩子函数里面做一些处理。本实验是在钩子函数中打印出现栈溢出的任务。
这种方法不能保证所有的栈溢出都能检测到。比如任务在执行的过程中发送过栈溢出。任务切换前栈指针又恢复到了正常水平,这种情况在任务切换的时候是检测不到的。又比如任务栈溢出后,把这部分栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还不会有什么问题,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
本实验就是简单的在任务vTaskUserIF中申请过大的栈空间,模拟出一种栈溢出的情况,溢出后触发钩子函数,因为我们将溢出部分的数据修改了,进而造成进入硬件异常。
实验内容:
K1按键按下,串口打印任务执行情况(波特率115200,数据位8,奇偶校验位无,停止位1)。
K2按键按下,模拟栈溢出。
各个任务实现的功能如下:
&&&&&&&&&&&&&
vTaskUserIF任务
&&:按键消息处理。
&&&&&&&&&&&&&
vTaskLED任务&&&
&:LED闪烁。
&&&&&&&&&&&&&
vTaskMsgPro任务
:消息处理,这里是用作LED闪烁。
&&&&&&&&&&&&&
vTaskStart任务&
&&:启动任务,也是最高优先级任务,这里实现按键扫描。
FreeRTOS的配置:
FreeRTOSConfig.h文件中的配置如下:
defined(__ICCARM__) || defined(__CC_ARM) ||
defined(__GNUC__)
volatile uint32_t ulHighFrequencyTimerTicks;
configUSE_PREEMPTION&&&&&&&&
configUSE_IDLE_HOOK&&&&&&&&&
configUSE_TICK_HOOK&&&&&&&&&
configCPU_CLOCK_HZ&&&&&&&&&&
( ( unsigned long )
configTICK_RATE_HZ&&&&&&&&&&
( ( TickType_t ) 1000 )
configMAX_PRIORITIES&&&&&&&&
configMINIMAL_STACK_SIZE&
&& ( ( unsigned short ) 128
configTOTAL_HEAP_SIZE&&&&&&&
( ( size_t ) ( 30 * 1024 ) )
configMAX_TASK_NAME_LEN&&&&&
configUSE_TRACE_FACILITY
configUSE_16_BIT_TICKS&&&&&&
configIDLE_SHOULD_YIELD&&&&&
configCHECK_FOR_STACK_OVERFLOW&
configGENERATE_RUN_TIME_STATS&&&&&&&&&&&&&&&
configUSE_STATS_FORMATTING_FUNCTIONS&&&&&&&&
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()&&&&
(ulHighFrequencyTimerTicks = 0ul)
portGET_RUN_TIME_COUNTER_VALUE()&&&&&&&&&&&&
ulHighFrequencyTimerTicks
portALT_GET_RUN_TIME_COUNTER_VALUE&&&&&&&&&&
configUSE_CO_ROUTINES
configMAX_CO_ROUTINE_PRIORITIES ( 2 )
INCLUDE_vTaskPrioritySet&&&&
INCLUDE_uxTaskPriorityGet&&&&&&&&
INCLUDE_vTaskDelete&&&&&&&&&&&&&&
INCLUDE_vTaskCleanUpResources
INCLUDE_vTaskSuspend&&&&&&&&&&&&&
INCLUDE_vTaskDelayUntil&&&&&&&&&&
INCLUDE_vTaskDelay&&&&&&&&&&&&&&&
__NVIC_PRIO_BITS
configPRIO_BITS&&&&&&
__NVIC_PRIO_BITS
configPRIO_BITS&&&&&&
configLIBRARY_LOWEST_INTERRUPT_PRIORITY&&&&&&&&&&&&&
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY&&&
几个重要选项说明:
configUSE_PREEMPTION&&&&&&&
使能抢占式调度器
configCPU_CLOCK_HZ&&&&
( ( unsigned long )
系统主频168MHz。
configTICK_RATE_HZ&&&&&&&&&&&&&
( ( TickType_t ) 1000 )
系统时钟节拍1KHz,即1ms。
configMAX_PRIORITIES&&&&&&&&&
定义可供用户使用的最大优先级数,如果这个定义的是5,那么用户可以使用的优先级号是0,1,2,3,4,不包含5,对于这一点,初学者要特别的注意。
configTOTAL_HEAP_SIZE&&&&&&&
( ( size_t ) (30 * 1024 ) )
定义堆大小,FreeRTOS内核,用户动态内存申请,任务栈等都需要用这个空间。
configCHECK_FOR_STACK_OVERFLOW&&
任务栈检测采用方式一。
INCLUDE_vTaskSuspend&&&&&&&&&&&&&&
使用FreeRTOS的任务挂起函数vTaskSuspend和任务恢复函数vTaskResume必须配置此宏定义为1。
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY&&&&&&&&&
定义受FreeRTOS管理的最高优先级中断。简单的说就是允许用户在这个中断服务程序里面调用FreeRTOS的API的最高优先级。为了进一步说明这个宏定义的的作用,解释如下:
使用CM内核的MCU,官方强烈建议将NVIC的优先级分组配置为全抢占式优先级,全部配置为抢占式优先级的好处就是方便管理。
对于STM32来说,设置NVIC的优先级分组为4时,NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)就是全部配置为抢占式优先级。又因为STM32的优先级设置仅使用CM内核8bit中的高4bit,即只能区分2^4
16种优先级。因此当优先级分组设置为4的时候可供用户选择抢占式优先级为0到15,共16个优先级,配置为0表示最高优先级,配置为15表示最低优先级,不存在子优先级。
这里配置configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY为0x01表示用户可以在抢占式优先级为1到15的中断里面调用FreeRTOS的API函数,抢占式优先级为0的中断里面是不允许调用的。
更多关于这个参数说明请参看第12章。
FreeRTOS任务调试信息(按K1按键,串口打印):
上面截图中打印出来的任务状态字母B,
R, D, S对应如下含义:
tskBLOCKED_CHAR&&&&&&&&&
( 'B' )& 任务阻塞
tskREADY_CHAR&&&&&&
)& 任务就绪
tskDELETED_CHAR&&&&&&&&&&
( 'D' )& 任务删除
tskSUSPENDED_CHAR& &( 'S'
)& 任务挂起
栈溢出串口打印效果(按K2按键,多次打印溢出任务后最终进入硬件异常):
程序设计:
任务栈大小分配:
vTaskUserIF任务
&&:2048字节
vTaskLED任务&&&
&:2048字节
vTaskMsgPro任务 :2048字节
vTaskStart任务&
&&:2048字节
任务栈空间是在任务创建的时候从FreeRTOSConfig.h文件中定义的heap空间中申请的
configTOTAL_HEAP_SIZE&&&&&&&
( ( size_t ) ( 30 * 1024 ) )
系统栈大小分配:
FreeROTS初始化:
main(void)
__set_PRIMASK(1);&
bsp_Init();
vSetupSysInfoTest();
AppTaskCreate();
vTaskStartScheduler();
硬件外设初始化
硬件外设的初始化是在bsp.c文件实现:
bsp_Init(void)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
bsp_InitUart(); &&
bsp_InitKey();&&&&
bsp_InitLed();
FreeRTOS任务创建:
void AppTaskCreate (void)
xTaskCreate(
vTaskTaskUserIF,&&
&&&&&&&&&&&&&&&&
"vTaskUserIF",&&&&
&&&&&&&&&&&&&&&&
512,&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&
NULL,&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&
1,&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&
&xHandleTaskUserIF );& /*
任务句柄& */
xTaskCreate(
vTaskLED,&&&
&&&&&&&&&&&&&&&&
"vTaskLED",&
&&&&&&&&&&&&&&&&
512,&&&&&&&&
&&&&&&&&&&&&&&&&
NULL,&&&&&&&
&&&&&&&&&&&&&&&&
2,&&&&&&&&&&
&&&&&&&&&&&&&&&&
&xHandleTaskLED );
xTaskCreate(
vTaskMsgPro,&&&&
&&&&&&&&&&&&&&&&
"vTaskMsgPro",&&
&&&&&&&&&&&&&&&&
512,&&&&&&&&&&&&
&&&&&&&&&&&&&&&&
NULL,&&&&&&&&&&
&&&&&&&&&&&&&&&&
3,&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&
&xHandleTaskMsgPro );& /*
任务句柄& */
xTaskCreate(
vTaskStart,&&&&
&&&&&&&&&&&&&&&&
"vTaskStart",&&
&&&&&&&&&&&&&&&&
512,&&&&&&&&&&&
&&&&&&&&&&&&&&&&
NULL,&&&&&&&&&&
&&&&&&&&&&&&&&&&
4,&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&
&xHandleTaskStart );& &
四个FreeRTOS任务的实现:
void vTaskTaskUserIF(void *pvParameters)
uint8_t ucKeyC
uint8_t pcWriteBuffer[500];
ucKeyCode = bsp_GetKey();
if (ucKeyCode != KEY_NONE)
&&&&&&&&&&&&&
switch (ucKeyCode)
&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
KEY_DOWN_K1:&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&
printf("=================================================\r\n");
&&&&&&&&&&&&&&&&&&&&&&
printf("任务名&&&&&
任务状态 优先级&& 剩余栈
任务序号\r\n");
&&&&&&&&&&&&&&&&&&&&&&
vTaskList((char *)&pcWriteBuffer);
&&&&&&&&&&&&&&&&&&&&&&
printf("%s\r\n", pcWriteBuffer);
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&
printf("\r\n任务名&&&&&&
运行计数&&&&&&&&
使用率\r\n");
&&&&&&&&&&&&&&&&&&&&&&
vTaskGetRunTimeStats((char *)&pcWriteBuffer);
&&&&&&&&&&&&&&&&&&&&&&
printf("%s\r\n", pcWriteBuffer);
&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
KEY_DOWN_K2:&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&
printf("K2键按下,模拟任务栈溢出检测\r\n");
&&&&&&&&&&&&&&&&&&&&&&
StackOverflowTest();
&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&
default:&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&
vTaskDelay(20);
void vTaskLED(void *pvParameters)
bsp_LedToggle(2);
vTaskDelay(200);
void vTaskMsgPro(void *pvParameters)
bsp_LedToggle(3);
vTaskDelay(300);
void vTaskStart(void *pvParameters)
bsp_KeyScan();
vTaskDelay(10);
栈溢出测试函数:
栈溢出检测函数是通过用户按下按键K2后,通过任务vTaskTaskUserIF调用此函数。
void StackOverflowTest(void)
uint8_t buf[2048];
for(i = 2047; i &= 0; i--)
buf[i] = 0x55;
vTaskDelay(1);
栈溢出钩子函数:
vApplicationStackOverflowHook( TaskHandle_t xTask, signed char
*pcTaskName )
printf("任务:%s
发现栈溢出\r\n", pcTaskName);
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。}

我要回帖

更多关于 结构体初始化 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信