freertos随意记录 freertos是一个开源免费的嵌入式实时操作系统非常好用也比较简单对freertos的学习过程做一些记录。1、移植freertos的移植应该是比较简单的下载官方最新版的LTS版本然后拷贝一些文件和做一些设置就行了。a、文件复制在目标工程下新建文件夹freertos将freertos-kernel根目录下的C文件复制到前面的文件夹中复制including文件夹复制portable文件夹中的文件我使用的是正点原子的STM32H750开发板使用的keil ac6创建的工程文件目录及文件如下另外freertos还有内存管理复制下列文件到工程目录freertos还需要一个用于裁剪代码的配置文件直接复制example里面的至此文件的拷贝已经完成了。完整目录结构如下b、工程配置新建一个hal工程把开发板上的led灯和串口初始化然后配置工程添加头文件freertos的任务切换有几个非常重要的中断函数PendSV_Handler SysTick_Handler SVC_Handler这是任务切换的关键需要注释掉hal工程中stm32h7xx_it.c中的这几个函数并在freertosconfig.h文件中添加下列代码让stm32h750的中断和freertos内核的中断关联起来freertos的框架已经搭建起来了下面需要调用freertos的入口函数vTaskStartScheduler()这个地方要注意不要添加错了我之前就添加成xPortStartScheduler()找了好久才发现。vTaskStartScheduler里面会调用xPortStartScheduler不过在这之前需要进行一些闲置任务这些初始化。完成上面这些freertos已经在运行了为了观看效果新建几个任务这是几个简单的任务led和printf任务其中led是可以阻塞的print则没有阻塞。非常不建议print这种写法因为该任务不会离开RUN状态会一直执行导致优先级比他低的任务没有机会执行包括IDLE任务这里仅仅是为了测试项目中千万不要这样写每个任务一定需要离开RUN状态要么延时等待要么等待信号量这些。通过测试print优先级18几个任务都可以正常运行print优先级高于20print任务正常运行led任务无法运行。2、学习记录任务切换原理某个任务正在执行但是系统时钟也在运行其中systick中断设置为1000HZ每1ms进入一次systick中断看下systickHandler干了啥#define xPortSysTickHandler SysTick_Handler 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. */ // 屏蔽掉中断优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断我们认为优先级低于这个值的中断可以被屏蔽 portDISABLE_INTERRUPTS(); traceISR_ENTER(); { /* Increment the RTOS tick. */ // 系统tick累加 if( xTaskIncrementTick() ! pdFALSE ) { traceISR_EXIT_TO_SCHEDULER(); /* A context switch is required. Context switching is performed in * the PendSV interrupt. Pend the PendSV interrupt. */ // 产生pendSV中断请求 // portmacro.h文件如下 // #define portNVIC_INT_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000ed04 ) ) // #define portNVIC_PENDSVSET_BIT ( 1UL 28UL ) portNVIC_INT_CTRL_REG portNVIC_PENDSVSET_BIT; } else { traceISR_EXIT(); } } portENABLE_INTERRUPTS(); }其中产生pendSV中断请求从上面的代码可以看出systick中断就干了一件事产生pendSV中断。下面看看pendSV中断又干了啥void xPortPendSVHandler( void ) { /* This is a naked function. */ __asm volatile ( mrs r0, psp \n isb \n \n ldr r3, pxCurrentTCB \n /* Get the location of the current TCB. */ ldr r2, [r3] \n \n tst r14, #0x10 \n /* Is the task using the FPU context? If so, push high vfp registers. */ it eq \n vstmdbeq r0!, {s16-s31} \n \n stmdb r0!, {r4-r11, r14} \n /* Save the core registers. */ str r0, [r2] \n /* Save the new top of stack into the first member of the TCB. */ \n stmdb sp!, {r0, r3} \n mov r0, %0 \n cpsid i \n /* ARM Cortex-M7 r0p1 Errata 837070 workaround. */ msr basepri, r0 \n dsb \n isb \n cpsie i \n /* ARM Cortex-M7 r0p1 Errata 837070 workaround. */ bl vTaskSwitchContext \n mov r0, #0 \n msr basepri, r0 \n ldmia sp!, {r0, r3} \n \n ldr r1, [r3] \n /* The first item in pxCurrentTCB is the task top of stack. */ ldr r0, [r1] \n \n ldmia r0!, {r4-r11, r14} \n /* Pop the core registers. */ \n tst r14, #0x10 \n /* Is the task using the FPU context? If so, pop the high vfp registers too. */ it eq \n vldmiaeq r0!, {s16-s31} \n \n msr psp, r0 \n isb \n \n #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */ #if WORKAROUND_PMU_CM001 1 push { r14 } \n pop { pc } \n #endif #endif \n bx r14 \n \n .ltorg \n ::i ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) ); } void vTaskSwitchContext( void ) { /** 无关代码 ***/ // 查找当前ready的最高优先级并把pxCurrentTCB赋值为最高优先级TCB中的下一个TCB taskSELECT_HIGHEST_PRIORITY_TASK(); portTASK_SWITCH_HOOK( pxCurrentTCB ); } #define taskSELECT_HIGHEST_PRIORITY_TASK() \ do { \ UBaseType_t uxTopPriority uxTopReadyPriority; \ \ /* Find the highest priority queue that contains ready tasks. */ \ while( listLIST_IS_EMPTY( ( pxReadyTasksLists[ uxTopPriority ] ) ) ! pdFALSE ) \ { \ 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; \ } while( 0 ) /* taskSELECT_HIGHEST_PRIORITY_TASK */ ldr r3, pxCurrentTCB \n /* Get the location of the current TCB. */获取到当前的任务的TCB bl vTaskSwitchContext \n任务切换函数寻找处于ready的最高优先级并把pxCurrentTCB赋值为最高优先级中的下一个如果有如果没有不变后面的代码看注释知道是加载pxCurrentTCB中的寄存器值然后进行跳转完成任务切换。最后看一下SVCHandlervoid vPortSVCHandler( void ) { __asm volatile ( ldr r3, pxCurrentTCB \n /* Restore the context. */ ldr r1, [r3] \n /* Get the pxCurrentTCB address. */ ldr r0, [r1] \n /* The first item in pxCurrentTCB is the task top of stack. */ ldmia r0!, {r4-r11, r14} \n /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ msr psp, r0 \n /* Restore the task stack pointer. */ isb \n mov r0, #0 \n msr basepri, r0 \n bx r14 \n \n .ltorg \n ); } static void prvPortStartFirstTask( void ) { /* Start the first task. This also clears the bit that indicates the FPU is * in use in case the FPU was used before the scheduler was started - which * would otherwise result in the unnecessary leaving of space in the SVC stack * for lazy saving of FPU registers. */ __asm volatile ( ldr r0, 0xE000ED08 \n /* Use the NVIC offset register to locate the stack. */ ldr r0, [r0] \n ldr r0, [r0] \n msr msp, r0 \n /* Set the msp back to the start of the stack. */ mov r0, #0 \n /* Clear the bit that indicates the FPU is in use, see comment above. */ msr control, r0 \n cpsie i \n /* Globally enable interrupts. */ cpsie f \n dsb \n isb \n svc 0 \n /* System call to start first task. */ nop \n .ltorg \n ); } BaseType_t xPortStartScheduler( void ) { prvPortStartFirstTask(); }这个代码貌似只执行一次就是在vTaskStartScheduler中会执行一次启动第一个任务。configMAX_SYSCALL_INTERRUPT_PRIORITY设置系统最高优先级即如果代码中有优先级大于优先级数值小于configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断不受freertos控制可以正常产生中断如果代码中有优先级小于configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断则会因为freertos调用vPortEnterCritical()函数进入临界状态无法进入中断。比如串口非常重要不想让串口被系统打断那么如果configMAX_SYSCALL_INTERRUPT_PRIORITY设置为16串口的中断优先级就需要设置为16;按键中断不是很重要可能在发送数据的时候不需要响应按键可以把按键的中断优先级设置为大于16那么在进入临界状态时候按键中断是无法响应的。// 进入临界状态禁止中断 void vPortEnterCritical( void ) { // 进入临界状态禁止中断 portDISABLE_INTERRUPTS(); 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 ); } } #define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() #define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() // 将configMAX_SYSCALL_INTERRUPT_PRIORITY 的值写入basepri屏蔽优先级小于configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断优先级数值大于configMAX_SYSCALL_INTERRUPT_PRIORITY portFORCE_INLINE static void vPortRaiseBASEPRI( void ) { uint32_t ulNewBASEPRI; __asm volatile ( mov %0, %1 \n \ cpsid i \n \ msr basepri, %0 \n \ isb \n \ dsb \n \ cpsie i \n \ : r ( ulNewBASEPRI ) : i ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : memory ); }在更精巧的设计中需要对中断掩蔽进行更细腻的控制——只掩蔽优先级低于某一阈值 的中断——它们的优先级在数字上大于等于某个数。那么这个数存储在哪里就存储在 BASEPRI中。不过如果往BASEPRI中写0则另当别论——BASEPRI将停止掩蔽任何中断。例 如如果你需要掩蔽所有优先级不高于0x60的中断则可以如下编程 MOV R0, #0x60 MSR BASEPRI, R0 如果需要取消BASEPRI 对中断的掩蔽则示例代码如下 MOV R0, #0 MSR BASEPRI, R0 《CORTEX M3 权威指南》退出临界状态void vPortExitCritical( void ) { configASSERT( uxCriticalNesting ); uxCriticalNesting--; if( uxCriticalNesting 0 ) { portENABLE_INTERRUPTS(); } } #define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS() #define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) // 将0写入basepri恢复所有中断 portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue ) { __asm volatile ( msr basepri, %0 ::r ( ulNewMaskValue ) : memory ); }待续...

本周精选

本月热点