Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
memheap
RT-Thread一般讨论
memheap 管理多块内存的使用方法
发布于 2022-05-10 21:59:57 浏览:3469
订阅该版
文章目录 [toc] ------------------ 在开发中由于单片机自带的 RAM 空间比较小,有时候需要扩展片外的 RAM 以供使用,RT-Thread 提供了 memheap 管理算法来管理多块不相邻的内存空间,本文以正点原子的 STM32F429 阿波罗开发板为例,讲解使用 memheap 内存管理算法对内部 RAM 和片外的 SDRAM 进行管理的方法,使用的软件包为基于开发板的 STM32F429-ATK-APOLLO v0.1.0 软件包。 # 1 memheap 管理算法简介 本部分来源于 RT-Thread 的 [memheap 管理算法官方文档](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/memory/memory?id=memheap-%e7%ae%a1%e7%90%86%e7%ae%97%e6%b3%95)。 memheap 管理算法适用于系统含有多个地址可不连续的内存堆。使用 memheap 内存管理可以简化系统存在多个内存堆时的使用:当系统中存在多个内存堆的时候,用户只需要在系统初始化时将多个所需的 memheap 初始化,并开启 memheap 功能就可以很方便地把多个 memheap(地址可不连续)粘合起来用于系统的 heap 分配。 注:在开启 memheap 之后原来的 heap 功能将被关闭,两者只可以通过打开或关闭 RT_USING_MEMHEAP_AS_HEAP 来选择其一 memheap 工作机制如下图所示,首先将多块内存加入 memheap_item 链表进行粘合。当分配内存块时,会先从默认内存堆去分配内存,当分配不到时会查找 memheap_item 链表,尝试从其他的内存堆上分配内存块。应用程序不用关心当前分配的内存块位于哪个内存堆上,就像是在操作一个内存堆。 ![image.png](https://oss-club.rt-thread.org/uploads/20220510/07b5a50c4b799d7f6491394d2acb0bb2.png) # 2 只使用片内 RAM 的示例 平时用的就是从片内申请内存,写这部分主要是为了和从片外 SDRAN 申请内存进行对比。选择以开发板创建工程后,选择 STM32F429-ATK-APOLLO 开发板,工程创建后默认是没有开启片外的 SDRAM 的,此时工程中只配置了片内的 RAM 作为内存堆,我们编写一个 sram 内存堆的申请测试函数进行内存堆的测试,测试代码如下所示(示例仅作为测试使用,目的是了解原理,均没有写内存堆的释放函数,下面的测试函数一样)。 ```c void sram_test(void) { int size = 50 * 1024; // 50KBytes rt_uint8_t * ptr = RT_NULL; ptr = rt_malloc(size); if(ptr != RT_NULL) { LOG_D("ptr = %p", ptr); // 打印申请到的空间的首地址 } else { LOG_E("malloc failed"); } } MSH_CMD_EXPORT(sram_test, sram test) ``` 编译烧写后我们使用定义的 `sram_test` 命令来进行内存堆的申请测试,测试结果如下。根据测试的日志信息我们可以看出系统复位后内存堆的空间为 183400 字节,我们设定的是每次申请 50KB = 51200 字节的空间,每次申请后打印出剩余的内存堆空间的大小。从结果可以看出,每次申请的内存空间的地址都是 0x2000****,这是因为 STM32 的内部 RAM 空间的起始地址为 0x20000000,等到第四次申请时内部 RAM 的剩余空间大小不够导致申请失败。 ```c \ | / - RT - Thread Operating System / | \ 4.0.4 build May 10 2022 21:04:03 2006 - 2021 Copyright by rt-thread team msh >list_memheap /* 初始时,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- heap 190756 7356 183400 msh >sram_test [D/main] ptr = 20003380 msh >list_memheap /* 第 1 次申请后,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- heap 190756 58580 132176 msh >sram_test [D/main] ptr = 2000fb98 msh >list_memheap /* 第 2 次申请后,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- heap 190756 109804 80952 msh >sram_test [D/main] ptr = 2001c3b0 msh >list_memheap /* 第 3 次申请后,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- heap 190756 161028 29728 msh >sram_test [E/main] malloc failed msh >sram_test [E/main] malloc failed msh >list_memheap /* 申请失败后,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- heap 190756 161028 29728 ``` # 3 配置片外 SDRAM 和 内存管理算法 在 RT-Thread Settings 里面可以配置使能片外的 SDRAM,配置方式如下图所示,配置后 SDRAM 的驱动代码位于路径 libraries/HAL_Drivers/drv_sdram.c 下。SDRAM 外设的配置讲解可以参考文章 [RT Thread Studio RGB屏幕之 SDRAM 配置](https://club.rt-thread.org/ask/article/47789c36753224f8.html) ![image.png](https://oss-club.rt-thread.org/uploads/20220510/a9f38bdc4f9743485db075887803edda.png) 配置好了片外的 SDRAM 后,我们还需要选择相应的内存管理算法,同样在 RT-Thread Settings 里面进行配置,配置界面如下图所示。 ![image.png](https://oss-club.rt-thread.org/uploads/20220510/998590cf7bafe91299da16c1d8645b67.png.webp) # 4 SDRAM 的读写测试 配置完 SDRAM 和内存管理算法后,我们需要将片外的 SDRAM 加入到 memheap_item 链表中进行管理,添加的方法如下: ```c struct rt_memheap sdram_heap; // memheap 控制块 #define SDRAM_BANK_ADDR ((uint32_t)0XC0000000) // SDRAM 的起始地址 #define SDRAM_SIZE ((uint32_t)0x2000000) // SDRAM 的大小 /* SDRAM 内存堆的初始化 */ rt_memheap_init(&sdram_heap, "sdram", (void *)SDRAM_BANK_ADDR, SDRAM_SIZE); ``` 将 SDRAM 内存堆进行初始化后,编译下载置开发板可以后,使用 `list_memheap` 可以看到新增加的 sdram 内存堆,如下所示。我们可以看到片外的 SDRAM 初始化之后我们并没有使用,但是在 max used size 字段中确显示已经使用了 48 字节的空间,这部分空间是内存堆的数据头,用于 magic、used 信息及链表节点使用。 ```c \ | / - RT - Thread Operating System / | \ 4.0.4 build May 10 2022 21:24:46 2006 - 2021 Copyright by rt-thread team msh >list_memheap memheap pool size max used size available size -------- ---------- ------------- -------------- sdram 33554432 48 33554384 /* 新增加的 SDRAM */ heap 190584 7356 183228 /* 片内的 RAM */ ``` 为了测试我们初始化的 SDRAM 是否可以正常使用,通常我们会首先写一个 SDRAM 的读写测试函数,对 SDRAM 的每个字节进行读写测试,根据写入和读出的结果是否一致来判断 SDRAM 是否配置正确,读写测试代码如下。正点原子 F429 阿波罗开发板的 SDRAM 使用的是 16 根地址线,因此读写测试时数据位的宽度定义为 16。 ```c #define SDRAM_DATA_WIDTH 16 // 数据位的宽度 #define SDRAM_BANK_ADDR ((uint32_t)0XC0000000) // SDRAM 的起始地址 int sdram_test(void) { int i = 0; uint32_t start_time = 0, time_cast = 0; #if SDRAM_DATA_WIDTH == 8 char data_width = 1; uint8_t data = 0; #elif SDRAM_DATA_WIDTH == 16 char data_width = 2; uint16_t data = 0; #else char data_width = 4; uint32_t data = 0; #endif /* write data */ LOG_D("Writing the %ld bytes data, waiting....", SDRAM_SIZE); start_time = rt_tick_get(); for (i = 0; i < SDRAM_SIZE / data_width; i++) { #if SDRAM_DATA_WIDTH == 8 *(__IO uint8_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint8_t)0x55; #elif SDRAM_DATA_WIDTH == 16 *(__IO uint16_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint16_t)(i % 65535); #else *(__IO uint32_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint32_t)0x55555555; #endif } time_cast = rt_tick_get() - start_time; LOG_D("Write data success, total time: %d.%03dS.", time_cast / RT_TICK_PER_SECOND, time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000)); /* read data */ LOG_D("start Reading and verifying data, waiting...."); for (i = 0; i < SDRAM_SIZE / data_width; i++) { #if SDRAM_DATA_WIDTH == 8 data = *(__IO uint8_t *)(SDRAM_BANK_ADDR + i * data_width); if (data != 0x55) { LOG_E("SDRAM test failed!"); break; } #elif SDRAM_DATA_WIDTH == 16 data = *(__IO uint16_t *)(SDRAM_BANK_ADDR + i * data_width); if (data != (i % 65535)) { LOG_E("SDRAM test failed!"); break; } #else data = *(__IO uint32_t *)(SDRAM_BANK_ADDR + i * data_width); if (data != 0x55555555) { LOG_E("SDRAM test failed!"); break; } #endif } if (i >= SDRAM_SIZE / data_width) { LOG_D("SDRAM test success!"); } return RT_EOK; } MSH_CMD_EXPORT(sdram_test, sdram test) ``` 执行读写测试函数后,如果测试成功,日志信息如下。需要注意的是,读写测试函数是对片外 SDRAM 的整片的测试,执行完读写测试代码后,如果申请片外 SDRAM 的空间会直接导致硬件错误,因为我们对 SDRAM 整片的读写测试破坏了 SDRAM 中保存的数据头的信息,所以申请会出错。测试 SDRAM 的读写没有问题后我们应该重启开发板进行内存的申请测试。 ```c \ | / - RT - Thread Operating System / | \ 4.0.4 build May 10 2022 21:24:46 2006 - 2021 Copyright by rt-thread team msh >list_memheap memheap pool size max used size available size -------- ---------- ------------- -------------- sdram 33554432 48 33554384 heap 190584 7356 183228 msh >sdram_test [D/drv.sdram] Writing the 33554432 bytes data, waiting.... [D/drv.sdram] Write data success, total time: 4.393S. [D/drv.sdram] start Reading and verifying data, waiting.... [D/drv.sdram] SDRAM test success! ``` # 5 内存堆申请测试 ## 5.1 内部 RAM 和 片外 SDRAM 顺序申请测试 同样的,我们编写一个函数对添加 SDRAM 后 menheap 管理的内存堆进行测试,测试代码如下 ```c void malloc_test(void) { int size = 50 * 1024; // 50KBytes rt_uint8_t * ptr = RT_NULL; ptr = rt_malloc(size); if(ptr != RT_NULL) { LOG_D("ptr = %p", ptr); // 打印申请到的空间的首地址 } else { LOG_E("malloc failed"); } } MSH_CMD_EXPORT(malloc_test, malloc test) ``` 下载程序到开发板后,根据测试结果我们可以看到使用 rt_malloc 函数进行申请是首先申请的是片内的 RAM 的空间,等到片内 RAM 的剩余空间不够时系统会去另一块内存堆(SDRAM)上申请空间。 ```c \ | / - RT - Thread Operating System / | \ 4.0.4 build May 10 2022 20:11:53 2006 - 2021 Copyright by rt-thread team msh >list_memheap /* 初始时,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- sdram 33554432 48 33554384 heap 190584 7356 183228 msh >sdram_malloc_test [D/drv.sdram] ptr = 2000342c /* 申请到的是片内 RAM 的空间 */ msh >list_memheap /* 第 1 次申请后,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- sdram 33554432 48 33554384 heap 190584 58580 132004 msh >sdram_malloc_test [D/drv.sdram] ptr = 2000fc44 /* 申请到的是片内 RAM 的空间 */ msh >list_memheap /* 第 2 次申请后,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- sdram 33554432 48 33554384 heap 190584 109804 80780 msh >sdram_malloc_test [D/drv.sdram] ptr = 2001c45c /* 申请到的是片内 RAM 的空间 */ msh >list_memheap /* 第 3 次申请后,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- sdram 33554432 48 33554384 heap 190584 161028 29556 msh >sdram_malloc_test [D/drv.sdram] ptr = c0000018 /* 片内 RAM 剩余空间不够,申请到的是片外 SDRAM 的空间 */ msh >list_memheap /* 第 4 次申请后,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- sdram 33554432 51272 33503160 heap 190584 161028 29556 msh >sdram_malloc_test [D/drv.sdram] ptr = c000c830 /* 片内 RAM 剩余空间不够,申请到的是片外 SDRAM 的空间 */ ``` ## 5.2 直接申请片外 SDRAM 内存测试 如果想直接从片外的 SDRAM 内存空间进行申请时,我们可以使用 `rt_memheap_alloc` 进行操作,同样我们也编写一个直接从片外 SDRAM 申请空间的测试函数,如下所示。其中 `sdram_heap` 控制块需要和上文对 SDRAM 初始化 `rt_memheap_init(&sdram_heap, ...)` 时的控制块的变量保持一致。 ```c void sdram_malloc_test(void) { int size = 50 * 1024; // 50KBytes uint8_t *ptr; ptr = rt_memheap_alloc(&sdram_heap, size); if(ptr != RT_NULL) { LOG_D("ptr = %p", ptr); // 打印申请到的空间的首地址 } else { LOG_E("sdram malloc failed"); } } MSH_CMD_EXPORT(sdram_malloc_test, sdram malloc test) ``` 直接从片外 SDRAM 申请空间的测试结果如下,从结果中可以看出每次申请的都是片外 SDRAM 中的空间,且此时片内 RAM 的剩余空间大于要申请的空间的大小。 ```c \ | / - RT - Thread Operating System / | \ 4.0.4 build May 10 2022 20:11:53 2006 - 2021 Copyright by rt-thread team msh >list_memheap /* 初始时,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- sdram 33554432 48 33554384 heap 190584 7356 183228 msh >sdram_malloc_test [D/drv.sdram] ptr = c0000018 /* 申请到的是片外 SDRAM 的空间 */ msh >list_memheap /* 第 1 次申请后,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- sdram 33554432 51272 33503160 heap 190584 7356 183228 msh >sdram_malloc_test [D/drv.sdram] ptr = c000c830 /* 申请到的是片外 SDRAM 的空间 */ msh >list_memheap /* 第 2 次申请后,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- sdram 33554432 102496 33451936 heap 190584 7356 183228 msh >sdram_malloc_test [D/drv.sdram] ptr = c0019048 /* 申请到的是片外 SDRAM 的空间 */ msh >list_memheap /* 第 3 次申请后,打印内存堆空间的信息 */ memheap pool size max used size available size -------- ---------- ------------- -------------- sdram 33554432 153720 33400712 heap 190584 7356 183228 ``` # 6 补充 ## 6.1 为什么 rt_malloc 优先申请片内 RAM 的内存 `rt_malloc()` 的源码(rt-thread/src/memheap.c)如下所示。 ```c void *rt_malloc(rt_size_t size) { void *ptr; /* try to allocate in system heap */ ptr = rt_memheap_alloc(&_heap, size); // 先从 _heap 控制块中申请内存 if (ptr == RT_NULL) // _heap 控制块申请失败,查找其他的 memheap 控制块 { struct rt_object *object; struct rt_list_node *node; struct rt_memheap *heap; struct rt_object_information *information; /* try to allocate on other memory heap 尝试从其他的内存堆中进行申请 */ information = rt_object_get_information(RT_Object_Class_MemHeap); // 获取类型为内存堆的对象信息 RT_ASSERT(information != RT_NULL); for (node = information->object_list.next; node != &(information->object_list); node = node->next) // 遍历 memheap_item 链表 { object = rt_list_entry(node, struct rt_object, list); // 获取结构体的首地址 container_of heap = (struct rt_memheap *)object; RT_ASSERT(heap); RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap); /* not allocate in the default system heap */ if (heap == &_heap) // 如果找到的控制块和 _heap 相同则继续查找其他控制块 continue; ptr = rt_memheap_alloc(heap, size); // 找到了其他的内存堆,就从该内存堆上申请空间 if (ptr != RT_NULL) break; } } ... ... // 省去分析无关代码 return ptr; } ``` 分析上述源码我们可以看到首先调用了 `rt_memheap_alloc(&_heap, size)` 从 `_heap` 控制块中申请内存,如果从 `_heap` 控制块中申请失败的话,就从 memheap_list 链表中查找其他的内存堆,如果有其他的内存堆就从找到的内存堆中申请空间,如果没有其他的内存堆则返回 `RT_NULL`。 那么 `_heap` 是在哪里定义和初始化的呢,继续分析源码我们可以发现,在文件 rt-thread/src/memheap.c 中对 `_heap` 进行了定义和初始化,代码如下。 ```c static struct rt_memheap _heap; void rt_system_heap_init(void *begin_addr, void *end_addr) { RT_ASSERT((rt_uint32_t)end_addr > (rt_uint32_t)begin_addr); /* initialize a default heap in the system */ rt_memheap_init(&_heap, "heap", begin_addr, (rt_uint32_t)end_addr - (rt_uint32_t)begin_addr); } ``` 在 [RT-Thread 自动初始化](https://club.rt-thread.org/ask/article/4548d81e7237255a.html)的代码中对片内的内存堆进行了初始化,代码如下 ```c void rt_hw_board_init() { rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END); // 初始化内部 RAM 的内存堆 // #define HEAP_BEGIN (&__bss_end) // #define HEAP_END STM32_SRAM_END // #define STM32_SRAM_END (0x20000000 + STM32_SRAM_SIZE * 1024) // #define STM32_SRAM_SIZE (192) } ``` 根据上面的两段代码,分析后我们可以看出 [RT-Thread 自动初始化](https://club.rt-thread.org/ask/article/4548d81e7237255a.html)的代码首先将未使用的片内 RAM 的空间都当做系统的内存堆空间进行初始化,对应的控制块的名称为 `_heap`,所以在使用 `rt_malloc` 进行空间的申请时会先申请片内的 RAM。 除此之外分析 `rt_malloc` 的源码我们还可以得到申请空间实际调用的是 `rt_memheap_alloc`,该函数的第一个参数决定了是从哪里申请的空间,所以我们可以直接使用该函数来确定从哪里来申请空间。如果我们想有先从片外的 SDRAM 申请,然后再从片内的 RAM 申请,也可以修改 `rt_malloc` 的源码,将 `ptr = rt_memheap_alloc(&_heap, size);` 的第一个参数修改为自己定义的外部 SDRAM 的控制块的名称,对应的将 `if (heap == &_heap)` 中的 `_heap` 也修改为自己定义的外部 SDRAM 的控制块的名称。 # 7 完整代码 在基于芯片创建的工程的技术上将 drv_sdram.c 的代码进行了部分的修改,修改后的完整代码如下 ```c #include
#ifdef BSP_USING_SDRAM #include
#define DRV_DEBUG #define LOG_TAG "drv.sdram" #include
static SDRAM_HandleTypeDef hsdram1; static FMC_SDRAM_CommandTypeDef command; #ifdef RT_USING_MEMHEAP_AS_HEAP static struct rt_memheap sdram_heap; #endif /** * @brief Perform the SDRAM exernal memory inialization sequence * @param hsdram: SDRAM handle * @param Command: Pointer to SDRAM command structure * @retval None */ static void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command) { __IO uint32_t tmpmrd = 0; uint32_t target_bank = 0; #if SDRAM_TARGET_BANK == 1 target_bank = FMC_SDRAM_CMD_TARGET_BANK1; #else target_bank = FMC_SDRAM_CMD_TARGET_BANK2; #endif /* Configure a clock configuration enable command */ Command->CommandMode = FMC_SDRAM_CMD_CLK_ENABLE; Command->CommandTarget = target_bank; Command->AutoRefreshNumber = 1; Command->ModeRegisterDefinition = 0; /* Send the command */ HAL_SDRAM_SendCommand(hsdram, Command, 0x1000); /* Insert 100 ms delay */ /* interrupt is not enable, just to delay some time. */ for (tmpmrd = 0; tmpmrd < 0xffffff; tmpmrd ++) ; /* Configure a PALL (precharge all) command */ Command->CommandMode = FMC_SDRAM_CMD_PALL; Command->CommandTarget = target_bank; Command->AutoRefreshNumber = 1; Command->ModeRegisterDefinition = 0; /* Send the command */ HAL_SDRAM_SendCommand(hsdram, Command, 0x1000); /* Configure a Auto-Refresh command */ Command->CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE; Command->CommandTarget = target_bank; Command->AutoRefreshNumber = 8; Command->ModeRegisterDefinition = 0; /* Send the command */ HAL_SDRAM_SendCommand(hsdram, Command, 0x1000); /* Program the external memory mode register */ #if SDRAM_DATA_WIDTH == 8 tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 | #elif SDRAM_DATA_WIDTH == 16 tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2 | #else tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_4 | #endif SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL | #if SDRAM_CAS_LATENCY == 3 SDRAM_MODEREG_CAS_LATENCY_3 | #else SDRAM_MODEREG_CAS_LATENCY_2 | #endif SDRAM_MODEREG_OPERATING_MODE_STANDARD | SDRAM_MODEREG_WRITEBURST_MODE_SINGLE; Command->CommandMode = FMC_SDRAM_CMD_LOAD_MODE; Command->CommandTarget = target_bank; Command->AutoRefreshNumber = 1; Command->ModeRegisterDefinition = tmpmrd; /* Send the command */ HAL_SDRAM_SendCommand(hsdram, Command, 0x1000); /* Set the device refresh counter */ HAL_SDRAM_ProgramRefreshRate(hsdram, SDRAM_REFRESH_COUNT); } static int SDRAM_Init(void) { int result = RT_EOK; FMC_SDRAM_TimingTypeDef SDRAM_Timing; /* SDRAM device configuration */ hsdram1.Instance = FMC_SDRAM_DEVICE; SDRAM_Timing.LoadToActiveDelay = LOADTOACTIVEDELAY; SDRAM_Timing.ExitSelfRefreshDelay = EXITSELFREFRESHDELAY; SDRAM_Timing.SelfRefreshTime = SELFREFRESHTIME; SDRAM_Timing.RowCycleDelay = ROWCYCLEDELAY; SDRAM_Timing.WriteRecoveryTime = WRITERECOVERYTIME; SDRAM_Timing.RPDelay = RPDELAY; SDRAM_Timing.RCDDelay = RCDDELAY; #if SDRAM_TARGET_BANK == 1 hsdram1.Init.SDBank = FMC_SDRAM_BANK1; #else hsdram1.Init.SDBank = FMC_SDRAM_BANK2; #endif #if SDRAM_COLUMN_BITS == 8 hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8; #elif SDRAM_COLUMN_BITS == 9 hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9; #elif SDRAM_COLUMN_BITS == 10 hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_10; #else hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_11; #endif #if SDRAM_ROW_BITS == 11 hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_11; #elif SDRAM_ROW_BITS == 12 hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12; #else hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13; #endif #if SDRAM_DATA_WIDTH == 8 hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_8; #elif SDRAM_DATA_WIDTH == 16 hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16; #else hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_32; #endif hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; #if SDRAM_CAS_LATENCY == 1 hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_1; #elif SDRAM_CAS_LATENCY == 2 hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_2; #else hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3; #endif hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE; #if SDCLOCK_PERIOD == 2 hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2; #else hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_3; #endif hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE; #if SDRAM_RPIPE_DELAY == 0 hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0; #elif SDRAM_RPIPE_DELAY == 1 hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1; #else hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_2; #endif /* Initialize the SDRAM controller */ if (HAL_SDRAM_Init(&hsdram1, &SDRAM_Timing) != HAL_OK) { LOG_E("SDRAM init failed!"); result = -RT_ERROR; } else { /* Program the SDRAM external device */ SDRAM_Initialization_Sequence(&hsdram1, &command); LOG_D("sdram init success, mapped at 0x%X, size is %d bytes, data width is %d", SDRAM_BANK_ADDR, SDRAM_SIZE, SDRAM_DATA_WIDTH); #ifdef RT_USING_MEMHEAP_AS_HEAP /* If RT_USING_MEMHEAP_AS_HEAP is enabled, SDRAM is initialized to the heap */ rt_memheap_init(&sdram_heap, "sdram", (void *)SDRAM_BANK_ADDR, SDRAM_SIZE); #endif } return result; } INIT_BOARD_EXPORT(SDRAM_Init); #ifdef DRV_DEBUG #ifdef FINSH_USING_MSH int sdram_test(void) { int i = 0; uint32_t start_time = 0, time_cast = 0; #if SDRAM_DATA_WIDTH == 8 char data_width = 1; uint8_t data = 0; #elif SDRAM_DATA_WIDTH == 16 char data_width = 2; uint16_t data = 0; #else char data_width = 4; uint32_t data = 0; #endif /* write data */ LOG_D("Writing the %ld bytes data, waiting....", SDRAM_SIZE); start_time = rt_tick_get(); for (i = 0; i < SDRAM_SIZE / data_width; i++) { #if SDRAM_DATA_WIDTH == 8 *(__IO uint8_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint8_t)0x55; #elif SDRAM_DATA_WIDTH == 16 *(__IO uint16_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint16_t)(i % 65535); #else *(__IO uint32_t *)(SDRAM_BANK_ADDR + i * data_width) = (uint32_t)0x55555555; #endif } time_cast = rt_tick_get() - start_time; LOG_D("Write data success, total time: %d.%03dS.", time_cast / RT_TICK_PER_SECOND, time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000)); /* read data */ LOG_D("start Reading and verifying data, waiting...."); for (i = 0; i < SDRAM_SIZE / data_width; i++) { #if SDRAM_DATA_WIDTH == 8 data = *(__IO uint8_t *)(SDRAM_BANK_ADDR + i * data_width); if (data != 0x55) { LOG_E("SDRAM test failed!"); break; } #elif SDRAM_DATA_WIDTH == 16 data = *(__IO uint16_t *)(SDRAM_BANK_ADDR + i * data_width); if (data != (i % 65535)) { LOG_E("SDRAM test failed!"); break; } #else data = *(__IO uint32_t *)(SDRAM_BANK_ADDR + i * data_width); if (data != 0x55555555) { LOG_E("SDRAM test failed!"); break; } #endif } if (i >= SDRAM_SIZE / data_width) { LOG_D("SDRAM test success!"); } return RT_EOK; } MSH_CMD_EXPORT(sdram_test, sdram test) /* 直接从片外 SDRAM 申请空间测试 */ void sdram_malloc_test(void) { int size = 50 * 1024; // 50KBytes uint8_t *ptr; ptr = rt_memheap_alloc(&sdram_heap, size); if(ptr != RT_NULL) { LOG_D("ptr = %p", ptr); // 打印申请到的空间的首地址 } else { LOG_E("malloc failed"); } } MSH_CMD_EXPORT(sdram_malloc_test, sdram malloc test) /* 从片内 RAM 和 片外 SDRAM 顺序申请测试 */ void malloc_test(void) { int size = 50 * 1024; // 50KBytes rt_uint8_t * ptr = RT_NULL; ptr = rt_malloc(size); if(ptr != RT_NULL) { LOG_D("ptr = %p", ptr); // 打印申请到的空间的首地址 } else { LOG_E("malloc failed"); } } MSH_CMD_EXPORT(malloc_test, malloc test) #endif /* FINSH_USING_MSH */ #endif /* DRV_DEBUG */ #endif /* BSP_USING_SDRAM */ ``` sdram_port.h ```c #ifndef __SDRAM_PORT_H__ #define __SDRAM_PORT_H__ /* parameters for sdram peripheral */ /* Bank1 or Bank2 */ #define SDRAM_TARGET_BANK 1 /* stm32f4 Bank1:0XC0000000 Bank2:0XD0000000 */ #define SDRAM_BANK_ADDR ((uint32_t)0XC0000000) /* data width: 8, 16, 32 */ #define SDRAM_DATA_WIDTH 16 /* column bit numbers: 8, 9, 10, 11 */ #define SDRAM_COLUMN_BITS 9 /* row bit numbers: 11, 12, 13 */ #define SDRAM_ROW_BITS 13 /* cas latency clock number: 1, 2, 3 */ #define SDRAM_CAS_LATENCY 3 /* read pipe delay: 0, 1, 2 */ #define SDRAM_RPIPE_DELAY 1 /* clock divid: 2, 3 */ #define SDCLOCK_PERIOD 2 /* refresh rate counter */ #define SDRAM_REFRESH_COUNT ((uint32_t)0x02AB) #define SDRAM_SIZE ((uint32_t)0x2000000) /* Timing configuration for W9825G6KH-6 */ /* 90 MHz of SD clock frequency (180MHz/2) */ /* TMRD: 2 Clock cycles */ #define LOADTOACTIVEDELAY 2 /* TXSR: 7x11.90ns */ #define EXITSELFREFRESHDELAY 8 /* TRAS: 4x11.90ns */ #define SELFREFRESHTIME 6 /* TRC: 7x11.90ns */ #define ROWCYCLEDELAY 6 /* TWR: 2 Clock cycles */ #define WRITERECOVERYTIME 2 /* TRP: 2x11.90ns */ #define RPDELAY 2 /* TRCD: 2x11.90ns */ #define RCDDELAY 2 /* memory mode register */ #define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000) #define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001) #define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002) #define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004) #define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000) #define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008) #define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020) #define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030) #define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000) #define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000) #define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200) #endif ```
11
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
crystal266
嵌入式
文章
14
回答
547
被采纳
161
关注TA
发私信
相关文章
1
有关动态模块加载的一篇论文
2
最近的调程序总结
3
晕掉了,这么久都不见layer2的踪影啊
4
继续K9ii的历程
5
[GUI相关] FreeType 2
6
[GUI相关]嵌入式系统中文输入法的设计
7
20081101 RT-Thread开发者聚会总结
8
嵌入式系统基础
9
linux2.4.19在at91rm9200 上的寄存器设置
10
[转]基于嵌入式Linux的通用触摸屏校准程序
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
ota在线升级
UART
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
at_device
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
张世争
8
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
KunYi
6
个答案
1
次被采纳
本月文章贡献
程序员阿伟
6
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部