智果芯
服务于百万大学生和电子工程师!

手把手教你STM32F103C8T6移植FreeRTOS

摘要:今天玩一下RTOS,将FreeRTOS移植到C8T6单片机。后期会不断更新RTOS和Linux的内容,单片机的内容也会持续更新。想看更加详细的内容的小伙伴可以访问果果的博客:https://zhiguoxin.blog.csdn.net/

本期主角:9块9的C8T6核心板和一个JlinkOB。

STM32F103C8T6移植FreeRTOS

1、FreeRTOS介绍

FreeRTOS其实不用再多介绍了,现在太火了,主要是免费,感觉很多厂家的出场demo都会带。而且FreeRTOS是一个十分小巧的系统,占用资源也不多,甚至可以在STM32F103C8T6(64k FLASH,20K RAM)上跑起来。具体能裁剪到多小倒是没有看到有介绍,但是基本够用。

在FreeRTOS官网可以轻松下载到最新的FreeRTOS源码、组件、demo、新功能,而且现在的官网比以前好看多了。

现在的官网
以前的官网

2、FreeRTOS源码下载

本次不使用STM32CubeMX来配置工程,所以需要去官网下载源码:https://www.freertos.org/

3、FreeRTOS文件结构

现在是2021.8.19,下载之后,打开文件夹,文件列表如下:

FreeRTOSv202107.00
    |
    | -- FreeRTOS
            | -- Demo 
            | -- License 
            | -- Source 
                    | -- include 
                    | -- portable
                    | -- croutine.c
                    | -- event_groups.c
                    | -- list.c
                    | -- queue.c
                    | -- stream_buffer.c
                    | -- tasks.c
                    | -- timers.c
    |
    | -- FreeRTOS-Plus
            | -- Demo
            | -- Source
                    | -- FreeRTOS-Plus-CLI 
                    | -- FreeRTOS-Plus-IO
                    | -- FreeRTOS-Plus-TCP
                    | -- FreeRTOS-Plus-Trace

下面这些文件就是需要移植到你的工程中去的。

4、移植FreeRTOS

4.1、文件需要

这里只是为了把系统跑起来,所以只移植了核心组件。我们需要移植的,最少的文件只需要:

FreeRTOSv202107.00\FreeRTOS\Source\tasks.c, queue.c and list.c 3个文件(kernel核心)
FreeRTOSv202107.00\FreeRTOS\Source\portable\RVDS\ARM_CM3\port.c + portmacro.h(3个中断都是在这里做的,pendSV,SVC,tick)
FreeRTOSv202107.00\FreeRTOS\Source\portable\MemMang\heap_4.c(内存管理,5中选1)
FreeRTOSv202107.00\FreeRTOS\Source\include 所有API接口
FreeRTOSv202107.00\FreeRTOS\Demo\CORTEX_STM32F103_Keil\FreeRTOSConfig.h(freeRTOS系统裁剪配置文件)

4.2、include文件夹

include文件夹是一些头文件,移植的时候是需要的。

4.3、portable文件夹

portable这个文件夹,我们知道FreeRTOS是个系统,归根结底就是个纯软件的东西,它是怎么和硬件联系在一起的呢?软件到硬件中间必须有一个桥梁,portable文件夹里面的东西就是FreeRTOS系统和具体的硬件之间的连接桥梁!不同的编译环境,不同的MCU,其桥梁应该是不同的,打开portable文件夹,如图所示

4.4、FreeRTOSConfig.h文件

FreeRTOSConfig.h是FreeRTOS的配置文件,一般的操作系统都有裁剪、配置功能,而这些裁剪及配置都是通过一个文件来完成的,基本都是通过宏定义来完成对系统的配置和裁剪的。

4.5、添加 FreeRTOS源码

在基础工程中新建一个名为FreeRTOS的文件夹,把刚刚上面说到的文件全部添加进去。注意一定要将FreeRTOSConfig.h添加到FreeRTOS的include文件夹中。

4.6、向工程分组中添加文件

打开基础工程,新建分组FreeRTOS,然后向这两个分组中添加文件,如图所示

5、修改stm32f10x_it.c

SysTick中断服务函数是一个非常重要的函数,FreeRTOS所有跟时间相关的事情都在里面处理,SysTick就是 FreeRToS的一个心跳时钟,驱动着 FreeRTOS的运行,就像人的心跳一样,假如没有心跳,我们就相当于“死了”,同样的,Freertos没有了心跳,那么它就会卡死在某个地方,不能进行任务调度,不能运行任何的东西,因此我们需要实现个FreeRTOS的心跳时钟,FreeRTOS帮我们实现了SysTick的启动的配置:在port.c文件中已经实现vPortSetupTimerInterrupt()函数,并且FreeRTOS通用的SysTick中断服务函数也实现了:在port.c文件中已经实现xPortsysTickHandler()函数,所以移植的时候只需要我们在stm32f10xit.c文件中注释掉SysTick_Handler函数即可。

FreeRTOS为开发者考虑得特别多,PendV_Handler与SVC_Handler这两个很重要的函数都帮我们实现了,在port.c文件中已经实现xPortPendSV_Handler()vPortSVChandler()函数,防止我们自己实现不了,那么在stm3210x_it.c中就需要我们注释掉PendSV_Handler与SVC_Handler这两个函数。

然后在FreeRTOSConfig.h文件中重新宏定义。

/****************************************************************
        FreeRTOS 与中断服务函数有关的配置选项
****************************************************************/
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler  

FreeRTOSConfig.h中的configTOTAL_HEAP_SIZE(size_t)(20*1024)。其中20代表STM32F10x系列的堆栈大小,需要按照型号修改匹配。20为ZET6,10为C8T6。

具体点说就是16和32K的flash属于小;64和128K的为中型;256/384/512K的为大型。对应RAM为6和10K小,20K中,48和64k大。
 至此移植配置基本完成。

6、编写main函数

1、使用动态任务创建函数xTaskCreate()创建一个开始任务start_task()然后在开始任务中创建任务一task1_task()和任务二task2_task()

2、开始任务start_task();在执行完创建两个后就马上删除自己这个开始任务,自己删自己,铁锅炖自己

3、之后系统就开始不断地执行任务一和任务二。

4、在任务一使用RTT不断打印:task1_task is running...

5、在任务二使用RTT不断打印:task1_task is running...

#include "sys.h"
#include "led.h"
#include "delay.h"
#include "SEGGER_RTT.h"
#include "FreeRTOS.h"
#include "task.h"

#define START_TASK_PRIO        1               //任务优先级
#define TASK1_TASK_PRIO        2               //任务优先级
#define TASK2_TASK_PRIO        3               //任务优先级

#define START_STK_SIZE         128             //任务堆栈大小
#define TASK1_STK_SIZE         128             //任务堆栈大小    
#define TASK2_STK_SIZE         128             //任务堆栈大小

TaskHandle_t StartTask_Handler;                //任务句柄
TaskHandle_t Task1Task_Handler;                //任务句柄
TaskHandle_t Task2Task_Handler;                //任务句柄

void start_task(void *pvParameters);        //任务函数声明
void task1_task(void *pvParameters);        //任务函数声明
void task2_task(void *pvParameters);        //任务函数声明

int main(void)
{
    delay_init();
    LED_Init();    
    //动态创建开始任务
    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);   //任务句柄
    //开启任务调度
    /* 启动RTOS,其实就是启动“任务管理器”,启动之后任务管理器就开始调度线程,
     * 此时pc(程序计数器)就会指向某线程的指令,开始多线程并发运行。
     * 如果没有创建多线程的话,那就只有一个线程。*/
    vTaskStartScheduler();

    /* 由于调用了vTaskStartScheduler之后,PC就指向了线程中的指令,因此vTaskStartScheduler后面代码
    * 并不会被CPU执行,所以vTaskStartScheduler后的代码没有意义。 */
    while(1)
    {
        //这里的代码不会被执行,写了也没用
    }
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
    //创建TASK1任务
    xTaskCreate((TaskFunction_t )task1_task,
                (const char*    )"task1_task",
                (uint16_t       )TASK1_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK1_TASK_PRIO,
                (TaskHandle_t*  )&Task1Task_Handler);
    //创建TASK2任务
    xTaskCreate((TaskFunction_t )task2_task,
                (const char*    )"task2_task",
                (uint16_t       )TASK2_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK2_TASK_PRIO,
                (TaskHandle_t*  )&Task2Task_Handler);
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}


//task1任务函数
void task1_task(void *pvParameters)
{
    for(;;)
    {
        SEGGER_RTT_SetTerminal(0);
        SEGGER_RTT_printf(0, "task1_task is running...\r\n");
        vTaskDelay(200);/* 延时 200 个 tick */
    }
}

//task2任务函数
void task2_task(void *pvParameters)
{
    for(;;)
    {
        SEGGER_RTT_SetTerminal(1);
        SEGGER_RTT_printf(0, "task2_task is running...\r\n");
        vTaskDelay(200);/* 延时 200 个 tick */
    }
}

7、试验现象

编译、链接、下载,然后打开RTT观察效果。

配置RTT

有意思的一点是,我设置的task1优先级是2,task2优先级是3,从日志里明显是task2先跑,难道移植出了问题?

实则不然,FreeRTOS中优先级数值越低,优先级等级越低,空闲任务的优先级为0,这一点和很多RTOS都不相同,需要特别注意!

小结

RTOS学起来不难,初学时肯定会有畏难情绪,一看到几十集十几个小时的视频就不想看了,但是搞嵌入式如果RTOS都不学,肯定不行对吧,自己都觉得不合适。考虑大大多数人没有用到F4这样的片子,所以今天用的是F103的片子,这里移植到C8T6上也只是为了学习一下流程,如果真的使用可以用个大点的MCU。

另外也没有说主函数中的代码是怎么来的,关于任务的创建、优先级、堆栈下期再说,先熟悉流程,RTOS到底真的有那么神秘吗?

赞(5) 打赏
未经允许不得转载:智果芯 » 手把手教你STM32F103C8T6移植FreeRTOS

评论 抢沙发

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏