【嵌入式】嵌入式Linux开发实战指南:从交叉编译到触摸屏交互

文章目录

  • 前言:
  • 1.简介
    • 1.1. 交叉编译工具
    • 1.2. 项目开发流程:
    • 1.3. ARM开发板的连接方法
  • 2. 开发板连接
  • 3. 系统文件 IO
  • 4. 设置共享文件夹
    • 3.1. 读文件
    • 3.2. 写文件
    • 3.2. 设置文件偏移量
  • 4. LCD显示屏显示
    • 4.1. LCD 显示颜色
    • 4.2. 将文件下载到开发板
      • 4.2.1. 在CRT链接开发板中输入命令"rx 需要下载的文件名"
    • 4.3. lcd 使用 write 函数直接写入像素点出现噪点等待一点后又能完全显示
    • 4.4. 开发板lcd 显示 bmp 图片
  • 5. 触摸屏
    • 5.1. 触摸屏 之 Linux 输入子系统模型
    • 5.1.1 input.h
    • 5.1.2 事件数据分析
  • 总结:

前言:

在嵌入式系统开发领域,交叉编译、系统文件IO操作、LCD显示以及触摸屏处理是一些基本且关键的技能。本文将详细介绍这些技能的实现方法和相关技术细节,旨在帮助开发者更好地理解和掌握嵌入式Linux开发过程中的常用操作。我们将从交叉编译工具的介绍开始,逐步深入到项目开发流程、开发板连接方法、系统文件IO的基本操作,再到LCD显示屏的显示技术以及触摸屏的Linux输入子系统模型和事件分析。通过本文的学习,读者将能够获得从硬件连接到软件编程的全面知识。

1.简介

1.1. 交叉编译工具

每个不同的平台可执行的格式都不尽相同,我们需要Linux系统的ARM平台执行程序,故需要ARM-Linux交叉编译工具,ubuntu 交叉编译工具链“arm-linux-gcc”

1.2. 项目开发流程:

1、使用C语言编写项目源代码
2、使用交叉编译工具编译项目源码
3、将编译生成的可执行程序下载到ARM开发板
4、在ARM开发板执行项目程序
5、验证项目程序的准确性

1.3. ARM开发板的连接方法

SecureCRT是一款支持SSH(SSH1和SSH2)的终端仿真程序,简单的说是Windows下登录UNIX或Linux服务器主机的软件。

2. 开发板连接

  1. 使用 串口线 与 usb 转串口线将开发板与电脑进行连接

  2. 检查电脑是否识别 usb 转串口驱动
    在这里插入图片描述

  3. 打开 CRT 软件

  4. 在快速链接向导里向导中进行配置
    在这里插入图片描述

  5. 打开开发板
    在这里插入图片描述

3. 系统文件 IO

  1. 打开文件 open
   #include <sys/types.h>
   #include <sys/stat.h>
   #include <fcntl.h>

   int open(const char *pathname, int flags); 
   // pathname:	需要打开的文件的路径名+文件名
   // flag:		打开文件的模式( O_RDONLY, O_WRONLY, or O_RDWR)
   // 返回值:	成功返回新的文件描述符,失败返回-1和出错码(perror)
  1. 关闭文件 close
   #include <unistd.h>
   int close(int fd);
   // fd:	文件描述符
   // 返回值: 成功返回0,失败返回-1和错误码
  1. 读取文件内容 read
   #include <unistd.h>
   ssize_t read(int fd, void *buf, size_t count);
   // fd:	文件描述符
   // buf:	读到的数据缓冲区
   // count:需要读取的字节数
   // 返回值: 成功返回实际度到的字节数,失败返回-1 和 错误码
  1. 写数据到文件中 write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
   // fd:	文件描述符
   // buf:	写到的数据缓冲区
   // count:需要写入的字节数
   // 返回值: 成功返回实际度写入字节数,失败返回-1 和 错误码
  1. 设置文件偏移量 Iseek
   #include <sys/types.h>
   #include <unistd.h>

   off_t lseek(int fd, off_t offset, int whence);  
   // fd:	文件描述符
   // offset:偏移量
   // whence:基准点(SEEK_SET开头、SEEK_CUR当前、SEEK_END结尾)
   // 返回值:成功返回文件新的文件偏移量,失败返回-1

4. 设置共享文件夹

  1. 打开VMware虚拟机打开设置
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在ubantu虚拟机中检查是否创建成功。

3.1. 读文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
    // 1.打开文件
    int rd_fd = open("1.txt", O_RDONLY); 
    if (rd_fd == -1) // 判断是否打开成功
    {
        perror("open 1.txt failed");
        exit(-1); // 退出程序
    }
    // 2.读取文件
    char buf[1024]; //定义缓冲区
    bzero(buf, sizeof(buf)); //清空缓冲区
    int ret = read(rd_fd, buf, sizeof(buf));
    if (ret == -1)
    {
        perror("read 1.txt failed");
        exit(-2); // 退出程序
    }
    
    // 3.查看数据
    printf("read 1.txt %d byte is: %s\n", ret, buf);

    // 4.关闭文件
    close(rd_fd);
}

在这里插入图片描述

3.2. 写文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
    // 1.打开文件
    int wd_fd = open("2.txt", O_WRONLY | O_CREAT, 0644); 
    if (wd_fd == -1) // 判断是否打开成功
    {
        perror("open 1.txt failaed");
        exit(-1); // 退出程序
    }
    // 2.写数据
    int ret = write(wd_fd, "zhangsan hello", sizeof("zhangsan hello"));
    if (ret == -1)
    {
        perror("write 2.txt failed");
        exit(-2); // 退出程序
    }

    // 4.关闭文件
    close(wd_fd);
}

在这里插入图片描述

3.2. 设置文件偏移量

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
    // 1.打开文件
    int wd_fd = open("2.txt", O_WRONLY | O_CREAT, 0644); 
    if (wd_fd == -1) // 判断是否打开成功
    {
        perror("open 1.txt failaed");
        exit(-1); // 退出程序
    }
    // 2.写数据
    int l_ret = lseek(wd_fd, 0, SEEK_END); // 将偏移量移动到文件末尾
    int ret = write(wd_fd, "zhangsan hello", sizeof("zhangsan hello"));
    if (ret == -1)
    {
        perror("write 2.txt failed");
        exit(-2); // 退出程序
    }
    printf("lseek %d byte; write %d byte\n", l_ret, ret);
    // 4.关闭文件
    close(wd_fd);
}

在这里插入图片描述

4. LCD显示屏显示

4.1. LCD 显示颜色

lcd屏可显示显示像素点得组成ARGB四个元素组成,一个元素得有256个不同的值故需要8位二进制来描述,因此4个元素要需要32位(4字节)二进制来表示。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
    // 打开lcd设备
    int lcd_fd = open("/dev/fb0", O_RDWR);
    if (lcd_fd == -1)
    {
        perror("open LCD failed");
        exit(-1);
    }
    // 定义色彩
    int color = 0xFF0000; // 假设是红色
    // 将色彩写入lcd设备文件
    for (int i = 0; i < 800 * 480; i++)
    {
        write(lcd_fd, &color, sizeof(color)); //写一个像素点
    }  
    // 关闭lcd设备
    close(lcd_fd);
    return 0;
}

在这里插入图片描述

4.2. 将文件下载到开发板

4.2.1. 在CRT链接开发板中输入命令"rx 需要下载的文件名"

在这里插入图片描述
通过Xmode发送
在这里插入图片描述
发送完成
在这里插入图片描述
下载文件执行成功后修改文件权限,修改文件权限后方可执行
在这里插入图片描述
屏幕显示红色:
请添加图片描述

4.3. lcd 使用 write 函数直接写入像素点出现噪点等待一点后又能完全显示

问题分析:由于系统调用需要花费时间
解决办法:给 lcd 申请一片显存
使用 mmap 函数可以采用内存映射的方式来为 lcd 映射一块特殊的内存作为 lcd 的显存

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); // 映射函数 
// 主要参数:
// addr:	映射内存的起始地址(NULL表述由内核自己找到符合要求的地址)
// length:	映射内存的大小
// prot: 	映射内存的权限(与 open 的权限一致)
//       		PROT_EXEC  Pages may be executed.
//       		PROT_READ  Pages may be read.
//       		PROT_WRITE Pages may be written.
//      		PROT_NONE  Pages may not be accessed.
// flags: 	映射内存的更新方式
//				MAP_SHARED: 共享内存的方式更新(更新已有的内存)
//				MAP_PRIVATE: 创建新的内存并更新
// fd: 		需要映射的文件描述符
// offset: 	映射内存的偏移量
// 返回值:
//	成功返回映射内存基地址
//	失败返回 MAP_FAILED 和 错误码

int munmap(void *addr, size_t length); // 解除映射函数

显示绿色:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>

int main()
{
    int i;
    // 打开lcd设备
    int lcd_fd = open("/dev/fb0", O_RDWR);
    if (lcd_fd == -1)
    {
        perror("open LCD failed");
        exit(-1);
    }

    // 开启映射
    int* lcd_map = mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, lcd_fd, 0);
    
    // 定义色彩
    int color = 0x00FF00; // 假设是绿色
    // 将色彩写入lcd设备文件
    for (i = 0; i < 800 * 480; i++)
    {
        *(lcd_map + i) = color; // 显示一个像素点
    }  
    // 关闭lcd设备
    close(lcd_fd);
    // 关闭映射
    munmap(lcd_map, 800 * 480);
    return 0;
}

请添加图片描述

4.4. 开发板lcd 显示 bmp 图片

  1. 常见的图片格式:jpeg / jpg, png, bmp
  2. jpeg / jpg: 采用专用的图像压缩算法进行压缩
    bmp: 它采用位映射存储格式,除了图像深度可选外,不采用其他任何压缩,因此,BMP 文件所占用的空间很大(推荐24位的位图)。
    在这里插入图片描述
    分析:由于 lcd 显示的像素点是 32位 的彩色,而bmp图片的像素点是 24位,所以需要要完成 24位 到 32位 的转换
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>


int main()
{
    int i;
    // 打开lcd设备
    int lcd_fd = open("/dev/fb0", O_RDWR);
    if (lcd_fd == -1)
    {
        perror("open LCD failed");
        exit(-1);
    }

    // 开启映射
    int* lcd_map = mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, lcd_fd, 0);
    if (lcd_map == MAP_FAILED)
    {
        perror("mmap failed");
        close(lcd_fd);
        exit(-1);
    }

    // 打开bmp图片
    int bmp_fd = open("bgt.bmp", O_RDONLY);
    if (bmp_fd == -1)
    {
        perror("open BMP failed");
        munmap(lcd_map, 800*480*4);
        close(lcd_fd);
        exit(-1);
    }

    // 跳过BMP文件头(假设54字节标准头)
    lseek(bmp_fd, 54, SEEK_SET);

    // 读取图片数据
    int bmp_buf[800*480]; // 图片的大致大小
    char tmp_buf[800*480*3]; 
    bzero(bmp_buf, sizeof(bmp_buf)); // 清除缓冲区
    bzero(tmp_buf, sizeof(tmp_buf));
    read(bmp_fd, tmp_buf, sizeof(tmp_buf)); // 读取数据

    // 关闭bmp图片文件
    close(bmp_fd);

    // 将 24位 的像素点转换为 32位
    for (i = 0; i < 800*480; ++i)
    {
        bmp_buf[i] = tmp_buf[i*3]|tmp_buf[i*3+1]<<8|tmp_buf[i*3+2]<<16|0x00<<24;
    }
    
    // 显示
    for (i = 0; i < 800*480; i++)
    {
        *(lcd_map + i) = bmp_buf[i]; // 显示一个像素点
    } 


    // 关闭lcd设备
    close(lcd_fd);
    // 关闭映射
    munmap(lcd_map, 800*480*4);
    return 0;
}

请添加图片描述
但是图片是反转过来的
在这里插入图片描述

// 将 24位 的像素点转换为 32位,并将图片翻转过来
for (i = 0; i < 480; ++i) {
    for (j = 0; j < 800; ++j) {
        bmp_buf[i*800+j] = tmp_buf[((479-i)*800 + j)*3] | 
                        tmp_buf[((479-i)*800 + j)*3+1]<<8 | 
                        tmp_buf[((479-i)*800 + j)*3+2]<<16 | 
                        0x00<<24;
    }
}

现在就正了:
请添加图片描述

5. 触摸屏

5.1. 触摸屏 之 Linux 输入子系统模型

可以通过查看 input.h 的代码,了解输入子系统的框架

cd /usr/include/linux/

5.1.1 input.h

/*
 * The event structure itself
 */

struct input_event {
	struct timeval time; // 事件发生的时间
	__u16 type;			 // 事件发生类型
	__u16 code;			 // 事件的代码
	__s32 value;		 // 事件的值
};

在开发板上有这样一些事件:
在这里插入图片描述

/*
 * Event types
 */

#define EV_SYN			0x00
#define EV_KEY			0x01
#define EV_REL			0x02
#define EV_ABS			0x03
#define EV_MSC			0x04
#define EV_SW			0x05
#define EV_LED			0x11
#define EV_SND			0x12
#define EV_REP			0x14
#define EV_FF			0x15
#define EV_PWR			0x16
#define EV_FF_STATUS		0x17
#define EV_MAX			0x1f
#define EV_CNT			(EV_MAX+1)
/*
 * Absolute axes
 */

#define ABS_X			0x00
#define ABS_Y			0x01
#define ABS_Z			0x02
#define ABS_RX			0x03
#define ABS_RY			0x04
#define ABS_RZ			0x05
#define ABS_THROTTLE		0x06
#define ABS_RUDDER		0x07
#define ABS_WHEEL		0x08
#define ABS_GAS			0x09
#define ABS_BRAKE		0x0a
// ...

5.1.2 事件数据分析

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include<linux/input.h>

int main(void)
{
    // 打开触摸屏
    int ts_fd = open("/dev/input/event0", O_RDONLY);
    if(ts_fd == -1)
    {
        perror("open touchscreen failed");
        exit(-1);
    }

    // 定义输入事件缓冲区
    struct input_event myevent;

    while(1)
    {
        // 读入数据并输出
        read(ts_fd, &myevent, sizeof(struct input_event)); //阻塞等待输入数据
        printf("type:%d code:%d value:%d\n", myevent.type, myevent.code, myevent.value);
    }

    // 关闭文件
    close(ts_fd);

    return 0;
}

在这里插入图片描述

type:0 code:0 value:0 // 同步事件
type:3/*EV_ABS触摸屏事件*/ code:0/*ABS_X x轴*/ value:520
type:3/*EV_ABS触摸屏事件*/ code:1/*ABS_Y y轴*/ value:457
type:0 code:0 value:0 // 同步事件
type:1/*EV_KEY键盘事件*/ code:330/*BTN_TOUCH键盘事件*/ value:0//松开
type:0 code:0 value:0 // 同步事件

获取触摸屏 的 x,y值:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include<linux/input.h>

int main(void)
{
    // 打开触摸屏
    int ts_fd = open("/dev/input/event0", O_RDONLY);
    if(ts_fd == -1)
    {
        perror("open touchscreen failed");
        exit(-1);
    }

    // 定义输入事件缓冲区
    struct input_event myevent;
    int x, y, count = 0;
    while(1)
    {
        // 读入数据并输出
        read(ts_fd, &myevent, sizeof(struct input_event)); //阻塞等待输入数据
        
        if(myevent.type == EV_ABS) // 触摸屏事件
        {
            if(myevent.code == ABS_X) // X轴
            {
                x = myevent.value;
                count++;
            }
            if(myevent.code == ABS_Y)
            {
                y=myevent.value;
                count++;
            }
            if (count == 2) 
            {
                count = 0;
                printf("(%d, %d)\n", x, y);
            }
        }
        
        //printf("type:%d code:%d value:%d\n", myevent.type, myevent.code, myevent.value);
    }

    // 关闭文件
    close(ts_fd);

    return 0;
}

在这里插入图片描述

总结:

本文全面介绍了嵌入式Linux开发中的一系列关键技术,包括交叉编译工具的使用、项目开发流程、开发板的连接方法、系统文件IO的基本操作、LCD显示屏的显示技术以及触摸屏的输入子系统模型和事件分析。通过详细的代码示例和步骤说明,读者可以对这些技术有一个清晰的认识,并能够在实际开发中灵活应用。

交叉编译工具是开发过程中不可或缺的一部分,它允许开发者在不同平台之间进行代码的编译和移植。而项目开发流程的介绍,为读者提供了一个清晰的开发路线图,确保开发工作的有序进行。开发板的连接方法和系统文件IO操作是进行硬件交互的基础,LCD显示屏的显示技术和触摸屏事件分析则进一步扩展了开发者在用户界面交互方面的技能。

通过本文的学习,开发者不仅能够掌握嵌入式Linux开发的基本技能,还能够对LCD显示和触摸屏处理等高级主题有更深入的理解。希望本文能够成为嵌入式Linux开发者的一份宝贵参考资料,帮助他们在项目开发中更加得心应手。

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

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

相关文章

JDK动态代理

JDK动态代理源码分析 4.1 JDK动态代理的实现 需要动态代理的接口 /**需要动态代理的接口 */ public interface Movie {void player();void speak();需要动态代理的接口的真实实现 /**需要动态代理接口的真实实现 */ public class RealMovie implements Movie {Override publi…

win10 安装openssl并使用openssl创建自签名证书

win10创建自签名证书 下载安装配置openssl 下载地址&#xff1a; https://slproweb.com/download/Win64OpenSSL-3_3_1.exe https://slproweb.com/products/Win32OpenSSL.html 完成后安装&#xff0c;一路next&#xff0c;到达选位置的之后选择安装的位置&#xff0c;我这里选…

一文详解分布式 ID

分布式系统中&#xff0c;我们经常需要对数据、消息等进行唯一标识&#xff0c;这个唯一标识就是分布式 ID&#xff0c;那么我们如何设计它呢&#xff1f;本文将详细讲述分布式 ID 及其生成方案。 一、为什么需要分布式 ID 目前大部分的系统都已是分布式系统&#xff0c;所以在…

如何打造稳定、好用的 Android LayoutInspector?

速度极慢&#xff0c;遇到复杂的布局经常超时 某些情况无法选中指定的 View 本文将围绕 LayoutInspector 的痛点&#xff0c;分析问题并修复&#xff0c;最终将 LayoutInspector 变成一个稳定、好用的插件。 二、加速 Dump View Hierarchy 2.1 问题描述 开发复杂业务的同学…

【STM32-启动文件 startup_stm32f103xe.s】

STM32-启动文件 startup_stm32f103xe.s ■ STM32-启动文件■ STM32-启动文件主要做了以下工作&#xff1a;■ STM32-启动文件指令■ STM32-启动文件代码详解■ 栈空间的开辟■ 栈空间大小 Stack_Size■ .map 文件的详细介绍■ 打开map文件 ■ 堆空间■ PRESERVE8 和 THUMB 指令…

用Java获取键盘输入数的个十百位数

这段Java代码是一个简单的程序&#xff0c;用于接收用户输入的一个三位数&#xff0c;并将其分解为个位、十位和百位数字&#xff0c;然后分别打印出来。下面是代码的详细解释&#xff1a; 导入所需类库: import java.util.Scanner;&#xff1a;导入Scanner类&#xff0c;用于从…

使用Gradle查看Android项目中库的依赖关系

| | -- com.android.support:support-compat:25.3.1 | | | — com.android.support:support-annotations:25.3.1 | | -- com.android.support:support-media-compat:25.3.1 | | | -- com.android.support:support-annotations:25.3.1 | | | — com.android.support:support…

Windows10中端口被占用处理方法

前言 在Windows 10中&#xff0c;查看端口被占用情况的方法主要依赖于命令行工具netstat。以下是详细步骤&#xff0c;以及必要的解释和归纳&#xff1a; 打开命令提示符 方法1&#xff1a;使用快捷键Win R&#xff0c;打开“运行”对话框&#xff0c;输入cmd&#xff0c;然…

深入讲解C++基础知识(一)

目录 一、基本内置类型1. 类型的作用2. 分类3. 整型3.1 内存描述及查询3.2 布尔类型 —— bool3.3 字符类型 —— char3.4 其他整型 4. 有符号类型和无符号类型5. 浮点型6. 如何选择类型7. 类型转换7.1 自动类型转换7.2 强制类型转换7.3 类型转换总结 8. 类型溢出8.1 注意事项 …

5.XSS-反射型(post)利用:获取cookie

原理&#xff1a; 文件路径&#xff1a;\pikachu\pkxss\xcookie\post.html 将post.html文件&#xff0c;复制到皮卡丘的根路径下或者根下随意路径即可&#xff0c;并编辑文件 需要修改以下两个地址&#xff0c;第一个地址是将原界面的样子链接过来&#xff0c;让用户认为是原…

数据结构十三:2 - 3树和红黑树

一开始就接触这五点&#xff0c;会让人云里雾里&#xff0c;不利于了解这个数据结构。因为这种先给定义在推导的方式并不适合学习。它没有介绍红黑树的来源&#xff0c;而只是给你生硬的定义。 而学习红黑树的最好学习资料就是大名鼎鼎的《算法4》&#xff0c;如下&#xff1a…

微型操作系统内核源码详解系列五(四):cm3下svc启动任务

系列一&#xff1a;微型操作系统内核源码详解系列一&#xff1a;rtos内核源码概论篇&#xff08;以freertos为例&#xff09;-CSDN博客 系列二&#xff1a;微型操作系统内核源码详解系列二&#xff1a;数据结构和对象篇&#xff08;以freertos为例&#xff09;-CSDN博客 系列…

LabVIEW电控旋翼测控系统

开发基于LabVIEW开发的电控旋翼测控系统&#xff0c;通过高效监控和控制提升旋翼系统的性能和安全性。系统集成了多种硬件设备&#xff0c;采用模块化设计&#xff0c;实现复杂的控制和数据处理功能&#xff0c;适用于现代航空航天领域。 项目背景 传统旋翼系统依赖机械和液压…

算法04 模拟算法之一维数组相关内容详解【C++实现】

大家好&#xff0c;我是bigbigli&#xff0c;模拟算法我们将分为几个章节来讲&#xff0c;今天我们只看一维数组相关的题目 目录 模拟的概念 训练&#xff1a;开关灯 解析 参考代码 训练&#xff1a;数组变化 解析 参考代码 训练&#xff1a;折叠游戏 解析 参考代码 …

vscode+picgo+gitee实现Markdown图床

vscode中编辑Markdown文件&#xff0c;复制的图片默认是保存在本地的。当文档上传csdn时&#xff0c;会提示图片无法识别 可以在gitee上创建图床仓库&#xff0c;使用picgo工具上传图片&#xff0c;在Markdown中插入gitee链接的方式来解决该问题。 一、 安装picgo工具 1.1 v…

IOS开发学习日记(十六)

目录 App间的唤起和通信 App跳转 通过Scheme唤起其他App Universal Link 组件化 App间的唤起和通信 App跳转 使用URL Scheme支持App启动、跳转及参数传递 分享 / 登陆 / 拉起App Store等 设置URL Type 在UIApplication中处理参数和业务逻辑 -(BOOL)application:(UIApp…

强化安全新篇章:韶关石油化工可燃气体报警器年检解析

韶关&#xff0c;这座位于广东省北部的城市&#xff0c;近年来在石油化工行业取得了显著的发展。 随着一批批大型石化企业的进驻和投产&#xff0c;韶关不仅成为了区域性的石化产业基地&#xff0c;也为地方经济带来了强劲的增长动力。 然而&#xff0c;随着石化产业的快速发…

Selenium WebDriver - 网络元素

本文翻译整理自&#xff1a;https://www.selenium.dev/documentation/webdriver/elements/ 文章目录 一、文件上传二、定位策略1、传统定位器2、创建定位器3、类名4、CSS选择器5、id6、NAME7、链接文本8、部分链接文本9、标签名称10、xpath11、相对定位器它是如何工作的可用相对…

2024最新版Python 3.12.4安装使用指南

2024最新版Python 3.12.4安装使用指南 2024最新版Python 3.12.4安装使用指南0. Python的受欢迎程度1. 安装最新版Python 3.12.42. 验证Python 3.12.4版本3. 验证Python功能4. 使用IDLE交互式开发模式5. 安装Python扩展库相关阅读&#xff1a; By Jackson 2024最新版Python 3.12…