赞
踩
我刚学FreeROTS时想移植到STM32,找了网上很多资料,但大多都不是很完整,于是我把我自己的移植过程分享出来,供大家参考。
我们以STM32F103ZE,正点原子的跑马灯实验为例,
准备工作:
跑马灯实验工程
FreeRTOS文件源码(可在官方下载)
第一步 移植文件到工程
首先在工程目录新建一个名为FreeRTOS的文件夹
然后打开从FreeRTOS官方下载的文件中路径为FreeRTOSv202212.01\FreeRTOS中的Source
文件夹
将里面的文件全部复制到工程目录的FreeRTOS文件夹
为了更加简洁,我们新建一个Source文件夹,将外面的.c文件放进去
回到官方下载的FreeRTOS文件中,在Demo文件夹中找到对应的内核
打开文件夹复制里面的FreeRTOSCongic.h文件放到工程文件FreeRTOS文件夹中的include目录里
这是我们的文件移植已经完成了
到目前为止FreeRTOS源码被我们分成三部分
① include目录
② portable目录
③ source目录
①和③包含的是FreeRTOS核心功能源文件及头文件 .c和.h,这两部分的文件是通用的,基本不需要修改,②为需要移植修改的目录,这与编译器和所使用的CPU有关,属于RTOS硬件接口层。
Portable目录是系统和硬件的桥梁,所以我们下一步就要在Portable文件夹中找到自己MCU与编译环境的文件
只需要保留这三个文件夹,其余的可删除
打开工程,新建一个名为 FreeRTOS_COR 的组,把Source目录的全部文件添加进去
然后再新建一个名为 FreeRTOS_PORTABLE 的组,添加Portable目录中的MemMang文件夹中的heap4.c(这是重要的内存管理文件)
再添加portable->RVDS->AMR_CM3中的port文件
最终是这样的
然后添加它们的头文件
编译发现没有错误
虽然没有错误了,但还有些步骤要做
先把FreeRTOSConfig.h文件添加进工程
然后在FreeRTOSConfig.h中添加
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
编译,发现有重复定义的错误
解决方法:进入对应的文件stm32f1xx_it.c屏蔽重复的3个函数
把 SysTick_Handler中断函数也注释了,因为我们等下要在delay文件里建立新的中断函数
再次编译已发现没有错误了
最后还需进行一些配置
将以下代码替换delay.c中的代码
- #include "delay.h"
- #include "FreeRTOS.h"
- #include "task.h"
- //
- //如果使用ucos,则包括下面的头文件即可.
- #if SYSTEM_SUPPORT_UCOS
- #include "includes.h" //ucos 使用
- #endif
-
- #if SYSTEM_SUPPORT_RTOS
- #include "includes.h"
-
- #define OS_TICKS_PER_SEC configTICK_RATE_HZ
- #define OSRunning xSchedulerRunning
-
- #endif
- //
- //本程序只供学习使用,未经作者许可,不得用于其它任何用途
- //ALIENTEK STM32开发板
- //使用SysTick的普通计数模式对延迟进行管理
- //包括delay_us,delay_ms
- //正点原子@ALIENTEK
- //技术论坛:www.openedv.com
- //修改日期:2012/9/2
- //版本:V1.5
- //版权所有,盗版必究。
- //Copyright(C) 广州市星翼电子科技有限公司 2009-2019
- //All rights reserved
- //********************************************************************************
- //V1.2修改说明
- //修正了中断中调用出现死循环的错误
- //防止延时不准确,采用do while结构!
-
- //V1.3修改说明
- //增加了对UCOSII延时的支持.
- //如果使用ucosII,delay_init会自动设置SYSTICK的值,使之与ucos的TICKS_PER_SEC对应.
- //delay_ms和delay_us也进行了针对ucos的改造.
- //delay_us可以在ucos下使用,而且准确度很高,更重要的是没有占用额外的定时器.
- //delay_ms在ucos下,可以当成OSTimeDly来用,在未启动ucos时,它采用delay_us实现,从而准确延时
- //可以用来初始化外设,在启动了ucos之后delay_ms根据延时的长短,选择OSTimeDly实现或者delay_us实现.
-
- //V1.4修改说明 20110929
- //修改了使用ucos,但是ucos未启动的时候,delay_ms中中断无法响应的bug.
- //V1.5修改说明 20120902
- //在delay_us加入ucos上锁,防止由于ucos打断delay_us的执行,可能导致的延时不准。
- //
-
- static u8 fac_us=0;//us延时倍乘数
- static u16 fac_ms=0;//ms延时倍乘数,在ucos下,代表每个节拍的ms数
-
-
- void SysTickInit()
- {
- SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8,HCLK = 72MHZ
- fac_us=SystemCoreClock/8000000; //为系统时钟的1/8 ,计算9次,9
- fac_ms=(u16)fac_us*1000;
- }
- void SysTick_Handler(void)
- {
- if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
- {
- xPortSysTickHandler();
- }
- }
- //初始化延迟函数
- //当使用ucos的时候,此函数会初始化ucos的时钟节拍
- //SYSTICK的时钟固定为HCLK时钟的1/8
- //SYSCLK:系统时钟
- //初始化延迟函数
- //SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
- //这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
- //SYSCLK:系统时钟频率
- void delay_init(u8 SYSCLK)
- {
- u32 reload;
- SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
- fac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用
- reload=SYSCLK; //每秒钟的计数次数 单位为M
- reload*=1000000/configTICK_RATE_HZ; //根据configTICK_RATE_HZ设定溢出时间
- //reload为24位寄存器,最大值:16777216,在168M下,约合0.0998s左右
- fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延时的最少单位
- SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
- SysTick->LOAD=reload; //每1/configTICK_RATE_HZ断一次
- SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
- }
-
-
- //延时nus
- //nus为要延时的us数.
- 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的范围
- //SysTick->LOAD为24位寄存器,所以,最大延时为:
- //nms<=0xffffff*8*1000/SYSCLK
- //SYSCLK单位为Hz,nms单位为ms
- //对72M条件下,nms<=1864
-
-
-
- void 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"
- //
- //本程序只供学习使用,未经作者许可,不得用于其它任何用途
- //ALIENTEK STM32开发板
- //使用SysTick的普通计数模式对延迟进行管理
- //包括delay_us,delay_ms
- //正点原子@ALIENTEK
- //技术论坛:www.openedv.com
- //修改日期:2012/9/2
- //版本:V1.5
- //版权所有,盗版必究。
- //Copyright(C) 广州市星翼电子科技有限公司 2009-2019
- //All rights reserved
- //********************************************************************************
- //V1.2修改说明
- //修正了中断中调用出现死循环的错误
- //防止延时不准确,采用do while结构!
-
- //V1.3修改说明
- //增加了对UCOSII延时的支持.
- //如果使用ucosII,delay_init会自动设置SYSTICK的值,使之与ucos的TICKS_PER_SEC对应.
- //delay_ms和delay_us也进行了针对ucos的改造.
- //delay_us可以在ucos下使用,而且准确度很高,更重要的是没有占用额外的定时器.
- //delay_ms在ucos下,可以当成OSTimeDly来用,在未启动ucos时,它采用delay_us实现,从而准确延时
- //可以用来初始化外设,在启动了ucos之后delay_ms根据延时的长短,选择OSTimeDly实现或者delay_us实现.
-
- //V1.4修改说明 20110929
- //修改了使用ucos,但是ucos未启动的时候,delay_ms中中断无法响应的bug.
- //V1.5修改说明 20120902
- //在delay_us加入ucos上锁,防止由于ucos打断delay_us的执行,可能导致的延时不准。
- //
- void delay_init(u8 SYSCLK);
- void delay_ms(u32 nms);
- void delay_us(u32 nus);
- void delay_xms(u32 nms);
- void SysTickInit();
- #endif
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

再次编译提示“xTaskGetSchedulerState”没有定义
我们只需在FReeRTOS.h中加上这句定义就行了
#define INCLUDE_xTaskGetSchedulerState 1
再次编译0错误
这样移植工作就完成了
加上主程序,实现点灯实验
- #include "stm32f10x.h"
- #include "delay.h"
- #include "usart.h"
- #include "LED.H"
- /* FreeRTOS头文件 */
- #include "FreeRTOS.h"
- #include "task.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 50 //任务栈大小
- TaskHandle_t LED0Task_Handler; //任务句柄
- void led0_task(void *pvParameters); //任务函数
-
- #define LED1_TASK_PRIO 3 //任务优先级
- #define LED1_STK_SIZE 50 //任务栈大小
- TaskHandle_t LED1Task_Handler; //任务句柄
- void led1_task(void *pvParameters); //任务函数
-
-
- static int TaskRun1,TaskRun2; //用来观察任务运行
-
- void BoardInit(void) //设置初始化环境
- {
- SysTickInit(); //系统时钟配置
- delay_init(72); //延迟函数配置
- LED_Init();
- uart_init(115200);
- }
-
-
- //开始任务任务函数
- 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(); //退出临界区
- }
-
-
- int main(void)
- {
- // BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
- BoardInit(); //设置初始化环境
- printf("Welcome to FreeRTOS,CoreClock:%d\r\n",SystemCoreClock);
-
- //创建开始任务
- 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(); //开启任务调度
- }
-
- //LED0任务函数
- void led0_task(void *pvParameters)
- {
- while(1)
- {
- TaskRun1=1;
- TaskRun2=0;
- LED0=~LED0;
- printf("Task1Running\n");
- vTaskDelay(500);
- }
- }
-
- //LED1任务函数
- void led1_task(void *pvParameters)
- {
- while(1)
- {
- TaskRun1=0;
- TaskRun2=1;
- LED1=~LED1;
- printf("Task2Running\n");
- vTaskDelay(800);
- }
- }

成功点亮!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。