鉴于对于一些实时性有一定要求的STM32系统,通常我们可以选择移植FreeRTOS操作系统到TM32 单片机上。本文主要以STM32F103ZET6为例来演示如何移植FreeRTOS操作系统。首先需要进入FreeRTOS官网进行下载相关驱动文件。
FreeRTOS官网地址:https://www.freertos.org/zh-cn-cmn-s/
进入后点击下载Free RTOS
然后点击第一个下载
下载后新建一个文件夹命名为Free RTOS并从下载中复制一下文件到文件夹中
这里也有我以及整理过的Free RTOS文件,打开百度网盘下载:
链接:https://pan.baidu.com/s/1RoAb5jquj1y4L6l7FgZhFw?pwd=drng
提取码:drng
对于portable文件夹中只需要留下以下文件夹其他均可以删除
接下来就要进行STM32工程文件的配置
首先打开STM32CubeMX进行基本配置,详情见STM32基于HAL库的开发与应用(1)STM32CubeMx基本配置_hal库配置-CSDN博客
由于滴答时钟给RTOS使用因此我们需要在添加一个定时器作为单片机时钟,本例子中是添加了TIM4作为时钟:
同时在NVIC中需要将滴答时钟和TIM4时钟中断优先级开到最高,因为RTOS系统其实就是在根据不同时间进行不断产生中断来实现并行运城任务。
在本实验中需要配置两个LED以及一个USART1,具体如下图所示
配置完后就点击生成文件并在Keil5中进行驱动移植操作
Keil5移植操作
首先将上面创建好的FreeRTOS文件夹复制到我们的工程文件中
接着打开keil5进行驱动文件移植,先点击魔法棒接着C/C++添加include路径将FreeRTOSs/include文件夹路径添加进去
同理将工程文件中的MDK-ARM路径添加进去,再去Free RTOS/portable/ RVDS/ARM_CM3路径添加进去,这里是因为STM32F103ZET6使用的是M3的内核所以添加这个,同理其他内核将其相对应的添加进去即可。
完成以上添加路径操作后开始添加.c文件
右击Core文件夹点击添加现有文件,打开FreeRTOS文件夹将其中.c文件都加入后,同理打开FreeRTOS/ MengMang文件夹后添加heap_4.c和port,c文件,这个是内存管理文件,可以根据实际应用选择。
接下来就是添加配置文件,在keil5中新建一个.h文件命名为FreeRTOSConfig.h
该新建文件保存在刚刚我们添加过的MDK-arm文件夹下。
接下来将以下代码复制进去
#ifndef FREERTOS_CONFIG_H__
#define FREERTOS_CONFIG_H__
//#define xPortPendSVHandler PendSV_Handler
//#define xPortSysTickHandler SysTick_Handler
//#define vPortSVCHandler SVC_Handler
// 设置为1使用抢占式,为0使用时间片轮转调度。
#define configUSE_PREEMPTION 1
// 设置为1使能低功耗tickless模式,为0保持系统节拍(tick)中断一直运行。
#define configUSE_TICKLESS_IDLE 0
// 系统时钟主频
#define configCPU_CLOCK_HZ 72000000
// 系统节拍中断的频率,即1s进中断的次数,配置为1000就是一秒进1000次中断,系统节拍就是1s。
#define configTICK_RATE_HZ 1000
// 任务最大优先级,对于STM32来说最大不要超过32
#define configMAX_PRIORITIES 32
// 任务最小栈大小
#define configMINIMAL_STACK_SIZE 64
// FreeRTOR堆空间大小
#define configTOTAL_HEAP_SIZE 8192
// 任务名称最大长度
#define configMAX_TASK_NAME_LEN 16
// 系统节拍计数器的变量类型,即定义portTickType是表示16位变量还是32位变量。
#define configUSE_16_BIT_TICKS 0
// 设置为1允许任务调度,为0不允许(时间片耗尽才让出CPU使用权),该参数抢占式方式下才生效
#define configIDLE_SHOULD_YIELD 1
// 设置是否使用互斥量
#define configUSE_MUTEXES 1
// 设置是否使用递归互斥量
#define configUSE_RECURSIVE_MUTEXES 0
// 设置是否使用计数信号量
#define configUSE_COUNTING_SEMAPHORES 0
// 设置可以记录的队列和信号量的最大数目
#define configQUEUE_REGISTRY_SIZE 10
// 是否使用空闲钩子函数
#define configUSE_IDLE_HOOK 0
// 是否使用TICK嘀嗒钩子函数
#define configUSE_TICK_HOOK 0
// 是否使用栈溢出检查
#define configCHECK_FOR_STACK_OVERFLOW 0
// 是否使用内存申请失败钩子函数
#define configUSE_MALLOC_FAILED_HOOK 0
// 是否使用软件定时器
#define configUSE_TIMERS 1
// 设置软件定时器服务/守护进程的优先级
#define configTIMER_TASK_PRIORITY 3
// 设置软件定时器命令队列的长度
#define configTIMER_QUEUE_LENGTH 10
// 设置软件定时器服务/守护进程任务的堆栈深度
#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE
// STM32的最低优先级
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
// 能够在中断服务函数中安全调用FreeRTOS API的中断最低优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 1
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << 4 )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << 4 )
// 将以下定义设置为1以包含API函数,或设置为0排除API函数
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_xResumeFromISR 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1
#define INCLUDE_uxTaskGetStackHighWaterMark 0
#define INCLUDE_xTaskGetIdleTaskHandle 0
#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
#define INCLUDE_pcTaskGetTaskName 0
#define INCLUDE_eTaskGetState 0
#define INCLUDE_xEventGroupSetBitFromISR 1
#define INCLUDE_xTimerPendFunctionCall 0
#endif /* FREERTOS_CONFIG_H__ */
由于我们更改了配置因此我们需要在stm32f1xxx_it.c文件中添加以下代码:
首先在此次全局引用函数
extern void xPortPendSVHandler(void);
extern void xPortSysTickHandler(void);
extern void vPortSVCHandler(void);
接着依次在以下几处添加函数:
在完成以上操作之后FreeRTOS操作系统就移植成功了接下来打开main.c文件进行代码编写。
首先添加以下头文件以及串口重定向,魔法般中勾选USEMicroLIB,这样子就可以在串口调试助手进行查看。
#include "FreeRTOSConfig.h"
#include "FreeRTOS.h"
#include "task.h"
//串口重定向
#include "stdio.h"
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);//HAL库串口发送函数
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);//HAL库串口接收函数
return ch;
}
接着就是任务编写,本实验我们编写三个任务函数,分别是led1翻转、led2翻转、串口发送。
void led1_task(void* arg)
{
while(1)
{
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5);
vTaskDelay(1000);//延时1000ms
}
}
void led2_task(void* arg)
{
while(1)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
vTaskDelay(1500);
}
}
void print_task(void* arg)
{
while(1)
{
HAL_UART_Transmit(&huart1, (uint8_t *)"HelloWorld\r\n", 12, 100);
vTaskDelay(1000);
}
}
完成任务函数编写后在主函数中各项初始化结束后添加以下代码
// xTaskCreate函数参数说明:
// 1、任务入口函数
// 2、任务名称
// 3、堆栈大小,单位word
// 4、传递给任务的参数
// 5、任务优先级
// 6、任务句柄
// 创建任务led1灯任务
xTaskCreate(led1_task, "led1_task", 64, NULL, 3, NULL);
// 创建任务led2灯任务
xTaskCreate(led2_task, "led2_task", 64, NULL, 4, NULL);
// 创建串口打印任务
xTaskCreate(print_task, "print_task", 128, NULL, 5, NULL);
// 启动任务调度
vTaskStartScheduler();
最后就是编译烧写可以发现LED1、2按时翻转,串口发送Halle world
以上就是简单的Free RTOS移植并且多任务执行的简单案例,后续会对RTOS的内核、任务管理等进行详细讲解。