0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

使用memheap內(nèi)存管理算法對(duì)片內(nèi)RAM和片外SDRAM進(jìn)行管理的方法

RT-Thread 操作系統(tǒng) ? 來(lái)源:RT-Thread 操作系統(tǒng) ? 作者:RT-Thread 操作系統(tǒng) ? 2022-06-17 08:53 ? 次閱讀

??在開(kāi)發(fā)中由于單片機(jī)自帶的 RAM 空間比較小,有時(shí)候需要擴(kuò)展片外的 RAM 以供使用,RT-Thread 提供了 memheap 管理算法來(lái)管理多塊不相鄰的內(nèi)存空間,本文以正點(diǎn)原子的 STM32F429 阿波羅開(kāi)發(fā)板為例,講解使用 memheap 內(nèi)存管理算法對(duì)內(nèi)部 RAM 和片外的 SDRAM 進(jìn)行管理的方法,使用的軟件包為基于開(kāi)發(fā)板的 STM32F429-ATK-APOLLO v0.1.0 軟件包。

1 memheap 管理算法簡(jiǎn)介

??本部分來(lái)源于 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 管理算法適用于系統(tǒng)含有多個(gè)地址可不連續(xù)的內(nèi)存堆。使用 memheap 內(nèi)存管理可以簡(jiǎn)化系統(tǒng)存在多個(gè)內(nèi)存堆時(shí)的使用:當(dāng)系統(tǒng)中存在多個(gè)內(nèi)存堆的時(shí)候,用戶(hù)只需要在系統(tǒng)初始化時(shí)將多個(gè)所需的 memheap 初始化,并開(kāi)啟 memheap 功能就可以很方便地把多個(gè) memheap(地址可不連續(xù))粘合起來(lái)用于系統(tǒng)的 heap 分配。
注:在開(kāi)啟 memheap 之后原來(lái)的 heap 功能將被關(guān)閉,兩者只可以通過(guò)打開(kāi)或關(guān)閉 RT_USING_MEMHEAP_AS_HEAP 來(lái)選擇其一。memheap 工作機(jī)制如下圖所示,首先將多塊內(nèi)存加入 memheap_item 鏈表進(jìn)行粘合。當(dāng)分配內(nèi)存塊時(shí),會(huì)先從默認(rèn)內(nèi)存堆去分配內(nèi)存,當(dāng)分配不到時(shí)會(huì)查找 memheap_item 鏈表,嘗試從其他的內(nèi)存堆上分配內(nèi)存塊。應(yīng)用程序不用關(guān)心當(dāng)前分配的內(nèi)存塊位于哪個(gè)內(nèi)存堆上,就像是在操作一個(gè)內(nèi)存堆。

poYBAGKrMfuAfgdEAAA8rVQrkd4182.png

2 只使用片內(nèi) RAM 的示例

平時(shí)用的就是從片內(nèi)申請(qǐng)內(nèi)存,寫(xiě)這部分主要是為了和從片外 SDRAN 申請(qǐng)內(nèi)存進(jìn)行對(duì)比。選擇以開(kāi)發(fā)板創(chuàng)建工程后,選擇 STM32F429-ATK-APOLLO 開(kāi)發(fā)板,工程創(chuàng)建后默認(rèn)是沒(méi)有開(kāi)啟片外的 SDRAM 的,此時(shí)工程中只配置了片內(nèi)的 RAM 作為內(nèi)存堆,我們編寫(xiě)一個(gè) sram 內(nèi)存堆的申請(qǐng)測(cè)試函數(shù)進(jìn)行內(nèi)存堆的測(cè)試,測(cè)試代碼如下所示(示例僅作為測(cè)試使用,目的是了解原理,均沒(méi)有寫(xiě)內(nèi)存堆的釋放函數(shù),下面的測(cè)試函數(shù)一樣)。

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); // 打印申請(qǐng)到的空間的首地址
   }
   else
   {
       LOG_E("malloc failed");
   }
}
MSH_CMD_EXPORT(sram_test, sram test)

??編譯燒寫(xiě)后我們使用定義的 sram_test 命令來(lái)進(jìn)行內(nèi)存堆的申請(qǐng)測(cè)試,測(cè)試結(jié)果如下。根據(jù)測(cè)試的日志信息我們可以看出系統(tǒng)復(fù)位后內(nèi)存堆的空間為 183400 字節(jié),我們?cè)O(shè)定的是每次申請(qǐng) 50KB = 51200 字節(jié)的空間,每次申請(qǐng)后打印出剩余的內(nèi)存堆空間的大小。從結(jié)果可以看出,每次申請(qǐng)的內(nèi)存空間的地址都是 0x2000****,這是因?yàn)?STM32 的內(nèi)部 RAM 空間的起始地址為 0x20000000,等到第四次申請(qǐng)時(shí)內(nèi)部 RAM 的剩余空間大小不夠?qū)е律暾?qǐng)失敗。

\ | /
- RT -     Thread Operating System
/ | \     4.0.4 build May 10 2022 21:04:03
2006 - 2021 Copyright by rt-thread team
msh >list_memheap    /* 初始時(shí),打印內(nèi)存堆空間的信息 */
memheap   pool size  max used size available size
-------- ---------- ------------- --------------
heap     190756     7356          183400
msh >sram_test
[D/main] ptr = 20003380
msh >list_memheap    /* 第 1 次申請(qǐng)后,打印內(nèi)存堆空間的信息 */
memheap   pool size  max used size available size
-------- ---------- ------------- --------------
heap     190756     58580         132176
msh >sram_test
[D/main] ptr = 2000fb98
msh >list_memheap    /* 第 2 次申請(qǐng)后,打印內(nèi)存堆空間的信息 */
memheap   pool size  max used size available size
-------- ---------- ------------- --------------
heap     190756     109804        80952
msh >sram_test
[D/main] ptr = 2001c3b0
msh >list_memheap    /* 第 3 次申請(qǐng)后,打印內(nèi)存堆空間的信息 */
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    /* 申請(qǐng)失敗后,打印內(nèi)存堆空間的信息 */
memheap   pool size  max used size available size
-------- ---------- ------------- --------------
heap     190756     161028        29728

3 配置片外 SDRAM 和 內(nèi)存管理算法

??在 RT-Thread Settings 里面可以配置使能片外的 SDRAM,配置方式如下圖所示,配置后 SDRAM 的驅(qū)動(dòng)代碼位于路徑 libraries/HAL_Drivers/drv_sdram.c 下。SDRAM 外設(shè)的配置講解可以參考文章 [RT Thread Studio RGB屏幕之 SDRAM 配置](https://club.rt-thread.org/ask/article/47789c36753224f8.html)

poYBAGKrMlaARxL8AAC0US3qXCQ960.png

??配置好了片外的 SDRAM 后,我們還需要選擇相應(yīng)的內(nèi)存管理算法,同樣在 RT-Thread Settings 里面進(jìn)行配置,配置界面如下圖所示。

poYBAGKrMoWAG4AkAAGL2SCvsFk339.png

4 SDRAM 的讀寫(xiě)測(cè)試

??配置完 SDRAM 和內(nèi)存管理算法后,我們需要將片外的 SDRAM 加入到 memheap_item 鏈表中進(jìn)行管理,添加的方法如下:

struct rt_memheap sdram_heap;                  // memheap 控制塊
#define SDRAM_BANK_ADDR ((uint32_t)0XC0000000) // SDRAM 的起始地址
#define SDRAM_SIZE      ((uint32_t)0x2000000)  // SDRAM 的大小
/* SDRAM 內(nèi)存堆的初始化 */
rt_memheap_init(&sdram_heap, "sdram", (void *)SDRAM_BANK_ADDR, SDRAM_SIZE);
??將 SDRAM 內(nèi)存堆進(jìn)行初始化后,編譯下載置開(kāi)發(fā)板可以后,使用 list_memheap 可以看到新增加的 sdram 內(nèi)存堆,如下所示。我們可以看到片外的 SDRAM 初始化之后我們并沒(méi)有使用,但是在 max used size 字段中確顯示已經(jīng)使用了 48 字節(jié)的空間,這部分空間是內(nèi)存堆的數(shù)據(jù)頭,用于 magic、used 信息及鏈表節(jié)點(diǎn)使用。
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     /* 片內(nèi)的 RAM */

??為了測(cè)試我們初始化的 SDRAM 是否可以正常使用,通常我們會(huì)首先寫(xiě)一個(gè) SDRAM 的讀寫(xiě)測(cè)試函數(shù),對(duì) SDRAM 的每個(gè)字節(jié)進(jìn)行讀寫(xiě)測(cè)試,根據(jù)寫(xiě)入和讀出的結(jié)果是否一致來(lái)判斷 SDRAM 是否配置正確,讀寫(xiě)測(cè)試代碼如下。正點(diǎn)原子 F429 阿波羅開(kāi)發(fā)板的 SDRAM 使用的是 16 根地址線,因此讀寫(xiě)測(cè)試時(shí)數(shù)據(jù)位的寬度定義為 16。

#define SDRAM_DATA_WIDTH 16  // 數(shù)據(jù)位的寬度
#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)

??執(zhí)行讀寫(xiě)測(cè)試函數(shù)后,如果測(cè)試成功,日志信息如下。需要注意的是,讀寫(xiě)測(cè)試函數(shù)是對(duì)片外 SDRAM 的整片的測(cè)試,執(zhí)行完讀寫(xiě)測(cè)試代碼后,如果申請(qǐng)片外 SDRAM 的空間會(huì)直接導(dǎo)致硬件錯(cuò)誤,因?yàn)槲覀儗?duì) SDRAM 整片的讀寫(xiě)測(cè)試破壞了 SDRAM 中保存的數(shù)據(jù)頭的信息,所以申請(qǐng)會(huì)出錯(cuò)。測(cè)試 SDRAM 的讀寫(xiě)沒(méi)有問(wèn)題后我們應(yīng)該重啟開(kāi)發(fā)板進(jìn)行內(nèi)存的申請(qǐng)測(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 內(nèi)存堆申請(qǐng)測(cè)試

5.1 內(nèi)部 RAM 和 片外 SDRAM 順序申請(qǐng)測(cè)試

??同樣的,我們編寫(xiě)一個(gè)函數(shù)對(duì)添加 SDRAM 后 menheap 管理的內(nèi)存堆進(jìn)行測(cè)試,測(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); // 打印申請(qǐng)到的空間的首地址
   }
   else
   {
       LOG_E("malloc failed");
   }
}
MSH_CMD_EXPORT(malloc_test, malloc test)

??下載程序到開(kāi)發(fā)板后,根據(jù)測(cè)試結(jié)果我們可以看到使用 rt_malloc 函數(shù)進(jìn)行申請(qǐng)是首先申請(qǐng)的是片內(nèi)的 RAM 的空間,等到片內(nèi) RAM 的剩余空間不夠時(shí)系統(tǒng)會(huì)去另一塊內(nèi)存堆(SDRAM)上申請(qǐng)空間。

\ | /
- RT -     Thread Operating System
/ | \     4.0.4 build May 10 2022 20:11:53
2006 - 2021 Copyright by rt-thread team
msh >list_memheap    /* 初始時(shí),打印內(nèi)存堆空間的信息 */
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 /* 申請(qǐng)到的是片內(nèi) RAM 的空間 */
msh >list_memheap    /* 第 1 次申請(qǐng)后,打印內(nèi)存堆空間的信息 */
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 /* 申請(qǐng)到的是片內(nèi) RAM 的空間 */
msh >list_memheap    /* 第 2 次申請(qǐng)后,打印內(nèi)存堆空間的信息 */
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 /* 申請(qǐng)到的是片內(nèi) RAM 的空間 */
msh >list_memheap    /* 第 3 次申請(qǐng)后,打印內(nèi)存堆空間的信息 */
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 /* 片內(nèi) RAM 剩余空間不夠,申請(qǐng)到的是片外 SDRAM 的空間 */
msh >list_memheap    /* 第 4 次申請(qǐng)后,打印內(nèi)存堆空間的信息 */
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 /* 片內(nèi) RAM 剩余空間不夠,申請(qǐng)到的是片外 SDRAM 的空間 */


5.2 直接申請(qǐng)片外 SDRAM 內(nèi)存測(cè)試

??如果想直接從片外的 SDRAM 內(nèi)存空間進(jìn)行申請(qǐng)時(shí),我們可以使用 rt_memheap_alloc 進(jìn)行操作,同樣我們也編寫(xiě)一個(gè)直接從片外 SDRAM 申請(qǐng)空間的測(cè)試函數(shù),如下所示。其中 sdram_heap 控制塊需要和上文對(duì) SDRAM 初始化 rt_memheap_init(&sdram_heap, ...) 時(shí)的控制塊的變量保持一致。

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); // 打印申請(qǐng)到的空間的首地址
   }
   else
   {
       LOG_E("sdram malloc failed");
   }
}
MSH_CMD_EXPORT(sdram_malloc_test, sdram malloc test)

??直接從片外 SDRAM 申請(qǐng)空間的測(cè)試結(jié)果如下,從結(jié)果中可以看出每次申請(qǐng)的都是片外 SDRAM 中的空間,且此時(shí)片內(nèi) RAM 的剩余空間大于要申請(qǐng)的空間的大小。

\ | /
- RT -     Thread Operating System
/ | \     4.0.4 build May 10 2022 20:11:53
2006 - 2021 Copyright by rt-thread team
msh >list_memheap    /* 初始時(shí),打印內(nèi)存堆空間的信息 */
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  /* 申請(qǐng)到的是片外 SDRAM 的空間 */
msh >list_memheap    /* 第 1 次申請(qǐng)后,打印內(nèi)存堆空間的信息 */
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  /* 申請(qǐng)到的是片外 SDRAM 的空間 */
msh >list_memheap    /* 第 2 次申請(qǐng)后,打印內(nèi)存堆空間的信息 */
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  /* 申請(qǐng)到的是片外 SDRAM 的空間 */
msh >list_memheap    /* 第 3 次申請(qǐng)后,打印內(nèi)存堆空間的信息 */
memheap   pool size  max used size available size
-------- ---------- ------------- --------------
sdram    33554432   153720        33400712
heap     190584     7356          183228

6 補(bǔ)充

6.1 為什么 rt_malloc 優(yōu)先申請(qǐng)片內(nèi) RAM 的內(nèi)存

??rt_malloc() 的源碼(rt-thread/src/memheap.c)如下所示。

void *rt_malloc(rt_size_t size)
{
   void *ptr;
   /* try to allocate in system heap */
   ptr = rt_memheap_alloc(&_heap, size);   // 先從 _heap 控制塊中申請(qǐng)內(nèi)存
   if (ptr == RT_NULL)                     // _heap 控制塊申請(qǐng)失敗,查找其他的 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 嘗試從其他的內(nèi)存堆中進(jìn)行申請(qǐng) */
       information = rt_object_get_information(RT_Object_Class_MemHeap);  // 獲取類(lèi)型為內(nèi)存堆的對(duì)象信息
       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); // 獲取結(jié)構(gòu)體的首地址 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 相同則繼續(xù)查找其他控制塊
               continue;
           ptr = rt_memheap_alloc(heap, size); // 找到了其他的內(nèi)存堆,就從該內(nèi)存堆上申請(qǐng)空間
           if (ptr != RT_NULL)
               break;
       }
   }
   ... ... // 省去分析無(wú)關(guān)代碼
   
   return ptr;
}

??分析上述源碼我們可以看到首先調(diào)用了 rt_memheap_alloc(&_heap, size) 從 _heap 控制塊中申請(qǐng)內(nèi)存,如果從 _heap 控制塊中申請(qǐng)失敗的話(huà),就從 memheap_list 鏈表中查找其他的內(nèi)存堆,如果有其他的內(nèi)存堆就從找到的內(nèi)存堆中申請(qǐng)空間,如果沒(méi)有其他的內(nèi)存堆則返回 RT_NULL。

??那么 _heap 是在哪里定義和初始化的呢,繼續(xù)分析源碼我們可以發(fā)現(xiàn),在文件 rt-thread/src/memheap.c 中對(duì) _heap 進(jìn)行了定義和初始化,代碼如下。

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 自動(dòng)初始化](https://club.rt-thread.org/ask/article/4548d81e7237255a.html)的代碼中對(duì)片內(nèi)的內(nèi)存堆進(jìn)行了初始化,代碼如下

void rt_hw_board_init()
{
   rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);  // 初始化內(nèi)部 RAM 的內(nèi)存堆
   
   // #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) 
}

??根據(jù)上面的兩段代碼,分析后我們可以看出 [RT-Thread 自動(dòng)初始化](https://club.rt-thread.org/ask/article/4548d81e7237255a.html)的代碼首先將未使用的片內(nèi) RAM 的空間都當(dāng)做系統(tǒng)的內(nèi)存堆空間進(jìn)行初始化,對(duì)應(yīng)的控制塊的名稱(chēng)為 _heap,所以在使用 rt_malloc 進(jìn)行空間的申請(qǐng)時(shí)會(huì)先申請(qǐng)片內(nèi)的 RAM。
??除此之外分析 rt_malloc 的源碼我們還可以得到申請(qǐng)空間實(shí)際調(diào)用的是 rt_memheap_alloc,該函數(shù)的第一個(gè)參數(shù)決定了是從哪里申請(qǐng)的空間,所以我們可以直接使用該函數(shù)來(lái)確定從哪里來(lái)申請(qǐng)空間。如果我們想有先從片外的 SDRAM 申請(qǐng),然后再?gòu)钠瑑?nèi)的 RAM 申請(qǐng),也可以修改 rt_malloc 的源碼,將 ptr = rt_memheap_alloc(&_heap, size); 的第一個(gè)參數(shù)修改為自己定義的外部 SDRAM 的控制塊的名稱(chēng),對(duì)應(yīng)的將 if (heap == &_heap) 中的 _heap 也修改為自己定義的外部 SDRAM 的控制塊的名稱(chēng)。

7 完整代碼

??在基于芯片創(chuàng)建的工程的技術(shù)上將 drv_sdram.c 的代碼進(jìn)行了部分的修改,修改后的完整代碼如下

#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 申請(qǐng)空間測(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); // 打印申請(qǐng)到的空間的首地址
   }
   else
   {
       LOG_E("malloc failed");
   }
}
MSH_CMD_EXPORT(sdram_malloc_test, sdram malloc test)
/* 從片內(nèi) RAM 和 片外 SDRAM 順序申請(qǐng)測(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); // 打印申請(qǐng)到的空間的首地址
   }
   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


審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • SDRAM
    +關(guān)注

    關(guān)注

    7

    文章

    420

    瀏覽量

    55047
  • RAM
    RAM
    +關(guān)注

    關(guān)注

    8

    文章

    1344

    瀏覽量

    114211
  • 內(nèi)存管理
    +關(guān)注

    關(guān)注

    0

    文章

    167

    瀏覽量

    14099
  • RT-Thread
    +關(guān)注

    關(guān)注

    31

    文章

    1239

    瀏覽量

    39427
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    RT-Thread內(nèi)存管理算法源碼閱讀

    RT-Thread對(duì)于內(nèi)存管理主要有三種方式:小內(nèi)存管理算法、slab管理算法memheap
    的頭像 發(fā)表于 08-10 16:03 ?1313次閱讀
    RT-Thread<b class='flag-5'>內(nèi)存</b><b class='flag-5'>管理算法</b>源碼閱讀

    求助 關(guān)于SDRAM讀寫(xiě)問(wèn)題

    進(jìn)行讀寫(xiě)控制操作。自刷新控制時(shí)間是7.5μs,設(shè)置了自動(dòng)預(yù)充電。當(dāng)前情況:寫(xiě)入情況: 讀出情況: 問(wèn)題:1、這款SDRAM芯片手冊(cè)里只給了一個(gè)133M時(shí)鐘,是指芯片正常工作的最
    發(fā)表于 04-24 10:38

    如何讓lwip+ucosiii的內(nèi)存內(nèi)申請(qǐng)?

    MEM1_ALLOC_TABLE_SIZEMEM1_MAX_SIZE/MEM1_BLOCK_SIZE//內(nèi)存表大小我把之前最大管理內(nèi)存從40K改為20K,如果在
    發(fā)表于 11-03 22:02

    基于嵌入式裸機(jī)或RTOS系統(tǒng)下內(nèi)存管理方法的探究

    嵌入式內(nèi)存管理探究-基于FreeRTOS文章封面本文基于嵌入式裸機(jī)或RTOS系統(tǒng)下內(nèi)存管理方法的探究,灰色方塊為正在使用的內(nèi)存塊,白色為可用
    發(fā)表于 12-17 07:40

    內(nèi)存的基本概念以及操作系統(tǒng)的內(nèi)存管理算法

    本文主要介紹內(nèi)存的基本概念以及操作系統(tǒng)的內(nèi)存管理算法。內(nèi)存的基本概念內(nèi)存是計(jì)算機(jī)系統(tǒng)中除了處理器以外最重要的資源,用于存儲(chǔ)當(dāng)前正在執(zhí)行的程序
    發(fā)表于 01-27 06:08

    RT-Thread系統(tǒng)動(dòng)態(tài)內(nèi)存堆有哪幾種管理算法

    塊所在的 zone 節(jié)點(diǎn),然后把內(nèi)存塊鏈接到 zone 的空閑內(nèi)存塊鏈表中 。3. memheap 管理算法這種管理方法適用于系統(tǒng)中含有多個(gè)
    發(fā)表于 03-31 13:53

    關(guān)于RT-Thread的動(dòng)態(tài)內(nèi)存管理簡(jiǎn)析

    空閑塊地址。(2)內(nèi)存釋放內(nèi)存釋放時(shí),分配器需要找到內(nèi)存塊所在的 zone 節(jié)點(diǎn),然后把內(nèi)存塊鏈接到 zone 的空閑內(nèi)存塊鏈表中 。3.
    發(fā)表于 04-06 17:11

    求解,rtthread內(nèi)存管理部分memheap使用問(wèn)題討論

    的部分是不是直接可以使用memheap接管全部的內(nèi)存管理api?2.內(nèi)存不連續(xù)的如STM32F4這種存在DMA不能使用的CCM內(nèi)存以及外掛S
    發(fā)表于 04-19 09:34

    講解使用memheap內(nèi)存管理算法對(duì)內(nèi)部RAMSDRAM進(jìn)行管理方法

    的讀寫(xiě)測(cè)試??配置完 SDRAM內(nèi)存管理算法后,我們需要將SDRAM 加入到
    發(fā)表于 05-11 14:45

    【原創(chuàng)精選】RT-Thread征文精選技術(shù)文章合集

    :RT-Thread 操作系統(tǒng)簡(jiǎn)介:本專(zhuān)欄對(duì) RT-Thread 的源碼進(jìn)行分析,方便開(kāi)發(fā)者快速了解相關(guān)代碼的設(shè)計(jì)思想。使用memheap內(nèi)存管理算法對(duì)
    發(fā)表于 07-26 14:56

    動(dòng)態(tài)內(nèi)存管理是什么?動(dòng)態(tài)內(nèi)存管理算法有哪幾種

    使用。RT-Thread 系統(tǒng)為了滿(mǎn)足不同的需求,提供了兩套不同的動(dòng)態(tài)內(nèi)存 管理算法,分別是小堆內(nèi)存管理算法和 SLAB 內(nèi)存
    發(fā)表于 08-29 15:23

    請(qǐng)問(wèn)rt-thread的小內(nèi)存管理算法能保證實(shí)時(shí)性嗎?

    請(qǐng)問(wèn)rt-thread的小內(nèi)存管理算法能保證實(shí)時(shí)性嗎?會(huì)不會(huì)產(chǎn)生內(nèi)存碎片?
    發(fā)表于 09-09 14:15

    請(qǐng)問(wèn)rt-thread的小內(nèi)存管理算法能保證實(shí)時(shí)性嗎?

    請(qǐng)問(wèn)rt-thread的小內(nèi)存管理算法能保證實(shí)時(shí)性嗎?會(huì)不會(huì)產(chǎn)生內(nèi)存碎片?
    發(fā)表于 10-31 15:25

    淺析RT-Thread系統(tǒng)添加外部內(nèi)存內(nèi)存管理的操作流程

    內(nèi)存資源時(shí)比較緊張的,因此我們不希望只使用外部SRAM而放棄內(nèi)部RAM。因此我們的RTT需要管理多個(gè)地址不連續(xù)的內(nèi)存堆。好在RTT支持memhea
    發(fā)表于 11-15 16:16

    內(nèi)存的基本概念以及操作系統(tǒng)的內(nèi)存管理算法

    本文主要介紹內(nèi)存的基本概念以及操作系統(tǒng)的內(nèi)存管理算法。
    的頭像 發(fā)表于 08-18 15:52 ?1499次閱讀