由于项目需要用到内存管理进行动态申请和释放,今天又重新学习了一下正点原子的内存管理实验,温习了一下内存管理的实质。首先先上正点原子内存管理的源代码:
malloc.c文件:
#include "./MALLOC/malloc.h"#if !(__ARMCC_VERSION >= 6010050) /* 不是AC6编译器,即使用AC5编译器时 *//* 内存池(32字节对齐) */
static __align(32) uint8_t mem1base[MEM1_MAX_SIZE]; /* 内部SRAM内存池 */
static __align(32) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((at(0X10000000))); /* 内部CCM内存池 */
static __align(32) uint8_t mem3base[MEM3_MAX_SIZE] __attribute__((at(0XC01F4000))); /* 外部SDRAM内存池,前面2M给LTDC用了(1280*800*2) *//* 内存管理表 */
static MT_TYPE mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; /* 内部SRAM内存池MAP */
static MT_TYPE mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X10000000 + MEM2_MAX_SIZE))); /* 内部CCM内存池MAP */
static MT_TYPE mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((at(0XC01F4000 + MEM3_MAX_SIZE))); /* 外部SRAM内存池MAP */#else /* 使用AC6编译器时 *//* 内存池(32字节对齐) */
static __ALIGNED(32) uint8_t mem1base[MEM1_MAX_SIZE]; /* 内部SRAM内存池 */
static __ALIGNED(32) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((section(".bss.ARM.__at_0X10000000"))); /* 内部CCM内存池 */
static __ALIGNED(32) uint8_t mem3base[MEM3_MAX_SIZE] __attribute__((section(".bss.ARM.__at_0XC01F4000"))); /* 外部SDRAM内存池,前面2M给LTDC用了(1280*800*2) *//* 内存管理表 */
static MT_TYPE mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; /* 内部SRAM内存池MAP */
static MT_TYPE mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((section(".bss.ARM.__at_0X1000F000"))); /* 内部CCM内存池MAP */
static MT_TYPE mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((section(".bss.ARM.__at_0XC1E30000"))); /* 外部SRAM内存池MAP */#endif/* 内存管理参数 */
const uint32_t memtblsize[SRAMBANK] = {MEM1_ALLOC_TABLE_SIZE, MEM2_ALLOC_TABLE_SIZE, MEM3_ALLOC_TABLE_SIZE}; /* 内存表大小 */
const uint32_t memblksize[SRAMBANK] = {MEM1_BLOCK_SIZE, MEM2_BLOCK_SIZE, MEM3_BLOCK_SIZE}; /* 内存分块大小 */
const uint32_t memsize[SRAMBANK] = {MEM1_MAX_SIZE, MEM2_MAX_SIZE, MEM3_MAX_SIZE}; /* 内存总大小 *//* 内存管理控制器 */
struct _m_mallco_dev mallco_dev=
{my_mem_init, /* 内存初始化 */my_mem_perused, /* 内存使用率 */mem1base, mem2base, mem3base, /* 内存池 */mem1mapbase, mem2mapbase, mem3mapbase, /* 内存管理状态表 */0, 0, 0, /* 内存管理未就绪 */
};/*** @brief 复制内存* @param *des : 目的地址* @param *src : 源地址* @param n : 需要复制的内存长度(字节为单位)* @retval 无*/
void my_mem_copy(void *des, void *src, uint32_t n)
{ uint8_t *xdes = des;uint8_t *xsrc = src; while (n--)*xdes++ = *xsrc++;
} /*** @brief 设置内存值* @param *s : 内存首地址* @param c : 要设置的值* @param count : 需要设置的内存大小(字节为单位)* @retval 无*/
void my_mem_set(void *s, uint8_t c, uint32_t count)
{ uint8_t *xs = s; while (count--)*xs++ = c;
} /*** @brief 内存管理初始化* @param memx : 所属内存块* @retval 无*/
void my_mem_init(uint8_t memx)
{ my_mem_set(mallco_dev.memmap[memx], 0, memtblsize[memx] * 4); /* 内存状态表数据清零 */mallco_dev.memrdy[memx] = 1; /* 内存管理初始化OK */
}/*** @brief 获取内存使用率* @param memx : 所属内存块* @retval 使用率(扩大了10倍,0~1000,代表0.0%~100.0%)*/
uint16_t my_mem_perused(uint8_t memx)
{ uint32_t used = 0; uint32_t i;for (i = 0; i < memtblsize[memx]; i++) {if (mallco_dev.memmap[memx][i]){used++;}}return (used * 1000) / (memtblsize[memx]);
}/*** @brief 内存分配(内部调用)* @param memx : 所属内存块* @param size : 要分配的内存大小(字节)* @retval 内存偏移地址* @arg 0 ~ 0xFFFFFFFE : 有效的内存偏移地址* @arg 0xFFFFFFFF : 无效的内存偏移地址*/
uint32_t my_mem_malloc(uint8_t memx, uint32_t size)
{ signed long offset = 0; uint32_t nmemb; /* 需要的内存块数 */uint32_t cmemb = 0; /* 连续空内存块数 */uint32_t i;if (!mallco_dev.memrdy[memx]){mallco_dev.init(memx); /* 未初始化,先执行初始化 */}if (size == 0) {return 0XFFFFFFFF; /* 不需要分配 */}nmemb = size / memblksize[memx]; /* 获取需要分配的连续内存块数 */if (size % memblksize[memx]){nmemb++;}for (offset = memtblsize[memx] - 1; offset >= 0; offset--) /* 搜索整个内存控制区 */{if (!mallco_dev.memmap[memx][offset]){cmemb++; /* 连续空内存块数增加 */}else {cmemb = 0; /* 连续内存块清零 */}if (cmemb == nmemb) /* 找到了连续nmemb个空内存块 */{for (i = 0;i < nmemb; i++) /* 标注内存块非空 */{ mallco_dev.memmap[memx][offset + i] = nmemb; }return (offset * memblksize[memx]); /* 返回偏移地址 */}}return 0XFFFFFFFF; /* 未找到符合分配条件的内存块 */
}/*** @brief 释放内存(内部调用)* @param memx : 所属内存块* @param offset : 内存地址偏移* @retval 释放结果* @arg 0, 释放成功;* @arg 1, 释放失败;* @arg 2, 超区域了(失败);*/
uint8_t my_mem_free(uint8_t memx, uint32_t offset)
{int i;if (!mallco_dev.memrdy[memx]) /* 未初始化,先执行初始化 */{mallco_dev.init(memx);return 1; /* 未初始化 */}if (offset < memsize[memx]) /* 偏移在内存池内. */{int index = offset / memblksize[memx]; /* 偏移所在内存块号码 */int nmemb = mallco_dev.memmap[memx][index]; /* 内存块数量 */for (i = 0; i < nmemb; i++) /* 内存块清零 */{mallco_dev.memmap[memx][index + i] = 0;}return 0;}else{return 2; /* 偏移超区了. */}
}/*** @brief 释放内存(外部调用)* @param memx : 所属内存块* @param ptr : 内存首地址* @retval 无*/
void myfree(uint8_t memx, void *ptr)
{uint32_t offset;if (ptr == NULL)return; /* 地址为0. */offset = (uint32_t)ptr - (uint32_t)mallco_dev.membase[memx];my_mem_free(memx, offset); /* 释放内存 */
}/*** @brief 分配内存(外部调用)* @param memx : 所属内存块* @param size : 要分配的内存大小(字节)* @retval 分配到的内存首地址.*/
void *mymalloc(uint8_t memx, uint32_t size)
{uint32_t offset;offset = my_mem_malloc(memx, size);if (offset == 0xFFFFFFFF) /* 申请出错 */{return NULL; /* 返回空(0) */}else /* 申请没问题, 返回首地址 */{return (void *)((uint32_t)mallco_dev.membase[memx] + offset);}
}/*** @brief 重新分配内存(外部调用)* @param memx : 所属内存块* @param *ptr : 旧内存首地址* @param size : 要分配的内存大小(字节)* @retval 新分配到的内存首地址.*/
void *myrealloc(uint8_t memx, void *ptr, uint32_t size)
{uint32_t offset;offset = my_mem_malloc(memx, size);if (offset == 0xFFFFFFFF) /* 申请出错 */{return NULL; /* 返回空(0) */}else /* 申请没问题, 返回首地址 */{my_mem_copy((void *)((uint32_t)mallco_dev.membase[memx] + offset), ptr, size); /* 拷贝旧内存内容到新内存 */myfree(memx, ptr); /* 释放旧内存 */return (void *)((uint32_t)mallco_dev.membase[memx] + offset); /* 返回新内存首地址 */}
}
malloc.h文件:
#ifndef __MALLOC_H
#define __MALLOC_H#include "./SYSTEM/sys/sys.h"#ifndef NULL
#define NULL 0
#endif/* 定义三个内存池 */
#define SRAMIN 0 /* 内部内存池 */
#define SRAMCCM 1 /* CCM内存池(此部分SRAM仅仅CPU可以访问!!!) */
#define SRAMEX 2 /* 外部内存池(SDRAM) *//* 定义内存管理表类型,当外扩SDRAM的时候,必须使用uint32_t类型,否则可以定义成uint16_t,以节省内存占用 */
#define MT_TYPE uint32_t#define SRAMBANK 3 /* 定义支持的SRAM块数. *//* mem1内存参数设定.mem1完全处于内部SRAM里面. */
#define MEM1_BLOCK_SIZE 64 /* 内存块大小为64字节 */
#define MEM1_MAX_SIZE 160 * 1024 /* 最大管理内存 160K */
#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE / MEM1_BLOCK_SIZE /* 内存表大小 *//* mem2内存参数设定.mem2处于CCM,用于管理CCM(特别注意,这部分SRAM,仅CPU可以访问!!) */
#define MEM2_BLOCK_SIZE 64 /* 内存块大小为64字节 */
#define MEM2_MAX_SIZE 60 * 1024 /* 最大管理内存60K */
#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE / MEM2_BLOCK_SIZE /* 内存表大小 *//* mem3内存参数设定.mem3的内存池处于外部SDRAM里面 */
#define MEM3_BLOCK_SIZE 64 /* 内存块大小为64字节 */
#define MEM3_MAX_SIZE 28912 * 1024 /* 最大管理内存28912K */
#define MEM3_ALLOC_TABLE_SIZE MEM3_MAX_SIZE / MEM3_BLOCK_SIZE /* 内存表大小 *//* 内存管理控制器 */
struct _m_mallco_dev
{void (*init)(uint8_t); /* 初始化 */uint16_t (*perused)(uint8_t); /* 内存使用率 */uint8_t *membase[SRAMBANK]; /* 内存池 管理SRAMBANK个区域的内存 */uint32_t *memmap[SRAMBANK]; /* 内存管理状态表 */uint8_t memrdy[SRAMBANK]; /* 内存管理是否就绪 */
};extern struct _m_mallco_dev mallco_dev; /* 在mallco.c里面定义 *//******************************************************************************************/void my_mem_set(void *s, uint8_t c, uint32_t count); /* 设置内存 */
void my_mem_copy(void *des, void *src, uint32_t n); /* 复制内存 */
void my_mem_init(uint8_t memx); /* 内存管理初始化函数(外/内部调用) */
uint32_t my_mem_malloc(uint8_t memx, uint32_t size); /* 内存分配(内部调用) */
uint8_t my_mem_free(uint8_t memx, uint32_t offset); /* 内存释放(内部调用) */
uint16_t my_mem_perused(uint8_t memx) ; /* 获得内存使用率(外/内部调用) *//* 用户调用函数 */
void myfree(uint8_t memx, void *ptr); /* 内存释放(外部调用) */
void *mymalloc(uint8_t memx, uint32_t size); /* 内存分配(外部调用) */
void *myrealloc(uint8_t memx, void *ptr, uint32_t size); /* 重新分配内存(外部调用) */#endif
对于单个内存池来说在移植的时候只需要修改以下内容:
malloc.h文件中的:内存块大小宏定义(MEMx_BLOCK_SIZE)、内存池大小宏定义(MEMx_MAX_SIZE)、内存管理表单个元素的大小(MT_TYPE),通过这三个参即可得到内存管理表元素的个数。
malloc.c文件中需要修改的是:内存池的对其方式[__align(32)]、内存池的指定地址[__attribute__((at(0XC0000000)))],修改了这两个参数就能保证内存池的定义不会出错。
对于内存管理的移植除了需要修改单个内存池的定义信息外还需要修改内存池的数量(SRAMBANK)、内存池的编号(#define SRAMIN 0 ;#define SRAMCCM 1 ;#define SRAMEX 2 ),同时还需要修改内存管理控制器结构体的参数(mallco_dev)。
从上面可以看出正点原子这份代码是基于“保证内存池的大小”的角度进行定义的,换句话说我是指定了了内存池的大小,但是没有指定整个内存池和内存管理表的大小,所以可以换一种方式进行定义:指定单个内存池全部的大小,然后自己程序自己去计算block的数量,这样就可以保证单个内存池所占用的内存不会超过指定的大小,修改如下:
/* mem1内存参数设定.mem1完全处于内部SRAM里面. */
#define MEM1_TOTAL_SIZE 64*1024 /* 内部SRAM大小为64K字节 */
#define MEM1_BLOCK_SIZE 64 /* 内存块大小为64字节 */
#define MEM1_ALLOC_TABLE_NUM MEM1_TOTAL_SIZE/(MEM1_BLOCK_SIZE+sizeof(MT_TYPE)) /* block的数量=内存管理表的数量*/
#define MEM1_MAX_SIZE MEM1_ALLOC_TABLE_NUM*MEM1_BLOCK_SIZE /* 内存池的大小为:block的大小*数量 *//* mem2内存参数设定.mem2处于CCM,用于管理CCM(特别注意,这部分SRAM,仅CPU可以访问!!) */
#define MEM2_TOTAL_SIZE 64*1024 /* CCM大小为64K字节 */
#define MEM2_BLOCK_SIZE 64 /* 内存块大小为64字节 */
#define MEM2_ALLOC_TABLE_NUM MEM2_TOTAL_SIZE/(MEM2_BLOCK_SIZE+sizeof(MT_TYPE)) /* block的数量=内存管理表的数量*/
#define MEM2_MAX_SIZE MEM2_ALLOC_TABLE_NUM*MEM2_BLOCK_SIZE /* 内存池的大小为:block的大小*数量 *//* mem3内存参数设定.mem3的内存池处于外部SDRAM里面 */
#define MEM3_TOTAL_SIZE 32*1024*1024 /* SDRAM大小为32M字节 */
#define MEM3_BLOCK_SIZE 64 /* 内存块大小为64字节 */
#define MEM3_ALLOC_TABLE_NUM MEM3_TOTAL_SIZE/(MEM3_BLOCK_SIZE+sizeof(MT_TYPE)) /* block的数量=内存管理表的数量*/
#define MEM3_MAX_SIZE MEM3_ALLOC_TABLE_NUM*MEM3_BLOCK_SIZE /* 内存池的大小为:block的大小*数量 */
最后再附上单独管理SDRAM的测试代码:
malloc.c源代码#include "malloc.h"/* 内存池(32字节对齐) */
static __align(32) uint8_t mem3base[MEM3_MAX_SIZE] __attribute__((at(0XC0000000))); /* 外部SDRAM内存池 SDRAM起始地址:0XC0000000*//* 内存管理表 */
static MT_TYPE mem3mapbase[MEM3_ALLOC_TABLE_NUM] __attribute__((at(0XC0000000 + MEM3_MAX_SIZE))); /* 外部SRAM内存池MAP *//* 内存管理参数 */
const uint32_t memtblsize[SRAMBANK] = {MEM3_ALLOC_TABLE_NUM}; /* 内存表数量 */
const uint32_t memblksize[SRAMBANK] = {MEM3_BLOCK_SIZE}; /* 单个block大小 */
const uint32_t memsize[SRAMBANK] = {MEM3_MAX_SIZE}; /* 每个内存池的大小 *//* 内存管理控制器 */
struct _m_mallco_dev mallco_dev=
{my_mem_init, /* 内存初始化 */my_mem_perused, /* 内存使用率 */mem3base, /* 内存池 */mem3mapbase, /* 内存管理状态表 */0, /* 内存管理未就绪 */
};/*** @brief 复制内存* @param *des : 目的地址* @param *src : 源地址* @param n : 需要复制的内存长度(字节为单位)* @retval 无*/
void my_mem_copy(void *des, void *src, uint32_t n)
{ uint8_t *xdes = des;uint8_t *xsrc = src; while (n--)*xdes++ = *xsrc++;
} /*** @brief 设置内存值* @param *s : 内存首地址* @param c : 要设置的值* @param count : 需要设置的内存大小(字节为单位)* @retval 无*/
void my_mem_set(void *s, uint8_t c, uint32_t count)
{ uint8_t *xs = s; while (count--)*xs++ = c;
} /*** @brief 内存管理初始化* @param memx : 所属内存块* @retval 无*/
void my_mem_init(uint8_t memx)
{ my_mem_set(mallco_dev.memmap[memx], 0, memtblsize[memx] * 4); /* 内存状态表数据清零 */mallco_dev.memrdy[memx] = 1; /* 内存管理初始化OK */
}/*** @brief 获取内存使用率* @param memx : 所属内存块* @retval 使用率(扩大了10倍,0~1000,代表0.0%~100.0%)*/
uint16_t my_mem_perused(uint8_t memx)
{ uint32_t used = 0; uint32_t i;for (i = 0; i < memtblsize[memx]; i++) {if (mallco_dev.memmap[memx][i]){used++;}}return (used * 1000) / (memtblsize[memx]);
}/*** @brief 内存分配(内部调用)* @param memx : 所属内存块* @param size : 要分配的内存大小(字节)* @retval 内存偏移地址* @arg 0 ~ 0xFFFFFFFE : 有效的内存偏移地址* @arg 0xFFFFFFFF : 无效的内存偏移地址*/
uint32_t my_mem_malloc(uint8_t memx, uint32_t size)
{ signed long offset = 0; uint32_t nmemb; /* 需要的内存块数 */uint32_t cmemb = 0; /* 连续空内存块数 */uint32_t i;if (!mallco_dev.memrdy[memx]){mallco_dev.init(memx); /* 未初始化,先执行初始化 */}if (size == 0) {return 0XFFFFFFFF; /* 不需要分配 */}nmemb = size / memblksize[memx]; /* 获取需要分配的连续内存块数 */if (size % memblksize[memx]){nmemb++;}for (offset = memtblsize[memx] - 1; offset >= 0; offset--) /* 搜索整个内存控制区 */{if (!mallco_dev.memmap[memx][offset]){cmemb++; /* 连续空内存块数增加 */}else {cmemb = 0; /* 连续内存块清零 */}if (cmemb == nmemb) /* 找到了连续nmemb个空内存块 */{for (i = 0;i < nmemb; i++) /* 标注内存块非空 */{ mallco_dev.memmap[memx][offset + i] = nmemb; }return (offset * memblksize[memx]); /* 返回偏移地址 */}}return 0XFFFFFFFF; /* 未找到符合分配条件的内存块 */
}/*** @brief 释放内存(内部调用)* @param memx : 所属内存块* @param offset : 内存地址偏移* @retval 释放结果* @arg 0, 释放成功;* @arg 1, 释放失败;* @arg 2, 超区域了(失败);*/
uint8_t my_mem_free(uint8_t memx, uint32_t offset)
{int i;if (!mallco_dev.memrdy[memx]) /* 未初始化,先执行初始化 */{mallco_dev.init(memx);return 1; /* 未初始化 */}if (offset < memsize[memx]) /* 偏移在内存池内. */{int index = offset / memblksize[memx]; /* 偏移所在内存块号码 */int nmemb = mallco_dev.memmap[memx][index]; /* 内存块数量 */for (i = 0; i < nmemb; i++) /* 内存块清零 */{mallco_dev.memmap[memx][index + i] = 0;}return 0;}else{return 2; /* 偏移超区了. */}
}/*** @brief 释放内存(外部调用)* @param memx : 所属内存块* @param ptr : 内存首地址* @retval 无*/
void myfree(uint8_t memx, void *ptr)
{uint32_t offset;if (ptr == NULL)return; /* 地址为0. */offset = (uint32_t)ptr - (uint32_t)mallco_dev.membase[memx];my_mem_free(memx, offset); /* 释放内存 */
}/*** @brief 分配内存(外部调用)* @param memx : 所属内存块* @param size : 要分配的内存大小(字节)* @retval 分配到的内存首地址.*/
void *mymalloc(uint8_t memx, uint32_t size)
{uint32_t offset;offset = my_mem_malloc(memx, size);if (offset == 0xFFFFFFFF) /* 申请出错 */{return NULL; /* 返回空(0) */}else /* 申请没问题, 返回首地址 */{return (void *)((uint32_t)mallco_dev.membase[memx] + offset);}
}/*** @brief 重新分配内存(外部调用)* @param memx : 所属内存块* @param *ptr : 旧内存首地址* @param size : 要分配的内存大小(字节)* @retval 新分配到的内存首地址.*/
void *myrealloc(uint8_t memx, void *ptr, uint32_t size)
{uint32_t offset;offset = my_mem_malloc(memx, size);if (offset == 0xFFFFFFFF) /* 申请出错 */{return NULL; /* 返回空(0) */}else /* 申请没问题, 返回首地址 */{my_mem_copy((void *)((uint32_t)mallco_dev.membase[memx] + offset), ptr, size); /* 拷贝旧内存内容到新内存 */myfree(memx, ptr); /* 释放旧内存 */return (void *)((uint32_t)mallco_dev.membase[memx] + offset); /* 返回新内存首地址 */}
}malloc.h源代码#ifndef __MALLOC_H
#define __MALLOC_H#include "main.h"#ifndef NULL
#define NULL 0
#endif/* 定义三个内存池 */
#define SRAMEX 0 /* 外部内存池(SDRAM) *//* 定义内存管理表类型,当外扩SDRAM的时候,必须使用uint32_t类型,否则可以定义成uint16_t,以节省内存占用 */
#define MT_TYPE uint32_t#define SRAMBANK 1 /* 定义支持的SRAM块数. */#define MEM3_TOTAL_SIZE 32*1024*1024 /* SDRAM大小为32M字节 */
#define MEM3_BLOCK_SIZE 64 /* 内存块大小为64字节 */
#define MEM3_ALLOC_TABLE_NUM MEM3_TOTAL_SIZE/(MEM3_BLOCK_SIZE+sizeof(MT_TYPE)) /* block的数量=内存管理表的数量*/
#define MEM3_MAX_SIZE MEM3_ALLOC_TABLE_NUM*MEM3_BLOCK_SIZE /* 内存池的大小为:block的大小*数量 *//* 内存管理控制器 */
struct _m_mallco_dev
{void (*init)(uint8_t); /* 初始化 */uint16_t (*perused)(uint8_t); /* 内存使用率 */uint8_t *membase[SRAMBANK]; /* 内存池 管理SRAMBANK个区域的内存 */uint32_t *memmap[SRAMBANK]; /* 内存管理状态表 */uint8_t memrdy[SRAMBANK]; /* 内存管理是否就绪 */
};extern struct _m_mallco_dev mallco_dev; /* 在mallco.c里面定义 *//******************************************************************************************/void my_mem_set(void *s, uint8_t c, uint32_t count); /* 设置内存 */
void my_mem_copy(void *des, void *src, uint32_t n); /* 复制内存 */
void my_mem_init(uint8_t memx); /* 内存管理初始化函数(外/内部调用) */
uint32_t my_mem_malloc(uint8_t memx, uint32_t size); /* 内存分配(内部调用) */
uint8_t my_mem_free(uint8_t memx, uint32_t offset); /* 内存释放(内部调用) */
uint16_t my_mem_perused(uint8_t memx) ; /* 获得内存使用率(外/内部调用) *//* 用户调用函数 */
void myfree(uint8_t memx, void *ptr); /* 内存释放(外部调用) */
void *mymalloc(uint8_t memx, uint32_t size); /* 内存分配(外部调用) */
void *myrealloc(uint8_t memx, void *ptr, uint32_t size); /* 重新分配内存(外部调用) */#endifmain函数添加测试代码char *str = 0;
mallco_dev.init(SRAMEX);
str = mymalloc(SRAMEX, 50);
if(str)
{sprintf(str, "012345678900123456789001234567890");HAL_Delay(10);//这里因为SDRAM的读写需要时间所以一定需要插入一点延时,不然串口无打印信息printf("%s\n",str);myfree(SRAMEX,str);
}
测试结果如下: