STM32 HAL库 +freeRTOS+Keil 移植
官方freeRTOS移植教程很多,本文不做赘述。本文基于Keil 5提供的freeRTOS库,进行移植。
Keil 版本:Keil MDK uVision5
首先准备好一个可以用的HAL库工程,为了方便调试,选择了原子哥的开发板上提供跑马灯工程。
打开工程,
点击,下载freeRTOS库,
我的已经安装过了 最新的版本10.3.1
下载完成后关闭,点击 ,在这个界面下,按下图选择对应的选项
选择完成后点击OK( 如果没有先下载),可以看到工程中已经添加RTOS
点击编译下,
有一个错误先不用管,接下来开始修改文件,先打开RTE_Components.h
将 #define CMSIS_device_header "stm32f10x.h" 修改成 #define CMSIS_device_header "stm32f1xx_hal.h" ,keil下载的默认是库函数版移植的,需要将头文件变更成"stm32f1xx_hal.h"
接下来修改FREERTOS_CONFIG.H,打开头文件FREERTOS_CONFIG.H,
将#define configUSE_TIMERS 0 修改成 #define configUSE_TIMERS 1
将#define xPortSysTickHandler SysTick_Handler 屏蔽
编译下,出现两个错误
打开 stm32f1xx_it.c,
将PendSV_Handler(void) 、SVC_Handler(void)、SysTick_Handler(void)3个函数 屏蔽
打开sys.h 将 #define SYSTEM_SUPPORT_OS 0 改成#define SYSTEM_SUPPORT_OS 1
打开delay.c 将内部所有代码替换
#include "delay.h"#include "sys.h"#include "FreeRTOS.h"#include "task.h" static u32 fac_us=0; //us延时倍乘数static u16 fac_ms=0; //ms延时倍乘数,在os下,代表每个节拍的ms数 extern void xPortSysTickHandler(void); //systick中断服务函数,使用ucos时用到void SysTick_Handler(void) { if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行 { xPortSysTickHandler(); } HAL_IncTick(); } //初始化延迟函数//当使用ucos的时候,此函数会初始化ucos的时钟节拍//SYSTICK的时钟固定为AHB时钟//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设定溢出时间 //reload为24位寄存器,最大值:16777216,在72M下,约合0.233s左右 fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延时的最少单位 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断 SysTick->LOAD=reload; //每1/OS_TICKS_PER_SEC秒中断一次 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK} //延时nus//nus:要延时的us数. //nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5) void delay_us(u32 nus) { u32 ticks; u32 told,tnow,tcnt=0; u32 reload=SysTick->LOAD; //LOAD的值 ticks=nus*fac_us; //需要的节拍数 told=SysTick->VAL; //刚进入时的计数器值 while(1) { tnow=SysTick->VAL; if(tnow!=told) { if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了. else tcnt+=reload-tnow+told; told=tnow; if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出. } }; } //延时nms//nms:要延时的ms数//nms:0~65535void delay_ms(u32 nms) { if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行 { if(nms>=fac_ms) //延时的时间大于OS的最少时间周期 { vTaskDelay(nms/fac_ms); //FreeRTOS延时 } nms%=fac_ms; //OS已经无法提供这么小的延时了,采用普通方式延时 } delay_us((u32)(nms*1000)); //普通方式延时}//延时nms,不会引起任务调度//nms:要延时的ms数void delay_xms(u32 nms) { u32 i; for(i=0;i<nms;i++) delay_us(1000); }
打开delay.h将内部所有代码替换
#ifndef __DELAY_H#define __DELAY_H #include "sys.h" void delay_init(u8 SYSCLK);void delay_ms(u32 nms);void delay_us(u32 nus);void SysTick_Handler(void);void delay_xms(u32 nms);#endif
打开usart.c将代码
#if SYSTEM_SUPPORT_OS#include "includes.h" //ucos 使用 #endif
替换成
#if SYSTEM_SUPPORT_OS#include "FreeRTOS.h"#include "task.h" #endif
将代码段
//#if SYSTEM_SUPPORT_OS //使用OS// OSIntEnter(); //#endif
和
//#if SYSTEM_SUPPORT_OS //使用OS// OSIntExit(); //#endif
屏蔽
编译没有错误
打开main.c 将所有代码替换成
#include "sys.h"#include "delay.h"#include "usart.h"#include "led.h"#include "FreeRTOS.h"#include "task.h"#include "FreeRTOSConfig.h"//任务优先级#define START_TASK_PRIO 1//任务堆栈大小 #define START_STK_SIZE 128 //任务句柄TaskHandle_t StartTask_Handler;//任务函数void start_task(void *pvParameters);//任务优先级#define LED0_TASK_PRIO 2//任务堆栈大小 #define LED0_STK_SIZE 20 //任务句柄TaskHandle_t LED0Task_Handler;//任务函数void led0_task(void *pvParameters);//任务优先级#define LED1_TASK_PRIO 3//任务堆栈大小 #define LED1_STK_SIZE 20 //任务句柄TaskHandle_t LED1Task_Handler;//任务函数void led1_task(void *pvParameters);int main(void) { HAL_Init(); //初始化HAL库 Stm32_Clock_Init(RCC_PLL_MUL10); //设置时钟,72M delay_init(70); //初始化延时函数 LED_Init(); //初始化LED //创建开始任务 xTaskCreate((TaskFunction_t )start_task, //任务函数 (const char* )"start_task", //任务名称 (uint16_t )START_STK_SIZE, //任务堆栈大小 (void* )NULL, //传递给任务函数的参数 (UBaseType_t )START_TASK_PRIO, //任务优先级 (TaskHandle_t* )&StartTask_Handler); //任务句柄 vTaskStartScheduler(); //开启任务调度 return(1);// while(1){// PAout(6)=~PAout(6);// delay_ms(500);// PAout(7)=~PAout(7);// delay_ms(1000);// }; }void start_task(void *pvParameters) { taskENTER_CRITICAL(); //进入临界区 //创建LED0任务 xTaskCreate((TaskFunction_t )led0_task, (const char* )"led0_task", (uint16_t )LED0_STK_SIZE, (void* )NULL, (UBaseType_t )LED0_TASK_PRIO, (TaskHandle_t* )&LED0Task_Handler); //创建LED1任务 xTaskCreate((TaskFunction_t )led1_task, (const char* )"led1_task", (uint16_t )LED1_STK_SIZE, (void* )NULL, (UBaseType_t )LED1_TASK_PRIO, (TaskHandle_t* )&LED1Task_Handler); vTaskDelete(StartTask_Handler); //删除开始任务 taskEXIT_CRITICAL(); //退出临界区}//LED0任务函数 void led0_task(void *pvParameters) { while(1) { LED0=~LED0; vTaskDelay(1000); } } //LED1任务函数void led1_task(void *pvParameters) { while(1) { LED1=~LED1; vTaskDelay(2000); } }
编译后下载,发现没有反应跑马灯不亮,调试发现卡死在下面这条指令处
/* The kernel interrupt priority should be set to the lowest priority. */ configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) );
打开"FreeRTOSConfig.h" 将下面的代码段屏蔽
/* Cortex-M specific definitions. */#ifdef __NVIC_PRIO_BITS /* __NVIC_PRIO_BITS will be specified when CMSIS is being used. */ #define configPRIO_BITS __NVIC_PRIO_BITS#else /* 7 priority levels */ #define configPRIO_BITS 3#endif
将
/* Interrupt priorities used by the kernel port layer itself. These are generic * to all Cortex-M ports, and do not rely on any particular library functions. */#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! * See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
修改成
#define configKERNEL_INTERRUPT_PRIORITY 255//(configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! * See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */#define configMAX_SYSCALL_INTERRUPT_PRIORITY 191//(configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
编译后下载,发现跑马灯常亮,
调试发现代码没有卡死,这个问题困扰我了还久,
再次打开"FreeRTOSConfig.h" 将下面
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE * 2)
修改成
#define configTIMER_TASK_STACK_DEPTH 256
编译下载 ,成功了!!!