【LED子系统】十、详细实现流程(番外篇)

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

文章目录

    • 1、LED驱动初始化流程
      • 1.1 LED驱动匹配以及设备的创建
        • 1.1.1 gpio_led_probe
        • 1.1.2 gpio_leds_create
        • 1.1.3 create_gpio_led
      • 1.2、LED设备的注册
        • 1.2.1 devm_of_led_classdev_register
        • 1.2.2 of_led_classdev_register
      • 1.3、leds class 创建
        • 1.3.1 leds_init
        • 1.3.2 led_groups
    • 2、LED读写流程
      • 2.1 用户空间的读写操作
      • 2.2 属性定义
        • 2.2.1 DEVICE_ATTR_RW
      • 2.3 读属性
      • 2.4 写属性

上文,我们了解了LED子系统核心的数据结构以及之间的关联,下面我们来看一下其详细的实现流程。

分析LED子系统有两个线路进行分析,一条是从驱动和设备匹配后,注册进入LED子系统,即LED驱动初始化,另一条是LED子系统框架的初始化,即**leds class 创建**。

1、LED驱动初始化流程

1.1 LED驱动匹配以及设备的创建

一般我们分析驱动,都是先从probe入手,也就是驱动的入口函数。

1.1.1 gpio_led_probe

static int gpio_led_probe(struct platform_device *pdev)
{
    struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);	//	获取平台数据
    struct gpio_leds_priv *priv;
    int i, ret = 0;

    if (pdata && pdata->num_leds) {
        priv = devm_kzalloc(&pdev->dev,
                sizeof_gpio_leds_priv(pdata->num_leds),
                    GFP_KERNEL);
        if (!priv)
            return -ENOMEM;

        priv->num_leds = pdata->num_leds;
        for (i = 0; i < priv->num_leds; i++) {
            ret = create_gpio_led(&pdata->leds[i], &priv->leds[i],
                          &pdev->dev, NULL,
                          pdata->gpio_blink_set);
            if (ret < 0)
                return ret;
        }
    } else {
        priv = gpio_leds_create(pdev);		//	创建LED
        if (IS_ERR(priv))
            return PTR_ERR(priv);
    }

    platform_set_drvdata(pdev, priv);

    return 0;
}

函数介绍gpio_led_probeLED驱动的入口函数,也是LED子系统中,硬件设备和驱动程序匹配后,第一个执行的函数。

实现思路

  1. 通过dev_get_platdata检索设备的平台数据,如果平台数据中的LED数量大于零,则使用devm_kzalloc为其分配内存空间,并且使用create_gpio_led进行初始化
  2. 如果平台数据不存在或LED的数量为零,则使用gpio_leds_create创建LED。
  3. 最后,设置驱动程序数据,并返回0,表示操作成功。

1.1.2 gpio_leds_create

static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct fwnode_handle *child;
    struct gpio_leds_priv *priv;
    int count, ret;

    count = device_get_child_node_count(dev);								//	获取子节点数量
    if (!count)
        return ERR_PTR(-ENODEV);

    priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL);		//	根据数量分配内存空间
    if (!priv)
        return ERR_PTR(-ENOMEM);

    device_for_each_child_node(dev, child) {								//	遍历每个LED设备树节点
        struct gpio_led_data *led_dat = &priv->leds[priv->num_leds];
        struct gpio_led led = {};
        const char *state = NULL;
        struct device_node *np = to_of_node(child);

        ret = fwnode_property_read_string(child, "label", &led.name);		//	获取设备树属性
        if (ret && IS_ENABLED(CONFIG_OF) && np)
            led.name = np->name;
        if (!led.name) {
            fwnode_handle_put(child);
            return ERR_PTR(-EINVAL);
        }

        led.gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child,
                                 GPIOD_ASIS,
                                 led.name);
        if (IS_ERR(led.gpiod)) {
            fwnode_handle_put(child);
            return ERR_CAST(led.gpiod);
        }

        fwnode_property_read_string(child, "linux,default-trigger",
                        &led.default_trigger);

        if (!fwnode_property_read_string(child, "default-state",
                         &state)) {
            if (!strcmp(state, "keep"))
                led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
            else if (!strcmp(state, "on"))
                led.default_state = LEDS_GPIO_DEFSTATE_ON;
            else
                led.default_state = LEDS_GPIO_DEFSTATE_OFF;
        }

        if (fwnode_property_present(child, "retain-state-suspended"))
            led.retain_state_suspended = 1;
        if (fwnode_property_present(child, "retain-state-shutdown"))
            led.retain_state_shutdown = 1;
        if (fwnode_property_present(child, "panic-indicator"))
            led.panic_indicator = 1;

        ret = create_gpio_led(&led, led_dat, dev, np, NULL);				//	创建LED设备
        if (ret < 0) {
            fwnode_handle_put(child);
            return ERR_PTR(ret);
        }
        led_dat->cdev.dev->of_node = np;
        priv->num_leds++;
    }

    return priv;
}

函数介绍gpio_leds_create主要用于创建LED设备。

实现思路

  1. 通过device_get_child_node_count获取设备树中LED子节点的数量,根据获取到的子节点数量,分配LED设备对应的内存空间
  2. 通过device_for_each_child_node遍历每个子节点,并为每个子节点创建对应的LED设备
  3. 对于每个子节点,使用fwnode_property_read_string接口,读取设备树中相关的属性信息,如:labellinux,default-trigger等,将这些信息赋值给gpio_led结构体中
  4. 最后将遍历的每个LED,调用create_gpio_led进行设备的创建

1.1.3 create_gpio_led

static int create_gpio_led(const struct gpio_led *template,
    struct gpio_led_data *led_dat, struct device *parent,
    struct device_node *np, gpio_blink_set_t blink_set)
{
    int ret, state;

    led_dat->gpiod = template->gpiod;
    if (!led_dat->gpiod) {
        /*
         * This is the legacy code path for platform code that
         * still uses GPIO numbers. Ultimately we would like to get
         * rid of this block completely.
         */
        unsigned long flags = GPIOF_OUT_INIT_LOW;

        /* skip leds that aren't available */
        if (!gpio_is_valid(template->gpio)) {								//	判断是否gpio合法
            dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
                    template->gpio, template->name);
            return 0;
        }

        if (template->active_low)
            flags |= GPIOF_ACTIVE_LOW;

        ret = devm_gpio_request_one(parent, template->gpio, flags,
                        template->name);
        if (ret < 0)
            return ret;

        led_dat->gpiod = gpio_to_desc(template->gpio);						//	获取gpio组
        if (!led_dat->gpiod)
            return -EINVAL;
    }

    led_dat->cdev.name = template->name;									//	赋值一些属性信息
    led_dat->cdev.default_trigger = template->default_trigger;
    led_dat->can_sleep = gpiod_cansleep(led_dat->gpiod);
    if (!led_dat->can_sleep)
        led_dat->cdev.brightness_set = gpio_led_set;
    else
        led_dat->cdev.brightness_set_blocking = gpio_led_set_blocking;
    led_dat->blinking = 0;
    if (blink_set) {
        led_dat->platform_gpio_blink_set = blink_set;
        led_dat->cdev.blink_set = gpio_blink_set;
    }
    if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) {
        state = gpiod_get_value_cansleep(led_dat->gpiod);
        if (state < 0)
            return state;
    } else {
        state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
    }
    led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
    if (!template->retain_state_suspended)
        led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
    if (template->panic_indicator)
        led_dat->cdev.flags |= LED_PANIC_INDICATOR;
    if (template->retain_state_shutdown)
        led_dat->cdev.flags |= LED_RETAIN_AT_SHUTDOWN;

    ret = gpiod_direction_output(led_dat->gpiod, state);
    if (ret < 0)
        return ret;

    return devm_of_led_classdev_register(parent, np, &led_dat->cdev);		//	将LED设备注册到子系统中
}

函数介绍create_gpio_led创建LED设备的核心函数

实现思路

  1. 先通过gpio_is_valid接口,判断GPIO是否合法
  2. 将上层从设备树解析出来的信息,填充到gpio_led_data字段中,并且初始化部分字段,如:led_classdevgpio_desc
  3. 最后调用devm_of_led_classdev_register接口,将LED设备注册到LED框架之中。

1.2、LED设备的注册

1.2.1 devm_of_led_classdev_register

/**
 * devm_of_led_classdev_register - resource managed led_classdev_register()
 *
 * @parent: parent of LED device
 * @led_cdev: the led_classdev structure for this device.
 */
int devm_of_led_classdev_register(struct device *parent,
                  struct device_node *np,
                  struct led_classdev *led_cdev)
{
    struct led_classdev **dr;
    int rc;

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

    rc = of_led_classdev_register(parent, np, led_cdev);		//	注册到子系统
    if (rc) {
        devres_free(dr);
        return rc;
    }

    *dr = led_cdev;
    devres_add(parent, dr);

    return 0;
}
EXPORT_SYMBOL_GPL(devm_of_led_classdev_register);

函数介绍devm_of_led_classdev_registerof_led_classdev_register函数的资源管理版本。即:在of_led_classdev_register之上,进行了资源的管理。

实现思路

  1. 先通过struct led_classdev **dr创建一个新对象,并将其与给定的设备节点关联
  2. 该函数分配了一个devres结构来管理led_classdev对象的生命周期。
  3. 如果注册成功,则led_classdev对象将存储在devres结构中,并与父设备关联。

1.2.2 of_led_classdev_register

/**
 * of_led_classdev_register - register a new object of led_classdev class.
 *
 * @parent: parent of LED device
 * @led_cdev: the led_classdev structure for this device.
 * @np: DT node describing this LED
 */
int of_led_classdev_register(struct device *parent, struct device_node *np,
                struct led_classdev *led_cdev)
{
    char name[LED_MAX_NAME_SIZE];
    int ret;

    ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));		//	将leds设备注册进sysfs文件系统中
    if (ret < 0)
        return ret;

    mutex_init(&led_cdev->led_access);
    mutex_lock(&led_cdev->led_access);
    led_cdev->dev = device_create_with_groups(leds_class, parent, 0,
                led_cdev, led_cdev->groups, "%s", name);					//	添加属性文件
    if (IS_ERR(led_cdev->dev)) {
        mutex_unlock(&led_cdev->led_access);
        return PTR_ERR(led_cdev->dev);
    }
    led_cdev->dev->of_node = np;

    if (ret)
        dev_warn(parent, "Led %s renamed to %s due to name collision",
                led_cdev->name, dev_name(led_cdev->dev));

    if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
        ret = led_add_brightness_hw_changed(led_cdev);
        if (ret) {
            device_unregister(led_cdev->dev);
            mutex_unlock(&led_cdev->led_access);
            return ret;
        }
    }

    led_cdev->work_flags = 0;
#ifdef CONFIG_LEDS_TRIGGERS
    init_rwsem(&led_cdev->trigger_lock);
#endif
#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
    led_cdev->brightness_hw_changed = -1;
#endif
    /* add to the list of leds */
    down_write(&leds_list_lock);
    list_add_tail(&led_cdev->node, &leds_list);
    up_write(&leds_list_lock);

    if (!led_cdev->max_brightness)
        led_cdev->max_brightness = LED_FULL;

    led_update_brightness(led_cdev);

    led_init_core(led_cdev);					//	核心层初始化

#ifdef CONFIG_LEDS_TRIGGERS
    led_trigger_set_default(led_cdev);
#endif

    mutex_unlock(&led_cdev->led_access);

    dev_dbg(parent, "Registered led device: %s\n",
            led_cdev->name);

    return 0;
}

函数介绍of_led_classdev_register注册一个新的led_classdev类。

实现思路

  1. 通过led_classdev_next_name来对LED名字添加序号,生成唯一名称
  2. 使用device_create_with_groups接口,并使用定义好的属性组,创建一个新的设备
  3. 使用led_init_core接口,初始化了 LED 核心并为设备设置了默认触发器。

1.3、leds class 创建

在上面,我们直接注册了led_classdev,但是在实际情况中,每一个设备都会去创建对应的classleds也不例外。

1.3.1 leds_init

static int __init leds_init(void)
{
    leds_class = class_create(THIS_MODULE, "leds");			//	创建leds文件节点
    if (IS_ERR(leds_class))
        return PTR_ERR(leds_class);
    leds_class->pm = &leds_class_dev_pm_ops;
    leds_class->dev_groups = led_groups;					
    return 0;
}

static void __exit leds_exit(void)
{
    class_destroy(leds_class);
}

subsys_initcall(leds_init);
module_exit(leds_exit);

MODULE_AUTHOR("John Lenz, Richard Purdie");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LED Class Interface");

函数介绍leds_init该函数在内核在加载的时候执行,主要用于初始化leds_class类,对应与sysfs文件系统下的leds目录。

实现思路

  1. 调用class_create接口,创建leds_class
  2. leds_class_dev_pm_ops配置电源管理接口
  3. led_groups配置文件属性

1.3.2 led_groups

#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,
};
static const struct attribute_group led_trigger_group = {
    .attrs = led_trigger_attrs,
};
#endif

//	属性文件
static struct attribute *led_class_attrs[] = {
    &dev_attr_brightness.attr,
    &dev_attr_max_brightness.attr,
    NULL,
};

static const struct attribute_group led_group = {
    .attrs = led_class_attrs,
};

static const struct attribute_group *led_groups[] = {
    &led_group,
#ifdef CONFIG_LEDS_TRIGGERS
    &led_trigger_group,
#endif
    NULL,
};

函数介绍led_groups来配置相关文件属性,这里有三个属性:max_brightnessbrightnesstrigger

这里仅仅是配置3个属性文件,但是还没有创建,真正的创建在于device注册之后。

综上,LED初始化流程如下

leds_init(drivers\leds\led-class.c)     //  系统开始就初始化
    |--> class_create                   //  创建leds_class类

gpio_led_probe(drivers/leds/leds-gpio.c)
    |--> gpio_leds_create
        |--> create_gpio_led            //  创建LED设备
            |--> devm_of_led_classdev_register      
                |--> of_led_classdev_register           //  注册到框架中 
                    |--> device_create_with_groups      //  添加属性
                    |--> led_init_core                  //  初始化LED核心层

最后创建的文件结构如下

image-20230402195854042

一些属性文件是我们配置的,如max_brightnessbrightnesstrigger,一些链接文件是自动创建的。

2、LED读写流程

2.1 用户空间的读写操作

在上文,我们知道,在Linux用户空间下,生成的属性文件,我们一般在用户空间通过两种方式访问:

  • 以C库或者系统调用来读写文件:相关接口有fopen或者openfclose或者closefwrite或者writefread或者read
  • 以命令行直接读写文件:相关命令有echocat

这两种方式在内核中的接口都是一致的,下面我们来分析一下驱动代码。

在文件led-class.c中,是如何配置相关属性文件的读写的呢?

在上面介绍,我们在初始化leds_class的时候,配置过相关属性文件,如下

static struct attribute *led_class_attrs[] = {
    &dev_attr_brightness.attr,
    &dev_attr_max_brightness.attr,
    NULL,
};

那么这些属性在哪里定义的呢?

2.2 属性定义

static ssize_t brightness_show(struct device *dev,
        struct device_attribute *attr, char *buf)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);

    /* no lock needed for this */
    led_update_brightness(led_cdev);

    return sprintf(buf, "%u\n", led_cdev->brightness);
}

static ssize_t brightness_store(struct device *dev,
        struct device_attribute *attr, const char *buf, size_t size)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);
    unsigned long state;
    ssize_t ret;

    mutex_lock(&led_cdev->led_access);

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

    ret = kstrtoul(buf, 10, &state);
    if (ret)
        goto unlock;

    if (state == LED_OFF)
        led_trigger_remove(led_cdev);
    led_set_brightness(led_cdev, state);

    ret = size;
unlock:
    mutex_unlock(&led_cdev->led_access);
    return ret;
}
static DEVICE_ATTR_RW(brightness);

上面定义了两个函数(brightness_showbrightness_store)和一个属性名称(brightness),那么在哪里将这两个函数与这个属性联系起来的呢?

关键在于DEVICE_ATTR_RW宏定义!

2.2.1 DEVICE_ATTR_RW

//	static DEVICE_ATTR_RW(brightness);

#define DEVICE_ATTR_RW(_name) \
    struct device_attribute dev_attr_##_name = __ATTR_RW(_name)

//	static struct device_attribute dev_attr_brightness = __ATTR_RW(brightness)

#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)

//	static struct device_attribute dev_attr_brightness = __ATTR(brightness, 0644, brightness_show, brightness_store)

#define __ATTR(_name, _mode, _show, _store) {				\
    .attr = {.name = __stringify(_name),				\
         .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },		\
    .show	= _show,						\
    .store	= _store,						\
}

//	static struct device_attribute dev_attr_brightness = {
//	.attr = {
//		.name = __stringify(brightness),
//		.mode = VERIFY_OCTAL_PERMISSIONS(0644),
//	}
//	.show = brightness_show,
//	.store = brightness_store,
//}

上面屏蔽的内容就是static DEVICE_ATTR_RW(brightness)展开的原貌,这样就与上面的两个函数(brightness_showbrightness_store)关联了起来!

2.3 读属性

static ssize_t brightness_show(struct device *dev,
        struct device_attribute *attr, char *buf)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);

    /* no lock needed for this */
    led_update_brightness(led_cdev);

    return sprintf(buf, "%u\n", led_cdev->brightness);
}

函数介绍:将亮度值读出到用户空间

实现思路

  1. 调用dev_get_drvdata接口,获取led_classdev结构体的地址
  2. 调用led_update_brightness接口更新亮度值,如果led_cdev->brightness_get自定义的接口实现的话,就调用该接口,如果未实现,则直接返回led_cdev->brightness

2.4 写属性

static ssize_t brightness_store(struct device *dev,
        struct device_attribute *attr, const char *buf, size_t size)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);
    unsigned long state;
    ssize_t ret;

    mutex_lock(&led_cdev->led_access);

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

    ret = kstrtoul(buf, 10, &state);
    if (ret)
        goto unlock;

    if (state == LED_OFF)
        led_trigger_remove(led_cdev);
    led_set_brightness(led_cdev, state);

    ret = size;
unlock:
    mutex_unlock(&led_cdev->led_access);
    return ret;
}

函数介绍:设置LED亮度值

实现思路

  1. 调用dev_get_drvdata接口,获取led_classdev结构体的地址
  2. 调用接口led_sysfs_is_disabled判断是否有写权限
  3. 调用led_set_brightness接口,设置亮度

详细的写流程如下

// 写亮度值
brightness_store(drivers/leds/led-class.c)  //  写亮度值
    |--> led_sysfs_is_disabled              //  判断是否有权限
    |--> led_set_brightness 
        |--> led_set_brightness_nosleep     //  设置亮度   
            |--> led_set_brightness_nopm 
                |--> schedule_work          //  加入到队列中
                    |--> set_brightness_delayed //  设置亮度值 
                        |--> __led_set_brightness  
                            |--> led_cdev->brightness_set(/home/donge/Develop/I_MX/source/kernel/drivers/leds/led-core.c)
                                |-->  gpio_led_set
                                    |--> gpiod_set_value

点赞+关注,永远不迷路

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

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

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

相关文章

Hive ---- 文件格式和压缩

Hive ---- 文件格式和压缩 1. Hadoop压缩概述2. Hive文件格式1. Text File2. ORC3. Parquet3. 压缩1. Hive表数据进行压缩2. 计算过程中使用压缩 1. Hadoop压缩概述 为了支持多种压缩/解压缩算法&#xff0c;Hadoop引入了编码/解码器&#xff0c;如下表所示&#xff1a; Hadoo…

CodeForces..李华和迷宫.[简单].[找规律]

题目描述&#xff1a; 题目解读&#xff1a; 存在矩阵迷宫nm&#xff0c;&#xff08;r&#xff0c;c&#xff09;表示从顶部开始的第r行和左起第c列。 如果两单元格共享一个边&#xff0c;则是相邻的。路径是相邻空单元格的序列。 每个单元格初始状态都为空。对于从&#x…

代码随想录额外题目——图论部分

记录一下代码随想录中额外题目的图论部分 841.钥匙和房间 有 n 个房间&#xff0c;房间按从 0 到 n - 1 编号。最初&#xff0c;除 0 号房间外的其余所有房间都被锁住。你的目标是进入所有的房间。然而&#xff0c;你不能在没有获得钥匙的时候进入锁住的房间。 当你进入一个…

山海大模型亮相,云知声交出AGI第一份答卷

有人说&#xff0c;AI大模型是少数巨头才能玩得转的游戏。 截至目前&#xff0c;认同此观点的人不在少数。自从ChatGPT去年迅速火遍全球之后&#xff0c;忽如一夜春风来&#xff0c;AI大模型遍地开。Google、Amazon、阿里、百度等巨头们纷纷加入AI大模型的“军备竞赛”&#x…

【数据分享】1929-2022年全球站点的逐日平均气温数据(Shp\Excel\无需转发)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;其中又以气温指标最为常用&#xff01;说到气温数据&#xff0c;最详细的气温数据是具体到气象监测站点的气温数据&#xff01;本次我们为大家带来的就是具体到气象监…

大数据分析案例-基于决策树算法构建世界杯比赛预测模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

EMC VNX Unified Storage 关机顺序方法

EMC Unfied的VNX存储系统要比单纯的Block系统复杂很多&#xff0c;相当于是两套存储系统&#xff0c;不管在物理硬件上还是逻辑的软件OS上&#xff0c;都复杂很多很多。 客户经常遇到由于机房停电或者机房搬迁等情况&#xff0c;需要对存储系统做关机下电甚至物理搬迁的动作&a…

FPGA基于AXI 1G/2.5G Ethernet Subsystem实现UDP通信DMA传输 提供工程源码和技术支持

目录 1、前言2、我这里已有的UDP方案3、详细设计方案传统UDP网络通信方案本方案详细设计说明DMA和BRAMAXIS-FIFOUDP模块设计UDP模块FIFOAXI 1G/2.5G Ethernet Subsystem&#xff1a;输出 4、vivado工程详解5、上板调试验证并演示注意事项 6、福利&#xff1a;工程代码的获取 1、…

【simple-cache】一款只用一个注解就实现缓存的框架-我们终于迎来了SpringBoot版本

上次我们讲了【simple-cache】的使用&#xff1a; 【simple-cache】我开发了一款只要一个注解就可以轻松实现缓存的框架 这次主要更新的内容为&#xff1a; 添加springboot项目框架中去除了redisconfig类&#xff0c;避免了redis的单机和集群问题用户可以自定义使用自己项目中…

Python之字符串(str)基础知识点

strip() 删除指定字符 当token为空时,默认删除空白符(含’\n’,‘\r’,‘\t’,’ )&#xff0c;当非空时&#xff0c;根据指定的token进行删除。 字符的删除又可分为以下几种情况&#xff1a; string.strip(token)&#xff1a;删除string字符串中开头(left)、结尾处(right)的…

【操作系统】02.进程管理

多道程序系统 多道就是将多个程序同时装入内存&#xff0c;使之并发运行。操作系统也是基于多道产生的&#xff0c;提高了资源利用率和系统吞吐量。 进程 定义 进程是程序的一次执行 进程是进程实体的运行过程&#xff0c;是系统进行资源分配和调度的一个独立单位 在引入线…

会流程图却不会UML活动图?活动图深度剖析,就怕你学不会!

1. UML活动图是啥&#xff1f; 也许很多人都不怎么了解活动图&#xff0c;但是却对流程图很熟悉&#xff0c;你暂且可以简单的把活动图理解为UML里的流程图&#xff0c;用来描述系统的行为特征。不过UML活动图对比于流程图来说也存在不少差异&#xff0c;本文将在第三章节讲解活…

解决 MobaXterm X11 server 打开 wsl2 linux 子系统 rviz 可视化窗口卡顿问题

1、问题 环境&#xff1a; MobaXtermwsl2 Ubuntu-18.04ROS1Intel 核显 一直使用 MobaXterm 这个远程软件 ssh 链接 linux 服务器&#xff0c;因为它集成了 X11 server&#xff0c;即可以显示一些 linux 下有图形化界面的程序&#xff0c;如 ROS 的 rviz 等。 但是 MobaXterm…

宝塔面板一键部署Z-Blog博客 - 内网穿透实现公网访问

文章目录 1.前言2.网站搭建2.1. 网页下载和安装2.2.网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测试5.结语 转发自cpolar极点云的…

一步一步的实现使用 Tensorflow Hub 进行图像分割

在本文中&#xff0c;我们将学习如何使用 TensorFlow Hub中提供的预训练模型执行语义图像分割。TensorFlow Hub 是一个库和平台&#xff0c;旨在共享、发现和重用预训练的机器学习模型。TensorFlow Hub 的主要目标是简化重用现有模型的过程&#xff0c;从而促进协作、减少冗余工…

[比赛简介]ICR - Identifying Age-Related Conditions

比赛链接&#xff1a;https://www.kaggle.com/competitions/icr-identify-age-related-conditions 比赛简介 本次比赛的目标是预测一个人是否患有三种疾病中的任何一种。您被要求预测该人是否患有三种疾病中的任何一种或多种&#xff08;1 类&#xff09;&#xff0c;或者三种…

第14届蓝桥杯Scratch选拔赛(STEMA) 真题集锦

一、选择题 第 1题单选题 运行以下程序 (小象仅有两个造型),小象的造型是哪个? () 答案 A 解析 本题正确答案是A,考察的知识点是角色造型,在Scratch中,切换造型有两个指令,分别是“换成xx造型“和“下一个造型”,其中前者将角色切换为指定造型,而后者则从当前造型切换…

面向小白的最全Python数据分析指南,超全的!

因工作需求经常会面试一些数据分析师&#xff0c;一些 coding 能力很强的小伙伴&#xff0c;当被问及数据分析方法论时一脸懵逼的&#xff0c;或者理所当然的认为就是写代码啊&#xff0c;在文章开头先来解释一下数据分析。 数据分析是通过明确分析目的&#xff0c;梳理并确定…

虎牙在全球 DNS 秒级生效上的实践

博主介绍&#xff1a;✌全网粉丝4W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战、定制、远程&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面…

六级备考18天|CET-6|写作第一讲|现象解释|10:00~10:45

目录 1. 议论文类型 现象解释 what,why,importance 观点选择 whether,choice,option 问题解决 how to,measures 图片图表 image,cartoon,diagram,chart 2. 基本题型 议论文、图表、谚语 3. 模版构成/段落结构划分 三个段落&#xff1a;10-11句 背景主题句过渡句 …