七、GPIO中断控制器(2)—— pcf8575

前言

上一小节,已经完成了基础的中断控制器驱动。现在需要完善并解决边缘检测的问题。这里可以参考 gpio-mcp23s08.c 驱动,仿造着它来实现,也可以直接参考 gpio-pcf857x.c 驱动,这个官方的驱动。后者使用的是 gpiolib_irqchip 的新版本API,实现起来较为简单,需要在config中配置 CONFIG_GPIOLIB_IRQCHIP 。这里介绍旧版本的API使用,无太大差异。

irq_chip

前面介绍的,在父中断处理函数中,我们直接一股脑的对所有电平变化的引脚触发中断处理函数,这将导致我们在使用GPIO扩展芯片的引脚时,设置的上升沿检测或者下降沿检测不起作用。因此,需要解决这个问题,就需要做一些额外的处理。当然,内核中的框架已经支持这些功能,只需要我们做一些简单接口实现就行了。这里就需要使用到 irq_chip 结构来完成这些事情。

static struct irq_chip pcf8575_irq_chip = {.name = "pcf8575_irqchip",.irq_mask = pcf8575_irq_mask,.irq_unmask = pcf8575_irq_unmask,.irq_set_type = pcf8575_irq_set_type,.irq_bus_lock = pcf8575_irq_bus_lock,.irq_bus_sync_unlock = pcf8575_irq_bus_unlock,.irq_request_resources = pcf8575_irq_reqres,.irq_release_resources = pcf8575_irq_relres,
};

参照 mcp23s08 的驱动来实现。如果没有定义 irq_enable,即irq_enable为NULL,那么 irq_enable 将映射到 irq_unmask 。同理,irq_disable 也是一样,会映射到 irq_mask 。因此这里实现其一即可。irq_set_type 就是我们实现上升沿和下降沿检测要用到的最关键的方法了。它会把用户配置传进来,这里的用户配置,指的配置使用GPIO扩展芯片的用户。因此,我们可以在这个方法中获取到对于某个引脚的中断检测配置。然后保存下来,在父中断处理函数中使用。

static irqreturn_t pcf8575_irq(int irq, void *data)
{struct pcf8575_dev *pcf8575 = data;unsigned int child_irq, i;u16 value;pcf8575_i2c_read(pcf8575->client, &value);for (i = 0; i < pcf8575->chip.ngpio; i++) {if (((pcf8575->in_mask >> i) & 0x01) == 0x01) {if (((value >> i) & 0x01) != ((pcf8575->r_val >> i) & 0x01)&& !(pcf8575->irq_mask & (1 << i))&& ((((pcf8575->irq_rising & (1 << i))) && (value & (1 << i)))|| ((pcf8575->irq_falling & (1 << i)) && !(value & (1 << i))))){pr_info("pcf8575 irq pin: %d", i);child_irq = irq_find_mapping(pcf8575->irq_domain, i);handle_nested_irq(child_irq);}}}pcf8575->r_val = value & pcf8575->in_mask;return IRQ_HANDLED;
}
static void pcf8575_irq_mask(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);pcf8575->irq_mask |= (1 << data->hwirq);
}static void pcf8575_irq_unmask(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);pcf8575->irq_mask &= ~(1 << data->hwirq);
}static void pcf8575_irq_bus_lock(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);mutex_lock(&pcf8575->irq_lock);
}static void pcf8575_irq_bus_unlock(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);mutex_unlock(&pcf8575->irq_lock);
}static int pcf8575_irq_reqres(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);if (gpiochip_lock_as_irq(&pcf8575->chip, data->hwirq)) {dev_err(pcf8575->chip.parent,"unable to lock HW IRQ %lu for IRQ usage\n",data->hwirq);return -EINVAL;}return 0;
}static void pcf8575_irq_relres(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);gpiochip_unlock_as_irq(&pcf8575->chip, data->hwirq);
}static int pcf8575_irq_set_type(struct irq_data *data, unsigned int type)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);if ((type & IRQ_TYPE_EDGE_BOTH) ==  IRQ_TYPE_EDGE_BOTH) {pcf8575->irq_rising |= (1 << data->hwirq);pcf8575->irq_falling |= (1 << data->hwirq);} else if (type & IRQ_TYPE_EDGE_RISING) {pcf8575->irq_rising |= (1 << data->hwirq);pcf8575->irq_falling &= ~(1 << data->hwirq);} else if (type & IRQ_TYPE_EDGE_FALLING) {pcf8575->irq_falling |= (1 << data->hwirq);pcf8575->irq_rising &= ~(1 << data->hwirq);} else {return -EINVAL;}return 0;
}

irq_domain_ops

在 irq_domain_ops.map 中,我们使用我们自己定义的这个 irq_chip,作为参数传入,而不再使用 dummy_irq_chip

static int pcf8575_irq_domain_map(struct irq_domain *domain, unsigned int virq,irq_hw_number_t hw)
{struct pcf8575_dev *pcf8575 = domain->host_data;irq_set_chip_data(virq, pcf8575);irq_set_chip_and_handler(virq, &pcf8575_irq_chip, handle_edge_irq);return 0;
}

这里需要特别注意的是,上一节的驱动是集中创建映射,在调用 irq_create_mapping 后才生成irq号,然后我们才使用 irq_set_chip_data 为每个 irq 设置私有数据。但是这里会出现一个bug。由于在 irq_create_mapping 中会调用到:

static void pcf8575_irq_bus_lock(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);mutex_lock(&pcf8575->irq_lock);
}

由于还没有调用 irq_set_chip_data 设置数据,就要获取数据,就会导致这里调用 irq_data_get_irq_chip_data 获取 pcf8575 对象不会成功,会得到一个空指针,导致panic。因此我们不再 probe 中集中的为每个引脚创建映射,而是在 chip.to_irq 回调汇总,分别为每个引脚创建映射。这里就可以在 pcf8575_irq_domain_map 中为 irq 号的中断号设置 chip_data, 然后再使用 pcf8575_irq_bus_lock 时,就不会获取到空指针了。

因此在 probe 中,取消集中创建映射:

    pcf8575->chip.to_irq = pcf8575_gpio_to_irq;pcf8575->irq_domain->host_data = pcf8575;// for (i = 0; i < pcf8575->chip.ngpio; i++) {//     irq = irq_create_mapping(pcf8575->irq_domain, i);// 	irq_set_chip_data(irq, pcf8575);// }

chip.to_irq 中,分别为每个pin脚创建映射:

static int pcf8575_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{struct pcf8575_dev *pcf8575 = container_of(chip, struct pcf8575_dev, chip);return irq_create_mapping(pcf8575->irq_domain, offset);// return irq_find_mapping(pcf8575->irq_domain, offset);
}

验证

完成上述修改后进行测试验证。

[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]# echo 1 > value
[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]# echo 0 > value
[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]# [ 2156.454264] pcf8575 irq pin: 7
[ 2156.461633] intb irq handler[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]# echo 1 > value
[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]# echo 0 > value
[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]# [ 2163.200807] pcf8575 irq pin: 7
[ 2163.208004] intb irq handler[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]# echo 0 >value
[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]# echo 0 > value
[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]# echo 1 > value
[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]# echo 1 > value
[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]# echo 0 > value
[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]# [ 2176.609468] pcf8575 irq pin: 7
[ 2176.616762] intb irq handler[root@rk3399:/sys/devices/platform/pinctrl/gpio/gpio41]#

可以看到,只有当我们电平变化是下降沿时,才会用中断处理函数的触发。

可以修改用户代码,申请中断线程化时, 设置上升沿有效进行测试。另外,设置高电平触发和低电平触发,会发现,无法申请成功。这是由于,我们的 irq_set_type 回调中,只支持了边沿检测,电平检测会直接返回 -EINVAL 。可以在其中修改,让其支持高低电平触发。以上就是一个较为完整的中断控制器驱动了。

代码

基于上节驱动代码进行修改。

#include "linux/device.h"
#include "linux/gpio/driver.h"
#include "linux/interrupt.h"
#include <linux/irq.h>
#include <linux/mutex.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/of_irq.h>
#include <linux/gpio/driver.h>#define GPIO_NUM 16struct pcf8575_dev {struct i2c_client *client;struct gpio_chip chip;u16 value;u16 in_mask;u16 r_val;struct irq_domain *irq_domain;struct mutex irq_lock;u16 irq_mask;u16 irq_rising;u16 irq_falling;
};static int pcf8575_i2c_read(struct i2c_client *client, u16 *value)
{u8 data[2] = {0};struct i2c_msg msg[] = {{.flags = I2C_M_RD | I2C_M_STOP,.addr = client->addr,.buf = data,.len = ARRAY_SIZE(data),},};if (i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)) != ARRAY_SIZE(msg)) {dev_err(&client->dev, "i2c read error, chip=0x%02x\n", client->addr);return -EREMOTEIO;}*value = data[0] | (data[1] << 8);return 0;
}static int pcf8575_i2c_write(struct i2c_client *client, u16 value)
{u8 data[2] = {(value & 0xFF), ((value >> 8) & 0xFF)};struct i2c_msg msg[] = {{.flags = I2C_M_STOP,.addr = client->addr,.buf = data,.len = ARRAY_SIZE(data),},};if (i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)) != ARRAY_SIZE(msg)) {dev_err(&client->dev, "i2c write error, chip=0x%02x\n", client->addr);return -EREMOTEIO;}return 0;
}static int pcf8575_get_value(struct gpio_chip *chip, unsigned offset)
{u16 value = 0;struct pcf8575_dev *pcf8575 = container_of(chip, struct pcf8575_dev, chip);if (offset >= GPIO_NUM) {dev_err(&pcf8575->client->dev, "offset over %d\n", GPIO_NUM);return 0;}pcf8575_i2c_read(pcf8575->client, &value);pcf8575->r_val = value & pcf8575->in_mask;return (value & (1 << offset));
}static void pcf8575_set_value(struct gpio_chip *chip, unsigned offset, int value)
{struct pcf8575_dev *pcf8575 = container_of(chip, struct pcf8575_dev, chip);if (offset >= GPIO_NUM) {dev_err(&pcf8575->client->dev, "offset over %d\n", GPIO_NUM);return ;}if (value) {pcf8575->value |= (1 << offset);} else {pcf8575->value &= (~(1 << offset));}pcf8575_i2c_write(pcf8575->client, pcf8575->value);
}static int pcf8575_direction_input(struct gpio_chip *chip, unsigned offset)
{struct pcf8575_dev *pcf8575 = container_of(chip, struct pcf8575_dev, chip);pcf8575->value |= (1 << offset);pcf8575->in_mask |= (1 << offset);pcf8575_i2c_write(pcf8575->client, pcf8575->value);return 0;
}static int pcf8575_direction_output(struct gpio_chip *chip, unsigned offset,int value)
{struct pcf8575_dev *pcf8575 = container_of(chip, struct pcf8575_dev, chip);pcf8575->in_mask &= (~(1 << offset));pcf8575_set_value(chip, offset, value);return 0;
}static void pcf8575_irq_mask(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);pcf8575->irq_mask |= (1 << data->hwirq);
}static void pcf8575_irq_unmask(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);pcf8575->irq_mask &= ~(1 << data->hwirq);
}static int pcf8575_irq_set_type(struct irq_data *data, unsigned int type)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);if ((type & IRQ_TYPE_EDGE_BOTH) ==  IRQ_TYPE_EDGE_BOTH) {pcf8575->irq_rising |= (1 << data->hwirq);pcf8575->irq_falling |= (1 << data->hwirq);} else if (type & IRQ_TYPE_EDGE_RISING) {pcf8575->irq_rising |= (1 << data->hwirq);pcf8575->irq_falling &= ~(1 << data->hwirq);} else if (type & IRQ_TYPE_EDGE_FALLING) {pcf8575->irq_falling |= (1 << data->hwirq);pcf8575->irq_rising &= ~(1 << data->hwirq);} else {return -EINVAL;}return 0;
}static void pcf8575_irq_bus_lock(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);mutex_lock(&pcf8575->irq_lock);
}static void pcf8575_irq_bus_unlock(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);mutex_unlock(&pcf8575->irq_lock);
}static int pcf8575_irq_reqres(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);if (gpiochip_lock_as_irq(&pcf8575->chip, data->hwirq)) {dev_err(pcf8575->chip.parent,"unable to lock HW IRQ %lu for IRQ usage\n",data->hwirq);return -EINVAL;}return 0;
}static void pcf8575_irq_relres(struct irq_data *data)
{struct pcf8575_dev *pcf8575 = irq_data_get_irq_chip_data(data);gpiochip_unlock_as_irq(&pcf8575->chip, data->hwirq);
}static struct irq_chip pcf8575_irq_chip = {.name = "pcf8575_irqchip",.irq_mask = pcf8575_irq_mask,.irq_unmask = pcf8575_irq_unmask,.irq_set_type = pcf8575_irq_set_type,.irq_bus_lock = pcf8575_irq_bus_lock,.irq_bus_sync_unlock = pcf8575_irq_bus_unlock,.irq_request_resources = pcf8575_irq_reqres,.irq_release_resources = pcf8575_irq_relres,
};static int pcf8575_irq_domain_map(struct irq_domain *domain, unsigned int virq,irq_hw_number_t hw)
{struct pcf8575_dev *pcf8575 = domain->host_data;irq_set_chip_data(virq, pcf8575);irq_set_chip_and_handler(virq, &pcf8575_irq_chip, handle_edge_irq);return 0;
}static struct irq_domain_ops pcf8575_irq_domain_ops = {.map = pcf8575_irq_domain_map,.xlate = irq_domain_xlate_twocell,
};static irqreturn_t pcf8575_irq(int irq, void *data)
{struct pcf8575_dev *pcf8575 = data;unsigned int child_irq, i;u16 value;// bool cond1, cond2, cond3;pcf8575_i2c_read(pcf8575->client, &value);// pr_info("pcf8575 irq handle, value:0x%02x, r_val:0x%02x\n", value, pcf8575->r_val);for (i = 0; i < pcf8575->chip.ngpio; i++) {if (((pcf8575->in_mask >> i) & 0x01) == 0x01) {// pr_info("pcf8575->irq_mask:0x%04x", pcf8575->irq_mask);// pr_info("pcf8575->irq_rising:0x%04x", pcf8575->irq_rising);// pr_info("pcf8575->irq_falling:0x%04x", pcf8575->irq_falling);// cond1 = ((value >> i) & 0x01) != ((pcf8575->r_val >> i) & 0x01);// cond2 = !(pcf8575->irq_mask & (1 << i));// cond3 = ((((pcf8575->irq_rising & (1 << i))) && (value & (1 << i)))//         || ((pcf8575->irq_falling & (1 << i)) && !(value & (1 << i))));// pr_info("cond1: %d, cond2: %d, cond3: %d", cond1, cond2, cond3);if (((value >> i) & 0x01) != ((pcf8575->r_val >> i) & 0x01)&& !(pcf8575->irq_mask & (1 << i))&& ((((pcf8575->irq_rising & (1 << i))) && (value & (1 << i)))|| ((pcf8575->irq_falling & (1 << i)) && !(value & (1 << i))))){pr_info("pcf8575 irq pin: %d", i);child_irq = irq_find_mapping(pcf8575->irq_domain, i);handle_nested_irq(child_irq);}}}/* If only we read from ports, we must cache last input value after using pcf8575_i2c_read */pcf8575->r_val = value & pcf8575->in_mask;return IRQ_HANDLED;
}static int pcf8575_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{struct pcf8575_dev *pcf8575 = container_of(chip, struct pcf8575_dev, chip);return irq_create_mapping(pcf8575->irq_domain, offset);// return irq_find_mapping(pcf8575->irq_domain, offset);
}static int pcf8575_irq_setup(struct pcf8575_dev *pcf8575)
{struct device *dev = &pcf8575->client->dev;int ret = 0;// int i = 0;// u32 irq;pcf8575->in_mask = 0;pcf8575->r_val = 0;pcf8575->irq_mask = 0xffff;mutex_init(&pcf8575->irq_lock);// pcf8575->client->irq = irq_of_parse_and_map(pcf8575->client->dev.of_node, 0);pcf8575->irq_domain = irq_domain_add_linear(dev->of_node,pcf8575->chip.ngpio, &pcf8575_irq_domain_ops, NULL);if (IS_ERR(pcf8575->irq_domain)) {mutex_destroy(&pcf8575->irq_lock);return PTR_ERR(pcf8575->irq_domain);}ret = devm_request_threaded_irq(dev, pcf8575->client->irq, NULL, pcf8575_irq,IRQF_ONESHOT | IRQF_TRIGGER_FALLING, dev_name(dev), pcf8575);if (ret) {mutex_destroy(&pcf8575->irq_lock);return ret;}pcf8575->chip.to_irq = pcf8575_gpio_to_irq;/*** There is a bug which was occured by mutex_lock.* Before we invoked irq_create_mapping, we doesn't * set irq_data to pcf8575 object. Beacuse of we can't get * the virq parameter before invoking the irq_create_mapping.* So, When we get pcf8575* object from the irq_data, we get the NULL. It occured a * NULL pointer panic. As above, we can invok irq_create_mapping* in the chip.to_irq, we get the virq, and set pcf8575 object* to irq_chip.*/pcf8575->irq_domain->host_data = pcf8575;/*** Can create mapping concentrated in here,* Or create maping in the chip.to_irq for every pin*/// for (i = 0; i < pcf8575->chip.ngpio; i++) {//     irq = irq_create_mapping(pcf8575->irq_domain, i);// 	irq_set_chip_data(irq, pcf8575);// }return 0;
}static int pcf8575_irq_remove(struct pcf8575_dev *pcf8575)
{int i;int virq;for (i = 0; i < pcf8575->chip.ngpio; i++) {virq = pcf8575_gpio_to_irq(&pcf8575->chip, i);irq_dispose_mapping(virq);}irq_domain_remove(pcf8575->irq_domain);mutex_destroy(&pcf8575->irq_lock);return 0;
}static int pcf8575_probe(struct i2c_client *client,const struct i2c_device_id *id)
{struct pcf8575_dev *pcf8575;int ret;if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {dev_err(&client->dev, "i2c_check_functionality not support I2C_FUNC_I2C\n");return -EIO;}pcf8575 = devm_kzalloc(&client->dev, sizeof(struct pcf8575_dev), GFP_KERNEL);if (IS_ERR(pcf8575)) {dev_err(&client->dev, "kzalloc error: 0x%lx\n", PTR_ERR(pcf8575));return PTR_ERR(pcf8575);}pcf8575->chip.label = client->name;pcf8575->chip.base = -1;pcf8575->chip.parent = &client->dev;pcf8575->chip.owner = THIS_MODULE,pcf8575->chip.ngpio = GPIO_NUM;pcf8575->chip.can_sleep = 1;pcf8575->chip.get = pcf8575_get_value,pcf8575->chip.set = pcf8575_set_value,pcf8575->chip.direction_input = pcf8575_direction_input,pcf8575->chip.direction_output = pcf8575_direction_output,/* default value is 0xFFFF after power on */pcf8575->value = 0xFFFF;i2c_set_clientdata(client, pcf8575);pcf8575->client = client;ret = devm_gpiochip_add_data(&client->dev, &pcf8575->chip, NULL);if (ret) {return ret;}ret = pcf8575_irq_setup(pcf8575);if (ret) {return ret;}dev_info(&client->dev, "pcf8575 probe success\n");return 0;
}static int pcf8575_remove(struct i2c_client *client)
{struct pcf8575_dev *pcf8575 = i2c_get_clientdata(client);pcf8575_irq_remove(pcf8575);return 0;
}static struct i2c_device_id pcf8575_id_table[] = {{"pcf8575", 0},{},
};
MODULE_DEVICE_TABLE(i2c, pcf8575_id_table);static const struct of_device_id pcf8575_of_match[] = {{.compatible = "nxp,pcf8575_new"},{ },
};
MODULE_DEVICE_TABLE(of, pcf8575_of_match);static struct i2c_driver pcf8575_driver = {.probe = pcf8575_probe,.remove = pcf8575_remove,.driver = {.owner = THIS_MODULE,.name = "pcf8575_drv",.of_match_table = of_match_ptr(pcf8575_of_match),},.id_table = pcf8575_id_table,
};module_i2c_driver(pcf8575_driver);MODULE_AUTHOR("duapple <duapple2@gmail.com>");
MODULE_LICENSE("GPL");

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

51c自动驾驶~合集26

我自己的原文哦~ https://blog.51cto.com/whaosoft/11968755 #大模型/Sora/世界模型之间是什么关系 1 什么是大模型 人工智能大模型&#xff08;Artificial Intelligence Large Model&#xff0c;简称AI大模型&#xff09;是指具有庞大的参数规模和复杂程度的机器学习模…

分布式环境下的重复请求防护:非Redis锁替代方案全解析

目录 引言 方案一&#xff1a;前端防护策略 方案二&#xff1a;后端协同控制 方案三&#xff1a;流量控制与过滤 滑动窗口限流 布隆过滤器 方案四&#xff1a;基于框架的实践方案 多层防护策略与最佳实践 总结 引言 在Web应用开发中&#xff0c;防止用户重复点…

4.1 C#获取目录的3个方法的区别

C#中常用有如下3个获取目录的方式如下 1.Directory.GetCurrentDirectory():获取当前工作目录&#xff0c;工作目录可能被用户或其他代码修改。尽量少用。&#xff08;似乎只要在运行中使用另存为或者打开某个文件夹&#xff0c;当前工作目录就修改&#xff09; 2.Application…

【漏洞复现】Next.js中间件权限绕过漏洞 CVE-2025-29927

什么是Next.js&#xff1f; Next.js 是由 Vercel 开发的基于 React 的现代 Web 应用框架&#xff0c;具备前后端一体的开发能力&#xff0c;广泛用于开发 Server-side Rendering (SSR) 和静态站点生成&#xff08;SSG&#xff09;项目。Next.js 支持传统的 Node.js 模式和基于边…

MCU-芯片时钟与总线和定时器关系,举例QSPI

时钟源&#xff1a; 时钟源为系统时钟提供原始频率信号&#xff0c;系统时钟则通过&#xff08;分频、倍频、选择器&#xff09;成为整个芯片的“主时钟”&#xff0c;驱动 CPU 内核、总线&#xff08;AHB、APB&#xff09;及外设的运行。 内部时钟源&#xff1a; HSI&#x…

使用 ByteDance 的 UI-TARS Desktop 探索 AI 驱动的 GUI 自动化新前沿

文章目录 UI-TARS Desktop 是什么&#xff1f;技术亮点应用场景如何快速上手&#xff1f;与其他技术的对比未来展望结语 随着人工智能技术的快速发展&#xff0c;AI 正在从单纯的文本生成和图像识别迈向更复杂的交互场景。ByteDance 近期推出的 UI-TARS Desktop&#xff08;基于…

DockerFile制作镜像(Dockerfile Creates an Image)

DockerFile制作镜像 hub.docker.com 搜索到的 Redis官方镜像&#xff0c;提示我们可以创建自己的 DockerFile 来添加 redis.conf 文件&#xff1a; 于是&#xff0c;我准备进行首次 DockerFile 的制作尝试。 一、准备工作 1.1 下载 redis.conf 我的方案是从 GitHub 上下载 …

C++List模拟实现|细节|难点|易错点|全面解析|类型转换|

目录 1.模拟代码全部 2.四大块代码理解 1.最底层&#xff1a;ListNode部分 2.第二层&#xff1a;ListIterator部分 3.第三层&#xff1a;ReserveListIterator部分 4最终层&#xff1a;List 1.模拟代码全部 using namespace std; template<class T> struct ListNode …

如何让自动驾驶汽车“看清”世界?坐标映射与数据融合概述

在自动驾驶领域,多传感器融合技术是实现车辆环境感知和决策控制的关键。其中,坐标系映射和对应是多传感器融合的重要环节,它涉及到不同传感器数据在统一坐标系下的转换和匹配,以实现对车辆周围环境的准确感知。本文将介绍多传感器融合中坐标系映射和对应的数学基础和实际应…

鸿蒙开发之背景图片的使用

在鸿蒙开发中&#xff0c;设置背景图片是提升应用界面视觉效果的重要一环。以下是关于鸿蒙开发中背景图片使用的详细方法&#xff1a; 一、通过XML布局文件设置背景图片 1.使用Image组件设置背景图片 在XML布局文件中&#xff0c;可以使用Image组件来设置背景图片。通过ohos…

如何在 HTML 中创建一个有序列表和无序列表,它们的语义有何不同?

大白话如何在 HTML 中创建一个有序列表和无序列表&#xff0c;它们的语义有何不同&#xff1f; 1. HTML 中有序列表和无序列表的基本概念 在 HTML 里&#xff0c;列表是一种用来组织信息的方式。有序列表就是带有编号的列表&#xff0c;它可以让内容按照一定的顺序呈现&#…

c++malloc出来的对象调用构造-------定位new

前言:之前在搓高并发内存池的时候就在想,类对象不能调用自身的构造函数,那直接申请内存出来的类对象岂不是很难受,然后我这两天仔细研究了一下,发现其实构造函数也可以显示去调用,而且含不限量,故做此文 在c中一个类对象不能直接调用自身的构造 class A { public:A() {cout &l…

ElementUI时间选择、日期选择

如大家所发现的&#xff0c;由于ElementUI 时间选择器&#xff0c;日期选择器&#xff0c;时间日期选择器点击清除按钮时&#xff0c;v-model 所绑定的属性值会变成 null&#xff0c;所以当使用 ElementUI 时间选择器&#xff0c;日期选择器&#xff0c;时间日期选择器 时&…

一篇文章入门Python Flask框架前后端数据库开发实践(pycharm在anaconda环境下)

Python Flask 是一个轻量级的 Web 应用框架&#xff0c;也被称为微框架。它以简洁、灵活和易于上手的特点而受到开发者的喜爱。 核心特点 轻量级&#xff1a;Flask 核心代码简洁&#xff0c;仅包含 Web 开发的基本功能&#xff0c;不强制使用特定的数据库、模板引擎等&#xf…

ctfshow WEB web2

1.查当前数据库名称 or 11 union select 1,database(),3 limit 1,2;#-- 得到数据库名称web2 2.查看数据库表的数量 or 11 union select 1,(select count(*) from information_schema.tables where table_schema web2),3 limit 1,2;#-- 得到数据库表数量为2 3.查表的名字 第…

【Git】--- 分支管理

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; Git 本篇博客我们来介绍Git的一个重要功能之一 ---- 分支。我们将讲解关于分支的各种操作&#xff0c;以及如何帮助我们进行开发。 &#x1f3e0; 理解分支…

系统思考与心智模式

“问题不是出在我们做了多少&#xff0c;而是出在我们做了什么。” — 赫尔曼凯恩 “一分耕耘一分收获”&#xff0c;这似乎是我们脑海中根深蒂固的心智模式。今天&#xff0c;我在一家餐厅用餐&#xff0c;店员告诉我&#xff0c;打卡收藏可以获得一份小食。没过多久&#xf…

纯文本驱动的数据可视化革命——AI生成图表「图表狐」全场景深度解析

一、技术架构重定义 图表狐核心能力边界 ✅ 纯文本输入&#xff1a;支持任意格式文字描述&#xff08;会议纪要/邮件/手写笔记&#xff09; ✅ 智能解析引擎&#xff1a; 实体识别&#xff08;数值/时间/分类维度&#xff09; 语义纠错&#xff08;自动修复错别字/单位混乱&…

多线程 --- 进程和线程的基本知识

进程 前面我们提到了一个概念是&#xff0c;多任务操作系统&#xff0c;即希望该系统能够同时运行多个程序。本质上说&#xff0c;进程&#xff0c;就算用来解决”并发编程“这样的问题的。 在一些特定的情况下&#xff0c;进程的表现&#xff0c;其实并不能很好的解决”并发…

SCI英文论文Accepted后的第一步——Rights and Access

SCI英文论文Accepted后的第一步——Rights and Access 目录 SCI英文论文Accepted后的第一步——Rights and AccessBased on information provided the embargo period/end date is 24 months. 因为选择闭源**Rights and Access(版权与访问权限)**环节是关键第一步,具体操作流…