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  0x77void 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 0xF4int 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.mzph.cn/news/850106.shtml

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

相关文章

transformers 阅读:BERT 模型

前言 想深入理解 BERT 模型&#xff0c;在阅读 transformers 库同时记录一下。 笔者小白&#xff0c;错误的地方请不吝指出。 Embedding 为了使 BERT 能处理大量下游任务&#xff0c;它的输入可以明确表示单一句子或句子对&#xff0c;例如<问题&#xff0c;答案>。 …

基本算法-枚举、模拟、递推(上)

目录 递归实现指数型枚举 题目描述 运行代码 代码思路 递归实现组合型枚举 题目描述 运行代码 代码思路 递归实现排列型枚举 题目描述 运行代码 代码思路 递归实现指数型枚举 题目描述 登录—专业IT笔试面试备考平台_牛客网 运行代码 #include<iostream> …

运动会信息管理系统(Springboot+MySQL)

本课题旨在实现对运动会信息的全面管理&#xff0c;提供用户友好的界面和高效的操作体验。系统的基础功能包括运动员报名比赛、比赛成绩查询、资讯留言等。为了确保系统的高扩展性和稳定性&#xff0c;选用主流的开发技术&#xff0c;实现规范的项目结构和高效的性能。 技术选型…

算法—字符串操作

394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:string longestCommonPrefix(vector<string>& strs) { string retstrs[0];//***1***记得先要初始化ret&#xff0c;作为第一个比较值for(int i0;i<strs.size();i){retfoundcom…

鸿蒙? 车载?Flutter? React Native? 为什么我劝你三思,说点不一样的

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 引言 当今信息技术领域日新月异&#xff0c;各种新技术和新平台层出不穷。鸿蒙&#xff08;HarmonyOS&#xff09;、Flutter、以及车载应用开发…

使用 Scapy 库编写 ICMP 不可达攻击脚本

一、介绍 ICMP不可达攻击是一种利用ICMP&#xff08;Internet Control Message Protocol&#xff09;不可达消息来干扰或中断目标系统的网络通信的攻击类型。通过发送伪造的ICMP不可达消息&#xff0c;攻击者可以诱使目标系统认为某些网络路径或主机不可达&#xff0c;从而导致…

前端开发高频面试题

好的&#xff0c;以下是对您提出的问题的详细回答&#xff1a; 说说vue动态权限绑定渲染列表&#xff08;权限列表渲染&#xff09; Vue中动态权限绑定渲染列表通常涉及以下步骤&#xff1a; 首先&#xff0c;通过API请求从服务器获取当前用户的权限数据。在Vue组件中&#xff…

Linux: ubi rootfs 故障案例 (1)

文章目录 1. 前言2. ubi rootfs 故障现场3. 故障分析与解决4. 参考资料 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. ubi rootfs 故障现场 问题故障内核日志如下&#xff1a; Starting ker…

btstack协议栈实战篇--GAP Link Key Management

btstack协议栈---总目录-CSDN博客 目录 1.GAP 链接密钥逻辑 2.蓝牙逻辑 3.主应用程序设置 4.log信息 展示了如何遍历存储在 NVS 中的经典链接密钥&#xff0c;链接密钥是每个设备-设备绑定的。如果蓝牙控制器可以交换&#xff0c;例如在桌面系统上&#xff0c;则每个控制器都需…

App UI 风格,引领时尚

App UI 风格&#xff0c;引领时尚

R语言探索与分析18-基于时间序列的汇率预测

一、研究背景与意义 汇率是指两个国家之间的货币兑换比率&#xff0c;而且在国家与国家的经济交流有着举足轻重的作用。随着经济全球化的不断深入&#xff0c;在整个全球经济体中&#xff0c;汇率还是一个评估国家与国家之间的经济状况和发展水平的一个风向标。汇率的变动会对…

【前端】响应式布局笔记——媒体查询

一、媒体查询 为不同尺寸的屏幕设定不同的css样式&#xff08;常用于移动端&#xff09;。 案例1 <style>.content{width: 400px;height: 400px;border: 1px solid;}media screen and (min-device-width:200px) and (min-device-width:300px) {.content{background: r…

零基础入门学用Arduino 第二部分(一)

重要的内容写在前面&#xff1a; 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。个人把这个教程学完之后&#xff0c;整体感觉是很好的&#xff0c;如果有条件的可以先学习一些相关课程&#xff0c;学起来会更加轻松&#xff0c;相关课程有数字电路…

构建智能汽车新质生产力丨美格智能亮相2024高通汽车技术与合作峰会

近日&#xff0c;以“我们一起&#xff0c;驭风前行”为主题的2024高通汽车技术与合作峰会在无锡国际会议中心隆重举行。作为高通公司的战略合作伙伴&#xff0c;美格智能受邀全程参与此次汽车技术与合作峰会。在峰会现场&#xff0c;美格智能产品团队隆重展示了多款基于高通平…

vscode中jupyter notebook执行bash命令,乱码解决方法

问题描述 使用vscode中使用jupyter notebook执行bash命令时,不管是中文还是英文,输出均是乱码 但是使用vscode的terminal执行同样的命令又没有问题,系统自带的cmd也没有问题。 最终解决后的效果如下: ## 问题分析 默认vscode会选择使用cmd执行shell, 但是通过vscode的设…

记一个ESP12-F芯片的坑

这两个都叫ESP-12F从外观上很难区分他们的差别&#xff0c;甚至背面的引脚都是一样的 这个单独的芯片就是从板子上拆下来的&#xff0c;使用这颗芯片按住FLASH按键LED灯会亮&#xff0c;很离谱&#xff0c;led灯的引脚是GPIO2 flash引脚是GPIO0&#xff0c;他们之间的内部封装…

PowerDesigner导入Excel模板生成数据表

PowerDesigner导入Excel模板生成数据表 1.准备好需要导入的Excel表结构数据,模板内容如下图所示 2.打开PowerDesigner,新建一个physical data model文件,填入文件名称,选择数据库类型 3.点击Tools|Execute Commands|Edit/Run Script菜单或按下快捷键Ctrl Shift X打开脚本窗口…

Day45 代码随想录打卡|二叉树篇---路径总和

题目&#xff08;leecode T112&#xff09;&#xff1a; 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;…

Redis-sentinel(哨兵模式)的搭建步骤及相关知识

1、什么是redis-sentinel&#xff0c;和redis主从复制相比&#xff0c;它具有什么优势 1.1、redis主从复制 Redis主从复制是一种用于数据冗余和可伸缩性的机制&#xff0c;它将一台Redis服务器的数据复制到其他Redis服务器。在这种模式下&#xff0c;数据会实时地从一个主节点…

Polar Web【中等】反序列化

Polar Web【中等】反序列化 Contents Polar Web【中等】反序列化思路&探索EXPPHP生成PayloadGET传递参数 运行&总结 思路&探索 一个经典的反序列化问题&#xff0c;本文采用PHP代码辅助生成序列字符串的方式生成 Payload 来进行手动渗透。 打开站点&#xff0c;分析…