这里写目录标题
- 1、下载FreeRTOS源码
- 1.1github仓库下载
- 1.2官网下载
- 1.3百度网盘下载
- 2、FreeRTOS移植
- 2.1首先需要有一个可运行的标准库工程
- 2.2在工程内创建一个FreeRTOS文件夹,然后在FreeRTOS文件夹中再新建port、include、src三个文件夹。
- 2.3 port文件夹移植
- 2.4 include文件夹移植
- 2.5 src文件夹移植
- 2.6 移植FreeRTOSConfig.h配置文件
- 2.7 在keil中添加文件路径
- 2.8 打开组文件夹管理,新建FreeRTOS组
- 2.9 添加freeRTOS源文件
- 2.10 编译排错
- 1、先编译链接下工程
- 2.根据下面图操作打开FreeRTOSConfig.h文件
- 3、修改后我们再编译,发现上面那个错误解决了,又爆了下面几个错误:
- 4、注释掉stm32f10x_it.c中的三个中断处理空函数
- 5、再次编译
- 6、再次编译,我这里不报错了。
- 3、调试测试
- 4.移植完成的工程
1、下载FreeRTOS源码
1.1github仓库下载
链接:FreeRTOS
下面的教程是基于从github下载压缩包进行的,最好下载这个或者直接看3.1,从我百度网盘下载。如果是别的下载源也问题不大,大同小异。
此时我们需要下载以下两个仓库,
点进去按下面的步骤下载就行了,另一个也是这样下。
1.2官网下载
链接: FreeRTOS官网
打开链接我们可以看到有两个下载选项,我们下载第一个就行。
下载速度较慢。
1.3百度网盘下载
链接:https://pan.baidu.com/s/1x00zgnJfCae75DpJ1W4RIg?pwd=1233
提取码:1233
若上述都不行可以直接私信。
2、FreeRTOS移植
2.1首先需要有一个可运行的标准库工程
2.2在工程内创建一个FreeRTOS文件夹,然后在FreeRTOS文件夹中再新建port、include、src三个文件夹。
- port文件夹存放移植平台的相关文件
- include文件夹存放FreeRTOS相关的头文件
- src文件夹用来存放FreeRTOS相关的源码
2.3 port文件夹移植
- 将下载的源码:FreeRTOS源码\FreeRTOS-Kernel-dc09a3dd5144c4033607565c71b9919900f95cff\ portable\ MemMang路径下的heap_4.c文件复制到自己创建的port文件夹中
- 将FreeRTOS源码\FreeRTOS-Kernel-dc09a3dd5144c4033607565c71b9919900f95cff\ portable\ RVDS\ ARM_CM4F路径下的port.c和portmacro.h文件复制到自己创建的port文件夹中
注:如果下载的源码是我这个版本的,那就去按着上面的路径找就可以,肯定能找到,如果是其他版本的,那就仔细找一找,或者直接去搜名字都行。
2.4 include文件夹移植
- 将下载源码:FreeRTOS源码\FreeRTOS-Kernel-dc09a3dd5144c4033607565c71b9919900f95cff\ include路径下的所有头文件复制到自己创建的include文件夹中。
2.5 src文件夹移植
- 将FreeRTOS源码\FreeRTOS-Kernel-dc09a3dd5144c4033607565c71b9919900f95cff文件夹中的所有FreeRTOS相关.c文件复制到自己创建的src文件夹中
2.6 移植FreeRTOSConfig.h配置文件
- 在下载源码:FreeRTOS源码\FreeRTOS-main\ FreeRTOS\ Demo\CORTEX_M4F_STM32F407ZG-SK路径下将FreeRTOSConfig.h文件复制到自己创建的FreeRTOS文件夹中
注:我所使用的单片机为F407VET6,但是在官方例程中F407系列的单片机仅有CORTEX_M4F_STM32F407ZG-SK这个,我们这里暂时就用这个就可以,差别不大。
2.7 在keil中添加文件路径
在keil中点击魔术棒,进入“C/C++”,在Include Paths中添加FreeRTO、src、inc、port路径。
2.8 打开组文件夹管理,新建FreeRTOS组
2.9 添加freeRTOS源文件
按下面的操作分别添加src文件夹和port文件夹下的所有.c文件
注意:下方图片仅演示了src文件夹下.c的添加过程,port文件夹下的heap_4.c和port.c也需要添加。
2.10 编译排错
1、先编译链接下工程
报如下错误:
..\FreeRTOS\port\port.c(813): error: #20: identifier "SystemCoreClock" is undefined
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
..\FreeRTOS\port\port.c: 0 warnings, 1 error
根据上方信息可知SystemCoreClock未定义,但是在FreeRTOSConfig.h中使用了SystemCoreClock来标记MCU的频率。(未定义就使用)
2.根据下面图操作打开FreeRTOSConfig.h文件
我们可以看到,SystemCoreClock在FreeRTOSConfig.h中是条件编译的,只有当定义了__ICCARM__时,才有效。
#ifdef __ICCARM__
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
我们可以给他随便增加几个可以满足的条件编译,如下:
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
3、修改后我们再编译,发现上面那个错误解决了,又爆了下面几个错误:
compiling tasks.c...
linking...
.\Objects\111.axf: Error: L6200E: Symbol SVC_Handler multiply defined (by port.o and stm32f4xx_it_1.o).
.\Objects\111.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by port.o and stm32f4xx_it_1.o).
.\Objects\111.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by port.o and stm32f4xx_it_1.o).
Not enough information to list image symbols.
Not enough information to list load addresses in the image map.
Finished: 2 information, 0 warning and 3 error messages.
".\Objects\111.axf" - 3 Error(s), 0 Warning(s).
Target not created.
Build Time Elapsed: 00:00:01
这几个错误是说SVC_Handler、PendSV_Handler、PendSV_Handler被重复定义了
我们可以看到在我们移植的FreeRTOSConfig.h文件的最下面几行有如下几行宏定义。
这里的宏定义和我们原来工程中#include "stm32f4xx_it.c"文件中的中断处理函数void SVC_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);冲突了。
4、注释掉stm32f10x_it.c中的三个中断处理空函数
注释掉以下几个函数
void SVC_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
5、再次编译
此时又有如下报错:
.\Objects\111.axf: Error: L6218E: Undefined symbol vApplicationMallocFailedHook (referred from heap_4.o).
.\Objects\111.axf: Error: L6218E: Undefined symbol vApplicationIdleHook (referred from tasks.o).
.\Objects\111.axf: Error: L6218E: Undefined symbol vApplicationStackOverflowHook (referred from tasks.o).
.\Objects\111.axf: Error: L6218E: Undefined symbol vApplicationTickHook (referred from tasks.o).
Not enough information to list image symbols.
Not enough information to list load addresses in the image map.
Finished: 2 information, 0 warning and 4 error messages.
".\Objects\111.axf" - 4 Error(s), 0 Warning(s).
发现这些未定义的函数都是Hook结尾的,这些函数有个共同的名称:钩子函数,这是报错就是因为在FreeRTOSConfig.h中开启了这些钩子函数,但是没有定义这些钩子函数导致的。我们在FreeRTOSConfig.h中将相应的宏定义改为0即可。这里将宏configUSE_IDLE_HOOK、configUSE_TICK_HOOK、configUSE_MALLOC_FAILED_HOOK和configCHECK_FOR_STACK_OVERFLOW定义为0。
6、再次编译,我这里不报错了。
若大家还有错误的话可以自行根据错误类型在互联网上查找并修改错误。
3、调试测试
因为我这里使用的是我字节画的一块核心板(只放了个RGB灯,没放普通的LED灯),所以我这里用FreeRTOS创建一个串口打印的任务来验证下。
- 1.首先添加几个有关FreeRTOS基本操作的头文件
#include "freertos.h"
#include "task.h"
#include "timers.h"
- 3.简单写一个串口1打印数据的任务
TickType_t PreTime;//定义变量,用来存储任务触发的时间
void usart_task(void *pvParams)
{
Serial_Init();
PreTime = xTaskGetTickCount();
while(1)
{
printf("hello Worid!!!\r\n");
vTaskDelayUntil(&PreTime,1000); /*用来准确演示1000MS*/
}
}
- 4.创建任务并启动调度
int main(void)
{
xTaskCreate(usart_task,"serial",1024,NULL,4,NULL);
vTaskStartScheduler();
}
- 5.下载到开发板上进行测试,效果很好。
main文件中的全部代码为
#include "stm32f4xx.h" // Device header
#include "Serial.h"
#include "freertos.h"
#include "task.h"
#include "timers.h"
TickType_t PreTime;//定义变量,用来存储任务触发的时间
void usart_task(void *pvParams)
{
Serial_Init();
PreTime = xTaskGetTickCount();
while(1)
{
printf("hello Worid!!!\r\n");
vTaskDelayUntil(&PreTime,1000);
}
}
int main(void)
{
xTaskCreate(usart_task,"serial",1024,NULL,4,NULL);
vTaskStartScheduler();
}
Serial.c
#include "Serial.h"
void usart1_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
//初始化GPIO引脚,以及复用
GPIO_InitTypeDef GPIO_struct_init;
GPIO_struct_init.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10; //引脚
GPIO_struct_init.GPIO_Mode = GPIO_Mode_AF; //输出模式:复位
GPIO_struct_init.GPIO_Speed = GPIO_Speed_25MHz; //输出速度:中速
GPIO_struct_init.GPIO_OType = GPIO_OType_PP; //输出类型:推挽
GPIO_struct_init.GPIO_PuPd = GPIO_PuPd_NOPULL; //上下拉:不拉
GPIO_Init(GPIOA, &GPIO_struct_init);
//复位声明
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
//配置嵌套向量表
NVIC_InitTypeDef NVIC_InitTypeDef_init;
NVIC_InitTypeDef_init.NVIC_IRQChannel = USART1_IRQn; //中断通道都不一样,一定要记得去中断向量表找,具体的中断向量表如何寻找,可查看以前中断配置
NVIC_InitTypeDef_init.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级
NVIC_InitTypeDef_init.NVIC_IRQChannelSubPriority = 0;//响应优先级
NVIC_InitTypeDef_init.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitTypeDef_init);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
//初始化串口
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_WordLength = USART_WordLength_8b; //字长
USART_InitStruct.USART_Parity = USART_Parity_No; //奇偶校验位
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制位
USART_InitStruct.USART_StopBits = USART_StopBits_1; //停止位
USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //模式
USART_InitStruct.USART_BaudRate = 115200; //波特率(可手动设置,比如函数传参)
USART_Init(USART1,&USART_InitStruct);
//串口使能
USART_Cmd(USART1, ENABLE);
}
void Serial_Sendbyte(USART_TypeDef* USARTx,uint8_t Byte) //串口发字符
{
USART_SendData(USARTx,Byte);
while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);
}
//===================================================================================================
int fputc(int ch,FILE *f) //重构定向,printf直接打印到串口1
{
Serial_Sendbyte(USART1,ch);
return ch;
}//***************************************************************************************************
Serial.h
#ifndef __SERIAL_H__
#define __SERIAL_H__
#include "stm32f4xx.h" // Device header
#include <stdarg.h>
#include "stdio.h"
#include "string.h"
void usart1_Init(void);
#endif
4.移植完成的工程
链接:https://pan.baidu.com/s/1foc3N8TcdHcrd2NGo61asQ?pwd=1233
提取码:1233