0. 前言
硬件平台:
- STM32F103ZET6
移植流程:
- 构建STM32运行环境
- 下载FreeRTOS源码
- 根据芯片型号选择对应的内核文件
- Keil配置项目
- 创建任务
- 运行
1. 构建STM32运行环境
搭建运行环境基本流程:
- 创建Keil项目
- 创建对应目录
- 复制stm32f103启动文件
- 创建main.c文件
- 创建串口驱动文件
- Keil配置项目结构
- 编写main.c与驱动代码
1.1 使用Keil创建项目
1.1.1 打开Keil创建新项目
创建一个项目文件夹用于存放项目(base_project):最上方Project->New … Project -> 输入项目名 -> 保存项目 -> 选择芯片 -> 保存
创建项目:
选择芯片:
1.2 创建目录
使用Keil创建好项目文件后,在目录中创建start、user、driver、freertos文件夹
- start:用于存放stm32的启动文件
- user:存放用户代码
- driver:存放驱动文件
- freertos:存放FreeRTOS的移植文件
1.3 复制stm32f103启动文件到start中
通过ST官网下载stm32f103的标准库文件,将以下6个文件拷贝到start文件夹中,这里主要讲FreeRTOS移植,就不一一讲解,可以私信作者获取项目结构源码。
1.4 创建main.c文件
在user文件夹中创建main.c文件,作为程序入口文件。
1.5 创建串口的驱动文件Driver_Usart
在driver文件夹中创建usart文件夹,在usart文件夹中创建Driver_Usart.c与Driver_Usart.h文件。用于调试代码时串口的输出打印。
1.6 打开Keil配置文件
流程步骤:
- 选择配置项目目录
- 添加文件夹
- 对应文件夹添加文件
- 配置头文件路径
1.6.1 选择配置项目目录
1.6.2 创建对应文件
1.6.3 添加对应文件
将对应的文件添加进对应的文件夹。
这里以usart的驱动文件为例,将Driver_Usart.c文件添加到对应的Driver/Usart中即可。
对应的main.c添加进文件夹。
添加启动文件,将所有的启动文件添加到Start文件夹中:
配置目录效果:
1.6.4 配置头文件路径
选择对应的头文件路径:
保存路径:
选择调试工具:
1.7 编写main.c与驱动代码
使用VSCode打开base_project文件夹,进行代码编写。
现在VSCode进行插件安装:
- C/C++
- Keil Assistant
main.c
#include "Driver_Usart.h"
int main()
{
/* 初始化窗口 */
USART_Init();
printf("Hello World!\n");
while (1)
{
}
return 0;
}
Driver_Usart.h
#ifndef __DRIVER_USART_H
#define __DRIVER_USART_H
#include "stm32f10x.h"
#include "stdio.h"
// 初始化
void USART_Init(void);
#endif
Driver_Usart.c
#include "Driver_Usart.h"
void USART_Init(void)
{
// 1. 配置时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
// 2. GPIO引脚配置
// 2.1 PA9 设为复用推挽输出 MODE: 11, CNF: 10
GPIOA->CRH |= GPIO_CRH_MODE9;
GPIOA->CRH |= GPIO_CRH_CNF9_1;
GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
// 2.2 PA10 设为浮空输入 MODE:00, CNF:01
GPIOA->CRH &= ~GPIO_CRH_MODE10;
GPIOA->CRH &= ~GPIO_CRH_CNF10_1;
GPIOA->CRH |= GPIO_CRH_CNF10_0;
// 3. 串口配置
// 3.1 设置波特率
USART1->BRR = 0x271;
// 3.2 串口使能和收发使能
USART1->CR1 |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
}
/* printf重定向 */
int fputc(int ch, FILE *fp)
{
/* 检查发送数据寄存器是否为空 */
while ((USART1->SR & USART_SR_TXE) == 0)
;
// 准备就绪,继续发送下一个字符
USART1->DR = ch;
return ch;
}
2. 下载FreeRTOS源码
在官网中下载FreeRTOS源码。
官网地址:https://www.freertos.org/
3. 根据芯片型号选择对应的内核文件
构建目录流程:
- 解压源码
- 在项目的freertos文件夹中,创建3个文件夹
- source
- portable
- include
- 将FreeRTOS源码复杂到对应的文件夹中
- 拷贝FreeRTOS的配置文件FreeRTOSConfig.h
- 在User文件夹下创建FreeRTOS相关的代码文件
3.1 解压源码
解压源码压缩文件,获得源码。源码存放于FreeRTOS文件夹中。
3.2 在项目的freertos文件夹中,创建3个文件夹
3.3 将FreeRTOS源码复杂到对应的文件夹中
-
将FreeRTOS中Source文件夹下的6个文件拷贝到自己创建的项目freertos/source文件下。
-
将FreeRTOS中Source文件夹下的include中所有的文件拷贝到自己创建的项目freertos/include文件下。
-
将FreeRTOS中Source文件夹下的portable中三个文件夹拷贝到自己创建的项目freertos/include文件下。
RVDS是移植芯片相关的文件,这里只保留CM3
MemMang是内存管理相关的文件夹,只保留heap_4.c文件即可
3.4 拷贝FreeRTOS的配置文件FreeRTOSConfig.h
在源码的文件夹中的FreeRTOS下找到Demo文件夹,在Demo文件夹找到我们芯片对应的文件夹,获取其中的FreeRTOSConfig.h文件,将这个文件拷贝到自己创建的项目的freertos文件夹下即可。
3.5 在User文件夹下创建FreeRTOS相关的代码文件
4. Keil配置项目
使用Keil打开项目,配置目录结构与头文件路径。
配置目录结构:
配置头文件路径:
5. 创建任务
main.c
#include "Driver_Usart.h"
#include "freertos_demo.h"
int main()
{
/* 初始化窗口 */
USART_Init();
printf("Hello World!\n");
FreeRTOS_Start();
while (1)
{
}
return 0;
}
freertos_demo.c
#include "freertos_demo.h"
/* 启动任务相关配置 */
void startTask(void *args); /* 启动任务执行函数 */
#define START_TASK_NAME "start" /* 启动任务的名称 */
#define START_TASK_STACK_DEPTH 128 /* 启动任务栈大小 = 2 * 128 字节 */
#define START_TASK_PRIORITY 4 /* 启动任务优先级 */
TaskHandle_t startTaskTCB; /* 启动任务的控制块 */
/* 3个子任务相关配置 */
/* --------------------Task1-------------------- */
void taskFunc1(void *args); /* 任务1执行函数 */
#define TASK_NAME_1 "task1" /* 任务1的名称 */
#define TASK1_PRIORITY 1 /* 任务1优先级 */
TaskHandle_t task1TCB; /* 任务1的控制块 */
/* --------------------Task2-------------------- */
void taskFunc2(void *args); /* 任务2执行函数 */
#define TASK_NAME_2 "task2" /* 任务2的名称 */
#define TASK2_PRIORITY 2 /* 任务2优先级 */
TaskHandle_t task2TCB; /* 任务2的控制块 */
void FreeRTOS_Start(void)
{
/* 1. 创建启动任务 */
xTaskCreate(
startTask,
START_TASK_NAME,
START_TASK_STACK_DEPTH,
NULL,
START_TASK_PRIORITY,
&startTaskTCB
);
/* 2. 启动任务调度器 */
vTaskStartScheduler();
}
void startTask(void *args)
{
// 1. 代码进入临界区,防止代码中断
vPortEnterCritical();
printf("执行启动任务...");
// 2. 创建3个任务
xTaskCreate( /* 任务1 */
taskFunc1,
TASK_NAME_1,
START_TASK_STACK_DEPTH,
NULL,
TASK1_PRIORITY,
&task1TCB
);
xTaskCreate( /* 任务2 */
taskFunc2,
TASK_NAME_2,
START_TASK_STACK_DEPTH,
NULL,
TASK2_PRIORITY,
&task2TCB
);
// 3. 代码退出临界区
printf("启动任务执行完毕...\n回收启动任务...");
vPortExitCritical();
// 4. 回收启动任务
vTaskDelete(NULL);
}
void taskFunc1(void *args) /* 任务1执行函数 */
{
while (1)
{
printf("Task1...");
vTaskDelay(500);
}
}
void taskFunc2(void *args) /* 任务2执行函数 */
{
while (1)
{
printf("Task2...");
vTaskDelay(500);
}
}
extern void xPortSysTickHandler(void);
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
}
freertos_demo.h
#ifndef __FREERTOS_DEMO_H
#define __FREERTOS_DEMO_H
/* 任务相关 */
#include "FreeRTOS.h"
#include "task.h"
/* 1. 启动操作系统 */
void FreeRTOS_Start(void);
#endif
FreeRTOSConfig.h
/*
* FreeRTOS V202212.01
* Copyright (C) 2020 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
#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
*----------------------------------------------------------*/
#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 )
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES ( 5 )
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) )
#define configMAX_TASK_NAME_LEN ( 16 )
#define configUSE_TRACE_FACILITY 0
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
/* 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 0
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
/* This is the raw value as per the Cortex-M3 NVIC. Values can be 255
(lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY 255
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /* equivalent to 0xb0, or priority 11. */
/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15. This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting. Here 15 corresponds to the lowest
NVIC value of 255. */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15
#endif /* FREERTOS_CONFIG_H */
/* 最基本的配置 */
// 其实也就是中断处理函数
#define xPortPendSVHandler PendSV_Handler /* 用于任务的上下文切换和延后处理,主要用于rtos */
#define vPortSVCHandler SVC_Handler /* 用于操作系统的调用,提供操作系统服务接口 */
#define INCLUDE_xTaskGetSchedulerState 1 /* 用于包含获取调度器状态的开关 */
/* 和任务相关的配置 */
#define configSUPPORT_DYNAMIC_ALLOCATION 1 /* 动态创建任务API的开关 */
// #define configSUPPORT_STATIC_ALLOCATION 1 /* 静态创建任务API的开关 */
6. 运行
编译:
下载:
运行: