当前位置: 首页 > news >正文

正点原子驱动开发BUG(一)--SPI无法正常通信

目录

    • 一、问题描述
    • 二、讲该问题的解决方案
    • 三、imx6ull的spi适配器驱动程序控制片选分析
        • 3.1 设备icm20608的驱动程序分析
        • 3.2 imx的spi适配器的驱动程序分析
    • 四、BUG修复测试
    • 五、其他问题

一、问题描述

使用正点的im6ull开发板进行spi通信驱动开发实验的时候,主机无法与从机进行正常通信。就算使用正点的例程,也无法正常通信。读不到从机寄存器中的值。以读取从机ID为例,例子为正点原子的例程基础上添加了几行printk用来打印信息:

void icm20608_reginit(void)
{u8 value = 1;u8 test = 1;icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_1, 0x80);		/* 复位 */mdelay(50);icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_1, 0x01);		/* 关闭睡眠 */mdelay(50);printk("ICM20608 ID = %#X\r\n", value);	value = icm20608_read_onereg(&icm20608dev, ICM20_WHO_AM_I);	 /* 这里读出来的ID不对 */printk("ICM20608 ID = %#X\r\n", value);	...
}
...
static unsigned char icm20608_read_onereg(struct icm20608_dev *dev, u8 reg)
{u8 data = 0;icm20608_read_regs(dev, reg, &data, 1);return data;
}
...
static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg, void *buf, int len)
{int ret = -1;unsigned char txdata[1];unsigned char * rxdata;struct spi_message m;struct spi_transfer *t;struct spi_device *spi = (struct spi_device *)dev->private_data;t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);	/* 申请内存 */if(!t) {return -ENOMEM;}rxdata = kzalloc(sizeof(char) * len, GFP_KERNEL);	/* 申请内存 */if(!rxdata) {goto out1;}/* 一共发送len+1个字节的数据,第一个字节为寄存器首地址,一共要读取len个字节长度的数据,*/txdata[0] = reg | 0x80;		/* 写数据的时候首寄存器地址bit8要置1 */			t->tx_buf = txdata;			/* 要发送的数据 */t->rx_buf = rxdata;			/* 要读取的数据 */t->len = len+1;				/* t->len=发送的长度+读取的长度 */spi_message_init(&m);		/* 初始化spi_message */spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */ret = spi_sync(spi, &m);	/* 同步发送 */if(ret) {goto out2;}memcpy(buf , rxdata+1, len);  /* 只需要读取的数据 */out2:kfree(rxdata);					/* 释放内存 */
out1:	kfree(t);						/* 释放内存 */return ret;
}

读ID失败,读出来ID是0,单纯是因为调用icm20608_read_onereg函数时会把返回值初始化为0,也就是根本没读到寄存器中内容

在这里插入图片描述

二、讲该问题的解决方案

首先直接说明问题所在:如果使用正点的spi驱动开发例程则设备树中spi适配器设备节点下的cs-gpios属性不能写成cs-gpio。当使用的属性名位cs-gpios的时候是由该spi适配器匹配的驱动程序来控制片选。

其实正点原子也在开发指南中说了:

2 行,设置当前片选数量为 1,因为就只接了一个 ICM20608。
第 3 行,一定要使用 “cs-gpios”属性来描述片选引脚,SPI 主机驱动就会控制片选引脚。
第 5 行,设置 IO 要使用的 pinctrl 子节点,也就是我们在示例代码 62.5.1.1 中新建的
pinctrl_ecspi3。

正点提供的linux源码中的documentation中也提到怎么编写对应设备树了,该文件位于linux源码位置/Documentation/devicetree/bindings/spi中:

* Freescale (Enhanced) Configurable Serial Peripheral Interface(CSPI/eCSPI) for i.MXRequired properties:
- compatible :- "fsl,imx1-cspi" for SPI compatible with the one integrated on i.MX1- "fsl,imx21-cspi" for SPI compatible with the one integrated on i.MX21- "fsl,imx27-cspi" for SPI compatible with the one integrated on i.MX27- "fsl,imx31-cspi" for SPI compatible with the one integrated on i.MX31- "fsl,imx35-cspi" for SPI compatible with the one integrated on i.MX35- "fsl,imx51-ecspi" for SPI compatible with the one integrated on i.MX51
- reg : Offset and length of the register set for the device
- interrupts : Should contain CSPI/eCSPI interrupt
- fsl,spi-num-chipselects : Contains the number of the chipselect
- cs-gpios : Specifies the gpio pins to be used for chipselects.
- clocks : Clock specifiers for both ipg and per clocks.
- clock-names : Clock names should include both "ipg" and "per"
See the clock consumer binding,Documentation/devicetree/bindings/clock/clock-bindings.txt
- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,Documentation/devicetree/bindings/dma/dma.txt
- dma-names: DMA request names should include "tx" and "rx" if present.Example:ecspi@70010000 {#address-cells = <1>;#size-cells = <0>;compatible = "fsl,imx51-ecspi";reg = <0x70010000 0x4000>;interrupts = <36>;fsl,spi-num-chipselects = <2>;cs-gpios = <&gpio3 24 0>, /* GPIO3_24 */<&gpio3 25 0>; /* GPIO3_25 */dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;dma-names = "rx", "tx";
};

但是我太信任正点提供的linux源码了,直接用了正点提供的出厂linux源码,这个出厂linux源码中的READ_ME解释如下:

说明:
1、使用此uboot和linux源码可以编译得到正点原子I.MX6U出货时的uboot和Linux固件。兼容正点原子所有RGB屏,板子上所有功能也相应调试好。直接按【正点原子】I.MX6U用户快速体验Vx.x.pdf文档编译出来使用!
2、驱动指南也提及过编译这里的源码,只是教学编译体验。问题答疑:
1、看到1、例程源码\10、开发板教程对应的uboot和linux源码这里还有一份正点原子的uboot和linux源码,为什么会有两份源码?
答:1、例程源码\10、开发板教程对应的uboot和linux源码是正点原子驱动指南做驱动实验、移植uboot和linux所用的源码。而出厂源码则是用于出货所使用,客户无需再调试。直接编译使用!2、为什么要和出厂源码分开?
答:由于多种原因,出厂使用的源码不能与驱动指南所使用的源码同时进行。出厂源码会随时修复bug或者添加新的驱动以兼容正点原子的其他模块。3、那我可不可以这么理解:出厂源码是给客户直接用在产品上使用,而教程源码则是用于初学者用于学习驱动和移植uboot和Linux上使用?
答:恩,可以这么理解。因为出厂源码是几乎无需再调试了,适用正点原子的ALPHA和Mini开发板,给一些快速上手的客户使用!而教程源码则是用于学习或者自己开发使用!

他说的是出厂源码是几乎无需再调试了,适用于正点原子的ALPHA开发板可以直接上手用,我就直接用了。但其实他的设备树是这么写的:

&ecspi3 {fsl,spi-num-chipselects = <1>;cs-gpio = <&gpio1 20 GPIO_ACTIVE_LOW>;		/* 必须使用cs-gpios属性名来描述片选引脚(如果你想让spi适配器驱动程序来设置片选的话) */pinctrl-names = "default";pinctrl-0 = <&pinctrl_ecspi3>;	/* 难不成是因为这里是pinctrl-0,这个0正好对应这第0通道,即icm20608 */status = "okay";spidev: icm20608@0 {compatible = "alientek,icm20608";spi-max-frequency = <8000000>;reg = <0>;};
};

打眼一看内容跟正点的驱动指南中提到的都一样,所以我就觉得没啥问题,但是实际上可以看到,有一个属性名写成了cs-gpio而不是cs-gpios,这是与正点的spi驱动开发例程不匹配的。为什么不匹配?这得分析分析适配器的驱动程序

三、imx6ull的spi适配器驱动程序控制片选分析

3.1 设备icm20608的驱动程序分析

首先说明问题:问题出现在厂商编写的spi适配器驱动程序上。但在此之前,我们要先来看看我们的设备驱动,也就是正点使用的设备icm20608的驱动程序中的一个函数:spi_setup()

static int icm20608_probe(struct spi_device *spi)
{.../*初始化spi_device */spi->mode = SPI_MODE_0;	/*MODE0,CPOL=0,CPHA=0*/spi_setup(spi);icm20608dev.private_data = spi; /* 设置私有数据 *//* 初始化ICM20608内部寄存器 */icm20608_reginit();		return 0;
}

该函数传入一个struct spi_device类型的变量。正点驱动对这个函数一带而过,只是说这个函数必须得有。其实就是这个函数中出了问题。该函数位于drivers/spi/spi.c中,我只列出源码中与错误相关的部分:

int spi_setup(struct spi_device *spi)
{unsigned	bad_bits, ugly_bits;int		status = 0;...spi_set_cs(spi, false);if (spi->master->setup)status = spi->master->setup(spi);...return status;
}
...
...
static void spi_set_cs(struct spi_device *spi, bool enable)
{if (spi->mode & SPI_CS_HIGH)enable = !enable;if (spi->cs_gpio >= 0)gpio_set_value(spi->cs_gpio, !enable);else if (spi->master->set_cs)spi->master->set_cs(spi, !enable);
}

spi_setup调用的spi_set_cs函数中可以看到,如果spi->cs_gpio >= 0,函数调用gpio_set_value(spi->cs_gpio, !enable)来设置某个goio口的电平,比如在正点的教程中如果要选中icm20608外设,那么就要设置gpio1_20这个gpio口的电平,所以应该出现的情况是spi->cs_gpio = 20。如果没满足第一个if,就会判断第二个else if,这个master->set_cs根本没设置,所以也用不到。

那么spi->cs_gpio是在哪里设置的呢?在spi.c文件中有这么一个函数spi_add_device(),截取出相关的内容:

int spi_add_device(struct spi_device *spi)
{static DEFINE_MUTEX(spi_add_lock);struct spi_master *master = spi->master;struct device *dev = master->dev.parent;int status;/* Chipselects are numbered 0..max; validate. */if (spi->chip_select >= master->num_chipselect) {dev_err(dev, "cs%d >= max %d\n",spi->chip_select,master->num_chipselect);return -EINVAL;}...if (master->cs_gpios)spi->cs_gpio = master->cs_gpios[spi->chip_select];...
}

可以看到在最后一个if中,如果master->cs_gpios不为0,那么就会设置spi->cs_gpio = master->cs_gpios[spi->chip_select]。这个spi->chip_select相当于master->cs_gpios这个数组的索引(master->cs_gpioscs_gpios的是struct spi_master结构体下的一个成员变量,为一个int类型的指针,其实就是一个数组),这个会在下一部分提到。

spi->chip_select又是在哪设置的呢?看一下spi.c文件中的of_register_spi_device()函数:

#if defined(CONFIG_OF)
static struct spi_device *
of_register_spi_device(struct spi_master *master, struct device_node *nc)
{struct spi_device *spi;int rc;u32 value;/* Alloc an spi_device */spi = spi_alloc_device(master);if (!spi) {dev_err(&master->dev, "spi_device alloc error for %s\n",nc->full_name);rc = -ENOMEM;goto err_out;}/* Select device driver */rc = of_modalias_node(nc, spi->modalias,sizeof(spi->modalias));if (rc < 0) {dev_err(&master->dev, "cannot find modalias for %s\n",nc->full_name);goto err_out;}/* Device address */rc = of_property_read_u32(nc, "reg", &value);if (rc) {dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",nc->full_name, rc);goto err_out;}spi->chip_select = value;.../* Register the new device */rc = spi_add_device(spi);if (rc) {dev_err(&master->dev, "spi_device register error %s\n",nc->full_name);goto err_out;}return spi;
}

该函数会调用of_property_read_u32()函数解析设备树中的spi设备的reg属性值,并赋值给变量value而在设备树中这个值中填入的正是片选值,然后设置spi->chip_select = value,并调用刚刚提到的spi_add_device()函数。该函数会在of_register_spi_devices()函数中调用:

static void of_register_spi_devices(struct spi_master *master)
{struct spi_device *spi;struct device_node *nc;if (!master->dev.of_node)return;for_each_available_child_of_node(master->dev.of_node, nc) {spi = of_register_spi_device(master, nc);if (IS_ERR(spi))dev_warn(&master->dev, "Failed to create SPI device for %s\n",nc->full_name);}
}

就相当于可能会注册很多个spi设备,所以用个for循环来一个一个注册。这个注册很多个spi设备的函数会在spi_register_master()函数中被调用,而这个函数是跟spi适配器有关。

可以看到,最终的最终,spi->cs_gpio其实是跟master->cs_gpios有关系,并且上述函数的调用的源头也是spi_register_master()函数。所以必须得去看spi适配器的驱动函数了。但在此之前先来看一下当设备树中写的属性为cs-gpio时出现的情况,我们在驱动程序中编写如下测试代码:

static int icm20608_probe(struct spi_device *spi)
{.../*初始化spi_device */spi->mode = SPI_MODE_0;	/*MODE0,CPOL=0,CPHA=0*/spi_setup(spi);printk("chip select:%d\r\n", spi->chip_select);printk("cs gpio:%d\r\n", spi->cs_gpio);icm20608dev.private_data = spi; /* 设置私有数据 */...return 0;
}

注册编译出来的ko模块的时候结果如下:

在这里插入图片描述
可以看到,spi->chip_select正常获取到了设备树中spi设备reg属性的值,但是spi->cs_gpio却没有获取到我们想要获取的gpio号20吗,而是-2。至于为什么,需要看下一部分。

3.2 imx的spi适配器的驱动程序分析

在第二部分中列出的设备树代码中只是列出了引用ecspi3时的一些修改,该设备真正定义在imx6ull.dtsi下:

ecspi3: ecspi@02010000 {#address-cells = <1>;#size-cells = <0>;compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";reg = <0x02010000 0x4000>;interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;clocks = <&clks IMX6UL_CLK_ECSPI3>,<&clks IMX6UL_CLK_ECSPI3>;clock-names = "ipg", "per";dmas = <&sdma 7 7 1>, <&sdma 8 7 2>;dma-names = "rx", "tx";status = "disabled";
};

其中的属性compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";,根据这个去找对应的驱动程序,找到的驱动程序为drivers/spi/spi-imx.c,找到其.probe函数,并列出其与问题相关的部分:

static int spi_imx_probe(struct platform_device *pdev)
{struct device_node *np = pdev->dev.of_node;const struct of_device_id *of_id =of_match_device(spi_imx_dt_ids, &pdev->dev);struct spi_imx_master *mxc_platform_info =dev_get_platdata(&pdev->dev);struct spi_master *master;struct spi_imx_data *spi_imx;struct resource *res;int i, ret, num_cs, irq;if (!np && !mxc_platform_info) {dev_err(&pdev->dev, "can't get the platform data\n");return -EINVAL;}ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);	/* 获取到num_cs为1 */if (ret < 0) {if (mxc_platform_info)num_cs = mxc_platform_info->num_chipselect;elsereturn ret;}master = spi_alloc_master(&pdev->dev,sizeof(struct spi_imx_data) + sizeof(int) * num_cs);	/* 这个函数会把num_chipselect初始化为1 */if (!master)return -ENOMEM;...spi_imx->bitbang.master = master;for (i = 0; i < master->num_chipselect; i++) {int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);if (!gpio_is_valid(cs_gpio) && mxc_platform_info)	/* gpio_is_valid: asm-generic/gpio.h return 1 or 0*/cs_gpio = mxc_platform_info->chipselect[i];spi_imx->chipselect[i] = cs_gpio;if (!gpio_is_valid(cs_gpio))continue;ret = devm_gpio_request(&pdev->dev, spi_imx->chipselect[i],DRIVER_NAME);if (ret) {dev_err(&pdev->dev, "can't get cs gpios\n");goto out_master_put;}}spi_imx->bitbang.chipselect = spi_imx_chipselect;spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;spi_imx->bitbang.txrx_bufs = spi_imx_transfer;spi_imx->bitbang.master->setup = spi_imx_setup;spi_imx->bitbang.master->cleanup = spi_imx_cleanup;spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;...master->dev.of_node = pdev->dev.of_node;ret = spi_bitbang_start(&spi_imx->bitbang);...return ret;
}

并会在spi_bitbang_start函数中调用了spi_register_master()函数,也就是上一部分提到的很多函数的源头,向内核注册spi_master设备,该函数位于drivers/spi/spi-bitbang.c中,列出相关部分:

int spi_bitbang_start(struct spi_bitbang *bitbang)
{struct spi_master *master = bitbang->master;int ret;if (!master || !bitbang->chipselect)return -EINVAL;spin_lock_init(&bitbang->lock);...ret = spi_register_master(spi_master_get(master));if (ret)spi_master_put(master);return 0;
}

接下来我们就可以看一下spi_register_master()这个函数了。上一部分讲的函数主要作用是帮助注册spi设备,而这个函数的作用是向内核注册spi适配器设备,该函数同样位于spi.c中,只列出与我们的bug相关的代码:

int spi_register_master(struct spi_master *master)
{static atomic_t		dyn_bus_id = ATOMIC_INIT((1<<15) - 1);struct device		*dev = master->dev.parent;struct boardinfo	*bi;int			status = -ENODEV;int			dynamic = 0;if (!dev)return -ENODEV;status = of_spi_register_master(master);.../* Register devices from the device tree and ACPI */of_register_spi_devices(master);		/* 这里会调用of_register_spi_device */acpi_register_spi_devices(master);
done:return status;
}

关键函数就是of_spi_register_master()这个函数,该函数同样定义在spi.c中,其源码如下:

#ifdef CONFIG_OF
static int of_spi_register_master(struct spi_master *master)
{int nb, i, *cs;struct device_node *np = master->dev.of_node;printk("in of_spi_register_master?\r\n");  /* 我自己加的test,判断一下CONFIG_OF这个宏定义是否开启了 */if (!np)return 0;nb = of_gpio_named_count(np, "cs-gpios");master->num_chipselect = max_t(int, nb, master->num_chipselect);/* Return error only for an incorrectly formed cs-gpios property */if (nb == 0 || nb == -ENOENT)return 0;else if (nb < 0)return nb;cs = devm_kzalloc(&master->dev,sizeof(int) * master->num_chipselect,GFP_KERNEL);master->cs_gpios = cs;if (!master->cs_gpios)return -ENOMEM;for (i = 0; i < master->num_chipselect; i++)cs[i] = -ENOENT;for (i = 0; i < nb; i++)cs[i] = of_get_named_gpio(np, "cs-gpios", i);return 0;
}

从这个函数中可以看出,该函数首先调用of_gpio_named_count来统计cs-gpios这个属性中设置的gpio的个数。很不幸,我们在设备树中把属性写成了cs-gpio,所以该函数找不到节点,就会返回负值。然后该函数将master->num_chipselect设定为了master->num_chipselectnb这两个变量中的最大值,master->num_chipselect这个变量会在.probe函数中通过读取设备树中spi适配器设备节点中的"fsl,spi-num-chipselects"属性来初始化。根据设备树来看,该属性的值被设为了1。

前文提到,master->cs_gpios是一个int类型的指针,所以在该函数中将他指向了一段使用devm_kzalloc()函数分配的内存,可以理解为现在master->cs_gpios就是一个数组了,里面有num_chipselect个数据

然后该函数调用两个for循环,来对master->cs_gpios这个数组进行初始化,因为master->num_chipselect为1,所以第一个for循环正常执行,csp[i] = -ENOENT而这个宏定义ENOENT恰恰就是2。并且因为变量nb是个负数,所以第二个for不会执行,就算执行这个循环他也根本找不到"cs-gpios"这个属性(我写的设备树少了一个s)。

所以综上,master->cs_gpios全被初始化为了-2,导致第一部分提到的spi设备结构体中的变量(即spi->cs_gpio)也为-2,从而导致在调用gpio_set_value()函数来对spi->cs_gpio这个gpio号的时候没有正确设置。

四、BUG修复测试

前面已经说明了,把设备树中的属性改为"cs-gpios"应该就行了:

&ecspi3 {fsl,spi-num-chipselects = <1>;cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;		/* 必须使用cs-gpio属性来描述片选引脚,可能spi适配器驱动里用的就是这个名称"cs-gpio" */pinctrl-names = "default";pinctrl-0 = <&pinctrl_ecspi3>;	/* 难不成是因为这里是pinctrl-0,这个0正好对应这第0通道,即icm20608 */status = "okay";spidev: icm20608@0 {compatible = "alientek,icm20608";spi-max-frequency = <8000000>;reg = <0>;};
};

然后重新编译设备树,测试结果如下:
在这里插入图片描述
gpio口获取也对了,也能正确读到icm20608的设备id了。

那如果我就是想用cs-gpio这个名呢?那也有办法,就是在你自己写的设备驱动程序中,使用of_get_named_gpio()函数来手动获取gpio号,但是传入该函数的属性名就不要是cs-gpios了,而应该是cs-gpio(如果用这样的方法相当于跳过spi框架和spi适配器驱动函数,那么你取啥名都可以,我把属性写成本gpios-cs也没问题)。然后再使用gpio_set_value()函数来设置这个gpio口即可

五、其他问题

其实有个问题不知道有没有注意到,就是在分析驱动程序的时候出现了一个#ifdef CONFIG_OF,上述很多函数都依赖于这个宏必须得被定义了才能起作用。这个宏我根本没找到在哪里定义的,如果没定义这个宏,那之前的分析就白费了,所以找了一个依赖于这个宏的函数进行测试:

#ifdef CONFIG_OF
static int of_spi_register_master(struct spi_master *master)
{int nb, i, *cs;struct device_node *np = master->dev.of_node;printk("in of_spi_register_master?\r\n");  /* 我自己加的test,判断一下CONFIG_OF这个宏定义是否开启了 */...return 0;
}

然后重新编译内核,再次启动内核,启动过程中打印结果如下:

在这里插入图片描述
看来这个宏确实是定义了,但是定义在哪个位置我真没找到,有知道的老哥可以教一下。

相关文章:

正点原子驱动开发BUG(一)--SPI无法正常通信

目录 一、问题描述二、讲该问题的解决方案三、imx6ull的spi适配器驱动程序控制片选分析3.1 设备icm20608的驱动程序分析3.2 imx的spi适配器的驱动程序分析 四、BUG修复测试五、其他问题 一、问题描述 使用正点的im6ull开发板进行spi通信驱动开发实验的时候&#xff0c;主机无法…...

SpringBoot接入轻量级分布式日志框架GrayLog

1.前言 日志在我们日常开发定位错误&#xff0c;链路错误排查时必不可少&#xff0c;如果我们只有一个服务&#xff0c;我们可以只简单的通过打印的日志文件进行排查定位就可以&#xff0c;但是在分布式服务环境下&#xff0c;多个环境的日志统一收集、展示则成为一个问题。目…...

光电器件:感知光与电的桥梁

光电器件是电子工程领域的一个重要分支&#xff0c;主要研究光与电之间的相互转换。这些器件在许多领域都有广泛的应用&#xff0c;如通信、生物医学、军事等。 光电器件主要包括光电二极管、光电晶体管、光电倍增管等。这些器件的工作原理都是基于光电效应&#xff0c;即光子…...

Ceph入门到精通-smartctl 查看硬盘参数

smartctl 参数含义 Model Family: Toshiba s... Enterprise Capacity HDD Device Model: TOSHIBA MG08ACss Serial Number: sssssss LU WWN Device Id: 5 ss ss Firmware Version: 4303 User Capacity: 16,000,900,661,248 bytes [16.0 TB] Sector Sizes: 51…...

Module build failed: TypeError: this.getOptions is not a function

在使用webpack打包出现以上错误时&#xff0c;可能是你安装的css-loader和style-loader的版本过高。 我用的webpack版本是3.6.0 因此需要降低一下版本 在你编辑器终端输入以下命令&#xff1a; npm install css-loader3.6.0 npm install --save-dev style-loader1.00 然后接下…...

蓝牙电子价签芯片OM6626/OM628超低功耗替代NRF52832

电子价签应用简介 在全球零售业受到电商冲击、劳动力成本和周转率上升、消费者需求改变的行业背景下&#xff0c;电子价签、AI货架监控系统、自助结账设备、相关的方案将零售行业的发展带上智能化数字化的发展道路上。为企业与客户带来的更高效更便捷的消费体验。 蓝牙电子价…...

ELK(八)—Metricbeat部署

目录 介绍修改配置文件启动 Modulenginx开启状态查询配置Nginx module查看是否配置成功 介绍 Metricbeat 是一个轻量级的开源度量数据收集器&#xff0c;用于监控系统和服务。它由 Elastic 公司开发&#xff0c;并作为 Elastic Stack&#xff08;Elasticsearch、Logstash、Kiba…...

Ansible自动化运维以及模块使用

ansible的作用&#xff1a; 远程操作主机功能 自动化运维(playbook剧本基于yaml格式书写) ansible是基于python开发的配置管理和应用部署工具。在自动化运维中&#xff0c;现在是异军突起 ansible能够批量配置、部署、管理上千台主机。类似于Xshell的一键输入工具。不需要每…...

数据分析场景下,企业大模型选型的思路与建议

来源/作者&#xff1a;爱分析 随着大模型带来能力突破&#xff0c;让AI与数据分析相互结合&#xff0c;使分析结果更好支撑业务&#xff0c;促进企业内部数据价值释放&#xff0c;成为了当下企业用户尤为关注的话题。本次分享主要围绕数据分析场景下大模型底座的选型思路&#…...

Mongodb复制集架构

目录 复制集架构 复制集优点 复制集模式 复制集搭建 复制集常用命令 复制集增删节点 复制集选举 复制集同步 oplog分析 什么是oplog 查看oplog oplog大小 复制集架构 复制集优点 数据复制: 数据在Primary节点上进行写入&#xff0c;然后异步地复制到Secondary节点&a…...

云原生之深入解析Kubernetes集群发生网络异常时如何排查

一、Pod 网络异常 网络不可达&#xff0c;主要现象为 ping 不通&#xff0c;其可能原因为&#xff1a; 源端和目的端防火墙&#xff08;iptables, selinux&#xff09;限制&#xff1b; 网络路由配置不正确&#xff1b; 源端和目的端的系统负载过高&#xff0c;网络连接数满…...

error: C2039: “qt_metacast“: 不是 “***“ 的成员

报错信息&#xff1a; error C2039: "qt_metacast": 不是 "radartrack::Base_track_data" 的成员 最近在使用Qt开发&#xff0c;自定义的数据类在继承数据抽象类和QObject类时总是报“qt_metacast 不是不是 "radartrack::Base_track_data" 的成员…...

量子计算:开启IT领域的新时代

量子计算&#xff1a;开启IT领域的新时代 一、量子计算的基本原理与背景 量子计算作为一项前沿高级技术&#xff0c;正逐渐引起IT领域的广泛关注。传统计算机是通过二进制位&#xff08;0和1&#xff09;来储存和处理信息&#xff0c;而量子计算机则利用量子位或称为“量子比特…...

数据可视化---柱状图

类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统计学检验箱…...

第十七章 爬虫scrapy登录与中间件2

文章目录 数据盘区太快会报错&#xff0c;setting中配置延迟 连接提取器...

运维知识点-Kubernetes_K8s

Kubernetes RBAC配置不当攻击场景攻击过程 RBAC配置不当 Service Account本质是服务账号&#xff0c;是Pod连接K8s集群的凭证。 在默认情况下&#xff0c;系统会为创建的Pod提供一个默认的Service Account&#xff0c; 用户也可以自定义Service Account&#xff0c;与Service…...

某电子文档安全管理系统存在任意用户登录漏洞

漏洞简介 某电子文档安全管理系统存在任意用户登录漏洞&#xff0c;攻击者可以通过用户名获取对应的cookie&#xff0c;登录后台。 资产测绘 Hunter语法&#xff1a;web.icon“9fd216c3e694850445607451fe3b3568” 漏洞复现 获取Cookie POST /CDGServer3/LinkFilterServi…...

音视频参数介绍

一、视频参数概念 单个视频帧&#xff1a;可以简单地理解成为一张图片 单个视频帧主要的参数概念&#xff1a; 分辨率&#xff1a; 分辨率是指图像或显示器上像素的数量&#xff0c;通常用横向像素数乘以纵向像素数表示。例如&#xff0c;1920x1080 表示宽度为1920像素&…...

vue中使用minio上传文件

创建一个 文件getOssClient import { getOssSetting } from "/api/common"; import Vue from "vue"; import { getCookies, getLocal } from "/utils/auth"; // get token from cookie export async function getStsToken() {//从后台获取stsTo…...

MySQL表的增删改查(初阶)

CRUD 即增加(Create)、查询(Retrieve)、更新(Update)、删除(Delete)四个单词的首字母缩写。且增删改查&#xff08;CRUD&#xff0c;create&#xff0c;retrieve&#xff0c;update&#xff0c;delete&#xff09;数据库的核心模块。 1. 新增&#xff08;Create&#xff09; 实…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...