STM32上的FreeRTOS移植指南(一):从零开始

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 碎碎念:
  • 主要内容:
  • 一、找资料
  • 1.任意教程提供的点灯程序
  • 2.FreeRTOS源码
  • 二、创建工程模板
  • 三、配置FreeRTOS
  • 四、小修小补
  • 总结

  • 碎碎念:

    最近搞电池把GD32E230烧坏了- -整个芯片和LDO都烧坏了- -嘉立创什么时候再免费派券让我白嫖一个开发板?


    主要内容:

    用KEIL5移植FreeRTOS到STM32F103C8T6上,因为我看网上好多教程都是用CUBE直接生成的,但是好多国产芯片还没有能使用CUBE,虽然也有逃课的办法但是我觉得也不太好用,而且在GITHUB上移植大多数也是需要对文件修改的,所以就当是个学习机会啦。
    后续也会写在国产芯片上移植FreeRTOS的办法,一步步来尽量做到通俗易懂。

    一、找资料

    我这次用的开发板是立创的STM32F103C8T6,这个板子很多人用而且也很便宜(我白嫖到的,谢谢嘉立创)。
    但是芯片不是重点,基本上STM下面的芯片都非常兼容FreeRTOS,更简单的办法是用CUBEIDE直接生成工程文件。
    这篇日志纯粹是能在其他芯片上也能移植FreeRTOS打基础的。

    好啦,闲话不多说,直接开始找资料。

    1.任意教程提供的点灯程序

    众所周知,FreeRTOS是基于滴答定时器的高级应用,所以滴答定时器尤其重要,在这个实验里我们要实现灯闪烁的效果。
    链接: 立创提供的STM32F103资料

    2.FreeRTOS源码

    链接: FreeRTOS

    二、创建工程模板

    请在创建FreeRTOS工程模板之前确保你的点灯程序是可以跑起来的。
    下面列出来的就是我们需要多加的文件(有些工程是有it.c的,这是一个关于中断的文件。)

    下载了FreeRTOS之后,我们需要找到FreeRTOS里面的Source,具体路径为:FreeRTOSv202212.01\FreeRTOSv202212.01\FreeRTOS\Source


    把不是.c后缀的文件删掉,再把这个文件夹移植到你的项目工程下,改名成FreeRTOS方便管理。

    打开里面的portable文件夹,把除了KEIL,MemMang和RVDS的其他文件都删掉。

    然后在KEIL里打开项目,在项目里加入头文件
    FreeRTOS\include
    FreeRTOS\portable\RVDS\ARM_CM3
    module

    第一个文件是FreeRTOS依赖的头文件,每个人都一样要加的。
    第二个文件的选择是基于你的芯片的ARM架构,数据手册上的第一页就会写,所以移植的时候也要特别注意是不是FreeRTOS写好的架构。有些芯片是不太支持的。(或者说大佬自己改)

    FreeRTOS目前支持的架构:

    第三个文件是中断函数相关的,大多数工程都会把他们放出来,但是立创这次没有,咱也不敢问,默默补上就行。中断函数非常重要,一定要加到工程里。(虽然里面都是空的,自己补写也是可以的。)
    再将对应的文件加入进来,如下:

    三、配置FreeRTOS

    这里我强烈建议各位去看官网的资料,实际上并没有想象的那么复杂,而且官网讲的非常详细!
    链接: FreeRTOSConfig介绍
    而且大多数的配置文件只需要修改很少很少地方。下面是我写的,可以复制过去,要改的地方我都写了备注的。

    /*
     * FreeRTOS Kernel V10.2.0
     * Copyright (C) 2019 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy of
     * this software and associated documentation files (the "Software"), to deal in
     * the Software without restriction, including without limitation the rights to
     * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
     * the Software, and to permit persons to whom the Software is furnished to do so,
     * subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
     * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
     * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
     * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     *
     * http://www.FreeRTOS.org
     * http://aws.amazon.com/freertos
     *
     * 1 tab == 4 spaces!
     */
    
    #ifndef FREERTOS_CONFIG_H
    #define FREERTOS_CONFIG_H
    
    /*-----------------------------------------------------------
     * Application specific definitions.
     *
     * These definitions should be adjusted for your particular hardware and
     * application requirements.
     *
     * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
     * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
     *
     * See http://www.freertos.org/a00110.html
     *----------------------------------------------------------*/
    
    /* Ensure stdint is only used by the compiler, and not the assembler. */
    /* 确保 stdint 只被编译器使用,而不是汇编器使用。 */
    #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
        #include <stdint.h>
        extern uint32_t SystemCoreClock;
    #endif
    
    // FreeRTOS 配置选项
    #define configUSE_PREEMPTION           					 1   // 使用抢占式调度
    #define configUSE_IDLE_HOOK             0   // 不使用空闲钩子函数
    #define configUSE_TICK_HOOK             0   // 不使用滴答钩子函数
    #define configCPU_CLOCK_HZ              ( SystemCoreClock )  // 设置 CPU 时钟频率,这里可以根据数据手册写死,也可以像我一样用内核文件定义
    #define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )  // 滴答频率设置为 1000Hz,这个官方示例给的
    #define configMAX_PRIORITIES            ( 5 )  // 最大优先级数目,根据需要修改
    #define configMINIMAL_STACK_SIZE        ( ( unsigned short ) 130 )  // 最小任务堆栈大小,根据需要修改,每个代表4个字节,即:130*4=520个字节
    #define configTOTAL_HEAP_SIZE           ( ( size_t ) ( 8 * 1024 ) )  // 堆大小设置为 8KB,,根据需要修改
    #define configMAX_TASK_NAME_LEN         ( 16 )  // 任务名字的最大长度
    #define configUSE_TRACE_FACILITY        1   // 启用追踪功能
    #define configUSE_16_BIT_TICKS          0   // 使用 16 位的滴答计数器或者32位的滴答计时器
    #define configIDLE_SHOULD_YIELD         1   // 空闲任务是否应该放弃 CPU,这里必须设置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   // 不生成运行时统计信息
    
    /* 协程定义 */
    #define configUSE_CO_ROUTINES           0   // 不使用协程
    #define configMAX_CO_ROUTINE_PRIORITIES ( 2 )  // 最大协程优先级数
    
    /* 软件定时器定义 */
    #define configUSE_TIMERS                1   // 启用软件定时器
    #define configTIMER_TASK_PRIORITY       ( configMAX_PRIORITIES - 1 )  // 定时器任务的优先级
    #define configTIMER_QUEUE_LENGTH        5   // 定时器队列长度
    #define configTIMER_TASK_STACK_DEPTH    ( configMINIMAL_STACK_SIZE * 2 )  // 定时器任务堆栈深度
    
    /* 包含 API 函数的设置,根据自己需求启用 */
    #define INCLUDE_vTaskPrioritySet      				  1   // 启用 vTaskPrioritySet 函数
    #define INCLUDE_uxTaskPriorityGet     				  1   // 启用 uxTaskPriorityGet 函数
    #define INCLUDE_vTaskDelete           				  1   // 启用 vTaskDelete 函数
    #define INCLUDE_vTaskCleanUpResources 				  1   // 启用 vTaskCleanUpResources 函数
    #define INCLUDE_vTaskSuspend        				  1   // 启用 vTaskSuspend 函数
    #define INCLUDE_vTaskDelayUntil       				  1   // 启用 vTaskDelayUntil 函数
    #define INCLUDE_vTaskDelay             				  1   // 启用 vTaskDelay 函数
    #define INCLUDE_xTaskGetSchedulerState         		  0
    #define INCLUDE_xTaskGetCurrentTaskHandle       	  1
    #define INCLUDE_uxTaskGetStackHighWaterMark           0
    #define INCLUDE_uxTaskGetStackHighWaterMark2    	  0
    #define INCLUDE_xTaskGetIdleTaskHandle          	  0
    #define INCLUDE_eTaskGetState                  		  0
    #define INCLUDE_xEventGroupSetBitFromISR        	  0
    #define INCLUDE_xTimerPendFunctionCall        		  0
    #define INCLUDE_xTaskAbortDelay              	      0
    #define INCLUDE_xTaskGetHandle                		  0
    #define INCLUDE_xTaskResumeFromISR            		  0
    
    /* Cortex-M 特定定义 */
    #ifdef __NVIC_PRIO_BITS
        /* 当使用 CMSIS 时,__NVIC_PRIO_BITS 会被指定 */
        #define configPRIO_BITS           __NVIC_PRIO_BITS
    #else
        #define configPRIO_BITS           4   /* 15 个优先级 */
    #endif
    
    /* 可用于调用 "set priority" 函数的最低中断优先级 */
    #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         0xf
    
    /* 可由任何中断服务例程使用的最高中断优先级
    (较高的优先级具有较低的数值)。不要从比这更高优先级
    的中断中调用 FreeRTOS API 函数!*/
    #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5
    
    /* 内核端口层自身使用的中断优先级。适用于所有 Cortex-M 端口,
    不依赖于任何特定的库函数 */
    #define configKERNEL_INTERRUPT_PRIORITY         ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
    /* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY 不能设置为零 !!!!
    参见 http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
    #define configMAX_SYSCALL_INTERRUPT_PRIORITY    ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
    
    /* 不依赖 assert.h 头文件提供的正常 assert() 语义 */
    #define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
    
    /* 定义将 FreeRTOS 端口中断处理程序映射到它们的 CMSIS 标准名称 */
    #define vPortSVCHandler SVC_Handler
    #define xPortPendSVHandler PendSV_Handler
    //#define xPortSysTickHandler SysTick_Handler
    
    #endif /* FREERTOS_CONFIG_H */
    
    
    

    将配置文件放到Include文件夹即可。

    四、小修小补

    这样FreeRTOS基本上就配置好了,我们先来写一个点灯函数进行测试:

    /*
    **GPIO口初始化
     */
    void LED_Init()
    {
    	GPIO_InitTypeDef GPIO_InitStructure;
    	
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    	
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOC, &GPIO_InitStructure);
    	GPIO_SetBits(GPIOC,GPIO_Pin_13);
    	
    }
    /*
    **创建灯闪烁任务
     */
    void vTaskLED(void *pvParameters)
    {
        (void) pvParameters;
    
        while(1)
        {
            // 切换 LED 状态
            GPIO_WriteBit(GPIOC, GPIO_Pin_13, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13)));
    
            // 延时 1 秒
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
    
    int main(void)
    {
        // 配置系统时钟
        board_init();
    
        // 初始化 LED
        LED_Init();
    
        // 创建 LED 闪烁任务
        xTaskCreate(vTaskLED, "LED Task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
    
        // 启动调度器
        vTaskStartScheduler();
    
        // 如果启动调度器失败,将会执行到这里
        while(1);
    }
    

    这个时候编译会出错,提示如下:

    这是因为在配置文件中我们对两个函数进行了映射,在中断文件中也有映射。

    /* 定义将 FreeRTOS 端口中断处理程序映射到它们的 CMSIS 标准名称 */
    #define vPortSVCHandler SVC_Handler
    #define xPortPendSVHandler PendSV_Handler
    

    我们不想用原来的,就需要到it.c文件中把这两个函数注释掉。


    目前到这里我就没报错问题了,现在将程序烧录到单片机上,现象是:程序烧录之后单片机没有正常亮灯,调试会发现执行到这里就会卡死。

            vTaskDelay(pdMS_TO_TICKS(1000));
    

    这还是因为中断的问题,FreeRTOS用了滴答定时器,我们要在滴答定时器中断时调用的函数里调用FreeRTOS的函数。在it.c文件里找到修改就好啦。

    /**
      * @brief  This function handles SysTick Handler.
      * @param  None
      * @retval None
      */
    void SysTick_Handler(void)
    {
    	xPortSysTickHandler();
    }
    

    再进行烧录,就能在单片机上看到灯闪烁了。

    总结

    这个DEMO我其实已经做了两次- -没想到第二次还是花了我两个多小时的时间,虽然里面有一半是因为在写文章。但是还是好累啊- -一定要保存好DEMO不要天天搭建环境啊- –
    文件放在这里,需要自提哈。
    链接:https://pan.baidu.com/s/1L0MrklDR94agIlbDJnlVqA?pwd=ki9e
    提取码:ki9e

    作者:YUki又叫6777

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32上的FreeRTOS移植指南(一):从零开始

    发表回复