STC15 - C51 - Memory Models

news2024/11/18 23:46:47

文章目录

    • STC15 - C51 - Memory Models
    • 概述
    • 笔记
    • 内存用量的优化思路
    • END

STC15 - C51 - Memory Models

概述

在STC上测试呢, 想看看变量(不同类型的定义)被编译器分配在哪个内存范围(idata, pdata, xdata)?
同时, 总结一下降低内存用量的思路(如果像上位机那样内存管够, 就不用考虑内存用量的优化).

笔记

MDK5的自带帮助中, 有关于内存模型的说明.
在这里插入图片描述
MDK5中可以设置默认的内存模型.
在这里插入图片描述
在这里插入图片描述
内存模型有3种可选 : Small, Compact, Large.
选定了内存模型后, 可以让编译器按照指定的内存模型, 来分配变量到默认的内存范围.
在这里插入图片描述
内存模型 - Small : 默认将变量都分配到idata(总共128bytes)
内存模型 - Compact : 默认将变量都分配到pdata(总共256bytes)
内存模型 - Large : 默认将变量都分配到xdata(具体是否超过对应MCU的内部扩展RAM或外部扩展RAM, 需要看具体硬件连接情况), 对于IAP15F2K61S2, 内部扩展RAM范围 0x0 ~ 0x6ff, 但是IAP15自己要用0x400~0x6ff, 所以可用的内部扩展RAM范围为0x0 ~ 0x3ff

选定内存模型后, 变量被编译器自动分配到默认的内存范围. 但是可以单独指定函数入参和局部变量, 全局变量到底存在哪里.

C51的函数调用时, 参数并不在栈内, 而是全局变量.
可以单独指定函数参数的存储位置.

#pragma small         /* Default to small model */

extern int calc (char i, int b) large reentrant;
extern int func (int i, float f) large;
extern void *tcp (char xdata *xp, int ndx) compact;


int mtest (int i, int y)            /* Small model */
  {
  return (i * y + y * i + func(-1, 4.75));
  }


int large_func (int i, int k) large /* Large model */
  {
  return (mtest (i, k) + 2);
  }

如上的函数定义, 只能告诉编译器将函数入参分配到哪个内存区, 但是对节约内存没有帮助. 多一个被调用的函数实现(假设函数有入参), 内存用量还是会增加.

在C51中的局部变量也不在栈中(其实也是全局变量). 可以单独指定局部变量分配在哪个内存范围.
在这里插入图片描述
我的测试程序, 变量空间> 0x256, 直接将内存模型选为Large了.
在一个函数内定义3个变量, idata, pdata, xdata, 看看编译后内存空间大小的变化.

int main(void)
{
	// int idata x1 = 0;
	// int pdata x2 = 0;
	// int xdata x3 = 0;

每次编译后, 就逐个注释掉3个变量定义, 再编译, 得到如下实验结果.

Program Size: data=25.1 xdata=969 code=28811 x1,x2, x3
Program Size: data=25.1 xdata=967 code=28803 x1,x2
Program Size: data=25.1 xdata=965 code=28796 x1
Program Size: data=23.1 xdata=965 code=28789 

可以看出:
idata变量被编译器分配在data区.
pdata变量被编译器分配在xdata区.
xdata变量被编译器分配在xdata区.

内存用量的优化思路

51MCU, 内部扩展RAM就那么多(1K+), 如果不规划内存用量, 多一个函数就多占用一些内存(函数入参, 函数内的局部变量), 函数写的越多, 内存占用越多. 最后直到程序编译不过去, 显示内存越界.
将不在一条调用链上的函数, 公用一些内存空间, 就能节省一些内存. 且不随着函数数量的增加, 内存占用量上升不明显.
中断函数的内存用量要是单独的.
顶层函数的内存用量最好是单独的.
被顶层函数调用的多个并排的底层函数(这些函数之间, 不存在同时被调用的可能), 内存可以是公用的.

自己做一个结构体, 将变量都放到里面.
不能公用的变量, 就是结构体中的一个变量或结构.
能公用的变量, 就是结构体中的联合中的一个变量或结构. 如下所示.

// D:\my_dev\my_local_git_prj\hardware\LS_stc_dev_board\src\stc15_exp_box4\v5\soft_prj\factory_mode\uart_cmd.c
typedef struct _tag_uart_cmd {
	int len;

	int i;
	int len1;
	int len2;
	TAG_CMD_ITEM code* pcmd_item;
	TAG_CMD_PARAM* pcmd_param;
} TAG_uart_cmd;

// D:\my_dev\my_local_git_prj\hardware\LS_stc_dev_board\src\stc15_exp_box4\v5\soft_prj\factory_mode\hardware_init.c
typedef struct _tag_hardware_init{
	COMx_InitDefine COMx_InitStructure;					//结构定义
}TAG_hardware_init;

typedef union un_app_cfg {
	// union中的内容不能为空
	TAG_hardware_init hardware_init;
	TAG_uart_cmd uart_cmd;
	TAG_uart_cmd_help uart_cmd_help;
	TAG_uart_cmd_test_ind_leds uart_cmd_test_ind_leds;
	TAG_uart_cmd_test_7seg uart_cmd_test_7seg;
	TAG_Exti Exti;
	TAG_uart_cmd_test_exint_2key uart_cmd_test_exint_2key;
	TAG_uart_cmd_test_key16_normal uart_cmd_test_key16_normal;
	TAG_uart_cmd_test_key16_adc uart_cmd_test_key16_adc;
	TAG_uart_cmd_test_ex_ram uart_cmd_test_ex_ram;
	TAG_uart_cmd_test_12864 uart_cmd_test_12864;
	TAG_uart_cmd_test_1602 uart_cmd_test_1602;
	TAG_EEPROM EEPROM;
	TAG_uart_cmd_test_e2prom uart_cmd_test_e2prom;
	TAG_uart_cmd_test_LVD uart_cmd_test_LVD;
	TAG_uart_cmd_test_RTC uart_cmd_test_RTC;
	TAG_uart_cmd_test_pm25 uart_cmd_test_pm25;
	Tag_uart_cmd_test_NTC uart_cmd_test_NTC;
} UN_APP_CFG;



typedef struct _tag_app_global_var {
	// 需要被同时使用的变量, 放在union外面的结构中
	u8 g_tmp_buf[TMP_BUFFER_LEN];
	u8 g_param_buf[TMP_BUFFER_LEN];
	UFP_uart_cmd g_UFP_uart_cmd;
	BOOL is_cmd_was_process;
	BOOL is_cmd_process_ok;

	// 能不同时使用的变量都放un中的每个应用参数中
	UN_APP_CFG un;
} TAG_APP_GLOBAL_VAR;

extern u8 bdata u8_flag;
extern TAG_APP_GLOBAL_VAR xdata app;

调用这些变量时, 示例如下:
不能公用的变量用法 app.is_cmd_process_ok
可以公用的变量用法 app.un.uart_cmd.pcmd_item
这种方法还挺管用的, 从1200字节, 优化到了965字节. 这样的话,使用片内扩展RAM(0x0~0x400)就可以满足逻辑运行的要求.

这种内存优化思路不好的地方, 没有直接写全局变量直观了(每个变量都好长, 需要好几个结构的.操作才能使用这个变量, 看起来也挺恶心).

u8 code fn_parse_user_input_form_uart1(void /*u8* pbuf, int len_buf*/)
{
	app.un.uart_cmd.len = 0;

	if ( (NULL == app.g_UFP_uart_cmd.fn_parse_user_input_form_uart1.pbuf)
	     || (app.g_UFP_uart_cmd.fn_parse_user_input_form_uart1.len_buf < 2)
	     || (COM1.RX_Cnt < 2)) {
		return FALSE;
	}

	memset(app.g_UFP_uart_cmd.fn_parse_user_input_form_uart1.pbuf, 0,
	       app.g_UFP_uart_cmd.fn_parse_user_input_form_uart1.len_buf);
	RX1_Buffer[COM1.RX_Cnt - 2] = '\0';
	app.un.uart_cmd.len = strlen(RX1_Buffer);
	PrintString1("您输入的命令为[");
	PrintString1(RX1_Buffer);
	PrintString1("]\r\n");
	PrintString2("您输入的命令为[");
	PrintString2(RX1_Buffer);
	PrintString2("]\r\n");

	if ((app.un.uart_cmd.len <= 0)
	    || (app.un.uart_cmd.len >= (app.g_UFP_uart_cmd.fn_parse_user_input_form_uart1.len_buf - 1))) {
		return FALSE;
	}

	strcpy(app.g_UFP_uart_cmd.fn_parse_user_input_form_uart1.pbuf, RX1_Buffer); // 得到了没有\r\n, 以\0结尾的用户命令
	return TRUE;
}

甚至为了进一步降低内存用量, 直接将有些函数改成(void)无入参. 实际的入参由外部调用直接赋值, 函数内部直接使用函数外部赋值的全局变量, 看着更恶心了.
函数调用

			app.un.uart_cmd_test_pm25.SPI_Write_Nbytesaddr = app.un.uart_cmd_test_pm25.Flash_addr;
			app.un.uart_cmd_test_pm25.SPI_Write_Nbytesbuffer = app.un.uart_cmd_test_pm25.tmp; 
			app.un.uart_cmd_test_pm25.SPI_Write_Nbytessize = 0x10;
			SPI_Write_Nbytes();		//写N个字节

函数实现

void	SPI_Write_Nbytes(void /* u32 addr, u8* buffer, u8 size */)
{
	if (app.un.uart_cmd_test_pm25.SPI_Write_Nbytessize == 0)	{
		return;
	}

	if (!app.un.uart_cmd_test_pm25.B_FlashOK)	{
		return;
	}

	while (CheckFlashBusy() > 0);		//Flash忙检测

	FlashWriteEnable();					//使能Flash写命令
	SPI_CE_Low();						// enable device
	
	app.un.uart_cmd_test_pm25.SPI_WriteByteout = SFC_PAGEPROG;
	SPI_WriteByte();		// 发送页编程命令

不过, 对于MCU这种内存, 寸土寸金的地方, 牺牲一些直观性, 来保证内存用量可控, 还是值得的(暂时想不到其他方法可以大规模的降低内存用量).

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/72467.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统编程第五节——进程创建、终止、等待(通俗易懂快速上手版本)

目录 进程的创建 写时拷贝 进程的终止 进程的等待 状态参数status wait函数和waitpid函数 我们本节内容&#xff0c;主要来讲述进程控制有关的内容。 同样&#xff0c;我们会用通俗易懂、不同于教科书的讲授思路&#xff0c;来为大家讲解。 同时&#xff0c;本节内容板块…

你了解你的身体吗?- 基因社会

关于作者 本书的两位作者分别是以太•亚奈和马丁 • 菜凯尔&#xff0c;前者是哈佛大学髙级研究学者&#xff0c; 任职于纽约大学&#xff0c;是生物化学和分子药理 学的教授&#xff1b;后者是杜塞尔多夫海因西里•海 涅大学的生物信息学教授。两位作者从基 因之间合作和竞争…

[附源码]计算机毕业设计的4s店车辆管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

5款十分好用,但是没有什么知名度的软件

随着网络信息技术的发展&#xff0c;越来越多的人在办公时需要用到电脑了。如果你想提高办公效率&#xff0c;那么就少不了工具的帮忙&#xff0c;今天给大家分享5款办公必备的好软件。 1.数据可视化软件——Power BI Power BI是一款出色的业务分析软件。Power BI主要是用于在…

[Go] go基础4

1. 并发编程 1.1 并发和并行 并发: 多个线程在同个核心的CPU上运行.并发的本质是串行. 并行: 多个线程在多个核心的CPU上运行. 1.2 协程和线程 协程: 独立的栈空间,共享堆空间,调度由用户控制,本质上有点类似用户及线程,这些用户及线程的调度也是自己实现的. 线程: 一个线…

[附源码]JAVA毕业设计网络饮品销售管理系统(系统+LW)

[附源码]JAVA毕业设计网络饮品销售管理系统&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目…

全新社交电商模式来袭,消费增值结合共享经济完成消费升级

大家好&#xff0c;我是林工&#xff0c;不知道大家是否了解消费增值&#xff1f;这是一个消费储量为基础的理念&#xff0c;体现的是消费者的消费与回报问题&#xff0c;普遍的消费返利&#xff0c;消费全返渐渐地已经不能够满足目前的客户&#xff0c;也就有了一个满足与这部…

【嵌入式硬件芯片开发笔记】4-20mA DAC芯片AD5421配置流程

【嵌入式硬件芯片开发笔记】4-20mA DAC芯片AD5421配置流程 16位、串行输入、环路供电、4 mA至20 mA DAC 可用于HART协议相关电路 同AD5700配合使用 AD5421的SPI和普通的不一样 回读时要发两段 CS中间拉高一次 数据在SCLK上升沿逐个输出&#xff0c;而且在 SCLK下降沿有效 固CP…

使用方法丨艾美捷Caspase-1活性分析试剂盒介绍

如何动态检测活细胞内的Caspase-1的活性&#xff0c;做更真实的实验&#xff1f;艾美捷推荐Immunochemistry Tech&#xff08;ICT&#xff09;的FLICA系列科研工具&#xff0c;轻松检测活细胞Caspase-1 活性。 艾美捷Immunochemistry Caspase-1活性分析试剂盒原理&#xff1a…

Qt第三十四章:总结【隐藏标题栏边框、隐藏背景、窗体透明】

目录 隐藏标题栏边框 ①隐藏标题栏代码&#xff1a; ​编辑②自定义标题栏(可以直接Copy) 使用 隐藏背景 ①隐藏背景代码,此时背景上的样式都是无效的。 ②自定义背景,通过重写paintEvent事件来完成 中间绘制的部分是我们想要的&#xff0c;只需要将标题栏边框隐藏掉就可…

五、卷积神经网络CNN5(图像卷积与反卷积)

图像卷积 首先给出一个输入输出结果那他是怎样计算的呢&#xff1f; 卷积的时候需要对卷积核进行 180 的旋转&#xff0c;同时卷积核中心与需计算的图像像素对齐&#xff0c;输出结构为中心对齐像素的一个新的像素值&#xff0c;计算例子如下&#xff1a;这样计算出左上角(即第…

【畅购商城】内网穿透之EchoSite

目录 概述 注册用户 抢注域名 ​​​​​​​下载客户端 ​​​​​​​编写配置文件 ​​​​​​​启动 ​​​​​​​访问 ​​​​​​​概述 EchoSite一款收费的内网映射工具&#xff08;已下架&#xff09; 花生壳&#xff1a;内网穿透工具&#xff0c;免费版…

springboot集成Lombok、MybaitsPlus、SwaggerUI

springboot集成Lombok、MybaitsPlus、SwaggerUI 基础环境&#xff1a;JDK8或者JDK11版本 Maven3.5(采⽤默认) IDEA旗舰版 Mysql5.7以上版本 创建springboot项目 在线构建⼯具 https://start.spring.io/ 修改pom.xml中内容 <!-- 代码库 --> <repositories> &l…

如何提高量化策略回测的效率

数量技术宅团队在CSDN学院推出了量化投资系列课程 欢迎有兴趣系统学习量化投资的同学&#xff0c;点击下方链接报名&#xff1a; 量化投资速成营&#xff08;入门课程&#xff09; Python股票量化投资 Python期货量化投资 Python数字货币量化投资 C语言CTP期货交易系统开…

教你轻松设计圣诞节日活动的邀请函?

即将迎来一年一度的圣诞活动&#xff0c;商家们要如何对此次活动展开宣传呢&#xff1f;要如何制作出一张符合节日气氛的活动邀请函&#xff1f;下面小编就教你如何使用在线工具乔拓云&#xff0c;用在线邀请函模板设计活动邀请函&#xff0c;一键就能生成活动邀请函链接&#…

springmvc整体运行流程

请求处理的流程 1&#xff1a;浏览器发送请求送至前端控制器DispatcherServlet。 2&#xff1a;DispatcherServlet收到请求后调用HandlerMapping处理器映射器。 3&#xff1a;处理器映射器找到具体的Handler处理器&#xff0c;封装成为执行链 4&#xff1a;返回执行链给Disp…

SpringBoot+MyBatis多表查询:以点餐系统的订单管理为例

文章目录项目场景数据库设计POJODaoServiceTest运行结果问题及解决项目场景 SpringBoot MyBatis &#xff0c;实现点餐系统的订单查询。 参考&#xff1a;SpringBootMyBatis多表联合查询 数据库设计 通常一个订单中会包含多个菜品&#xff0c;即一条 order 里含多个 item&am…

使用 Zotero Citation Counts Manager 自动获取文献引用数

前言 最近习惯了搜完文献把文献的引用贴进 Zotero 文献条目的 Note 里&#xff0c;但是自己一点一点打字太累。想到了 Zotero 有插件&#xff0c;简单调研一下&#xff0c;发现了一个可以用的插件。支持 CrossRef 和 SemanticScholar 的引用数获取。&#xff08;相对来说个人比…

QT QSlider、QHorizontalSlider、QVerticalSlider 控件 使用详解

本文详细的介绍了QSlider、QHorizontalSlider、QVerticalSlider控件的各种操作&#xff0c;例如&#xff1a;新建界面、设置刻度显示、设置范围值、设置值、获取值、设置步长、刻度间隔、改变方向、滑动信号、按下信号、滑动信号、释放滑块、样式表等操作。 本系列QT全面详解文…

职业生涯交叉点,7年测试工程师经历,继续做测试还是转开发?

我毕业后一直从事软件测试工作&#xff0c;今年28岁了。所做的工作都是功能测试&#xff0c;对于性能测试、自动化测试完全没有接触。 我是在深圳的一个小公司上班&#xff0c;每天朝九晚五的&#xff0c;工作不累。属于那种要来活了半天就可以搞定&#xff0c;剩下的时间都是…