链接:FreeRTOS
下面的教程是基于从github下载压缩包进行的,最好下载这个或者直接看3.1,从我百度网盘下载。如果是别的下载源也问题不大,大同小异。
此时我们需要下载以下两个仓库,
点进去按下面的步骤下载就行了,另一个也是这样下。
链接: FreeRTOS官网
打开链接我们可以看到有两个下载选项,我们下载第一个就行。
下载速度较慢。
链接:https://pan.baidu.com/s/1x00zgnJfCae75DpJ1W4RIg?pwd=1233
提取码:1233
若上述都不行可以直接私信。
注:如果下载的源码是我这个版本的,那就去按着上面的路径找就可以,肯定能找到,如果是其他版本的,那就仔细找一找,或者直接去搜名字都行。
注:我所使用的单片机为F407VET6,但是在官方例程中F407系列的单片机仅有CORTEX_M4F_STM32F407ZG-SK这个,我们这里暂时就用这个就可以,差别不大。
在keil中点击魔术棒,进入“C/C++”,在Include Paths中添加FreeRTO、src、inc、port路径。
按下面的操作分别添加src文件夹和port文件夹下的所有.c文件
注意:下方图片仅演示了src文件夹下.c的添加过程,port文件夹下的heap_4.c和port.c也需要添加。
报如下错误:
..\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的频率。(未定义就使用)
我们可以看到,SystemCoreClock在FreeRTOSConfig.h中是条件编译的,只有当定义了__ICCARM__时,才有效。
#ifdef __ICCARM__
#include
extern uint32_t SystemCoreClock;
#endif
我们可以给他随便增加几个可以满足的条件编译,如下:
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include
extern uint32_t SystemCoreClock;
#endif
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);冲突了。
注释掉以下几个函数
void SVC_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
此时又有如下报错:
.\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。
若大家还有错误的话可以自行根据错误类型在互联网上查找并修改错误。
因为我这里使用的是我字节画的一块核心板(只放了个RGB灯,没放普通的LED灯),所以我这里用FreeRTOS创建一个串口打印的任务来验证下。
#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); /*用来准确演示1000MS*/
}
}
int main(void)
{
xTaskCreate(usart_task,"serial",1024,NULL,4,NULL);
vTaskStartScheduler();
}
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
#include "stdio.h"
#include "string.h"
void usart1_Init(void);
#endif
链接:https://pan.baidu.com/s/1foc3N8TcdHcrd2NGo61asQ?pwd=1233
提取码:1233