正点原子驱动开发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_gpios
中cs_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_chipselect
和nb
这两个变量中的最大值,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通信驱动开发实验的时候,主机无法…...

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

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

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

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

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

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

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

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

云原生之深入解析Kubernetes集群发生网络异常时如何排查
一、Pod 网络异常 网络不可达,主要现象为 ping 不通,其可能原因为: 源端和目的端防火墙(iptables, selinux)限制; 网络路由配置不正确; 源端和目的端的系统负载过高,网络连接数满…...

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

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

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

第十七章 爬虫scrapy登录与中间件2
文章目录 数据盘区太快会报错,setting中配置延迟 连接提取器...

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

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

音视频参数介绍
一、视频参数概念 单个视频帧:可以简单地理解成为一张图片 单个视频帧主要的参数概念: 分辨率: 分辨率是指图像或显示器上像素的数量,通常用横向像素数乘以纵向像素数表示。例如,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)四个单词的首字母缩写。且增删改查(CRUD,create,retrieve,update,delete)数据库的核心模块。 1. 新增(Create) 实…...

搜维尔科技:关于“第九届元宇宙数字人设计大赛”线上+线下巡回宣讲本周高校行程通告!
为了让大家更全面地了解此次大赛,顺利地进行大赛的报名和参赛,组委会遴选了一批优秀讲师代表,组建了宣讲团,以线上线下的方式陆续开展巡回宣讲。宣讲兼顾不同学生群体的需求,结合在数字人设计制作的过程中常见的疑难点…...

Gemini 1.0:Google推出的全新AI模型,改变生成式人工智能领域的游戏规则!
Gemini 1.0:Google推出的全新AI模型,将改变生成式人工智能领域的游戏规则! 🎥 屿小夏 : 个人主页 🔥个人专栏 : IT杂谈 🌄 莫道桑榆晚,为霞尚满天! 文章目录 …...

excel打开并操作CAD(excel-vba实现)_另附:CAD打开excel
excelvba可实现调用cad应用程序,并通过excel内置api弹窗实现打开、另存CAD 文件(cad-vba弹窗功能较为麻烦),代码如下(在excel_vba环境下操作): Sub excel_vba打开另存并操作CAD文件() Dim cad …...

Prolist组件实现动态竖排展示
实现原理:主要是利用外层div属性display:"flex",overflowX: auto,配合内层prolist样式属性flex:"0 0 auto",overflowX:"auto",width:"350px",height:900,实现动态竖排展示 <div style{{display:…...

基于JAVA的海南旅游景点推荐系统 开源项目
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户端2.2 管理员端 三、系统展示四、核心代码4.1 随机景点推荐4.2 景点评价4.3 协同推荐算法4.4 网站登录4.5 查询景点美食 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的海南旅游推荐系统ÿ…...

【Apache-StreamPark】Flink 开发利器 StreamPark 的介绍、安装、使用
【Apache-StreamPark】Flink 开发利器 StreamPark 的介绍、安装、使用 1)框架介绍与引入1.1.🚀 什么是 StreamPark1.2.🎉 Features1.3.🏳🌈 组成部分1.4.引入 StreamPark 2)安装部署2.1.环境要求2.2.Hado…...

【STM32】STM32学习笔记-LED闪烁 LED流水灯 蜂鸣器(06-2)
00. 目录 文章目录 00. 目录01. GPIO之LED电路图02. GPIO之LED接线图03. LED闪烁程序示例04. LED闪烁程序下载05. LED流水灯接线图06. LED流水灯程序示例07. 蜂鸣器接线图08. 蜂鸣器程序示例09. 下载10. 附录 01. GPIO之LED电路图 电路图示例1 电路图示例2 02. GPIO之LED接线图…...

docker服务启动报错docker.service holdoff time over, scheduling restart.
docker服务启动报错docker.service holdoff time over, scheduling restart. 卸载docker 卸载安装包 yum remove -y docker docker-client docker-client-latest docker-ce-cli docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinu…...

cfa一级考生复习经验分享系列(八)
先分析一下CFA Level 1内容,考试总体难度并不大,每门课程都比大学本科开设的对应课程简单,但是因为有十门课综合在一起,知识点相对算比较多,内容较多。但对知识点的掌握要求不高,理解即可。比如财报&#x…...

React中的事件处理
React中的事件处理 亲爱的同学们,今天我们将一起探索React中的一个非常实用的话题:事件处理。当我们谈论事件处理,我们指的是在用户与我们的应用程序交互时发生的各种情况,比如点击一个按钮或是提交一个表单。这些动作是我们如何让…...