Linux驱动应用编程(四)IIC(获取BMP180温度/压力数据)

news2024/11/28 0:44:39

本文目录

  • 一、基础
    • 1. 查看开发板手册,获取可用IIC总线
    • 2. 挂载从机,查看从机地址。
    • 3. 查看BMP180手册,使用命令读/写某寄存器值。
    • 4. 查看BMP180手册通信流程。
  • 二、IIC常用API
    • 1. iic数据包/报
    • 2. ioctl函数
  • 三、数据包如何被处理
  • 四、代码编写流程
    • 1. IIC读取数据
    • 2. IIC写入数据
    • 3. 读取校验参数
    • 4. 读取未校准的温度值
    • 5. 读取未校准的压力值
    • 6. 将未校准的测量值转为真实值
  • 五、完整代码

  
   在 Linux ARM 平台上使用 I2C 时,不需要手动编写 I2C 时序是因为 Linux 内核和硬件抽象层已经处理了这些复杂的细节,提供了高层次的接口供开发者使用。
  无论是哪个IIC从机设备,我们只需要实现 IIC读数据和IIC写数据即可。然后根据不同设备的手册规则来向寄存器写或者读数据,从而实现某些特定的功能。

一、基础

1. 查看开发板手册,获取可用IIC总线

在这里插入图片描述

   香橙派OrangepiAipor引脚只有两个IIC可以使用,分别是IIC6和IIC7。分别对应如下两个设备节点。
在这里插入图片描述
  

2. 挂载从机,查看从机地址。

   我们将从机设备随便连接到IIC的其中一个上面,这里我们使用BMP180作为从机设备连接香橙派的IIC7总线,对应的设备节点为i2c-7。我们可以使用命令来查看IIC总线7上挂载的设备的地址。命令:i2cdetect -y -r 7。这样我们就获得了从机的地址(当然可以查看BMP180手册获得)。
在这里插入图片描述
  

3. 查看BMP180手册,使用命令读/写某寄存器值。

BMP180手册中寄存器地址分布如下:在这里插入图片描述

●读取 i2c-7 总线上,从机设备地址为0x77 ,寄存器地址为 0xF7上的值。

i2cget -y 7 0x77 0xF7

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

●写入i2c-7 总线上,从机设备地址为0x77 ,寄存器地址为 0xF4,值为0X2E。

i2cset 7 0x77 0xF4 0x2E

  

4. 查看BMP180手册通信流程。

   我们可以发现,通信大致流程就是往寄存器里写值,然后读取寄存器的值。将获取的值通过公式转换为真实的温度/压力值。

在这里插入图片描述

二、IIC常用API

1. iic数据包/报

头文件:#include <linux/i2c.h>

●数据包:
   在 Linux 内核中,struct i2c_msg 结构体用于描述 I2C 消息,这是在 I2C 总线上传输的数据块。它被用作 ioctl 调用的一部分,通过 I2C_RDWR 命令进行 I2C 读写操作。

struct i2c_msg {
    __u16 addr;   /* 从设备地址 */
    __u16 flags;  /* 消息标志,写:0, 读:1 */
    __u8 *buf;    /* 数据缓冲区。对于写操作,这里存储的是要发送的数据;对于读操作,这里存储的是接收到的数据。 */
    __u16 len;    /* 数据缓冲区长度 */
};

●数据报:包含多个数据包。

struct i2c_rdwr_ioctl_data {
    struct i2c_msg *msgs;  /* 指向 I2C 消息数组的指针 */
    __u32 nmsgs;           /* 消息的数量 */
};

2. ioctl函数

   是一个系统调用,专门用来让程序与设备进行通信。它有点像是一个“万能”函数,通过它可以向设备发送各种控制命令或者配置设备的某些参数。具体使用看下面的内容理解。相当于将消息报传给设备。

int ioctl(int fd, unsigned long request, ...);
//int fd :设备的文件描述符。
//unsigned long request:请求,例如可读可写等。
//...(可变参数):根据 request 的不同,ioctl 可能需要一个或多个额外的参数。这些参数的类型和数量取决于具体的控制命令。

三、数据包如何被处理

当数据包传输到设备时,设备的硬件或驱动程序会根据I2C协议进行解析和处理。

  1. 设备地址识别: 首先,设备会检查数据包中的地址字段,以确定它是否是被设备所识别的地址。如果是,则设备将继续处理数据包;如果不是,则设备会忽略该数据包。

  2. 数据包解析: 设备会根据数据包的格式进行解析。对于写数据包,设备会读取数据包中的数据内容,并根据寄存器地址将这些数据写入到对应的寄存器中。对于读数据包,设备会从指定的寄存器中读取数据,并将这些数据放置在响应数据包的缓冲区中。

  3. 数据处理: 一旦数据被写入或读取,设备可能会执行相应的操作。这可能包括修改设备内部的状态、更新设备的寄存器值、执行特定的功能等。

四、代码编写流程

   在IIC通信中,我们最主要的就是写出IIC读和IIC写的函数。无论是读还是写,我们在与设备通信时传输的第一个字节必须是要操作的寄存器的地址,因为数据的读写通常是通过向设备发送特定的寄存器地址来触发的。

1. IIC读取数据

   传入打开的IIC设备文件描述符、要读取的从机设备地址、 要读取的寄存器地址、将数据读取到哪、读多少。

/*
uint8_t slave_addr :从机地址
uint8_t reg_addr :要读取的寄存器
uint8_t* buffer:读取的数据存在哪
int length:读取的长度
*/
int iic_read(int fd, uint8_t slave_addr, uint8_t reg_addr, uint8_t* buffer, int length)
{
    struct i2c_msg msgs[2]; //读数据包以及写数据包
    struct i2c_rdwr_ioctl_data pack; //数据报
    int ret;

//第一个数据包:用于向目标设备发送要操作的寄存器地址。
    msgs[0].addr  = slave_addr;
    msgs[0].flags = 0;     // 写方向
    msgs[0].buf   = &reg_addr;  
    msgs[0].len   = sizeof(reg_addr);
    
//第二个数据包:表明是读取寄存器的内容。
    msgs[1].addr  = slave_addr;
    msgs[1].flags = 1;    // 读方向
    msgs[1].buf   = buffer;  //从寄存器读取的内容存到buffer中。
    msgs[1].len   = length; //读取的长度

    pack.msgs = msgs;
    pack.nmsgs = 2;//

//ioctl函数将消息报 pack 发送给指定的设备。
    ret = ioctl(fd, I2C_RDWR, &pack);
    if (ret < 0) {
        perror("ioctl I2C_RDWR failed");
        return -1;
    }

    return 0;
}

2. IIC写入数据

   传入打开的IIC设备文件描述符、要写入的从机设备地址、 要写入的寄存器地址、写什么数据、写多少。

   注意:这里我们要将寄存器地址和写入的数据放到一个数据包中传输。即第一个字节为寄存器地址,后面为传输的值。具体为什么使用一个数据包而不是两个,原因不太清楚,我使用两个数据包传输时,有问题。

int iic_write(int fd, uint8_t slave_addr, uint8_t reg_addr, uint8_t* data, int length)
{
    struct i2c_rdwr_ioctl_data pack;
    struct i2c_msg msg;
    uint8_t buffer[length + 1]; // 为了包含寄存器地址,需要额外的空间
    int ret;

    buffer[0] = reg_addr; // 将寄存器地址作为第一个字节

    // 将要写入的数据拷贝到缓冲区中
    memcpy(buffer + 1, data, length);

    msg.addr   = slave_addr;
    msg.flags  = 0; // 写方向
    msg.len    = length + 1; // 包含了寄存器地址
    msg.buf    = buffer;

    pack.msgs  = &msg;
    pack.nmsgs = 1;

    ret = ioctl(fd, I2C_RDWR, &pack);
    if (ret < 0) {
        perror("ioctl I2C_RDWR failed");
        return -1;
    }

    return 0;
}

3. 读取校验参数

在这里插入图片描述

   由于在Linux-arm下是大端序,则先读取高位,再读低位。因为每个数据占两个字节,所以要将这两个字节的数据进行位移操作来合并为一个数据。

#define Slave_Addr  0x77

void read_calibration_data(int fd, int16_t* AC1, int16_t* AC2, int16_t* AC3, 
							uint16_t* AC4, uint16_t* AC5, uint16_t* AC6, int16_t* B1, 
							int16_t* B2, int16_t* MB, int16_t* MC, int16_t* MD) 
{
    uint8_t buffer[22];  //每个数据占2个字节,共有11个数据。
    iic_read(fd, Slave_Addr , 0xAA,  buffer, 22);
    *AC1 = (buffer[0] << 8) | buffer[1];
    *AC2 = (buffer[2] << 8) | buffer[3];
    *AC3 = (buffer[4] << 8) | buffer[5];
    *AC4 = (buffer[6] << 8) | buffer[7];
    *AC5 = (buffer[8] << 8) | buffer[9];
    *AC6 = (buffer[10] << 8) | buffer[11];
    *B1 = (buffer[12] << 8) | buffer[13];
    *B2 = (buffer[14] << 8) | buffer[15];
    *MB = (buffer[16] << 8) | buffer[17];
    *MC = (buffer[18] << 8) | buffer[19];
    *MD = (buffer[20] << 8) | buffer[21];
}


//下面内容只是为了演示如何使用而已。
int main()
{
	int16_t AC1, AC2, AC3, B1, B2, MB, MC, MD;
    uint16_t AC4, AC5, AC6;
   // 读取校准数据
    read_calibration_data(fd, &AC1, &AC2, &AC3, &AC4, &AC5, &AC6, &B1, &B2, &MB, &MC, &MD);
}

4. 读取未校准的温度值

在这里插入图片描述

由于在Linux-arm下是大端序,则先读取高位,再读低位。

#define  Slave_Addr  0x77
#define  Data_Out_MSB 0xF6
#define  Data_Out_LSB 0xF7

#define  Tempture_Pressure_reg  0xF4
// 启动温度测量

int main()
{
	uint8_t send_data[1];
	uint8_t receive_data[2];
	int32_t raw_temp;  //未校准的温度数据。
	send_data[0] = 0x2e;  //要写入的数据

    if (iic_write(fd, Slave_Addr, Tempture_Pressure_reg , send_data,1) <0) {  //开始测量温度
        perror("iic_write error");
        close(fd);
        return -1;
    }
    usleep(4500); // 等待测量完成

    // 读取未校准的温度数据
    if (iic_read(fd, Slave_Addr, Data_Out_MSB , receive_data, 2) < 0) {
        perror("iic_read error");
        close(fd);
        return -1;
    }
    raw_temp= receive_data[0]<<8|receive_data[1];  //未校准的温度值
}

5. 读取未校准的压力值

在这里插入图片描述
由于在Linux-arm下是大端序,则先读取高位,再读低位。

#define  Slave_Addr  0x77
#define  Data_Out_MSB 0xF6
#define  Data_Out_LSB 0xF7
#define  Data_Out_XLSB 0xF8
#define Tempture_Pressure_reg 0xF4
// 启动温度测量

/*
通常,"oss" 的取值范围在 0 到 3 之间,代表不同的过采样率。具体取值对应的过采样率取决于传感器型号和制造商的实现。在 BMP180 中,oss 的取值对应着以下过采样率:
  	oss = 0: 单次采样
  	oss = 1: 2 倍过采样
  	oss = 2: 4 倍过采样
  	oss = 3: 8 倍过采样
本文采用单次采样即可。
*/
int main()
{
  uint8_t send_data[1];
  uint8_t receive_data[3];
  int32_t raw_pressure;  //未校准的压力数据。
  send_data[0] = 0x34;  //要写入的数据

  if (iic_write(fd, Slave_Addr, Tempture_Pressure_reg , send_data,1) <0) {  //开始测量压力
      perror("iic_write error");
      close(fd);
      return -1;
  }
  usleep(4500); // 等待测量完成

  // 读取未校准的压力数据
  if (iic_read(fd, Slave_Addr, Data_Out_MSB , receive_data, 3) < 0) {
      perror("iic_read error");
      close(fd);
      return -1;
  }
  raw_pressure =(receive_data[0]<<16|receive_data[1]<<8|receive_data[0]) >>8;  //未校准的压力值
}

6. 将未校准的测量值转为真实值

在这里插入图片描述

注意:代码中的右移多少位就相当于乘了2的几次方。左移相当于除。

//这里的参数很多都是校准参数。
void calculate_true_values(int32_t raw_temp, int32_t raw_pressure, int32_t* true_temp, int32_t* true_pressure, int16_t AC1, int16_t AC2, int16_t AC3, uint16_t AC4, uint16_t AC5, uint16_t AC6, int16_t B1, int16_t B2, int16_t MB, int16_t MC, int16_t MD) {
    int32_t X1, X2, X3, B3, B5, B6, B7, p;
    uint32_t B4;

    // 温度计算,
    X1 = (raw_temp - AC6) * AC5 >> 15;
    X2 = (MC << 11) / (X1 + MD);
    B5 = X1 + X2;
    *true_temp = (B5 + 8) >> 4;

    // 压力计算
    B6 = B5 - 4000;
    X1 = (B2 * (B6 * B6 >> 12)) >> 11;
    X2 = AC2 * B6 >> 11;
    X3 = X1 + X2;
    B3 = (((AC1 * 4 + X3) << 1) + 2) >> 2;
    X1 = AC3 * B6 >> 13;
    X2 = (B1 * (B6 * B6 >> 12)) >> 16;
    X3 = ((X1 + X2) + 2) >> 2;
    B4 = AC4 * (uint32_t)(X3 + 32768) >> 15;
    B7 = ((uint32_t)raw_pressure - B3) * (50000 >> 1);
    if (B7 < 0x80000000) {
        p = (B7 * 2) / B4;
    } else {
        p = (B7 / B4) * 2;
    }
    X1 = (p >> 8) * (p >> 8);
    X1 = (X1 * 3038) >> 16;
    X2 = (-7357 * p) >> 16;
    *true_pressure = p + ((X1 + X2 + 3791) >> 4);
}


五、完整代码

iic.c

#include <stdint.h>
#include <string.h>
#include "iic.h"

int iic_init(const char *device)
{            
   return open(device,O_RDWR);  //可读可写   
}

int iic_close(int fd)
{
   return close(fd);
}

/*
uint8_t slave_addr :从机地址
uint8_t reg_addr :要读取的寄存器
uint8_t* buffer:读取的数据存在哪
int length:读取的长度
*/
int iic_read(int fd, uint8_t slave_addr, uint8_t reg_addr, uint8_t* buffer, int length)
{
    struct i2c_msg msgs[2]; //读数据包以及写数据包
    struct i2c_rdwr_ioctl_data pack; //数据报
    int ret;

//第一个数据包:用于向目标设备发送要操作的寄存器地址。
    msgs[0].addr  = slave_addr;
    msgs[0].flags = 0;     // 写方向
    msgs[0].buf   = &reg_addr;  
    msgs[0].len   = sizeof(reg_addr);
    
//第二个数据包:表明是读取寄存器的内容。
    msgs[1].addr  = slave_addr;
    msgs[1].flags = 1;    // 读方向
    msgs[1].buf   = buffer;  //从寄存器读取的内容存到buffer中。
    msgs[1].len   = length; //读取的长度

    pack.msgs = msgs;
    pack.nmsgs = 2;//

//ioctl函数将消息报 pack 发送给指定的设备。
    ret = ioctl(fd, I2C_RDWR, &pack);
    if (ret < 0) {
        perror("ioctl I2C_RDWR failed");
        return -1;
    }

    return 0;
}

//IIC写数据
int iic_write(int fd, uint8_t slave_addr, uint8_t reg_addr, uint8_t* data, int length)
{
    struct i2c_rdwr_ioctl_data pack;
    struct i2c_msg msg;
    uint8_t buffer[length + 1]; // 为了包含寄存器地址,需要额外的空间
    int ret;

    buffer[0] = reg_addr; // 将寄存器地址作为第一个字节

    // 将要写入的数据拷贝到缓冲区中
    memcpy(buffer + 1, data, length);

    msg.addr   = slave_addr;
    msg.flags  = 0; // 写方向
    msg.len    = length + 1; // 包含了寄存器地址
    msg.buf    = buffer;

    pack.msgs  = &msg;
    pack.nmsgs = 1;

    ret = ioctl(fd, I2C_RDWR, &pack);
    if (ret < 0) {
        perror("ioctl I2C_RDWR failed");
        return -1;
    }

    return 0;
}

//读取校准参数
void read_calibration_data(int fd, int16_t* AC1, int16_t* AC2, int16_t* AC3, uint16_t* AC4, uint16_t* AC5, uint16_t* AC6, int16_t* B1, int16_t* B2, int16_t* MB, int16_t* MC, int16_t* MD) {
    uint8_t buffer[22];
    iic_read(fd, Slave_Addr, 0xAA, buffer, 22);
    *AC1 = (buffer[0] << 8) | buffer[1];
    *AC2 = (buffer[2] << 8) | buffer[3];
    *AC3 = (buffer[4] << 8) | buffer[5];
    *AC4 = (buffer[6] << 8) | buffer[7];
    *AC5 = (buffer[8] << 8) | buffer[9];
    *AC6 = (buffer[10] << 8) | buffer[11];
    *B1 = (buffer[12] << 8) | buffer[13];
    *B2 = (buffer[14] << 8) | buffer[15];
    *MB = (buffer[16] << 8) | buffer[17];
    *MC = (buffer[18] << 8) | buffer[19];
    *MD = (buffer[20] << 8) | buffer[21];
}

//计算真实值
void calculate_true_values(int32_t raw_temp, int32_t raw_pressure, int32_t* true_temp, int32_t* true_pressure, int16_t AC1, int16_t AC2, int16_t AC3, uint16_t AC4, uint16_t AC5, uint16_t AC6, int16_t B1, int16_t B2, int16_t MB, int16_t MC, int16_t MD) {
    int32_t X1, X2, X3, B3, B5, B6, B7, p;
    uint32_t B4;

    // 温度计算
    X1 = (raw_temp - AC6) * AC5 >> 15;
    X2 = (MC << 11) / (X1 + MD);
    B5 = X1 + X2;
    *true_temp = (B5 + 8) >> 4;

    // 压力计算
    B6 = B5 - 4000;
    X1 = (B2 * (B6 * B6 >> 12)) >> 11;
    X2 = AC2 * B6 >> 11;
    X3 = X1 + X2;
    B3 = (((AC1 * 4 + X3) << 1) + 2) >> 2;
    X1 = AC3 * B6 >> 13;
    X2 = (B1 * (B6 * B6 >> 12)) >> 16;
    X3 = ((X1 + X2) + 2) >> 2;
    B4 = AC4 * (uint32_t)(X3 + 32768) >> 15;
    B7 = ((uint32_t)raw_pressure - B3) * (50000 >> 1);
    if (B7 < 0x80000000) {
        p = (B7 * 2) / B4;
    } else {
        p = (B7 / B4) * 2;
    }
    X1 = (p >> 8) * (p >> 8);
    X1 = (X1 * 3038) >> 16;
    X2 = (-7357 * p) >> 16;
    *true_pressure = p + ((X1 + X2 + 3791) >> 4);
}

iic.h

#ifndef __IIC_H
#define __IIC_H

#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>

#define  Slave_Addr 0x77
#define  Data_Out_MSB 0xF6
#define  Data_Out_LSB 0xF7
#define  Data_Out_XLSB 0xF8
#define  Tempture_Pressure_reg 0xF4


int iic_init(const char *device);
int iic_close(int fd);
int iic_read(int fd, uint8_t slave_addr, uint8_t reg_addr, uint8_t* buffer, int length);
int iic_write(int fd, uint8_t slave_addr, uint8_t reg_addr, uint8_t* data, int length);
void read_calibration_data(int fd, int16_t* AC1, int16_t* AC2, int16_t* AC3, uint16_t* AC4, uint16_t* AC5, uint16_t* AC6, int16_t* B1, int16_t* B2, int16_t* MB, int16_t* MC, int16_t* MD);
void calculate_true_values(int32_t raw_temp, int32_t raw_pressure, int32_t* true_temp, int32_t* true_pressure, int16_t AC1, int16_t AC2, int16_t AC3, uint16_t AC4, uint16_t AC5, uint16_t AC6, int16_t B1, int16_t B2, int16_t MB, int16_t MC, int16_t MD) ;
#endif

main.c

#include <stdio.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdint.h>
#include <linux/i2c.h>
#include <string.h>
#include "iic.h"



int main() {
    int fd;
    int32_t raw_temp, raw_pressure, true_temp, true_pressure;
    int16_t AC1, AC2, AC3, B1, B2, MB, MC, MD;
    uint16_t AC4, AC5, AC6;
	uint8_t send_data[1];
	uint8_t receive_temp[2];
	uint8_t receive_pressure[3];
	
	
    // 打开I2C设备
    fd = iic_init("/dev/i2c-7");
    if (fd < 0) {
        perror("iic_init error");
        return -1;
    }

    // 读取校准数据
    read_calibration_data(fd, &AC1, &AC2, &AC3, &AC4, &AC5, &AC6, &B1, &B2, &MB, &MC, &MD);

    //开始测量温度 
    send_data[0] = 0x2e;  //要写入的数据
    if (iic_write(fd, Slave_Addr, Tempture_Pressure_reg , send_data, 1) <0) { 
        perror("iic_write error");
        close(fd);
        return -1;
    }
    usleep(4500); // 等待测量完成

    // 读取未校准的温度数据
    if (iic_read(fd, Slave_Addr, Data_Out_MSB , receive_temp, 2) < 0) {
        perror("iic_read error");
        close(fd);
        return -1;
    }
    raw_temp= receive_temp[0]<<8|receive_temp[1];  //未校准的温度值
    
	
	
	send_data[0] = 0x34;  //要写入的数据
    if (iic_write(fd, Slave_Addr, Tempture_Pressure_reg , send_data, 1) <0) {  //开始测量压力
        perror("iic_write error");
        close(fd);
        return -1;
    }
    usleep(4500); // 等待测量完成

    // 读取未校准的压力数据
    if (iic_read(fd, Slave_Addr, Data_Out_MSB , receive_pressure, 3) < 0) {
        perror("iic_read error");
        close(fd);
        return -1;
    }
    raw_pressure =(receive_pressure[0]<<16|receive_pressure[1]<<8|receive_pressure[0]) >>8;  //未校准的压力值
  
    // 计算实际温度和压力
    calculate_true_values(raw_temp, raw_pressure, &true_temp, &true_pressure, AC1, AC2, AC3, AC4, AC5, AC6, B1, B2, MB, MC, MD);

    // 输出实际温度和压力
    printf("True Temperature: %.2f C\n", true_temp / 10.0);
    printf("True Pressure: %.2f hPa\n", true_pressure / 100.0);

    // 关闭I2C设备
    close(fd);
    return 0;
}

在这里插入图片描述

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

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

相关文章

(2024,Vision-LSTM,ViL,xLSTM,ViT,ViM,双向扫描)xLSTM 作为通用视觉骨干

Vision-LSTM: xLSTM as Generic Vision Backbone 公和众与号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2 方法 3 实验 3.1 分类设计 4 结论 0. 摘要 Transformer 被广泛用作计算…

FM148R,FM147A和利时卡件

FM148R,FM147A和利时卡件。软件组成及各部分功能软件组成---各组件功能注意事项&#xff1a;仿真功能&#xff1a;仿真系统可以用于在单机上对组态完成的工程内容进行模拟运行。FM148R,FM147A和利时卡件。便于对这些组态内容的正确性和合理性进行初步调试。二、FM148R,FM147A和…

【Vue】面经基础版-案例效果分析

面经效果演示 功能分析 通过演示效果发现&#xff0c;主要的功能页面有两个&#xff0c;一个是列表页&#xff0c;一个是详情页&#xff0c;并且在列表页点击时可以跳转到详情页底部导航可以来回切换&#xff0c;并且切换时&#xff0c;只有上面的主题内容在动态渲染 实现思路…

C++基础四:C++模板编程

目录 一:函数模板 二:类模板 空间配置器allocator 一:函数模板 模板代码只能同一实现,不能先声明,再在另一文件实现,模板代码都是放在头文件当中的,在头文件中直接实现 二:类模板 template<typename T=int> class SeqStack // 模板名称+类型参数列表 = 类名称…

8.3 Go 包的组织结构

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

list模拟与实现(附源码)

文章目录 声明list的简单介绍list的简单使用list中sort效率测试list的简单模拟封装迭代器insert模拟erase模拟头插、尾插、头删、尾删模拟自定义类型迭代器遍历const迭代器clear和析构函数拷贝构造&#xff08;传统写法&#xff09;拷贝构造&#xff08;现代写法&#xff09; 源…

LabVIEW控制PLC的实现方式

LabVIEW与PLC的结合可以充分发挥两者的优点&#xff0c;实现更高效、灵活和可靠的自动化控制系统。本文将详细介绍LabVIEW控制PLC的实现方式&#xff0c;包括通信接口、数据交换、编程方法及实际应用案例&#xff0c;帮助用户理解并应用这一技术。 通信接口 常见通信协议 La…

Swift 序列(Sequence)排序面面俱到 - 从过去到现在(二)

概览 在上篇 Swift 序列(Sequence)排序面面俱到 - 从过去到现在(一)博文中,我们讨论了 Swift 语言中序列和集合元素排序的一些基本知识,我们还给出了以自定义类型中任意属性排序的“康庄大道”。 不过在实际的撸码场景中,我们往往需要的是“多属性”同时参与到排序的考…

Unity HDRP水系统

1.开启水系统 Script Interactions启用后&#xff0c;HDRP会为CPU上的水模拟分配内存。这允许您在水面上启用CPU模拟来查询高度信息。 2.Sky and Fog Volume中添加Water Rendering并勾选State 3.点击GameObject选择WaterSurface&#xff0c;选择要添加的水类型&#xff08;海…

【Vue】vuex 的使用 - 创建仓库

通用的地方我们一般会称之为仓库 1.安装 vuex 安装vuex与vue-router类似&#xff0c;vuex是一个独立存在的插件&#xff0c;如果脚手架初始化没有选 vuex&#xff0c;就需要额外安装。 yarn add vuex3 或者 npm i vuex32.新建 store/index.js 专门存放 vuex ​ 为了维护项目…

基于协调过滤算法商品推荐系统的设计#计算机毕业设计

基于协调过滤算法商品推荐系统的设计#计算机毕业设计 管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;商品管理&#xff0c;论坛管理&#xff0c;商品资讯管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;论坛&#xf…

HIP的应用可移植性

Application portability with HIP — ROCm Blogs (amd.com) 许多科学应用程序在配备AMD的计算平台和超级计算机上运行&#xff0c;包括Frontier&#xff0c;这是世界上第一台Exascale系统。这些来自不同科学领域的应用程序通过使用Heterogeneous-compute Interface for Portab…

Electron qt开发教程

模块安装打包 npm install -g electron-forge electron-forge init my-project --templatevue npm start //进入目录启动 //打包成一个目录到out目录下&#xff0c;注意这种打包一般用于调试&#xff0c;并不是用于分发 npm run package //打出真正的分发包&#xff0c;放在o…

FJSP:烟花算法(FWA)求解柔性作业车间调度问题(FJSP),提供MATLAB代码

一、烟花算法介绍 参考文献&#xff1a; Tan, Y. and Y. Zhu. Fireworks Algorithm for Optimization. in Advances in Swarm Intelligence. 2010. Berlin, Heidelberg: Springer Berlin Heidelberg. 二、烟花算法求解FJSP 2.1FJSP模型介绍 柔性作业车间调度问题(Flexible …

在VMware虚拟机上安装win10 跳过 通过microsoft登录

在VMware虚拟机上安装win10 跳过 “通过microsoft登录” 配置虚拟机&#xff0c;将网卡断开&#xff0c; 具体操作&#xff1a; 虚拟机/设置/硬件/网络适配器/设备状态&#xff0c;取消已连接和启动时连接的两个对号&#xff0c; 再把虚拟机重启&#xff0c;然后就可以跳过这个…

Type-C转音频(C/3.5mm接口USB2.0数据传输)带PD充电低成本解决方案

LDR6500&#xff1a;领先市场的USB-C DRP接口USB PD通信芯片 产品介绍 LDR6500&#xff0c;由乐得瑞科技精心研发&#xff0c;是一款针对USB Type-C标准中Bridge设备而优化的USB-C DRP&#xff08;Dual Role Port&#xff0c;双角色端口&#xff09;接口USB PD&#xff08;Po…

【原创】springboot+mysql农业园区管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

Redis进阶知识个人汇总

持久化 三种方式实现它的持久化&#xff1a; RDB持久化 全称Redis数据备份文件&#xff0c;又称Redis数据快照 这种就是将Redis内存中所有数据记录到磁盘中&#xff0c;当实例出故障后&#xff0c;从磁盘中读快照文件进行恢复数据。 一般使用bgsave指令实现 复制主线程得到一…

五分钟上手IoT小程序

五分钟上手IoT小程序 IoT小程序框架搭建开发环境首先安装NodeJs安装NodeJs验证安装成功 安装cnpm 安装VSCode 开发IDE下载开发IDE安装开发IDE安装框架脚手架 下载模拟器创建工程项目应用编译(打包构建) VSCode 开发IDE安装插件通过开发插件创建工程编译工程debug编译编译太慢问…

01Linux的安装,时区,固定IP的配置

Linux系统的简介与安装 Linux简介 计算机是由硬件和软件所组成 硬件&#xff1a;计算机系统中由电子,机械和光电元件等组成的各种物理装置的总称软件&#xff1a;是用户和计算机硬件之间的接口和桥梁&#xff0c;用户通过软件与计算机进行交流(操作系统) 操作系统作为用户和…