单片机内存实验
单片机 :STM32F407
开发板:DMF407电机开发板
平台:keil V5.31
HSE 为8MHZ
HSI为16MHZ
一、flash模拟EEPROM实验
主函数:
/* 要写入到STM32 FLASH的字符串数组 */ const uint8_t g_text_buf[] = {"If you were a teardrop in my eye,\ For fear of losing you,I would never cry.\ And if the golden sun,Should cease to shine its light,\ Just one smile from you,Would make my whole world bright."}; #define TEXT_LENTH sizeof(g_text_buf) /* 数组长度 */ /*SIZE表示半字长(4字节), 大小必须是4的整数倍, 如果不是的话, 强制对齐到2的整数倍 */ #define SIZE TEXT_LENTH / 4 + ((TEXT_LENTH % 4) ? 1 : 0) #define FLASH_SAVE_ADDR 0x08010000 /* 设置FLASH 保存地址(必须为4的整数倍,且大于本代码所占用FLASH的大小 + 0x08000000) */ int main(void) { uint8_t key = 0; uint16_t i = 0; uint8_t datatemp[SIZE]; HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */ delay_init(168); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ usmart_dev.init(84); /* 初始化USMART */ led_init(); /* 初始化LED */ lcd_init(); /* 初始化LCD */ key_init(); /* 初始化按键 */ lcd_show_string(30, 50, 200, 16, 16, "STM32", RED); lcd_show_string(30, 70, 200, 16, 16, "FLASH EEPROM TEST", RED); lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED); lcd_show_string(30, 110, 200, 16, 16, "KEY1:Write KEY0:Read", RED); while (1) { key = key_scan(0); if (key == KEY1_PRES) /* KEY1按下,写入STM32 FLASH */ { lcd_fill(0, 150, 239, 319, WHITE); /* 清除半屏 */ lcd_show_string(30, 150, 200, 16, 16, "Start Write FLASH....", RED); stmflash_write(FLASH_SAVE_ADDR, (uint32_t *)g_text_buf, SIZE); lcd_show_string(30, 150, 200, 16, 16, "FLASH Write Finished!", RED); /* 提示传送完成 */ } if (key == KEY0_PRES) /* KEY0按下,读取字符串并显示 */ { lcd_show_string(30, 150, 200, 16, 16, "Start Read FLASH.... ", RED); stmflash_read(FLASH_SAVE_ADDR, (uint32_t *)datatemp, SIZE); lcd_show_string(30, 150, 200, 16, 16, "The Data Readed Is: ", RED); /* 提示传送完成 */ // datatemp[SIZE-1]='0x00'; printf("poem:%s",(char *)datatemp); lcd_show_string(30, 170, 33*8, 16, 16, (char *)datatemp, BLUE); /* 显示读到的字符串 */ lcd_show_string(30, 190, 23*8, 16, 16, (char *)(datatemp+33), BLUE); /* 显示读到的字符串 */ lcd_show_string(30, 210, 18*8, 16, 16, (char *)(datatemp+33+23), BLUE); /* 显示读到的字符串 */ lcd_show_string(30, 230, 22*8, 16, 16, (char *)(datatemp+33+23+18), BLUE); /* 显示读到的字符串 */ lcd_show_string(30, 250, 32*8, 16, 16, (char *)(datatemp+33+23+18+22), BLUE); /* 显示读到的字符串 */ lcd_show_string(30, 270, 24*8, 16, 16, (char *)(datatemp+33+23+18+22+32), BLUE); /* 显示读到的字符串 */ lcd_show_string(30, 290, 33*8, 16, 16, (char *)(datatemp+33+23+18+22+32+24), BLUE); /* 显示读到的字符串 */ } i++; delay_ms(10); if (i == 20) { LED0_TOGGLE(); /* 提示系统正在运行 */ i = 0; } } }写flash
void stmflash_write(uint32_t waddr, uint32_t *pbuf, uint32_t length) { uint8_t status = 0; uint32_t addrx = 0; uint32_t endaddr = 0; if (waddr < STM32_FLASH_BASE || waddr % 4 || /* 写入地址小于 STM32_FLASH_BASE, 或不是4的整数倍, 非法. */ waddr > (STM32_FLASH_BASE + STM32_FLASH_SIZE)) /* 写入地址大于 STM32_FLASH_BASE + STM32_FLASH_SIZE, 非法. */ { return; } HAL_FLASH_Unlock(); /* 解锁 */ FLASH->ACR &= ~(1 << 10); /* FLASH擦除期间,必须禁止数据缓存!!! */ addrx = waddr; /* 写入的起始地址 */ endaddr = waddr + length * 4; /* 写入的结束地址 */ if (addrx < 0X1FFF0000) /* 只有主存储区,才需要执行擦除操作!! */ { while (addrx < endaddr) /* 扫清一切障碍.(对非FFFFFFFF的地方,先擦除) */ { if (stmflash_read_word(addrx) != 0XFFFFFFFF) /* 有非0XFFFFFFFF的地方,要擦除这个扇区 */ { status = stmflash_erase_sector(stmflash_get_flash_sector(addrx)); if (status)break; /* 发生错误了 */ } else { addrx += 4; } } } if (status == 0) { while (waddr < endaddr) /* 写数据 */ { if (stmflash_write_word(waddr, *pbuf)) /* 写入数据 */ { break; /* 写入异常 */ } waddr += 4; pbuf++; } } FLASH->ACR |= 1 << 10; /* FLASH擦除结束,开启数据fetch */ HAL_FLASH_Lock(); /* 上锁 */ } static uint8_t stmflash_write_word(uint32_t faddr, uint32_t data) { uint8_t res; res = stmflash_wait_done(0XFFFFF); if (res == 0) /* OK */ { FLASH->CR &= ~(3 << 8); /* 清除PSIZE原来的设置 */ FLASH->CR |= 2 << 8; /* 设置为32bit宽,确保VCC=2.7~3.6V之间!! */ FLASH->CR |= 1 << 0; /* 编程使能 */ *(volatile uint32_t *)faddr = data; /* 写入数据 */ res = stmflash_wait_done(0XFFFFF); /* 等待操作完成,一个字编程 */ if (res != 1) /* 操作成功 */ { FLASH->CR &= ~(1 << 0); /* 清除PG位 */ } } return res; }读flash
void stmflash_read(uint32_t raddr, uint32_t *pbuf, uint32_t length) { uint32_t i; for (i = 0; i < length; i++) { pbuf[i] = stmflash_read_word(raddr); /* 读取4个字节. */ raddr += 4; /* 偏移4个字节. */ } } uint32_t stmflash_read_word(uint32_t faddr) { return *(volatile uint32_t *)faddr; }擦除flash
static uint8_t stmflash_erase_sector(uint32_t saddr) { uint8_t res = 0; res = stmflash_wait_done(0XFFFFFFFF); /* 等待上次操作结束 */ if (res == 0) { FLASH->CR &= ~(3 << 8); /* 清除PSIZE原来的设置 */ FLASH->CR |= 2 << 8; /* 设置为32bit宽,确保VCC=2.7~3.6V之间!! */ FLASH->CR &= ~(0X1F << 3); /* 清除原来的设置 */ FLASH->CR |= saddr << 3; /* 设置要擦除的扇区 */ FLASH->CR |= 1 << 1; /* 扇区擦除 */ FLASH->CR |= 1 << 16; /* 开始擦除 */ res = stmflash_wait_done(0XFFFFFFFF); /* 等待操作结束 */ if (res != 1) /* 非忙 */ { FLASH->CR &= ~(1 << 1); /* 清除扇区擦除标志 */ } } return res; }测试结果:
二、内存管理实验
主函数:
const char *SRAM_NAME_BUF[SRAMBANK] = {" SRAMIN ", " SRAMCCM "}; int main(void) { uint8_t paddr[20]; /* 存放P Addr:+p地址的ASCII值 */ uint16_t memused = 0; uint8_t key; uint8_t i = 0; uint8_t *p = 0; uint8_t *tp = 0; uint8_t sramx = 0; /* 默认为内部sram */ HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */ delay_init(168); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ usmart_dev.init(84); /* 初始化USMART */ led_init(); /* 初始化LED */ lcd_init(); /* 初始化LCD */ key_init(); /* 初始化按键 */ my_mem_init(SRAMIN); /* 初始化内部SRAM内存池 */ my_mem_init(SRAMCCM); /* 初始化内部SRAMCCM内存池 */ lcd_show_string(30, 50, 200, 16, 16, "STM32", RED); lcd_show_string(30, 70, 200, 16, 16, "MALLOC TEST", RED); lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED); lcd_show_string(30, 110, 200, 16, 16, "KEY0:Malloc & WR & Show", RED); lcd_show_string(30, 130, 200, 16, 16, "KEY2:SRAMx KEY1:Free", RED); lcd_show_string(60, 160, 200, 16, 16, " SRAMIN ", BLUE); lcd_show_string(30, 176, 200, 16, 16, "SRAMIN USED:", BLUE); lcd_show_string(30, 192, 200, 16, 16, "SRAMCCM USED:", BLUE); while (1) { key = key_scan(0); /* 不支持连按 */ switch (key) { case KEY0_PRES: /* KEY0按下 */ p = mymalloc(sramx, 2048); /* 申请2K字节,并写入内容,显示在lcd屏幕上面 */ if (p != NULL) { sprintf((char *)p, "Memory Malloc Test%03d", i); /* 向p写入一些内容 */ lcd_show_string(30, 260, 209, 16, 16, (char *)p, BLUE); /* 显示P的内容 */ } break; case KEY1_PRES: /* KEY1按下 */ myfree(sramx, p); /* 释放内存 */ p = 0; /* 指向空地址 */ break; case KEY2_PRES: /* KEY2按下 */ sramx++; if (sramx > 1)sramx = 0; lcd_show_string(60, 160, 200, 16, 16, (char *)SRAM_NAME_BUF[sramx], BLUE); break; } if (tp != p) { tp = p; sprintf((char *)paddr, "P Addr:0x%08X", (uint32_t)tp); lcd_show_string(30, 240, 209, 16, 16, (char *)paddr, BLUE); /* 显示p的地址 */ if (p) { lcd_show_string(30, 260, 280, 16, 16, (char *)p, BLUE); /* 显示p的内容 */ } else { lcd_fill(30, 260, 209, 296, WHITE); /* p=0,清除显示 */ } } delay_ms(10); i++; if ((i % 20) == 0) /* DS0闪烁. */ { memused = my_mem_perused(SRAMIN); sprintf((char *)paddr, "%d.%01d%%", memused / 10, memused % 10); lcd_show_string(30 + 112, 176, 200, 16, 16, (char *)paddr, BLUE); /* 显示内部内存使用率 */ memused = my_mem_perused(SRAMCCM); sprintf((char *)paddr, "%d.%01d%%", memused / 10, memused % 10); lcd_show_string(30 + 112, 192, 200, 16, 16, (char *)paddr, BLUE); /* 显示TCM内存使用率 */ LED0_TOGGLE(); /* LED0闪烁 */ } } }Program Size: Code=29246 RO-data=12982 RW-data=396 ZI-data=175636
1、增加一个数组:
uint8_t mytemparr[256];Program Size: Code=29246 RO-data=12982 RW-data=396 ZI-data=175636
2、给数组初始化
uint8_t mytemparr[256]={0};Program Size: Code=29246 RO-data=12982 RW-data=396 ZI-data=175636
3、使用数组:
mytemparr[0]=1;Program Size: Code=29258 RO-data=12982 RW-data=396 ZI-data=175892
4、使用数组:
mytemparr[0]=1; mytemparr[1]=2;Program Size: Code=29262 RO-data=12982 RW-data=396 ZI-data=175892
5、修改为更大的数组:
uint8_t mytemparr[512]={0};Program Size: Code=29262 RO-data=12982 RW-data=396 ZI-data=176148
6、修改为更大的数组:
uint8_t mytemparr[1024]={0};Program Size: Code=29262 RO-data=12982 RW-data=396 ZI-data=176660
7、修改为更大的数组:
uint8_t mytemparr[2048]={0};Program Size: Code=29262 RO-data=12982 RW-data=396 ZI-data=177684
8、修改为更大的数组:
uint8_t mytemparr[4096]={0};Program Size: Code=29262 RO-data=12982 RW-data=396 ZI-data=179732
9、修改为更大的数组:
uint8_t mytemparr[8192]={0};Program Size: Code=29262 RO-data=12982 RW-data=396 ZI-data=183828
10、修改为更大的数组:
uint8_t mytemparr[16384]={0};Program Size: Code=29262 RO-data=12982 RW-data=396 ZI-data=192020
11、修改为更大的数组:
uint8_t mytemparr[32768]={0};..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching main.o(.bss).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching startup_stm32f407xx.o(STACK).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching usmart_config.o(.data).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching usart.o(.bss).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching libspace.o(.bss).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching lcd.o(.bss).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching usmart_port.o(.bss).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching usmart.o(.data).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching malloc.o(.data).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching stm32f4xx_hal.o(.data).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching main.o(.data).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching lcd.o(.data).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching usart.o(.data).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching system_stm32f4xx.o(.data).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching delay.o(.data).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching key.o(.data).
..\..\Output\atk_f407.axf: Error: L6406E: No space in execution regions with .ANY selector matching startup_stm32f407xx.o(HEAP).
..\..\Output\atk_f407.axf: Error: L6407E: Sections of aggregate size 0x87a0 bytes could not fit into .ANY selector(s).
12、数组改小一点
uint8_t mytemparr[16384+2048+1024+512+256+64+32]={0};..\..\Output\atk_f407.axf: Error: L6220E: Execution region RW_IRAM1 size (131104 bytes) exceeds limit (131072 bytes). Region contains 41 bytes of padding and 0 bytes of veneers (total 41 bytes of linker generated content).
13、数组改小一点:
uint8_t mytemparr[16384+2048+1024+512+256+64+1]={0};..\..\Output\atk_f407.axf: Error: L6220E: Execution region RW_IRAM1 size (131104 bytes) exceeds limit (131072 bytes). Region contains 72 bytes of padding and 0 bytes of veneers (total 72 bytes of linker generated content).
14、数组最大值:
uint8_t mytemparr[16384+2048+1024+512+256+64]={0};Program Size: Code=29262 RO-data=12982 RW-data=396 ZI-data=195924
分析map文件
是malloc分配太多了。
/* mem1内存参数设定.mem1完全处于内部SRAM里面 */ #define MEM1_BLOCK_SIZE 32 /* 内存块大小为32字节 */ #define MEM1_MAX_SIZE 100 * 1024 /* 最大管理内存 100K */ #define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE /* 内存表大小 */ /* mem2内存参数设定.mem2处于CCM,用于管理CCM(特别注意,这部分SRAM,仅CPU可以访问!!) */ #define MEM2_BLOCK_SIZE 32 /* 内存块大小为32字节 */ #define MEM2_MAX_SIZE 60 * 1024 /* 最大管理内存 60K */ #define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE /* 内存表大小 */ /* 内存管理参数 */ const uint32_t memtblsize[SRAMBANK]= { MEM1_ALLOC_TABLE_SIZE, MEM2_ALLOC_TABLE_SIZE }; /* 内存表大小 */ const uint32_t memblksize[SRAMBANK]= { MEM1_BLOCK_SIZE, MEM2_BLOCK_SIZE }; /* 内存分块大小 */ const uint32_t memsize[SRAMBANK]= { MEM1_MAX_SIZE, MEM2_MAX_SIZE }; /* 内存总大小 */去掉这些后编译:
Program Size: Code=11842 RO-data=770 RW-data=348 ZI-data=87028
15、继续加大数组:
uint8_t mytemparr[65536+32768+16384+8192+4096+2048+128+64+32+16]={0};Program Size: Code=11842 RO-data=770 RW-data=348 ZI-data=196004
查询map文件:
继续去掉
/* 内存池(64字节对齐) */ //static __align(64) uint8_t mem1base[MEM1_MAX_SIZE]; /* 内部SRAM内存池 */ //static __align(64) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((at(0x10000000))); /* 内部CCM内存池 */ ///* 内存管理表 */ //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 */Program Size: Code=11842 RO-data=770 RW-data=348 ZI-data=130724
16、继续加大数组:
uint8_t mytemparr[112*1024+14*1024]={0};Program Size: Code=11842 RO-data=770 RW-data=348 ZI-data=130484
17、CCM RAM怎么使用?
STM32F407 的 CCM RAM (Core Coupled Memory) 是一块 64KB 的特殊高速内存,其核心优势是零等待周期且不与 DMA 共享总线。正确使用 CCM RAM 可以显著提升实时控制、中断响应和复杂算法的执行效率。
- 极速访问:直接连接 Cortex-M4 内核的数据总线(D-Bus),CPU 访问无需经过总线矩阵仲裁,无等待状态(0 WS)。在 168MHz 主频下,随机访问速度比普通 SRAM 快约 15%-30%。
- 独占性:仅 CPU 可访问。DMA 控制器、以太网、USB 等外设无法直接读写 CCM RAM。
- 地址范围:
0x10000000至0x1000FFFF(共 64KB)。 - 初始化注意:CCM RAM 中的数据在复位后是不确定的。如果存放已初始化的全局变量,必须在启动代码中手动将初始值从 Flash 拷贝到 CCM RAM(普通 SRAM 的
.data段通常由标准启动文件自动处理,但 CCM 需要额外配置)
第一步:修改链接脚本 (.ld 文件)
需要在链接脚本中定义 CCMRAM 内存区域,并创建一个新的段(section)。
/* 1. 定义内存区域 */ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K /* SRAM1 + SRAM2 */ CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K /* CCM RAM */ } /* 2. 在 SECTIONS 中添加 .ccmram 段 */ .ccmram : { . = ALIGN(4); _sccmram = .; /* CCM RAM 起始地址符号 */ *(.ccmram) /* 所有标记为 .ccmram 的变量 */ *(.ccmram*) . = ALIGN(4); _eccmram = .; /* CCM RAM 结束地址符号 */ } >CCMRAM AT> FLASH /* 运行时在 CCM,加载地址在 Flash */ /* 3. 记录加载地址(用于启动代码拷贝初始化数据) */ _sidata_ccm = LOADADDR(.ccmram);第二步:修改启动文件 (startup_stm32f4xx.s)
标准启动文件只拷贝.data到 SRAM。你需要添加代码,在复位后将.ccmram段的初始数据从 Flash 拷贝到 CCM RAM。
在Reset_Handler中,原有的数据拷贝循环后,添加如下逻辑(汇编或 C 调用):
/* 示例:在汇编启动文件中添加 */ ldr r0, =_sccmram /* CCM RAM 目标起始地址 */ ldr r1, =_eccmram /* CCM RAM 目标结束地址 */ ldr r2, =_sidata_ccm /* Flash 中初始数据源地址 */ bl memory_copy /* 调用拷贝函数,需确保该函数不在 CCM 中执行 */第三步:在代码中指定变量位置
使用__attribute__((section(".ccmram")))将变量放入 CCM。
/* 示例 1:全局变量 */ uint8_t fast_buffer __attribute__((section(".ccmram"))); /* 示例 2:FreeRTOS 堆分配 */ #if defined(__GNUC__) uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] __attribute__((section(".ccmram"))); #endif /* 示例 3:结构体 */ typedef struct { float pid_kp; float pid_ki; float error_sum; } PidController_t; PidController_t motor_pid __attribute__((section(".ccmram")));