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

本文目录

  • 一、基础
    • 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;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《软件定义安全》之二:SDN/NFV环境中的安全问题

第2章 SDN/NFV环境中的安全问题 1.架构安全 SDN强调了控制平面的集中化&#xff0c;从架构上颠覆了原有的网络管理&#xff0c;所以SDN的架构安全就是首先要解决的问题。例如&#xff0c;SDN实现中网络控制器相关的安全问题。 1.1 SDN架构的安全综述 从网络安全的角度&…

Client does not support authentication protocol requested by server

连接mysql数据库报错 Client does not support authentication protocol requested by server; 打开命令行进入mysql mysql -uroot -p ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123456;FLUSH PRIVILEGES;重新连接成功

P1072 [NOIP2009 提高组] Hankson 的趣味题

Hankson 的趣味题 这题要有思维&#xff01;对。数论&#xff01;最大公约数与最小公倍数。 用LaTex写公式&#xff0c;真的麻烦&#xff01;wcnmd!,,,,,,be---- 于是我用手写了&#xff1a; 大功告成&#xff01;上马&#xff01; #include<cstdio> using namespace …

Python | Leetcode Python题解之第134题加油站

题目&#xff1a; 题解&#xff1a; class Solution:def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:start, cur_res, total_res 0, 0, 0for i in range(len(gas)):cur_res gas[i] - cost[i]total_res gas[i] - cost[i]if cur_res < 0:cur_r…

MFA 轰炸:苹果用户的攻击目标

一些 Apple (苹果) 用户报告了利用密码重置功能进行的网络钓鱼攻击。 你注意到 iPhone 上的系统提示你输入密码。你点击“不允许”。然后这种情况一次又一次地发生。 在某个时候&#xff0c;你可能会感到恼火或开始恐慌&#xff0c;然后点击“允许”。 然后&#xff0c;你接…

【LeetCode:312. 戳气球+ 动态规划】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

创建google cloud storage notification 的权限问题

问题 根据google 的文档&#xff1a; https://cloud.google.com/storage/docs/reporting-changes#command-line 明确表示&#xff0c; 要创建storage notificaiton &#xff0c; 创建者(or service account) 只需要bucket 和 pubsub admin roles 但是实际上我在公司尝试为1个…

《令狐带你阅读JDK源码之简单集合ArrayList》

文章目录 Java简单集合ArrayList继承体系源码解析 总结 大家好哈&#xff0c;欢迎来到令狐小哥本期专栏&#xff0c;这期专栏主要是带着大家阅读JDK源码&#xff0c;我会分几期篇幅来介绍这个jdk源码、会进行剖析、梳理&#xff0c;欢迎大家指正阅读。后面我会配套自己的视频进…

C 语言实现Linux终端显示IP二维码

调试信息&#xff1a;开发者可以在终端生成二维码&#xff0c;包含调试信息或日志数据&#xff0c;便于移动设备扫描和查看。设备配置&#xff1a;物联网设备配置时&#xff0c;通过终端生成配置二维码&#xff0c;扫描后进行设备配置。 Ubuntu/Debian 环境安装二维码库 sudo a…

超详解——python数字和运算——小白篇

目录 1.位运算 2. 常用内置函数/模块 math模块&#xff1a; random模块&#xff1a; decimal模块&#xff1a; 3.内置函数&#xff1a; 总结&#xff1a; 1.位运算 位运算是对整数在内存中的二进制表示进行操作。Python支持以下常见的位运算符&#xff1a; 按位与&…

python字典应用

""" 字典应用 字典中保存了股票信息&#xff0c;完成下面的操作 1.找出股票价格大于100元的股票并创建一个新的字典 2、找出价格最高和最低的股票对应的股票代码 3.按照股票价格从高到低给股票代码排序 """stocks {AAPL: 191.88,G00G: 1186.96,…

记一次postgresql拼接函数string_agg() 和row_number() 使用

PG两个函数使用需求和简单介绍 需求背景介绍第一个需求背景是这样的需求升级一下接下来讲讲STRING_AGG()基本语法排序 然后我们再说说ROW_NUMBER()基本语法使用 row_number() over (partition by) 进行分组统计使用 row_num限定每组数量 需求背景介绍 第一个需求背景是这样的 …

【传知代码】BLIP - VLP任务的新框架(论文复现)

前言&#xff1a;在当今人工智能与机器学习领域&#xff0c;视觉-语言预训练&#xff08;Vision-and-Language Pre-training, VLP&#xff09;任务正逐渐崭露头角&#xff0c;其对于推动跨模态智能系统的进步起着至关重要的作用。在这些系统中&#xff0c;图像与文本不再是孤立…

Python | Leetcode Python题解之第137题只出现一次的数字II

题目&#xff1a; 题解&#xff1a; class Solution:def singleNumber(self, nums: List[int]) -> int:a b 0for num in nums:b ~a & (b ^ num)a ~b & (a ^ num)return b

【vue实战项目】通用管理系统:图表功能

目录 前言 1.概述 2.数据概览页 2.1.柱状图 2.2.折线图 2.3.地图 前言 本文是博主前端Vue实战系列中的一篇文章&#xff0c;本系列将会带大家一起从0开始一步步完整的做完一个小项目&#xff0c;让你找到Vue实战的技巧和感觉。 专栏地址&#xff1a; https://blog.csd…

harbor1.7.1的访问报错502 bad gateway

背景&#xff1a; 在访问harbor镜像仓库时提示报错如下&#xff1a; 问题分析&#xff1a; 根据提供的报错内容来看时harbor服务的nginx组件服务异常了的&#xff0c;导致无法访问harbor服务&#xff0c;查看harbor服务结果如下&#xff1a; serviceharbor:~/harbor$ docker…

MicroPython esp32 连接wifi 配网

整体流程&#xff1a; 1&#xff09;开启STA 和 AP 模式 2&#xff09;扫描周围wifi 保存在 变量 wifi_list&#xff08;后面要用到&#xff09; 3) 尝试STA模式连接Wifi&#xff0c;并查寻状态。 4) 如果STA 无法连网&#xff0c;就用AP模式&#xff0c;创建热点。 5&a…

Vue数据动态代理机制的实现

Object.defineProperty() &#xff08;1&#xff09;这个方法是ES5新增的 &#xff08;2&#xff09;这个方法的作用是&#xff1a;给对象新增属性&#xff0c;或者设置对象原有的属性 &#xff08;3&#xff09;用法&#xff1a;Object.defineProperty(给哪个对象新增属性,‘…

【吊打面试官系列-Mysql面试题】BLOB 和 TEXT 有什么区别 ?

大家好&#xff0c;我是锋哥。今天分享关于 【BLOB 和 TEXT 有什么区别&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; BLOB 和 TEXT 有什么区别 &#xff1f; BLOB 是一个二进制对象&#xff0c;可以容纳可变数量的数据。TEXT 是一个不区分大小写的 BLOB。 1…

InfiniGate自研网关实现思路七

25.网关Nginx负载模型配置 通过模拟多个HTTP服务配置到 Nginx 做负载均衡&#xff0c;以学习API网关负载的配置和使用 API 网关是用于支撑分布式 RPC 接口协议转换提供 HTTP 调用的一套服务&#xff0c;那么 API 网关系统就需要可横向扩展来满足系统的吞吐量诉求。所以这里需…