正点原子嵌入式linux驱动开发——Linux Regmap驱动

在前面学习I2C和SPI驱动的时候,针对I2C和SPI设备寄存器的操作都是通过相关的API函数进行操作的。这样Linux内核中就会充斥着大量的重复、冗余代码,但是这些本质上都是对寄存器的操作,所以为了方便内核开发人员统一访问I2C/SPI设备的时候,为此引入了Regmap子系统,本章就来学习一下如何使用Regmap API函数来读写I2C/SPI设备寄存器。

Regmap API简介

Regmap

Linux下大部分设备的驱动开发都是操作其内部寄存器,比如I2C/SPI设备的本质都是一样的,通过I2C/SPI接口读写芯片内部寄存器。芯片内部寄存器也是同样的道理,比如STM32MP157的PWM、TIM等外设初始化,最终都是要落到寄存器的设置上。

Linux下使用i2c_transfer来读写I2C设备中的寄存器,SPI接口的话使用spi_write/spi_read等。I2C/SPI芯片又非常的多,因此Linux内核里面就会充斥了大量的i2c_transfer这类的冗余代码,再者,代码的复用性也会降低。比如icm20608这个芯片既支持I2C接口,也支持SPI接口。假设在产品设计阶段一开始将icm20608设计为SPI接口,但是后面发现SPI接口不够用,或者SOC的引脚不够用,需要将icm20608改为I2C接口。这个时候icm20608的驱动就要大改,需要将SPI接口函数换为I2C的,工作量比较大

基于代码复用的原则,Linux内核引入了regmap模型,regmap将寄存器访问的共同逻辑抽象出来,驱动开发人员不需要再去纠结使用SPI或者I2C接口API函数,统一使用regmap API函数。这样的好处就是统一使用regmap,降低了代码冗余,提高了驱动的可以移植性。regmap模型的重点在于:通过regmap模型提供的统一接口函数来访问器件的寄存器,SOC内部的寄存器也可以使用regmap接口函数来访问。

regmap是Linux内核为了减少慢速I/O在驱动上的冗余开销,提供了一种通用的接口来操作硬件寄存器。另外,regmap在驱动和硬件之间添加了cache,降低了低速I/O的操作次数,提高了访问效率,缺点是实时性会降低。

regmap的应用场景有如下所示:

  1. 硬件寄存器操作,比如选用通过I2C/SPI接口来读写设备的内部寄存器,或者需要读写SOC内部的硬件寄存器。
  2. 提高代码复用性和驱动一致性,简化驱动开发过程。
  3. 减少底层 I/O 操作次数,提高访问效率。

本章教程就来重点学习一下如何将SPI接口的icm20608驱动改为使用regmap API。

Regmap驱动框架

regmap框架结构

regmap驱动框架如下图所示:

regmap框架

regmap框架分为三层:

  1. 底层物理总线:regmap就是对不同的物理总线进行封装,目前regmap支持的物理总线有i2c、i3c、spi、mmio、sccb、sdw、slimbus、irq、spmi和w1。
  2. regmap核心层,用于实现regmap,不用关心具体实现。
  3. regmapAPI抽象层,regmap向驱动编写人员提供的API接口,驱动编写人员使用这些API接口来操作具体的芯片设备,也是驱动编写人员重点要掌握的

regmap结构体

Linux内核将regmap框架抽象为regmap结构体,这个结构体定义在文件include/linux/regmap.h 中,结构体内容如下(有缩减):

示例代码 55.1.2.1 regmap 结构体
49  struct regmap {
50      union {
51          struct mutex mutex;
52          struct {
53              spinlock_t spinlock;
54              unsigned long spinlock_flags;
55          };
56      };
57      regmap_lock lock;
58      regmap_unlock unlock;
59      void *lock_arg; /* This is passed to lock/unlock functions */
60      gfp_t alloc_flags;
......
89      unsigned int max_register;
90      bool (*writeable_reg)(struct device *dev, unsigned int reg);
91      bool (*readable_reg)(struct device *dev, unsigned int reg);
92      bool (*volatile_reg)(struct device *dev, unsigned int reg);
93      bool (*precious_reg)(struct device *dev, unsigned int reg);
94      bool (*writeable_noinc_reg)(struct device *dev,
unsigned int reg);
95      bool (*readable_noinc_reg)(struct device *dev,
unsigned int reg);
96      const struct regmap_access_table *wr_table;
97      const struct regmap_access_table *rd_table;
98      const struct regmap_access_table *volatile_table;
99      const struct regmap_access_table *precious_table;
100     const struct regmap_access_table *wr_noinc_table;
101     const struct regmap_access_table *rd_noinc_table;
102
103     int (*reg_read)(void *context, unsigned int reg,
unsigned int *val);
104     int (*reg_write)(void *context, unsigned int reg,
unsigned int val);
105     int (*reg_update_bits)(void *context, unsigned int reg,
106     unsigned int mask, unsigned int val);
......
159
160     struct rb_root range_tree;
161     void *selector_work_buf; /* Scratch buffer used for selector */
162
163     struct hwspinlock *hwlock;
164 };

要使用regmap,肯定要先给驱动分配一个具体的regmap结构体实例,一会讲解如何分配regmap实例。可以看到示例代码55.1.2.1中第90-101行有很多的函数以及table,这些需要驱动编写人员根据实际情况选择性的初始化,regmap的初始化通过结构体regmap_config来完成

regmap_config结构体

regmap_config结构体就是用来初始化regmap的,这个结构体也定义在include/linux/regmap.h 文件中,结构体内容如下:

示例代码 55.1.2.2 regmap_config 结构体
352 struct regmap_config {
353     const char *name;
354
355     int reg_bits;
356     int reg_stride;
357     int pad_bits;
358     int val_bits;
359
360     bool (*writeable_reg)(struct device *dev, unsigned int reg);
361     bool (*readable_reg)(struct device *dev, unsigned int reg);
362     bool (*volatile_reg)(struct device *dev, unsigned int reg);
363     bool (*precious_reg)(struct device *dev, unsigned int reg);
364     bool (*writeable_noinc_reg)(struct device *dev,
unsigned int reg);
365     bool (*readable_noinc_reg)(struct device *dev,
unsigned int reg);
366
367     bool disable_locking;
368     regmap_lock lock;
369     regmap_unlock unlock;
370     void *lock_arg;
371
372     int (*reg_read)(void *context, unsigned int reg,
unsigned int *val);
373     int (*reg_write)(void *context, unsigned int reg,
unsigned int val);
374
375     bool fast_io;
376
377     unsigned int max_register;
378     const struct regmap_access_table *wr_table;
379     const struct regmap_access_table *rd_table;
380     const struct regmap_access_table *volatile_table;
381     const struct regmap_access_table *precious_table;
382     const struct regmap_access_table *wr_noinc_table;
383     const struct regmap_access_table *rd_noinc_table;
384     const struct reg_default *reg_defaults;
385     unsigned int num_reg_defaults;
386     enum regcache_type cache_type;
387     const void *reg_defaults_raw;
388     unsigned int num_reg_defaults_raw;
389
390     unsigned long read_flag_mask;
391     unsigned long write_flag_mask;
392     bool zero_flag_mask;
393
394     bool use_single_read;
395     bool use_single_write;
396     bool can_multi_write;
397
398     enum regmap_endian reg_format_endian;
399     enum regmap_endian val_format_endian;
400
401     const struct regmap_range_cfg *ranges;
402     unsigned int num_ranges;
403
404     bool use_hwlock;
405     unsigned int hwlock_id;
406     unsigned int hwlock_mode;
407 };

Linux内核里面已经对regmap_config各个成员变量进行了详细的讲解,这里只看一些比较重要的:

第353行name:名字。

第355行reg_bits:寄存器地址位数,必填字段。

第356行reg_stride:寄存器地址步长。

第357行pad_bits:寄存器和值之间的填充位数。

第358行val_bits:寄存器值位数,必填字段。

第360行writeable_reg:可选的可写回调函数,寄存器可写的话此回调函数就会被调用,并返回true。

第361行readable_reg:可选的可读回调函数,寄存器可读的话此回调函数就会被调用,并返回true。

第362行volatile_reg:可选的回调函数,当寄存器值不能缓存的时候此回调函数就会被调用,并返回 true。

第363行precious_reg:当寄存器值不能被读出来的时候此回调函数会被调用,比如很多中断状态寄存器读清零,读这些寄存器就可以清除中断标志位,但是并没有读出这些寄存器内部的值。

第372行reg_read:可选的读操作回调函数,所有读寄存器的操作此回调函数就会执行。

第373行reg_write:可选的写操作回调函数,所有写寄存器的操作此回调函数就会执行。

第375行fast_io:快速I/O,使用spinlock替代mutex来提升锁性能。

第377行max_register:有效的最大寄存器地址,可选。

第378行wr_table:可写的地址范围,为regmap_access_table结构体类型。后面的rd_table、volatile_table、precious_table、wr_noinc_table和rd_noinc_table同理。

第384行reg_defaults:寄存器模式值,为reg_default结构体类型,此结构体有两个成员变
量:reg和def,reg是寄存器地址,def是默认值。

第385行num_reg_defaults:默认寄存器表中的元素个数。

第390行read_flag_mask:读标志掩码。

第391行write_flag_mask:写标志掩码。

关于regmap_config 结构体成员变量就介绍这些,其他没有介绍的自行查阅Linux内核中的相关描述。

Regmap操作函数

Regmap申请与初始化

regmap支持多种物理总线,比如I2C和SPI,需要根据所使用的接口来选择合适的regmap初始化函数。Linux内核提供了针对不同接口的regmap初始化函数,SPI接口初始化函数为regmap_init_spi,函数原型如下:

struct regmap * regmap_init_spi(struct spi_device *spi,
								const struct regmap_config *config)

函数参数和返回值含义如下:

  • spi:需要使用regmap的spi_device。
  • config:regmap_config结构体,需要程序编写人员初始化一个regmap_config实例,然后将其地址赋值给此参数。
  • 返回值:申请到的并进过初始化的regmap。

I2C接口的regmap初始化函数为regmap_init_i2c,函数原型如下:

struct regmap * regmap_init_i2c(struct i2c_client *i2c,
								const struct regmap_config *config)

函数参数和返回值含义如下:

  • i2c:需要使用regmap的i2c_client。
  • config:regmap_config结构体,需要程序编写人员初始化一个regmap_config实例,然后将其地址赋值给此参数。
  • 返回值:申请到的并进过初始化的regmap。

还有很多其他物理接口对应的regmap初始化函数,这里就不介绍了,可以直接查阅Linux内核即可,基本和SPI/I2C的初始化函数相同。

退出驱动的时候需要释放掉申请到的regmap不管是什么接口,全部使用regmap_exit这个函数来释放regmap,函数原型如下:

void regmap_exit(struct regmap *map)

函数参数和返回值含义如下:

  • map:需要释放的regmap。
  • 返回值:无。

一般会在probe函数中初始化regmap_config,然后申请并初始化regmap

regmap设备访问API函数

不管是I2C还是SPI等接口,还是SOC内部的寄存器,对于寄存器的操作就两种:读和写。regmap提供了最核心的两个读写操作:regmap_read和regmap_write。这两个函数分别用来读/写寄存器,regmap_read函数原型如下:

int regmap_read(struct regmap *map, 
				unsigned int reg, 
				unsigned int *val)

函数参数和返回值含义如下:

  • map:要操作的regmap。
  • reg:要读的寄存器。
  • val:读到的寄存器值。
  • 返回值:0,读取成功;其他值,读取失败。

regmap_write函数原型如下:

int regmap_write(struct regmap *map, 
				 unsigned int reg, 
				 unsigned int val)

函数参数和返回值含义如下:

  • map:要操作的regmap。
  • reg:要写的寄存器。
  • val:要写的寄存器值。
  • 返回值:0,写成功;其他值,写失败。

在regmap_read和regmap_write的基础上还衍生出了其他一些regmap的API函数,首先是regmap_update_bits函数,此函数用来修改寄存器指定的bit,函数原型如下:

int regmap_update_bits (struct regmap *map, 
						unsigned int reg,
						unsigned int mask, 
						unsigned int val)

函数参数和返回值含义如下:

  • map:要操作的regmap。
  • reg:要操作的寄存器。
  • mask:掩码,需要更新的位必须在掩码中设置为1。
  • val:需要更新的位值。
  • 返回值:0,写成功;其他值,写失败。

比如要将寄存器的bit1和bit2置1,那么mask应该设置为0X00000011,此时val的bit1和bit2应该设置为1,也就是0Xxxxxxx11。如果要清除寄存器的bit4和bit7,那么mask应该设置为0X10010000,val的bit4和bit7设置为0,也就是0X0xx0xxxx

接下来看一下regmap_bulk_read函数,此函数用于读取多个寄存器的值,函数原型如下:

int regmap_bulk_read(struct regmap *map, 
					 unsigned int reg, 
					 void *val,
					 size_t val_count)

函数参数和返回值含义如下:

  • map:要操作的regmap。
  • reg:要读取的第一个寄存器。
  • val:读取到的数据缓冲区。
  • val_count:要读取的寄存器数量。
  • 返回值:0,写成功;其他值,读失败。

另外也有多个寄存器写函数regmap_bulk_write,函数原型如下:

int regmap_bulk_write(struct regmap *map, 
 					  unsigned int reg, 
 					  const void *val,
 					  size_t val_count)

函数参数和返回值含义如下:

  • map:要操作的regmap。
  • reg:要写的第一个寄存器。
  • val:要写的寄存器数据缓冲区。
  • val_count:要写的寄存器数量。
  • 返回值:0,写成功;其他值,读失败。

关于regmap常用到API函数就讲解到这里,还有很多其他功能的API函数,可以自行查阅Linux内核即可,内核里面对每个API函数都有详细的讲解。

regmap_config掩码设置

结构体regmap_config里面有三个关于掩码的成员变量:read_flag_mask、write_flag_mask和zero_flag_mask,这三个掩码非常重要,本节来学习一下如何使用这三个掩码。在学习icm20608的时候讲过了,icm20608支持i2c和spi接口,但是当使用spi接口的时候,读取icm20608寄存器的时候地址最高位必须置1,写内部寄存器的是时候地址最高位要设置为0。因此这里就涉及到对寄存器地址最高位的操作,在之前的SPI驱动实验中在使用SPI接口函数读取icm20608内部寄存器的时候手动将寄存器地址的最高位置1,代码如下所示:

示例代码 55.1.4.1 icm20608 驱动
1  static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg,
void *buf, int len)
2  {
3 
......
21     txdata[0] = reg | 0x80; /* 写数据的时候首寄存器地址 bit7 要置 1 */ 
22     t->tx_buf = txdata; /* 要发送的数据 */
23     t->rx_buf = rxdata; /* 要读取的数据 */
24     t->len = len+1; /* t->len=发送的长度+读取的长度 */
25     spi_message_init(&m); /* 初始化 spi_message */
26     spi_message_add_tail(t, &m);
27     ret = spi_sync(spi, &m); /* 同步发送 */
......
39     return ret;
40 }

示例代码55.1.4.1就是标准的SPI驱动,其中第21行,将寄存器的地址bit7置1,表示这是一个读操作。

当使用regmap的时候就不需要手动将寄存器地址的bit7置 1,在初始化regmap_config的时候直接将read_flag_mask设置为0X80即可,这样通过regmap读取SPI内部寄存器的时候就会将寄存器地址与read_flag_mask进行或运算,结果就是将bit7置1,但是整个过程不需要自行操作,全部由regmap框架来完成的。

同理write_flag_mask用法也一样的,只是write_flag_mask用于写寄存器操作。

打开regmap-spi.c文件,这个文件就是regmap的spi总线文件,找到如下所示内容:

regmap_spi.c代码段

第101-110行初始化了一个regmap_bus实例:regmap_spi,重点看一下第107行中read_flag_mask默认为0X80。注意,这里是将regmap_bus的read_flag_mask成员变量设置为0X80。regmap_bus结构体大家自行查看一下,这里就不讲了。

第112-119行__regmap_init_spi函数,前面说了要想在spi总线中使用regmap框架,首先要使用regmap_init_spi函数用于并申请一个SPI总线的regmapregmap_init_spi函数会调用这里的__regmap_init_spi函数,从第117行可以看出__regmap_init_spi函数只是对__regmap_init的简单封装,因此最终完成regmap申请并初始化的是__regmap_init函数。在__regmap_init函数中找到如下所示内容:

__regmap_init函数代码段

第812-817行就是用regmap_config中的读写掩码来初始化regmap_bus中的掩码。由于regmap_spi默认将read_flag_mask设置为0X80,当所使用的SPI设备不需要读掩码,在初始化regmap_config的时候一定要将read_flag_mask设置为0X00,并且也要将zero_flag_mask设置为true

regmap框架就讲解到这里,接下来学习如何之前SPI驱动实验中编写的icm20608驱动改为regmap框架。

实验程序编写

修改设备结构体,添加regmap和regmap_config

regmap框架的核心就是regmap和regmap_config结构体,一般都是在自定义的设备结构体里面添加这两个类型的成员变量,所以首先在icm20608_dev结构体里面添加regmap和regmap_config。

只需要在icm20608_dev设备结构体中,添加regmap结构体变量指针regmap,以及regmap_config结构体变量regmap_config就可以了。

初始化regmap

一般在probe函数中初始化regmap,本章节就是icm20608_probe函数。

在原先的基础上,添加regmap_config的初始化,配置icm20608dev这个设备结构体的regmap_config成员变量就好了,设置reg_bits=8,val_bits=8,read_flag_mask=0x80。

然后配置icm20608dev的regmap变量,由regmap_init_spi来进行初始化配置SPI总线的regmap。

同理,需要注销的话就是在remove函数中,添加regmap_exit来注销就可以了。

读写设备内部寄存器

在read的操作函数中,可以直接通过regmap_read来进行寄存器读取;在write的函数中,通过regmap_write来写入。

在最后的readdata函数中,可以通过regmap_buld_read来进行多个寄存器的读取。

运行测试

测试APP直接用之前SPI驱动编写的icm20608App.c即可。测试方法也和之前一样,输入如下命令:

depmod //第一次加载驱动的时候需要运行此命令
modprobe icm20608.ko //加载驱动模块
./icm20608App /dev/icm20608 //app 读取内部数据

如果regmap API工作正常,那么就会正确的初始化icm20608,并且读出传感器数据,结果和SPI驱动是一样的,如下图所示:

获取到的ICM20608数据

IIC总线的regmap框架基本和SPI一样,只是需要使用regmap_init_i2c来申请并初始化对应的regmap,同样都是使用regmap_read和regmap_write来读写I2C设备内部寄存器

总结

Regmap就是对IIC、SPI总线的再次封装,可以通过regmap_init_xxx(xxx就是你的具体总线名称),然后通过regmap_read和regmap_write来进行单个寄存器的读写操作就可以了。

也就是说,之前的IIC和SPI总线的实验,需要自己去翻手册,然后对寄存器进行读写,如果使用Regmap,就可以用一样的函数来进行读写了,相当于代码复用不用重新编写了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法 | 模拟No.3】leetcode 38. 外观数列

个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【Leetcode】 🍔本专栏旨在提高自己算法能力的同时,记录一下自己的学习过程,希望…

技术分享 | app自动化测试(Android)--App 控件定位

客户端的页面通过 XML 来实现 UI 的布局,页面的 UI 布局作为一个树形结构,而树叶被定义为节点。这里的节点也就对应了要定位的元素,节点的上级节点,定义了元素的布局结构。在 XML 布局中可以使用 XPath 进行节点的定位。 App的布…

openGauss学习笔记-117 openGauss 数据库管理-设置数据库审计-查看审计结果

文章目录 openGauss学习笔记-117 openGauss 数据库管理-设置数据库审计-查看审计结果117.1 前提条件117.2 背景信息117.3 操作步骤 openGauss学习笔记-117 openGauss 数据库管理-设置数据库审计-查看审计结果 117.1 前提条件 审计功能总开关已开启。需要审计的审计项开关已开…

Java EE进阶2

包如果下载不下来怎么办? 1,确认包是否存在 2.如果包存在就多下载几次 3.如果下载了很多次都下载不下来,看看是不是下面几步出现了问题? 1)是否配置了国内源 settings.xml 2)目录是否为全英文,存在中文的话就修改路径 3)删除本地仓库的 jar 包,重新下载(可能由于网络的原…

大语言模型幻觉解决方案综述

论文题目:《Cognitive Mirage: A Review of Hallucinations in Large Language Models》 论文链接:https://arxiv.org/abs/2309.06794v1 论文代码:https://github.com/hongbinye/cognitive-mirage-hallucinations-in-llms 技术交流群 建了…

【QT】设置焦点及光标位置

很高兴在雪易的CSDN遇见你 ,给你糖糖 欢迎大家加入雪易社区-CSDN社区云 前言 本文分享Qt中如何设置焦点和光标位置的解决方案,并给出常见的问题解决方案,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(…

第19章_体系结构

文章目录 1. 逻辑架构剖析1.1 服务器处理客户端请求1.2 Connectors1.3 第1层:连接层1.4 第2层:服务层1.4.1 SQL Interface: SQL接口1.4.2 Parser: 解析器1.4.3 Optimizer: 查询优化器1.4.4 Caches & Buffers: 查询缓存组件 1.5 第3层&…

uniApp获取当前位置经纬度

以下是使用uni.getLocation获取当前位置的示例代码: 调用uni.getLocation方法获取当前位置信息 uni.getLocation({type: wgs84, // 坐标类型,默认为wgs84,可选的值为gcj02和bd09llsuccess: res > {// 获取成功,经度和纬度在r…

Java Spring Boot----ruoyi项目部署 前后端分离

nginx服务器部署java服务器部署db服务器部署配置打包环境配置前端打包环境(java服务器)配置后端打包环境获取代码 前端代码打包后端代码打包项目上线前端项目上线后端项目上线 将jar包传送到后端服务器导入初始化数据 ip主机名服务名称192.168.20.138ngi…

【pyspider】爬取ajax请求数据(post),如何处理python2字典的unicode编码字段?

情景:传统的爬虫只需要设置fetch_typejs即可,因为可以获取到整个页面。但是现在ajax应用越来越广泛,所以有的网页不能用此种爬虫类型来获取页面的数据,只能用slef.crawl()来发起http请求来抓取数据。 直接上例子: 可以…

webgoat-Request Forgeries 请求伪造

(A8:2013) Request Forgeries Cross-Site Request Forgeries 跨站请求伪造,又称一键攻击或会话骑乘,简称CSRF (有时发音为 sea-surf)或 XSRF,是一种恶意利用网站,其中传输未经授权的命令 来自网站信任的用…

【电路笔记】-并联RLC电路分析

并联RLC电路分析 文章目录 并联RLC电路分析1、概述2、AC的行为3、替代配置3.1 带阻滤波器3.2 带通滤波器 4、总结 电子器件三个基本元件的串联行为已在我们之前的文章系列 RLC 电路分析中详细介绍。 在本文中,介绍了另一种称为并联 RLC 电路的关联。 在第一部分中&a…

浅析刚入门Python初学者的注意事项

文章目录 一、注意你的Python版本1.print()函数2.raw_input()与input()3.比较符号&#xff0c;使用!替换<>4.repr函数5.exec()函数 二、新手常遇到的问题1、如何写多行程序&#xff1f;2、如何执行.py文件&#xff1f;3、and&#xff0c;or&#xff0c;not4、True和False…

Visual Studio 2010 软件安装教程(附下载链接)——计算机二级专用编程软件

下载链接&#xff1a; 提取码:2wAKhttps://www.123pan.com/s/JRpSVv-9injv.html 安装步骤如下&#xff1a; 1.如图所示&#xff0c;双击打开【Visual Studio 2010简体中文旗舰版】文件夹 2.如图所示&#xff0c;找到“Setup”文件夹打开&#xff0c;双击运行“setup” 3.如图…

网站源码备份 [极客大挑战 2019]PHP1

打开题目 题目提示我们备份网站 我们输入/www.zip 下载zip文件&#xff0c;打开发现 打开index.php <?phpinclude class.php;$select $_GET[select];$resunserialize($select);?> 文件包含class.php&#xff0c;get传参一个select函数&#xff0c;反序列化select参…

MySQL第七讲·怎么利用聚合函数实现高效地分组统计?

你好&#xff0c;我是安然无虞。 文章目录 聚合函数&#xff1a;怎么高效地进行分组统计&#xff1f;sum( )avg( ) & max( ) & min( )count( ) 聚合函数&#xff1a;怎么高效地进行分组统计&#xff1f; MySQL中有5种聚合函数较为常用&#xff0c;分别是求和函数sum(…

智能网联汽车基础软件信息安全需求分析

目录 1.安全启动 2.安全升级 3.安全存储 4.安全通信 5.安全调试 6.安全诊断 7.小结 1.安全启动 对于MCU&#xff0c;安全启动主要是以安全岛BootROM为信任根&#xff0c;在MCU启动后&#xff0c;用户程序运行前&#xff0c;硬件加密模块采用逐级校验、并行校验或者混合校…

[AndroidStudio]_[初级]_[修改虚拟设备镜像文件的存放位置]

场景 在使用Android Studio的虚拟设备运行App时&#xff0c;需要创建很大镜像文件。这些镜像文件一般都在系统盘&#xff0c;导致系统盘占用增大。怎么把这些镜像的存放路径设置在其他盘&#xff1f; 说明 虚拟设备的和它的镜像默认是放在用户目录\.android\avd位置。如果是在…

python-全自动二维码识别

纯笔记&#xff0c;可以做到全屏识别二维码&#xff0c;自动识别&#xff0c;复制链接&#xff0c;生成简单的二维码&#xff0c;将识别到的内容转为txt import pyautogui from PIL import Image from pyzbar.pyzbar import decode import tkinter as tk from tkinter import …

javaee实验:搭建maven+spring boot开发环境,开发“Hello,Spring Boot”应用

目录 mavenspringboot实验目的实验内容环境的搭建 在开发中&#xff0c;maven和spring都是非常常用、非常重要的管理工具和框架&#xff0c;今天就在这里使用idea进行环境的搭建和创建第一个spring程序 maven 1.1maven是一个跨平台的项目管理工具&#xff08;主要管理jar包&am…