本篇文章主要是使用STM32Cubemx生成Keil工程,然后在移植FreeRTOS源码,最后测试使用。
一、FreeRTOS简介
Free 和 RTOS,Free 就是免费的、自由的、不受约束的意思,RTOS 全称是 Real Time Operating System,中文名就是实时操作系统。可以看出 FreeROTS 就是一个免费的 RTOS 类系统,FreeRTOS 是 RTOS 系统的一种,FreeRTOS 十分的小巧,可以在资源有限的微控制器中运行,当然了,FreeRTOS 不仅局限于在微控制器中使用。但从文件数量上来看 FreeRTOS 要比UCOSII 和 UCOSIII 小的多。
1、FreeRTOS特点
FreeRTOS是一个可裁剪、可固化到 ROM 的抢占式实时内核,并且可管理任务的数量不受限制,具有以下几个重要的特性:
■免费、开源且小巧:FreeRTOS 免费!这是最重要的一点,UCOS 是要收费的, 并且FreeRTOS 系统简单、小巧、易用,通常情况下内核占用 4k-9k 字节的空间。
■支持多种不同架构的不同型号的处理器: ARM架构系列,例如STM32和GD32的 F1、 F4、 F7 和 H7 等型号的 MCU 都可支持,已经在超过 30 种架构的芯片上进行了移植。,非常方便
■资料齐全:文档相对齐全,在 FreeRTOS 的官网:https://www.freertos.org/zh-cn-cmn-s/上可以找到所需的文档和源码,但是所有的文档都是英文版本的,而且下载 pdf 文档的时候是要收费的。
■应用范围广:高可移植性,代码主要 C 语言编写,因此许多软件厂商也使用 FreeRTOS 做本公司软件的操作系统,比如著名的 TouchGFX,其所有的例程都是基于 FreeRTOS 操作系统的。ST 公司的所有要使用到 RTOS 系统的例程也均采用了 FreeRTOS,由此可见免费的力量啊!
■内部资源丰富:持抢占式、合作式、时间片调度三种工作模式,任务数量不限、任务优先级不限、软件定时器、创新的事件组(或者事件标志)、消息队列、多种信号量、任务通知、内存管理、时间戳等;还包括强大的跟踪执行功能和堆栈溢出检测功能
2、FreeRTOS源码获取
在移植FreeRTOS 时候还需要用到FreeRTOS官方提供的两个额外的库源码文件,中文官方地址:FreeRTOS - Market leading RTOS (Real Time Operating System) for embedded systems with Internet of Things extensions
提供了大量的 µC/OS-III 相关的资料和不同版本的源代码,现在的目标就是要获取 FreeRTOS的源代码,打开后如下图所示:
点击下载FreeRTOS即可,如下图所示:
最后下载的是一个exe文件,运行exe即可得到源码,打开的源码如下:
二、STM32Cubemx工程生成
1、芯片选型
2、下载模式以及基准时钟配置
3、时钟配置
4、串口配置
5、工程输出
以上是最基本的工程配置。
三、RTOS源码移植到工程
需要移植的文件
上上述文件全部复制到,keil工程的FreeRTOS(创建的文件夹)
将portable文件夹多余文件删除,只留Keil、MemMang、RVDS三个就行
还需在Demo里面将对应keil工程中的FreeRTOSConfig.h复制到工程中的include中
到这里,文件的移植就基本完毕,剩下的是将文件添加至工程中。
添加文件的.h文件目录
在stm32f1xx_it.c中加入以下代码
extern void vPortSVCHandler( void );
extern void xPortPendSVHandler( void );
extern void xPortSysTickHandler( void );
然后再对应的函数下,将代码运行加入其中
一一对应的函数当中,相当于把启动文件加入其中。到这里就完成了90%了,剩下就是验证。
四、FreeRTOS验证
1、重写fputc()函数
int fputc(int ch, FILE *f) //串口重定向
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
到这里我们就可以使用printf来进行打印输出了。
main.c文件如下
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "FreeRTOSConfig.h"
#include "FreeRTOS.h"
#include "task.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED0_TASK_PRIO 2
//任务堆栈大小
#define LED0_STK_SIZE 128
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 3
//任务堆栈大小
#define LED1_STK_SIZE 128
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED0任务
xTaskCreate((TaskFunction_t )led0_task,
(const char* )"led0_task",
(uint16_t )LED0_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED0_TASK_PRIO,
(TaskHandle_t* )&LED0Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED0任务函数
void led0_task(void *pvParameters)
{
while(1)
{
printf("任务1 每隔1S进行工作\r\n");
vTaskDelay(1000);
}
}
//LED1任务函数
void led1_task(void *pvParameters)
{
while(1)
{
printf("任务2 每隔2S进行工作\r\n");
vTaskDelay(2000);
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM4 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM4) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
然后编译下来就是0错误0警告就算成功了,查看输出结果
测试结果:
四、总结
在本次移植时,遇见一个问题,就是任务只运行一下就不运行了,最终排查问题则是任务开辟空间小了,也就是LED0_STK_SIZE,最开始是20,发现log只打印一次就不打印了,然后改成128就可以了