逸珺 · 2021年07月02日

手把手教你在STM32F4上跑freeRTOS

首发:嵌入式客栈
作者:逸珺

之前挖了图解freeRTOS的坑,挖了坑就得填。今天就从使用开始,先把freeRTOS用起来。先聊一聊在STM32F407上的如何电灯,如果只用freeRTOS点灯,无疑有点杀鸡用牛刀的感觉。但是想用freeRTOS做产品,先照类似步骤则可以验证最小系统,验证freeRTOS是否能正确跑起来,再在此基础上开始做应用开发,就把任务分解了。本文主要梳理一下如何利用官方移植例子,创建一个自己demo工程,对于没用过的有点参考价值。

注:本文基于IAR for ARM 8.22.1(个人喜欢IAR),硬件采用正点原子的STM32F407探索者开发板,STLink V2。

准备工作

去官网https://www.freertos.org/a001...下载:

640.png

按图中所示下载,目前最新为202104.00,这个带多种移植好的例子。解压出来,是这样的:

1.png

  • tools为AWS iot配置工具,quick start例子以及cmake
  • FreeRTOS-Plus包含了MQTT,TCP,FAT等很多例子,先不去管
  • FreeRTOS为真正的操作系统源码以及多平台移植例子,如下:

2.png
使用开源软件,记得看看README,一般会有些介绍信息。分别介绍一下该文件夹下的大体内容:

  1. Demo:大部分硬件平台的官方移植例子
  2. Source:内核代码,包含硬件独立文件与可移植文件
  3. Test:内核测试代码
  4. license:MIT开源协议,可以直接商业应用。

建立工程

1.打开IAR,创建Workspace

3.png

2.创建工程

5.png

选择空工程(Empty project),命名为demo保存。
7.png

  1. 设置工程

    右键选择Options,进入设置界面
    8.png
    设置单片机为STM32F407VG
    9.png
    Library Configuration使能CMSIS
    11.png

  2. 添加源文件

    4.1 新建freeRTOS文件夹
    12.png
    4.2 添加内核文件,在工程文件夹下新建freeRTOS文件夹,将下列文件拷贝过来
    13.png
    4.3 添加进IAR工程
    15.png
    4.4 将freeRTOS下的include文件夹以及portable文件夹拷贝到当前工程下
    16.png
    4.5 portable文件夹下保留下面这几个文件夹
    17.png
    4.6 IAR文件夹下保留下面这两个文件夹
    18.png

注:全保留也可以,就是其他的用不到,会使工程文件夹变的很大,如果用版本控制软件管理,会增加仓库的大小。ARM\_CM4F\_MPU文件夹暂时没用,这个留做备用。

4.7 在IAR工程中新建portable文件夹,并加入相应的文件

18.png

4.8 将下面几个文件从官方例子中拷贝过来,并加入工程
image.png

FreeRTOSConfig.h是内核配置文件,本文配置如下:


*   #define configUSE_PREEMPTION   1  
    #define configUSE_IDLE_HOOK    0  
    #define configUSE_TICK_HOOK    0  
    #define configCPU_CLOCK_HZ    ( SystemCoreClock )  
    #define configTICK_RATE_HZ    ( ( TickType_t ) 1000 )  
    #define configMAX_PRIORITIES   ( 5 )  
    #define configMINIMAL_STACK_SIZE  ( ( unsigned short ) 130 )  
    #define configTOTAL_HEAP_SIZE   ( ( size_t ) ( 75 * 1024 ) )  
    #define configMAX_TASK_NAME_LEN   ( 10 )  
    #define configUSE_TRACE_FACILITY  1  
    #define configUSE_16_BIT_TICKS   0  
    #define configIDLE_SHOULD_YIELD   1  
    #define configUSE_MUTEXES    1  
    #define configQUEUE_REGISTRY_SIZE  8  
    #define configCHECK_FOR_STACK_OVERFLOW 0  
    #define configUSE_RECURSIVE_MUTEXES  1  
    #define configUSE_MALLOC_FAILED_HOOK 0  
    #define configUSE_APPLICATION_TASK_TAG 0  
    #define configUSE_COUNTING_SEMAPHORES 1  
    #define configGENERATE_RUN_TIME_STATS 0  
      
    /* Co-routine definitions. */  
    #define configUSE_CO_ROUTINES   0  
    #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )  
      
    /* Software timer definitions. */  
    #define configUSE_TIMERS    1  
    #define configTIMER_TASK_PRIORITY  ( 2 )  
    #define configTIMER_QUEUE_LENGTH  10  
    #define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 )  
      
    /* Set the following definitions to 1 to include the API function, or zero  
    to exclude the API function. */  
    #define INCLUDE_vTaskPrioritySet  1  
    #define INCLUDE_uxTaskPriorityGet  1  
    #define INCLUDE_vTaskDelete    1  
    #define INCLUDE_vTaskCleanUpResources 1  
    #define INCLUDE_vTaskSuspend   1  
    #define INCLUDE_vTaskDelayUntil   1  
    #define INCLUDE_vTaskDelay    1  

将IDLE_HOOK/TICK_HOOK禁止了,先不去关注这两个功能。

  • 将STM32相关文件搬过来,如下加入到工程
    image.png
  • 建立main.c

正点原子的板子,有两个LED,我们就建立两个任务来闪灯吧,相当于hello world,容易理解。
image.png

/* 操作系统头文件. */
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "semphr.h"

#include "stm32f4xx.h"
//LED管脚
#define LED1_PIN        GPIO_Pin_9
#define LED1_GPIO_PORT    GPIOF
#define LED1_GPIO_CLK     RCC_AHB1Periph_GPIOF  
  
#define LED2_PIN       GPIO_Pin_10
#define LED2_GPIO_PORT    GPIOF
#define LED2_GPIO_CLK    RCC_AHB1Periph_GPIOF  

static void prvLedInitialise()
{
  GPIO_InitTypeDef  GPIO_InitStructure;
  
  /* LED1时能时钟 */
  RCC_AHB1PeriphClockCmd(LED1_GPIO_CLK, ENABLE);
  /* LED1 GPIO 配置为输出 */
  GPIO_InitStructure.GPIO_Pin = LED1_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);  
  
  /* LED2时能时钟 */
  RCC_AHB1PeriphClockCmd(LED2_GPIO_CLK, ENABLE);
  /* LED2 GPIO配置为输出 */
  GPIO_InitStructure.GPIO_Pin = LED2_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);    
}

static void prvSetupHardware( void )
{
  /* 配置时钟,PLL,FLASH */
  SystemInit();

  /* 配置NVIC优先级. */
  NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
    
  /*LED GPIO配置*/
  prvLedInitialise();
}

//500ms LED1灯状态翻转一次
static void led1Task( void * pvParameters )
{
  while(1)
  {
     LED1_GPIO_PORT->ODR ^= LED1_PIN;
     vTaskDelay(500);
  }
}
//1000ms LED2灯状态翻转一次
static void led2Task( void * pvParameters )
{
  while(1)
  {
    LED2_GPIO_PORT->ODR ^= LED2_PIN;
    vTaskDelay(1000);
  }
}

int main(void)
{
  prvSetupHardware();
    
  xTaskCreate(led1Task,"LED1",256,NULL,100,(TaskHandle_t *)NULL);
  xTaskCreate(led2Task,"LED2",256,NULL,101,(TaskHandle_t *)NULL);
    
  /* 启动任务调度器. */
  vTaskStartScheduler();    
}

5.设置包含路径

5.1 右键打开options,设置C/C++编译器包含路径
image.png
5.2 设置包含路径为:

$PROJECT_DIR$是一个变量,表示当前路径,不要设成绝对路径,否则拷贝到不同路径就无法编译了。
$PROJ_DIR$\
$PROJ_DIR$\hal\CMSIS\Device\ST\STM32F4xx\Include
$PROJ_DIR$\hal\STM32F4xx_StdPeriph_Driver\inc
$PROJ_DIR$\freeRTOS\include
$PROJ_DIR$\freeRTOS\portable\IAR\ARM_CM4F
$PROJ_DIR$\Common\include示当前工程目录,利用这个变量就可以设置和工程相关的路径了,不要设置为绝对路径,否则如果工程拷贝到其他路径,就无法正确编译了。

并设置两个宏:

USE_STDPERIPH_DRIVER
STM32F4XX

5.3 设置汇编器包含路径
22.png

  1. 设置链接配置文件
    23.png
    注:如果遇到下面的错误,应该是你使用了老版本的startup_stm32f4xx.s

    Warning[25]: Label 'CAN2_SCE_IRQHandler' is defined pubweak in a section implicitly declared root 

    官方例子中下面路径的startup_stm32f4xx.s为老版本

CMSIS\Device\ST\STM32F4xx\Source\Templates\iar

该警告详细信息可参考:

https://www.iar.com/knowledge...

编译运行
33.png
设置ST-Link
35.png
下载运行
36.png
image.png

总结一下
官方的例子,要直接拿来开发产品有很多不需要的文件,可以按照本文的步骤做些删减,先建立这样一个基本工程,然后就可以结合自己的实际硬件以及需求,开始做应用开发了。

如对本文例子工程有兴趣,可以后台回复“demo”,自行领取。

END

推荐阅读

更多硬核嵌入式技术干货请关注嵌入式客栈专栏。
推荐阅读
关注数
2882
内容数
266
分享一些在嵌入式应用开发方面的浅见,广交朋友
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息