FreeRTOS在STM32上的移植实战:从环境准备到代码实现的详细步骤(基于标准库-Keil环境)

前言

  在嵌入式系统开发中,FreeRTOS以其轻量级、免费商用的特性成为实时操作系统的首选之一。然而,对于新手来说,将FreeRTOS成功移植到STM32平台往往是一道不小的门槛——从源码复制到配置文件修改,从冲突解决到调试验证,每一步都可能遇到“编译报错”“任务调度异常”等挑战。这篇文章将以正点原子STM32F4探索者开发板为例,手把手演示FreeRTOS的完整移植流程,帮助你避开新手常见的坑。

  文章将从基础准备工作开始:如何获取官方工程、准备硬件环境,到源码目录结构解析、关键文件筛选;接着深入配置细节:修改FreeRTOSConfig.h中的中断优先级、解决SysTick函数冲突,调整USART和延时函数的OS兼容性;最后通过多任务实验验证移植效果——让LED以不同频率闪烁、串口实时输出浮点数据。每一步都配有详细图解和代码片段,即使是首次接触RTOS移植的开发者,也能跟着步骤完成从“零基础”到“运行验证”的全过程。

  无论你是想系统学习FreeRTOS移植的学生,还是需要在项目中集成RTOS的工程师,这篇文章都将成为实用的操作指南。文中特别针对正点原子工程的适配细节进行了说明,并提供了修改前后的代码对比,帮助你理解移植过程中每一步操作的目的和影响。建议收藏本文,当你在FreeRTOS移植中遇到问题时,它将成为你查阅的实用手册。

  本文基于的硬件是STM32F407ZGT6,软件工程基于正点原子的官方例程——LED跑马灯工程。使用的FreeRTOS版本是V9.0.0。至于这个版本的源码,在FreeRTOS官网上已经无法获取了,我这里为大家提供一下,大家自行下载即可。

通过网盘分享的文件:FreeRTOS V9.0.0官方源码
链接: https://pan.baidu.com/s/1ieP0ECsRtAMYQG03TTVFhQ?pwd=cdp3 提取码: cdp3

FreeRTOS简介

  FreeRTOS是一款轻量级的实时操作系统,市场占有率高。学会了可以找好工作的啦!相比起来UCOS,它是完全免费的,可以免费商用,所以使用的人多。
在FreeRTOS的应用上,我们可以调用它内部的函数,新建几个“任务”,然后初始化并启动操作系统调度。之后就可以看到我们的几个任务会被操作系统调度执行。所谓的任务,就是一个个函数,里面写while(1)死循环,死循环里写上我们想做的事。

FreeRTOS移植注意事项

  在移植过程中,新手往往会遇到各样的坑,然后可能会气馁,产生消极情绪:“怎么移植起来这么难!”,哈哈,不过不用怕,我在这里一步步讲清楚步骤,大家跟着我的步骤,复制我的代码就可以实现啦!

前期准备

软件工程准备

  首先我们准备一个正点原子官方的LED跑马灯工程,如果你没有,又不想去官网上下载,我在这里很贴心的为大家准备好了,大家复制我的百度网盘链接下载即可。

通过网盘分享的文件:实验1 跑马灯实验.zip
链接: https://pan.baidu.com/s/1nQhNMqttZoa3cJGM0zwsLA?pwd=u2f4 提取码: u2f4

硬件准备

  如果你使用的是正点原子STM32F4探索者开发板,那就直接使用即可。如果你使用的是同款主控芯片的其他开发板,那么就把LED灯对应的引脚改一下即可,也没有问题。如果是其他的芯片,也可以参照着本文修改。
【 STM32F4无法在keil上进行软件仿真,所以希望您有一个硬件开发板来验证FreeRTOS的功能】

复制FreeRTOS源码到工程

复制源代码

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

将上图中的Source文件夹的内容,全部复制到工程路径下新建的FreeRTOS下面。

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

删除无关代码文件

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

在keil中增加分组

最终的结果如下图:
在这里插入图片描述
具体的操作如下:
在这里插入图片描述
之后选择Manage Project Items,进去下面的界面。
在这里插入图片描述
在这里插入图片描述
新建的分组名称:
FreeRTOS_CORE、FreeRTOS_PORTABLE。
(大家直接复制即可,就不用打字啦)
其中FreeRTOS_CORE分组需要加入:
在这里插入图片描述
对于FreeRTOS_PORTABLE,需要加入:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在keil中增加头文件路径

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

修改工程代码

修改FreeRTOSConfig.h

在这里插入图片描述
修改FreeRTOSConfig.h的宏定义
【因为这几个宏开启了相关的钩子函数,但在源码中未定义这些函数,所以需要修改宏定义,否则会编译报错】
在这里插入图片描述
在FreeRTOS中有定义SysTick_Handler函数,但正点原子的delay.c文件中有定义此函数,所以会有冲突。这里将FreeRTOS里的宏注释,免得重复定义,编译报错。
在这里插入图片描述
而在delay.c里面,其实有调用FreeRTOS的函数,可以看一下下面的截图,对比一下上面的宏定义,是不是一样啊?
在这里插入图片描述

修改后的代码如下,方便复制粘贴:

/*
    FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.
    All rights reserved

    VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.

    This file is part of the FreeRTOS distribution.

    FreeRTOS is free software; you can redistribute it and/or modify it under
    the terms of the GNU General Public License (version 2) as published by the
    Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception.

    ***************************************************************************
    >>!   NOTE: The modification to the GPL is included to allow you to     !<<
    >>!   distribute a combined work that includes FreeRTOS without being   !<<
    >>!   obliged to provide the source code for proprietary components     !<<
    >>!   outside of the FreeRTOS kernel.                                   !<<
    ***************************************************************************

    FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    FOR A PARTICULAR PURPOSE.  Full license text is available on the following
    link: http://www.freertos.org/a00114.html

    ***************************************************************************
     *                                                                       *
     *    FreeRTOS provides completely free yet professionally developed,    *
     *    robust, strictly quality controlled, supported, and cross          *
     *    platform software that is more than just the market leader, it     *
     *    is the industry's de facto standard.                               *
     *                                                                       *
     *    Help yourself get started quickly while simultaneously helping     *
     *    to support the FreeRTOS project by purchasing a FreeRTOS           *
     *    tutorial book, reference manual, or both:                          *
     *    http://www.FreeRTOS.org/Documentation                              *
     *                                                                       *
    ***************************************************************************

    http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading
    the FAQ page "My application does not run, what could be wrong?".  Have you
    defined configASSERT()?

    http://www.FreeRTOS.org/support - In return for receiving this top quality
    embedded software for free we request you assist our global community by
    participating in the support forum.

    http://www.FreeRTOS.org/training - Investing in training allows your team to
    be as productive as possible as early as possible.  Now you can receive
    FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
    Ltd, and the world's leading authority on the world's leading RTOS.

    http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
    including FreeRTOS+Trace - an indispensable productivity tool, a DOS
    compatible FAT file system, and our tiny thread aware UDP/IP stack.

    http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
    Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.

    http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
    Integrity Systems ltd. to sell under the OpenRTOS brand.  Low cost OpenRTOS
    licenses offer ticketed support, indemnification and commercial middleware.

    http://www.SafeRTOS.com - High Integrity Systems also provide a safety
    engineered and independently SIL3 certified version for use in safety and
    mission critical applications that require provable dependability.

    1 tab == 4 spaces!
*/


#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/*-----------------------------------------------------------
 * Application specific definitions.
 *
 * These definitions should be adjusted for your particular hardware and
 * application requirements.
 *
 * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
 * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
 *
 * See http://www.freertos.org/a00110.html.
 *----------------------------------------------------------*/

/* Ensure stdint is only used by the compiler, and not the assembler. */
/*#ifdef __ICCARM__*/
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
	#include <stdint.h>
	extern uint32_t SystemCoreClock;
#endif

#define configUSE_PREEMPTION			1
#define configUSE_IDLE_HOOK				0/*1*/
#define configUSE_TICK_HOOK				0/*1*/
#define configCPU_CLOCK_HZ				( SystemCoreClock )
#define configTICK_RATE_HZ				( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES			( 5 )
#define configMINIMAL_STACK_SIZE		( ( unsigned short ) 130 )
#define configTOTAL_HEAP_SIZE			( ( size_t ) ( 75 * 1024 ) )
#define configMAX_TASK_NAME_LEN			( 10 )
#define configUSE_TRACE_FACILITY		1
#define configUSE_16_BIT_TICKS			0
#define configIDLE_SHOULD_YIELD			1
#define configUSE_MUTEXES				1
#define configQUEUE_REGISTRY_SIZE		8
#define configCHECK_FOR_STACK_OVERFLOW	0/*2*/
#define configUSE_RECURSIVE_MUTEXES		1
#define configUSE_MALLOC_FAILED_HOOK	0/*1*/
#define configUSE_APPLICATION_TASK_TAG	0
#define configUSE_COUNTING_SEMAPHORES	1
#define configGENERATE_RUN_TIME_STATS	0

/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 		0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Software timer definitions. */
#define configUSE_TIMERS				1
#define configTIMER_TASK_PRIORITY		( 2 )
#define configTIMER_QUEUE_LENGTH		10
#define configTIMER_TASK_STACK_DEPTH	( configMINIMAL_STACK_SIZE * 2 )

/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet		1
#define INCLUDE_uxTaskPriorityGet		1
#define INCLUDE_vTaskDelete				1
#define INCLUDE_vTaskCleanUpResources	1
#define INCLUDE_vTaskSuspend			1
#define INCLUDE_vTaskDelayUntil			1
#define INCLUDE_vTaskDelay				1

/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
	/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
	#define configPRIO_BITS       		__NVIC_PRIO_BITS
#else
	#define configPRIO_BITS       		4        /* 15 priority levels */
#endif

/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			0xf

/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY	5

/* Interrupt priorities used by the kernel port layer itself.  These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! 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) )
	
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }	
	
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
/*#define xPortSysTickHandler SysTick_Handler*/

#endif /* FREERTOS_CONFIG_H */

修改stm32f4xx_it.c

【因为port.c和stm32f4xx_it.c里面有重复定义的函数,所以需要将stm32f4xx_it.c里面的相关函数注释掉】
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

修改SYSTEM文件夹下的文件

在正点原子的工程中,有SYSTEM文件夹下的文件需要修改。
如果不是使用正点原子的工程,那么这里的修改不用管啦。

修改sys.h

在这里插入图片描述
修改后的代码(方便大家复制粘贴):

//0,不支持ucos
//1,支持ucos
#define SYSTEM_SUPPORT_OS		1		//定义系统文件夹是否支持UCOS

修改usart.c

在这里插入图片描述
修改后的代码(方便大家复制粘贴):

//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
/*#include "includes.h"*/					//ucos 使用	  
#include "FreeRTOS.h"
#endif

FreeRTOS不需要在中断进入和退出的时候加上OSIntEnter()和OSIntExit(),所以注释掉。
在这里插入图片描述
在这里插入图片描述
修改后的代码(方便大家复制粘贴):

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 Res;
#if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	/*OSIntEnter();   */ 
#endif
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
	{
		Res =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
		{
			if(USART_RX_STA&0x4000)//接收到了0x0d
			{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
			}
			else //还没收到0X0D
			{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
				{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
				}		 
			}
		}   		 
  } 
#if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	/*OSIntExit();  */											 
#endif
} 
#endif	

修改delay.c

直接上代码吧,里面的修改内容有点多……

#include "delay.h"
#include "sys.h"
////////////////////////////////////////////////////////////////////////////////// 	 
//如果使用OS,则包括下面的头文件(以ucos为例)即可.
#if SYSTEM_SUPPORT_OS
/*#include "includes.h"	*/				//支持OS时,使用	  
#include "FreeRTOS.h"
#include "task.h"
#endif
//////////////////////////////////////////////////////////////////////////////////  
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F407开发板
//使用SysTick的普通计数模式对延迟进行管理(支持OS)
//包括delay_us,delay_ms
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/5/2
//版本:V1.3
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//修改说明
//V1.1 20140803 
//1,delay_us,添加参数等于0判断,如果参数等于0,则直接退出. 
//2,修改ucosii下,delay_ms函数,加入OSLockNesting的判断,在进入中断后,也可以准确延时.
//V1.2 20150411  
//修改OS支持方式,以支持任意OS(不限于UCOSII和UCOSIII,理论上任意OS都可以支持)
//添加:delay_osrunning/delay_ostickspersec/delay_osintnesting三个宏定义
//添加:delay_osschedlock/delay_osschedunlock/delay_ostimedly三个函数
//V1.3 20150521
//修正UCOSIII支持时的2个bug:
//delay_tickspersec改为:delay_ostickspersec
//delay_intnesting改为:delay_osintnesting
////////////////////////////////////////////////////////////////////////////////// 

static u8  fac_us=0;							//us延时倍乘数			   
static u16 fac_ms=0;							//ms延时倍乘数,在os下,代表每个节拍的ms数
	
#if SYSTEM_SUPPORT_OS							//如果SYSTEM_SUPPORT_OS定义了,说明要支持OS了(不限于UCOS).
//当delay_us/delay_ms需要支持OS的时候需要三个与OS相关的宏定义和函数来支持
//首先是3个宏定义:
//    delay_osrunning:用于表示OS当前是否正在运行,以决定是否可以使用相关函数
//delay_ostickspersec:用于表示OS设定的时钟节拍,delay_init将根据这个参数来初始哈systick
// delay_osintnesting:用于表示OS中断嵌套级别,因为中断里面不可以调度,delay_ms使用该参数来决定如何运行
//然后是3个函数:
//  delay_osschedlock:用于锁定OS任务调度,禁止调度
//delay_osschedunlock:用于解锁OS任务调度,重新开启调度
//    delay_ostimedly:用于OS延时,可以引起任务调度.

//本例程仅作UCOSII和UCOSIII的支持,其他OS,请自行参考着移植
//支持UCOSII
#ifdef 	OS_CRITICAL_METHOD						//OS_CRITICAL_METHOD定义了,说明要支持UCOSII				
#define delay_osrunning		OSRunning			//OS是否运行标记,0,不运行;1,在运行
#define delay_ostickspersec	OS_TICKS_PER_SEC	//OS时钟节拍,即每秒调度次数
#define delay_osintnesting 	OSIntNesting		//中断嵌套级别,即中断嵌套次数
#endif

//支持UCOSIII
#ifdef 	CPU_CFG_CRITICAL_METHOD					//CPU_CFG_CRITICAL_METHOD定义了,说明要支持UCOSIII	
#define delay_osrunning		OSRunning			//OS是否运行标记,0,不运行;1,在运行
#define delay_ostickspersec	OSCfg_TickRate_Hz	//OS时钟节拍,即每秒调度次数
#define delay_osintnesting 	OSIntNestingCtr		//中断嵌套级别,即中断嵌套次数
#endif

 
//systick中断服务函数,使用OS时用到
extern void xPortSysTickHandler(void);
void SysTick_Handler(void)
{	
	/*
	if(delay_osrunning==1)					//OS开始跑了,才执行正常的调度处理
	{
		OSIntEnter();						//进入中断
		OSTimeTick();       				//调用ucos的时钟服务程序               
		OSIntExit();       	 				//触发任务切换软中断
	}
	*/
	if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)//系统已经运行
	{
		xPortSysTickHandler();
	}
}
#endif
			   
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
	u32 reload;
	/*SysTick频率为HCLK*/
 	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); /*SysTick_CLKSource_HCLK_Div8*/
	fac_us=SYSCLK;
	reload=SYSCLK;
	reload*=1000000/configTICK_RATE_HZ;	//根据delay_ostickspersec设定溢出时间
											//reload为24位寄存器,最大值:16777216,在168M下,约合0.7989s左右	
	fac_ms=1000/configTICK_RATE_HZ;		//代表OS可以延时的最少单位	   
	SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;   	//开启SYSTICK中断
	SysTick->LOAD=reload; 					//每1/delay_ostickspersec秒中断一次	
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; 	//开启SYSTICK    
}								    

#if SYSTEM_SUPPORT_OS 						//如果需要支持OS.
//延时nus
//nus:要延时的us数.	
//nus:0~204522252(最大值即2^32/fac_us@fac_us=21)	    								   
void delay_us(u32 nus)
{		
	u32 ticks;
	u32 told,tnow,tcnt=0;
	u32 reload=SysTick->LOAD;				//LOAD的值	    	 
	ticks=nus*fac_us; 						//需要的节拍数 
	told=SysTick->VAL;        				//刚进入时的计数器值
	while(1)
	{
		tnow=SysTick->VAL;	
		if(tnow!=told)
		{	    
			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
			else tcnt+=reload-tnow+told;	    
			told=tnow;
			if(tcnt>=ticks)break;			//时间超过/等于要延迟的时间,则退出.
		}  
	};
	/*delay_osschedunlock();*/					//恢复OS调度											    
}  
//延时nms
//nms:要延时的ms数
//nms:0~65535
void delay_ms(u16 nms)
{	
	if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)//如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度)	    
	{		 
		if(nms>=fac_ms)						//延时的时间大于OS的最少时间周期 
		{ 
   			vTaskDelay(nms/fac_ms);	//OS延时
		}
		nms%=fac_ms;						//OS已经无法提供这么小的延时了,采用普通方式延时    
	}
	delay_us((u32)(nms*1000));				//普通方式延时
}

void delay_xms(u32 nms)
{	 		  	  
	u32 i;		   
	
	for(i = 0; i< nms; i++)
	{
		delay_us(1000);
	}	
} 
#else  //不用ucos时
//延时nus
//nus为要延时的us数.	
//注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; 				//时间加载	  		 
	SysTick->VAL=0x00;        				//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 	 
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));	//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
	SysTick->VAL =0X00;       				//清空计数器 
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对168M条件下,nms<=798ms 
void delay_xms(u16 nms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD=(u32)nms*fac_ms;			//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL =0x00;           			//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;          //开始倒数 
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));	//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;       //关闭计数器
	SysTick->VAL =0X00;     		  		//清空计数器	  	    
} 
//延时nms 
//nms:0~65535
void delay_ms(u16 nms)
{	 	 
	u8 repeat=nms/540;						//这里用540,是考虑到某些客户可能超频使用,
											//比如超频到248M的时候,delay_xms最大只能延时541ms左右了
	u16 remain=nms%540;
	while(repeat)
	{
		delay_xms(540);
		repeat--;
	}
	if(remain)delay_xms(remain);
} 
#endif

修改main.c

这里定义了三个任务外加一个开始任务,并分别定义了优先级,堆栈的大小,任务句柄和任务函数。
在主函数中创建开始任务,之后启动任务调度。在开始任务中,创建其余的三个任务。
话不多说,直接上代码:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"

#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 50//任务堆栈大小
TaskHandle_t LED0Task_Handler;//任务句柄
void led0_task(void * p_arg);//任务函数

#define LED1_TASK_PRIO 3//任务优先级
#define LED1_STK_SIZE 50//任务堆栈大小
TaskHandle_t LED1Task_Handler;//任务句柄
void led1_task(void * p_arg);//任务函数

#define FLOAT_TASK_PRIO 4//任务优先级
#define FLOAT_STK_SIZE 128//任务堆栈大小
TaskHandle_t FLOATTask_Handler;//任务句柄
void float_task(void * p_arg);//任务函数

int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	delay_init(168);
	uart_init(115200);
	LED_Init();

	//创建开始任务
    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();
}

//开始任务函数
void start_task(void * pxParameters)
{
	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);	

    //创建浮点测试任务
	xTaskCreate((TaskFunction_t) float_task,
	            (const char*) "float_task",
				(uint16_t) FLOAT_STK_SIZE,
				(void*) NULL,
				(UBaseType_t)FLOAT_TASK_PRIO,
				(TaskHandle_t*) &FLOATTask_Handler);
	
	vTaskDelete(StartTask_Handler);//删除开始任务
	taskEXIT_CRITICAL();//推出临界区
}

//LED0任务函数
void led0_task(void * pxParameters)
{
    while(1)
	{
		LED0 = ~LED0;
		vTaskDelay(500);
	}
}

//LED1任务函数
void led1_task(void * pxParameters)
{
    while(1)
	{
		LED1 = 0;
		vTaskDelay(200);
		LED1 = 1;
		vTaskDelay(800);
	}
}

//浮点测试任务函数
void float_task(void * p_arg)
{
	static float float_num = 0.00;
    while(1)
	{
		float_num += 0.01f;
		printf("float_num的值为:%.4f\r\n", float_num);
		vTaskDelay(1000);
	}
}

实验结果

编译,下载代码之后,可以看到两个LED在闪烁,频率不同,串口在打印信息。
在这里插入图片描述

参考文献

本文参考了正点原子的书籍:
《STM32F4 FreeRTOS开发手册》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值