当前位置:   article > 正文

FreeRTOS移植STM32超详细(以STM32F103ZE为例)_freertos移植到stm32

freertos移植到stm32

我刚学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中的代码

  1. #include "delay.h"
  2. #include "FreeRTOS.h"
  3. #include "task.h"
  4. //
  5. //如果使用ucos,则包括下面的头文件即可.
  6. #if SYSTEM_SUPPORT_UCOS
  7. #include "includes.h" //ucos 使用
  8. #endif
  9. #if SYSTEM_SUPPORT_RTOS
  10. #include "includes.h"
  11. #define OS_TICKS_PER_SEC configTICK_RATE_HZ
  12. #define OSRunning xSchedulerRunning
  13. #endif
  14. //
  15. //本程序只供学习使用,未经作者许可,不得用于其它任何用途
  16. //ALIENTEK STM32开发板
  17. //使用SysTick的普通计数模式对延迟进行管理
  18. //包括delay_us,delay_ms
  19. //正点原子@ALIENTEK
  20. //技术论坛:www.openedv.com
  21. //修改日期:2012/9/2
  22. //版本:V1.5
  23. //版权所有,盗版必究。
  24. //Copyright(C) 广州市星翼电子科技有限公司 2009-2019
  25. //All rights reserved
  26. //********************************************************************************
  27. //V1.2修改说明
  28. //修正了中断中调用出现死循环的错误
  29. //防止延时不准确,采用do while结构!
  30. //V1.3修改说明
  31. //增加了对UCOSII延时的支持.
  32. //如果使用ucosII,delay_init会自动设置SYSTICK的值,使之与ucos的TICKS_PER_SEC对应.
  33. //delay_ms和delay_us也进行了针对ucos的改造.
  34. //delay_us可以在ucos下使用,而且准确度很高,更重要的是没有占用额外的定时器.
  35. //delay_ms在ucos下,可以当成OSTimeDly来用,在未启动ucos时,它采用delay_us实现,从而准确延时
  36. //可以用来初始化外设,在启动了ucos之后delay_ms根据延时的长短,选择OSTimeDly实现或者delay_us实现.
  37. //V1.4修改说明 20110929
  38. //修改了使用ucos,但是ucos未启动的时候,delay_ms中中断无法响应的bug.
  39. //V1.5修改说明 20120902
  40. //在delay_us加入ucos上锁,防止由于ucos打断delay_us的执行,可能导致的延时不准。
  41. //
  42. static u8 fac_us=0;//us延时倍乘数
  43. static u16 fac_ms=0;//ms延时倍乘数,在ucos下,代表每个节拍的ms数
  44. void SysTickInit()
  45. {
  46. SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8,HCLK = 72MHZ
  47. fac_us=SystemCoreClock/8000000; //为系统时钟的1/8 ,计算9次,9
  48. fac_ms=(u16)fac_us*1000;
  49. }
  50. void SysTick_Handler(void)
  51. {
  52. if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
  53. {
  54. xPortSysTickHandler();
  55. }
  56. }
  57. //初始化延迟函数
  58. //当使用ucos的时候,此函数会初始化ucos的时钟节拍
  59. //SYSTICK的时钟固定为HCLK时钟的1/8
  60. //SYSCLK:系统时钟
  61. //初始化延迟函数
  62. //SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
  63. //这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
  64. //SYSCLK:系统时钟频率
  65. void delay_init(u8 SYSCLK)
  66. {
  67. u32 reload;
  68. SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
  69. fac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用
  70. reload=SYSCLK; //每秒钟的计数次数 单位为M
  71. reload*=1000000/configTICK_RATE_HZ; //根据configTICK_RATE_HZ设定溢出时间
  72. //reload为24位寄存器,最大值:16777216,在168M下,约合0.0998s左右
  73. fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延时的最少单位
  74. SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断
  75. SysTick->LOAD=reload; //每1/configTICK_RATE_HZ断一次
  76. SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
  77. }
  78. //延时nus
  79. //nus为要延时的us数.
  80. void delay_us(u32 nus)
  81. {
  82. u32 ticks;
  83. u32 told,tnow,tcnt=0;
  84. u32 reload=SysTick->LOAD; //LOAD的值
  85. ticks=nus*fac_us; //需要的节拍数
  86. told=SysTick->VAL; //刚进入时的计数器值
  87. while(1)
  88. {
  89. tnow=SysTick->VAL;
  90. if(tnow!=told)
  91. {
  92. if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
  93. else tcnt+=reload-tnow+told;
  94. told=tnow;
  95. if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
  96. }
  97. };
  98. }
  99. //延时nms
  100. //注意nms的范围
  101. //SysTick->LOAD为24位寄存器,所以,最大延时为:
  102. //nms<=0xffffff*8*1000/SYSCLK
  103. //SYSCLK单位为Hz,nms单位为ms
  104. //对72M条件下,nms<=1864
  105. void delay_ms(u32 nms)
  106. {
  107. if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
  108. {
  109. if(nms>=fac_ms) //延时的时间大于OS的最少时间周期
  110. {
  111. vTaskDelay(nms/fac_ms); //FreeRTOS延时
  112. }
  113. nms%=fac_ms; //OS已经无法提供这么小的延时了,采用普通方式延时
  114. }
  115. delay_us((u32)(nms*1000)); //普通方式延时
  116. }
  117. //延时nms,不会引起任务调度
  118. //nms:要延时的ms数
  119. void delay_xms(u32 nms)
  120. {
  121. u32 i;
  122. for(i=0;i<nms;i++)delay_us(1000);
  123. }

delay.h同理

  1. #ifndef __DELAY_H
  2. #define __DELAY_H
  3. #include "sys.h"
  4. //
  5. //本程序只供学习使用,未经作者许可,不得用于其它任何用途
  6. //ALIENTEK STM32开发板
  7. //使用SysTick的普通计数模式对延迟进行管理
  8. //包括delay_us,delay_ms
  9. //正点原子@ALIENTEK
  10. //技术论坛:www.openedv.com
  11. //修改日期:2012/9/2
  12. //版本:V1.5
  13. //版权所有,盗版必究。
  14. //Copyright(C) 广州市星翼电子科技有限公司 2009-2019
  15. //All rights reserved
  16. //********************************************************************************
  17. //V1.2修改说明
  18. //修正了中断中调用出现死循环的错误
  19. //防止延时不准确,采用do while结构!
  20. //V1.3修改说明
  21. //增加了对UCOSII延时的支持.
  22. //如果使用ucosII,delay_init会自动设置SYSTICK的值,使之与ucos的TICKS_PER_SEC对应.
  23. //delay_ms和delay_us也进行了针对ucos的改造.
  24. //delay_us可以在ucos下使用,而且准确度很高,更重要的是没有占用额外的定时器.
  25. //delay_ms在ucos下,可以当成OSTimeDly来用,在未启动ucos时,它采用delay_us实现,从而准确延时
  26. //可以用来初始化外设,在启动了ucos之后delay_ms根据延时的长短,选择OSTimeDly实现或者delay_us实现.
  27. //V1.4修改说明 20110929
  28. //修改了使用ucos,但是ucos未启动的时候,delay_ms中中断无法响应的bug.
  29. //V1.5修改说明 20120902
  30. //在delay_us加入ucos上锁,防止由于ucos打断delay_us的执行,可能导致的延时不准。
  31. //
  32. void delay_init(u8 SYSCLK);
  33. void delay_ms(u32 nms);
  34. void delay_us(u32 nus);
  35. void delay_xms(u32 nms);
  36. void SysTickInit();
  37. #endif

再次编译提示“xTaskGetSchedulerState”没有定义

 

 

我们只需在FReeRTOS.h中加上这句定义就行了

#define INCLUDE_xTaskGetSchedulerState          1

 再次编译0错误

这样移植工作就完成了

加上主程序,实现点灯实验

  1. #include "stm32f10x.h"
  2. #include "delay.h"
  3. #include "usart.h"
  4. #include "LED.H"
  5. /* FreeRTOS头文件 */
  6. #include "FreeRTOS.h"
  7. #include "task.h"
  8. #define START_TASK_PRIO 1 //任务优先级
  9. #define START_STK_SIZE 128 //任务栈大小
  10. TaskHandle_t StartTask_Handler; //任务句柄
  11. void start_task(void *pvParameters);//任务函数
  12. #define LED0_TASK_PRIO 2 //任务优先级
  13. #define LED0_STK_SIZE 50 //任务栈大小
  14. TaskHandle_t LED0Task_Handler; //任务句柄
  15. void led0_task(void *pvParameters); //任务函数
  16. #define LED1_TASK_PRIO 3 //任务优先级
  17. #define LED1_STK_SIZE 50 //任务栈大小
  18. TaskHandle_t LED1Task_Handler; //任务句柄
  19. void led1_task(void *pvParameters); //任务函数
  20. static int TaskRun1,TaskRun2; //用来观察任务运行
  21. void BoardInit(void) //设置初始化环境
  22. {
  23. SysTickInit(); //系统时钟配置
  24. delay_init(72); //延迟函数配置
  25. LED_Init();
  26. uart_init(115200);
  27. }
  28. //开始任务任务函数
  29. void start_task(void *pvParameters)
  30. {
  31. taskENTER_CRITICAL(); //进入临界区
  32. //创建LED0任务
  33. xTaskCreate((TaskFunction_t )led0_task,
  34. (const char* )"led0_task",
  35. (uint16_t )LED0_STK_SIZE,
  36. (void* )NULL,
  37. (UBaseType_t )LED0_TASK_PRIO,
  38. (TaskHandle_t* )&LED0Task_Handler);
  39. //创建LED1任务
  40. xTaskCreate((TaskFunction_t )led1_task,
  41. (const char* )"led1_task",
  42. (uint16_t )LED1_STK_SIZE,
  43. (void* )NULL,
  44. (UBaseType_t )LED1_TASK_PRIO,
  45. (TaskHandle_t* )&LED1Task_Handler);
  46. vTaskDelete(StartTask_Handler); //删除开始任务
  47. taskEXIT_CRITICAL(); //退出临界区
  48. }
  49. int main(void)
  50. {
  51. // BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  52. BoardInit(); //设置初始化环境
  53. printf("Welcome to FreeRTOS,CoreClock:%d\r\n",SystemCoreClock);
  54. //创建开始任务
  55. xTaskCreate((TaskFunction_t )start_task, //任务函数
  56. (const char* )"start_task", //任务名称
  57. (uint16_t )START_STK_SIZE, //任务堆栈大小
  58. (void* )NULL, //传递给任务函数的参数
  59. (UBaseType_t )START_TASK_PRIO, //任务优先级
  60. (TaskHandle_t* )&StartTask_Handler); //任务句柄
  61. vTaskStartScheduler(); //开启任务调度
  62. }
  63. //LED0任务函数
  64. void led0_task(void *pvParameters)
  65. {
  66. while(1)
  67. {
  68. TaskRun1=1;
  69. TaskRun2=0;
  70. LED0=~LED0;
  71. printf("Task1Running\n");
  72. vTaskDelay(500);
  73. }
  74. }
  75. //LED1任务函数
  76. void led1_task(void *pvParameters)
  77. {
  78. while(1)
  79. {
  80. TaskRun1=0;
  81. TaskRun2=1;
  82. LED1=~LED1;
  83. printf("Task2Running\n");
  84. vTaskDelay(800);
  85. }
  86. }

成功点亮! 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/730297
推荐阅读
相关标签
  

闽ICP备14008679号