当前位置:   article > 正文

从零开始学习FreeRTOS之在STM32上移植FreeRTOS(一)_gd32f103 freertos

gd32f103 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 */


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135

将配置文件放到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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

这个时候编译会出错,提示如下:
在这里插入图片描述
这是因为在配置文件中我们对两个函数进行了映射,在中断文件中也有映射。

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

我们不想用原来的,就需要到it.c文件中把这两个函数注释掉。
在这里插入图片描述
在这里插入图片描述
目前到这里我就没报错问题了,现在将程序烧录到单片机上,现象是:程序烧录之后单片机没有正常亮灯,调试会发现执行到这里就会卡死。

        vTaskDelay(pdMS_TO_TICKS(1000));
  • 1

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

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
	xPortSysTickHandler();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

总结

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

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

闽ICP备14008679号