【LED子系统】六、核心层详解(三)

news2024/12/21 22:08:31
img
个人主页:董哥聊技术
我是董哥,高级嵌入式软件开发工程师,从事嵌入式Linux驱动开发和系统开发,曾就职于世界500强公司!
创作理念:专注分享高质量嵌入式文章,让大家读有所得!
img

文章目录

    • 1、前言
    • 2、触发器设置相关函数
      • 2.1 struct led_trigger
      • 2.2 led_trigger_show
      • 2.3 led_trigger_store
      • 2.4 led_trigger_set
      • 2.5 led_trigger_remove
    • 3、触发器注册注销函数
      • 3.1 led_trigger_register
      • 3.2 led\_trigger\_unregister
      • 3.3 devm_led_trigger_register
      • 3.4 led_trigger_register_simple
      • 3.5 led_trigger_unregister_simple
    • 4、闪烁功能相关函数
      • 4.1 led_trigger_blink
      • 4.2 led_trigger_blink_oneshot
      • 4.3 led_trigger_blink_setup
      • 4.4 led_blink_set_oneshot和led_blink_set
      • 4.5 led_blink_setup
      • 4.6 led_set_software_blink
      • 4.7 调用流程
    • 5、总结回顾

1、前言

上篇文章我们了解了子系统的核心层led-core.c,下面我们来分析驱动框架中核心层的led-triggers.c实现以及作用。

image-20230417084033734

我们在了解led-class.c文件的时候,就看到有定义好的触发控制相关的文件节点:

#ifdef CONFIG_LEDS_TRIGGERS
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
static struct attribute *led_trigger_attrs[] = {
    &dev_attr_trigger.attr,
    NULL,
};

DEVICE_ATTR定义的led_trigger_showled_trigger_store就是在led-triggers.c文件中定义的!

 

2、触发器设置相关函数

2.1 struct led_trigger

struct led_trigger {
    /* Trigger Properties */
    const char	 *name;
    int		(*activate)(struct led_classdev *led_cdev);
    void		(*deactivate)(struct led_classdev *led_cdev);

    /* LEDs under control by this trigger (for simple triggers) */
    rwlock_t	  leddev_list_lock;
    struct list_head  led_cdevs;

    /* Link to next registered trigger */
    struct list_head  next_trig;

    const struct attribute_group **groups;
};

结构体名称led_trigger

文件位置include/linux/leds.h

主要作用:用于表示 Linux 内核中的 LED 触发器。LED 触发器是一种控制 LED 设备行为的机制,例如打开或关闭、闪烁或改变亮度等。

  • name:标识触发器的字符串。
  • activate:当触发器被激活时调用的函数指针。
  • deactivate:当触发器被停用时调用的函数指针。
  • leddev_list_lock:保护触发器控制的 LED 设备列表的读写锁。
  • led_cdevs:由触发器控制的 LED 设备列表。
  • next_trig:指向下一个已注册触发器的指针。
  • groups:与触发器关联的属性组的数组。

 

2.2 led_trigger_show

ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
        char *buf)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);
    struct led_trigger *trig;
    int len = 0;

    down_read(&triggers_list_lock);
    down_read(&led_cdev->trigger_lock);

    if (!led_cdev->trigger)
        len += scnprintf(buf+len, PAGE_SIZE - len, "[none] ");
    else
        len += scnprintf(buf+len, PAGE_SIZE - len, "none ");

    list_for_each_entry(trig, &trigger_list, next_trig) {
        if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
                            trig->name))
            len += scnprintf(buf+len, PAGE_SIZE - len, "[%s] ",
                     trig->name);
        else
            len += scnprintf(buf+len, PAGE_SIZE - len, "%s ",
                     trig->name);
    }
    up_read(&led_cdev->trigger_lock);
    up_read(&triggers_list_lock);

    len += scnprintf(len+buf, PAGE_SIZE - len, "\n");
    return len;
}

函数介绍led_trigger_show该函数主要用于显示给定 LED 设备的当前触发方式的函数,还记得第一章我们讲的,触发方式包括以下几种:

触发方式说明
none无触发方式
disk-activity硬盘活动
nand-disknand flash活动
mtdmtd设备活动
timer定时器
heartbeat系统心跳

实现方式

  1. 先通过dev_get_drvdata接口,获取LED数据
  2. down_readup_read是读写锁的函数,用于获取和释放读锁。具体用法如下:

当一个线程调用 down_read 时,如果没有其他线程持有写锁,则该线程将获得读锁并继续执行。如果有其他线程持有写锁,则该线程将被阻塞,直到所有写锁都被释放

当一个线程完成对共享资源的读取时,它应该调用 up_read 来释放读锁,以便其他线程可以获取读锁或写锁。

总之,down_readup_read 是用于获取和释放读锁的函数,用于确保多个线程可以同时读取共享资源,而不会发生竞争条件。

  1. 对触发器进行判断,如果没有,则返回[none][]是选中的意思,表示触发器的当前触发方式;如果存在,则将触发器名称与当前触发器匹配,则将 [trigger_name>] 添加到缓冲区。

 

2.3 led_trigger_store

ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
        const char *buf, size_t count)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);
    struct led_trigger *trig;
    int ret = count;

    mutex_lock(&led_cdev->led_access);

    if (led_sysfs_is_disabled(led_cdev)) {
        ret = -EBUSY;
        goto unlock;
    }

    if (sysfs_streq(buf, "none")) {
        led_trigger_remove(led_cdev);
        goto unlock;
    }

    down_read(&triggers_list_lock);
    list_for_each_entry(trig, &trigger_list, next_trig) {
        if (sysfs_streq(buf, trig->name)) {
            down_write(&led_cdev->trigger_lock);
            led_trigger_set(led_cdev, trig);
            up_write(&led_cdev->trigger_lock);

            up_read(&triggers_list_lock);
            goto unlock;
        }
    }
    /* we come here only if buf matches no trigger */
    ret = -EINVAL;
    up_read(&triggers_list_lock);

unlock:
    mutex_unlock(&led_cdev->led_access);
    return ret;
}
EXPORT_SYMBOL_GPL(led_trigger_store);

函数介绍led_trigger_store函数用于设置给定LED设备的触发器。

相关实现

  1. 先通过dev_get_drvdata接口,获取LED数据。
  2. 调用led_sysfs_is_disabled接口,判断文件系统接口是否使能
  3. 如果使能,则函数检查输入缓冲区buf是否等于none。如果是,则函数调用led_trigger_removeLED设备中删除触发器并返回。
  4. 如果输入缓冲区buf不等于none,则遍历触发器列表,并依次比较,如果匹配,则调用led_trigger_set设置触发器

led_trigger_store其内部根据不同情况,将设置 led_trigger_set或者移除led_trigger_remove相关的LED触发器。下面我们分析这两个函数。

 

2.4 led_trigger_set

int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
{
    unsigned long flags;
    char *event = NULL;
    char *envp[2];
    const char *name;
    int ret;

    if (!led_cdev->trigger && !trig)
        return 0;

    name = trig ? trig->name : "none";
    event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name);

    /* Remove any existing trigger */
    if (led_cdev->trigger) {
        write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
        list_del(&led_cdev->trig_list);
        write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
            flags);
        cancel_work_sync(&led_cdev->set_brightness_work);
        led_stop_software_blink(led_cdev);
        if (led_cdev->trigger->deactivate)
            led_cdev->trigger->deactivate(led_cdev);
        device_remove_groups(led_cdev->dev, led_cdev->trigger->groups);
        led_cdev->trigger = NULL;
        led_cdev->trigger_data = NULL;
        led_cdev->activated = false;
        led_set_brightness(led_cdev, LED_OFF);
    }
    if (trig) {
        write_lock_irqsave(&trig->leddev_list_lock, flags);
        list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
        write_unlock_irqrestore(&trig->leddev_list_lock, flags);
        led_cdev->trigger = trig;

        if (trig->activate)
            ret = trig->activate(led_cdev);
        else
            ret = 0;

        if (ret)
            goto err_activate;

        ret = device_add_groups(led_cdev->dev, trig->groups);
        if (ret) {
            dev_err(led_cdev->dev, "Failed to add trigger attributes\n");
            goto err_add_groups;
        }
    }

    if (event) {
        envp[0] = event;
        envp[1] = NULL;
        if (kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp))
            dev_err(led_cdev->dev,
                "%s: Error sending uevent\n", __func__);
        kfree(event);
    }

    return 0;

err_add_groups:

    if (trig->deactivate)
        trig->deactivate(led_cdev);
err_activate:

    write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
    list_del(&led_cdev->trig_list);
    write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags);
    led_cdev->trigger = NULL;
    led_cdev->trigger_data = NULL;
    led_set_brightness(led_cdev, LED_OFF);
    kfree(event);

    return ret;
}
EXPORT_SYMBOL_GPL(led_trigger_set);

函数介绍:该函数主要用于清空所有触发器,并且设置给定的LED触发器

实现方式

  1. 调用name = trig ? trig->name : "none";获取给定的触发器名称
  2. 调用kasprintf格式化字符串,用于生成内核事件。
  3. 如果现有的LED触发器不为空,则删除所有的触发器,取消led-core.c内部初始化的工作队列,调用led_stop_software_blink停止所有LED闪烁,并调用led_set_brightnessLED熄灭
  4. 如果给定的LED触发器不为空,调用list_add_tail将给定的触发器添加到触发器列表,并调用trig->activate激活该触发器。
  5. 最后调用kobject_uevent_env,出发*KOBJ*CHANGE事件

 

2.5 led_trigger_remove

void led_trigger_remove(struct led_classdev *led_cdev)
{
    down_write(&led_cdev->trigger_lock);
    led_trigger_set(led_cdev, N	ULL);
    up_write(&led_cdev->trigger_lock);
}

函数介绍:该函数主要用于清空所有触发器

实现方式:调用led_trigger_set,将给定触发器的参数设置为NULL,即可做到清空所有触发器。

 

3、触发器注册注销函数

led-triggers.c中,除了触发器设置相关的函数外,还有一些关于触发器注册注销的函数,如:led_trigger_registerled_trigger_unregisterdevm_led_trigger_registerdevm_led_trigger_release

3.1 led_trigger_register

int led_trigger_register(struct led_trigger *trig)
{
    struct led_classdev *led_cdev;
    struct led_trigger *_trig;

    rwlock_init(&trig->leddev_list_lock);
    INIT_LIST_HEAD(&trig->led_cdevs);

    down_write(&triggers_list_lock);
    /* Make sure the trigger's name isn't already in use */
    list_for_each_entry(_trig, &trigger_list, next_trig) {
        if (!strcmp(_trig->name, trig->name)) {
            up_write(&triggers_list_lock);
            return -EEXIST;
        }
    }
    /* Add to the list of led triggers */
    list_add_tail(&trig->next_trig, &trigger_list);
    up_write(&triggers_list_lock);

    /* Register with any LEDs that have this as a default trigger */
    down_read(&leds_list_lock);
    list_for_each_entry(led_cdev, &leds_list, node) {
        down_write(&led_cdev->trigger_lock);
        if (!led_cdev->trigger && led_cdev->default_trigger &&
                !strcmp(led_cdev->default_trigger, trig->name))
            led_trigger_set(led_cdev, trig);
        up_write(&led_cdev->trigger_lock);
    }
    up_read(&leds_list_lock);

    return 0;
}
EXPORT_SYMBOL_GPL(led_trigger_register);

函数介绍:该函数主要用于注册一个LED触发器

实现方式:作为触发器的入口函数,必须先进行初始化,才能去设置触发器的相关属性,执行相应动作。

  1. 调用rwlock_init接口,初始化读写锁;调用INIT_LIST_HEAD初始化触发器链表
  2. 调用list_for_each_entry来遍历所有的触发器,与添加的触发器进行比较,调用led_trigger_set设置触发器
  3. 调用list_add_tail接口,将该触发器加入到链表中
  4. 最后再次调用list_for_each_entry接口,将该触发器注册到任何具有该触发器作为默认触发器的LED设备上。

 

3.2 led_trigger_unregister

void led_trigger_unregister(struct led_trigger *trig)
{
    struct led_classdev *led_cdev;

    if (list_empty_careful(&trig->next_trig))
        return;

    /* Remove from the list of led triggers */
    down_write(&triggers_list_lock);
    list_del_init(&trig->next_trig);
    up_write(&triggers_list_lock);

    /* Remove anyone actively using this trigger */
    down_read(&leds_list_lock);
    list_for_each_entry(led_cdev, &leds_list, node) {
        down_write(&led_cdev->trigger_lock);
        if (led_cdev->trigger == trig)
            led_trigger_set(led_cdev, NULL);
        up_write(&led_cdev->trigger_lock);
    }
    up_read(&leds_list_lock);
}

函数介绍:该函数主要用于注销一个LED触发器

实现方式:与注册相反的操作

  1. 调用list_del_init,反初始化链表
  2. 调用list_for_each_entry遍历触发器,匹配给定的触发器,然后调用led_trigger_set,传入NULL参数,来清空该触发器。

 

3.3 devm_led_trigger_register

static void devm_led_trigger_release(struct device *dev, void *res)
{
    led_trigger_unregister(*(struct led_trigger **)res);
}

int devm_led_trigger_register(struct device *dev,
                  struct led_trigger *trig)
{
    struct led_trigger **dr;
    int rc;

    dr = devres_alloc(devm_led_trigger_release, sizeof(*dr),
              GFP_KERNEL);
    if (!dr)
        return -ENOMEM;

    *dr = trig;

    rc = led_trigger_register(trig);
    if (rc)
        devres_free(dr);
    else
        devres_add(dev, dr);

    return rc;
}
EXPORT_SYMBOL_GPL(devm_led_trigger_register);

函数介绍:上面两个函数,为led_trigger_unregisterled_trigger_register的资源管理版本,这里不详细介绍。

 

3.4 led_trigger_register_simple

void led_trigger_register_simple(const char *name, struct led_trigger **tp)
{
	struct led_trigger *trig;
	int err;

	trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);

	if (trig) {
		trig->name = name;
		err = led_trigger_register(trig);
		if (err < 0) {
			kfree(trig);
			trig = NULL;
			pr_warn("LED trigger %s failed to register (%d)\n",
				name, err);
		}
	} else {
		pr_warn("LED trigger %s failed to register (no memory)\n",
			name);
	}
	*tp = trig;
}
EXPORT_SYMBOL_GPL(led_trigger_register_simple);

函数介绍led_trigger_register_simple函数是一个简单的LED触发器注册函数,用于注册一个LED触发器。

它的实现与led_trigger_register函数类似,但是它不涉及资源管理。

调用者只需要传入触发器的名称以及触发器的指针即可创建成功。

 

3.5 led_trigger_unregister_simple

void led_trigger_unregister_simple(struct led_trigger *trig)
{
	if (trig)
		led_trigger_unregister(trig);
	kfree(trig);
}

函数介绍:直接调用led_trigger_unregister来注销一个已存在的触发器,并释放空间。

 

4、闪烁功能相关函数

led-triggers.c中,除了上面提到的函数外,还包括最重要的闪烁功能控制接口。如果说上面的是一些基本配置,那么下面的就是核心实现。

4.1 led_trigger_blink

void led_trigger_blink(struct led_trigger *trig,
		       unsigned long *delay_on,
		       unsigned long *delay_off)
{
	led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
}
EXPORT_SYMBOL_GPL(led_trigger_blink);

 

4.2 led_trigger_blink_oneshot

void led_trigger_blink_oneshot(struct led_trigger *trig,
			       unsigned long *delay_on,
			       unsigned long *delay_off,
			       int invert)
{
	led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
}
EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot);

 

上面两个函数,通过不同的参数,均对led_trigger_blink_setup调用来实现,下面我们来分析一下这个函数

4.3 led_trigger_blink_setup

static void led_trigger_blink_setup(struct led_trigger *trig,
			     unsigned long *delay_on,
			     unsigned long *delay_off,
			     int oneshot,
			     int invert)
{
	struct led_classdev *led_cdev;

	if (!trig)
		return;

	read_lock(&trig->leddev_list_lock);
	list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
		if (oneshot)
			led_blink_set_oneshot(led_cdev, delay_on, delay_off,
					      invert);
		else
			led_blink_set(led_cdev, delay_on, delay_off);
	}
	read_unlock(&trig->leddev_list_lock);
}

函数介绍:该函数是触发器实现闪烁功能的接口,用于控制LED亮灭的时间,以及闪烁一次,反转的标志位设定。

 

下面的函数属于led-core.c

4.4 led_blink_set_oneshot和led_blink_set

void led_blink_set(struct led_classdev *led_cdev,
		   unsigned long *delay_on,
		   unsigned long *delay_off)
{
	del_timer_sync(&led_cdev->blink_timer);

	clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
	clear_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
	clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);

	led_blink_setup(led_cdev, delay_on, delay_off);
}
EXPORT_SYMBOL_GPL(led_blink_set);

void led_blink_set_oneshot(struct led_classdev *led_cdev,
			   unsigned long *delay_on,
			   unsigned long *delay_off,
			   int invert)
{
	if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
	     timer_pending(&led_cdev->blink_timer))
		return;

	set_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
	clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);

	if (invert)
		set_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
	else
		clear_bit(LED_BLINK_INVERT, &led_cdev->work_flags);

	led_blink_setup(led_cdev, delay_on, delay_off);
}
EXPORT_SYMBOL_GPL(led_blink_set_oneshot);

函数介绍:这两个函数均用于LED闪烁功能的设置,其主要差别在于是否为闪烁一次的功能。

实现方式:两者的差别,是通过test_bitset_bitclear_bit等接口,来控制LED标志位的变化,从而设置特定的功能。

 

4.5 led_blink_setup

static void led_blink_setup(struct led_classdev *led_cdev,
		     unsigned long *delay_on,
		     unsigned long *delay_off)
{
	if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
	    led_cdev->blink_set &&
	    !led_cdev->blink_set(led_cdev, delay_on, delay_off))
		return;

	/* blink with 1 Hz as default if nothing specified */
	if (!*delay_on && !*delay_off)
		*delay_on = *delay_off = 500;

	led_set_software_blink(led_cdev, *delay_on, *delay_off);
}

函数介绍:当LED亮灭时间均设置为0时,默认为1Hz,最后调用led_set_software_blink来控制LED闪烁

 

4.6 led_set_software_blink

static void led_set_software_blink(struct led_classdev *led_cdev,
				   unsigned long delay_on,
				   unsigned long delay_off)
{
	int current_brightness;

	current_brightness = led_get_brightness(led_cdev);
	if (current_brightness)
		led_cdev->blink_brightness = current_brightness;
	if (!led_cdev->blink_brightness)
		led_cdev->blink_brightness = led_cdev->max_brightness;

	led_cdev->blink_delay_on = delay_on;
	led_cdev->blink_delay_off = delay_off;

	/* never on - just set to off */
	if (!delay_on) {
		led_set_brightness_nosleep(led_cdev, LED_OFF);
		return;
	}

	/* never off - just set to brightness */
	if (!delay_off) {
		led_set_brightness_nosleep(led_cdev,
					   led_cdev->blink_brightness);
		return;
	}

	set_bit(LED_BLINK_SW, &led_cdev->work_flags);
	mod_timer(&led_cdev->blink_timer, jiffies + 1);
}

函数介绍:设置LED软件闪烁功能

实现思路

  1. 调用led_get_brightness接口,获取当前亮度
  2. delay_ondelay_off赋值LED亮和灭的时间间隔
  3. 特殊情况处理,如果delay_on为0,则将LED设置为关闭状态。如果delay_off为0,则将LED设置为最大亮度。
  4. 设置LED_BLINK_SW闪烁标志位,并且调用mod_timer启动一个1ms定时器,该定时器用于调用led_timer_function函数

 

4.7 调用流程

上面介绍的函数为闪烁功能的核心函数,其调用流程总结如下:

// LED触发层实现逻辑
led_trigger_blink(drivers/leds/led-triggers.c)
led_trigger_blink_oneshot(drivers/leds/led-triggers.c)
    |--> led_trigger_blink_setup(drivers/leds/led-triggers.c)
        |--> led_blink_set_oneshot                          //  闪烁一次
            |--> led_blink_setup                            //  闪烁设置
                |--> led_set_software_blink                 //  设置LED打开和关闭
                    |--> led_set_brightness_nosleep
                        |--> led_set_brightness_nopm
                            |--> __led_set_brightness
        |--> led_blink_set                                  //  闪烁
            |--> led_blink_setup

由上面调用流程可知,led-triggers.c的闪烁功能对外提供接口led_trigger_blinkled_trigger_blink_oneshot来设置闪烁。

 

5、总结回顾

我们这一章,了解了核心层的led-triggers.c实现,其主要包括了:

  1. 触发器配置函数:led_trigger_setled_trigger_remove
  2. 触发器注册注销函数:led_trigger_registerdevm_led_trigger_register
  3. 触发器闪烁设置:led_trigger_blink_oneshotled_trigger_blink

 

点赞+关注,永远不迷路

img
欢迎关注【嵌入式艺术】,董哥原创!

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

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

相关文章

腾讯云轻量应用服务器性能测评(全网超详细)

腾讯云轻量应用服务器性能如何&#xff1f;CPU型号主频、内存、公网带宽和系统盘存储多维对比&#xff0c;轻量应用服务器会不会比云服务器CVM性能差&#xff1f;相对于CVM云服务器轻量服务器更适合轻量级的应用&#xff0c;轻量服务适合中小企或个人开发者用于搭建We网站b应用…

测试用例02

复习 基于需求设计测试用例 分析需求&#xff0c;验证需求&#xff0c;细化需求&#xff0c;从需求中提取一个个的测试点&#xff0c;根据每一个测试点&#xff0c;采用具体的设计测试用例的方法取设计测试用例&#xff1a; 有多细致的需求&#xff0c;就有多细致的测试用例&am…

BATJ架构师首推!分布式事务原理与实战,出神入化

前言 广度与深度兼备、理论与实战兼顾的《分布式事务原理与实战》从以下5个维度全面深入的讲解了分布式事务&#xff1a; 基础知识维度&#xff1a;首先全面介绍了事务和分布式事务的概念和基础知识&#xff0c;然后详细讲解了MySQL事务和Spring事务的实现原理。 解决方案维度…

新一代产业变革席卷全球,深眸科技融合AI+机器视觉实现智造升级

随着新一代产业变革席卷全球&#xff0c;人工智能成为变革的核心方向&#xff0c;借助人工智能崛起的东风&#xff0c;机器视觉也逐渐成为工业制造业的必选项。在工业4.0和中国制造2025的推动下&#xff0c;中国制造企业纷纷投入到智能化转型的过程中&#xff0c;但就目前来看&…

YOLO 算法系列

我们分享了YOLO系列的文章&#xff0c;包括前段时间刚刚发布的YOLOv7检测算法&#xff0c;虽然YOLOv7对象检测算法是YOLO最新的模型&#xff0c;且可以支持对象分割&#xff0c;人体姿态检测等&#xff0c;但是最新的YOLO系列检测模型也同步进行了更新&#xff0c;那就是刚刚开…

正版软件,官方授权。RadiAnt DICOM Viewer - 软件版 / CD/DVD版

灵活、简单、快速 RadiAnt 是一个医学图像的 PACS DICOM 浏览器 设计是为了给您独特体验&#xff0c;它界面直观&#xff0c;性能无与伦比&#xff0c;您的体验感一定爆棚&#xff01; RadiAnt DICOM Viewer是一款专为医学影像浏览看图而设计的软件。它具有单个窗口界面&…

全屋智能,始终在等一双“究极手”

《塞尔达传说&#xff1a;王国之泪》是今年迄今为止最火爆的游戏&#xff0c;也是全球科技爱好者在这个5月里共同关注的热门话题。 游戏中&#xff0c;有一个广大玩家津津乐道的新功能&#xff0c;是主角林克具备了一种名为“究极手”的能力。它可以把游戏里的各种各样的物品组…

卷起来!Dr. LLaMA:通过生成数据增强改进特定领域 QA 中的小型语言模型,重点关注医学问答任务...

大家好&#xff0c;最近突然发现了一篇在专门应用于医学领域的LLaMA&#xff0c;名为Dr.LLaMA&#xff08;太卷了太卷了&#xff09;&#xff0c;就此来分享下该语言模型的构建方法和最终的性能情况。 论文&#xff1a;Dr. LLaMA: Improving Small Language Models in Domain-S…

晶能转债,正元转02上市价格预测

晶能转债 基本信息 转债名称&#xff1a;晶能转债&#xff0c;评级&#xff1a;AA&#xff0c;发行规模&#xff1a;100.0亿元。 正股名称&#xff1a;晶科能源&#xff0c;今日收盘价&#xff1a;12.19元&#xff0c;转股价格&#xff1a;13.79元。 当前转股价值 转债面值 / …

word怎么转换pdf?提供几个思路

在现代互联网时代&#xff0c;我们经常需要将文档转换为PDF格式&#xff0c;以便于共享和打印。虽然Word文件是最常见的文档格式之一&#xff0c;但是许多人不知道如何将其转换为PDF格式。在本文中&#xff0c;我们将介绍如何使用不同的方法将Word文档转换为PDF格式。 使用第三…

Flutter实现PopupMenu(弹出设置菜单)

PopupMenuButton简介 PopupMenuButton是一个用于创建弹出菜单的小部件。它通常与IconButton或其他触发菜单显示的小部件一起使用。当用户点击触发按钮时&#xff0c;PopupMenuButton会在屏幕上方或下方弹出一个菜单&#xff0c;显示一组选项供用户选择。 PopupMenuButton常用…

测试的缺陷密度如何减少

测试的缺陷密度如何减少 随着软件开发的不断进步&#xff0c;测试已经成为一个非常重要的环节。测试的目的是发现空间中的缺陷&#xff0c;以确保软件的质量和稳定性。然而&#xff0c;缺陷密度的高低是影响测试效果的重要因素之一。高缺陷密度意味着测试人员需要更多的时间和精…

在 Ubuntu 22.04 上安装 KubeSphere 实战教程

作者&#xff1a;老 Z&#xff0c;中电信数智科技有限公司山东分公司运维架构师&#xff0c;云原生爱好者&#xff0c;目前专注于云原生运维&#xff0c;云原生领域技术栈涉及 Kubernetes、KubeSphere、DevOps、OpenStack、Ansible 等。 前言 导图 知识点 定级&#xff1a;入…

JavaScript中eval和with语句如何影响作用域链:探索深度知识

前言 系列首发于公众号『前端进阶圈』&#xff0c;若不想错过更多精彩内容&#xff0c;请“星标”一下&#xff0c;敬请关注公众号最新消息。 JavaScript中eval和with语句如何影响作用域链&#xff1a;探索深度知识 前言 在上篇文章中&#xff0c;我们介绍了深度剖析了作用域…

maxwell小白入门

执行同步binlog数据命令路径 maxwell安装目录下执行启动命令增量同步命令: ./bin/maxwell --config ./conf/meituan(文件目录)/具体配置文件名.properties --daemon采集历史数据 ./bin/maxwell-bootstrap --config ./conf/meituan(文件目录)/具体配置文件名.properties --da…

javaIO流之缓冲流

目录 简介1、字节缓冲流1.1构造方法1.2缓冲流的高效1.3为什么字节缓冲流会这么快&#xff1f;1.4byte & 0xFF 2、字符缓冲流2.1构造方法2.2字符缓冲流特有方法 3、练习 简介 Java 的缓冲流是对字节流和字符流的一种封装&#xff0c;通过在内存中开辟缓冲区来提高 I/O 操作…

Kotlin DSL 现已成为全新 Gradle 构建的默认设置

作者 / Kotlin 产品经理 James Ward、开发者关系工程师 Boris Farber 四年来&#xff0c;Android 一直贯彻 "Kotlin 优先" 的原则&#xff0c;许多 Android 开发者已经做出了改变&#xff0c;陆续推出了效率更高、性能更稳定的应用。然而&#xff0c;尽管 Kotlin (bu…

2023—Unity打包Pico4(3)全流程(Pico插件)

一、项目选择了2021.3.0版本的URP&#xff0c;把项目Build成Android 二、打开Project Setting→ 安装最下面的XR Plugin Management 安装完成后的界面&#xff0c;此时还没有Pico选项出现 三、我们需要在该网站下载Pico的SDK包 picoxr/VRTK-Support (github.com) 解压该文件到…

理解搜索引擎优化

我们需要了解搜索引擎如何对网站进行排名&#xff0c;并优化我们的网站以便于搜索引擎的抓取&#xff0c;这被称为SEO&#xff08;搜索引擎优化&#xff09;。 搜索引擎的工作分为三个阶段&#xff1a; •爬虫读取页面内容&#xff08;HTML代码&#xff09;并跟随超链接读取更多…

VR数字乡村:打造乡村振兴新通道,让乡村“走出去”

VR数字乡村是基于VR全景技术来振兴农村经济&#xff0c;并改善农村生活的一种发展模式。在建设数字化乡村的背景下&#xff0c;VR全景技术在改造升级农村农业、养殖管理、设备生产等多个方面都有积极的作用。 乡村振兴的难题就在于如何走出去&#xff0c;如何宣传自身&#xff…