LoRaWAN网关源码分析(SPI篇)

目录

一、前言

二、lgw_spi_open函数

三、lgw_spi_w函数

四、lgw_spi_r函数

五、lgw_spi_wb函数

六、lgw_spi_rb函数


一、前言

        本篇文章整理了LoRaWAN网关如何处理与 LoRa 前端设备之间的 SPI通信(在loralgw_spi.c文件中)。对SPI协议不了解的可以看这篇文章---《SPI总线协议》。

二、lgw_spi_open函数

        该函数通常用于初始化 SPI 接口并准备与 LoRa 网关的前端设备进行通信。我们需要打开指定的 SPI 设备文件,并配置必要的 SPI 参数(如模式、速度等)。

主要流程:

1、打开SPI设备文件

spi_fd = open(SPI_DEVICE, O_RDWR);
  • 调用 open 函数以读写模式 (O_RDWR) 打开指定的 SPI 设备文件(如 /dev/spidev0.0)。
  • 如果打开失败,open 将返回 -1,并且 spi_fd 将保持为 -1。
  • 错误处理:如果 open 失败,调用 perror 打印错误信息,然后返回 -1。

2、设置SPI模式

ret = ioctl(spi_fd, SPI_IOC_WR_MODE, &SPI_MODE);
  • 使用 ioctl 函数将 SPI 模式设置为指定的模式(这里是模式 0)。
  • SPI_MODE 变量的值决定了时钟相位和极性。
  • 错误处理:如果 ioctl 失败,调用 perror 打印错误信息,关闭 SPI 设备文件并返回 -1。

3、设置每个传输字的位数

ret = ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &SPI_BITS);
  • 使用 ioctl 函数设置每个 SPI 传输字的位数(这里是 8 位)。
  • 错误处理:如果 ioctl 失败,调用 perror 打印错误信息,关闭 SPI 设备文件并返回 -1。

4、设置SPI总线速度

ret = ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &SPI_SPEED);
  • 使用 ioctl 函数设置 SPI 总线的最大速度(这里是 500 kHz)。
  • 错误处理:如果 ioctl 失败,调用 perror 打印错误信息,关闭 SPI 设备文件并返回 -1。

5、返回0表示成功

         如果所有设置都成功,函数返回 0 表示 SPI 初始化成功。

int lgw_spi_open(const char * com_path, void **com_target_ptr) {int *spi_device = NULL;int dev;int a=0, b=0;int i;/* check input variables */CHECK_NULL(com_path);CHECK_NULL(com_target_ptr);/* allocate memory for the device descriptor */spi_device = malloc(sizeof(int));if (spi_device == NULL) {DEBUG_MSG("ERROR: MALLOC FAIL\n");return LGW_SPI_ERROR;}/* open SPI device */dev = open(com_path, O_RDWR);if (dev < 0) {DEBUG_PRINTF("ERROR: failed to open SPI device %s\n", com_path);return LGW_SPI_ERROR;}/* setting SPI mode to 'mode 0' */i = SPI_MODE_0;a = ioctl(dev, SPI_IOC_WR_MODE, &i);b = ioctl(dev, SPI_IOC_RD_MODE, &i);if ((a < 0) || (b < 0)) {DEBUG_MSG("ERROR: SPI PORT FAIL TO SET IN MODE 0\n");close(dev);free(spi_device);return LGW_SPI_ERROR;}/* setting SPI max clk (in Hz) */i = SPI_SPEED;a = ioctl(dev, SPI_IOC_WR_MAX_SPEED_HZ, &i);b = ioctl(dev, SPI_IOC_RD_MAX_SPEED_HZ, &i);if ((a < 0) || (b < 0)) {DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MAX SPEED\n");close(dev);free(spi_device);return LGW_SPI_ERROR;}/* setting SPI to MSB first */i = 0;a = ioctl(dev, SPI_IOC_WR_LSB_FIRST, &i);b = ioctl(dev, SPI_IOC_RD_LSB_FIRST, &i);if ((a < 0) || (b < 0)) {DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MSB FIRST\n");close(dev);free(spi_device);return LGW_SPI_ERROR;}/* setting SPI to 8 bits per word */i = 0;a = ioctl(dev, SPI_IOC_WR_BITS_PER_WORD, &i);b = ioctl(dev, SPI_IOC_RD_BITS_PER_WORD, &i);if ((a < 0) || (b < 0)) {DEBUG_MSG("ERROR: SPI PORT FAIL TO SET 8 BITS-PER-WORD\n");close(dev);return LGW_SPI_ERROR;}*spi_device = dev;*com_target_ptr = (void *)spi_device;DEBUG_MSG("Note: SPI port opened and configured ok\n");return LGW_SPI_SUCCESS;
}

        相对应的,竟然要打开,就要关闭,关闭相应的SPI文件我们要使用lgw_spi_close函数,代码如下:

int lgw_spi_close(void *com_target) {int spi_device;int a;/* check input variables */CHECK_NULL(com_target);/* close file & deallocate file descriptor */spi_device = *(int *)com_target; /* must check that spi_target is not null beforehand */a = close(spi_device);free(com_target);/* determine return code */if (a < 0) {DEBUG_MSG("ERROR: SPI PORT FAILED TO CLOSE\n");return LGW_SPI_ERROR;} else {DEBUG_MSG("Note: SPI port closed\n");return LGW_SPI_SUCCESS;}
}

三、lgw_spi_w函数

        该函数通过 SPI 接口向指定的目标设备(由 spi_mux_target 标识)写入一个字节的数据到指定的寄存器地址。

主要流程:

1、获取SPI设备文件描述符

spi_device = *(int *)com_target;

        将 com_target 解释为指向 int 的指针,并解引用以获取 SPI 设备文件描述符。

2、准备要发送的帧

out_buf[0] = spi_mux_target;
out_buf[1] = WRITE_ACCESS | ((address >> 8) & 0x7F);
out_buf[2] =                ((address >> 0) & 0xFF);
out_buf[3] = data;
command_size = 4;
  • 构建要发送的 SPI 帧:
    • out_buf[0]:目标设备选择(SPI 多路复用目标)。
    • out_buf[1]:写访问标志和地址的高位(写命令)。
    • out_buf[2]:地址的低位。
    • out_buf[3]:要写入的数据字节。
  • command_size:设置为 4,表示总共发送 4 个字节。

3、准备I/O传输结构体

memset(&k, 0, sizeof(k));
k.tx_buf = (unsigned long) out_buf;
k.len = command_size;
k.speed_hz = SPI_SPEED;
k.cs_change = 0;
k.bits_per_word = 8;

4、执行I/O传输

a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);

5、判断返回值并处理错误

if (a != (int)k.len) {DEBUG_MSG("ERROR: SPI WRITE FAILURE\n");return LGW_SPI_ERROR;
} else {DEBUG_MSG("Note: SPI write success\n");return LGW_SPI_SUCCESS;
}
int lgw_spi_w(void *com_target, uint8_t spi_mux_target, uint16_t address, uint8_t data) {int spi_device;uint8_t out_buf[4];uint8_t command_size;struct spi_ioc_transfer k;int a;/* check input variables */CHECK_NULL(com_target);spi_device = *(int *)com_target; /* must check that spi_target is not null beforehand *//* prepare frame to be sent */out_buf[0] = spi_mux_target;out_buf[1] = WRITE_ACCESS | ((address >> 8) & 0x7F);out_buf[2] =                ((address >> 0) & 0xFF);out_buf[3] = data;command_size = 4;/* I/O transaction */memset(&k, 0, sizeof(k)); /* clear k */k.tx_buf = (unsigned long) out_buf;k.len = command_size;k.speed_hz = SPI_SPEED;k.cs_change = 0;k.bits_per_word = 8;a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);/* determine return code */if (a != (int)k.len) {DEBUG_MSG("ERROR: SPI WRITE FAILURE\n");return LGW_SPI_ERROR;} else {DEBUG_MSG("Note: SPI write success\n");return LGW_SPI_SUCCESS;}
}

四、lgw_spi_r函数

        该函数通过 SPI 接口从指定的目标设备(由 spi_mux_target 标识)的指定寄存器地址读取一个字节的数据。

主要流程:

1、获取SPI设备文件描述符

spi_device = *(int *)com_target;

        将 com_target 解释为指向 int 的指针,并解引用以获取 SPI 设备文件描述符。

2、准备要发送的帧

out_buf[0] = spi_mux_target;
out_buf[1] = READ_ACCESS | ((address >> 8) & 0x7F);
out_buf[2] = ((address >> 0) & 0xFF);
out_buf[3] = 0x00;
out_buf[4] = 0x00;
command_size = 5;
  • out_buf[0]:目标设备选择(SPI 多路复用目标)。
  • out_buf[1]:读访问标志和地址的高位(读命令)。
  • out_buf[2]:地址的低位。
  • out_buf[3]out_buf[4]:填充字节,用于接收数据时的占位符。

3、准备I/O传输结构体

memset(&k, 0, sizeof(k)); /* 清空 k */
k.tx_buf = (unsigned long) out_buf;
k.rx_buf = (unsigned long) in_buf;
k.len = command_size;
k.cs_change = 0;

4、执行I/O传输

a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);

5、判断返回值并处理错误

if (a != (int)k.len) {DEBUG_MSG("ERROR: SPI READ FAILURE\n");return LGW_SPI_ERROR;
} else {DEBUG_MSG("Note: SPI read success\n");*data = in_buf[command_size - 1];return LGW_SPI_SUCCESS;
}
int lgw_spi_r(void *com_target, uint8_t spi_mux_target, uint16_t address, uint8_t *data) {int spi_device;uint8_t out_buf[5];uint8_t command_size;uint8_t in_buf[ARRAY_SIZE(out_buf)];struct spi_ioc_transfer k;int a;/* check input variables */CHECK_NULL(com_target);CHECK_NULL(data);spi_device = *(int *)com_target; /* must check that com_target is not null beforehand *//* prepare frame to be sent */out_buf[0] = spi_mux_target;out_buf[1] = READ_ACCESS | ((address >> 8) & 0x7F);out_buf[2] =               ((address >> 0) & 0xFF);out_buf[3] = 0x00;out_buf[4] = 0x00;command_size = 5;/* I/O transaction */memset(&k, 0, sizeof(k)); /* clear k */k.tx_buf = (unsigned long) out_buf;k.rx_buf = (unsigned long) in_buf;k.len = command_size;k.cs_change = 0;a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);/* determine return code */if (a != (int)k.len) {DEBUG_MSG("ERROR: SPI READ FAILURE\n");return LGW_SPI_ERROR;} else {DEBUG_MSG("Note: SPI read success\n");*data = in_buf[command_size - 1];return LGW_SPI_SUCCESS;}
}

五、lgw_spi_wb函数

        该函数用于在 SPI 设备上执行一个批量写操作,可以一次性写入多个字节的数据到指定的寄存器地址。

主要流程:

1、SPI设备选择

spi_device = *(int *)com_target;

2、准备命令字节

    command[0] = spi_mux_target;command[1] = WRITE_ACCESS | ((address >> 8) & 0x7F);command[2] =                ((address >> 0) & 0xFF);command_size = 3;size_to_do = size;
  • command[0]:设备选择字节,设置为 spi_mux_target
  • command[1]command[2]:组合地址和写操作标志,其中 (address >> 8) & 0x7F 是地址的高 7 位,(address >> 0) & 0xFF 是地址的低 8 位。写操作标志由 WRITE_ACCESS 定义。

3、准备SPI传输结构体

    memset(&k, 0, sizeof(k)); /* clear k */k[0].tx_buf = (unsigned long) &command[0];k[0].len = command_size;k[0].cs_change = 0;k[1].cs_change = 0;
  • 使用 struct spi_ioc_transfer k[2] 数组来准备两个传输结构体,因为这是一个批量操作。
  • k[0] 结构体用于发送命令字节。
  • k[1] 结构体用于发送数据部分。

4、执行批量写操作

  for (i=0; size_to_do > 0; ++i) {chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK;offset = i * LGW_BURST_CHUNK;k[1].tx_buf = (unsigned long)(data + offset);k[1].len = chunk_size;byte_transfered += (ioctl(spi_device, SPI_IOC_MESSAGE(2), &k) - k[0].len );DEBUG_PRINTF("BURST WRITE: to trans %d # chunk %d # transferred %d \n", size_to_do, chunk_size, byte_transfered);size_to_do -= chunk_size; /* subtract the quantity of data already transferred */}
  • 使用循环来处理数据的分块写入,每次写入不超过 LGW_BURST_CHUNK 大小的数据块。
  • chunk_size:计算每次实际写入的数据块大小。
  • offset:计算当前写入的数据在 data 数组中的偏移量。
  • 调用 ioctl 函数发送 k 数组中的两个结构体,分别发送命令和数据部分。
  • 累计 byte_transfered 变量记录实际传输的字节数。

5、返回状态

int lgw_spi_wb(void *com_target, uint8_t spi_mux_target, uint16_t address, const uint8_t *data, uint16_t size) {int spi_device;uint8_t command[3];uint8_t command_size;struct spi_ioc_transfer k[2];int size_to_do, chunk_size, offset;int byte_transfered = 0;int i;/* check input parameters */CHECK_NULL(com_target);CHECK_NULL(data);if (size == 0) {DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n");return LGW_SPI_ERROR;}spi_device = *(int *)com_target; /* must check that com_target is not null beforehand *//* prepare command byte */command[0] = spi_mux_target;command[1] = WRITE_ACCESS | ((address >> 8) & 0x7F);command[2] =                ((address >> 0) & 0xFF);command_size = 3;size_to_do = size;/* I/O transaction */memset(&k, 0, sizeof(k)); /* clear k */k[0].tx_buf = (unsigned long) &command[0];k[0].len = command_size;k[0].cs_change = 0;k[1].cs_change = 0;for (i=0; size_to_do > 0; ++i) {chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK;offset = i * LGW_BURST_CHUNK;k[1].tx_buf = (unsigned long)(data + offset);k[1].len = chunk_size;byte_transfered += (ioctl(spi_device, SPI_IOC_MESSAGE(2), &k) - k[0].len );DEBUG_PRINTF("BURST WRITE: to trans %d # chunk %d # transferred %d \n", size_to_do, chunk_size, byte_transfered);size_to_do -= chunk_size; /* subtract the quantity of data already transferred */}/* determine return code */if (byte_transfered != size) {DEBUG_MSG("ERROR: SPI BURST WRITE FAILURE\n");return LGW_SPI_ERROR;} else {DEBUG_MSG("Note: SPI burst write success\n");return LGW_SPI_SUCCESS;}
}

六、lgw_spi_rb函数

       该函数用于 在 SPI 设备上执行一个批量读取操作,将多个字节的数据从指定地址的寄存器中读取到给定的数据数组中。

主要流程:

1、SPI设备选择

spi_device = *(int *)com_target;

2、准备命令字节

    command[0] = spi_mux_target;command[1] = READ_ACCESS | ((address >> 8) & 0x7F);command[2] =               ((address >> 0) & 0xFF);command[3] = 0x00;command_size = 4;size_to_do = size;
    • command[0]:设备选择字节,设置为 spi_mux_target
    • command[1]command[2]command[3]:组合地址和读操作标志,其中 (address >> 8) & 0x7F 是地址的高 7 位,(address >> 0) & 0xFF 是地址的低 8 位。读操作标志由 READ_ACCESS 定义,最后一个字节设为 0x00

 3、准备SPI传输结构体

    memset(&k, 0, sizeof(k)); /* clear k */k[0].tx_buf = (unsigned long) &command[0];k[0].len = command_size;k[0].cs_change = 0;k[1].cs_change = 0;
  • 使用 struct spi_ioc_transfer k[2] 数组来准备两个传输结构体,因为这是一个批量操作。
  • k[0] 结构体用于发送命令字节。
  • k[1] 结构体用于接收数据部分。

4、执行批量读取操作

for (i=0; size_to_do > 0; ++i) {chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK;offset = i * LGW_BURST_CHUNK;k[1].rx_buf = (unsigned long)(data + offset);k[1].len = chunk_size;byte_transfered += (ioctl(spi_device, SPI_IOC_MESSAGE(2), &k) - k[0].len );DEBUG_PRINTF("BURST READ: to trans %d # chunk %d # transferred %d \n", size_to_do, chunk_size, byte_transfered);size_to_do -= chunk_size;  /* subtract the quantity of data already transferred */}
  • 使用循环来处理数据的分块读取,每次读取不超过 LGW_BURST_CHUNK 大小的数据块。
  • chunk_size:计算每次实际读取的数据块大小。
  • offset:计算当前读取的数据在 data 数组中的偏移量。
  • 调用 ioctl 函数发送 k 数组中的两个结构体,分别发送命令和接收数据部分。
  • 累计 byte_transfered 变量记录实际传输的字节数。

5、返回状态

int lgw_spi_rb(void *com_target, uint8_t spi_mux_target, uint16_t address, uint8_t *data, uint16_t size) {int spi_device;uint8_t command[4];uint8_t command_size;struct spi_ioc_transfer k[2];int size_to_do, chunk_size, offset;int byte_transfered = 0;int i;/* check input parameters */CHECK_NULL(com_target);CHECK_NULL(data);if (size == 0) {DEBUG_MSG("ERROR: BURST OF NULL LENGTH\n");return LGW_SPI_ERROR;}spi_device = *(int *)com_target; /* must check that com_target is not null beforehand *//* prepare command byte */command[0] = spi_mux_target;command[1] = READ_ACCESS | ((address >> 8) & 0x7F);command[2] =               ((address >> 0) & 0xFF);command[3] = 0x00;command_size = 4;size_to_do = size;/* I/O transaction */memset(&k, 0, sizeof(k)); /* clear k */k[0].tx_buf = (unsigned long) &command[0];k[0].len = command_size;k[0].cs_change = 0;k[1].cs_change = 0;for (i=0; size_to_do > 0; ++i) {chunk_size = (size_to_do < LGW_BURST_CHUNK) ? size_to_do : LGW_BURST_CHUNK;offset = i * LGW_BURST_CHUNK;k[1].rx_buf = (unsigned long)(data + offset);k[1].len = chunk_size;byte_transfered += (ioctl(spi_device, SPI_IOC_MESSAGE(2), &k) - k[0].len );DEBUG_PRINTF("BURST READ: to trans %d # chunk %d # transferred %d \n", size_to_do, chunk_size, byte_transfered);size_to_do -= chunk_size;  /* subtract the quantity of data already transferred */}/* determine return code */if (byte_transfered != size) {DEBUG_MSG("ERROR: SPI BURST READ FAILURE\n");return LGW_SPI_ERROR;} else {DEBUG_MSG("Note: SPI burst read success\n");return LGW_SPI_SUCCESS;}
}

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

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

相关文章

Hive SQL:实现炸列(列转行)以及逆操作(行转列)

目录 列转行行转列 列转行 函数&#xff1a; EXPLODE(ARRAY)&#xff1a;将ARRAY中的每一元素转换为每一行 EXPLODE(MAP)&#xff1a;将MAP中的每个键值对转换为两行&#xff0c;其中一行数据包含键&#xff0c;另一行数据包含值 数据样例&#xff1a; 1、将每天的课程&#…

免费代码生成工具

领取&安装链接&#xff1a;Baidu Comate 领取季卡 代码自动化生成工具&#xff0c;软件工程师可以在ide中沉浸式写代码&#xff0c;自动化给出代码生成&#xff0c;自然语言直接输出代码。 1.Baidu Comate是什么&#xff1f; Baidu Comate是JetBrains/VSCode插件&#…

ServletConfig与ServletContext详解

文章目录 概要web.xmlServletConfig介绍ServletConfig实例ServletConfig细节ServletContext介绍ServletContext实例ServletContext细节ServletContext获得服务访问次数&#xff08;可拓展&#xff09;总结 概要 web.xml <?xml version"1.0" encoding"UTF-…

OBD诊断(ISO15031) 02服务

文章目录 功能简介请求和响应1、read-supported PIDs1.1、请求1.2、肯定响应 2、read PID value1.1、请求1.2、肯定响应 3、同时请求多个PID4、同时读取多个PID数据 Parameter definition报文示例1、单个PID请求和读取2、多个PID请求和读取 功能简介 02服务&#xff0c;即 Req…

索引失效的场景主要有那些

1、不满足最左匹配原则 当使用联合索引时&#xff0c;查询条件没有从最左索引列开始&#xff0c;或者跳过了索引中的列&#xff0c;那么索引可能会失效。例如&#xff0c;对于联合索引(sex, age, name)&#xff0c;如果查询条件只包含了sex和name而没有age&#xff0c;那么索引…

亚太杯赛题思路发布(中文版)

导读&#xff1a; 本文将继续修炼回归模型算法&#xff0c;并总结了一些常用的除线性回归模型之外的模型&#xff0c;其中包括一些单模型及集成学习器。 保序回归、多项式回归、多输出回归、多输出K近邻回归、决策树回归、多输出决策树回归、AdaBoost回归、梯度提升决策树回归…

oracle数据库之使用Python程序调用存储过程(二十五)

在Oracle数据库中&#xff0c;你可以使用Python程序通过Oracle的数据库适配器&#xff08;如cx_Oracle&#xff09;来调用存储过程。以下是一个简单的步骤和示例代码&#xff0c;说明如何使用Python程序调用Oracle的存储过程&#xff1a; 1. 安装cx_Oracle库 首先&#xff0c…

UI(三)布局

文章目录 1、Colum和Row——垂直方向容器和水平方向容器2、ColumnSplit和RowSplit——子组件之间插入一条分割线3、Flex——弹性布局子组件的容器4、Grid和GridItem——网格容器和网格容器单元格5、GridRow和GridCol——栅格容器组件和栅格子组件6、List、ListItem、ListItemGr…

力扣每日一题 6/28 动态规划/数组

博客主页&#xff1a;誓则盟约系列专栏&#xff1a;IT竞赛 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 2742.给墙壁刷油漆【困难】 题目&#xff1a; 给你两个长度为 n 下标从 0…

密码学及其应用 —— 非对称加密/公匙密码技术

1 RSA加密算法 RSA加密算法是一种基于公钥密码学的加密技术&#xff0c;由罗纳德里维斯特&#xff08;Ron Rivest&#xff09;、阿迪萨莫尔&#xff08;Adi Shamir&#xff09;和伦纳德阿德曼&#xff08;Leonard Adleman&#xff09;在1977年共同发明。RSA算法是第一个既能用于…

C++ sizeof的各种

C sizeof的各种 1. 含有虚函数的类对象的空间大小2. 虚拟继承的类对象的空间大小3. 普通变量所占空间大小4. 复合数据类型&#xff08;结构体和类&#xff09;5. 数组6. 类型别名7. 动态分配内存8. 指针9. 静态变量10. 联合体11. 结构体使用#program pack 1. 含有虚函数的类对象…

如何用 php 实现邮件发送功能

一、使用 PHPMailer 发送邮件 1、需要先安装 PHPMailer 库。你可以使用 Composer 来安装它&#xff1a; composer require phpmailer/phpmailer2、安装完成后&#xff0c;可以创建一个简单的 PHP 脚本来发送邮件。以下是一个示例脚本&#xff1a; <?php use PHPMailer\P…

RuoYi_Cloud本地搭建

1.进入若依官网获取git地址 &#xff08;1&#xff09;百度搜“若依官网进”入如下界面 &#xff08;2&#xff09;点击进入git&#xff0c;点克隆下载 &#xff08;3&#xff09;复制http地址 2.在git链接在idea本地打开 &#xff08;1&#xff09;返回桌面——右键&#xf…

金属波纹管

金属波纹管是一种外型规则的波浪样的管材&#xff0c;常用的金属波纹管有碳钢的&#xff0c;和不锈钢的&#xff0c;也有钢质衬塑的、铝质的等等。这种管材主要用于需要很小的弯曲半径非同心轴向传动&#xff0c;或者不规则转弯、伸缩&#xff0c;或者吸收管道的热变形等&#…

大数据面试题之HBase(1)

目录 介绍下HBase HBase优缺点 说下HBase原理 介绍下HBase架构 HBase读写数据流程 HBase的读写缓存 在删除HBase中的一个数据的时候&#xff0c;它什么时候真正的进行删除呢?当你进行删除操作&#xff0c;它是立马就把数据删除掉了吗? HBase中的二级索引 HBa…

数据结构历年考研真题对应知识点(数组和特殊矩阵)

目录 3.4数组和特殊矩阵 3.4.2数组的存储结构 【二维数组按行优先存储的下标对应关系(2021)】 3.4.3特殊矩阵的压缩存储 【对称矩阵压缩存储的下标对应关系(2018、2020)】 【上三角矩阵采用行优先存储的应用(2011)】 【三对角矩阵压缩存储的下标对应关系(2016)】 3.4.…

python中Mysql的模糊查询

1.方法一&#xff1a;使用pymysql库的方法 当在Python中使用MySQL进行模糊查询时&#xff0c;我们通常会使用pymysql或mysql-connector-python这样的库来连接MySQL数据库并执行查询。以下是一个使用pymysql进行模糊查询的详细示例&#xff0c;包括安装库、连接数据库、执行查询…

为什么有的手机卡没有语音功能呢?

大家好&#xff0c;今天这篇文章为大家介绍一下&#xff0c;无通话功能的手机卡&#xff0c; 在网上申请过手机卡的朋友应该都知道&#xff0c;现在有这么一种手机卡&#xff0c;虽然是运营商推出的正规号卡&#xff0c;但是却屏蔽了通话功能&#xff0c;你知道这是为什么吗&am…

自组装mid360便捷化bag包采集设备

一、问题一&#xff1a;电脑太重&#xff0c;换nuc 采集mid360数据的过程中&#xff0c;发现了头疼的问题&#xff0c;得一手拿着电脑&#xff0c;一手拿着mid360来采集&#xff0c;实在是累胳膊。因此&#xff0c;网购了一个intel nuc, 具体型号是12wshi5000华尔街峡谷nuc12i…

何时使用查询字符串参数和路径参数_2024-06-28

REST API 已成为构建和使用 Web 服务的标准。在设计 REST API 时&#xff0c;一个重要方面是决定如何构造 URL 和参数。向 API 传递参数的两种常见方法是通过查询字符串参数和路径参数。本文将解释这两种方法之间的区别、何时使用每种方法&#xff0c;并提供示例来说明它们的用…