STM32CubeMx+HAL库+小熊派+FreeRTOS+EasyLogger+Gitee+手把手教你

1、创建工程

主控芯片:STM32L431RCT6
封装类型:LQFP64
软件:STM32CubeMx 6.1.1 + keil 5.36
库:HAL

配置RCC与SYS

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
 

配置LED

在这里插入图片描述
 

配置KEY

在这里插入图片描述
在这里插入图片描述

 

配置串口

在这里插入图片描述
 

生成工程

在这里插入图片描述
 
 

2、手动移植Freertos

  声明:STM32CubeMx内可以直接选择并配置FreeRTOS,但是手动移植FreeRTOS到HAL库的教程很少,所以我才选择写了这个教程。希望能够帮助到有这样需求的朋友。
 

获取源码

1、FreeRTOS官网
在这里插入图片描述
 
2、sourceforge代码托管平台
在这里插入图片描述
 

  这里我是从代码托管平台下载的v202012.00版本。
  随后我们在上面创建的工程里面新建FreeRTOS文件夹,并在此文件夹内部新建include、portable、src文件夹。

.
|-- FreeRTOS
|   |-- include
|   |-- portable
|   `-- src

在这里插入图片描述
 
 

移植include

  在解压后的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

在这里插入图片描述
 
 

移植portable

  将FreeRTOSv202012.00\FreeRTOS\Source\portable文件夹下的MemMang、RVDS复制到我们工程MYBearPiFreeRTOS\FreeRTOS\portable文件夹下
在这里插入图片描述
 
 

移植src

  将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

在这里插入图片描述
 
 

复制并修改FreeRTOSConfig.h

  将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 <stdio.h> */
#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 */

 
 

keil中添加路径与配置

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)。说明我们移植成功了。
在这里插入图片描述

3、移植EasyLogger

获取源码

EasyLogger Github仓库
 

复制easylogger文件

  在工程目录下创建Peripheral文件夹,并将解压出来的EasyLogger-master目录下的easylogger文件夹复制到我们工程中创建的Peripheral文件夹下。
在这里插入图片描述
 

keil中添加路径

只需要将
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

  首先要去elog.h中ElogErrCode的枚举变量中添加ELOG_SEM_FAIL;并且添加我们自己的写的日志打印格式静态设置函数声明;修改宏定义LOG_TAG的内容,使其满足你的需求。我这里改成了FreeRTOS_TAG
在这里插入图片描述
 
 

修改elog_port.c

随后按照如下对elog_port.c代码进行修改

#include "main.h"
#include <elog.h>

/* 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();
}

 
 

修改elog_cfg.h

在这里插入图片描述
 
 

4、创建任务进行测试

  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 <elog.h>

/* 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 */
}
现象

在这里插入图片描述
 
 

5、使用Gitee远程仓库管理代码

  声明:本文之所以使用Gitee而不使用其他的代码托管平台。主要还是因为有些朋友在登录github的时候或多或少都会出现网络方面的原因,导致看着教程操作不是那么顺畅。所以我改用Gitee。

以下的教程希望可以帮助到你使用远程代码托管平台
Git官方教程——Pro Git
Gitee官方教程
Github官方教程
GitLab官方教程
 
 

Gitee创建远程仓库

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  这里值得注意的是,如果你没有把你本地的SSH公钥上传到你自己的账号上的话,还是建议你先使用HTTPS地址进行提交。后续等你掌握了上传本地的SSH公钥后再转成SSH提交
 
 

工程添加readme.md、.gitignore、keilkill.bat

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书籍

标准库

《FreeRTOS_Reference_Manual_V10.0.0》——FreeRTOS官方
《FreeRTOS内核实现与应用开发实战指南基于STM32》——野火
《STM32F103 FreeRTOS开发手册V1.1》——正点原子
《FreeRTOS源码详解与应用开发》——正点原子
《安富莱_STM32-V4开发板_FreeRTOS教程(V1.0)》——安富莱

HAL库

《UM1722:在具有 RTOS 的 STM32Cube 上开发应用》——ST官网教程

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值