RT-Thread (1) 添加外部内存到内存管理





系列文章目录

RT-Thread (1) 添加外部内存到内存管理

RT-Thread (2) RTT SPI设备驱动流程 || LWIP + ENC28J60

RT-Thread (3) 为RTT增加SP485驱动||RTT UART设备







0 前言

以前接触过一段时间的RTT,但是当时是直接使用了RTT的一个BSP,所以并未深入研究过。这次我有一个物联网项目需要从头适配RTT,所以记录一下适配过程中遇到的问题。

0.1 硬件资源

MCU: STM32F103ZET6
SRAM: XM8A51216 16 位宽 512K(512*16,即 1M 字节)FSMC总线挂载

0.2 软件资源

开发环境:RT-Thread Studio
RT-Thread:4.0.3


1 初始化硬件资源

XM8A51216挂载在STM32的FSMC总线上。这部分我在设计电路时参考的是正点原子的战舰开发板。之前使用的是库函数开发环境。这次使用RTT也是我正式转到HAL库。
① 在【driver】目录下新建drv_sram_ex.c文件。

② 引入头文件

#include "board.h"
#include <drv_common.h>
#include <rtthread.h>
#include <rtdevice.h>

③ 视情况引入debug头文件

#define DBG_TAG "drv_exsram"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

④ 编写FSMC和外部SRAM的初始化程序
这部分可以使用STM32CubeMX配置,也可以直接复制正点原子的初始化程序。这里我直接使用正点原子的初始化程序。如下:

SRAM_HandleTypeDef SRAM_Handler;    //SRAM句柄
//SRAM初始化
int DRV_SRAM_Init(void)
{
    GPIO_InitTypeDef GPIO_Initure;
    FSMC_NORSRAM_TimingTypeDef FSMC_ReadWriteTim;
    __HAL_RCC_FSMC_CLK_ENABLE();                //使能FSMC时钟
    __HAL_RCC_GPIOD_CLK_ENABLE();               //使能GPIOD时钟
    __HAL_RCC_GPIOE_CLK_ENABLE();               //使能GPIOE时钟
    __HAL_RCC_GPIOF_CLK_ENABLE();               //使能GPIOF时钟
    __HAL_RCC_GPIOG_CLK_ENABLE();               //使能GPIOG时钟
    //PD0,1,4,5,8~15
    GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_8|\
                     GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|\
                     GPIO_PIN_14|GPIO_PIN_15;
    GPIO_Initure.Mode=GPIO_MODE_AF_PP;          //推挽复用
    GPIO_Initure.Pull=GPIO_PULLUP;              //上拉
    GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;    //高速
    HAL_GPIO_Init(GPIOD,&GPIO_Initure);
    //PE0,1,7~15
    GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|\
                     GPIO_PIN_10| GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|\
                     GPIO_PIN_15;
    HAL_GPIO_Init(GPIOE,&GPIO_Initure);
    //PF0~5,12~15
    GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|\
                     GPIO_PIN_5|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
    HAL_GPIO_Init(GPIOF,&GPIO_Initure);
    //PG0~5,10
    GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_10;
    HAL_GPIO_Init(GPIOG,&GPIO_Initure);
    SRAM_Handler.Instance=FSMC_NORSRAM_DEVICE;
    SRAM_Handler.Extended=FSMC_NORSRAM_EXTENDED_DEVICE;
    SRAM_Handler.Init.NSBank=FSMC_NORSRAM_BANK3;                        //使用NE3
    SRAM_Handler.Init.DataAddressMux=FSMC_DATA_ADDRESS_MUX_DISABLE;     //地址/数据线不复用
    SRAM_Handler.Init.MemoryType=FSMC_MEMORY_TYPE_SRAM;                 //SRAM
    SRAM_Handler.Init.MemoryDataWidth=FSMC_NORSRAM_MEM_BUS_WIDTH_16;    //16位数据宽度
    SRAM_Handler.Init.BurstAccessMode=FSMC_BURST_ACCESS_MODE_DISABLE;   //是否使能突发访问,仅对同步突发存储器有效,此处未用到
    SRAM_Handler.Init.WaitSignalPolarity=FSMC_WAIT_SIGNAL_POLARITY_LOW; //等待信号的极性,仅在突发模式访问下有用
    SRAM_Handler.Init.WaitSignalActive=FSMC_WAIT_TIMING_BEFORE_WS;      //存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT
    SRAM_Handler.Init.WriteOperation=FSMC_WRITE_OPERATION_ENABLE;       //存储器写使能
    SRAM_Handler.Init.WaitSignal=FSMC_WAIT_SIGNAL_DISABLE;              //等待使能位,此处未用到
    SRAM_Handler.Init.ExtendedMode=FSMC_EXTENDED_MODE_DISABLE;          //读写使用相同的时序
    SRAM_Handler.Init.AsynchronousWait=FSMC_ASYNCHRONOUS_WAIT_DISABLE;  //是否使能同步传输模式下的等待信号,此处未用到
    SRAM_Handler.Init.WriteBurst=FSMC_WRITE_BURST_DISABLE;              //禁止突发写
    //FMC读时序控制寄存器
    FSMC_ReadWriteTim.AddressSetupTime=0x00;        //地址建立时间(ADDSET)为1个HCLK 1/72M=13.8ns
    FSMC_ReadWriteTim.AddressHoldTime=0x00;         //地址保持时间(ADDHLD)模式A未用到
    FSMC_ReadWriteTim.DataSetupTime=0x01;           //数据保存时间为3个HCLK =4*13.8=55ns
    FSMC_ReadWriteTim.BusTurnAroundDuration=0X00;
    FSMC_ReadWriteTim.AccessMode=FSMC_ACCESS_MODE_A;//模式A
    HAL_SRAM_Init(&SRAM_Handler,&FSMC_ReadWriteTim,&FSMC_ReadWriteTim);
    return 0;
}

⑤ 通过INIT_BOARD_EXPORT(fn) 导出初始化,使其在RTT的板级初始化阶段运行

INIT_BOARD_EXPORT(DRV_SRAM_Init);

至此外部SRAM的硬件初始化即完成,下面开始将外部SRAM加入内存管理。

2 注册内存管理

使用RT-Thread Studio建立STM32 的 RT-Thread 项目时,内存管理默认使用的是内存池。如下图所示。

2.1 RTT内存堆初探

① 修改RTT 内核中内存管理配置
将内存管理配置修改为:
【取消】【使用内存池】;
【勾选】【使用内存堆对象】
【修改】【使能动态内存】为【Use all of memheap objects as heap】
【保存修改】
此时,编译可以编译下载工程。在终端中使用list_memheap命令查看所有内存堆,可以看到已经有了一个内存堆。如下图所示。



该内存堆为程序编译后,内部RAM剩余空间。这点引用RTT文档中的说明:

程序运行之前,需要有文件实体被烧录到 STM32 的 Flash 中,一般是 bin 或者 hex 文件,该被烧录文件称为可执行映像文件。如下图左边部分所示,是可执行映像文件烧录到 STM32 后的内存分布,它包含 RO 段和 RW 段两个部分:其中 RO 段中保存了 Code、RO-data 的数据,RW 段保存了 RW-data 的数据,由于 ZI-data 都是 0,所以未包含在映像文件中。

STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 地址和大小分配出 ZI 段,并将这块 RAM 区域清零。其中动态内存堆为未使用的 RAM 空间,RT-Thread 将 “ZI 段结尾处” 到内存尾部的空间用作内存堆。

这一点我们可以打开【工程】→【driver】→board.c文件。



可以看到,在RT_WEAK void rt_hw_board_init()中调用了void rt_system_heap_init(void begin_addr, void end_addr);进行内存堆初始化。
RTT手册上说:

在使用内存堆时,必须要在系统初始化的时候进行堆的初始化,可以通过下面的函数接口完成:

void rt_system_heap_init(void begin_addr, void end_addr);

这个函数会把参数 begin_addr,end_addr 区域的内存空间作为内存堆来使用。下表描述了该函数的输入参数:

rt_system_heap_init() 的输入参数

begin_addr 堆内存区域起始地址
end_addr 堆内存区域结束地址

2.2 memheap管理算法

在我们使用RTT时,嵌入式的内存资源时比较紧张的,因此我们不希望只使用外部SRAM而放弃内部RAM。因此我们的RTT需要管理多个地址不连续的内存堆。好在RTT支持memheap管理算法,可以很方便地把多个 memheap(地址可不连续)粘合起来用于系统的 heap 分配。

在RTT的手册中有一个提示,刚开始我被这个提示迷惑住了。提示如下:

这个提示的意思是系统原来的heap和memheap是两套不同的机制。而我理解成如果使用heap管理则使用void rt_system_heap_init(void begin_addr, void end_addr);接口初始化内存堆;而使用memheap则全部使用rt_err_t rt_memheap_init(...)初始化所有内存堆(包括首堆)。

然而并非如此。即使使用memheap管理算法,第一个内存堆仍然要使用void rt_system_heap_init(void begin_addr, void end_addr);所以,board.c中的void rt_hw_board_init()内初始化内存堆的代码不需要修改。

② 在drv_sram_ex.c中增加内存注册函数。

#define RAM_HEAP_EX_START           (0x68000000)
#define RAM_HEAP_EX_SIZE            (1024 * 1024)
#define RAM_HEAP_EX_END             (RAM_EX_START + RAM_EX_SIZE)
#include 
struct rt_memheap ex_ram_heap;
int DRV_EX_SRAM_Register(void)
{
    //注册内存堆到内存堆管理
    rt_memheap_init(&ex_ram_heap,"SRAM EX",(void*)RAM_HEAP_EX_START,RAM_HEAP_EX_SIZE);

    return 0;
}

首先引入#include 头文件。
而后定义了一个静态结构体变量,struct rt_memheap ex_ram_heap 作为内存堆控制块。rt_memheap变量类型需要引入上述的头文件。
然后调用rt_memheap_init接口将外部内存添加到内存堆中。
最后,调用INIT_COMPONENT_EXPORT将其导出到RTT组件初始化,使其在RTT组件级自动运行。

编译下载后使用list_memheap命令在终端中可以查看内存堆,效果如下:

可以看到外部内存堆已经初始化完成了,后续我们就可以使用RTT的内存管理API申请、释放内存。


更多内容请查看Skyate的个人博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值