关于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.rhkb.cn/news/309454.html

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

相关文章

【系统分析师】计算机网络

文章目录 1、TCP/IP协议族1.1 DHCP协议1.2 DNS协议1.3网络故障诊断 2、网路规划与设计2.1逻辑网络设计2.2物理网络设计2.3 分层设计 3、网络接入3.1 接入方式3.2 IPv6地址 4、综合布线技术5、物联网5.1物联网概念与分层5.2 物联网关键技术 6、云计算7、网络存储技术&#xff08…

C语言中局部变量和全局变量是否可以重名?为什么?

可以重名 在C语言中, 局部变量指的是定义在函数内的变量, 全局变量指的是定义在函数外的变量 他们在程序中的使用方法是不同的, 当重名时, 局部变量在其所在的作用域内具有更高的优先级, 会覆盖或者说隐藏同名的全局变量 具体来说: 局部变量的生命周期只在函数内部,如果出了…

AI来了,Spring还会远吗?(Spring AI初体验)

目录 一、创建项目二、first demo1、application.properties2、ChatController3、结果 三、个人思考 一、创建项目 官方文档的Getting Started 最低要求&#xff1a;JDK17 阿里云的Server URL&#xff08;https://start.aliyun.com/&#xff09;搜不到Spring AI&#xff0c;…

数据库:SQL分类之DQL详解

1.DQL语法 select 字段列表 from 表名列表 where 条件列表 group by 分组字段列表 having 分组后条件列表 order by 排序字段列表 limit 分页参数 基本查询 条件查询&#xff08;where&#xff09; 聚合函数&#xff08;count、max、min、avg、sum &#xff09; 分组查询&…

C语言100道练习题打卡(1)

1 有1&#xff0c;2&#xff0c;3&#xff0c;4四个数字&#xff0c;能组成多少个互不相同且不重复的三位数&#xff0c;都是多少 #include<stdio.h> //有1&#xff0c;2&#xff0c;3&#xff0c;4四个数字&#xff0c;能组成多少个互不相同且不重复的三位数&#xff…

分享一些有趣的 Linux 命令

1、sl 会显示一辆火车穿过你的终端屏幕 2、cmatrix 在终端中显示类似于《黑客帝国》电影中的绿色数字雨效果 3、fortune 显示一个随机的名人名言或者笑话 4、cowsay 让一头牛说出你输入的话 5、toilet 在终端中将输入的文本以艺术字体的形式呈现 6、figlet 类似于 toile…

ssm051网上医院预约挂号系统+jsp

网上医院预约挂号系统设计与实现 摘 要 如今的信息时代&#xff0c;对信息的共享性&#xff0c;信息的流通性有着较高要求&#xff0c;因此传统管理方式就不适合。为了让医院预约挂号信息的管理模式进行升级&#xff0c;也为了更好的维护医院预约挂号信息&#xff0c;网上医院…

Vue入门:天不生Vue,前端万古如长夜 - Vue从入门到放弃

目录 &#x1f44b; Vue环境搭建 1.安装node.js 2.配置环境变量 3.VSCode配置 4.安装Vue CLI 5.在VS Code中打开Vue项目 6.运行Vue项目 &#x1f440; Vue基础学习 1.引入vue.js 2.数据方法 3.生命周期&#xff01; 4.模板语法 5.对象语法 6.条件渲染 7.列表渲…

简历上写熟悉Linux下常用命令?直接寄

大家写简历技术栈时&#xff0c;都觉得越多越好&#xff0c;其中一条&#xff0c;熟悉Linux下常用命令&#xff1f;其实开发中Linux不是必备考点&#xff0c;除了运维&#xff0c;真正用的多的仅仅cd ls mkdir等&#xff0c;但当面试官问到上面命令时&#xff0c;是不是就傻眼了…

Java使用OpenOffice将office文件转换为PDF

Java使用OpenOffice将office文件转换为PDF 1. 先行工作1.1 OpenOffice官网下载1.2 JODConverter官网下载1.3 下载内容 2.介绍3. 安装OpenOffice服务3.1.Windows环境3.2 Linux环境 4. maven依赖5. 转换代码 1. 先行工作 请注意&#xff0c;无论是windows还是liunx环境都需要安装…

基于深度学习的花卉检测系统(含PyQt界面)

基于深度学习的花卉检测系统&#xff08;含PyQt界面&#xff09; 前言一、数据集1.1 数据集介绍1.2 数据预处理 二、模型搭建三、训练与测试3.1 模型训练3.2 模型测试 四、PyQt界面实现参考资料 前言 本项目是基于swin_transformer深度学习网络模型的花卉检测系统&#xff0c;…

建模设计软件 Archicad 27 for mac激活版

在建筑设计领域&#xff0c;每一次技术的革新都意味着设计效率和质量的飞跃。Archicad 27 for Mac&#xff0c;就是这样一款引领行业变革的设计软件。 Archicad 27凭借出色的性能优化和强大的功能更新&#xff0c;为Mac用户带来了前所未有的建筑设计体验。它支持BIM&#xff08…

基于java的社区生活超市管理系统

开发语言&#xff1a;Java 框架&#xff1a;ssm 技术&#xff1a;JSP JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclip…

低代码开发师中级实操题

目录 实操题一 1.创建空白应用 2.创建普通表单仓库库存表 3.创建客户信息表 4.创建进货登记表 5.创建出货登记表 6.设置数据联动 1&#xff09;进货登记表 2&#xff09;出货登记表数据联动 实操题二 1.提前下载好数据 2.新建空白应用 3.创建表单-员工信息录入名单…

26-51单片机-LCD12864液晶显示实验

一 主要涉及到的知识点: 1.1LCD12864 介绍: LCD12864 液晶屏结构上与 LCD1602 一样&#xff0c;只是在行列数与显示像素上区别很LCD12864&#xff0c; 以下简称 12864&#xff0c;注意区分 LCD1602 和 LCD12864。12864是 64行 128 列&#xff0c;当然也有可能会设 计成 64 列…

5-51单片机-蜂鸣器实验

一 主要涉及到的知识点: 1. 对系统默认的数据类型进行重定义,u8,u16; 2.延时函数,主要是将传过来的整数进行减减实现延时; 3.还用到了while()循环; 二 代码分析: 1. 首次定义一个变量,主要是为了延时蜂鸣器响亮的时长; 2.通过while()循环,使得Buzzer_Port产生脉冲信号,从而使蜂…

python-numpy(3)-线性代数

一、方程求解 参考资料 对于Ax b 这种方程&#xff1a; np.linalg.inv(A).dot(B)np.linalg.solve(A,b) 1.1 求解多元一次方程一个直观的例子 # AXB # X A^(-1)*B A np.array([[7, 3, 0, 1], [0, 1, 0, -1], [1, 0, 6, -3], [1, 1, -1, -1]]) B np.array([8, 6, -3, 1]…

嵌入式单片机 TTL电平、232电平、485电平的区别和联系

一、简介 TTL、232和485是常见的串口通信标准&#xff0c;它们在电平和通信方式上有所不同&#xff0c; ①一般情况下TTL电平应用于单片机外设&#xff0c;属于MCU/CPU等片外外设&#xff1b; ②232/485电平应用于产品整体对外的接口&#xff0c;一般是片外TTL串口转232/485…

【C语言】每日一题,快速提升(1)!

调整数组使奇数全部都位于偶数前面 题目&#xff1a; 输入一个整数数组&#xff0c;实现一个函数 来调整该数组中数字的顺序使得数组中所有的奇数位于数组的前半部分 所有偶数位于数组的后半部分 解题思路&#xff1a; 给定两个下标left和right&#xff0c;left放在数组的起始…

Linux三剑客-sed、awk、egrep(上)

一、知识梗概 二、正则表达式 定义&#xff1a;正则表达式是一种强大的文本处理工具&#xff0c;用于在文本中搜索符合特定模式的字符串。它由一系列特殊字符和普通字符组成&#xff0c;可以定义复杂的搜索模式。正则表达式被广泛应用于各种编程语言和文本处理工具中。 简单来…