关于MCU产品开发参数存储的几种方案

关于MCU产品开发参数存储的几种方案

  • Chapter1 关于MCU产品开发参数存储的几种方案
  • Chapter2 单片机参数处理[保存与读取]
  • Chapter3 嵌入式设备参数存储技巧
  • Chapter4 STM32硬件I2C的一点心得(AT24C32C和AT24C64C)


Chapter1 关于MCU产品开发参数存储的几种方案

原文链接

在工作中,几乎所有参与产品开发的产品都将实现参数存储功能。

通常,参数存储将使用以下存储介质,例如:eeprom,spi闪存,nand闪存,SD卡等,至于如何存储,现在有很多种类。

1.使用eeprom(以at24c02为例)定义结构,然后定义两个结构变量,一个用于读取参数,一个用于立即写入修改的参数。

参考:2.使用spi_flash(以w25q64为例)方法1与使用eeprom方法相同。

方法2使用文件系统并创建一个ini文件来获取参数。

Chapter2 单片机参数处理[保存与读取]

原文链接:https://blog.csdn.net/WangSanHuai2010/article/details/6988583

/*------------------------------------------------------------Func: 加载参数到系统Time: 2011-11-13Ver.: V1.0Note:
------------------------------------------------------------*/
void WFS_LoadParams(uint16 Addr,uint16 *Buffer,uint16 Length)
{Addr<<=1;Length<<=1;EEPROM_Read(Addr+2,(uint8 *)Buffer,Length);
}

参数按以上方法加载到内存,注意参数的起始地址为2,这是因为前两个字节区域要用来做校验用。

/*------------------------------------------------------------Func: 保存参数Time: 2011-11-13Ver.: V1.0Note:
------------------------------------------------------------*/
void WFS_SaveParams(uint16 Addr,uint16 *Buffer,uint16 Length)
{Addr<<=1;Length<<=1;EEPROM_Write(Addr+2,(uint8 *)Buffer,Length);
}

以上方法保存参数到EEPROM中,实际上与Load方法一一对应。

/*------------------------------------------------------------Func: 参数系统初始化Time: 2011-11-13Ver.: V1.0Note:
------------------------------------------------------------*/
uint8 WFS_InitParams(void *DefaultValues,uint16 Length)
{uint16 D;EEPROM_Read(0,(uint8 *)(&D),2);if(D!=0x55AA){D=0x55AA;EEPROM_Write(0,(uint8 *)(&D),2);EEPROM_Write(2,(uint8 *)DefaultValues,Length);return 0xFF;}return 0x00;
}

参数的初始化方法,首先读取EEPROM的0位置处的数据,判断是否为0x55AA合法标志,若不是0x55AA,则说明参数区为首次使用,需要进行初始化默认参数填充,于是将DefaultValues所指的默认值填入EEPROM中,并设置0x55AA标志,以后每次上电便会检测到参数的合法性。

以下为使用示例,存储了地址码,波特率,数据位,停止位四个参数,以及一个18字的数组。

const uint16 WFS_ParmasValue_Default[]=
{1,9600,8,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};

以下为参数进行初始化并加载到内存:


WFS_InitParams(WFS_ParmasValue_Default,sizeof(WFS_ParmasValue_Default));
WFS_LoadParams(0,&DevAddr,1);
WFS_LoadParams(1,&BaudRate,1);
WFS_LoadParams(2,&DataLength,1);
WFS_LoadParams(3,&StopBits,1);
WFS_LoadParams(4,Array,18);

以下为参数修改后进行保存:

BaudRate=115200;
StopBits=2;
WFS_SaveParams(1,&BaudRate,1);
WFS_LoadParams(3,&StopBits,1);

Chapter3 嵌入式设备参数存储技巧

原文链接

1、前言
想必做嵌入式产品开发都遇到过设备需要保存参数,常用的方式就是按照结构体的方式管理参数,保存时将整个结构体数据保存在 Flash 中,方便下次读取。

1.1、目的
本文时分析嵌入式/单片机中参数保存的几种方式的优点和缺点(仅针对单片机/嵌入式开发而言),同时针对以结构体的方式解决一些弊端问题(重点在第 3 节)。

2、参数保存格式
2.1、结构体格式
该方式是嵌入式/单片机中开发最常用的,将所有的系统参数通过结构体的方式定义,然后保存数据,介绍一下该方式的优缺点。

储存方式:二进制 bin 文件格式
1
优点:

管理简单:无需额外的代码直接就能很方便的管理参数
内存最小:通过结构体的形式保存在Flash中,占用内存最小
缺点:

1.扩展性差:

从产品角度来说,产品需要升级,若是涉及增加参数,则升级后参数通常无法校验通过(通常包含长度校验等),导致参数被恢复默认
若是每个模块都存在自己的独有结构体参数定义,删除/新增时势必影响到其他的,导致设备升级后参数错乱(结构体中的变量地址在 bin 文件中是固定的)
2.阅读性差:
若参数需要导出,bin文件没有可读性

改进措施:
结构体增加预留定义,若之后需要新增参数,则在预留空间新增即可,能在一定程度上解决扩展性差的问题,即新增不影响原有的结构体大小和其他成员变量的位置,删除恢复成预留即可。

为啥说只能在一定程度上解决该问题,因为之后的升级某些模块可能很长时间或者从不需要增加新的参数,这种势必就会造成内存的无效占用,或者有些模块频繁增加参数导致预留大小不够等问题,只能在前期设计时多加思考预留的分配情况(毕竟内存只有那么大)

/*****************************改进之前
*****************************/typedef struct
{uint8_t testParam;uint8_t testParam2;
} TestParam_t;    /* 某模块参数 */typedef struct
{uint8_t testParam;uint8_t testParam2;TestParam_t tTestParam;
} SystemParam_t; /* 系统参数 *//*****************************改进之后
*****************************/typedef struct
{uint8_t testParam;uint8_t testParam2;uint8_t reserve[6];    // 预留
} TestParam_t;    /* 某模块参数 */typedef struct
{uint8_t testParam;uint8_t testParam2;TestParam_t tTestParam;uint8_t reserve[50];   // 预留
} SystemParam_t; /* 系统参数 */

2.2、JSON格式
最近Json格式很是流行使用,特别是数据交换中用的很多,但是它也可以用来保存参数使用,JSON 的是 “{键:值}” 的方式。

储存方式:字符串格式,即文本的形式
1
优点:

扩展性好:由于Json的格式,找到对应键值(一般都是该变量的标识),就能找到对应的值
阅读性好:有标识所以导出参数文件通过普通的文本文件打开都能看懂
缺点:

管理相对复杂:没有结构体那么简单,不熟还得先学习 JSON 的写法
内存占用较大:内容不只有值,而且都按照字符串的形式保存的
使用相关困难:需要解析,C语言虽然有开源库,但是由于语言性质使用不方便,C++ 反而使用简单

{"SYS":{"testParam" : 2,"testParam2" : 5,"tTestParam":{"testParam" : 2,"testParam2" : 5}}
}//压缩字符串为:
{"SYS":{"testParam":2,"testParam2":5,"tTestParam":{"testParam":2,"testParam2":5}}}

2.3、键值格式
和上述的 JSON 格式很类似,都是键值对的格式,但是比JSON简单

储存方式:字符串格式,即文本的形式
1
优点:

扩展性好:找到对应键值(一般都是该变量的标识),就能找到对应的值
阅读性好:有标识所以导出参数文件通过普通的文本文件打开都能看懂
缺点:

内存占用较大:内容不只有值,而且都按照字符串的形式保存的
使用稍微困难:需要简单解析处理
管理不变:不方便按照一定的规则管理各模块的参数

testParam=2
testParam2=5
T_testParam=2
T_testParam2=5

2.4 其他
还有其他,如 xml (类似JSON)等,就不多介绍了

3、编译器检查结构体的大小和成员变量的偏移
在第 2 节中介绍了关于参数保存的三种方式,但是对于嵌入式单片机开发而言,Flash 大小不富裕,所以通常都是通过二进制的形式保存的,所以这节重点解决结构体管理保存参数的扩展性问题。

先说一下痛点(虽然对扩展性问题做了改进措施,除了前面讲到的问题,还有其他痛点,虽不算问题,但是一旦出现往往最要命)

在原来的预留空间中新增参数,要确保新增后结构体的大小不变,否则会导致后面的其他参数偏移,最后升级设备后参数出现异常(如果客户升级那就是要命啊)
确保第一点,就必须在每次新增参数都要计算检查一下结构体的大小有没有发生变化,而且有没有对结构体中的其他成员也产生影响
每次新增参数,手动计算和校验 99% 可以检查出来,但是人总有粗心的时候(加班多了,状态不好…),且结构体存在填充,一不留神就以为没问题,提交代码,出版本(测试不一定能发现),给客户,升级后异常,客户投诉、扣工资(难啊…)
遇到这种问题后:难道编译器就不能在编译的时候检查这个大小或者结构体成员的偏移吗,每次手动计算校验好麻烦啊,一不留神还容易算错 # _ #

按照正常情况,编译器可不知道你写的结构体大小和你想要的多大,所以检查不出来(天啊,崩溃了0.0…)

别急,有另类的方式可以达到这种功能,在编译时让编译器为你检查,而且准确性 100%(当然,这个添加新参数时你还得简单根据新增的参数大小减少预留的大小,这个是必须要的)

见代码:

/*** @brief 检查结构体大小是否符合*        在编译时会进行检查* @param type 结构体类型* @param size 结构体检查大小*/
#define TYPE_CHECK_SIZE(type, size) extern int sizeof_##type##_is_error [!!(sizeof(type)==(size_t)(size)) - 1]/*** @brief 结构体成员* @param type   结构体类型* @param member 成员变量*/
#define TYPE_MEMBER(type, member) (((type *)0)->member)/*** @brief 检查结构体成员大小是否符合*        在编译时会进行检查* @param type 结构体类型* @param member 结构体类型* @param size 结构体检查大小*/
#define TYPE_MEMBER_CHECK_SIZE(type, member, size) extern int sizeof_##type##_##member##_is_error \[!!(sizeof(TYPE_MEMBER(type, member))==(size_t)(size)) - 1]/*** @brief 检查结构体中结构体成员大小是否符合*        在编译时会进行检查* @param type 结构体类型* @param member 结构体类型* @param size 结构体检查大小*/
#define TYPE_CHILDTYPE_MEMBER_CHECK_SIZE(type, childtype, member, size) extern int sizeof_##type##_##childtype##_##member##_is_error \[!!(sizeof(TYPE_MEMBER(type, childtype.member))==(size_t)(size)) - 1]/*** @brief 检查结构体成员偏移位置是否符合*        在编译时会进行检查* @param type 结构体类型* @param member 结构体成员* @param value 成员偏移*/
#define TYPE_MEMBER_CHECK_OFFSET(type, member, value) \extern int offset_of_##member##_in_##type##_is_error \[!!(__builtin_offsetof(type, member)==((size_t)(value))) - 1]/*** @brief 检查结构体成员偏移位置是否符合*        在编译时会进行检查* @param type 结构体类型* @param member 结构体成员* @param value 成员偏移*/
#define TYPE_CHILDTYPE_MEMBER_CHECK_OFFSET(type, childtype, member, value) \extern int offset_of_##member##_in_##type##_##childtype##_is_error \[!!(__builtin_offsetof(type, childtype.member)==((size_t)(value))) - 1]

通过以上代码,就能解决这个问题,这个写法只占用文本大小,编译后不占内存!!!

用法:

typedef struct
{uint8_t testParam;uint8_t testParam2;uint8_t reserve[6];    // 预留
} TestParam_t;    /* 某模块参数 */TYPE_CHECK_SIZE(TestParam_t, 8); // 检查结构体的大小是否符合预期typedef struct
{uint8_t testParam;uint8_t testParam2;TestParam_t tTestParam;uint8_t reserve[54];   // 预留
} SystemParam_t; /* 系统参数 */TYPE_CHECK_SIZE(SystemParam_t, 64); // 检查结构体的大小是否符合预期
TYPE_MEMBER_CHECK_OFFSET(SystemParam_t, tTestParam, 2); // 检查结构体成员tTestParam偏移是否符合预期

假设新增了参数,预留写错了,导致结构体的大小不符合,则编译时报错,且提示内容也能快速定位问题。
在这里插入图片描述

Chapter4 STM32硬件I2C的一点心得(AT24C32C和AT24C64C)

原文链接:https://blog.csdn.net/whitefish520/article/details/110070972

从设备读写
一般的EEPROM,像AT24C02这种小容量的设备,地址都只需要8位,页大小一般是16字节一个页
而像AT24C32C、AT24C64C这种32K、64K字节的大容量EEPROM,8位地址是不够的,使用了16位地址,页大小在这两个器件中也变成了32字节
正是由于容量的不同,导致代码上需要做差异化处理,才能正确读取EEPROM芯片

以下代码可以参考,写的时候无法跨页,因此写大量数据的时候,只能一页一页的写,两次写之间保证5MS的间隔
读没有跨页的影响,可以一次性把全部数据读出来,但是要注意,读和写之间,是要有5MS的间隔的,否则读不到数据。也就是说每次写完,延迟5MS,就能保证后续的程序没有问题。

//#define I2C_MEMADD_SIZE		I2C_MEMADD_SIZE_8BIT			//小容量EEPROM芯片8位地址用此参数
#define I2C_MEMADD_SIZE		I2C_MEMADD_SIZE_16BIT		//大容量EEPROM芯片16位地址用此参数
#define ADDR_AT24C02_Write 0xA0		//EEPROM I2C写地址
#define ADDR_AT24C02_Read 0xA0+1	//EEPROM I2C读地址typedef enum SYS_PARA_ENUM {LOCAL_IP = 0,UDP_LOCAL_PORT,UDP_PC_PORT,NETMASK,GATEWAY,SERVER_IP,SERVER_PORT,VERSION,SN_NUM,	//16字节SENSOR_TYPE = SN_NUM + 4,SENSOR_DATA_TYPE,SENSOR_INTERVAL,SYS_PARA_MAX,		//end
}sys_para_e;//请注意,为了方便flash读写操作,此处的每一项均设为uint32_t类型
//如果不为uint32_t类型,则flash_write函数将出现错误
typedef struct SYS_PARA_TYPE {uint32_t local_ip;			//本机IP,大端模式uint32_t udp_local_port;	//本机端口号uint32_t udp_pc_port;		//PC端口号uint32_t netmask;			//本机子网掩码,大端模式uint32_t gateway;			//本机网关,大端模式uint32_t server_ip;			//服务器IP,大端模式uint32_t server_port;		//服务器端口号uint32_t version;			//stm32软件版本号uint32_t sn_num[4];			//SN号	5 6 7 8uint32_t sensor_type;		//传感器类型 9uint32_t sensor_data_type;	//传感器数据类型uint32_t sensor_interval;	//传感器采集时间间隔
}sys_para_t;/* -----------------------------------------------------------------------------
函数名:  	i2c_write
作者:    	glx
日期:    	2020-11-10
功能:    	数据写入eeprom
输入参数:	pData:数据指针
返回值:  	类型:HAL_StatusTypeDefHAL_OK:操作成功HAL_ERROR:操作失败
修改记录:
------------------------------------------------------------------------------*/
HAL_StatusTypeDef i2c_write(void *pData)
{HAL_StatusTypeDef ret = HAL_ERROR;uint8_t i, page, pageSize;uint8_t *p = (uint8_t *)pData;if(pData != NULL){if(I2C_MEMADD_SIZE == I2C_MEMADD_SIZE_8BIT){pageSize = 16;			//一页16字节,不能跨页写page = SYS_PARA_MAX/4;}else if(I2C_MEMADD_SIZE == I2C_MEMADD_SIZE_16BIT){pageSize = 32;			//一页32字节,不能跨页写page = SYS_PARA_MAX/8;}//写完整的页for(i = 0; i<page; i++){ret = HAL_I2C_Mem_Write(&hi2c1, ADDR_AT24C02_Write, i*pageSize, I2C_MEMADD_SIZE, p, pageSize, 100);p += pageSize;if(ret != HAL_OK){printf("I2C_Write Sys Para write error\r\n");return HAL_ERROR;}HAL_Delay(5);}//写残缺的页if(SYS_PARA_MAX > (page*4))	{ret = HAL_I2C_Mem_Write(&hi2c1, ADDR_AT24C02_Write, i*pageSize, I2C_MEMADD_SIZE, p, 4*SYS_PARA_MAX-pageSize*page, 100);if(ret != HAL_OK){printf("I2C_Write Sys Para write error\r\n");return HAL_ERROR;}HAL_Delay(5);}}else{printf("I2C_Write pData NULL\r\n");return HAL_ERROR;}return HAL_OK;
}/* -----------------------------------------------------------------------------
函数名:  	i2c_read
作者:    	glx
日期:    	2020-11-10
功能:    	读取eeprom数据
输入参数:	pData:数据指针
返回值:  	类型:HAL_StatusTypeDefHAL_OK:操作成功HAL_ERROR:操作失败
修改记录:
------------------------------------------------------------------------------*/
HAL_StatusTypeDef i2c_read(void *pData)
{HAL_StatusTypeDef ret = HAL_ERROR;if(pData != NULL){ret = HAL_I2C_Mem_Read(&hi2c1, ADDR_AT24C02_Read, 0, I2C_MEMADD_SIZE, (uint8_t *)pData, SYS_PARA_MAX*4, 100);if(ret != HAL_OK){printf("I2C_Read Sys Para read error\r\n");return HAL_ERROR;}		}else{printf("I2C_Read pData NULL\r\n");return HAL_ERROR;}return HAL_OK;
}

读写的代码


//用于保存系统参数
sys_para_t sys_para = {0};	
//用于保存系统默认参数
sys_para_t default_para = {.local_ip = 107<<24 | 10<<16 | 168<<8 | 192,.udp_local_port = 18080,.udp_pc_port = 18081,.netmask = 0<<24 | 255<<16 | 255<<8 | 255,.gateway = 1<<24 | 10<<16 | 168<<8 | 192,.server_ip = 9<<24 | 10<<16 | 168<<8 | 192,.server_port = 18082,.version = 20201124,.sn_num[0] = 0xffffffff,.sn_num[1] = 0xffffffff,.sn_num[2] = 0xffffffff,.sn_num[3] = 0xffffffff,.sensor_type = SENSOR_DOOR,.sensor_data_type = SENSOR_DATA_GPIO,.sensor_interval = 1000,
};i2c_read(&sys_para);
i2c_write(&default_para);

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

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

相关文章

【随笔】Git 高级篇 -- 纠缠不清的分支 rebase | cherry-pick(二十四)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

位图布隆过滤器的原理及实现

目录 位图的概念&#xff1a; 位图的前置知识&#xff1a;位运算 位图的实现&#xff1a; 位图的基本参数和构造方法&#xff1a; 位图的插入&#xff1a; 位图的查找&#xff1a; 位图的删除&#xff1a; 布隆过滤器概念&#xff1a; 布隆过滤器的实现&#xff1a; …

TI-ST论文速读

Domain Adaptation for Medical Image Segmentation Using Transformation-Invariant Self-training 摘要 能够利用未标记数据的模型对于克服不同成像设备和配置中获取的数据集之间的巨大分布差距至关重要。在这方面&#xff0c;基于伪标记的自训练技术已被证明对半监督域适应…

微商商城源码小程序好用么?

商城APP作为电子商务行业的重要组成部分&#xff0c;已经成为了人们购物的主要方式之一。为了在竞争激烈的市场中脱颖而出&#xff0c;开发一款专业且思考深度的商城APP方案显得尤为关键。本文将从专业性和思考深度两个方面&#xff0c;探讨商城APP的开发方案。 一、专业性的重…

HTML+CSS+JS实现京东首页[web课设代码+模块说明+效果图]

系列文章目录 Web前端大作业htmlcss静态页面–掌****有限公司 Web前端大作业起点小说静态页面 Web前端大作业网易云页面 Web前端大作业商城页面 Web前端大作业游戏官网页面 Web前端大作业网上商城页面 HTMLCSS淘宝首页[web课设代码模块说明效果图] 文章目录 系列文章目录前言一…

Java Set基础篇

目录 前言一、常用Set1.1 Set1.1.1 特点 1.2 HashSet1.2.1 特点1.2.2 使用 1.3 TreeSet1.3.1 特点1.3.2 使用 1.4 LinkedHashSet1.4.1 特点1.4.2 使用 二、对比总结 目录 前言 一、常用Set 1.1 Set Set是一个继承自Collection的接口&#xff1a; public interface Set<…

如何在Rust中操作JSON

❝ 越努力&#xff0c;越幸运 ❞ 大家好&#xff0c;我是「柒八九」。一个「专注于前端开发技术/Rust及AI应用知识分享」的Coder。 前言 我们之前在Rust 赋能前端-开发一款属于你的前端脚手架中有过在Rust项目中如何操作JSON。 由于文章篇幅的原因&#xff0c;我们就没详细介绍…

基于SSM的教材管理系统

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的教材管理系统2拥有两种角色 管理员&#xff1a;教师管理、分类管理、教材管理、入库和出库管理、教材申请管理等 教师&#xff1a;查看教材、申请教材 1.1 背景描述 SSM教材管…

服务器数据恢复—EqualLogic PS6100系列存储数据恢复案例

服务器数据恢复环境&#xff1a; 某品牌EqualLogic PS6100系列存储阵列是一款容错功能较强的存储设备&#xff0c;具有较高的安全性能。一些硬件故障或者误操作也会破坏该系列存储内的数据&#xff0c;下面分享一个北亚企安数据恢复工程师接到的一个关于EQ PS6100存储的数据恢复…

java版数字藏品深色UI仿鲸探数藏盲盒合成短视频卡牌模式支持高并发

Java版数字藏品深色UI仿鲸探数藏盲盒合成短视频卡牌模式支持高并发&#xff0c;是一种结合了Java技术、深色用户界面&#xff08;UI&#xff09;设计、数字藏品概念、盲盒合成玩法以及短视频卡牌模式的综合性应用。该模式旨在为用户提供一种新颖、有趣的数字藏品体验&#xff0…

LC 450.删除二叉搜索树中的节点

450.删除二叉搜索树中的节点 给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key 对应的节点&#xff0c;并保证二叉搜索树的性质不变。返回二叉搜索树&#xff08;有可能被更新&#xff09;的根节点的引用。 一般来说&#xff0c;删除节点可分…

解锁CodeGeeX智能问答中3项独有的隐藏技能

CodeGeeX是一款强大的AI编程助手&#xff0c;它能帮你快速解决编程中的各种问题。无论是代码调试、功能实现&#xff0c;还是学习新语言&#xff0c;CodeGeeX都能助你一臂之力。 在IDE中搜索“CodeGeeX”就可以免费下载使用 CodeGeeX在侧边栏提供的Ask CodeGeeX智能问答功能&a…

代码随想录-算法训练营day04【链表02:两两交换链表中的节点、删除链表的倒数第N个节点、链表相交、环形链表II】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第二章 链表part02● day 1 任务以及具体安排&#xff1a;https://docs.qq.com/doc/DUG9UR2ZUc3BjRUdY ● day 2 任务以及具体安排&#xff1a;https://docs.qq.com/doc/DUGRwWXNOVEpyaVpG ● day 3 任务以及具体安排…

C++第十六弹---string使用(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、标准库中的string类 1.1、string类的常用接口说明 1.1.1、string类对象的常见构造 1.1.2、string类对象的容量操作 1.1.3、string类对象的…

网上打印a4纸大概多少钱一张

在快节奏的现代生活中&#xff0c;网上打印服务因其便捷性而受到越来越多用户的青睐。对于许多需要打印大量文件的用户来说&#xff0c;了解网上打印A4纸的价格至关重要。那么&#xff0c;网上打印A4纸300页大概需要多少钱呢&#xff1f; 网上打印价格取决于多个因素&#xff…

Severt和tomcat的使用(补充)

打包程序 在pom.xml中添加上述代码之后打包时会生成war包并且包的名称是test 默认情况打的是jar包.jar里量但是tomcat要求的是war包. war包Tomcat专属的压缩包. war里面不光有.class还有一些tomcat要求的配置文件(web.xml等)还有前端的一些代码(html, css, js) 点击其右边的m…

前端学习<四>JavaScript基础——14-基本包装类型

基本数据类型不能绑定属性和方法 属性和方法只能添加给对象&#xff0c;不能添加给基本数据类型。我们拿字符串来举例。 1、基本数据类型&#xff1a; 基本数据类型string是无法绑定属性和方法的。 var str qianguyihao;​str.aaa 12;console.log(typeof str); //打印结果…

十大经典排序算法及其C语言实现--带动图展示

排序算法大概算起来有以下十种 一、冒泡排序&#xff08;Bubble Sort&#xff09; 冒泡排序&#xff08;Bubble Sort&#xff09;也是一种简单直观的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。走访数…

python统计分析——一般线性回归模型

参考资料&#xff1a;python统计分析【托马斯】 当我想用一个或多个其他的变量预测一个变量的时候&#xff0c;我们可以用线性回归的方法。 例如&#xff0c;当我们寻找给定数据集的最佳拟合线的时候&#xff0c;我们是在寻找让下式的残差平方和最小的参数(k,d)&#xff1a; 其…

基于51单片机的秒表设计—0.01精度99.99s

基于51单片机的秒表设计 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;PCB&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.数码管显示秒表&#xff0c;精度为0.01&#xff1b; 2.记时最大为99.99s,过后清零记数。 3.一个按键实现开始…