一、下载FreeRTOS源码
上一篇文章介绍了通过修改FreeRTOS官方例程,制作自己的FreeRTOS工程模板的方法
移植FreeRTOS工程(标准库)-优快云博客文章浏览阅读446次,点赞9次,收藏14次。裁剪FreeRTOS官方示例工程(v2022.12.01),制作自己的FreeRTOS工程模板,更新其中的库函数https://blog.youkuaiyun.com/weixin_69034136/article/details/146118975?fromshare=blogdetail&sharetype=blogdetail&sharerId=146118975&sharerefer=PC&sharesource=weixin_69034136&sharefrom=from_link这篇文章介绍另一种方法,不修改官方示例工程,直接将FreeRTOS源码移植到自己的工程中,以下面这个OLED工程为例
【免费】江协科技0.96寸OLED驱动函数(HAL库移植)_stm32串口调试助手资源-优快云文库https://download.youkuaiyun.com/download/weixin_69034136/89945311?spm=1001.2014.3001.5503还是先下载FreeRTOS官方源码(v202212.01)https://github.com/FreeRTOS/FreeRTOS/releases/download/202212.01/FreeRTOSv202212.01.zip
二、移植FreeRTOS源码到自己的工程
在自己的工程文件夹中新建一个FreeRTOS文件夹,将官方源码中的FreeRTOS\Source目录复制到这个文件夹中,把官方源码中的FreeRTOS\Demo\CORTEX_STM32F103_Keil目录下的FreeRTOSConfig.h文件复制到FreeRTOS\Source\include目录下
进入Keil工程,新建一个FreeRTOS组,把FreeRTOS\Source目录下的list.c、queue.c、 tasks.c、把FreeRTOS\Source\portable\RVDS\ARM_CM3目录下的port.c、把FreeRTOS\Source\portable\MemMang目录下的heap_4.c添加到FreeRTOS组中
添加头文件路径FreeRTOS\Source\include
FreeRTOS\Source\portable\RVDS\ARM_CM3
在工程中添加LED.c、LED.h文件,用于测试
LED.c文件内容如下:
#include "stm32f1xx_hal.h"
#define LED_PORTA_Pin (GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4)
/* LED点亮方式 0表示低电平点亮,1表示高电平点亮 */
#define LED_Light_Mode 0
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pin = LED_PORTA_Pin;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
HAL_GPIO_WritePin(GPIOA, LED_PORTA_Pin,(GPIO_PinState)(!LED_Light_Mode));
}
void LED_ON(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
HAL_GPIO_WritePin(GPIOx, GPIO_Pin,(GPIO_PinState)(LED_Light_Mode));
}
void LED_OFF(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
HAL_GPIO_WritePin(GPIOx, GPIO_Pin,(GPIO_PinState)(!LED_Light_Mode));
}
void LED_Turn(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
HAL_GPIO_WritePin(GPIOx, GPIO_Pin,(GPIO_PinState)(!(HAL_GPIO_ReadPin(GPIOx, GPIO_Pin))));
}
LED.h文件内容如下:
#ifndef __LED_H
#define __LED_H
void LED_Init(void);
void LED_ON(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void LED_OFF(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void LED_Turn(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
#endif
进入tasks.c文件,在第1959行处,右键跳转到configSUPPORT_STATIC_ALLOCATION定义处,
把configSUPPORT_STATIC_ALLOCATION这个宏的值从0改为1
进入STM32CubeMx,把HAL库的时基源从Systick改为其他的定时器
因为FreeRTOS也使用Systick作为时基,防止冲突,所以修改HAL库的时基源
配置USART1
配置PA1、PA2、PA3,配置为通用推挽输出模式,初始电平为高电平
进入stm32f1xx_it.c文件,定义这三个宏
进入FreeRTOS.h文件,进入FreeRTOSConfig.h文件,在这个文件中定义这三个宏
在FreeRTOS中,SysTick_Handler(),PendSV_Handler()与 SVC_Handler()这三个很重要的函数都帮我们实现了,分别叫xPortPendSVHandler()、xPortSysTick_Handler()、 vPortSVCHandler()。在FreeRTOS官方例程的启动文件中,xPortPendSVHandler、xPortSysTick_Handler与 vPortSVCHandler直接取代了SysTick_Handler,PendSV_Handler与 SVC_Handler。也就是FreeRTOS源码中的启动文件是在原版的基础上经过了一定的修改,为了尽可能不去修改启动文件,所以我们通过宏定义来互换这些函数的名称,也能实现相同的效果
三、测试
创建五个任务,任务一到任务三实现LED闪烁,任务四和任务五使用一个函数,分别打印4和5
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "OLED.h"
#include "LED.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 */
const char task4 = 0;
const char task5 = 1;
TaskHandle_t task2_handle;
void Task1(void *param)
{
while(1)
{
LED_Turn(GPIOA,GPIO_PIN_1);
vTaskDelay(100);
}
}
void Task2(void *param)
{
while(1)
{
LED_Turn(GPIOA,GPIO_PIN_2);
vTaskDelay(200);
}
}
void Task3(void *param)
{
while(1)
{
LED_Turn(GPIOA,GPIO_PIN_3);
vTaskDelay(300);
}
}
void Task4(void *param)
{
while(1)
{
if ((int)param == 0)
{
printf("4");
vTaskDelay(1);
}
if ((int)param == 1)
{
printf("5");
vTaskDelay(1);
}
}
}
StaticTask_t ppxIdleTaskTCB;
StackType_t ppxIdleTaskStack[100];
void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
StackType_t ** ppxIdleTaskStackBuffer,
uint32_t * pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &ppxIdleTaskTCB;
*ppxIdleTaskStackBuffer = ppxIdleTaskStack;
*pulIdleTaskStackSize = 100;
}
/* 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 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
OLED_Init();
xTaskCreate(Task1,"Task1",100,NULL,1,NULL);
xTaskCreate(Task2,"Task2",100,NULL,1,&task2_handle);
xTaskCreate(Task3,"Task3",100,NULL,1,NULL);
xTaskCreate(Task4,"Task4",100,(void *)task4,1,NULL);
xTaskCreate(Task4,"Task5",100,(void *)task5,1,NULL);
vTaskStartScheduler();
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 */
int fputc(int ch, FILE * f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,1000);
return ch;
}
/* USER CODE END 4 */
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM1 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 == TIM1) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
编译后将其下载到开发板上,现象如下:
video_20250308_164905
下载地址: