历史上的今天
返回首页

历史上的今天

今天是:2024年08月26日(星期一)

正在发生

2019年08月26日 | 【STM32H7教程】第27章 STM32H7的TCM,SRAM等动态内存分配实现

2019-08-26 来源:eefocus

27.1 初学者重要提示


学习本章节前,务必优先学习第25章,了解TCM,SRAM等五块内存区的基础知识,比较重要。


将RTX5系统的动态内存管理整理了出来,可以同时管理多个分区。如果其它RTOS中使用,记得做互斥保护或者加个调度锁均可。


支持动态内存使用情况统计。


27.2 动态内存管理移植


移植比较简单,仅需添加两个文件到工程即可。


27.2.1 MDK版的移植


第1步,添加如下两个文件到MDK中

注,以本章配套例子为例,这两个文件的路径Usermalloc。

第2步,添加路径。

第3步,添加头文件。


如果哪个源文件要用到动态内存,包含rtx_lib.h即可,本章配套例子是直接将其放在了bsp.h文件里面,哪个源文件要用到动态内存,直接包含bsp.h头文件即可。


通过这简单的三步就完成了MDK的移植。


27.2.2 IAR版的移植

  第1步,添加如下两个文件到IAR中

注,以本章配套例子为例,这两个文件的路径Usermalloc。


  第2步,添加路径。


  第3步,添加头文件。

如果哪个源文件要用到动态内存,包含rtx_lib.h即可,本章配套例子是直接将其放在了bsp.h文件里面,哪个源文件要用到动态内存,直接包含bsp.h头文件即可。


通过这简单的三步就完成了IAR的移植。


27.3 动态内存的使用方法

下面分别以MDK和IAR为例进行说明:


27.3.1 MDK上的动态内存用法

  定义动态内存区

比如当前的主RAM用的DTCM,我们就可以直接定义一块大的数组作为动态内存空间:


/* DTCM, 64KB */

/* 用于获取当前使用的空间大小 */

mem_head_t *DTCMUsed; 

/* 定义为64位变量,首地址是8字节对齐 */             

uint64_t AppMallocDTCM[64*1024/8]; 

如果要使用AXI SRAM作为动态内存空间,可以使用__attribute__((at( )))指定地址。


/* D1域, AXI SRAM, 512KB */

/* 用于获取当前使用的空间大小 */

mem_head_t *AXISRAMUsed;  

/* 定义为64位变量,首地址是8字节对齐 */ 

uint64_t AppMallocAXISRAM[512*1024/8]__attribute__((at(0x24000000))); 

  初始化动态内存区

调用动态内存管理提供的函数osRtxMemoryInit即可做初始化:


osRtxMemoryInit(AppMallocDTCM,    sizeof(AppMallocDTCM));

osRtxMemoryInit(AppMallocAXISRAM, sizeof(AppMallocAXISRAM));

  申请动态内存

通过函数void *osRtxMemoryAlloc (void *mem, uint32_t size, uint32_t type)做动态内存申请。


第1个参数填写内存区首地址,比如申请的AppMallocDTCM,就填AppMallocDTCM即可。


第2个参数填写申请的字节大小,单位字节。


第3个参数固定填0即可。


返回值是所申请缓冲区的首地址,如果没有空间可用,将返回NULL,这点要特别注意!


举个例子:


uint32_t *DTCM_Addres0, *AXISRAM_Addres0;

 

/* 从DTCM申请280字节空间,使用指针变量DTCM_Addres0操作这些空间时不要超过280字节大小 */

DTCM_Addres0 = osRtxMemoryAlloc(AppMallocDTCM, 280, 0);

DTCMUsed = MemHeadPtr(AppMallocDTCM);

printf("DTCM总大小 = %d字节,申请大小 = 0280字节,当前共使用大小 = %d字节rn", 

                             DTCMUsed->size, DTCMUsed->used);

 

/* 从AXI SRAM 申请160字节空间,使用指针变量AXISRAM_Addres0操作这些空间时不要超过160字节大小 */

AXISRAM_Addres0 = osRtxMemoryAlloc(AppMallocAXISRAM, 160, 0);

AXISRAMUsed = MemHeadPtr(AppMallocAXISRAM);

printf("AXI SRAM总大小 = %d字节,申请大小 = 0162字节,当前共使用大小 = %d字节rn", 

                            AXISRAMUsed->size, AXISRAMUsed->used);

申请了空间后,就可以直接使用了。另外注意红色字体部分,通过DTCMUsed->used和AXISRAMUsed->used可以获取当前使用的空间大小。


  释放动态内存

通过函数uint32_t osRtxMemoryFree (void *mem, void *block)做动态内存释放。


第1个参数填写内存区首地址,比如释放的AppMallocDTCM,就填AppMallocDTCM即可。


第2个参数填写申请内存时所获取的内存区首地址,这里用于释放。


返回值,返回1表示成功,返回0表示失败。


举个例子:


/* 释放从DTCM申请的280字节空间 */

osRtxMemoryFree(AppMallocDTCM, DTCM_Addres0);

DTCMUsed = MemHeadPtr(AppMallocDTCM);

printf("释放DTCM动态内存区申请的0280字节,当前共使用大小 = %d字节rn", DTCMUsed->used);

 

/* 释放从AXI SRAM申请的160字节空间 */

osRtxMemoryFree(AppMallocAXISRAM, AXISRAM_Addres0);

AXISRAMUsed = MemHeadPtr(AppMallocAXISRAM);

printf("释放AXI SRAM动态内存区申请的0160字节,当前共使用大小 = %d字节rn", AXISRAMUsed->used);

27.3.2 IAR上的动态内存用法

注:IAR使用这个动态内存管理,仅在定义时跟MDK略有不同,其它地方是一样的。


  定义动态内存区

比如当前的主RAM用的DTCM,我们就可以直接定义一块大的数组作为动态内存空间:


/* DTCM, 64KB */

/* 用于获取当前使用的空间大小 */

mem_head_t *DTCMUsed; 

/* 定义为64位变量,首地址是8字节对齐 */             

uint64_t AppMallocDTCM[64*1024/8]; 

如果要使用AXI SRAM作为动态内存空间,可以使用__attribute__((at( )))指定地址。


/* D1域, AXI SRAM, 512KB */

/* 用于获取当前使用的空间大小 */

mem_head_t *AXISRAMUsed;  

/* 指定下面数组的地址为0x24000000 */ 

#pragma location = 0x24000000

uint64_t AppMallocAXISRAM[512*1024/8];

  初始化动态内存区

调用动态内存管理提供的函数osRtxMemoryInit即可做初始化:


osRtxMemoryInit(AppMallocDTCM,    sizeof(AppMallocDTCM));

osRtxMemoryInit(AppMallocAXISRAM, sizeof(AppMallocAXISRAM));

  申请动态内存

通过函数void *osRtxMemoryAlloc (void *mem, uint32_t size, uint32_t type)做动态内存申请。


第1个参数填写内存区首地址,比如申请的AppMallocDTCM,就填AppMallocDTCM即可。


第2个参数填写申请的字节大小,单位字节。


第3个参数固定填0即可。


返回值是所申请缓冲区的首地址,如果没有空间可用,将返回NULL,这点要特别注意!


举个例子:


uint32_t *DTCM_Addres0, *AXISRAM_Addres0;

 

/* 从DTCM申请280字节空间,使用指针变量DTCM_Addres0操作这些空间时不要超过280字节大小 */

DTCM_Addres0 = osRtxMemoryAlloc(AppMallocDTCM, 280, 0);

DTCMUsed = MemHeadPtr(AppMallocDTCM);

printf("DTCM总大小 = %d字节,申请大小 = 0280字节,当前共使用大小 = %d字节rn", 

                             DTCMUsed->size, DTCMUsed->used);

 

/* 从AXI SRAM 申请160字节空间,使用指针变量AXISRAM_Addres0操作这些空间时不要超过160字节大小 */

AXISRAM_Addres0 = osRtxMemoryAlloc(AppMallocAXISRAM, 160, 0);

AXISRAMUsed = MemHeadPtr(AppMallocAXISRAM);

printf("AXI SRAM总大小 = %d字节,申请大小 = 0162字节,当前共使用大小 = %d字节rn", 

                            AXISRAMUsed->size, AXISRAMUsed->used);

申请了空间后,就可以直接使用了。另外注意红色字体部分,通过DTCMUsed->used和AXISRAMUsed->used可以获取当前使用的空间大小。


  释放动态内存

通过函数uint32_t osRtxMemoryFree (void *mem, void *block)做动态内存释放。


第1个参数填写内存区首地址,比如释放的AppMallocDTCM,就填AppMallocDTCM即可。


第2个参数填写申请内存时所获取的内存区首地址,这里用于释放。


返回值,返回1表示成功,返回0表示失败。


举个例子:


/* 释放从DTCM申请的280字节空间 */

osRtxMemoryFree(AppMallocDTCM, DTCM_Addres0);

DTCMUsed = MemHeadPtr(AppMallocDTCM);

printf("释放DTCM动态内存区申请的0280字节,当前共使用大小 = %d字节rn", DTCMUsed->used);

 

/* 释放从AXI SRAM申请的160字节空间 */

osRtxMemoryFree(AppMallocAXISRAM, AXISRAM_Addres0);

AXISRAMUsed = MemHeadPtr(AppMallocAXISRAM);

printf("释放AXI SRAM动态内存区申请的0160字节,当前共使用大小 = %d字节rn", AXISRAMUsed->used)

27.4 实验例程说明(MDK)

配套例子:


V7-006_TCM,SRAM等五块内存的动态内存分配实现


实验目的:


学习TCM,SRAM等五块内存的动态内存分配实现。

实验内容:


启动自动重装软件定时器0,每100ms翻转一次LED2。

实验操作:


K1键按下,从DTCM依次申请280字节,64字节和6111字节。

K1键松开,释放从DTCM申请的空间。

K2键按下,从AXI SRAM依次申请160字节,32字节和2333字节。

K2键松开,释放从AXI SRAM申请的空间。

K3键按下,从D2域SRAM依次申请200字节,96字节和4111字节。

K3键松开,释放从D2域SRAM申请的空间。

摇杆OK键按下,从D3域SRAM依次申请300字节,128字节和5111字节。

摇杆OK键松开,释放从D3域SRAM申请的空间。

上电后串口打印的信息:


波特率 115200,数据位 8,奇偶校验位无,停止位 1

程序设计:


  系统栈大小分配:


RAM空间用的DTCM:


  硬件外设初始化


硬件外设的初始化是在 bsp.c 文件实现:


/*

*********************************************************************************************************

* 函 数 名: bsp_Init

* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次

* 形    参:无

* 返 回 值: 无

*********************************************************************************************************

*/

void bsp_Init(void)

{

    /* 配置MPU */

MPU_Config();

/* 使能L1 Cache */

CPU_CACHE_Enable();

 

/* 

       STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:

   - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。

   - 设置NVIV优先级分组为4。

*/

HAL_Init();

 

/* 

       配置系统时钟到400MHz

       - 切换使用HSE。

       - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。

    */

SystemClock_Config();

 

/* 

   Event Recorder:

   - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。

   - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章

*/

#if Enable_EventRecorder == 1  

/* 初始化EventRecorder并开启 */

EventRecorderInitialize(EventRecordAll, 1U);

EventRecorderStart();

#endif

bsp_InitKey();    /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */

bsp_InitTimer();  /* 初始化滴答定时器 */

bsp_InitUart(); /* 初始化串口 */

bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */

bsp_InitLed();    /* 初始化LED */

}

  MPU配置和Cache配置:

数据Cache和指令Cache都开启。


AXI SRAM的MPU属性:


Write back, Read allocate,Write allocate。


FMC的扩展IO的MPU属性:


必须Device或者Strongly Ordered。


D2 SRAM1,SRAM2和SRAM3的MPU属性:


Write through, read allocate,no write allocate。


D3 SRAM4的MPU属性:


Write through, read allocate,no write allocate。


/*

*********************************************************************************************************

* 函 数 名: MPU_Config

* 功能说明: 配置MPU

* 形    参: 无

* 返 回 值: 无

*********************************************************************************************************

*/

static void MPU_Config( void )

{

MPU_Region_InitTypeDef MPU_InitStruct;

 

/* 禁止 MPU */

HAL_MPU_Disable();

 

/* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */

MPU_InitStruct.Enable           = MPU_REGION_ENABLE;

MPU_InitStruct.BaseAddress      = 0x24000000;

MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;

MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;

MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;

MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;

MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;

MPU_InitStruct.Number           = MPU_REGION_NUMBER0;

MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;

MPU_InitStruct.SubRegionDisable = 0x00;

MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

 

HAL_MPU_ConfigRegion(&MPU_InitStruct);

/* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */

MPU_InitStruct.Enable           = MPU_REGION_ENABLE;

MPU_InitStruct.BaseAddress      = 0x60000000;

MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;

MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;

MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;

MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;

MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;

MPU_InitStruct.Number           = MPU_REGION_NUMBER1;

MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;

MPU_InitStruct.SubRegionDisable = 0x00;

MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

HAL_MPU_ConfigRegion(&MPU_InitStruct);

/* 配置SRAM1的属性为Write through, read allocate,no write allocate */

    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;

    MPU_InitStruct.BaseAddress      = 0x30000000;

    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_128KB;

    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;

    MPU_InitStruct.IsBufferable     = MPU_ACCESS_NOT_BUFFERABLE;

推荐阅读

史海拾趣

Arco Electronics公司的发展小趣事

随着公司规模的扩大,Arco Electronics开始实施全球化战略。公司先后在多个国家和地区设立了研发中心和生产基地,以便更好地服务当地市场。通过全球化的布局,Arco不仅降低了生产成本,还提高了产品的竞争力。同时,公司还积极与当地的合作伙伴建立战略合作关系,共同开拓市场,实现了互利共赢。

Advanced Illumination Inc公司的发展小趣事

随着技术的不断进步,Ai公司在LED照明光源领域取得了重大突破。公司成功研发出了一系列高效、稳定、可靠的LED光源产品,这些产品不仅性能卓越,而且设计新颖,满足了市场对于高质量照明光源的迫切需求。同时,公司还不断创新,推出了多款具有自主知识产权的新型LED光源,进一步巩固了公司在行业内的领先地位。

Agere System(LSI Logic)公司的发展小趣事

在技术创新的基础上,Alpha 3 Manufacturing公司开始积极拓展市场。公司参加了多个国际电子展会,与全球客户建立了广泛的联系。同时,公司还加强了对市场的调研和分析,针对不同地区和客户的需求,推出了定制化的产品和服务。这些举措不仅提高了公司的市场份额,还进一步提升了公司的品牌知名度和影响力。

Datalogic公司的发展小趣事

2005年,Datalogic收购了美国PSC公司,这一收购对公司在电子行业中的发展具有重要意义。PSC公司是一家在自动识别领域有着丰富经验和技术积累的企业,其产品线与Datalogic高度互补。通过整合PSC公司的技术和资源,Datalogic进一步巩固了其在电子行业中的领先地位。

ALCOA公司的发展小趣事

近年来,随着环保意识的提高,ALCOA积极推动可持续发展和环保倡议。公司致力于减少生产过程中的能源消耗和废弃物排放,同时也在研发更环保的铝材和生产工艺。这些努力不仅提升了公司的社会形象,也为其在电子行业中的长期发展奠定了坚实的基础。

以上五个故事,只是ALCOA公司发展历程中的一部分。然而,这些故事足以展现出这家公司在电子行业中的卓越成就和持续创新的精神。

Defense Supply Center Columbus公司的发展小趣事

在电子行业,产品质量是企业生存和发展的关键。Defense Supply Center Columbus公司始终将质量管理放在首位,建立起一套完善的质量管理体系。公司严格按照国际标准进行生产和检测,确保每一件产品都符合质量要求。同时,公司还注重员工的培训和教育,提高员工的质量意识和操作技能。这些措施的实施,使得公司的产品质量得到了显著提升,赢得了客户的广泛赞誉。

问答坊 | AI 解惑

μCOS-II 在51单片机

μCOS-II是个不错的操作系统,体积小,可装在8位,16位,32位CPU上…

查看全部问答>

PCB布线的几点经验

1、输入端与输出端的边线应避免相邻平行, 以免产生反射干扰。必要时应加地线隔离;两相邻层的布线要互相垂直,平行容易产生寄生耦合。 2、地线>电源线>信号线,通常信号线宽为:8mil~12mil;电源线为50mil~100mil。对数字电路 ...…

查看全部问答>

公交车字符串移动显示

   公交车上那个长字符串移动,并且随时切换,且都是点阵做起来的。不知用的是什么处理器?    然后,移动怎么个原理? 感觉自己实现起来很难。…

查看全部问答>

EVC中是不是不支持settimer的SLIDER_TIMER参数??

EVC中是不是不支持settimer的SLIDER_TIMER参数??         mSliderTimer = SetTimer(SLIDER_TIMER, 100, NULL); 报错是:error C2065: \'SLIDER_TIMER\' : undeclared identifier…

查看全部问答>

mobile c++有人做过电话功能吗?如何拒绝来电?

mobile c++有人做过电话功能吗?如何拒绝来电?…

查看全部问答>

努力了几天,STM32终于快要输出SVPWM了

晚上回家测试波形是否正确, 软仿好向没问题了!整个算法一次耗时,4.125us,在10KHz的PWM时占用CPU资源4.125%,硬件是可能更长一点儿,晚上在报告.执行的算法:   模拟的角度发生器,   电压变化自补尝 &nbs ...…

查看全部问答>

ADI实验室电路DIY项目指南

报名参与:『ADI实验室电路DIY大赛』正式启动!https://bbs.eeworld.com.cn/thread-293726-1-1.html ADI实验室电路品种繁多,涉及面很广,如何选择合适的DIY项目可能是件容易令人困扰的事,但深究起来,其实可玩性是很大的,从今天开始,我将陆续 ...…

查看全部问答>

大家帮看看为什么DeviceIoControl访问OID_802_11_BSSID_LIST老是失败

如题,下面一段程序中,DeviceIoControl访问OID_802_11_BSSID_LIST老是失败(见下面红色),也就是说bResult一直等于0;为什么?请教牛人!!!可能的问题出现在哪?为什么第二个DeviceIoControl失败?#include \"stdafx.h\"#include <windows.h ...…

查看全部问答>