FreeRTOS stm32 完整移植包 正式版 下载

FreeRTOS stm32 完整移植包 正式版

  • 分   类:行业软件
  • 大   小:8.41 MB
  • 适合系统:Win7/WinXP/Win8/Win10兼容软件
  • 系统位数:-
  • 最新版本:
  • 评   分:8
  • 下载量:2次
  • 发   布:2021-03-23

#简介

介绍freertos在stm32上的移植说明,每一步都很详细,让你学会在stm32+freeRTOS的平台上开发应用程序一、各文件关键部分的实现:1、PORTMACRO.H 宏定义部分 1)定义编译器相关的各种数据类型 define portCHAR char define portFLOA...

#内容

介绍freertos在stm32上的移植说明,每一步都很详细,让你学会在stm32+freeRTOS的平台上开发应用程序一、各文件关键部分的实现:1、PORTMACRO.H 宏定义部分 1)定义编译器相关的各种数据类型 define portCHAR char define portFLOAT float define portDOUBLE double define portLONG long define portSHORT short define portSTACK_TYPE unsigned portLONG define portBASE_TYPE long 2)架构相关的定义 Cortex-M3的堆栈增长方向为高地址向低地址增长 define portSTACK_GROWTH ( -1 ) 每毫秒的心跳次数 define portTICK_RATE_MS ( ( portTickType ) 1000 / configTICK_RATE_HZ ) 访问SRAM的字节对齐 define portBYTE_ALIGNMENT 8 3)定义用户主动引起内核调度的2个函数 强制上下文切换,用在任务环境中调用 define portYIELD() vPortYieldFromISR() 强制上下文切换,用在中断处理环境中调用 define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) vPortYieldFromISR() 4)定义临界区的管理函数 中断允许和关闭 define portDISABLE_INTERRUPTS() vPortSetInterruptMask() define portENABLE_INTERRUPTS() vPortClearInterruptMask() 临界区进入和退出 define portENTER_CRITICAL() vPortEnterCritical() define portEXIT_CRITICAL() vPortExitCritical() 用于在中断环境的中断允许和关闭 define portSET_INTERRUPT_MASK_FROM_ISR() 0;vPortSetInterruptMask() define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask();(void)x 2、PORT.C C接口部分 1)堆栈初始化 portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ) { *pxTopOfStack = portINITIAL_XPSR; /* 程序状态寄存器 */ pxTopOfStack--; *pxTopOfStack = ( portSTACK_TYPE ) pxCode; /* 任务的入口点 */ pxTopOfStack--; *pxTopOfStack = 0; /* LR */ pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ *pxTopOfStack = ( portSTACK_TYPE ) pvParameters; /* 任务的参数 */ pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ return pxTopOfStack; } 2)启动任务调度 portBASE_TYPE xPortStartScheduler( void ) { 让任务切换中断和心跳中断位于最低的优先级,使更高优先级可以抢占mcu *(portNVIC_SYSPRI2) |= portNVIC_PENDSV_PRI; *(portNVIC_SYSPRI2) |= portNVIC_SYSTICK_PRI; 设置并启动系统的心跳时钟 prvSetupTimerInterrupt(); 初始化临界区的嵌套的个数 uxCriticalNesting = 0; 启动第一个任务 vPortStartFirstTask(); 执行到vPortStartFirstTask函数,内核已经开始正常的调度 return 0; } 3)主动释放mcu使用权 void vPortYieldFromISR( void ) { 触发PendSV系统服务中断,中断到来时由汇编函数xPortPendSVHandler()处理 *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET; } 进入临界区时,首先关闭中断;当退出所以嵌套的临界区后再使能中断 void vPortEnterCritical( void ) { portDISABLE_INTERRUPTS(); uxCriticalNesting++; } void vPortExitCritical( void ) { uxCriticalNesting--; if( uxCriticalNesting == 0 ) { portENABLE_INTERRUPTS(); } } 4)心跳时钟处理函数 void xPortSysTickHandler( void ) { unsigned portLONG ulDummy; 如果是抢占式调度,首先看一下有没有需要调度的任务 if configUSE_PREEMPTION == 1 *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET; endif ulDummy = portSET_INTERRUPT_MASK_FROM_ISR(); { 通过task.c的心跳处理函数vTaskIncrementTick(),进行时钟计数和延时任务的处理 vTaskIncrementTick(); } portCLEAR_INTERRUPT_MASK_FROM_ISR( ulDummy ); } 3、PORTASM.S 汇编处理部分 1)请求切换任务 xPortPendSVHandler: 保存当前任务的上下文到其任务控制块 mrs r0, psp ldr r3, =pxCurrentTCB 获取当前任务的任务控制块指针 ldr r2, [r3] stmdb r0!, {r4-r11} 保存R4-R11到该任务的堆栈 str r0, [r2] 将最后的堆栈指针保存到任务控制块的pxTopOfStack stmdb sp!, {r3, r14} 关闭中断 mov r0, configMAX_SYSCALL_INTERRUPT_PRIORITY msr basepri, r0 切换任务的上下文,pxCurrentTCB已指向新的任务 bl vTaskSwitchContext mov r0, 0 msr basepri, r0 ldmia sp!, {r3, r14} 恢复新任务的上下文到各寄存器 ldr r1, [r3] ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */ ldmia r0!, {r4-r11} /* Pop the registers. */ msr psp, r0 bx r14 2.)中断允许和关闭的实现,通过BASEPRI屏蔽相应优先级的中断源 vPortSetInterruptMask: push { r0 } mov R0, configMAX_SYSCALL_INTERRUPT_PRIORITY msr BASEPRI, R0 pop { R0 } bx r14 vPortClearInterruptMask: PUSH { r0 } MOV R0, 0 MSR BASEPRI, R0 POP { R0 } bx r14 3)直接切换任务,用于vPortStartFirstTask第一次启动任务时初始化堆栈和各寄存器 vPortSVCHandler; ldr r3, =pxCurrentTCB ldr r1, [r3] ldr r0, [r1] ldmia r0!, {r4-r11} msr psp, r0 mov r0, 0 msr basepri, r0 orr r14, r14, 13 bx r14 4)启动第一个任务的汇编实现 vPortStartFirstTask 通过中断向量表的定位堆栈的地址 ldr r0, =0xE000ED08 向量表偏移量寄存器 (VTOR) ldr r0, [r0] ldr r0, [r0] msr msp, r0 将堆栈地址保存到主堆栈指针msp中 触发SVC软中断,由vPortSVCHandler()完成第一个任务的具体切换工作 svc 0 FreeRTOS内核调度器启动的流程如下: 以上3个文件实现了FreeRTOS内核调度所需的底层接口,相关代码十分精简。 二、创建测试任务:下面创建第一个测试任务,LED测试 int main( void ) { 设置系统时钟,中断向量表和LED使用的GPIO 使用stm32的固件包提供的初始化函数,具体说明见相关手册 prvSetupHardware(); 通过xTaskCreate()创建4个LED任务vLEDFlashTask(), 每个任务根据各自的频率闪烁,分别对应开发板上的4个LED vStartLEDFlashTasks( mainFLASH_TASK_PRIORITY ); ? 创建一个IDLE任务后,通过xPortStartScheduler启动调度器 vTaskStartScheduler(); 调度器工作不正常时返回 return 0; } portTASK_FUNCTION()是FreeRTOS定义的函数声明,没特殊作用 static portTASK_FUNCTION( vLEDFlashTask, pvParameters ) { ……省略……,具体为计算各LED的闪烁频率 for(;;) { vTaskDelayUntil( &xLastFlashTime, xFlashRate ); vParTestToggleLED( uxLED ); vTaskDelayUntil()的延时时间xFlashRate,是从上一次的延时时间xLastFlashTime算起的, 相对vTaskDelay()的直接延时更为精准。 vTaskDelayUntil( &xLastFlashTime, xFlashRate ); vParTestToggleLED( uxLED ); } } FreeRTOS的任务创建与UC/OSII差异不大,主要参数为任务函数,堆栈大小和任务的优先级。如: xTaskCreate( vLEDFlashTask, ( signed portCHAR * ) "LEDx", ledSTACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL ); 下面再创建一个LCD显示任务,以最低优先级运行: xTaskCreate( vLCDTask, ( signed portCHAR * ) "LCD", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); void vLCDTask( void *pvParameters ) { ……省略…… for( ;; ) { vTaskDelay(1000); printf("%c ", usDisplayChar); } } 该任务很简单,每隔1000个ticks(就是1000ms),从LCD上刷新一个数字。 至此,FreeRTOS在STM32上的移植基本完成。与UC/OSII相比,FreeRTOS精简的实现更适合用来学习实时操作系统的工作原理,对其进行剖析也相对容易。 接下来,我们将会移植CAN,RS485,SD卡和USB等接口到FreeRTOS,使其在STM32平台上更加完善。