【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十六章 设备驱动IO控制

news2024/9/25 9:30:36

i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口一应俱全。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相关历程,支持8路PDM接口、5路SAI接口、2路Speaker。系统支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9系统。适用于智能充电桩,物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网应用、

【公众号】迅为电子

【粉丝群】258811263(加群获取驱动文档+例程)


第五十六章 设备驱动IO控制

本章导读

本章节我们将来学习ioctl接口,那么我们在学习之前先来回想一下,我们以前操作一个GPIO是怎么操作的呢?在前面章节中我们使用GPIO函数来操作GPIO的,在使用GPIO函数之前是使用寄存器来进行操作的,我们使用GPIO函数操作GPIO比直接使用寄存器来操作GPIO进一步升级了。我们操作GPIO的时候,使用的是逻辑判断,kbuf[0]的数据是从应用层传递过来的我们要使用到copy_from_usr。当我们在应用层write驱动节点的时候,会触发驱动层的write函数,然后我们在write函数里面控制我们的GPIO。虽然这样的方法是可以的,但是这样的方法是不是比较麻烦呢?有没有更简单的办法呢?当然是有了,方法就是我们这节课讲的ioctl接口。

56.1章节讲解了设备驱动IO控制原理

56.2章节编写驱动程序和应用程序,测试应用层不传递数据

56.3章节编写驱动程序和应用程序,测试应用层写数据

56.4章节编写驱动程序和应用程序,测试应用层读数据

本章内容对应视频讲解链接(在线观看):

ioctl接口(  https://www.bilibili.com/video/BV1Vy4y1B7ta?p=32

ioctl接口(  https://www.bilibili.com/video/BV1Vy4y1B7ta?p=33

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\013-设备驱动io控制”路径下。

56.1 设备驱动IO控制原理

在内核3.0 以前,ioctl接口的名字叫ioctl;内核3.0以后,ioctl接口的名字叫unlocked_ioctl。unlocked_ioctl就是ioctl接口,但是功能和对应的系统调用均没有发生变化。unlocked_ioctl和read/write函数有什么异同呢?相同点:都可以往内核写数据。不同点:read函数只能完成读的功能,write只能完成写的功能。读取大数据的时候效率高。ioctl既可以读也可以写,读取大数据的时候效率不高。

我们现在有了ioctl函数,内核已经把工作的任务给我们区分了,定义命令就不再使用read和write函数了,而是使用ioctl函数,因为ioctl函数的任务就是对我们的工作参数进行设置和查询,write和read函数专注于数据传输。

在驱动程序里, ioctl()函数上传送的变量 cmd是应用程序用于区别设备驱动程序请求处理内容的值。cmd除了可区别数字外,还包含有助于处理的几种相应信息。 cmd的大小为 32位,共分 4 个域,命令规则为:

第一个分区:0-7,命令的编号,范围是0-255.

第二个分区:8-15,命令的幻数。

第一个分区和第二个分区主要作用是用来区分命令的。

第三个分区:16-29表示传递的数据大小。

第四个分区:30-31 代表读写的方向。

00:表示用户程序和驱动程序没有数据传递

10:表示用户程序从驱动里面读数据

01:表示用户程序向驱动里面写数据

11:先写数据到驱动里面然后在从驱动里面把数据读出来。

 合成宏:

 _IO(type,nr)  :用来定义没有数据传递的命令

_IOR(type,nr,size)  :用来定义从驱动中读取数据的命令

_IOW(type,nr,size)  :用来定义向驱动写入数据的命令

_IOWR(type,nr,size) :用来定义数据交换类型的命令,先写入数据,再读取数据这类命令。 

参数说明:

  • type:表示命令组成的魔数,也就是8~15位
  • nr:表示命令组成的编号,也就是0~7位
  • size:表示命令组成的参数传递大小,注意这里不是传递数字,而是数据类型,如要传递4字节,就可以写成int。

分解宏:

_IOC_DIR(nr)    分解命令的方向,也就是上面说30~31位的值

_IOC_TYPE(nr)   :分解命令的魔数,也就是上面说8~15位的值

_IOC_NR(nr)    分解命令的编号,也就是上面说0~7位

_IOC_SIZE(nr)    :分解命令的复制数据大小,也就是上面说的16~29位

参数说明:

  • nr :要分解的命令

验证程序代码如下所示:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define CMD_TEST0 _IO('L', 0)
#define CMD_TEST1 _IO('A', 1)
#define CMD_TEST2 _IOW('L', 2, int)
#define CMD_TEST3 _IOR('L', 3, int)

int main(int argc, char *argv[])
{
    printf("30-31 is %d \n", _IOC_DIR(CMD_TEST0));
    printf("30-31 is %d \n", _IOC_DIR(CMD_TEST3));

    printf("7-15 is %c \n", _IOC_TYPE(CMD_TEST0));
    printf("7-15 is %c \n", _IOC_TYPE(CMD_TEST1));

    printf("0-7 is %d \n", _IOC_NR(CMD_TEST2));
}

那么我们定义的这些命令要怎么用呢?第一种方法是用分解宏将它分解出来,还有一种用法是搭配switch来用。

56.2 编写测试-不传递数据

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\013-设备驱动io控制\001”路径下。

我们拷贝第42章的驱动代码,在此基础上进行修改,完整代码如下所示:

//初始化头文件
#include <linux/init.h>
//最基本的文件,支持动态添加和卸载模块。
#include <linux/module.h>
//包含了miscdevice结构的定义及相关的操作函数。
#include <linux/miscdevice.h>
//文件系统头文件,定义文件表结构(file,buffer_head,m_inode等)
#include <linux/fs.h>
//包含了copy_to_user、copy_from_user等内核访问用户进程内存地址的函数定义。
#include <linux/uaccess.h>
//包含了ioremap、iowrite等内核访问IO内存等函数的定义。
#include <linux/io.h>
//驱动要写入内核,与内核相关的头文件
#include <linux/kernel.h>

#define CMD_TEST1 _IO('A', 1)
#define CMD_TEST0 _IO('L', 0)

ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
    printk("misc_read\n ");
    return 0;
}
ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
    /*应用程序传入数据到内核空间,然后控制led的逻辑,在此添加*/
    // kbuf保存的是从应用层读取到的数据
    char kbuf[64] = {0};
    // copy_from_user 从应用层传递数据给内核层
    if (copy_from_user(kbuf, ubuf, size) != 0)
    {
        // copy_from_user 传递失败打印
        printk("copy_from_user error \n ");
        return -1;
    }
    //打印传递进内核的数据
    printk("kbuf is %d\n ", kbuf[0]);
    return 0;
}
/****************************************************************************************
 * @brief misc_release : 用户空间关闭设备节点时执行此函数
 * @param inode        : 文件索引
 * @param file         : 文件
 * @return             :成功返回 0  
 ****************************************************************************************/
int misc_release(struct inode *inode, struct file *file)
{
    printk("hello misc_relaease bye bye \n ");
    return 0;
}
/****************************************************************************************
 * @brief misc_open : 用户空间打开设备节点时执行此函数
 * @param inode     : 文件索引
 * @param file      : 文件
 * @return          : 成功返回 0    
 ****************************************************************************************/
int misc_open(struct inode *inode, struct file *file)
{
    printk("hello misc_open\n ");
    return 0;
}
/****************************************************************************************
 * @brief misc_ioctl : 用户空间使用ioctl(int fd, unsigned long request, ...(void* arg))时
 *                      自动执行此函数,根据命令执行对应的操作
 * @param file       : 设备文件
 * @param cmd        : 用户空间ioctl接口命令request
 * @param value      : 用户空间的arg指针,依赖于接口命令request
 * @return           : 成功返回 0   
 ****************************************************************************************/
long misc_ioctl(struct file *file, unsigned int cmd, unsigned long value)
{
    printk("LED ON \n");
    switch (cmd) //根据命令进行对应的操作
    {
    case CMD_TEST0:
        printk("LED ON \n");
        break;
    case CMD_TEST1:
        printk("LED OFF \n");
        break;
    }
    return 0;
}
struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .open = misc_open,
    .release = misc_release,
    .read = misc_read,
    .write = misc_write,
    .unlocked_ioctl = misc_ioctl /* 64 bit system special */};
struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "hello_misc",
    .fops = &misc_fops,
};
static int misc_init(void)
{
    int ret;
    ret = misc_register(&misc_dev);
    if (ret < 0)
    {
        printk("misc registe is error \n");
    }
    printk("misc registe is succeed \n");

    return 0;
}
static void misc_exit(void)
{
    misc_deregister(&misc_dev);

    printk(" misc gooodbye! \n");
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");

参考第三十七章 Linux内核模块,将led.c编译为led.ko 文件,如下图所示:

应用程序ioctrl.c:

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

#define CMD_TEST0 _IO('L',0)
#define CMD_TEST1 _IO('A',1)
//#define CMD_TEST2 _IOW('L',2,int)
//#define CMD_TEST3 _IOR('L',3,int)

int main(int argc,char *argv[])
{
    int fd;
    char buf[64] ={0};
    fd = open("/dev/hello_misc",O_RDWR);
    if(fd < 0)
    {
    perror("open error \n");
    return fd;  
    }
    while(1)
    {
    ioctl(fd,CMD_TEST0);
    sleep(2);
    ioctl(fd,CMD_TEST1);
    }
    
    return 0;
}

我们将ioctl.c文件拷贝到Ubuntu的/home/topeet/imx8mm/13/001目录下,输入以下命令编译ioctl.c

我们进入共享目录,加载驱动模块以后,再运行应用程序,如下图所示: 

56.3 编写测试-写数据

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\013-设备驱动io控制\002”路径下。

 我们在56.2章节代码的基础上进行修改,完整代码如下所示:

//初始化头文件
#include <linux/init.h>
//最基本的文件,支持动态添加和卸载模块。
#include <linux/module.h>
//包含了miscdevice结构的定义及相关的操作函数。
#include <linux/miscdevice.h>
//文件系统头文件,定义文件表结构(file,buffer_head,m_inode等)
#include <linux/fs.h>
//包含了copy_to_user、copy_from_user等内核访问用户进程内存地址的函数定义。
#include <linux/uaccess.h>
//包含了ioremap、iowrite等内核访问IO内存等函数的定义。
#include <linux/io.h>
//驱动要写入内核,与内核相关的头文件
#include <linux/kernel.h>

#define CMD_TEST1 _IO('A', 1)
#define CMD_TEST0 _IO('L', 0)
#define CMD_TEST2 _IOW('L', 2, int)
#define CMD_TEST3 _IOW('L', 3, int)

ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
    printk("misc_read\n ");
    return 0;
}
ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
    /*应用程序传入数据到内核空间,然后控制led的逻辑,在此添加*/
    // kbuf保存的是从应用层读取到的数据
    char kbuf[64] = {0};
    // copy_from_user 从应用层传递数据给内核层
    if (copy_from_user(kbuf, ubuf, size) != 0)
    {
        // copy_from_user 传递失败打印
        printk("copy_from_user error \n ");
        return -1;
    }
    //打印传递进内核的数据
    printk("kbuf is %d\n ", kbuf[0]);
    return 0;
}
/****************************************************************************************
 * @brief misc_release : 用户空间关闭设备节点时执行此函数
 * @param inode        : 文件索引
 * @param file         : 文件
 * @return             :成功返回 0  
 ****************************************************************************************/
int misc_release(struct inode *inode, struct file *file)
{
    printk("hello misc_relaease bye bye \n ");
    return 0;
}
/****************************************************************************************
 * @brief misc_open : 用户空间打开设备节点时执行此函数
 * @param inode     : 文件索引
 * @param file      : 文件
 * @return          : 成功返回 0    
 ****************************************************************************************/
int misc_open(struct inode *inode, struct file *file)
{
    printk("hello misc_open\n ");
    return 0;
}
/****************************************************************************************
 * @brief misc_ioctl : 用户空间使用ioctl(int fd, unsigned long request, ...(void* arg))时
 *                      自动执行此函数,根据命令执行对应的操作
 * @param file       : 设备文件
 * @param cmd        : 用户空间ioctl接口命令request
 * @param value      : 用户空间的arg指针,依赖于接口命令request
 * @return           : 成功返回 0   
 ****************************************************************************************/
long misc_ioctl(struct file *file, unsigned int cmd, unsigned long value)
{

    switch (cmd)//根据命令进行对应的操作
    {
    case CMD_TEST2:
        printk("LED ON \n");
        printk("value is %ld\n", value);
        break;
    case CMD_TEST3:
        printk("LED OFF \n");
        printk("value is %ld\n", value);
        break;
    }
    return 0;
}
struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .open = misc_open,
    .release = misc_release,
    .read = misc_read,
    .write = misc_write,
    .unlocked_ioctl = misc_ioctl };
struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "hello_misc",
    .fops = &misc_fops,
};
static int misc_init(void)
{
    int ret;
    ret = misc_register(&misc_dev);
    if (ret < 0)
    {
        printk("misc registe is error \n");
    }
    printk("misc registe is succeed \n");

    return 0;
}
static void misc_exit(void)
{
    misc_deregister(&misc_dev);

    printk(" misc goodbye! \n");
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");

将led.c编译为led.ko 文件,如下图所示:

应用程序代码ioctrl.c代码如下:

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

#define CMD_TEST0 _IO('L',0)
#define CMD_TEST1 _IO('A',1)

#define CMD_TEST2 _IOW('L',2,int)
#define CMD_TEST3 _IOW('L',3,int)
//#define CMD_TEST3 _IOR('L',3,int)

int main(int argc,char *argv[])
{
    int fd;
    char buf[64] ={0};
    fd = open("/dev/hello_misc",O_RDWR);
    if(fd < 0)
    {
    perror("open error \n");
    return fd;  
    }
    while(1)
    {
    ioctl(fd,CMD_TEST2,0);
    sleep(2);
    ioctl(fd,CMD_TEST3,1);
    }
    
    return 0;
}

编译完成如下图所示:

我们进入共享目录,卸载原来加载的驱动模块,然后再加载新的驱动模块以后,再运行应用程序,如下图所示: 

56.4 编写测试-读数据

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\013-设备驱动io控制\003”路径下。

我们在56.3的代码的基础上进行修改,完整代码如下所示:

//初始化头文件
#include <linux/init.h>
//最基本的文件,支持动态添加和卸载模块。
#include <linux/module.h>
//包含了miscdevice结构的定义及相关的操作函数。
#include <linux/miscdevice.h>
//文件系统头文件,定义文件表结构(file,buffer_head,m_inode等)
#include <linux/fs.h>
//包含了copy_to_user、copy_from_user等内核访问用户进程内存地址的函数定义。
#include <linux/uaccess.h>
//包含了ioremap、iowrite等内核访问IO内存等函数的定义。
#include <linux/io.h>
//驱动要写入内核,与内核相关的头文件
#include <linux/kernel.h>

#define CMD_TEST1 _IO('A', 1)
#define CMD_TEST0 _IO('L', 0)
#define CMD_TEST2 _IOW('L', 2, int)
#define CMD_TEST3 _IOW('L', 3, int)
#define CMD_TEST4 _IOR('L', 4, int)

ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
    printk("misc_read\n ");
    return 0;
}
ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
    /*应用程序传入数据到内核空间,然后控制led的逻辑,在此添加*/
    // kbuf保存的是从应用层读取到的数据
    char kbuf[64] = {0};
    // copy_from_user 从应用层传递数据给内核层
    if (copy_from_user(kbuf, ubuf, size) != 0)
    {
        // copy_from_user 传递失败打印
        printk("copy_from_user error \n ");
        return -1;
    }
    //打印传递进内核的数据
    printk("kbuf is %d\n ", kbuf[0]);
    return 0;
}
/****************************************************************************************
 * @brief misc_release : 用户空间关闭设备节点时执行此函数
 * @param inode        : 文件索引
 * @param file         : 文件
 * @return             :成功返回 0  
 ****************************************************************************************/
int misc_release(struct inode *inode, struct file *file)
{
    printk("hello misc_relaease bye bye \n ");
    return 0;
}
/****************************************************************************************
 * @brief misc_open : 用户空间打开设备节点时执行此函数
 * @param inode     : 文件索引
 * @param file      : 文件
 * @return          : 成功返回 0    
 ****************************************************************************************/
int misc_open(struct inode *inode, struct file *file)
{
    printk("hello misc_open\n ");
    return 0;
}
/****************************************************************************************
 * @brief misc_ioctl : 用户空间使用ioctl(int fd, unsigned long request, ...(void* arg))时
 *                      自动执行此函数,根据命令执行对应的操作
 * @param file       : 设备文件
 * @param cmd        : 用户空间ioctl接口命令request
 * @param value      : 用户空间的arg指针,依赖于接口命令request
 * @return           : 成功返回 0   
 ****************************************************************************************/
long misc_ioctl(struct file *file, unsigned int cmd, unsigned long value)
{
    int val;
    switch (cmd)//根据命令进行对应的操作
    {
    case CMD_TEST2:
        printk("LED ON \n");
        printk("value is %ld\n", value);
        break;
    case CMD_TEST3:
        printk("LED OFF \n");
        printk("value is %ld\n", value);
        break;
    case CMD_TEST4:
        val = 12;
        if (copy_to_user((int *)value, &val, sizeof(val)) != 0)
        {
            printk("cpoy_to_usr error \n");
            return -1;
        }
        break;
    }
    return 0;
}
struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .open = misc_open,
    .release = misc_release,
    .read = misc_read,
    .write = misc_write,
    .unlocked_ioctl = misc_ioctl };
struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "hello_misc",
    .fops = &misc_fops,
};
static int misc_init(void)
{
    int ret;
    ret = misc_register(&misc_dev);
    if (ret < 0)
    {
        printk("misc registe is error \n");
    }
    printk("misc registe is succeed \n");

    return 0;
}
static void misc_exit(void)
{
    misc_deregister(&misc_dev);

    printk(" misc gooodbye! \n");
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");

将led.c编译为led.ko 文件,如下图所示:

应用程序代码ioctl.c代码如下:

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

#define CMD_TEST0 _IO('L', 0)
#define CMD_TEST1 _IO('A', 1)

#define CMD_TEST2 _IOW('L', 2, int)
#define CMD_TEST3 _IOW('L', 3, int)
#define CMD_TEST4 _IOR('L', 4, int)

int main(int argc, char *argv[])
{
    int fd;
    int value;
    char buf[64] = {0};
    fd = open("/dev/hello_misc", O_RDWR);
    if (fd < 0)
    {
        perror("open error \n");
        return fd;
    }
    while (1)
    {
        ioctl(fd, CMD_TEST4, &value);
        printf("value is %d \n", value);
        sleep(2);
    }
    return 0;
}

 编译完成如我们进入共享目录,卸载原来加载的驱动模块,然后再加载新的驱动模块以后,再运行应用程序,如下图所示:

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

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

相关文章

云计算安全扩展要求解读

云计算简介&#xff1a; 一种全新网络服务方式&#xff0c;将传统的以桌面为核 心的任务处理转变为以网络为核心的任务处理&#xff0c; 利用互联网实现自己想要完成的一切处理任务&#xff0c; 使网络成为传递服务、计算力和信息的综合媒 介&#xff0c;真正实现按需计算、多…

揭秘Django与Neo4j:构建智能知识图谱的终极指南

揭秘Django与Neo4j:构建智能知识图谱的终极指南 前言 图是一种用于对象之间的成对关系进行建模的数学结构。 它由两个主要元素组成:节点和关系。 节点:节点可以看作是传统数据库中的记录。每个节点代表一个对象或实体,例如一个人或一个地方。节点按标签分类,这有助于根…

自研点直播转码核心

1. 背景 视频转码是将视频文件经过解封装、解码、滤镜处理、编码、封装从而转换为另一个视频文件的过程&#xff0c;B站每天都有大量的视频原片上传后经过转码系统转换为多个不同分辨率。转换后的视频在画质接近原片的前提下会拥有更低的码率&#xff0c;因此会提高网络传输时的…

3.3-LSTM的改进

文章目录 1改进点1.1多层化1.2 dropout1.2.1具体概念1.2.2应该插入到LSTM模型的哪里 1.3权重共享 2改进之后的LSTMLM的代码实现2.1初始化2.2前向计算2.3反向传播 3相应的学习代码的实现4总结 1改进点 1.1多层化 加深神经网络的层数往往能够学习更复杂的模式&#xff1b;因此这…

定制化爬虫管理:为企业量身打造的数据抓取方案

在数据驱动的时代&#xff0c;企业如何高效、安全地获取互联网上的宝贵信息&#xff1f;定制化爬虫管理服务应运而生&#xff0c;成为解锁专属数据宝藏的金钥匙。本文将深入探讨定制化爬虫管理如何为企业量身打造数据抓取方案&#xff0c;揭秘其在海量信息中精准捕获价值数据的…

C++初阶学习——探索STL奥秘——标准库中的string类

1. 为什么学习string类&#xff1f; 在我们学习C语言的时候&#xff0c;有一个点是非常难处理的&#xff0c;那就是字符串&#xff0c;在我们对字符串访问&#xff0c;增删查改时都是非常不便的&#xff0c;所以我们封装了一个string类主要来处理字符串有关的问题 2. 标准库中…

Premiere简约手绘风格箭头标题文字动画素材MOGRT

18个简约手绘风格箭头标题文字动画 | Premiere Pro mogrt 具有可定制的颜色和可编辑的文本。包括手绘箭头和文本动画&#xff0c;非常适合在项目中添加动态指针、标记和方向指示器。非常适合信息图表、社交媒体内容、下三分之一、徽章和简约界面。 项目特点&#xff1a; 独特…

python机器学习8--网络

1.超文本传输协议HTTP GET 在实际开发应用程序时&#xff0c;一定都会利用WiFi网络进行连接&#xff0c;再通过HTTP的方式读入后台的数据&#xff0c;并下载和显示在用户的PC上。这靠的是网络服务的技术&#xff0c;也就是大家提到的Web Service。而与HTTP服务器交换数据有两种…

北斗卫星导航:改变出行方式的科技奇迹

随着科技的不断发展&#xff0c;人们的出行方式也发生了翻天覆地的变化。而现代导航技术在其中扮演着重要的角色&#xff0c;尤其是北斗卫星导航系统的出现&#xff0c;更是给出行方式带来了巨大的改变。北斗卫星导航系统作为全球最大的卫星导航系统之一&#xff0c;不仅改善了…

《Milvus Cloud向量数据库指南》——BGE-M3:多功能、多语言、多粒度的文本表示学习模型

引言 在自然语言处理(NLP)领域,随着大数据时代的到来,对文本信息的精准处理与高效检索成为了研究热点。BERT(Bidirectional Encoder Representations from Transformers)作为近年来NLP领域的里程碑式模型,以其强大的上下文理解能力在多项任务中取得了显著成效。然而,面…

一文看懂以太坊智能合约!

点击下方“JavaEdge”&#xff0c;选择“设为星标” 第一时间关注技术干货&#xff01; 免责声明~ 任何文章不要过度深思&#xff01; 万事万物都经不起审视&#xff0c;因为世上没有同样的成长环境&#xff0c;也没有同样的认知水平&#xff0c;更「没有适用于所有人的解决方案…

学习笔记之Java篇(0725)

p this 普通方法中&#xff0c;this总是指向调用该方法的对象。 构造方法中&#xff0c;this总是指向正要初始化的对象。 this&#xff08;&#xff09;调用必须重载的构造方法&#xff0c;避免相同地址初始化代码&#xff0c;但只能在构造方法中用&#xff0c;比企鹅必须位…

相关性模型-正态分布均值假设检验★★★

该博客为个人学习清风建模的学习笔记&#xff0c;部分课程可以在B站&#xff1a;【强烈推荐】清风&#xff1a;数学建模算法、编程和写作培训的视频课程以及Matlab等软件教学_哔哩哔哩_bilibili 目录 1双侧检验 2单侧检验 3t检验 4两个正态总体均值差的检验 5逐对比较法 …

Origin制作线性拟合回归图

选中数据&#xff0c;点下方散点图 调整散点颜色 在分析中打开线性拟合回归 添加文本 显示上轴

四、单线程多路IO复用+多线程业务工作池

文章目录 一、前言1 编译方法 二、单线程多路IO复用多线程业务工作池结构三、重写Client_Context类四、编写Server类 一、前言 我们以及讲完单线程多路IO复用 以及任务调度与执行的C线程池&#xff0c;接下来我们就给他结合起来。 由于项目变大&#xff0c;尝试解耦项目&#…

基于opencv的答题卡识别

文章目录 一、背景需求二、处理步骤图片预处理检测到答题卡轮廓透视变换找每个圆圈的轮廓轮廓排序判断是否答题正确 一、背景需求 传统的手动评分方法耗时且容易出错&#xff0c;自动化评分可以可以显著提高评分过程的速度和准确性、减少人工成本。 答题卡图片处理效果如下&am…

使用法国云手机进行面向法国的社媒营销

在当今数字化和全球化的时代&#xff0c;社交媒体已经成为企业营销和拓展市场的重要工具。对于想进入法国市场的企业来说&#xff0c;如何在海外社媒营销中脱颖而出、抓住更多的市场份额&#xff0c;成为了一个关键问题。法国云手机正为企业提供全新的营销工具&#xff0c;助力…

观测云产品更新 | 异常追踪、场景图表、快照、监控等

观测云更新 异常追踪 1、新增【分析看板】&#xff1a;可视化展示不同指标数据。 2、新增【日程】管理和【通知策略】&#xff1a;对 Issue 的内容范围做进一步通知分配。 场景 1、图表&#xff1a;新增【时间偏移】设置。启用时间偏移后&#xff0c;当查询相对时间区间时&a…

项目架构知识点总结

项目架构知识点总结 【一】重要注解【1】SpringBootApplication&#xff08;1&#xff09;⭐️ComponentScan 注解&#xff08;2&#xff09;⭐️EnableAutoConfiguration 注解&#xff08;3&#xff09;⭐️SpringBootConfiguration 注解&#xff08;4&#xff09;Inherited 注…

昇思25天学习打卡营第01天|昇思MindSpore大模型基础j介绍

昇思MindSpore和华为昇思MindSpore大模型学习打卡系列文章&#xff0c;本文仅供参考~ 文章目录 前言一、昇思MindSpore是什么&#xff1f;二、执行流程三、设计理念四、层次结构五、Huawei昇腾AI全栈 前言 随着计算机大模型的不断发展&#xff0c;Ai这门技术也越来越重要&#…