FreeRTOS内存管理 基于STM32

这篇具有很好参考价值的文章主要介绍了FreeRTOS内存管理 基于STM32。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、内存管理的基本概念

二、内存管理的应用场景

三、heap_4.c

1.内存申请函数 pvPortMalloc()

2.内存释放函数 vPortFree()

 四、内存管理的实验

五、内存管理的实验现象


一、内存管理的基本概念

      在计算系统中,变量、中间数据一般存放在系统存储空间中,只有在实际使用时才将 它们从存储空间调入到中央处理器内部进行运算。通常存储空间可以分为两种:内部存储 空间和外部存储空间。内部存储空间访问速度比较快,能够按照变量地址随机地访问,也 就是我们通常所说的 RAM(随机存储器),或电脑的内存;而外部存储空间内所保存的内 容相对来说比较固定,即使掉电后数据也不会丢失,可以把它理解为电脑的硬盘。在这一 章中我们主要讨论内部存储空间(RAM)的管理——内存管理。

      FreeRTOS 操作系统将内核与内存管理分开实现,操作系统内核仅规定了必要的内存管 理函数原型,而不关心这些内存管理函数是如何实现的,所以在 FreeRTOS 中提供了多种 内存分配算法(分配策略),但是上层接口(API)却是统一的。这样做可以增加系统的 灵活性:用户可以选择对自己更有利的内存管理策略,在不同的应用场合使用不同的内存 分配策略。

      在嵌入式程序设计中内存分配应该是根据所设计系统的特点来决定选择使用动态内存 分配还是静态内存分配算法,一些可靠性要求非常高的系统应选择使用静态的,而普通的 业务系统可以使用动态来提高内存使用效率。静态可以保证设备的可靠性但是需要考虑内 存上限,内存使用效率低,而动态则是相反。

      FreeRTOS 内存管理模块管理用于系统中内存资源,它是操作系统的核心模块之一。主 要包括内存的初始化、分配以及释放。

      很多人会有疑问,什么不直接使用 C 标准库中的内存管理函数呢?在电脑中我们可以 用 malloc()和 free()这两个函数动态的分配内存和释放内存。但是,在嵌入式实时操作系统 中,调用 malloc()和 free()却是危险的,原因有以下几点:

 1.这些函数在小型嵌入式系统中并不总是可用的,小型嵌入式设备中的 RAM 不足。

 2.它们的实现可能非常的大,占据了相当大的一块代码空间。

 3.他们几乎都不是安全的。

 4.它们并不是确定的,每次调用这些函数执行的时间可能都不一样。

 5. 它们有可能产生碎片。

 6. 这两个函数会使得链接器配置得复杂。

 7.如果允许堆空间的生长方向覆盖其他变量占据的内存,它们会成为 debug 的灾难。

      在一般的实时嵌入式系统中,由于实时性的要求,很少使用虚拟内存机制。所有的内 存都需要用户参与分配,直接操作物理内存,所分配的内存不能超过系统的物理内存,所 有的系统堆栈的管理,都由用户自己管理。

      同时,在嵌入式实时操作系统中,对内存的分配时间要求更为苛刻,分配内存的时间 必须是确定的。一般内存管理算法是根据需要存储的数据的长度在内存中去寻找一个与这段数据相适应的空闲内存块,然后将数据存储在里面。而寻找这样一个空闲内存块所耗费 的时间是不确定的,因此对于实时系统来说,这就是不可接受的,实时系统必须要保证内 存块的分配过程在可预测的确定时间内完成,否则实时任务对外部事件的响应也将变得不可确定。

      而在嵌入式系统中,内存是十分有限而且是十分珍贵的,用一块内存就少了一块内存, 而在分配中随着内存不断被分配和释放,整个系统内存区域会产生越来越多的碎片,因为 在使用过程中,申请了一些内存,其中一些释放了,导致内存空间中存在一些小的内存块, 它们地址不连续,不能够作为一整块的大内存分配出去,所以一定会在某个时间,系统已 经无法分配到合适的内存了,导致系统瘫痪。其实系统中实际是还有内存的,但是因为小 块的内存的地址不连续,导致无法分配成功,所以我们需要一个优良的内存分配算法来避 免这种情况的出现。

      不同的嵌入式系统具有不同的内存配置和时间要求。所以单一的内存分配算法只可能 适合部分应用程序。因此,FreeRTOS 将内存分配作为可移植层面(相对于基本的内核代码 部分而言),FreeRTOS 有针对性的提供了不同的内存分配管理算法,这使得应用于不同场 景的设备可以选择适合自身内存算法。

      FreeRTOS 对内存管理做了很多事情,FreeRTOS 的 V9.0.0 版本为我们提供了 5 种内存 管理算法,分别是 heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c,源文件存放于 FreeRTOS\Source\portable\MemMang 路径下,在使用的时候选择其中一个添加到我们的工 程中去即可。

      FreeRTOS 的内存管理模块通过对内存的申请、释放操作,来管理用户和系统对内存的 使用,使内存的利用率和使用效率达到最优,同时最大限度地解决系统可能产生的内存碎片问题。

二、内存管理的应用场景

      首先,在使用内存分配前,必须明白自己在做什么,这样做与其他的方法有什么不同, 特别是会产生哪些负面影响,在自己的产品面前,应当选择哪种分配策略。

      内存管理的主要工作是动态划分并管理用户分配好的内存区间,主要是在用户需要使 用大小不等的内存块的场景中使用,当用户需要分配内存时,可以通过操作系统的内存申 请函数索取指定大小内存块,一旦使用完毕,通过动态内存释放函数归还所占用内存,使 之可以重复使用(heap_1.c 的内存管理除外)。

      例如我们需要定义一个 float 型数组:floatArr[];

      但是,在使用数组的时候,总有一个问题困扰着我们:数组应该有多大?在很多的情 况下,你并不能确定要使用多大的数组,可能为了避免发生错误你就需要把数组定义得足 够大。即使你知道想利用的空间大小,但是如果因为某种特殊原因空间利用的大小有增加 或者减少,你又必须重新去修改程序,扩大数组的存储范围。这种分配固定大小的内存分 配方法称之为静态内存分配。这种内存分配的方法存在比较严重的缺陷,在大多数情况下会浪费大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错 误,甚至导致严重后果。

      我们用动态内存分配就可以解决上面的问题。所谓动态内存分配就是指在程序执行的 过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内 存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的 大小就是程序要求的大小。

三、heap_4.c

     我们这里只讲heap_4.c 因为这个最常用 。 内 存 分 配 时 需 要 的 总 的 堆 空 间 由 文 件 FreeRTOSConfig.h 中 的 宏 configTOTAL_HEAP_SIZE 配置,单位为字。通过调用函数 xPortGetFreeHeapSize() 我们可 以知道还剩下多少内存没有使用,但是并不包括内存碎片。这样一来我们可以实时的调整 和优化 configTOTAL_HEAP_SIZE 的大小。

      heap_4.c 方案的空闲内存块也是以单链表的形式连接起来的,BlockLink_t 类型的局部 静态变量 xStart 表示链表头,但 heap_4.c 内存管理方案的链表尾部则保存在内存堆空间最 后位置,并使用 BlockLink_t 指针类型局部静态变量 pxEnd 指向这个区域(而 heap_2.c 内 存管理方案则使用 BlockLink_t 类型的静态变量 xEnd 表示链表尾)

      heap_4.c 内存管理方案的空闲块链表不是以内存块大小进行排序的,而是以内存块起 始地址大小排序,内存地址小的在前,地址大的在后,因为 heap_4.c 方案还有一个内存合 并算法,在释放内存的时候,假如相邻的两个空闲内存块在地址上是连续的,那么就可以 合并为一个内存块,这也是为了适应合并算法而作的改变。

      heap_4.c 方案具有以下特点:

     1、可用于重复删除任务、队列、信号量、互斥量等的应用程序

     2、可用于分配和释放随机字节内存的应用程序,但并不像 heap2.c 那样产生严重的内 存碎片。

     3、具有不确定性,但是效率比标准 C 库中的 malloc 函数高得多。

1.内存申请函数 pvPortMalloc()

      heap_4.c 方案的内存申请函数与 heap_2.c 方案的内存申请函数大同小异,同样是从链 表头 xStart 开始遍历查找合适的内存块,如果某个空闲内存块的大小能容得下用户要申请 的内存,则将这块内存取出用户需要内存空间大小的部分返回给用户,剩下的内存块组成 一个新的空闲块,按照空闲内存块起始地址大小顺序插入到空闲块链表中,内存地址小的 在前,内存地址大的在后。在插入到空闲内存块链表的过程中,系统还会执行合并算法将 地址相邻的内存块进行合并:判断这个空闲内存块是相邻的空闲内存块合并成一个大内存 块,如果可以则合并,合并算法是 heap_4.c 内存管理方案和 heap_2.c 内存管理方案最大的 不同之处,这样一来,会导致的内存碎片就会大大减少,内存管理方案适用性就很强,能 一样随机申请和释放内存的应用中,灵活性得到大大的提高,heap_4.c 内存初始化完成示意图具体见图1。

heap_4.c 的内存 申请源码

void *pvPortMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
void *pvReturn = NULL;

	vTaskSuspendAll();
	{
		/* If this is the first call to malloc then the heap will require
		initialisation to setup the list of free blocks. */
		if( pxEnd == NULL )
		{
			prvHeapInit();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		/* Check the requested block size is not so large that the top bit is
		set.  The top bit of the block size member of the BlockLink_t structure
		is used to determine who owns the block - the application or the
		kernel, so it must be free. */
		if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
		{
			/* The wanted size is increased so it can contain a BlockLink_t
			structure in addition to the requested amount of bytes. */
			if( xWantedSize > 0 )
			{
				xWantedSize += xHeapStructSize;

				/* Ensure that blocks are always aligned to the required number
				of bytes. */
				if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
				{
					/* Byte alignment required. */
					xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
					configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 );
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}

			if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
			{
				/* Traverse the list from the start	(lowest address) block until
				one	of adequate size is found. */
				pxPreviousBlock = &xStart;
				pxBlock = xStart.pxNextFreeBlock;
				while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
				{
					pxPreviousBlock = pxBlock;
					pxBlock = pxBlock->pxNextFreeBlock;
				}

				/* If the end marker was reached then a block of adequate size
				was	not found. */
				if( pxBlock != pxEnd )
				{
					/* Return the memory space pointed to - jumping over the
					BlockLink_t structure at its start. */
					pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );

					/* This block is being returned for use so must be taken out
					of the list of free blocks. */
					pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;

					/* If the block is larger than required it can be split into
					two. */
					if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
					{
						/* This block is to be split into two.  Create a new
						block following the number of bytes requested. The void
						cast is used to prevent byte alignment warnings from the
						compiler. */
						pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
						configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );

						/* Calculate the sizes of two blocks split from the
						single block. */
						pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
						pxBlock->xBlockSize = xWantedSize;

						/* Insert the new block into the list of free blocks. */
						prvInsertBlockIntoFreeList( pxNewBlockLink );
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}

					xFreeBytesRemaining -= pxBlock->xBlockSize;

					if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
					{
						xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}

					/* The block is being returned - it is allocated and owned
					by the application and has no "next" block. */
					pxBlock->xBlockSize |= xBlockAllocatedBit;
					pxBlock->pxNextFreeBlock = NULL;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		traceMALLOC( pvReturn, xWantedSize );
	}
	( void ) xTaskResumeAll();

	#if( configUSE_MALLOC_FAILED_HOOK == 1 )
	{
		if( pvReturn == NULL )
		{
			extern void vApplicationMallocFailedHook( void );
			vApplicationMallocFailedHook();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif

	configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
	return pvReturn;
}

FreeRTOS内存管理 基于STM32,FreeRTOS,笔记,STM32,stm32,系统架构,arm

图1 内存初始化完成示意图

2.内存释放函数 vPortFree()

    heap_4.c 内存管理方案的内存释放函数 vPortFree()也比较简单,根据传入要释放的内 存块地址,偏移之后找到链表节点,然后将这个内存块插入到空闲内存块链表中,在内存 块插入过程中会执行合并算法,这个我们已经在内存申请中讲过了(而且合并算法多用于 释放内存中)。最后是将这个内存块标志为“空闲”(内存块节点的 xBlockSize 成员变量 最高位清 0)、再更新未分配的内存堆大小即可,下面来看看 vPortFree()的源码实现过程。

void vPortFree( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;

	if( pv != NULL )
	{
		/* The memory being freed will have an BlockLink_t structure immediately
		before it. */
		puc -= xHeapStructSize;

		/* This casting is to keep the compiler from issuing warnings. */
		pxLink = ( void * ) puc;

		/* Check the block is actually allocated. */
		configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
		configASSERT( pxLink->pxNextFreeBlock == NULL );

		if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
		{
			if( pxLink->pxNextFreeBlock == NULL )
			{
				/* The block is being returned to the heap - it is no longer
				allocated. */
				pxLink->xBlockSize &= ~xBlockAllocatedBit;

				vTaskSuspendAll();
				{
					/* Add this block to the list of free blocks. */
					xFreeBytesRemaining += pxLink->xBlockSize;
					traceFREE( pv, pxLink->xBlockSize );
					prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
				}
				( void ) xTaskResumeAll();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
}

      调用 prvInsertBlockIntoFreeList()函数将释放的内存块添加到空闲 内存块链表中,在这过程中,如果内存块可以合并就会进行内存块合并,否则就单纯插入 空闲内存块链表(按内存地址排序)。 按照内存释放的过程,当我们释放一个内存时,如果与它相邻的内存块都不是空闲的, 那么该内存块并不会合并,只会被添加到空闲内存块链表中,其过程示意图具体见图 2。而如果某个时间段释放了另一个内存块,发现该内存块前面有一个空闲内存块与它 在地址上是连续的,那么这两个内存块会合并成一个大的内存块,并插入空闲内存块链表 中,其过程示意图具体见图 3,

FreeRTOS内存管理 基于STM32,FreeRTOS,笔记,STM32,stm32,系统架构,arm

图二释放一个内存块(无法合并)

FreeRTOS内存管理 基于STM32,FreeRTOS,笔记,STM32,stm32,系统架构,arm

图3 释放一个内存块(可以合并)

 四、内存管理的实验

      内存管理实验使用 heap_4.c 方案进行内存管理测试,创建了两个任务,分别是 LED 任 务与内存管理测试任务,内存管理测试任务通过检测按键是否按下来申请内存或释放内存, 当申请内存成功就像该内存写入一些数据,如当前系统的时间等信息,并且通过串口输出 相关信息;LED 任务是将 LED 翻转,表示系统处于运行状态。在不需要再使用内存时,注 意要及时释放该段内存,避免内存泄露。

/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_key.h"
/**************************** 任务句柄 ********************************/
/* 
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为NULL。
 */
static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
static TaskHandle_t LED_Task_Handle = NULL;/* LED_Task任务句柄 */
static TaskHandle_t Test_Task_Handle = NULL;/* Test_Task任务句柄 */



/******************************* 全局变量声明 ************************************/
/*
 * 当我们在写应用程序的时候,可能需要用到一些全局变量。
 */
uint8_t *Test_Ptr = NULL;


/*
*************************************************************************
*                             函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */

static void LED_Task(void* pvParameters);/* LED_Task任务实现 */
static void Test_Task(void* pvParameters);/* Test_Task任务实现 */

static void BSP_Init(void);/* 用于初始化板载相关资源 */

/*****************************************************************
  * @brief  主函数
  * @param  无
  * @retval 无
  * @note   第一步:开发板硬件初始化 
            第二步:创建APP应用任务
            第三步:启动FreeRTOS,开始多任务调度
  ****************************************************************/
int main(void)
{	
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  /* 开发板硬件初始化 */
  BSP_Init();
	printf("这是一个FreeRTOS内存管理实验\n");
  printf("按下KEY1申请内存,按下KEY2释放内存\n");
   /* 创建AppTaskCreate任务 */
  xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate,  /* 任务入口函数 */
                        (const char*    )"AppTaskCreate",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )1, /* 任务的优先级 */
                        (TaskHandle_t*  )&AppTaskCreate_Handle);/* 任务控制块指针 */ 
  /* 启动任务调度 */           
  if(pdPASS == xReturn)
    vTaskStartScheduler();   /* 启动任务,开启调度 */
  else
    return -1;  
  
  while(1);   /* 正常不会执行到这里 */    
}


/***********************************************************************
  * @ 函数名  : AppTaskCreate
  * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  * @ 参数    : 无  
  * @ 返回值  : 无
  **********************************************************************/
static void AppTaskCreate(void)
{
  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  
  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)
    printf("创建LED_Task任务成功\n");
  
  /* 创建Test_Task任务 */
  xReturn = xTaskCreate((TaskFunction_t )Test_Task,  /* 任务入口函数 */
                        (const char*    )"Test_Task",/* 任务名字 */
                        (uint16_t       )512,  /* 任务栈大小 */
                        (void*          )NULL,/* 任务入口函数参数 */
                        (UBaseType_t    )3, /* 任务的优先级 */
                        (TaskHandle_t*  )&Test_Task_Handle);/* 任务控制块指针 */ 
  if(pdPASS == xReturn)
    printf("创建Test_Task任务成功\n\n");
  
  vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  
  taskEXIT_CRITICAL();            //退出临界区
}



/**********************************************************************
  * @ 函数名  : LED_Task
  * @ 功能说明: LED_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void LED_Task(void* parameter)
{	
  while (1)
  {
    LED1_TOGGLE;
    vTaskDelay(1000);/* 延时1000个tick */
  }
}

/**********************************************************************
  * @ 函数名  : Test_Task
  * @ 功能说明: Test_Task任务主体
  * @ 参数    :   
  * @ 返回值  : 无
  ********************************************************************/
static void Test_Task(void* parameter)
{	 
  uint32_t g_memsize;
  while (1)
  {
    if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
    {
      /* KEY1 被按下 */
      if(NULL == Test_Ptr)
      {
                  
        /* 获取当前内存大小 */
        g_memsize = xPortGetFreeHeapSize();
        printf("系统当前内存大小为 %d 字节,开始申请内存\n",g_memsize);
        Test_Ptr = pvPortMalloc(1024);
        if(NULL != Test_Ptr)
        {
          printf("内存申请成功\n");
          printf("申请到的内存地址为%#x\n",(int)Test_Ptr);

          /* 获取当前内剩余存大小 */
          g_memsize = xPortGetFreeHeapSize();
          printf("系统当前内存剩余存大小为 %d 字节\n",g_memsize);
                  
          //向Test_Ptr中写入当数据:当前系统时间
          sprintf((char*)Test_Ptr,"当前系统TickCount = %d \n",xTaskGetTickCount());
          printf("写入的数据是 %s \n",(char*)Test_Ptr);
        }
      }
      else
      {
        printf("请先按下KEY2释放内存再申请\n");
      }
    } 
    if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
    {
      /* KEY2 被按下 */
      if(NULL != Test_Ptr)
      {
        printf("释放内存\n");
        vPortFree(Test_Ptr);	//释放内存
        Test_Ptr=NULL;
        /* 获取当前内剩余存大小 */
        g_memsize = xPortGetFreeHeapSize();
        printf("系统当前内存大小为 %d 字节,内存释放完成\n",g_memsize);
      }
      else
      {
        printf("请先按下KEY1申请内存再释放\n");
      }
    }
    vTaskDelay(20);/* 延时20个tick */
  }
}

/***********************************************************************
  * @ 函数名  : BSP_Init
  * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  * @ 参数    :   
  * @ 返回值  : 无
  *********************************************************************/
static void BSP_Init(void)
{
	/*
	 * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
	 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
	 * 都统一用这个优先级分组,千万不要再分组,切忌。
	 */
	NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
	
	/* LED 初始化 */
	LED_GPIO_Config();

	/* 串口初始化	*/
	USART_Config();
  
  /* 按键初始化	*/
  Key_GPIO_Config();

}

/********************************END OF FILE****************************/

五、内存管理的实验现象

程序编译好,用 USB 线连接电脑和开发板的 USB 接口(对应丝印为 USB 转串口), 用 DAP 仿真器把配套程序下载到 STM32 开发板,在电脑上打开串口调试助手,然后复位开发板,我 们按下 KEY1 申请内存,然后按下 KEY2 释放内存,可以在调试助手中看到串口打印信息 与运行结果,开发板的 LED 也在闪烁。

FreeRTOS内存管理 基于STM32,FreeRTOS,笔记,STM32,stm32,系统架构,arm

 文章来源地址https://www.toymoban.com/news/detail-627718.html

到了这里,关于FreeRTOS内存管理 基于STM32的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FreeRTOS小项目实战------基于FreeRTOS和stm32的门禁系统

    目录 收获 系统总体框架 程序框架 具体程序实现 工程文件网盘链接 收获 学习 freertos的移植与裁剪 ,对任务间通信的认识更加深刻,加深了实时操作系统的理解,学习了as608指纹模块,rc522刷卡模块等模块的简单使用。 系统总体框架 该系统采用STM32F407ZGT6为主控芯片,在Fre

    2024年02月02日
    浏览(45)
  • STM32操作系统FreeRTOS学习——基于hal库

    目录 一、基础概念 1、FreeRTOS 2、单片机编程的系统概念 a、裸机系统,包括轮询系统(不包括中断)和前后台系统(中断为前台,轮询为后台) b、多任务系统 3、FreeRTOS编程风格 a、数据类型 b、变量名的定义 c、函数名 d、宏定义 二、Cubemx创建工程 1、创建任务 2、创建队列

    2024年02月10日
    浏览(39)
  • STM32 FreeRTOS 内存问题

    2024年02月09日
    浏览(39)
  • STM32 hal库使用笔记之FreeRTOS—任务创建、删除,任务挂起、恢复,任务中断管理

    一、简介 1.FreeRTOS简介     RTOS全称为:Real Time OS,就是实时操作系统,强调的是:实时性。而Free显而易见体现的是其免费性。总的来说这是一个免费的嵌入式实时操作系统。     其特点是:免费开源、可剪裁(独立性强,适应范围广)、简单、优先级/任务不限(但是受到不

    2024年02月04日
    浏览(49)
  • stm32 freeRTOS lwip TCP快速发送,内存泄露问题

    现象1: 发送缓慢,tcp_write之后要等200多ms才能过发送出去,而且粘包严重。 解决办法 tcp_write之后,立马调用tcp_output ,tcp就会立马发送。 现象2: 持续快速发送和接受TCP数据出现断言 而且出现TCP断连,死机情况。 其实就是一句话 主程序和以太网中断程序中对PCB-unsent 之类的

    2024年02月08日
    浏览(34)
  • FreeRTOS事件组 基于STM32

    文章对事件组的,应用场景,运作机制,以及事件的创建,删除,等待,置位,同步等操作 文章目录 概述 一、事件标志组简介 1、事件位(事件标志) 2、事件组 3、事件标志组和事件位的数据类型 二、事件的应用场景 三、事件运作机制 四、事件控制块  五、事件组函数 1.事

    2024年02月11日
    浏览(35)
  • 基于STM32的homeassistant(采用FreeRTOS操作系统)【第一章:设备配网、连接服务器、断网重连、断服务器重接】

      主控 STM32F103C8T6 WIFI模块 ESP01S 开发语言 C 开发编译器· KEIL 组网方式 WIFI 服务器协议 MQTT ESP01S的AT指令配网以及服务器连接 STM32与ESP01S的usart协议通信 断网重连以及断服务器重连 STM32向服务器端口发送对应指令         ESP01S的配网方式为AT指令集,通过识别对对应的AT指令

    2024年02月12日
    浏览(50)
  • 基于STM32CubeMX创建FreeRTOS—以STM32F429为例

    目录 1. 实验任务 2. 使用STM32CubeMX创建基础工程 2.1 使用STM32CubeMX创建项目 2.2 创建新项目 2.3 时钟设置 2.4 时钟配置树 2.5 修改时钟基准,打开串行调试 2.6 配置串口 2.7 配置状态指示灯 2.8 FreeRTOS配置 2.9 配置工程输出项 3. 代码编辑 3.1 printf重映射 3.1.1 使用ARMCC 5编译器时的print

    2024年01月22日
    浏览(42)
  • FreeRTOS软件定时器 基于STM32

    文章目录 一·、软件定时器的基本概念 二、软件定时器应用场景 三、软件定时器的精度 四、软件定时器的运作机制 五、软件定时器函数接口讲解 1.软件定时器创建函数 xTimerCreate() 2.软件定时器启动函数 xTimerStart()  3.软件定时器停止函数  xTimerStop()  4.软件定时器任务 5.软件

    2024年02月11日
    浏览(44)
  • 基于STM32的homeassistant(采用FreeRTOS操作系统)【第一、二章优化拓展:Wifi、服务器连接验证以及UASRT串口区分】

      主控 STM32F103C8T6 WIFI模块 ESP01S 开发语言 C 开发编译器· KEIL 组网方式 WIFI 服务器协议 MQTT STM32 ESP01S 3.3V 3.3V GND GND GPIO2  (USRAT2-TX) RX GPIO3 (USART3-RX) TX 对ESP01S的AT指令的反馈指令进行验证解析 对ESP的USART2通信串口进行printf端口重映射 对调试用的USART1通信串口编写USART_printf函数并

    2024年02月17日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包