【RTT驱动框架分析05】-spi驱动框架分析

news2024/11/16 11:48:58

spi

1.应用层的spi操作

spi 消息结构

struct rt_spi_message
{
    const void *send_buf;//发送数据的缓存
    void *recv_buf;//接收数据的缓存
    rt_size_t length;//数据长度
    struct rt_spi_message *next;//指向下一个消息结构

    unsigned cs_take    : 1;//是否执行获取cs
    unsigned cs_release : 1;//是否执行释放cs
};

通用的数据收发函数

struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device  *device,
                                               struct rt_spi_message *message)

2.摘录自RTT官方-应用层文档

挂载 SPI 设备

SPI 驱动会注册 SPI 总线,SPI 设备需要挂载到已经注册好的 SPI 总线上。

rt_err_t rt_spi_bus_attach_device_cspin(struct rt_spi_device *device,
                                        const char           *name,
                                        const char           *bus_name,
                                        rt_base_t            cs_pin,
                                        void                 *user_data)
参数描述
deviceSPI 设备句柄
nameSPI 设备名称
bus_nameSPI 总线名称
cs_pinSPI 片选引脚号(基于PIN框架)
user_data用户数据指针
返回——
RT_EOK成功
其他错误码失败

此函数用于挂载一个 SPI 设备到指定的 SPI 总线,并向内核注册 SPI 设备。并且可以依赖RT-Thread的PIN框架来绑定SPI的片选引脚(cs_pin),避免了不同bsp的上层应用对片选引脚操作不统一的问题。

一般 SPI 总线命名原则为 spix, SPI 设备命名原则为 spixy ,如 spi10 表示挂载在 spi1 总线上的 0 号设备。cs_pin可以通过PIN框架rt_pin_get函数来获取,也可以使用BSP级提供的GET_PIN宏定义来获取。user_data 在用户使用不到的情况下可以设置为RT_NULL

使用示例:

struct rt_spi_device *spi_device;
spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
/* BSP级 GET_PIN 宏定义方式 */
rt_hw_spi_device_attach(spi_device, "spi1", "spi10", GET_PIN(B, 14), RT_NULL);
/* PIN框架级 rt_pin_get api方式 */
rt_hw_spi_device_attach(spi_device, "spi1", "spi10", rt_pin_get("PB.14"), RT_NULL);

[!NOTE]

此函数是RT-Thread 5.0.0 添加的新函数,如果低于5.0.0版本不支持这个函数。

为了兼容RT-Thread 5.0.0 版本前的SPI设备片选引脚通过user_data挂载的方式,我们保留了rt_spi_bus_attach_device这个api,但是希望大家在今后使用的时候,尽量使用rt_spi_bus_attach_device_cspin这个新特性api。

rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device,
                                  const char           *name,
                                  const char           *bus_name,
                                  void                 *user_data)
参数描述
deviceSPI 设备句柄
nameSPI 设备名称
bus_nameSPI 总线名称
user_data用户数据指针
返回——
RT_EOK成功
其他错误码失败

此函数用于挂载一个 SPI 设备到指定的 SPI 总线,并向内核注册 SPI 设备,并将 user_data 保存到 SPI 设备的控制块里。

一般 SPI 总线命名原则为 spix, SPI 设备命名原则为 spixy ,如 spi10 表示挂载在 spi1 总线上的 0 号设备。user_data 一般为 SPI 设备的 CS 引脚指针,进行数据传输时 SPI 控制器会操作此引脚进行片选。

下面的示例代码挂载 SPI FLASH W25Q128 到 SPI 总线:

static int rt_hw_spi_flash_init(void)
{
    struct rt_spi_device *spi_device = RT_NULL;

    spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
    if(RT_NULL == spi_device)
    {
        LOG_E("Failed to malloc the spi device.");
        return -RT_ENOMEM;
    }
    if (RT_EOK != rt_spi_bus_attach_device_cspin(spi_device, "spi10", "spi1",GET_PIN(B, 14), RT_NULL))
    {
        LOG_E("Failed to attach the spi device.");
        return -RT_ERROR;
    }
    if (RT_NULL == rt_sfud_flash_probe("W25Q128", "spi10"))
    {
        LOG_E("Failed to probe the W25Q128.");
        return -RT_ERROR;
    };

    return RT_EOK;
}

/* 导出到自动初始化 */
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);

配置 SPI 设备

挂载 SPI 设备到 SPI 总线后需要配置 SPI 设备的传输参数。

主要是工作模式,数据宽度,spi工作频率

rt_err_t rt_spi_configure(struct rt_spi_device *device,
                          struct rt_spi_configuration *cfg)
参数描述
deviceSPI 设备句柄
cfgSPI 配置参数指针
返回——
RT_EOK成功

此函数会保存 cfg 指向的配置参数到 SPI 设备 device 的控制块里,当传输数据时会使用此配置参数。

struct rt_spi_configuration 原型如下:

struct rt_spi_configuration
{
    rt_uint8_t mode;        /* 模式 */
    rt_uint8_t data_width;  /* 数据宽度,可取8位、16位、32位 */
    rt_uint16_t reserved;   /* 保留 */
    rt_uint32_t max_hz;     /* 最大频率 */
};

模式: 包含 MSB/LSB、主从模式、 时序模式等,可取宏组合如下:

/* 设置数据传输顺序是MSB位在前还是LSB位在前 */
#define RT_SPI_LSB      (0<<2)                        /* bit[2]: 0-LSB */
#define RT_SPI_MSB      (1<<2)                        /* bit[2]: 1-MSB */

/* 设置SPI的主从模式 */
#define RT_SPI_MASTER   (0<<3)                        /* SPI master device */
#define RT_SPI_SLAVE    (1<<3)                        /* SPI slave device */

/* 设置时钟极性和时钟相位 */
#define RT_SPI_MODE_0   (0 | 0)                       /* CPOL = 0, CPHA = 0 */
#define RT_SPI_MODE_1   (0 | RT_SPI_CPHA)             /* CPOL = 0, CPHA = 1 */
#define RT_SPI_MODE_2   (RT_SPI_CPOL | 0)             /* CPOL = 1, CPHA = 0 */
#define RT_SPI_MODE_3   (RT_SPI_CPOL | RT_SPI_CPHA)   /* CPOL = 1, CPHA = 1 */

#define RT_SPI_CS_HIGH  (1<<4)                        /* Chipselect active high */
#define RT_SPI_NO_CS    (1<<5)                        /* No chipselect */
#define RT_SPI_3WIRE    (1<<6)                        /* SI/SO pin shared */
#define RT_SPI_READY    (1<<7)                        /* Slave pulls low to pause */

数据宽度: 根据 SPI 主设备及 SPI 从设备可发送及接收的数据宽度格式设置为8位、16位或者32位。

最大频率: 设置数据传输的波特率,同样根据 SPI 主设备及 SPI 从设备工作的波特率范围设置。

配置示例如下所示:

    struct rt_spi_configuration cfg;
    cfg.data_width = 8;
    cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
    cfg.max_hz = 20 * 1000 *1000;                           /* 20M */

    rt_spi_configure(spi_dev, &cfg);

访问 SPI 设备

一般情况下 MCU 的 SPI 器件都是作为主机和从机通讯,在 RT-Thread 中将 SPI 主机虚拟为 SPI 总线设备,应用程序使用 SPI 设备管理接口来访问 SPI 从机器件,主要接口如下所示:

函数描述
rt_device_find()根据 SPI 设备名称查找设备获取设备句柄
rt_spi_transfer_message()自定义传输数据
rt_spi_transfer()传输一次数据
rt_spi_send()发送一次数据
rt_spi_recv()接受一次数据
rt_spi_send_then_send()连续两次发送
rt_spi_send_then_recv()先发送后接收

[!NOTE] 注:SPI 数据传输相关接口会调用 rt_mutex_take(), 此函数不能在中断服务程序里面调用,会导致 assertion 报错。

查找 SPI 设备

在使用 SPI 设备前需要根据 SPI 设备名称获取设备句柄,进而才可以操作 SPI 设备,查找设备函数如下所示,

rt_device_t rt_device_find(const char* name);
参数描述
name设备名称
返回——
设备句柄查找到对应设备将返回相应的设备句柄
RT_NULL没有找到相应的设备对象

一般情况下,注册到系统的 SPI 设备名称为 spi10, qspi10等,使用示例如下所示:

#define W25Q_SPI_DEVICE_NAME     "qspi10"   /* SPI 设备名称 */
struct rt_spi_device *spi_dev_w25q;     /* SPI 设备句柄 */

/* 查找 spi 设备获取设备句柄 */
spi_dev_w25q = (struct rt_spi_device *)rt_device_find(W25Q_SPI_DEVICE_NAME);

自定义传输数据

获取到 SPI 设备句柄就可以使用 SPI 设备管理接口访问 SPI 设备器件,进行数据收发。可以通过如下函数传输消息:

struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device  *device,struct rt_spi_message *message)
参数描述
deviceSPI 设备句柄
message消息指针
返回——
RT_NULL成功发送
非空指针发送失败,返回指向剩余未发送的 message 的指针

此函数可以传输一连串消息,用户可以自定义每个待传输的 message 结构体各参数的数值,从而可以很方便的控制数据传输方式。struct rt_spi_message 原型如下:

struct rt_spi_message
{
    const void *send_buf;           /* 发送缓冲区指针 */
    void *recv_buf;                 /* 接收缓冲区指针 */
    rt_size_t length;               /* 发送 / 接收 数据字节数 */
    struct rt_spi_message *next;    /* 指向继续发送的下一条消息的指针 */
    unsigned cs_take    : 1;        /* 片选选中 */
    unsigned cs_release : 1;        /* 释放片选 */
};
  • sendbuf 为发送缓冲区指针,其值为 RT_NULL 时,表示本次传输为只接收状态,不需要发送数据。
  • recvbuf 为接收缓冲区指针,其值为 RT_NULL 时,表示本次传输为只发送状态,不需要保存接收到的数据,所以收到的数据直接丢弃。
  • length 的单位为 word,即数据长度为 8 位时,每个 length 占用 1 个字节;当数据长度为 16 位时,每个 length 占用 2 个字节。
  • 参数 next 是指向继续发送的下一条消息的指针,若只发送一条消息,则此指针值为 RT_NULL。多个待传输的消息通过 next 指针以单向链表的形式连接在一起。
  • cs_take 值为 1 时,表示在传输数据前,设置对应的 CS 为有效状态
  • cs_release 值为 1 时,表示在数据传输结束后,释放对应的 CS

[!NOTE] 注:* 当 send_buf 或 recv_buf 不为空时,两者的可用空间都不得小于 length

  • 若使用此函数传输消息,传输的第一条消息 cs_take 需置为 1,设置片选为有效,最后一条消息的 cs_release 需置 1,释放片选

使用示例如下所示:

#define W25Q_SPI_DEVICE_NAME     "qspi10"   /* SPI 设备名称 */
struct rt_spi_device *spi_dev_w25q;     /* SPI 设备句柄 */
struct rt_spi_message msg1, msg2;
rt_uint8_t w25x_read_id = 0x90;         /* 命令 */
rt_uint8_t id[5] = {0};

/* 查找 spi 设备获取设备句柄 */
spi_dev_w25q = (struct rt_spi_device *)rt_device_find(W25Q_SPI_DEVICE_NAME);
/* 发送命令读取ID */
struct rt_spi_message msg1, msg2;

msg1.send_buf   = &w25x_read_id;
msg1.recv_buf   = RT_NULL;
msg1.length     = 1;
msg1.cs_take    = 1;
msg1.cs_release = 0;
msg1.next       = &msg2;

msg2.send_buf   = RT_NULL;
msg2.recv_buf   = id;
msg2.length     = 5;
msg2.cs_take    = 0;
msg2.cs_release = 1;
msg2.next       = RT_NULL;

rt_spi_transfer_message(spi_dev_w25q, &msg1);
rt_kprintf("use rt_spi_transfer_message() read w25q ID is:%x%x\n", id[3], id[4]);

传输一次数据

如果只传输一次数据可以通过如下函数:

rt_size_t rt_spi_transfer(struct rt_spi_device *device,
                          const void           *send_buf,
                          void                  *recv_buf,
                          rt_size_t             length);
参数描述
deviceSPI 设备句柄
send_buf发送数据缓冲区指针
recv_buf接收数据缓冲区指针
length发送/接收 数据字节数
返回——
0传输失败
非 0 值成功传输的字节数

此函数等同于调用rt_spi_transfer_message() 传输一条消息,开始发送数据时片选选中,函数返回时释放片选,message 参数配置如下:

struct rt_spi_message msg;

msg.send_buf   = send_buf;
msg.recv_buf   = recv_buf;
msg.length     = length;
msg.cs_take    = 1;
msg.cs_release = 1;
msg.next        = RT_NULL;

发送一次数据

如果只发送一次数据,而忽略接收到的数据可以通过如下函数:

rt_size_t rt_spi_send(struct rt_spi_device *device,
                      const void           *send_buf,
                      rt_size_t             length)
参数描述
deviceSPI 设备句柄
send_buf发送数据缓冲区指针
length发送数据字节数
返回——
0发送失败
非 0 值成功发送的字节数

调用此函数发送 send_buf 指向的缓冲区的数据,忽略接收到的数据,此函数是rt_spi_transfer() 函数的封装

此函数等同于调用 rt_spi_transfer_message() 传输一条消息,开始发送数据时片选选中,函数返回时释放片选,message 参数配置如下:

struct rt_spi_message msg;

msg.send_buf   = send_buf;
msg.recv_buf   = RT_NULL;
msg.length     = length;
msg.cs_take    = 1;
msg.cs_release = 1;
msg.next       = RT_NULL;

接收一次数据

如果只接收一次数据可以通过如下函数:

rt_size_t rt_spi_recv(struct rt_spi_device *device,
                      void                 *recv_buf,
                      rt_size_t             length);
参数描述
deviceSPI 设备句柄
recv_buf接收数据缓冲区指针
length接收数据字节数
返回——
0接收失败
非 0 值成功接收的字节数

调用此函数接收数据并保存到 recv_buf 指向的缓冲区。此函数是对 rt_spi_transfer() 函数的封装。SPI 总线协议规定只能由主设备产生时钟,因此在接收数据时,主设备会发送数据 0XFF。

此函数等同于调用 rt_spi_transfer_message() 传输一条消息,开始接收数据时片选选中,函数返回时释放片选,message 参数配置如下:

struct rt_spi_message msg;

msg.send_buf   = RT_NULL;
msg.recv_buf   = recv_buf;
msg.length     = length;
msg.cs_take    = 1;
msg.cs_release = 1;
msg.next       = RT_NULL;

连续两次发送数据

如果需要先后连续发送 2 个缓冲区的数据,并且中间片选不释放,可以调用如下函数:

rt_err_t rt_spi_send_then_send(struct rt_spi_device *device,
                               const void           *send_buf1,
                               rt_size_t             send_length1,
                               const void           *send_buf2,
                               rt_size_t             send_length2);
参数描述
deviceSPI 设备句柄
send_buf1发送数据缓冲区 1 指针
send_length1发送数据缓冲区 1 数据字节数
send_buf2发送数据缓冲区 2 指针
send_length2发送数据缓冲区 2 数据字节数
返回——
RT_EOK发送成功
-RT_EIO发送失败

此函数可以连续发送 2 个缓冲区的数据,忽略接收到的数据,发送 send_buf1 时片选选中,发送完 send_buf2 后释放片选。

本函数适合向 SPI 设备中写入一块数据,第一次先发送命令和地址等数据,第二次再发送指定长度的数据。之所以分两次发送而不是合并成一个数据块发送,或调用两次 rt_spi_send(),是因为在大部分的数据写操作中,都需要先发命令和地址,长度一般只有几个字节。如果与后面的数据合并在一起发送,将需要进行内存空间申请和大量的数据搬运。而如果调用两次 rt_spi_send()那么在发送完命令和地址后,片选会被释放,大部分 SPI 设备都依靠设置片选一次有效为命令的起始,所以片选在发送完命令或地址数据后被释放,则此次操作被丢弃。

此函数等同于调用 rt_spi_transfer_message() 传输 2 条消息,message 参数配置如下:

struct rt_spi_message msg1,msg2;

msg1.send_buf   = send_buf1;
msg1.recv_buf   = RT_NULL;
msg1.length     = send_length1;
msg1.cs_take    = 1;
msg1.cs_release = 0;
msg1.next       = &msg2;

msg2.send_buf   = send_buf2;
msg2.recv_buf   = RT_NULL;
msg2.length     = send_length2;
msg2.cs_take    = 0;
msg2.cs_release = 1;
msg2.next       = RT_NULL;

先发送后接收数据

如果需要向从设备先发送数据,然后接收从设备发送的数据,并且中间片选不释放,可以调用如下函数:

rt_err_t rt_spi_send_then_recv(struct rt_spi_device *device,
                               const void           *send_buf,
                               rt_size_t             send_length,
                               void                 *recv_buf,
                               rt_size_t             recv_length);
参数描述
deviceSPI 从设备句柄
send_buf发送数据缓冲区指针
send_length发送数据缓冲区数据字节数
recv_buf接收数据缓冲区指针
recv_length接收数据字节数
返回——
RT_EOK成功
-RT_EIO失败

此函数发送第一条数据 send_buf 时开始片选,此时忽略接收到的数据,然后发送第二条数据,此时主设备会发送数据 0XFF,接收到的数据保存在 recv_buf 里,函数返回时释放片选。

本函数适合从 SPI 从设备中读取一块数据,第一次会先发送一些命令和地址数据,然后再接收指定长度的数据。

此函数等同于调用 rt_spi_transfer_message() 传输 2 条消息,message 参数配置如下:

struct rt_spi_message msg1,msg2;

msg1.send_buf   = send_buf;
msg1.recv_buf   = RT_NULL;
msg1.length     = send_length;
msg1.cs_take    = 1;
msg1.cs_release = 0;
msg1.next       = &msg2;

msg2.send_buf   = RT_NULL;
msg2.recv_buf   = recv_buf;
msg2.length     = recv_length;
msg2.cs_take    = 0;
msg2.cs_release = 1;
msg2.next       = RT_NULL;

SPI 设备管理模块还提供 rt_spi_sendrecv8()rt_spi_sendrecv16() 函数,这两个函数都是对此函数的封装,rt_spi_sendrecv8() 发送一个字节数据同时收到一个字节数据,rt_spi_sendrecv16() 发送 2 个字节数据同时收到 2 个字节数据。

3.底层的spi操作

spi 分为spi总线spi设备,分为3层框架,spi设备继承自spi总线,spi总线继承自io设备驱动框架,例如w25q128就是一个spi设备

spi总线抽象接口

struct rt_spi_ops
{
    rt_err_t (*configure)(struct rt_spi_device *device, struct rt_spi_configuration *configuration);
    rt_uint32_t (*xfer)(struct rt_spi_device *device, struct rt_spi_message *message);
};

spi设备抽象

继承自rt_device 设备驱动模型

struct rt_spi_bus
{
    struct rt_device parent;//父类
    rt_uint8_t mode;
    const struct rt_spi_ops *ops;//spi操作函数

    struct rt_mutex lock;//一个总线可能会挂载多个设备,所以需要使用锁操作
    struct rt_spi_device *owner; //总线目前归属那个spi设备
};

spi设备

继承自rt_device 和spi总线

//老结构
struct rt_spi_device
{
    struct rt_device parent;
    struct rt_spi_bus *bus;

    struct rt_spi_configuration config;
    void   *user_data;
};

//新结构-增加了CS片选
struct rt_spi_device
{
    struct rt_device parent;
    struct rt_spi_bus *bus;

    struct rt_spi_configuration config;
    rt_base_t cs_pin;//直接提供一个RTT框架下的pin使用 GET_PIN(C,1);
    void   *user_data;
};

如何注册一个spi总线

  1. 实现一个结构体对象rt_spi_bus ,实现内部的操作函数configure和xfer
  2. 注册spi总线 rt_spi_bus_register

spi总线注册如下图

在这里插入图片描述

spi设备注册

spi 设备注册依赖一个spi总线,在rt_spi_bus_attach_device 函数注册时,需要提供spi总线,cs片选

rt_err_t rt_spi_bus_attach_device_cspin(struct rt_spi_device *device,
                                        const char           *name,//设备名字
                                        const char           *bus_name,//总线名字
                                        rt_base_t            cs_pin,
                                        void                 *user_data)

下图就是spi设备如何注册,如何读写数据到spi设备
在这里插入图片描述
5.0框架下的spi设备注册
在这里插入图片描述

4.如何修改spi总线的引脚

在drv_spi.h文件中定义了spi外设的引脚

#ifdef RT_USING_SPI1
#define SPI1_SCK_PIN             GPIO_PIN_5 /* PA.05 */
#define SPI1_SCK_GPIO_PORT       GPIOA      /* GPIOA */
#define SPI1_SCK_GPIO_CLK        RCC_APB2_PERIPH_GPIOA
#define SPI1_MISO_PIN            GPIO_PIN_6 /* PA.06 */
#define SPI1_MISO_GPIO_PORT      GPIOA      /* GPIOA */
#define SPI1_MISO_GPIO_CLK       RCC_APB2_PERIPH_GPIOA
#define SPI1_MOSI_PIN            GPIO_PIN_7 /* PA.07 */
#define SPI1_MOSI_GPIO_PORT      GPIOA      /* GPIOA */
#define SPI1_MOSI_GPIO_CLK       RCC_APB2_PERIPH_GPIOA
#endif  /* RT_USING_SPI1 */

#ifdef RT_USING_SPI2
#define SPI2_SCK_PIN             GPIO_PIN_13 /* PB.13 */
#define SPI2_SCK_GPIO_PORT       GPIOB      /* GPIOB */
#define SPI2_SCK_GPIO_CLK        RCC_APB2_PERIPH_GPIOB
#define SPI2_MISO_PIN            GPIO_PIN_14 /* PB.14 */
#define SPI2_MISO_GPIO_PORT      GPIOB      /* GPIOB */
#define SPI2_MISO_GPIO_CLK       RCC_APB2_PERIPH_GPIOB
#define SPI2_MOSI_PIN            GPIO_PIN_15 /* PB.15 */
#define SPI2_MOSI_GPIO_PORT      GPIOB      /* GPIOB */
#define SPI2_MOSI_GPIO_CLK       RCC_APB2_PERIPH_GPIOB
#endif  /* RT_USING_SPI2 */

5.W25Q128 nor flash

状态寄存器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

芯片id

在这里插入图片描述

指令

在这里插入图片描述

在这里插入图片描述

时序

每个指令都是在cs拉高后开始执行

写使能时序

在这里插入图片描述

读取状态寄存器

在这里插入图片描述

读数据时序

在这里插入图片描述

写时序

在这里插入图片描述

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

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

相关文章

SQL-每日一题【1174. 即时食物配送 II】

题目 配送表: Delivery 如果顾客期望的配送日期和下单日期相同&#xff0c;则该订单称为 「即时订单」&#xff0c;否则称为「计划订单」。 「首次订单」是顾客最早创建的订单。我们保证一个顾客只会有一个「首次订单」。 写一条 SQL 查询语句获取即时订单在所有用户的首次订…

零碎小知识点汇总——记录工作中遇到的问题——基础积累

1.npm install安装包时&#xff0c;常用的-S -D有什么区别&#xff1f; 参考链接&#xff1a;https://blog.csdn.net/sunyctf/article/details/127667543 主要的区别就是依赖配置写入package.json文件的位置不同而已 npm install有一个别名&#xff1a;npm i -S:写入dependen…

抢夺本地生活万亿蛋糕:“抖音美团们”的攻防战

有人就有市场&#xff0c;有市场就有竞争。面对本地生活这一大“富矿”&#xff0c;互联网大厂们正在开启一场争夺战。 前有抖音、小红书在团购业务上火速推进&#xff0c;近日&#xff0c;随着拼多多正式上线本地生活入口&#xff0c;这个领域的玩家越来越多。 艾瑞咨询数据…

Linux中的特殊进程(孤儿进程、僵尸进程、守护进程)

一、孤儿进程 1&#xff09;父进程退出&#xff0c;子进程不退出&#xff0c;此时子进程被1号&#xff08;init&#xff09;进程收养&#xff0c;变成孤儿进程。 2&#xff09;孤儿进程会脱离终端控制&#xff0c;且运行在后端&#xff0c;不能用ctrlc杀死后端进程&#xff0c;…

Linux ALSA音频工具aplay、arecord、amixer的使用方法

ALSA 是Advanced Linux Sound Architecture的缩写&#xff0c;先进的Linux音频架构&#xff0c;为Linux操作系统提供音频和MIDI功能。 aplay命令 aplay是播放命令。 rootimx6ul7d:~# aplay -h Usage: aplay [OPTION]... [FILE]...-h, --help help--version …

MyBatis-Plus 和达梦数据库实现高效数据持久化

一、添加依赖 首先&#xff0c;我们需要在项目的 pom.xml 文件中添加 MyBatis-Plus 和达梦数据库的依赖&#xff1a; <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifac…

pyspark_自定义udf_解析json列【附代码】

pyspark_自定义udf_解析json列【附代码】 一、背景&#xff1a;二、调研方案&#xff1a;三、利用Pyspark udf自定义函数实现大数据并行计算整体流程案例代码运行结果&#xff1a;案例代码&#xff1a;代码地址&#xff1a;代码 一、背景&#xff1a; 车联网数据有很多车的时…

【MATLAB第63期】基于MATLAB的改进敏感性分析方法IPCC,拥挤距离与皮尔逊系数法结合实现回归与分类预测

【MATLAB第63期】基于MATLAB的改进敏感性分析方法IPCC&#xff0c;拥挤距离与皮尔逊系数法结合实现回归与分类预测 思路 考虑拥挤距离指标与PCC皮尔逊相关系数法相结合&#xff0c;对回归或分类数据进行降维&#xff0c;通过SVM支持向量机交叉验证得到平均指标&#xff0c;来…

《华为认证》SR MPLS BE配置

实验需求&#xff1a;在PE1和PE3之间建立mp-bgp邻居传递CE1和CE2的私网路由&#xff0c;并且使用SR mpls BE的方式传递私网流量 实验步骤 步骤1&#xff1a;配置设备接口ip地址以及AS 100内的igp协议&#xff08;略&#xff09; 步骤2&#xff1a;AS 100内的设备开启mpls &am…

Vue2 第十四节 scoped样式和本地存储

1.scoped样式 2.本地存储 一.scoped样式 ① 作用&#xff1a;让样式在局部生效&#xff0c;防止冲突 ② 写法&#xff1a;<style scoped> ③ 代码示例&#xff1a; <style scoped> .demo {background-color: lightblue; } </style> ④ scoped样式一般…

【redis】创建集群

这里介绍的是创建redis集群的方式&#xff0c;一种是通过create-cluster配置文件创建部署在一个物理机上的伪集群&#xff0c;一种是先在不同物理机启动单体redis&#xff0c;然后通过命令行使这些redis加入集群的方式。 一&#xff0c;通过配置文件创建伪集群 进入redis源码…

分布式应用:ELK企业级日志分析系统

目录 一、理论 1.ELK 2.ELK场景 3.完整日志系统基本特征 4.ELK 的工作原理 5.ELK集群准备 6.Elasticsearch部署&#xff08;在Node1、Node2节点上操作&#xff09; 7.Logstash 部署&#xff08;在 Apache 节点上操作&#xff09; 8.Kiabana 部署&#xff08;在 Node1 节点…

C++设计模式之适配器设计模式

文章目录 C适配器设计模式什么是适配器设计模式该模式有什么优缺点优点缺点 如何使用 C适配器设计模式 什么是适配器设计模式 适配器设计模式是一种行为型设计模式&#xff0c;它允许你将两个不兼容的接口组合在一起&#xff0c;使它们能够协同工作。 该模式有什么优缺点 优…

elementUI全屏loading的使用(白屏的解决方案)

官网中有使用方法&#xff0c;但是我实际上手之后会出现白屏&#xff0c;解决办法如下&#xff1a; <el-button type"text" size"small" click"delRow(scope)"> 删除</el-button>loading: false, // loading 动画loadingInstance…

玩转Java IO流:轻松读写文件、网络

申明&#xff1a;本人于公众号Java筑基期&#xff0c;CSDN先后发当前文章&#xff0c;标明原创&#xff0c;转载二次发文请注明转载公众号&#xff0c;另外请不要再标原创 &#xff0c;注意违规 字符流和字节流 在Java中&#xff0c;IO&#xff08;输入输出&#xff09;操作涉…

TCP三次握手与四次断开

TCP三次握手机制 三次握手是指建立一个TCP连接时&#xff0c;需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。 1、客户端发送建立TCP连接的请求报文&#xff0c;其…

IDEA设置快捷操作

步骤&#xff1a; 1、 2、 3、 4、 然后直接用就可以啦 常用的接口测试模板&#xff1a; given().contentType(JSON).body($requestBody$).log().all().when().post($path$).then().log().all().statusCode(200);given().contentType(ContentType.JSON).body().log().all().w…

使用低代码平台提高生产力

一、前言 低代码平台的概念很火爆&#xff0c;产品也是鱼龙混杂。 对于开发人员来说&#xff0c;在使用绝大部分低代码平台的时候都会遇到一个致命的问题&#xff1a;我在上面做的项目无法得到源码&#xff0c;完全黑盒。一旦我的需求平台满足不了&#xff0c;那就是无解。 与其…

面试热题(无重复字符的最长子串)

无重复字符的最长子串 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。 解法一&#xff1a; public int lengthOfLonge…

UE4/5 PoseDriver 动画蓝图节点使用

PoseDriver节点可以进行Pose的比对&#xff0c;从而针对不同Pose生成不同权重数值&#xff0c;权重数值可应用至MorphTarget上使动画更加逼真&#xff0c;或应用至角色挂件上&#xff0c;制作出类惯性或弹簧的附加效果。 1.创建Pose 这里创建Box作为演示&#xff0c;下图大Bo…