主控芯片:STM32L431RCT6
封装类型:LQFP64
软件:STM32CubeMx 6.1.1 + keil 5.36
库:HAL
声明:STM32CubeMx内可以直接选择并配置FreeRTOS,但是手动移植FreeRTOS到HAL库的教程很少,所以我才选择写了这个教程。希望能够帮助到有这样需求的朋友。
1、FreeRTOS官网
2、sourceforge代码托管平台
这里我是从代码托管平台下载的v202012.00版本。
随后我们在上面创建的工程里面新建FreeRTOS文件夹,并在此文件夹内部新建include、portable、src文件夹。
.
|-- FreeRTOS
| |-- include
| |-- portable
| `-- src
在解压后的FreeRTOSv202012.00\FreeRTOS\Source\include文件夹下将以下14个.h复制到我们工程MYBearPiFreeRTOS\FreeRTOS\include文件夹下
croutine.h
deprecated_definitions.h
event_groups.h
FreeRTOS.h
list.h
mpu_prototypes.h
mpu_wrappers.h
portable.h
projdefs.h
queue.h
semphr.h
stack_macros.h
task.h
timers.h
将FreeRTOSv202012.00\FreeRTOS\Source\portable文件夹下的MemMang、RVDS复制到我们工程MYBearPiFreeRTOS\FreeRTOS\portable文件夹下
将FreeRTOSv202012.00\FreeRTOS\Source文件夹下除stream_buffer.c以外的.c复制到我们工程MYBearPiFreeRTOS\FreeRTOS\src文件夹下。总共6个.c
croutine.c
event_groups.c
list.c
queue.c
tasks.c
timers.c
将FreeRTOSv202012.00\FreeRTOS\Demo\CORTEX_STM32F103_Keil文件夹下的FreeRTOSConfig.h复制到我们工程MYBearPiFreeRTOS\Core\Inc文件夹下。
随后我们对FreeRTOSConfig.h进行修改,这里我使用的是野火的FreeRTOSConfig.h
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#include "main.h"
//针对不同的编译器调用不同的stdint.h文件
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
//断言
/* 因为这里用了printf所以我们需要在main.h或者此文件中#include */
#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
/************************************************************************
* FreeRTOS基础配置配置选项
*********************************************************************/
/* 置1:RTOS使用抢占式调度器;置0:RTOS使用协作式调度器(时间片)
*
* 注:在多任务管理机制上,操作系统可以分为抢占式和协作式两种。
* 协作式操作系统是任务主动释放CPU后,切换到下一个任务。
* 任务切换的时机完全取决于正在运行的任务。
*/
#define configUSE_PREEMPTION 1
//1使能时间片调度(默认式使能的)
#define configUSE_TIME_SLICING 1
/* 某些运行FreeRTOS的硬件有两种方法选择下一个要执行的任务:
* 通用方法和特定于硬件的方法(以下简称“特殊方法”)。
*
* 通用方法:
* 1.configUSE_PORT_OPTIMISED_TASK_SELECTION 为 0 或者硬件不支持这种特殊方法。
* 2.可以用于所有FreeRTOS支持的硬件
* 3.完全用C实现,效率略低于特殊方法。
* 4.不强制要求限制最大可用优先级数目
* 特殊方法:
* 1.必须将configUSE_PORT_OPTIMISED_TASK_SELECTION设置为1。
* 2.依赖一个或多个特定架构的汇编指令(一般是类似计算前导零[CLZ]指令)。
* 3.比通用方法更高效
* 4.一般强制限定最大可用优先级数目为32
* 一般是硬件计算前导零指令,如果所使用的,MCU没有这些硬件指令的话此宏应该设置为0!
*/
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
/* 置1:使能低功耗tickless模式;置0:保持系统节拍(tick)中断一直运行
* 假设开启低功耗的话可能会导致下载出现问题,因为程序在睡眠中,可用以下办法解决
*
* 下载方法:
* 1.将开发版正常连接好
* 2.按住复位按键,点击下载瞬间松开复位按键
*
* 1.通过跳线帽将 BOOT 0 接高电平(3.3V)
* 2.重新上电,下载
*
* 1.使用FlyMcu擦除一下芯片,然后进行下载
* STMISP -> 清除芯片(z)
*/
#define configUSE_TICKLESS_IDLE 0
/*
* 写入实际的CPU内核时钟频率,也就是CPU指令执行频率,通常称为Fclk
* Fclk为供给CPU内核的时钟信号,我们所说的cpu主频为 XX MHz,
* 就是指的这个时钟信号,相应的,1/Fclk即为cpu时钟周期;
*/
#define configCPU_CLOCK_HZ (SystemCoreClock)
//RTOS系统节拍中断的频率。即一秒中断的次数,每次中断RTOS都会进行任务调度
#define configTICK_RATE_HZ (( TickType_t )1000)
//可使用的最大优先级
#define configMAX_PRIORITIES (32)
//空闲任务使用的堆栈大小
#define configMINIMAL_STACK_SIZE ((unsigned short)128)
//任务名字字符串长度
#define configMAX_TASK_NAME_LEN (16)
//系统节拍计数器变量数据类型,1表示为16位无符号整形,0表示为32位无符号整形
#define configUSE_16_BIT_TICKS 0
//空闲任务放弃CPU使用权给其他同优先级的用户任务
#define configIDLE_SHOULD_YIELD 1
//启用队列
#define configUSE_QUEUE_SETS 1
//开启任务通知功能,默认开启
#define configUSE_TASK_NOTIFICATIONS 1
//使用互斥信号量
#define configUSE_MUTEXES 1
//使用递归互斥信号量
#define configUSE_RECURSIVE_MUTEXES 0
//为1时使用计数信号量
#define configUSE_COUNTING_SEMAPHORES 0
/* 设置可以注册的信号量和消息队列个数 */
#define configQUEUE_REGISTRY_SIZE 10
#define configUSE_APPLICATION_TASK_TAG 0
/*****************************************************************
FreeRTOS与内存申请有关配置选项
*****************************************************************/
//支持动态内存申请
#define configSUPPORT_DYNAMIC_ALLOCATION 1
//支持静态内存
#define configSUPPORT_STATIC_ALLOCATION 0
//系统所有总的堆大小
#define configTOTAL_HEAP_SIZE ((size_t)(36*1024))
/***************************************************************
FreeRTOS与钩子函数有关的配置选项
**************************************************************/
/* 置1:使用空闲钩子(Idle Hook类似于回调函数);置0:忽略空闲钩子
*
* 空闲任务钩子是一个函数,这个函数由用户来实现,
* FreeRTOS规定了函数的名字和参数:void vApplicationIdleHook(void ),
* 这个函数在每个空闲任务周期都会被调用
* 对于已经删除的RTOS任务,空闲任务可以释放分配给它们的堆栈内存。
* 因此必须保证空闲任务可以被CPU执行
* 使用空闲钩子函数设置CPU进入省电模式是很常见的
* 不可以调用会引起空闲任务阻塞的API函数
*/
#define configUSE_IDLE_HOOK 0
/* 置1:使用时间片钩子(Tick Hook);置0:忽略时间片钩子
*
*
* 时间片钩子是一个函数,这个函数由用户来实现,
* FreeRTOS规定了函数的名字和参数:void vApplicationTickHook(void )
* 时间片中断可以周期性的调用
* 函数必须非常短小,不能大量使用堆栈,
* 不能调用以”FromISR" 或 "FROM_ISR”结尾的API函数
*/
/*xTaskIncrementTick函数是在xPortSysTickHandler中断函数中被调用的。因此,vApplicationTickHook()函数执行的时间必须很短才行*/
#define configUSE_TICK_HOOK 0
//使用内存申请失败钩子函数
#define configUSE_MALLOC_FAILED_HOOK 0
/*
* 大于0时启用堆栈溢出检测功能,如果使用此功能
* 用户必须提供一个栈溢出钩子函数,如果使用的话
* 此值可以为1或者2,因为有两种栈溢出检测方法 */
#define configCHECK_FOR_STACK_OVERFLOW 0
/********************************************************************
FreeRTOS与运行时间和任务状态收集有关的配置选项
**********************************************************************/
//启用运行时间统计功能
#define configGENERATE_RUN_TIME_STATS 0
//启用可视化跟踪调试
#define configUSE_TRACE_FACILITY 0
/* 与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数
* prvWriteNameToBuffer()
* vTaskList(),
* vTaskGetRunTimeStats()
*/
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
/********************************************************************
FreeRTOS与协程有关的配置选项
*********************************************************************/
//启用协程,启用协程以后必须添加文件croutine.c
#define configUSE_CO_ROUTINES 0
//协程的有效优先级数目
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/***********************************************************************
FreeRTOS与软件定时器有关的配置选项
**********************************************************************/
//启用软件定时器
#define configUSE_TIMERS 0
//软件定时器优先级
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
//软件定时器队列长度
#define configTIMER_QUEUE_LENGTH 10
//软件定时器任务堆栈大小
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2)
/************************************************************
FreeRTOS可选函数配置选项
************************************************************/
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTimerPendFunctionCall 0
//#define INCLUDE_xTaskGetCurrentTaskHandle 1
//#define INCLUDE_uxTaskGetStackHighWaterMark 0
//#define INCLUDE_xTaskGetIdleTaskHandle 0
/******************************************************************
FreeRTOS与中断有关的配置选项
******************************************************************/
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
//中断最低优先级
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
//系统可管理的最高中断优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) /* 240 */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15 /*配置系统内核优先级,这里使用的是4bit优先级,所以最低为15,跟1 中的配置相同*/
/****************************************************************
FreeRTOS与中断服务函数有关的配置选项
****************************************************************/
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define xPortSysTickHandler SysTick_Handler
/* 以下为使用Percepio Tracealyzer需要的东西,不需要时将 configUSE_TRACE_FACILITY 定义为 0 */
#if ( configUSE_TRACE_FACILITY == 1 )
#include "trcRecorder.h"
#define INCLUDE_xTaskGetCurrentTaskHandle 1 // 启用一个可选函数(该函数被 Trace源码使用,默认该值为0 表示不用)
#endif
#endif /* FREERTOS_CONFIG_H */
FreeRTOS/portablde组中添加
port.c——路径:工程MYBearPiFreeRTOS\FreeRTOS\portable\RVDS\ARM_CM4F目录下
heap_4.c——路径:工程MYBearPiFreeRTOS\FreeRTOS\portable\MemMang目录下
FreeRTOS/src组中添加
croutine.c
event_groups.c
list.c
queue.c
tasks.c
timers.c
路径:工程MYBearPiFreeRTOS\FreeRTOS\src目录下
在Application/User/Core组中添加FreeRTOSConfig.h——路径:MYBearPiFreeRTOS\Core\Inc目录下
魔术棒添加路径
随后编译,可以看到0 Error(s),0 Warning(s)。说明我们移植成功了。
EasyLogger Github仓库
在工程目录下创建Peripheral文件夹,并将解压出来的EasyLogger-master目录下的easylogger文件夹复制到我们工程中创建的Peripheral文件夹下。
只需要将
elog.c ——路径:MYBearPiFreeRTOS\Peripheral\easylogger\src
elog_utils.c ——路径:MYBearPiFreeRTOS\Peripheral\easylogger\src
elog_port.c ——路径:MYBearPiFreeRTOS\Peripheral\easylogger\port
添加到我们创建的EasyLogger组中即可
main.c重定向fputc,因为我们已经移植了FreeRTOS并且在此基础上移植EasyLogger。main.c添加FreeRTOS的头文件,并且重定向fputc使用串口1进行发送。
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* FreeRTOS */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.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 ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
/* 进入临界区 */
taskENTER_CRITICAL();
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
/* 退出临界区 */
taskEXIT_CRITICAL();
return ch;
}
/* 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 */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
首先要去elog.h中ElogErrCode的枚举变量中添加ELOG_SEM_FAIL;并且添加我们自己的写的日志打印格式静态设置函数声明;修改宏定义LOG_TAG的内容,使其满足你的需求。我这里改成了FreeRTOS_TAG
随后按照如下对elog_port.c代码进行修改
#include "main.h"
#include
/* FreeRTOS */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* 日志互斥信号量句柄 */
static SemaphoreHandle_t LogMutexSemaphore = NULL;
/**
* EasyLogger port initialize
*
* @return result
*/
ElogErrCode elog_port_init(void) {
ElogErrCode result = ELOG_NO_ERR;
/* add your code here */
/* 创建日志互斥量 */
LogMutexSemaphore = xSemaphoreCreateMutex();
if ( NULL == LogMutexSemaphore )
{
printf("elog xSemaphoreCreateMutex create fail!\r\n");
result = ELOG_SEM_FAIL;
}
return result;
}
/**
* EasyLogger port deinitialize
*
*/
void elog_port_deinit(void) {
/* add your code here */
}
/**
* output log port interface
*
* @param log output of log
* @param size log size
*/
void elog_port_output(const char *log, size_t size) {
/* add your code here */
printf("%.*s", size, log);
}
/**
* output lock
*/
void elog_port_output_lock(void) {
/* add your code here */
if( NULL != LogMutexSemaphore )
{
/* 获取日志互斥量 */
xSemaphoreTake(LogMutexSemaphore,portMAX_DELAY);
}
}
/**
* output unlock
*/
void elog_port_output_unlock(void) {
/* add your code here */
if( NULL != LogMutexSemaphore )
{
/* 释放日志互斥量 */
xSemaphoreGive(LogMutexSemaphore);
}
}
/**
* get current time interface
*
* @return current time
*/
const char *elog_port_get_time(void) {
/* add your code here */
static char cur_system_time[16] = { 0 };
#if (INCLUDE_xTaskGetSchedulerState == 1 )
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
#endif
TickType_t tick = xTaskGetTickCount();
snprintf(cur_system_time, 16, "%d.%.3d", (tick / configTICK_RATE_HZ), tick % configTICK_RATE_HZ);
return cur_system_time;
#if (INCLUDE_xTaskGetSchedulerState == 1 )
}
#endif
return "";
}
/**
* get current process name interface
*
* @return current process name
*/
const char *elog_port_get_p_info(void) {
/* add your code here */
return "";
}
/**
* get current thread name interface
*
* @return current thread name
*/
const char *elog_port_get_t_info(void) {
/* add your code here */
#if (INCLUDE_xTaskGetSchedulerState == 1 )
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
#endif
return pcTaskGetName(xTaskGetCurrentTaskHandle());
#if (INCLUDE_xTaskGetSchedulerState == 1 )
}
#endif
return "";
}
/*******************************************************************************
* Function Name : static_set_output_log_format
* Description : 静态设置日志打印格式
* Input : None
* Output : None
* Return : None
* example ; log_a("Hello EasyLogger!"); //断言
log_e("Hello EasyLogger!"); //错误
log_w("Hello EasyLogger!"); //警告
log_i("Hello EasyLogger!"); //信息
log_d("Hello EasyLogger!"); //调试
log_v("Hello EasyLogger!"); //详细
*******************************************************************************/
void Static_Set_Output_Log_Format(void)
{
/* 初始化 EasyLogger */
elog_init();
/* 断言:输出所有内容 */
elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL);
/* 错误:输出日志级别、日志标签、日志时间和日志线程信息 */
elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME| ELOG_FMT_T_INFO);
/* 警告:输出日志级别、日志标签、日志时间和日志线程信息 */
elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME | ELOG_FMT_T_INFO);
/* 信息:输出日志级别、日志标签、日志时间和日志线程信息 */
elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME | ELOG_FMT_T_INFO);
/* 调试:输出除了函数名与进程信息之外的所有内容 */
elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_ALL & ~(ELOG_FMT_FUNC | ELOG_FMT_P_INFO));
/* 详细:输出所有内容 */
elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL);
/* start EasyLogger */
elog_start();
}
FreeRTOSConfig.h中打开任务通知的宏,因为我们的按键与LED通信使用了任务通知
#define configUSE_TASK_NOTIFICATIONS 1
并将以下宏修改为32;如果不修改,当你任务名称稍微长点的时候easylogger打印出来的任务名会不全。
#define configMAX_TASK_NAME_LEN (16)
创建AppTaskCreate任务用于创建LED任务,LED任务主要用于接收按键中断中发出来的任务通知,如果收到按键的任务通知则,翻转LED的电平状态。
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include
/* FreeRTOS */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/*====================各任务句柄====================*/
/* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;
/* LED任务句柄 */
static TaskHandle_t LED_Task_Handle = NULL;
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
/* 进入临界区 */
taskENTER_CRITICAL();
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
/* 退出临界区 */
taskEXIT_CRITICAL();
return ch;
}
/* 在许多使用场景中,使用直达任务通知要比使用信号量的速度更快,内存效率更高 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if( GPIO_Pin == KEY_Pin )
{
/* 使用任务通知代替二值信号量,发送一个任务通知给LED Task */
vTaskNotifyGiveFromISR(LED_Task_Handle,&xHigherPriorityTaskWoken);
}
/* 进行任务切换 */
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
static void LED_Task(void* parameter)
{
uint32_t ulEventsToProcess=0;
while(1)
{
/* 利用任务通知代替二值信号量 */
ulEventsToProcess = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
if(ulEventsToProcess != 0)
{
log_i("通知获取成功!");
/* 翻转LED引脚电平 */
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
}
else
{
log_e("通知获取失败!");
}
}
}
static void AppTaskCreate(void)
{
BaseType_t xReturn = pdFAIL;
/* 进入临界区 */
taskENTER_CRITICAL();
/* 创建LED_Task任务 */
xReturn = xTaskCreate(
(TaskFunction_t )LED_Task, //任务函数
(const char* )"LED Task", //任务名称
(uint16_t )512, //任务栈大小
(void* )NULL, //任务入口函数参数
(UBaseType_t )2, //任务优先级
(TaskHandle_t* )&LED_Task_Handle //任务控制块
);
if( pdPASS == xReturn )
log_i("LED Task任务创建成功!");
else
log_e("LED Task任务创建失败!");
/* 删除AppTaskCreate任务 */
vTaskDelete(AppTaskCreate_Handle);
/* 退出临界区 */
taskEXIT_CRITICAL();
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
BaseType_t xReturn = pdFAIL;
/* 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 */
Static_Set_Output_Log_Format();
/* 动态创建任务 */
xReturn = xTaskCreate(
(TaskFunction_t )AppTaskCreate, //任务函数
(const char* )"AppTaskCreate", //任务名称
(uint16_t )512, //任务栈大小
(void* )NULL, //任务入口函数参数
(UBaseType_t )1, //任务优先级
(TaskHandle_t* )&AppTaskCreate_Handle //任务控制块
);
if( pdPASS == xReturn )
{
log_i("启动任务,开始调度!");
vTaskStartScheduler(); //启动任务,开启调度
}
else
{
return -1;
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
声明:本文之所以使用Gitee而不使用其他的代码托管平台。主要还是因为有些朋友在登录github的时候或多或少都会出现网络方面的原因,导致看着教程操作不是那么顺畅。所以我改用Gitee。
以下的教程希望可以帮助到你使用远程代码托管平台
Git官方教程——Pro Git
Gitee官方教程
Github官方教程
GitLab官方教程
这里值得注意的是,如果你没有把你本地的SSH公钥上传到你自己的账号上的话,还是建议你先使用HTTPS地址进行提交。后续等你掌握了上传本地的SSH公钥后再转成SSH提交
readme.md——用于对你的项目进行一些说明
.gitignore——用于git忽略你每次编译生成的中间文件以及其他一些你想忽略的文件
keilkill.bat——正点原子STM32中的批处理文件。
.gitignore内容
常用.gitignore
如果你后续修改了.gitignore要使其生效则可以参考以下帖子
Git 修改.gitignore如何生效?
# Keil Generated output files in the sub-directories .\Listings and .\Objects
*.lst
*.o
*.d
*.crf
*.lnp
*.axf
*.htm
*.build_log.htm
*.dep
*.iex
*.i
*.bin
*.hex
*.map
#Keil Project screen layout file
*.uvguix.*
*.uvgui.*
*.uvoptx
#Keil file
MDK-ARM/*/
#JLINK file
JLinkLog.txt
keilkill.bat内容
del *.bak /s
del *.ddk /s
del *.edk /s
del *.lst /s
del *.lnp /s
del *.mpf /s
del *.mpj /s
del *.obj /s
del *.orig /s
del *.omf /s
::del *.opt /s ::不允许删除JLINK的设置
del *.plg /s
del *.rpt /s
del *.tmp /s
del *.__i /s
del *.crf /s
del *.o /s
del *.d /s
del *.axf /s
del *.tra /s
del *.dep /s
del JLinkLog.txt /s
del *.iex /s
del *.htm /s
del *.sct /s
del *.map /s
exit
前提是你的电脑里面安装了git
在我们创建的目录下右键点击Git Bash Here进入命令行界面
在Git Bash中输入以下命令
首次提交准备工作
git init
git config --global user.name "用户名" //这里的用户名与邮箱用于远程仓库区分是谁提交了代码
git config --global user.email "邮箱"
git remote add origin 你创建的远程仓库地址 //也就是之前我们复制的HTTPS或SSH的仓库地址
提交流程
git pull //首次提交的时候可以不使用此命令,但是后续每次提交都需要执行;目的是为了从远程仓库下拉代码,保证每次提交之前都与远程库保持一致
git status
git add .
git commit -m "你为本次提交备注的信息"
git push --set-upstream origin master //首次提交执行此命令,后续提交只需要git push即可
https://gitee.com/MYYouJiang/my-bear-pi-free-rtos-project.git
① SYS ⇒ Timebase Source的配置不能使用Systick,而是要使用除了Systick以外的配置。
原因参考如下帖子:
STM32CubeMX学习笔记(36)——FreeRTOS实时操作系统使用(中断管理)
② 如果要使用中断,则中断优先级的分组应该选择为组4,并且抢占优先级只能在5-15之间进行选择。
原因:因为受到我们在FreeRTOSConsig.h中
/*
/* 中断最低优先级 */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
/* 系统可管理的最高中断优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* This is the raw value as per the Cortex-M3 NVIC. Values can be 255
(lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) /* 240 */
/*
* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
* See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html.
*/
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) /* equivalent to 0xb0, or priority 11. */
configMAX_SYSCALL_INTERRUPT_PRIORITY这个宏的影响,所以我们的抢占优先级只能是5-15。要不然会出现临界区无法屏蔽我们的外部中断的错误出现。(也就是卡死在port.c中424行的void vPortEnterCritical( void )函数当中。)
③ 如果你的OS任务名过长,则需要修改FreeRTOSConfig.h中任务名字字符串长度#define configMAX_TASK_NAME_LEN (16)的宏定义。要不然你easylogger打印出的任务名会出现残缺的现象。
《FreeRTOS_Reference_Manual_V10.0.0》——FreeRTOS官方
《FreeRTOS内核实现与应用开发实战指南基于STM32》——野火
《STM32F103 FreeRTOS开发手册V1.1》——正点原子
《FreeRTOS源码详解与应用开发》——正点原子
《安富莱_STM32-V4开发板_FreeRTOS教程(V1.0)》——安富莱
《UM1722:在具有 RTOS 的 STM32Cube 上开发应用》——ST官网教程