Linux驱动开发——LED驱动开发

文章目录

  • 1 概述
    • 1.1 说明
  • 2 基础知识
    • 2.1 地址映射
      • 2.1.1 ioremap函数
      • 2.1.2 iounmap函数
    • 2.2 I/O内存访问函数
      • 2.2.1 读操作函数
      • 2.2.2 写操作函数
  • 3 硬件原理图分析
  • 4 RK3568 GPIO驱动原理
    • 4.1 引脚复用设置
    • 4.2 引脚驱动能力配置
    • 4.3 GPIO输入输出设置
    • 4.4 GPIO引脚高低电平设置
  • 5 实验程序编写
    • 5.1 配置头文件路径
    • 5.2 驱动代码
    • 5.3 测试应用程序
    • 5.4 驱动测试
      • 5.4.1 关闭心跳灯
      • 5.4.2 编写Makefile并编译
      • 5.4.3 测试驱动程序

系列文章
Linux驱动开发——字符设备驱动开发

1 概述

1.1 说明

本文是学习rk3568开发板驱动开发的记录,代码依托于rk3568开发板。
Linux下外设驱动,最终都是配置相应的硬件寄存器,本文中的LED灯驱动也是对rk3568的io口进行配置。

2 基础知识

2.1 地址映射

在编写驱动之前,需要先了解一下MMU,MMU(Memory Manage Unit),是内存管理单元,老版本的Linux中要求处理器必须有MMU,但是现在Linux内核已经支持无MMU的处理器了。MMU主要完成的功能有:

  1. 完成虚拟空间到物理空间的映射
  2. 内存保护,设置寄存器的访问呢权限,设置虚拟存储空间的缓冲特性
    首先看第1点,虚拟空间到物理空间的映射,也叫地址映射。首先了解两个地址概念:虚拟地址、物理地址。对于32位处理器来说,虚拟地址范围是 2^32=4GB(64 位的处理器则是 2^64=18.45 x 10^18 GB,即从 0 到 2^64-1 的范围。这个地址范围比 32 位处理器的地址范围要大得多,可以支持更大的内存空间,提高了计算机的性能)。例如我们的开发板上有 1GB 的 DDR3,这 1GB 的内存就是物理内存,经过 MMU 可以将其映射到整个 4GB 的虚拟空间,如下图所示:
    在这里插入图片描述
    物理内存只有1G,虚拟内存有4G,肯定存在多个虚拟地址映射同一个物理地址空间,这个会由处理器进行处理。
    Linux内核启动的时候会初始化MMU,设置好内存映射,设置好之后CPU访问的都是虚拟地址。比如 RK3568 的 GPIO0_C0 引脚的 IO 复用寄存器 PMU_GRF_GPIO0C_IOMUX_L 物理地址为 0xFDC20010。如果没有开启 MMU 的话直接向 0xFDC20010)这个寄存器地址写入数据就可以配置 GPIO0_C0 的引脚的复用功能。现在开启了 MMU,并且设置了内存映射,因此就不能直接向 0xFDC20010 这个地址写入数据了。我们必须得到 0xFDC20010 这个物理地址在Linux 系统里面对应的虚拟地址,这里就涉及到了物理内存和虚拟内存之间的转换,需要用到两个函数:ioremap 和 iounmap。

2.1.1 ioremap函数

ioremap 函 数 用 于 获 取 指 定 物 理 地 址 空 间 对 应 的 虚 拟 地 址 空 间 , 定 义 在arch/arm/include/asm/io.h 文件中,定义如下:

void __iomem *ioremap(resource_size_t res_cookie, size_t size);

实现如下:

void __iomem *ioremap(resource_size_t res_cookie, size_t size)
{
return arch_ioremap_caller(res_cookie, size, MT_DEVICE,
__builtin_return_address(0));
}
EXPORT_SYMBOL(ioremap);

这些参数和返回值的含义如下:

  • res_cookie:要映射的物理起始地址。
  • size:要映射的内存空间大小。
  • 返回值:__iomem 类型的指针,指向映射后的虚拟空间首地址。

2.1.2 iounmap函数

卸载驱动的时候需要使用 iounmap 函数释放掉 ioremap 函数所做的映射,iounmap 函数原型如下:

void iounmap (volatile void __iomem *addr)

2.2 I/O内存访问函数

当外部寄存器或内存映射到 IO 空间时,称为 I/O 端口。当外部寄存器或内存映射到内存空间时,称为 I/O 内存。但是对于 ARM 来说没有 I/O 空间这个概念,因此 ARM 体系下只有 I/O 内存(可以直接理解为内存)。使用 ioremap 函数将寄存器的物理地址映射到虚拟地址以后,我们就可以直接通过指针访问这些地址,但是 Linux 内核不建议这么做,而是推荐使用一组操作函数来对映射后的内存进行读写操作。

2.2.1 读操作函数

u8 readb(const volatile void __iomem *addr)
u16 readw(const volatile void __iomem *addr)
u32 readl(const volatile void __iomem *addr)

readb、readw 和 readl 这三个函数分别对应 8bit、16bit 和 32bit 读操作,参数 addr 就是要读取写内存地址,返回值就是读取到的数据。

2.2.2 写操作函数

void writeb(u8 value, volatile void __iomem *addr)
void writew(u16 value, volatile void __iomem *addr)
void writel(u32 value, volatile void __iomem *addr)

writeb、writew 和 writel 这三个函数分别对应 8bit、16bit 和 32bit 写操作,参数 value 是要写入的数值,addr 是要写入的地址。

3 硬件原理图分析

在这里插入图片描述
LED 接到了 GPIO0_C0(WORKING_LEDN_H)上,当 GPIO0_C0 输出高电平(1)的时候 Q1 这个三极管就能导通,LED (DS1)这个绿色的发光二极管就会点亮。当GPIO0_C0 输出低电平(0)的时候 Q1 这个三极管就会关闭,发光二极管 LED (DS1)不会导通,因此 LED 也就不会点亮。所以 LED 的亮灭取决于 GPIO0_C0 的输出电平,输出 1 就亮,输出 0 就灭。

4 RK3568 GPIO驱动原理

4.1 引脚复用设置

rk3568的一个引脚一般用多个功能,也就是引脚复用,比如 GPIO0_C0 这个 IO 就可以用作:GPIO,PWM1_M0,GPU_AVS 和 UART0_RX 这四个功能,这里使用的是GPIO功能。
rk3568芯片有5组GPIO,这里使用的是GPIO0这组中的C0这个端口,首先从芯片的参考手册中,找到对应寄存器的地址。
在这里插入图片描述
所有GPIO相关的寄存器都属于PMU_GRF,查询,其中的PMU是电源管理模块,GRF是通用寄存器。基地址是0xFDC20000。
在这里插入图片描述
这个就是GPIOC相关的两个寄存器,偏移地址是0x0010和0x0014,大小是4个字节。低寄存器控制GPIO0中的C0-C3这4个引脚的复用,高寄存器控制C4-C7这4个引脚的复用。
在这里插入图片描述
首先看该寄存器的地址,由基址+偏移地址,0xFDC20000+0x0010=0xFDC20010。
一共4字节,32位,其中的高16位是对于低16位的使能位,0对应16,15对应31。只有高16位对应的使能之后,低16位的设置才生效。
低16位,分4组,每组3位加1位预留,用于表示对应GPIO引脚的复用功能。3个bit可以表示8种功能,这里最多的是5种复用功能。
以现在要使用的GPIO0_C0引脚为例,该引脚有4种复用功能:

  • 0 :GPIO0_C0
  • 1 :PWM1_M0
  • 2:GPU_AVS
  • 3 :UART0_RX
    如果要使用GPIO功能,则需要配置bit2:0为000,bit18:16为111,对应的这四个字节为0x00070000。

4.2 引脚驱动能力配置

引脚驱动能力的配置,在PMU_GRF_GPIOC_DS_0这个寄存器中
在这里插入图片描述
该寄存器的地址是:0xFDC20000 + 0x0090 = 0xFDC20090
该寄存器也分为两部分,高16位是对于低16位的使能位,低16位是驱动能力配置。0-5这6个bit用于定义C0的驱动能力,8-13这6个bit用于定义C1的驱动能力。
驱动能力一共有6级,这里设置为5级,具体原因不太清楚,可能跟硬件参数相关。
那么0-5设置为111111,同时需要设置使能位16-21为111111

4.3 GPIO输入输出设置

GPIO是双向的,既可以做输入,也可以做输出。这里是使用GPIO口来控制LED的亮灭,因此这里设置成输出。GPIO_SWPORT_DDR_L 和 GPIO_SWPORT_DDR_H 这两个寄存器用于设置 GPIO 的输入输出功能。RK3568 一共有 GPIO0、GPIO1、GPIO2、GPIO3 和GPIO4 这五组 GPIO。其中 GPIO0-3 这四组每组都有 A0-A7、B0-B7、C0-C7 和 D0~D7 这 32个 GPIO。每个 GPIO 需要一个 bit 来设置其输入输出功能,一组 GPIO 就需要 32bit,GPIO_SWPORT_DDR_L 和GPIO_SWPORT_DDR_H 这两个寄存器就是用来设置这一组 GPIO所 有 引 脚 的 输 入 输 出 功 能 的 。 其 中 GPIO_SWPORT_DDR_L 设 置 的 是 低 16bit ,GPIO_SWPORT_DDR_H 设置的是高 16bit。一组GPIO引脚对应如下:
在这里插入图片描述
GPIO_SWPORT_DDR_H寄存器也是base+offset。其中基地址为:
在这里插入图片描述
对于GPIO0_C0来说,其对应寄存器地址为:0xFDD60000 + 0x000C = 0x
FDD6000C。
在这里插入图片描述
也是32位寄存器,其中高16位是控制低16位的使能位。低16位对应GPIO的16个端口的输入输出设置,1是输出,0是输入。

4.4 GPIO引脚高低电平设置

GPIO的引脚高低电平设置和输入输出设置的原理是一样的,只是使用的寄存器不同,使用的是GPIO_SWPORT_DR_L 和GPIO_SWPORT_DR_H。
在这里插入图片描述

5 实验程序编写

5.1 配置头文件路径

目前开发版刷入的版本不是Linux系统,是Android11系统,所以这里依赖的是Android11系统的kernel头文件,测试程序的编译则使用ndk工具进行编译
首先在vscode中配置头文件依赖路径,配置c_cpp_properties.json文件

{"configurations": [{"name": "Linux","includePath": ["${workspaceFolder}/**","/home/alientek/code/atk-rk3568-11/kernel/arch/arm64/include","/home/alientek/code/atk-rk3568-11/kernel/include","/home/alientek/code/atk-rk3568-11/kernel/arch/arm64/include/generated"],"defines": [],"compilerPath": "/usr/bin/gcc","cStandard": "c17","cppStandard": "gnu++17","intelliSenseMode": "linux-gcc-x64"}],"version": 4
}

5.2 驱动代码

#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define LED_MAJOR 200
#define LED_NAME "led"#define LEDOFF 0
#define LEDON 1#define PMU_GRF_BASE 0xFDC20000
#define PMU_GRF_GPIO0C_IOMUX_L (PMU_GRF_BASE + 0x0010)
#define PMU_GRF_GPIO0C_DS_0 (PMU_GRF_BASE + 0x0090)#define GPIO0_BASE 0xFDD60000
#define GPIO0_SWPORT_DR_H (GPIO0_BASE + 0x0004)
#define GPIO0_SWPORT_DDR_H (GPIO0_BASE + 0x000C)static void __iomem* PMU_GRF_GPIO0C_IOMUX_L_PI;
static void __iomem* PMU_GRF_GPIO0C_DS_0_PI;
static void __iomem* GPIO0_SWPORT_DR_H_PI;
static void __iomem* GPIO0_SWPORT_DDR_H_PI;void led_switch(u8 sta)
{u32 val = 0;if (sta == LEDON){val = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0x01 << 0);val |= ((0x01 << 16) | (0x1 << 0));writel(val, GPIO0_SWPORT_DR_H_PI);}else if (sta == LEDOFF){val = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0x01 << 0);val |= ((0x01 << 16) | (0x0 << 0));writel(val, GPIO0_SWPORT_DR_H_PI);}
}void led_remap(void)
{PMU_GRF_GPIO0C_IOMUX_L_PI = ioremap(PMU_GRF_GPIO0C_IOMUX_L, 4);PMU_GRF_GPIO0C_DS_0_PI = ioremap(PMU_GRF_GPIO0C_DS_0, 4);GPIO0_SWPORT_DR_H_PI = ioremap(GPIO0_SWPORT_DR_H, 4);GPIO0_SWPORT_DDR_H_PI = ioremap(GPIO0_SWPORT_DDR_H, 4);
}void led_unmap(void)
{iounmap(PMU_GRF_GPIO0C_IOMUX_L_PI);iounmap(PMU_GRF_GPIO0C_DS_0_PI);iounmap(GPIO0_SWPORT_DR_H_PI);iounmap(GPIO0_SWPORT_DDR_H_PI);
}static int led_open(struct inode* inode, struct file* flip)
{return 0;
}static ssize_t led_read(struct file* flip, char __user* buf, size_t cnt, loff_t* offt)
{return 0;
}static ssize_t led_write(struct file* flip, const char __user* buf, size_t cnt, loff_t* offt)
{int retValue;unsigned char databuf[1];unsigned char ledstat;retValue = copy_from_user(databuf, buf, cnt);if (retValue < 0){printk("kernel write failed!\r\n");return EFAULT;}ledstat = databuf[0];printk("ledstat = %d", ledstat);if (ledstat == LEDON){led_switch(LEDON);}else if (ledstat == LEDOFF){led_switch(LEDOFF);}return 0;
}static int led_release(struct inode* inode, struct file* flip)
{return 0;
}static struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.release = led_release,.read = led_read,.write = led_write,
};static int __init led_init(void)
{int retValue = 0;u32 val = 0;led_remap();val = readl(PMU_GRF_GPIO0C_IOMUX_L_PI);val &= ~(0x7 << 0);val |= ((0x7 << 16) | (0x0 << 0));writel(val, PMU_GRF_GPIO0C_IOMUX_L_PI);val = readl(PMU_GRF_GPIO0C_DS_0_PI);val &= ~(0x3F << 0);val |= ((0x3F << 16) | (0x3F << 0));writel(val, PMU_GRF_GPIO0C_DS_0_PI);val = readl(GPIO0_SWPORT_DDR_H_PI);val &= ~(0x1 << 0);val |= ((0x1 << 16) | (0x1 << 0));writel(val, GPIO0_SWPORT_DDR_H_PI);val = readl(GPIO0_SWPORT_DR_H_PI);val &= ~(0x1 << 0);val |= ((0x1 << 16) | (0x0 << 0));writel(val, GPIO0_SWPORT_DR_H_PI);retValue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);if (retValue < 0) {printk("register chrdev failed!\r\n");goto fail_map;}return 0;fail_map:led_unmap();return EIO;
}static void __exit led_exit(void) {led_unmap();unregister_chrdev(LED_MAJOR, LED_NAME);
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");

5.3 测试应用程序

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"#define LEDON 1
#define LEDOFF 0int main(int argc, char *argv[]) {int fd, retValue;char *fileName;unsigned char deataBuf[1];if (argc != 3) {printf("arg num error!\r\n");return -1;}fileName = argv[1];fd = open(fileName, O_RDWR);if (fd < 0) {printf("open dev failed!\r\n");return -1;}printf("open dev success");deataBuf[0] = atoi(argv[2]);retValue = write(fd, deataBuf, 1);if (retValue < 0) {printf("write to dev failed!\r\n");return -1;}retValue = close(fd);if (retValue < 0) {printf ("close fd failed!\r\n");return -1;}return 0;
}

5.4 驱动测试

5.4.1 关闭心跳灯

目前系统中的led用作心跳灯,首先需要关闭才能进行本实验。

adb shell
echo none > /sys/class/leds/work/trigger

使用以上命令暂时关闭心跳灯,持续周期为本次启动,永久关闭心跳灯需要修改设备树。

5.4.2 编写Makefile并编译

然后编写Makefile文件

KERNELDIR := /home/alientek/code/atk-rk3568-11/kernel
CURRENT_PATH := $(shell pwd)
obj-m := led.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

Makefile和代码在同一个目录中,在该目录中执行编译命令

make ARCH=arm64

即可编译出内核模块文件led.ko
接下来使用ndk编译应用层程序

/home/alientek/code/android-ndk-r27/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang ledApp.c -o ledApp

使用ndk中的aarch64-linux-android30-clang工具编译,编译后产生ledApp可执行文件

5.4.3 测试驱动程序

将led.ko和ledApp推到设备中,其中led.ko推到vendor/lib/modules目录,ledApp可以随意找个目录
接下来是加载模块

adb shell
cd /vendor/lib/modules
insmod led.ko
mknod /dev/led c 200 0

加载完模块,并创建对应的设备节点之后,就可以通过应用程序进行测试了。

chmod +x ./ledApp
./ledApp /dev/led 1
./ledApp /dev/led 0

输入1是打开led灯,输入0是关闭led灯。

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

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

相关文章

【GeekBand】C++设计模式笔记5_Observer_观察者模式

1. “组件协作”模式 现代软件专业分工之后的第一个结果是“框架与应用程序的划分”&#xff0c;“组件协作”模式通过晚期绑定&#xff0c;来实现框架与应用程序之间的松耦合&#xff0c;是二者之间协作时常用的模式。典型模式 Template MethodStrategyObserver / Event 2.…

HarmonyOS/OpenHarmony 自定义弹窗页面级层级控制解决方案

关键词&#xff1a;CuntomDialog自定义弹窗、SubWindow子窗口、页面级、弹窗层级控制、鸿蒙、弹窗展示层级异常 问题存在API版本&#xff1a;API10 - API12&#xff08;该问题已反馈&#xff0c;期望后续官方能增加页面级控制能力&#xff09; 在正常的鸿蒙app开发过程中&…

aws(学习笔记第二课) AWS SDK(node js)

aws(学习笔记第二课) 使用AWS SDK&#xff08;node js&#xff09; 学习内容&#xff1a; 使用AWS SDK&#xff08;node js&#xff09; 1. AWS SDK&#xff08;node js&#xff09; AWS支持多种SDK开发(除了AWS CLI&#xff0c;还支持其他的SDK) AndroidPythonNode.js(Javas…

约数个数约数之和

好久没发文章了.......不过粉丝还是一个没少...... 今天来看两道超级恶心的数论题目&#xff01; No.1 约数个数 No.2 约数之和 先来看第一道&#xff1a;约数个数 题目描述 给定 n 个正整数 ai​,请你输出这些数的乘积的约数个数,答案对 10^97 取模 输入格式 第一行包含…

五种IO模型与阻塞IO

一、前言 在网络中通信的本质其实是网络中的两台主机的进程间进行通信&#xff0c;而进程通信的本质就是IO。 IO分为输入&#xff08;input&#xff09;和输出&#xff08;output&#xff09;站在进程的角度讲&#xff0c;进程出去数据为输出&#xff0c;外部数据进入进程为输…

YOLOv8 基于NCNN的安卓部署

YOLOv8 NCNN安卓部署 前两节我们依次介绍了基于YOLOv8的剪枝和蒸馏 本节将上一节得到的蒸馏模型导出NCNN&#xff0c;并部署到安卓。 NCNN 导出 YOLOv8项目中提供了NCNN导出的接口&#xff0c;但是这个模型放到ncnn-android-yolov8项目中你会发现更换模型后app会闪退。原因…

[ComfyUI]Flux:太强了!任意扩图神器,小红书极致逼真风格出游打卡写实风

随着人工智能技术的不断发展&#xff0c;图像生成与反推技术已经成为了AI领域的一大热点。今天&#xff0c;我们就来为大家详细介绍一款由ComfyUI团队开发的超强图像反推工具——Flux&#xff0c;以及如何使用它实现任意扩图和极致逼真风格出游打卡写实风。 一、Flux&#xff…

【AI大模型】使用Embedding API

一、使用OpenAI API 目前GPT embedding mode有三种&#xff0c;性能如下所示&#xff1a; 模型每美元页数MTEB得分MIRACL得分text-embedding-3-large9,61554.964.6text-embedding-3-small62,50062.344.0text-embedding-ada-00212,50061.031.4 MTEB得分为embedding model分类…

centos7安装配置nginx

先安装依赖 安装依赖之前最好先执行下update yum update yum install gcc gcc-c pcre pcre-devel zlib zlib-devel openssl openssl-devel -y cd /usr/local/nginx wget http://nginx.org/download/nginx-1.18.0.tar.gz tar -zxvf nginx-1.18.0.tar.gz cd /usr/local/ngi…

双非本 985 硕,上岸快手大模型算法岗!

最近已有不少大厂都在秋招宣讲&#xff0c;也有一些已在 Offer 发放阶段了。 节前&#xff0c;我们邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对新手如何入门算法岗、该如何准备面试攻略、面试常考点、大模型技术趋势、算法项目落地经验分享等热门话题进行…

高校校园交友系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;用户管理&#xff0c;基础数据管理&#xff0c;论坛管理&#xff0c;公告信息管理&#xff0c;轮播图信息管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;用户&#…

反调试—1

IsDebuggerPresent() CheckRemoteDebuggerPresent() 其内部实际调用NtQueryInformationProcess() bool _stdcall ThreadCall() {while (true){BOOL pbDebuggerPresent FALSE;CheckRemoteDebuggerPresent(GetCurrentProcess(), &pbDebuggerPresent);if (pbDebuggerPres…

fiddler抓包18-2_导出jmeter、postman脚本(带请求头)

课程大纲 1. Fiddler导出请求为curl脚本 选中请求&#xff0c;“文件” - “导出会话” - “选中的会话” - “cURL Script”。 2. 导入jmeter ① 复制curl脚本。 ② 打开jmeter&#xff0c;“工具” - “import from cURL”&#xff0c;粘贴脚本&#xff0c;勾选“Add cooki…

二分查找一>寻找峰值

1.题目&#xff1a; 2.解析&#xff1a; 暴力遍历代码&#xff1a;O(N),由于该题数据很少所以可以通过 暴力遍历&#xff1a;O(N),由于该题数据很少所以可以通过int index 0;for(int i 1; i < nums.length-1; i) {//某段区域内一直递增&#xff0c;更新就indexif(nums[i]…

红黑树学习

红黑树: k v 方式 用在哪里&#xff1a; 1.hash 强查找的过程&#xff1a; 1.rbtree 2.hash 3.b/b tree 4.链表 红黑树&#xff1a; 1.每个结点是红的或者是黑的 2.根结点是黑的 3.每个叶子结点是黑的 4.如果一个结点是红的&#xff0c;则它的两个儿子是黑的 5.对每个节点&…

性能学习5:性能测试的流程

一.需求分析 二.性能测试计划 1&#xff09;测什么&#xff1f; - 项目背景 - 测试目的 - 测试范围 - ... 2&#xff09;谁来测试 - 时间进度与分工 - 交付清单 - ... 3&#xff09;怎么测 - 测试策略 - ... 三.性能测试用例 四.性能测试执行 五.性能分析和调优 六…

ElasticSearch备考 -- Search across cluster

一、题目 配置两个集群&#xff0c;集群名称为my-application-01、my-application-02&#xff0c;导入es自带Sample flight data数据集&#xff0c;配置扩集群检索&#xff0c;查询数据 二、思考 准备工作有两个集群&#xff0c;并需要对集群配置角色中增加 remote_cluster_cl…

【优选算法】(第八篇)

目录 串联所有单词的⼦串&#xff08;hard&#xff09; 题目解析 讲解算法原理 编写代码 最⼩覆盖⼦串&#xff08;hard&#xff09; 题目解析 讲解算法原理 编写代码 串联所有单词的⼦串&#xff08;hard&#xff09; 题目解析 1.题目链接&#xff1a;. - 力扣&#…

光伏组件模型模板在SketchUp中如何完成成模数化设计?

选中模板组件&#xff0c;点击左侧工具栏中移动工具&#xff0c;按住Ctrl再依次点击组件起始点和终点&#xff0c;完成组件复制&#xff0c;输入需要复制的组件数量&#xff08;*n&#xff09;后回车&#xff0c;即可完成模数化设计。 选中模组的多块模型右键进行创建组件或群…

高考技术——pandas使用

百家讲坛&#xff0c;谈论古今&#xff0c;今天我们不聊别的&#xff0c;我们来聊一聊中国的国宝——大熊猫&#xff08;bushi&#xff09; 好好&#xff0c;言归正传&#xff0c;我们今天来讲pandas import pandas as pd 申明无需多言&#xff0c;高考主要考察Series和Data…