??在開發中由于單片機自帶的 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 鏈表,嘗試從其他的內存堆上分配內存塊。應用程序不用關心當前分配的內存塊位于哪個內存堆上,就像是在操作一個內存堆。
2 只使用片內 RAM 的示例
平時用的就是從片內申請內存,寫這部分主要是為了和從片外 SDRAN 申請內存進行對比。選擇以開發板創建工程后,選擇 STM32F429-ATK-APOLLO 開發板,工程創建后默認是沒有開啟片外的 SDRAM 的,此時工程中只配置了片內的 RAM 作為內存堆,我們編寫一個 sram 內存堆的申請測試函數進行內存堆的測試,測試代碼如下所示(示例僅作為測試使用,目的是了解原理,均沒有寫內存堆的釋放函數,下面的測試函數一樣)。
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 的剩余空間大小不夠導致申請失敗。
\ | /
- 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)
??配置好了片外的 SDRAM 后,我們還需要選擇相應的內存管理算法,同樣在 RT-Thread Settings 里面進行配置,配置界面如下圖所示。
4 SDRAM 的讀寫測試
??配置完 SDRAM 和內存管理算法后,我們需要將片外的 SDRAM 加入到 memheap_item 鏈表中進行管理,添加的方法如下:
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。
#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 的讀寫沒有問題后我們應該重啟開發板進行內存的申請測試。
\ | /
- 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 管理的內存堆進行測試,測試代碼如下
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)上申請空間。
\ | /
- 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, ...) 時的控制塊的變量保持一致。
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 的剩余空間大于要申請的空間的大小。
\ | /
- 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)如下所示。
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 進行了定義和初始化,代碼如下。
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)的代碼中對片內的內存堆進行了初始化,代碼如下
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 的代碼進行了部分的修改,修改后的完整代碼如下
#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
審核編輯:湯梓紅
-
SDRAM
+關注
關注
7文章
422瀏覽量
55161 -
RAM
+關注
關注
8文章
1367瀏覽量
114545 -
內存管理
+關注
關注
0文章
168瀏覽量
14128 -
RT-Thread
+關注
關注
31文章
1274瀏覽量
39944
發布評論請先 登錄
相關推薦
評論