FreeRTOS --(4)任务管理之启动调度器

2023-05-16

目录

1、vTaskStartScheduler

2、xPortStartScheduler

3、vPortSetupTimerInterrupt

4、prvStartFirstTask

5、vPortSVCHandler


 

在使用 FreeRTOS 的时候,一般的,先创建若干任务,但此刻任务并没有被调度起来,仅仅是创建了,如果想要真正的跑起来,那么还需要调用让调度器跑起来的函数:

vTaskStartScheduler

典型的用法是:


   
   
  1. xTaskCreate(.. "task_1"..);
  2. xTaskCreate(.. "task_2"..);
  3. xTaskCreate(.. "task_3"..);
  4. vTaskStartScheduler();
  5. // Never reach here
  6. DUMP_ERROR();

现在就来看看 vTaskStartScheduler 具体做了些什么;

 

1、vTaskStartScheduler

vTaskStartScheduler 的实现在 task.c 中:


   
   
  1. void vTaskStartScheduler( void )
  2. {
  3. BaseType_t xReturn;
  4. /* Add the idle task at the lowest priority. */
  5. #if( configSUPPORT_STATIC_ALLOCATION == 1 )
  6. {
  7. StaticTask_t *pxIdleTaskTCBBuffer = NULL;
  8. StackType_t *pxIdleTaskStackBuffer = NULL;
  9. uint32_t ulIdleTaskStackSize;
  10. /* The Idle task is created using user provided RAM - obtain the
  11. address of the RAM then create the idle task. */
  12. vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
  13. xIdleTaskHandle = xTaskCreateStatic( prvIdleTask,
  14. configIDLE_TASK_NAME,
  15. ulIdleTaskStackSize,
  16. ( void * ) NULL,
  17. portPRIVILEGE_BIT,
  18. pxIdleTaskStackBuffer,
  19. pxIdleTaskTCBBuffer );
  20. if( xIdleTaskHandle != NULL )
  21. {
  22. xReturn = pdPASS;
  23. }
  24. else
  25. {
  26. xReturn = pdFAIL;
  27. }
  28. }
  29. #else
  30. {
  31. /* The Idle task is being created using dynamically allocated RAM. */
  32. xReturn = xTaskCreate( prvIdleTask,
  33. configIDLE_TASK_NAME,
  34. configMINIMAL_STACK_SIZE,
  35. ( void * ) NULL,
  36. portPRIVILEGE_BIT,
  37. &xIdleTaskHandle );
  38. }
  39. #endif /* configSUPPORT_STATIC_ALLOCATION */
  40. #if ( configUSE_TIMERS == 1 )
  41. {
  42. if( xReturn == pdPASS )
  43. {
  44. xReturn = xTimerCreateTimerTask();
  45. }
  46. else
  47. {
  48. mtCOVERAGE_TEST_MARKER();
  49. }
  50. }
  51. #endif /* configUSE_TIMERS */
  52. if( xReturn == pdPASS )
  53. {
  54. /* freertos_tasks_c_additions_init() should only be called if the user
  55. definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is
  56. the only macro called by the function. */
  57. #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
  58. {
  59. freertos_tasks_c_additions_init();
  60. }
  61. #endif
  62. /* Interrupts are turned off here, to ensure a tick does not occur
  63. before or during the call to xPortStartScheduler(). The stacks of
  64. the created tasks contain a status word with interrupts switched on
  65. so interrupts will automatically get re-enabled when the first task
  66. starts to run. */
  67. portDISABLE_INTERRUPTS();
  68. #if ( configUSE_NEWLIB_REENTRANT == 1 )
  69. {
  70. /* Switch Newlib's _impure_ptr variable to point to the _reent
  71. structure specific to the task that will run first. */
  72. _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
  73. }
  74. #endif /* configUSE_NEWLIB_REENTRANT */
  75. xNextTaskUnblockTime = portMAX_DELAY;
  76. xSchedulerRunning = pdTRUE;
  77. xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;
  78. /* If configGENERATE_RUN_TIME_STATS is defined then the following
  79. macro must be defined to configure the timer/counter used to generate
  80. the run time counter time base. NOTE: If configGENERATE_RUN_TIME_STATS
  81. is set to 0 and the following line fails to build then ensure you do not
  82. have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your
  83. FreeRTOSConfig.h file. */
  84. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();
  85. traceTASK_SWITCHED_IN();
  86. /* Setting up the timer tick is hardware specific and thus in the
  87. portable interface. */
  88. if( xPortStartScheduler() != pdFALSE )
  89. {
  90. /* Should not reach here as if the scheduler is running the
  91. function will not return. */
  92. }
  93. else
  94. {
  95. /* Should only reach here if a task calls xTaskEndScheduler(). */
  96. }
  97. }
  98. else
  99. {
  100. /* This line will only be reached if the kernel could not be started,
  101. because there was not enough FreeRTOS heap to create the idle task
  102. or the timer task. */
  103. configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
  104. }
  105. /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
  106. meaning xIdleTaskHandle is not used anywhere else. */
  107. ( void ) xIdleTaskHandle;
  108. }

在 vTaskStartScheduler 函数中,首先通过 xTaskCreate 创建了一个 Idle 任务,优先级为最低 0;(关于 Idle 任务,后面专门来讲);

调用 portDISABLE_INTERRUPTS(); 关闭全局中断,因为后面要初始化 TICK 中断;

接着初始化了全局变量:


   
   
  1. xNextTaskUnblockTime = portMAX_DELAY;
  2. xSchedulerRunning = pdTRUE;
  3. xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;

下一个未阻塞的任务时间为 0xFFFF_FFFF;调度器启动的标志位为 TRUE,代表调度器已经初始化,Tick 的计数器为 0;

 

2、xPortStartScheduler

接着调用 xPortStartScheduler 来配置和体系结构相关调度,这里主要是一些和处理器相关的寄存器(比如 SYSTICK 等);这里还是以 Cortex-M3 作为例子,xPortStartScheduler 实现在 port.c 文件:


   
   
  1. /* Constants required to check the validity of an interrupt priority. */
  2. #define portFIRST_USER_INTERRUPT_NUMBER ( 16 )
  3. #define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 )
  4. #define portAIRCR_REG ( * ( ( volatile uint32_t * ) 0xE000ED0C ) )
  5. #define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff )
  6. #define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 )
  7. #define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 )
  8. #define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL )
  9. #define portPRIGROUP_SHIFT ( 8UL )
  10. BaseType_t xPortStartScheduler( void )
  11. {
  12. #if(configASSERT_DEFINED == 1 )
  13. {
  14. volatile uint32_t ulOriginalPriority;
  15. /* 中断优先级寄存器0: PRI_0 */
  16. volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) (portNVIC_IP_REGISTERS_OFFSET_16 +portFIRST_USER_INTERRUPT_NUMBER );
  17. volatile uint8_t ucMaxPriorityValue;
  18. /* 这一大段代码用来确定一个最高ISR优先级,在这个ISR或者更低优先级的ISR中可以安全的调用以FromISR结尾的API函数.*/
  19. /* 保存中断优先级值,因为下面要覆写这个寄存器(PRI_0) */
  20. ulOriginalPriority = *pucFirstUserPriorityRegister;
  21. /* 确定有效的优先级位个数. 首先向所有位写1,然后再读出来,由于无效的优先级位读出为0,然后数一数有多少个1,就能知道有多少位优先级.*/
  22. *pucFirstUserPriorityRegister= portMAX_8_BIT_VALUE;
  23. ucMaxPriorityValue = *pucFirstUserPriorityRegister;
  24. /* 冗余代码,用来防止用户不正确的设置RTOS可屏蔽中断优先级值 */
  25. ucMaxSysCallPriority =configMAX_SYSCALL_INTERRUPT_PRIORITY &ucMaxPriorityValue;
  26. /* 计算最大优先级组值 */
  27. ulMaxPRIGROUPValue =portMAX_PRIGROUP_BITS;
  28. while( (ucMaxPriorityValue &portTOP_BIT_OF_BYTE ) ==portTOP_BIT_OF_BYTE )
  29. {
  30. ulMaxPRIGROUPValue--;
  31. ucMaxPriorityValue <<= ( uint8_t ) 0x01;
  32. }
  33. ulMaxPRIGROUPValue <<=portPRIGROUP_SHIFT;
  34. ulMaxPRIGROUPValue &=portPRIORITY_GROUP_MASK;
  35. /* 将PRI_0寄存器的值复原*/
  36. *pucFirstUserPriorityRegister= ulOriginalPriority;
  37. }
  38. #endif /*conifgASSERT_DEFINED */
  39. /* 将PendSV和SysTick中断设置为最低优先级*/
  40. portNVIC_SYSPRI2_REG |=portNVIC_PENDSV_PRI;
  41. portNVIC_SYSPRI2_REG |=portNVIC_SYSTICK_PRI;
  42. /* 启动系统节拍定时器,即SysTick定时器,初始化中断周期并使能定时器*/
  43. vPortSetupTimerInterrupt();
  44. /* 初始化临界区嵌套计数器 */
  45. uxCriticalNesting = 0;
  46. /* 启动第一个任务 */
  47. prvStartFirstTask();
  48. /* 永远不会到这里! */
  49. return 0;
  50. }

首先,如果定义 configASSERT_DEFINED 了的话,那么先:

volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) (portNVIC_IP_REGISTERS_OFFSET_16 +portFIRST_USER_INTERRUPT_NUMBER );
   
   

根据 Cortex-M3 的数据手册可以知道,这个地方获取到的是:NVIC 中断优先级寄存器的基地址,Cortex-M3 的 NVIC 中断优先级寄存器定义如下:

CM3 中断优先级寄存器组  (0xE000_E400 ~ 0xE000_E4EF)
NameAccessBase AddressReset ValueDescription
PRI_0R/W0xE000_E4000 (8 bits)外中断 #0 的优先级
PRI_1R/W0xE000_E4010 (8 bits)外中断 #1 的优先级
.....R/W.....0 (8 bits).....
PRI_239R/W0xE000_E4EF0 (8 bits)外中断 #239 的优先级

这里注意一下,每个优先级 8 bit;

这里,首先将中断 0 优先级读出来,放置到 ulOriginalPriority

在往这个 PRI_0 优先级寄存器中写全 1,也就是 0xFF;

再将这个寄存器读出来读到 ucMaxPriorityValue 中;

这样做的目的是为了判断这个中断优先级寄存器哪些 bit 是可写(可配置)的;这个和处理器对这方面的定义相关;

在 Cortex-M3 处理器上,NVIC 中断控制器支持中断优选级配置,它分为了组优先级和组内优先级概念,虽然看起来每个优先级使用 8 bits 来表示,看似最大配置到 0xFF,也就是 255,其实不然,因为引入了组优先级和组内优先级的概念,让这个 8 bits 配置得有点玄机;

优先级组也叫抢占优先级,组内优先级也叫子优先级;抢占优先级高的中断可以嵌套抢占优先级低的,同样抢占优先级的中断,则比较的是子优先级;

Cortex-M3 只是处理器的架构,实际上,芯片公司在利用这种架构实现芯片设计的时候,优先级组和组内优先级,并不是这 8 个 bit 都用到了,因为大量的优先级会增加 NVIC 的复杂度;所以,一般的,具体芯片,PRI_0 的 8 bit 只会用到一部分的 bit,其中中用几个 bit 来表示抢占优先级,用几个 bit 来表示子优先级,这个是可以配置的;具体一点的话,比如:

一款 Cortex-M3 内核做的处理器,它在设计的时候,就定义了 PRI_x 优先级的 8 bit,只有高 3 bit 有效:

一款 Cortex-M3 内核做的处理器,它在设计的时候,就定义了 PRI_x 优先级的 8 bit,只有高 4 bit 有效:

比如ST的STM32F1xx和F4xx只使用了这个8位中的高4位[7:4],低四位取零,这样2^4=16,只能表示16级中断嵌套。

那么用高 4 bit 表示优先级,那么这 4 个 bit 哪几个 bit 代表抢占优先级?哪几个 bit 代表子优先级呢?

这个由另一个寄存器的值说了算,SCB->AIRCR[10:8](0xE000_ED00) 寄存器的 PRIGROUP 的值说了算;


   
   
  1. #define NVIC_PriorityGroup_0          ((u32)0x700) /* 0 bits for pre-emption priority 4 bits for subpriority */
  2. #define NVIC_PriorityGroup_1          ((u32)0x600) /* 1 bits for pre-emption priority 3 bits for subpriority */
  3. #define NVIC_PriorityGroup_2          ((u32)0x500) /* 2 bits for pre-emption priority 2 bits for subpriority */
  4. #define NVIC_PriorityGroup_3          ((u32)0x400) /* 3 bits for pre-emption priority 1 bits for subpriority */
  5. #define NVIC_PriorityGroup_4          ((u32)0x300) /* 4 bits for pre-emption priority 0 bits for subpriority */
SCB->AIRCR[10:8]
GroupAIRCR[10:8] ValuePRI_x bit[7:4] 分配情况分配结果
03‘b1110:40位抢占优先级,4位响应优先级
13‘b1101:31位抢占优先级,3位响应优先级
23‘b1012:22位抢占优先级,2位响应优先级
33‘b1003:13位抢占优先级,1位响应优先级
43‘b0114:04位抢占优先级,0位响应优先

Cortex-M3 中断优先级数值越大,表示优先级越低。而 FreeRTOS 的任务优先级则与之相反:优先级数值越大的任务,优先级越高。

好了,言归正传,这里应该说清楚为何要写进去 0xF,在读出来,就是因为这 8bit 并不是全部都用了,这样便可以得到最大支持的优先级个数 ucMaxPriorityValue

接着计算最大优先级组的值,从读出来有效的 PRI_0 的最高位开始判断(因为)优先级组和子优先级是从最高位开始;

接着配置 PendSV 和 SysTick 的优先级为最低:


   
   
  1. /* Make PendSV and SysTick the lowest priority interrupts. */
  2. portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
  3. portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;

 

3、vPortSetupTimerInterrupt

调用 vPortSetupTimerInterrupt 配置 SysTick,这个是操作系统的心跳,和体系架构相关:


   
   
  1. void vPortSetupTimerInterrupt( void )
  2. {
  3. /* Calculate the constants required to configure the tick interrupt. */
  4. #if( configUSE_TICKLESS_IDLE == 1 )
  5. {
  6. ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
  7. xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
  8. ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
  9. }
  10. #endif /* configUSE_TICKLESS_IDLE */
  11. /* Stop and clear the SysTick. */
  12. portNVIC_SYSTICK_CTRL_REG = 0UL;
  13. portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
  14. /* Configure SysTick to interrupt at the requested rate. */
  15. portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
  16. portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
  17. }

STM32 的 SysTick 是一个向下计数的计数器,可以配置产生 Tick 中断;

configSYSTICK_CLOCK_HZ 定义了 CPU 的时钟频率,需要和处理器同步;

configTICK_RATE_HZ 定义了 Tick 来的频率,比如:

configTICK_RATE_HZ为100,则系统节拍时钟周期为10ms,设置宏configTICK_RATE_HZ为1000,则系统节拍时钟周期为1ms

太频繁的 Tick 中断会导致过频繁的上下文切换,增加系统负担,过于长的上下文切换,会导致任务响应不及时;

典型的,STM32 的 configTICK_RATE_HZ 为 1000,也就是 1ms 一次 Tick 中断;

然后配置寄存器,使能了 SysTick,使能了 SysTick 中断;

接着初始化了嵌套深度:

uxCriticalNesting = 0;

 

4、prvStartFirstTask

最后调用了 prvStartFirstTask(); 启动第一个任务,它的实现使用汇编写的:


   
   
  1. __asm void prvStartFirstTask( void )
  2. {
  3. PRESERVE8
  4. /* Cortext-M3硬件中,0xE000ED08 地址处为VTOR(向量表偏移量)寄存器,存储向量表起始地址*/
  5. /* 将 0xE000ED08 加载到 R0 */
  6. ldr r0, = 0xE000ED08
  7. /* 将 0xE000ED08 中的值,也就是向量表的实际地址加载到 R0 */
  8. ldr r0, [r0]
  9. /* 根据向量表实际存储地址,取出向量表中的第一项,向量表第一项存储主堆栈指针MSP的初始值*/
  10. ldr r0, [r0]
  11. /* 将堆栈地址写入主堆栈指针 */
  12. msr msp, r0
  13. /* 使能全局中断*/
  14. cpsie i
  15. cpsie f
  16. dsb
  17. isb
  18. /* 调用SVC启动第一个任务 */
  19. svc 0
  20. nop
  21. nop
  22. }

PRESERVE8 用于 8 字节对齐;

从 0xE000ED08 获取向量表的偏移,为啥要获得向量表呢?因为向量表的第一个是 MSP 指针!

取 MSP 的初始值的思路是先根据向量表的位置寄存器 VTOR (0xE000ED08) 来获取向量表存储的地址;

在根据向量表存储的地址,来访问第一个元素,也就是初始的 MSP;

此刻呢,将初始的 MSP 存入到了 R0 中,通过 MSR 指令,写到 MSP 中:

打个比方,Cortex-M3 处理器,上电默认进入线程的特权模式,使用 MSP 作为堆栈指针,从上电跑到这里,经过一系列的函数调用,出栈,入栈,MSP 自然已经不是最开始的初始化的位置,这里通过 MSR 重新复制了 MSP,岂不是堆栈都没了么?是的,因为这是一条不归路,代码跑到这里,首先不会返回,之前压栈的内容再也不会用到,所以破坏之前的堆栈也没关系;其次既然不会用到,那么岂不是之前的压栈空间都废了,如果把 MSP 重新初始化到头,就 OK 了嘛,大不了就是破坏了堆栈,反正再也回不去啦;

OK,堆栈指针 MSP 刷完,赋予了新的生命,此刻开中断,开异常,刷流水线;

调用 svc 并传入系统调用号为 0 手动拉 SVC 中断;

 

5、vPortSVCHandler

手动拉了 SVC 中断,而且开启了中断,那么就会进入它的  ISR:vPortSVCHandler,它的实现也是和处理器体系结构相关,在 port.c 中实现:


   
   
  1. __asm void vPortSVCHandler( void )
  2. {
  3. PRESERVE8
  4. ldr r3, =pxCurrentTCB /* pxCurrentTCB指向处于最高优先级的就绪任务TCB */
  5. ldr r1, [r3] /* 获取任务TCB地址 */
  6. ldr r0, [r1] /* 获取任务TCB的第一个成员,即当前堆栈栈顶pxTopOfStack */
  7. ldmia r0!, {r4-r11} /* 出栈,将寄存器r4~r11出栈 */
  8. msr psp, r0 /* 最新的栈顶指针赋给线程堆栈指针PSP */
  9. isb
  10. mov r0, # 0
  11. msr basepri, r0
  12. orr r14, # 0xd /* 这里0x0d表示:返回后进入线程模式,从进程堆栈中做出栈操作,返回Thumb状态*/
  13. bx r14
  14. }

首先还是 PRESERVE8 的 8字节对齐操作;

还记得吗,pxCurrentTCB 指向的是最高优先级的 Ready 状态的任务指针;

根据 pxCurrentTCB 获取到对应 TCB 的地址;然后获取第一个成员变量,也就是当前栈顶地址 pxTopOfStack;这个值在任务分配的时候,就已经计算好,并且模拟的 Cortex-M3 的异常入栈顺序,手动入栈了;

使用 LDMIA 指令,以 pxTopOfStack 开始顺序出栈,先出 R4~R11(在创建任务的时候,最后入栈的就是这些个),同时 R0 递增;

将此刻的 R0 赋值给 PSP(因为弹栈的时候,处理器会按照入栈的顺序去取 xPSR、PC、LR、R12、R3、R2、R1、R0,而这些寄存器在我们创建任务的时候已经手动压栈);

ISB 指令屏障,刷流水线;

将 BASEPRI 寄存器赋值为 0,也就是允许任何中断;

ORR 指令时按位或,所以 ORR R14, #0xd 相当于 R14 |= 0xd;这个操作也和体系架构相关,R14 是链接寄存器 LR,在 ISR 中(此刻我们在 SVC 的 ISR 中),它记录了异常返回值 EXC_RETURN(更多细节参考《Cortex-M3 处理器窥探》Chapter 7.4);

因为当前在 ISR 中还是使用的 MSP,启动任务后,我们期望在任务执行过程中,处于线程模式,并使用 PSP(前面几行已经给 PSP 赋值了),所以我们需要将 LR 设计成为 0xFFFF_FFFD,让处理器知道返回的时候呢,使用线程模式+PSP堆栈;

最后执行 bx R14,告诉处理器 ISR 完成,需要返回,此刻处理器便会使用 PSP 做为堆栈指针,进行出栈操作,将xPSR、PC、LR、R12、R3~R0 出栈,初始化的时候,PC 被我们赋值成为了执行任务的函数的入口,所以呢,就正常跳入到了优先级最高的 Ready 状态的第一个任务的入口函数了;

处理器相关的部分,可以参考《Cortex-M3 处理器窥探》,创建任务的部分参考《FreeRTOS --(8)任务管理之创建任务》

大致的流程如下:

紫色部分,是和体系架构相关的,黑色的是开关中断的地方,蓝色的是 FreeRTOS 的代码;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS --(4)任务管理之启动调度器 的相关文章

  • 详解FreeRTOS中的软件定时器

    软件定时器用于让某个任务定时执行 或者周期性执行 比如设定某个时间后执行某个函数 或者每隔一段时间执行某个函数 由软件定时器执行的函数称为软件定时器的回调函数 参考资料 Mastering the FreeRTOS Real Time Ke
  • 【FreeRtos学习笔记】STM32 CubeMx——Timers(定时器)

    目录 1 软件定时器 2 示例程序 2 1 例程功能 2 2 步骤 2 3 实验结果 2 4 函数讲解 1 软件定时器 定时器是MCU常用的外设 我们在学习各种单片机时必然会学习它的硬件定时器 但是 MCU自带的硬件定时器资源是有限的 而且
  • 【FreeRTOS开发问题】FreeRTOS内存溢出

    FreeRTOS内存溢出 如下图所示 FreeRTOS编译完成后可以看到 系统提示无法分配内存到堆 Objects Template axf Error L6406E No space in execution regions with A
  • FreeRTOS学习笔记<中断>

    中断概念 Cortex M的NVIC最多支持240个IRQ 中断请求 1个不可屏蔽中断 NMI 1个Systick 滴答定时器 定时器中断和多个系统异常 Cortex M处理器有多个用于管中断和异常的可编程寄存器 这些寄存器大多数都在 NV
  • FreeRTOS打印任务对CPU的占有率

    1 配置RTOS 1 打开RTOS Config Parameter 找到Run Time And Task States gathering related definitions 使能GENERATE RUN TIME STATS US
  • FreeRTOS+CubeMX系列第一篇——初识FreeRTOS

    文章目录 一 关于FreeRTOS 二 FreeRTOS的特点 三 如何在CubeMX上配置FreeRTOS 四 FreeRTOS文档资料 五 同系列博客 一 关于FreeRTOS 1 什么是FreeRTOS FreeRTOS是一个迷你的实
  • FreeRTOS临界区

    FreeRTOS临界区是指那些必须完整运行 不能被打断的代码段 比如有的外设的初始化需要严格的时序 初始化过程中不能被打断 FreeRTOS 在进入临界区代码的时候需要关闭中断 当处理完临界区代码以后再打开中断 FreeRTOS 系统本身就
  • 【FreeRTOS(三)】任务状态

    文章目录 任务状态 任务挂起 vTaskSuspend 取消任务挂起 vTaskResume 挂起任务调度器 vTaskSuspendAll 取消挂起任务调度器 xTaskResumeAll 代码示例 任务挂起 取消任务挂起 代码示例 挂起
  • freeRTOS使用uxTaskGetStackHighWaterMark函数查看任务堆栈空间的使用情况

    摘要 每个任务都有自己的堆栈 堆栈的总大小在创建任务的时候就确定了 此函数用于检查任务从创建好到现在的历史剩余最小值 这个值越小说明任务堆栈溢出的可能性就越大 FreeRTOS 把这个历史剩余最小值叫做 高水位线 此函数相对来说会多耗费一点
  • FreeRTOS:中断配置

    目录 一 Cortex M 中断 1 1中断简介 1 2中断管理简介 1 3优先级分组定义 1 4优先级设置 1 5用于中断屏蔽的特殊寄存器 1 5 1PRIMASK 和 FAULTMASK 寄存器 1 5 2BASEPRI 寄存器 二 F
  • FreeRTOS,串口中断接收中使用xQueueOverwriteFromISR()函数,程序卡死在configASSERT

    原因 UART的中断优先级设置的太高 高于了configMAX SYSCALL INTERRUPT PRIORITY宏定义的安全中断等级 UART的中断等级小于等于宏定义的优先等级即可
  • FreeRTOS死机原因

    1 中断回调函数中没有使用中断级API xxFromISR 函数 xSemaphoreGiveFromISR uart busy HighterTask 正确 xSemaphoreGive uart busy 错误 2 比configMAX
  • [FreeRTOS入门学习笔记]定时器

    定时器的使用步骤 1 定义一个handle xTimerCreate创建 2 启动定时器 在Task1中调用 通过队列通知守护任务来执行定时器任务 要再config头文件中定义守护任务相关配置 虽然定时器是在task1中启动 但是定时器的任
  • FreeRTOS轻量级同步--任务通知

    1 简介 在FreeRTOS的配置参数中的configUSE TASK NOTIFICATIONS宏打开 一般RTOS会默认打开 如图1所示 图1 notify宏开关 RTOS在创建任务时 会创建一个32位的通知值ulNotifiedVal
  • 单片机通信数据延迟问题排查

    1 问题说明 笔者在最近的项目中 发现系统的响应延迟较高 经过排查 排除了单片机运行卡死的问题 2 原因分析 具体排查过程这里就不细致说明了 直接给出排查后原因 任务执行周期规划不合理 导致freertos队列发送接收到的命令有延迟 为了便
  • FreeRTOS之系统配置

    1 FreeRTOS的系统配置文件为FreeRTOSConfig h 在此配置文件中可以完成FreeRTOS的裁剪和配置 在官方的demo中 每个工程都有一个该文件 2 先说一下 INCLUDE 开始的宏 使用 INCLUDE 开头的宏用来
  • 再论FreeRTOS中的configTOTAL_HEAP_SIZE

    关于任务栈和系统栈的基础知识 可以参考之前的随笔 FreeRTOS 任务栈大小确定及其溢出检测 这里再次说明 define configTOTAL HEAP SIZE size t 17 1024 这个宏 官方文档解释 configTOTA
  • FreeRTOS 匈牙利表示法 [重复]

    这个问题在这里已经有答案了 我是 RTOS 和 C 编程的新手 而且我仍在习惯 C 的良好实践 因此 我打开了一个使用 FreeRTOS 的项目 我注意到操作系统文件使用匈牙利表示法 我知道一点符号 但面临一些新的 标准 FreeRTOS
  • 有可用的 FreeRTOS 解释语言库吗?

    我在一家公司工作 该公司使用 FreeRTOS 为多个设备创建固件 最近 我们对新功能的要求已经超出了我们固件工程师的工作能力 但我们现在也无力雇用任何新人 即使进行微小的更改 也需要固件人员在非常低的级别上进行修改 我一直在为 FreeR
  • 哪些变量类型/大小在 STM32 微控制器上是原子的?

    以下是 STM32 微控制器上的数据类型 http www keil com support man docs armcc armcc chr1359125009502 htm http www keil com support man d

随机推荐