1.freeRtos源码分析之移植到stm32F103
1.freeRtos源码获取
-
官方地址
1.sourceforge官方地址里有各个版本的freeRtos
FreeRTOS Real Time Kernel (RTOS) - Browse /FreeRTOS at SourceForge.net
2.freertos官方网站
FreeRTOS - Free RTOS Source Code Downloads, the official FreeRTOS zip file release download
-
国内gitee
2.freeRtos源码结构
└─FreeRTOS
├─Demo // 各种开发平台的Demo例程
│ ├─CORTEX_M4F_STM32F407ZG-SK // STM32F407例程
│ ├─CORTEX_STM32F103_IAR // STM32F103 IAR 例程
│ └─CORTEX_STM32F103_Keil // STM32F103 Keil例程
├─License // 许可证文件
│ └─license.txt // 许可证文件
└─Source // 移植所需的源码和头文件等
├─include // 通用头文件,无编译器区分
│ └─xxx.h // 通用头文件,无编译器区分
├─portable // 编译器相关文件
│ ├─IAR // IAR编译器
│ │ ├─ARM_CM0 // M0内核
│ │ ├─ARM_CM3 // M3内核
│ │ ├─ARM_CM4F // M4内核
│ │ └─ARM_CM7 // M7内核
│ │ ├─port.c // 处理器写的接口文件
│ │ └─portmacro.h // port.c对应的头文件
│ ├─Keil // Keil编译器
│ │ └─See-also-the-RVDS-directory.txt // 提示查看RVDS文件夹
│ │ └─Nothing to see here. // txt里面的内容
│ ├─MemMang // 内存管理相关
│ │ ├─heap_1.c // 动态内存分配相关函数接口
│ │ ├─heap_2.c
│ │ ├─heap_3.c
│ │ ├─heap_4.c
│ │ └─heap_5.c
│ └─RVDS // Keil编译器
│ ├─ARM_CM0
│ ├─ARM_CM3
│ ├─ARM_CM4F
│ └─ARM_CM7
│ ├─port.c // 处理器写的接口文件
│ └─portmacro.h // port.c对应的头文件
├─croutine.c // c协程文件,可以不要
├─event_groups.c // 事件组c文件,可以不要
├─list.c // 核心链表c文件,必须
├─queue.c // 队列互斥锁信号量c文件,必须
├─tasks.c // 核心任务创建调度c文件,必须
├─timers.c // 软件定时器c文件,可以不要
└─readme.txt // readme文件
3.移植到stm32f103上
3.1标准库下移植
第一步找一个能够正常使用的标准库程序,并创建FreeRtos文件夹,名字随便。
将FreeRtos源码复制到FreeRtos目录下。
source目录下的所有.c文件,也可以只要list.c queue.c tasks.c
include目录下所有.h文件
porttable目录下只保留MemMang文件夹和RVDS文件夹,RVDS文件夹下只保留ARM_CM3文件夹,因为stm32F103它是M3架构的内核。如果是f4就选CM4F。
将源码目录\Demo\CORTEX_STM32F103_Keil下的FreeRTOSConfig.h文件复制到include目录下。
全部复制完成后目录结构如下图,可以在CMD终端输入tree 文件夹名字 /f指令显示详细结构。
复制完成后打开Kile工具添加对应的文件。
头文件添加。
源文件添加
这写文件可以随便放到哪,为了结构化,将这些文件进行分组。因为FreeRtos是通过中断进任务切换的在port.c文件中实现了下面这三个中断服务函数。
void xPortPendSVHandler( void );
void xPortSysTickHandler( void );
void vPortSVCHandler( void );
我们需要将原来的stm32f10x_it.c中的下面三个服务函数替换掉。
void SVC_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
第一种是使用typedef重命名,在FreeRTOSConfig.h中添加下面语句。并删除stm32f10x_it.c中的定义。
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define xPortSysTickHandler SysTick_Handler
第二种直接将S汇编文件中的,如下所示的三个名称替换掉。
完后编译出现几个问题,我的freeRtos是10.04版本。
问题
第一个问题提示内存不够
这个将FreeRTOSConfig.h中的堆栈改小一点解决。
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 10 * 1024 ) )
第二个问题提示
这个将FreeRTOS.h中内容修改为
#ifndef INCLUDE_xTaskGetCurrentTaskHandle
#define INCLUDE_xTaskGetCurrentTaskHandle 1
#endif
或者直接在头文件定义
#define INCLUDE_xTaskGetCurrentTaskHandle 1
修改完成后编译通过。
创建任务测试
添加头文件
#include "FreeRTOS.h"
#include "task.h"
创建任务函数
static void vLedTask( void *pvParameters )
{
for(;;)
{
LED_BLUE_TOGGLE;
vTaskDelay(50);
}
}
static void vprintfTask( void *pvParameters )
{
for(;;)
{
printf("test is .....\n");
vTaskDelay(100);
}
}
启动调度
xTaskCreate( vLedTask, "LED", 128, NULL, 2, NULL );
xTaskCreate( vprintfTask, "printf", 128, NULL, 1, NULL );
vTaskStartScheduler();
注意任务函数内一定要有死循环。
main.c内容如下
#include <stm32f10x.h> // 头文件引用(标准库); 内核、芯片外设....;(stm32f10x.conf.h, 对标准库头文件进行调用)
#include "stm32f10x_conf.h" // 头文件引用(标准库); 内核、芯片外设....;(stm32f10x.conf.h, 对标准库头文件进行调用)
#include "system_f103.h"
#include "stdio.h" // C标准库文件,用于调用printf, sprintf等常用函数
#include "bsp_led.h" // LED指示灯
#include "bsp_key.h" // 按键
#include "bsp_usart.h" // USART1、2、3,UART4、5
#include "bsp_W25Q128.h"
#include "string.h"
#include "FreeRTOS.h"
#include "task.h"
#define mainCHECK_TASK_STACK_SIZE ( 128 )
#define mainCHECK_TASK_PRIORITY ( tskIDLE_PRIORITY+1 )
extern const unsigned char gImage_red[]; // 声明外部变量:图片缓存数组,位于外部文件font.h中
extern const unsigned char gImage_g[];
extern const unsigned char gImage_b[];
extern const unsigned char gImage_y[];
unsigned char* cmd;
int i = 0;
static void delay_ms(uint32_t ms) // 定义一个ms延时函数,减少移植时对外部文件依赖;
{
ms = ms * 10286; // 注意:打勾 "Options --> C/C++ ---> One ELF Section per Function选项"
for (uint32_t i = 0; i < ms; i++); // 72MHz系统时钟下,大约多少个空循环耗时1ms
}
static void vLedTask( void *pvParameters )
{
for(;;)
{
LED_BLUE_TOGGLE;
vTaskDelay(50);
}
}
static void vprintfTask( void *pvParameters )
{
for(;;)
{
printf("test is .....\n");
vTaskDelay(100);
}
}
// 主函数
int main(void)
{
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组,组2:抢占级0~3,子优先级0~3 ; 全局只设置一次,尽量放在显眼的地方
System_SwdMode(); // 设置芯片调试方式(SWD); 关闭JTAG只保留SWD; 目的:释放PB3、PB4、PA15,只需PA13、PA14
USART1_Init(115200); // 串口1初始化; 用于与串口软件通信,方便代码调试; USART1(115200-N-8-1), 且工程已把printf重定向至USART1输出
Led_Init(); // LED 初始化
// 点亮蓝灯
W25Q128_Init(); // 设备W25Q128初始化:引脚配置,SPI配置,测试连接, 测试是否存在字库
// LCD_Init(); // 显示屏初始化: 引脚配置,SPI配置,LCD参数配置
xTaskCreate( vLedTask, "LED", 128, NULL, 2, NULL );
xTaskCreate( vprintfTask, "printf", 128, NULL, 1, NULL );
vTaskStartScheduler(); //启动任务调度器
return 0;
}
// 注意:每一个代码文件,末尾保留一空行,否则编译会产生警告
3.2HAL库下移植
和标准库的移植过程一样,也可以通过STM32CubeMX直接生成,但st对freeRtos进行和简单封装,调用的还是原来的函数,个人感觉没必要,直接用原来的API就好。HAL库只是方便STM32CubeMX生成和标准库没有太大差别只是换了个名字。
3.3 现象
板上led灯以一定间隔闪烁,同时串口打印字符串。