RK3568驱动指南|第十三篇 输入子系统-第148章 通用事件处理层open函数分析

本文主要是介绍RK3568驱动指南|第十三篇 输入子系统-第148章 通用事件处理层open函数分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。


【公众号】迅为电子

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

【视频观看】嵌入式学习之Linux驱动(第十三篇 输入子系统_全新升级)_基于RK3568

【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板


第148章 通用事件处理层open函数分析

148.1 open函数分析

在上个章节中,我们已经学习了通用事件处理层connect函数,在connect函数中,要创建字符设备,创建字符设备中最重要的操作是实现文件操作集中的函数,如下所示:

比如我们在应用程序中使用open函数打开设备节点,会执行文件操作集中的open函数,当我们在应用程序中使用read函数读设备节点的时候,会执行文件操作集中的read函数。本章节我们重点学习open函数,如下所示:

static int evdev_open(struct inode *inode, struct file *file)
{// 从 inode 的 i_cdev 成员中获取 evdev 结构体的指针struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);// 计算缓冲区大小unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);// 计算分配内存的大小,包括 evdev_client 结构体和输入事件缓冲区unsigned int size = sizeof(struct evdev_client) +bufsize * sizeof(struct input_event);// 定义 evdev_client 结构体指针struct evdev_client *client;int error;// 分配内存用于存储 evdev_client 结构体和输入事件缓冲区client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);if (!client)client = vzalloc(size);if (!client)return -ENOMEM;// 初始化 client 结构体的成员变量client->bufsize = bufsize;spin_lock_init(&client->buffer_lock);client->evdev = evdev;// 将 client 添加到 evdev 的客户端列表中evdev_attach_client(evdev, client);// 打开底层设备error = evdev_open_device(evdev);if (error)goto err_free_client;// 将 client 结构体设置为文件的私有数据file->private_data = client;// 使用 nonseekable_open 函数打开文件,设置文件为不可寻址的nonseekable_open(inode, file);// 返回成功的状态码return 0;err_free_client:// 打开设备失败,需要进行错误处理// 从 evdev 的客户端列表中移除 clientevdev_detach_client(evdev, client);// 释放 client 分配的内存kvfree(client);// 返回错误码return error;
}

上面这段代码是输入设备驱动程序中evdev_open函数,用于处理输入设备的打开操作,以下是对代码的解释。

1 首先,函数使用container_of宏从inode->i_cdev中获取指向struct evdev的指针。这里假设struct evdev结构体包含一个名为cdev的成员变量,该成员变量保存了指向字符设备的指针。

2 函数调用evdev_compute_buffer_size函数来计算输入设备缓冲区的大小,存储在bufsize变量中。该函数根据输入设备的属性计算出缓冲区的大小。

3 接下来,函数计算需要分配的内存大小,即struct evdev_client结构体的大小加上输入事件缓冲区的大小。这个大小存储在size变量中。

4 函数使用kzalloc函数尝试以GFP_KERNEL | __GFP_NOWARN标志分配内存空间,并将返回的指针赋给client变量。如果kzalloc分配失败,则使用vzalloc函数尝试以GFP_KERNEL标志分配内存空间。

5如果最终分配的内存空间为空,则表示内存分配失败,函数返回-ENOMEM。

6如果内存分配成功,函数根据分配的大小设置client结构体的字段,包括缓冲区大小(bufsize)、缓冲区的自旋锁(buffer_lock)、关联的输入设备指针(evdev)。

7 接下来,函数调用evdev_attach_client函数将client结构体与输入设备关联起来,表示该客户端正在访问该输入设备。

8 函数调用evdev_open_device函数打开输入设备,执行一些与设备相关的初始化操作。如果打开设备失败,函数跳转到err_free_client标签处进行错误处理。如果打开设备成功,函数将client指针存储在file结构的private_data字段中,以便在后续的文件操作中可以访问到client结构体。

9最后,函数调用nonseekable_open函数将文件标记为不可寻址,以指示该文件不支持随机访问。

10如果一切顺利,函数返回0,表示输入设备的打开操作成功。

11如果在任何步骤中出现错误,函数通过跳转到err_free_client标签处进行错误处理。在错误处理中,函数调用evdev_detach_client函数将client从输入设备中分离,然后使用kvfree函数释放client占用的内存空间,并返回相应的错误码。

函数调用evdev_open_device函数打开输入设备,我们来详细看看这个函数

在上述代码中,首先使用mutex_lock_interruptible函数获取输入设备的互斥锁,如果无法获取锁,函数将返回相应的错误码。如果成功获取了锁,函数会继续执行后续的操作。

函数首先检查输入设备的exist字段,如果该字段为假(0),表示输入设备不存在,函数将返回-ENODEV,表示设备不存在的错误码。否则,如果输入设备的open字段为0,表示设备之前没有被打开过,函数将进一步执行以下操作。

  1. 调用input_open_device函数打开输入设备,并将返回值存储在retval变量中。
  2. 如果打开设备失败,函数会将evdev->open减一,表示设备的打开计数器递减。

最后,函数释放输入设备的互斥锁,使用mutex_unlock函数,并返回retval,表示打开设备的结果。

148.2 ioctl函数分析

接下来我们继续分析下ioctl函数,如下图所示:

evdev_ioctl函数如下所示:

上述函数中调用evdev_ioctl_handler函数来处理IO控制操作,传递给它file指针,cmd和类型转换后的arg作为参数。函数将arg转换为void __user *类型,以便在用户空间和内核空间之间传递指针。函数将evdev_ioctl_handler的返回值作为自己的返回值,并将其直接返回给调用者。

evdev_ioctl_handler函数如下所示:

static long evdev_ioctl_handler(struct file *file, unsigned int cmd,void __user *p, int compat_mode)
{// 从文件结构获取指向evdev_client的指针struct evdev_client *client = file->private_data;// 从evdev_client获取指向evdev的指针struct evdev *evdev = client->evdev;int retval;// 尝试获取evdev的互斥锁,如果无法获取则返回相应的错误码retval = mutex_lock_interruptible(&evdev->mutex);if (retval)return retval;// 检查设备是否存在或者客户端是否已被撤销if (!evdev->exist || client->revoked) {// 如果设备不存在或客户端已被撤销,则返回设备不存在的错误码retval = -ENODEV;goto out;}// 调用evdev_do_ioctl函数来执行实际的IO控制操作,并将返回值存储在retval变量中retval = evdev_do_ioctl(file, cmd, p, compat_mode);out:// 解锁evdev的互斥锁mutex_unlock(&evdev->mutex);// 返回IO控制操作的结果return retval;
}

上面的代码是evdev_ioctl_handler的函数,用于处理输入设备的IO控制操作,以下是对代码的解释。

1 函数接收一个指向struct file结构的指针file,一个无符号整数cmd,一个指向void __user类型的指针p,以及一个整数compat_mode作为参数。

2 函数从file结构的private_data字段获取指向struct evdev_client的指针client,并从中获取指向struct evdev的指针evdev。这些结构体用于表示输入设备和客户端信息。

3函数尝试获取输入设备的互斥锁,使用mutex_lock_interruptible函数。如果无法获取锁,函数将返回相应的错误码。如果成功获取了锁,函数会继续执行后续的操作。

4 函数首先检查输入设备的exist字段和客户端的revoked字段,如果设备不存在或者客户端已被撤销,则将返回-ENODEV,表示设备不存在的错误码。否则,函数调用evdev_do_ioctl函数来执行实际的IO控制操作,传递给它file指针,cmd,p和compat_mode作为参数,并将返回值存储在retval变量中。

5 函数通过标签out跳转到mutex_unlock处,在解锁输入设备的互斥锁之前最终返回retval的值。

6 最后,函数解锁输入设备的互斥锁,使用mutex_unlock函数,并返回retval,表示IO控制操作的结果。

evdev_do_ioctl函数来执行实际的IO控制操作,我们详细来看看这个函数,如下所示:

static long evdev_do_ioctl(struct file *file, unsigned int cmd,void __user *p, int compat_mode)
{struct evdev_client *client = file->private_data;struct evdev *evdev = client->evdev;struct input_dev *dev = evdev->handle.dev;struct input_absinfo abs;struct input_mask mask;struct ff_effect effect;int __user *ip = (int __user *)p;unsigned int i, t, u, v;unsigned int size;int error;/* First we check for fixed-length commands */switch (cmd) {case EVIOCGVERSION:return put_user(EV_VERSION, ip);case EVIOCGID:if (copy_to_user(p, &dev->id, sizeof(struct input_id)))return -EFAULT;return 0;case EVIOCGREP:if (!test_bit(EV_REP, dev->evbit))return -ENOSYS;if (put_user(dev->rep[REP_DELAY], ip))return -EFAULT;if (put_user(dev->rep[REP_PERIOD], ip + 1))return -EFAULT;return 0;case EVIOCSREP:if (!test_bit(EV_REP, dev->evbit))return -ENOSYS;if (get_user(u, ip))return -EFAULT;if (get_user(v, ip + 1))return -EFAULT;input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);return 0;case EVIOCRMFF:return input_ff_erase(dev, (int)(unsigned long) p, file);case EVIOCGEFFECTS:i = test_bit(EV_FF, dev->evbit) ?dev->ff->max_effects : 0;if (put_user(i, ip))return -EFAULT;return 0;case EVIOCGRAB:if (p)return evdev_grab(evdev, client);elsereturn evdev_ungrab(evdev, client);case EVIOCREVOKE:if (p)return -EINVAL;elsereturn evdev_revoke(evdev, client, file);case EVIOCGMASK: {void __user *codes_ptr;if (copy_from_user(&mask, p, sizeof(mask)))return -EFAULT;codes_ptr = (void __user *)(unsigned long)mask.codes_ptr;return evdev_get_mask(client,mask.type, codes_ptr, mask.codes_size,compat_mode);}case EVIOCSMASK: {const void __user *codes_ptr;if (copy_from_user(&mask, p, sizeof(mask)))return -EFAULT;codes_ptr = (const void __user *)(unsigned long)mask.codes_ptr;return evdev_set_mask(client,mask.type, codes_ptr, mask.codes_size,compat_mode);}case EVIOCSCLOCKID:if (copy_from_user(&i, p, sizeof(unsigned int)))return -EFAULT;return evdev_set_clk_type(client, i);case EVIOCGKEYCODE:return evdev_handle_get_keycode(dev, p);case EVIOCSKEYCODE:return evdev_handle_set_keycode(dev, p);case EVIOCGKEYCODE_V2:return evdev_handle_get_keycode_v2(dev, p);case EVIOCSKEYCODE_V2:return evdev_handle_set_keycode_v2(dev, p);}size = _IOC_SIZE(cmd);/* Now check variable-length commands */
#define EVIOC_MASK_SIZE(nr)	((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))switch (EVIOC_MASK_SIZE(cmd)) {case EVIOCGPROP(0):return bits_to_user(dev->propbit, INPUT_PROP_MAX,size, p, compat_mode);case EVIOCGMTSLOTS(0):return evdev_handle_mt_request(dev, size, ip);case EVIOCGKEY(0):return evdev_handle_get_val(client, dev, EV_KEY, dev->key,KEY_MAX, size, p, compat_mode);case EVIOCGLED(0):return evdev_handle_get_val(client, dev, EV_LED, dev->led,LED_MAX, size, p, compat_mode);case EVIOCGSND(0):return evdev_handle_get_val(client, dev, EV_SND, dev->snd,SND_MAX, size, p, compat_mode);case EVIOCGSW(0):return evdev_handle_get_val(client, dev, EV_SW, dev->sw,SW_MAX, size, p, compat_mode);case EVIOCGNAME(0):return str_to_user(dev->name, size, p);case EVIOCGPHYS(0):return str_to_user(dev->phys, size, p);case EVIOCGUNIQ(0):return str_to_user(dev->uniq, size, p);case EVIOC_MASK_SIZE(EVIOCSFF):if (input_ff_effect_from_user(p, size, &effect))return -EFAULT;error = input_ff_upload(dev, &effect, file);if (error)return error;if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))return -EFAULT;return 0;}/* Multi-number variable-length handlers */if (_IOC_TYPE(cmd) != 'E')return -EINVAL;if (_IOC_DIR(cmd) == _IOC_READ) {if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))return handle_eviocgbit(dev,_IOC_NR(cmd) & EV_MAX, size,p, compat_mode);if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {if (!dev->absinfo)return -EINVAL;t = _IOC_NR(cmd) & ABS_MAX;abs = dev->absinfo[t];if (copy_to_user(p, &abs, min_t(size_t,size, sizeof(struct input_absinfo))))return -EFAULT;return 0;}}if (_IOC_DIR(cmd) == _IOC_WRITE) {if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {if (!dev->absinfo)return -EINVAL;t = _IOC_NR(cmd) & ABS_MAX;if (copy_from_user(&abs, p, min_t(size_t,size, sizeof(struct input_absinfo))))return -EFAULT;if (size < sizeof(struct input_absinfo))abs.resolution = 0;/* We can't change number of reserved MT slots */if (t == ABS_MT_SLOT)return -EINVAL;/** Take event lock to ensure that we are not* changing device parameters in the middle* of event.*/spin_lock_irq(&dev->event_lock);dev->absinfo[t] = abs;spin_unlock_irq(&dev->event_lock);return 0;}}return -EINVAL;
}

上述代码中的这些命令解释如下:

EVIOCGVERSION: 获取去掉版本号
EVIOCGID:获取输入设备的ID信息
EVIOCSREP:获取按键重复设置
EVIOCGKEYCODE: 获取按键码
EVIOCGKEYCODE_V2: 获取按键映射表
EVIOCSKEYCODE:设置按键值
EVIOCSKEYCODE_V2:设置按键映射表
EVIOCGNAME(len):获取设备名称
EVIOCGPHYS(len):获取物理位置
EVIOCGUNIQ(len):获取唯一标识符
EVIOCGPROP(len):获取设备属性
EVIOCGMTSLOTS(len):获取多点触控信息
EVIOCGKEY(len):获取全局按键状态
EVIOCGLED(len):获取所有LED状态
EVIOCGSND(len):获取所有声音状态
EVIOCGSW(len):获取所有开关状态
EVIOCGBIT(ev,len):获取事件位图
EVIOCGABS(abs):获取绝对值/范围
EVIOCSABS(abs):设置绝对值/范围
EVIOCSFF:发送力反馈效果到力反馈设备
EVIOCRMFF:删除力反馈效果
EVIOCGEFFECTS:报告同时可播放的效果数量
EVIOCGRAB:占用/释放输入设备
EVIOCREVOKE:撤销设备访问权限
EVIOCGMASK:检索当前事件掩码
EVIOCSMASK:设置事件掩码
EVIOCSCLOCKID:设置用于时间戳的时钟标识

总结来说,evdev_do_ioctl函数是通用事件处理层中用于处理输入设备ioctl命令的函数。它根据传入的参数,找到对应的输入设备对象,并执行相应的操作。这使得开发者可以通过ioctl命令与输入设备进行交互,设置参数、查询状态或执行其他特定的操作,以满足特定的应用需求。

148.3 poll函数分析

接下来我们继续分析下poll函数,如下所示:

evdev_poll函数如下所示:

static __poll_t evdev_poll(struct file *file, poll_table *wait)
{// 获取文件私有数据中的evdev_client结构体指针struct evdev_client *client = file->private_data;// 获取evdev_client结构体中的evdev指针struct evdev *evdev = client->evdev;__poll_t mask;// 将当前进程加入到等待队列中,等待evdev->wait的唤醒事件poll_wait(file, &evdev->wait, wait);// 检查evdev->exist和client->revoked的值if (evdev->exist && !client->revoked)// 如果evdev存在且client未被撤销,设置mask为EPOLLOUT | EPOLLWRNORMmask = EPOLLOUT | EPOLLWRNORM;else// 否则,设置mask为EPOLLHUP | EPOLLERRmask = EPOLLHUP | EPOLLERR;// 检查client中的packet_head和tail的值if (client->packet_head != client->tail)// 如果packet_head和tail不相等,设置mask为mask | EPOLLIN | EPOLLRDNORMmask |= EPOLLIN | EPOLLRDNORM;// 返回最终的mask值return mask;
}

上述代码是用于处理evdev设备文件的poll操作。它通过检查相应的事件状态来确定是否需要等待,读取或写入。函数首先将当前进程添加到等待队列中,以等待evdev->wait的唤醒事件。然后根据evdev->exist和client->revoked的值设置mask的值。如果evdev存在且client未被撤销,则将mask设置为EPOLLOUT | EPOLLWRNORM,表示可写入。否则,将mask设置为EPOLLHUP | EPOLLERR,表示发生错误或连接已挂断。接下来,函数检查client中的packet_head和tail的值,如果它们不相等,则将mask设置为mask | EPOLLIN | EPOLLRDNORM,表示可读取。最后,函数返回最终的mask值,表示等待、读取和写入的事件状态。

148.4 fasync函数分析

接下来我们继续分析下fasync函数,如下所示:、

evdev_fasync函数如下所示:

static int evdev_fasync(int fd, struct file *file, int on)
{// 获取文件私有数据中的evdev_client结构体指针struct evdev_client *client = file->private_data;// 调用fasync_helper函数来处理进程的异步通知// 该函数会根据on的值,将进程添加到或从异步通知的列表中// 并将通知相关的数据存储在client->fasync中return fasync_helper(fd, file, on, &client->fasync);
}

上述函数用于处理evdev设备文件的异步通知注册和注销。它接收文件描述符fd,文件指针file和一个整数on作为参数。函数首先从文件的私有数据中获取evdev_client结构体指针。然后调用fasync_helper函数,该函数负责处理进程的异步通知。根据on的值,fasync_helper函数将进程添加到异步通知的列表中或从列表中移除,并将通知相关的数据存储在client->fasync中。最后函数返回fasync_helper的返回值,表示异步通知的注册和注销是否成功。

148.5 llseek函数

接下来我们继续分析下llseek函数

no_llseek函数如下所示:

在函数开始时,首先将-ESPIPE作为返回值直接返回。这个函数的作用是阻止对设备文件执行llseek操作,也就是不允许通过改变文件位置指针来随机访问设备文件。这可能是因为设备特性或驱动程序的限制所导致的。通过禁止定位操作,可确保对设备文件的访问方式按照预期方式进行,避免潜在的问题和错误。

148.6 release函数分析

接下来我们继续分析release函数,如下所示:

evdev_release函数如下所示:

static int evdev_release(struct inode *inode, struct file *file)
{// 获取文件私有数据中的evdev_client结构体指针struct evdev_client *client = file->private_data;// 获取evdev_client结构体中的evdev指针struct evdev *evdev = client->evdev;unsigned int i;// 获取evdev的互斥锁,确保对evdev的操作是原子的mutex_lock(&evdev->mutex);// 检查evdev->exist和client->revoked的值if (evdev->exist && !client->revoked)// 如果evdev存在且client未被撤销,调用input_flush_device函数刷新设备的输入缓冲区input_flush_device(&evdev->handle, file);// 释放evdev的抢占状态,将客户端从抢占列表中移除evdev_ungrab(evdev, client);// 解锁evdev的互斥锁mutex_unlock(&evdev->mutex);// 从evdev中分离并释放客户端evdev_detach_client(evdev, client);// 释放客户端的事件掩码内存for (i = 0; i < EV_CNT; ++i)bitmap_free(client->evmasks[i]);// 释放客户端的内存kvfree(client);// 关闭evdev设备evdev_close_device(evdev);// 返回0表示成功释放资源return 0;
}

该函数用于释放evdev设备文件相关的资源。它接收一个inode结构体指针和一个file结构体指针作为参数。函数首先从文件的私有数据中获取evdev_client结构体指针和evdev指针。然后获取evdev的互斥锁,确保对evdev的操作是原子的。

接下来,函数检查evdev->exist和client->revoked的值。如果evdev存在且client未被撤销,则调用input_flush_device函数刷新设备的输入缓冲区,确保所有未处理的输入事件被处理。

然后,函数释放evdev的抢占状态,通过调用evdev_ungrab函数将客户端从抢占列表中移除。接着,解锁evdev的互斥锁。

接下来,函数调用evdev_detach_client函数从evdev中分离并释放客户端。

然后,函数使用循环遍历释放客户端的事件掩码内存,通过调用bitmap_free函数进行释放。

接着,函数使用kvfree函数释放客户端的内存。

最后,函数调用evdev_close_device函数关闭evdev设备。

最终,函数返回0表示成功释放资源。

整体来说,这个函数的作用是用于释放设备文件的资源和与之关联的内存空间,以便其他客户端能够继续访问设备并避免占用过多的系统资源。该函数在设备文件关闭时自动调用,通常不需要用户手动调用。

这篇关于RK3568驱动指南|第十三篇 输入子系统-第148章 通用事件处理层open函数分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/845443

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(五):Blender锥桶建模

前言 本系列教程旨在使用UE5配置一个具备激光雷达+深度摄像机的仿真小车,并使用通过跨平台的方式进行ROS2和UE5仿真的通讯,达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础,Nav2相关的学习教程可以参考本人的其他博客Nav2代价地图实现和原理–Nav2源码解读之CostMap2D(上)-CSDN博客往期教程: 第一期:基于UE5和ROS2的激光雷达+深度RG

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud