外设驱动库开发笔记21:BME680环境传感器驱动

2023-05-16

环境传感器是一类我们很常用的传感器。它可以方便我们获取压力、温度、湿度以及空气质量等数据。在这一篇中,我们将分析BME680环境传感器的功能,并设计和实现BME680环境传感器的驱动。

1、功能概述

BME680是一款专为移动应用和可穿戴设备开发的集成环境传感器,其尺寸和低功耗是关键要求。

1.1、硬件接口

BME680由一个8针金属盖3.0 x 3.0 x0.93mm³LGA封装组成,旨在根据特定的工作模式,长期稳定性和高EMC稳健性进行优化消耗。可以选择采用I2C接口或者SPI接口。其管脚排布如下图:

BME680环境传感器可以选择使用I2C接口或者SPI接口,在不同的接口模式及下各个引脚的定义及功能有一些差别。其具体分配及定义如下所示:

从上表中我们可以知道当CSB引脚接高电平VDDIO时,采用的是I2C接口。此时I2C的设备地址的最后一位由SDO引脚的电平决定。所以设备地址计7位为0x760x77,计8位则是0xEC0xEE

CSB引脚用作片选信号时,则使用SPI接口。SPI接口支持模式0CPOL=0CPHA=0)和模式3CPOL=1CPHA=1)。同时支持3线SPI4线SPI。控制字节的最高位为0时表示写,为1时表示读。

1.2、内置传感器

BME680扩展了博世现有的环境传感器系列,首次集成了高线性度和高精度的气体,压力,湿度和温度传感器。

1.2.1、气体传感器

BME680内的气体传感器可以检测各种气体,以测量个人健康的空气质量。BME680可检测到的气体包括油漆(如甲醛),油漆,脱漆剂,清洁用品,家具等的挥发性有机化合物(VOC)。大气质量传感器的特性参数如下:

BME680采用了博世软件环境群组解决方案。该解决方案使用智能算术方法将空气质量索引(IAQ)作为输出。该指标将IAQ划分为0500的索引数值用以指示IAQ,具体划分如下所示:

1.2.2、湿度传感器

BME680集成了湿度传感器用于外部环境中湿度数据的采集。湿度传感器的性能参数如下:

1.2.3、压力传感器

BME680集成有大气压力传感器用于检测外部环境的绝对压力。压力传感器的性能参数如下:

1.2.4、温度传感器

BME680也集成了温度传感器用以检测温度数据,温度数据除了指示环境温度外,同时用于压力和湿度的补偿计算。温度传感器的性能参数如下:

1.3、数据存储结构

BME680采用特定的存储器区域来存储控制及数据信息。存储的数据包括测量数据、控制信息以及校准数据。

对于温度传感器,包括3个校准参数和一个ADC测量数据,其测量数据和校准数据的存储结构及地址如下:

对于压力传感器,包括10个计算校准数据和一个ADC转换数据,其测量数据的校准数据存储结构及地址如下:

对于湿度传感器,包括7个计算校准数据和一个ADC转换数据,其测量数据的校准数据存储结构及地址如下:

大气质量传感器,包括3个计算校准数据、一个加热器范围存储数据、一个加热器电阻校准因子存储数据、气体ADC测量数据、气体范围数据以及范围转换错误,其测量数据的校准数据存储结构及地址如下:

BME680环境传感器寄存器都是8位的,所有的操作均通过对寄存器的读写来实现。全部控制寄存器及数据寄存器的结构和地址如下:

这里我们需要说明一下,BME680的存储器地址范围是0x00~0xFF,在I2C接口通讯时,通讯采用的是8位寄存器地址正好符合对应的寻址范围。但是采用SPI接口通讯时,寄存器地址的最高为被用于区分读写操作,所以地址只有7位,存储空间被分为2页。具体如下:

所以在使用SPI接口时需要分辨是哪一页。当前操作的是哪一页由Status寄存器来决定。

2、驱动设计与实现

我们对BME680环境传感器的基本情况已经有了整体了解,接下来我们将为BME680环境传感器设计并实现驱动程序。

2.1、对象定义

我们依然是采用基于对象的操作。所以我们需要定义对象,所以我们需要抽象出对象类型,并对我们想要操作的对象进行初始化。

2.1.1、对象抽象

对于一个对象来说,一般包括有属性和操作两方面的内容。接下来我们就从这两个方面分析BME680环境传感器的对象。

我们需要从BME680对象抽象出其属性,这些属性能够定义一个对象的特点并将其与其它对象区别开来。BME680支持SPI通讯和I2C通讯,所以我们将通讯端口作为属性以规定对象的通讯方式。在使用I2C时,设备有地址以区别不同的设备,所以我们将I2C设备地址也定义为属性。每台BME680都有一个ID用以区别于其它设备,所以我们将它定义为对象的属性。还有配置寄存器、测量控制寄存器、湿度控制寄存器、气体控制寄存器都记录了设备的配置状态,所以我们也将它们作为属性。每台设备都有特定的校准数据,这些校准数据每次数据检测都是需要的,所以我们用属性将它们记录下来。还有测量数据,它们标识了设备当前的工作状态,所以我们将它们也作为属性。

接下来我们分析BME680的操作。首先来讲,我们肯定要与BME680交互,但我们对BME680的读写依赖于具体的硬件平台,所以我们将它们作为对象的操作。在进行相关操作时,我们需要控制时序,则需要使用延时操作,但延时处理总是依赖于具体的软硬件平台,所以我们将延时处理作为对象的操作。而使用SPI时,没有设备地址但有片选信号,如何操作片选信号依赖于硬件平台,我们将对片选的操作定义为对象的操作函数。

根据上述的分析,我们可以得到BME680环境传感器的对象类型如下:

/*定义BME680操作对象*/
typedef struct BME680Object{
       uint8_t chipID;       //芯片ID
       uint8_t bmeAddress;         //I2C通讯时的设备地址
       uint8_t memeryPage;       //用于在SPI接口时记录当前所处的内存页
       uint8_t config;                         //配置寄存器
       uint8_t ctrlMeas;                 //测量控制寄存器
       uint8_t ctrlHumi;              //湿度测量控制寄存器
       uint8_t ctrlGas0;               //气体控制寄存器0
       uint8_t ctrlGas1;               //气体控制寄存器1
       uint8_t resHeat;
       uint8_t gasWait;
      
       BME680PortType port;                                              //接口选择
       BME680CalibParamType caliPara;   //校准参数

#if BME680_COMPENSATION_SELECTED > (0)
       int32_t temperature;         //温度值
       int32_t pressure;                      //压力值
       int32_t humidity;                     //湿度值
       int32_t gasResistence;      //大气质量电阻值
       int32_t iaq;                                      //空气质量水平
#else
       float temperature;             //温度值
       float pressure;                          //压力值
       float humidity;                         //湿度值
       float gasResistence;   //大气质量电阻值
       float iaq;                                          //空气质量水平
#endif

       void (*Read)(struct BME680Object *bme,uint8_t regAddress,uint8_t *rData,uint16_t rSize);       //读数据操作指针
       void (*Write)(struct BME680Object *bme,uint8_t regAddress,uint8_t command);    //谢数据操作指针
       void (*Delayms)(volatile uint32_t nTime);       //延时操作指针
       void (*ChipSelect)(BME680CSType cs);    //使用SPI接口时,片选操作
}BME680ObjectType;

片选操作有一点需要注意,如果片选信号在硬件电路上固定有效时,可以将NULL给它,同样在SPI接口时也需要将NULL给它。

2.1.2、对象初始化函数

一个对象必须对其进行初始化才可使用。初始化对象主要有四个方面的内容:检查对象赋值的合法性;属性赋初值;为对象操作指定函数指针;对象所指向设备的初始配置。据此我们可以编写BME680环境传感器的初始化函数如下:

/*实现BME680初始化配置*/
void BME680Initialization(BME680ObjectType *bme,       //BMP280对象
                          uint8_t bmeAddress,         //I2C接口是设备地址
                          BME680PortType port,    //接口选择
                          BME680IIRFilterType filter,                //过滤器
                          BME680SPI3wUseType spi3W_en,   //3线SPI控制
                          BME680TempSampleType osrs_t,       //温度精度
                          BME680PresSampleType osrs_p,         //压力精度
                          BME680SPI3wIntType spi3wint_en,//3线SPI中断控制
                          BME680HumiSampleType osrs_h,       //湿度精度
                          BME680GasRunType run_gas,      //气体运行设置
                          BME680HeaterSPType nb_conv,  //加热器设定点选择
                          BME680HeaterOffType heat_off, //加热器关闭
                          uint16_t duration,      //TPHG测量循环周期,ms单位
                          uint8_t tempTarget,   //加热器的目标温度
                          BME680Read Read,  //读数据操作指针
                          BME680Write Write,       //写数据操作指针
                          BME680Delayms Delayms,           //延时操作指针
                          BME680ChipSelect ChipSelect     //片选操作指针
                          )
{
       uint8_t try_count = 5;
       uint8_t regValue=0;
      
       if((bme==NULL)||(Read==NULL)||(Write==NULL)||(Delayms==NULL))
       {
              return;
       }
       bme->Read=Read;
       bme->Write=Write;
       bme->Delayms=Delayms;
      
       bme->port=port;
       if(bme->port==BME680_I2C)
       {
              if((bmeAddress==0xEC)||(bmeAddress==0xEE))
              {
                     bme->bmeAddress=bmeAddress;
              }
              else if((bmeAddress==0x76)||(bmeAddress==0x77))
              {
                     bme->bmeAddress=(bmeAddress<<1);
              }
              else
              {
                     return;
              }
              bme->ChipSelect=NULL;
       }
       else
       {
              if(ChipSelect!=NULL)
              {
                     bme->ChipSelect=ChipSelect;
              }
              else
              {
                     bme->ChipSelect=BME680ChipSelectDefault;
              }
       }
      
       bme->chipID=0x00;
       bme->pressure=0.0;
       bme->temperature=25.0;
       bme->humidity=0.0;
       bme->bmeAddress=0x00;
       bme->caliPara.t_fine=0;
             
       if(!ObjectIsValid(bme))
       {
             return;
       }
    
       while(try_count--)
       {
             ReadBME680Register(bme,REG_BME680_ID,&regValue,1);
             bme->chipID=regValue;
             if(0x61==bme->chipID)
             {
                   BME680SoftReset(bme);
     
                   break;
             }
       }
 
       if(try_count)
       {
              uint8_t waitTime;
              waitTime=CalcProfileDuration(bme,duration,osrs_t,osrs_p,osrs_h);
             
              //控制寄存器配置
              ConfigControlRegister(bme,filter,spi3W_en,osrs_t,osrs_p,spi3wint_en,osrs_h,run_gas,nb_conv,heat_off,waitTime,tempTarget);
             
             //读取校准值
             GetBME680CalibrationData(bme);
       }
}

2.2、对象操作

每一个对象都有操作,我们使用对象的目的当然是通过操作对象来获取我们需要的数据。所以开发驱动时,对象的操作才是我们主要的工作内容。在这里对BME680的操作就是对其寄存器的操作。

2.2.1、写寄存器操作

我们已经说过了,对BME680的操作都是通过读写寄存器实现的。这里我们先来看写寄存器。在I2C接口方式下,写寄存器操作是在从站地址的最后一位来识别的,再加上要写的寄存器地址和数据来实现的,这也是I2C协议的标准做法。其时序图如下所示:

而在SPI接口方式下,由于SPI并未有设备地址,也不存在用从还在那地址最后为来标记读写的模式。通常一些设备需要定义操作码来实现读写区分,但BME680采取了将寄存器地址的最高位置零表示为写。之所以可以这样定义,是因为BME680寄存器地址分配的特殊性决定的。改变寄存器地址的最高位也能区分不同的寄存器,绝不会重复。在SPI接口方式下,写寄存器的时序图如下所示:

根据上述描述和时序图,我们可以实现写BME680环境传感器寄存器的程序。

/* 向BME680寄存器写一个字节 */
static void WriteBME680Register(BME680ObjectType *bme,uint8_t regAddress,uint8_t command)
{
       if(ObjectIsValid(bme))
       {
              if(bme->port==BME680_SPI)
              {
                     bme->ChipSelect(BME680CS_Enable);
                     bme->Delayms(1);
                     SetMemeryPageNumber(bme,regAddress);
                     regAddress&=0x7F;
                     bme->Delayms(1);
                     bme->Write(bme,regAddress,command);
                     bme->Delayms(1);
                     bme->ChipSelect(BME680CS_Disable);
              }
              else
              {
                     bme->Write(bme,regAddress,command);
              }
       }
}

2.2.2、读寄存器操作

读寄存器的处理方式与写寄存器是类似。在I2C接口方式下,将从站地址的最低位置1来表示读。在I2C接口方式下,读寄存器的时序图如下所示:

而在SPI接口方式下,通过将寄存器地址的最改为置1来标识为读操作。事实上,所有寄存器地址的最高为都是1,所以在读操作时实际不需要做处理。在SPI接口方式下,读寄存器的时序图如下所示:

根据上述描述和时序图,我们可以实现读BME680环境传感器寄存器的程序。

/*从BME680寄存器读取数据*/
static uint8_t ReadBME680Register(BME680ObjectType *bme,uint8_t regAddress,uint8_t *rDatas,uint16_t rSize)
{
      uint8_t bmeError=0xFF;
 
      if(ObjectIsValid(bme))
      {
              if(bme->port==BME680_SPI)
              {
                     bme->ChipSelect(BME680CS_Enable);
                     bme->Delayms(1);
                     SetMemeryPageNumber(bme,regAddress);
                     regAddress |= 0x80;
                     bme->Delayms(1);
                     bme->Read(bme,regAddress,rDatas,rSize);
                     bme->Delayms(1);
                     bme->ChipSelect(BME680CS_Disable);
              }
              else
              {
                     bme->Read(bme,regAddress,rDatas,rSize);
              }
             
              bmeError=0x00;
      }

      return bmeError;
}

3、驱动的使用

上一节我们设计并实现了BME680环境传感器的驱动程序,但这个驱动设计的是否合理还不确定,所以我们来设计一个简单的应用验证BME680环境传感器的驱动。

3.1、声明并初始化对象

使用基于对象的操作我们需要先得到这个对象,所以我们先要使用前面定义的BME680环境传感器对象类型声明一个BME680环境传感器对象变量,具体操作格式如下:

BME680ObjectType bme680;

声明了这个对象变量并不能立即使用,我们还需要使用驱动中定义的初始化函数对这个变量进行初始化。这个初始化函数所需要的输入参数如下:

BME680ObjectType *bmeBMP680对象

uint8_t bmeAddressI2C接口是设备地址

BME680PortType port,接口选择

BME680IIRFilterType filter,过滤器

BME680SPI3wUseType spi3W_en3线SPI控制

BME680TempSampleType osrs_t,温度精度

BME680PresSampleType osrs_p,压力精度

BME680SPI3wIntType spi3wint_en3线SPI中断控制

BME680HumiSampleType osrs_h,湿度精度

BME680GasRunType run_gas,气体运行设置

BME680HeaterSPType nb_conv,加热器设定点选择

BME680HeaterOffType heat_off,加热器关闭

uint16_t durationTPHG测量循环周期,ms单位

uint8_t tempTarget,加热器的目标温度

BME680Read Read,读数据操作指针

BME680Write Write,写数据操作指针

BME680Delayms Delayms,延时操作指针

BME680ChipSelect ChipSelect,片选操作指针

对于这些参数,对象变量我们已经定义了。其他的参数基本都是配置参数,我们根据实际使用需求选择输入就好了。主要的是我们需要定义几个函数,并将函数指针作为参数。这几个函数的类型如下:

/* 定义读数据操作函数指针类型 */
typedef void (*BME680Read)(struct BME680Object *bme,uint8_t regAddress,uint8_t *rData,uint16_t rSize);
/* 定义写数据操作函数指针类型 */
typedef void (*BME680Write)(struct BME680Object *bme,uint8_t regAddress,uint8_t command);
/* 定义延时操作函数指针类型 */
typedef  void (*BME680Delayms)(volatile uint32_t nTime);
/* 定义使用SPI接口时,片选操作函数指针类型 */
typedef  void (*BME680ChipSelect)(BME680CSType cs);

对于这几个函数我们根据样式定义就可以了,具体的操作可能与使用的硬件平台有关系。若采用的SPI接口则需注意片选操作,片选操作函数用于多设备需要软件操作时,如采用硬件片选可以传入NULL即可。同样如果采用的是I2C接口,则片选可以传入NULL即可。具体函数定义如下:

/*读BME680寄存器值*/
static void ReadDataFromBME680(BME680ObjectType *bme680,uint8_t regAddress,uint8_t *rData,uint16_t rSize)
{
  HAL_I2C_Master_Transmit(&bme680hi2c, bme680->bmeAddress,&regAddress,1,1000);
 
  HAL_I2C_Master_Receive(&bme680hi2c, bme680->bmeAddress+1,rData, rSize, 1000);
}

/*写BME680寄存器值*/
static void WriteDataToBME680(BME680ObjectType *bme680,uint8_t regAddress,uint8_t command)
{
  uint8_t pData[2];
 
  pData[0]=regAddress;
  pData[1]=command;
 
  HAL_I2C_Master_Transmit(&bme680hi2c,bme680->bmeAddress, pData, 2,1000);
}

对于延时函数我们可以采用各种方法实现。我们采用的STM32平台和HAL库则可以直接使用HAL_Delay()函数。于是我们可以调用初始化函数如下:

BME680Initialization(&bme680,  //BME280对象
                     0xEC,           //I2C接口是设备地址
                     BME680_I2C,    //接口选择
                     BME680_IIR_FILTER_COEFF_X127,      //过滤器
                     BME680_SPI3W_DISABLE,        //3线SPI控制
                     BME680_TEMP_SAMPLE_X16, //温度精度
                     BME680_PRES_SAMPLE_X16,          //压力精度
                     BME680_SPI3W_INT_DISABLE,       ///3线SPI中断使能
                     BME680_HUMI_SAMPLE_X16,         //湿度精度
                     BME680_GAS_RUN_ENABLE,//气体运行设置
                     BME680_HEATER_SP0,//加热器设定点选择
                     BME680_HEATER_DISABLE,//加热器关闭
                     20,//TPHG测量循环周期,ms单位
                     200,//加热器的目标温度
                     ReadDataFromBME680,  //读数据操作指针
                     WriteDataToBME680,     //写数据操作指针
                     HAL_Delay,                     //延时操作指针
                     NULL                                //片选操作指针
                     );

3.2、基于对象进行操作

我们定义了对象变量并使用初始化函数给其作了初始化。接着我们就来考虑操作这一对象获取我们想要的数据。我们在驱动中已经将获取数据并转换为转换值的比例值,接下来我们使用这一驱动开发我们的应用实例。

/*获取环境数据*/
void BME680GetEnvironmentalData(void)
{
       float pressure;                //压力值
       float temperature;             //温度值
       float humidity;                //湿度值
       float gasResistance;           //气体电阻
      
       GetBME680Measure(&bme680);

       pressure=bme680.pressure;
       temperature=bme680.temperature;
       humidity=bme680.humidity;
       gasResistance=bme680.gasResistence;
}

4、应用总结

我们设计并实现了BME680环境传感器的驱动程序,并基于这一驱动程序设计了简单的应用。我们获得了BME680检测的全部环境数据,结果也是令我们满意的,这说明我们的驱动设计是正确的。

在使用驱动时需注意,采用SPI接口的器件需要考虑片选操作的问题。如果片选信号是通过硬件电路来实现的,我们在初始化时给其传递NULL值。如果是软件操作片选则传递我们编写的片选操作函数。而如果采用I2C接口,那么在初始化时也应传递NULL值。

BME680环境传感器支持SPII2C两种接口,而且SPI也支持3线和4线模式,但我们在测试应用中只使用了I2C接口,SPI接口还有待测试。BME680环境传感器在使用SPI接口时,支持SPI模式0CPOL=CPHA=0)和模式3CPOL=CPHA=1)。而在使用I2C接口时,支持标准模式、快速模式以及高速模式。而且在使用I2C接口时,SDO引脚必须接高电平或低电平,以确定设备地址。

BME680环境传感器有2种工作模式:休眠模式和强制模式。在设备上电后就进入休眠模式,在这种模式下设备不执行测量工作。一旦启动强制模式则执行一遍TPHG循环检测。模式设定的具体定义如下:

对于BME680环境传感器有一个测量范围寄存器,这个寄存器的值对应两组计算常数,这下常数用于测量值的计算,具体如下:

总的来说对BME680环境传感器的读写操作本身并不复杂,但其计算与修正关系却相对复杂,特别是气体质量数据更应注意。

源码下载:https://github.com/foxclever/ExPeriphDriver

欢迎关注:

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

外设驱动库开发笔记21:BME680环境传感器驱动 的相关文章

  • IOS 推送通知 本地推送和远程推送

    什么是推送通知 首先明确 此处的推送通知跟我们的 NSNotification 没有半毛钱关系 可以理解为 向用户推送一条信息来通知用户某件事情 作用 可以在APP退到后台 或者关闭时 继续推送一条消息告诉用户某件事情 推送通知的应用场景
  • IOS Xcode用git管理我们的代码git的使用

    项目部署 xff0c 代码仓库 进入网站 创建项目 xff0c 2选择开源许可证的时候很关键 创建成功 然后找到 ssh https链接 用来认证 可以先用https不需要生成公钥和私钥 在终端cd到当前项目的文件夹 然后 使用 git c
  • Linux系统与设置命令

    2 系统与设置命令 在前面的两个章节中 xff0c 我们主要介绍了Linux基本知识和虚拟机的安装 xff0c 从当前章节开始 xff0c 我们一起学习下Linux的基本命令 xff0c 在当前章节 xff0c 我们先简单的学习下一些系统的
  • 4. iOS面试题原理篇2

    lldb gdb 常用的调试命令 xff1f po xff1a 打印对象 xff0c 会调用对象description方法 是print object的简写expr xff1a 可以在调试时动态执行指定表达式 xff0c 并将结果打印出来
  • SQL语言

    1 什么是SQL SQL structured query language xff1a 结构化查询语言 SQL是一种对关系型数据库中的数据进行定义和操作的语言 SQL语言简洁 xff0c 语法简单 xff0c 好学好用 什么是SQL语句
  • 实测:Ubuntu16.04 ROS中实现RVIZ控制驱动UR10E机械臂!

    踩过了太多的坑 xff0c 此博客警醒自己 xff01 能用的收藏一下 一 演示一切之前 xff0c 要清楚两个问题 xff1a 1 你的UR是什么型号 xff0c 示教器软件版本是什么 xff1f xff08 这个不会看的话 xff0c
  • STM32CubeMX安装及使用教程(一)

    背景 随着芯片行业不断发展 xff0c STM32系列芯片不断扩大市场份额 为了方便开发者的不同需求 xff0c 意法半导体 xff08 ST xff09 推出了一款图形化配置工具 STM32CubeMX STM32CubeMX可以通过图形
  • STM32CubeMX安装及使用教程(二)

    背景 第一章我们讲述了如何安装和配置STM32CubeMX软件 xff0c 这一章我们来说说如何简单的使用STM32CubeMX软件 软件使用 1 打开STM32CubeMX软件 xff0c 点击File gt New Project 2
  • xshell无法连接虚拟机中的ubuntu解决方案

    xshell无法连接虚拟机中的ubuntu解决方案 查看虚拟机中ubuntu的IP地址的方法 ifconfig a 利用xshell工具进行远程连接 ssh IP地址 发现无法连接 xff0c 这就是我一开始将VM安装好 xff0c 一次性
  • 气压计融合定高控制逻辑

  • Git简介

    什么是版本控制 学习Git之前首先要了解一个概念 版本控制 xff0c 什么是版本控制 xff0c 版本控制就是一种在开发的过程中用于管理我们对文件 目录或工程等内容的修改历史 xff0c 方便查看更改历史记录 xff0c 备份以便恢复以前
  • Git图形化管理工具Sourcetree的使用及git冲突问题

    目录 一 了解 Sourcetree1 什么是Sourcetree2 为什么要使用Sourcetree 二 Sourcetree下载与安装1 下载2 安装 三 Sourcetree的使用1 Gitee的介绍与使用什么是GiteeGitee的
  • [智能车]平衡车/直立车的入门经验(代码讲解)

    做为第十六届智能车的FW 在半年的做车经历中把能踩的坑都踩了个遍 写这篇文章是为了留个纪念 xff0c 也是为了帮新车友快速入门 xff08 可能完全0基础 xff09 我自己的经验也不足 xff0c 所以可能会存在一些漏洞 xff0c 还
  • Mac系统 dockerfile 报错 COPY failed: stat /var/lib/docker/tmp/docker-builderxxx

    报错 xff1a Step 3 4 span class token keyword span COPY nginx 1 12 2 tar gz usr local src COPY failed span class token func
  • VS code的git设置

    先建立一个空文件夹 在git窗口先点 初始化仓库 xff0c 再点侧边栏右上角的 更多 xff0c 选 远程 下的 添加远程存储仓库 输入远程仓库地址 xff0c 再输入仓库名称 打开 源代码管理存储库 视图 xff0c 点上面的git分支
  • L4Linux的版本比较

  • 利用XML文件的一个写日志的类!!!!!

    对于程序执行期间的错误跟踪 xff01 相信大家都有自己的一套办法 xff01 xff01 xff01 但都是利用文件文件 xff0c 我这次利用的是XML amp XSL xff0c 可产生报表格式的日志 轻松生成报表 xff01 xff
  • 【Kubernetes实战】(四)MiniKube方式部署

    目录 一 当前配置环境 二 准备工作 1 关闭防火墙和SeLinux 2 禁用swap交换分区 三 Docker安装 1 配置docker源 2 安装docker环境依赖 3 安装docker 4 启动docker并设置为开机自启 5 配置
  • DGPS与RTK的区别

    2013 10 11 10 49 11 分类 xff1a GNSS 举报 字号 订阅 最近一直感觉身在这个行业不能对这个行业理论知识一无所知 xff0c 这对于技术人来说应该是一种遗憾 所以决定要学一些东西 xff0c 并记录下来以便于以后
  • 基于Ubuntu19.04安装docker

    输入命令 lsb release a 显示如下 Distributor ID Ubuntu 类别是ubuntu Description Ubuntu 19 04 16年3月发布的稳定版本 xff0c LTS是Long Release 19

随机推荐

  • PHP获取当月开始时间和结束时间

    startTime 61 date 39 Y m 01 39 time 获取该月份的第一天 endTime 61 date 39 Y m t 39 time 获取该月份的最后一天
  • CSS3实现动态进度条

    CSS3的线性渐变使制造动态进度条成为可能 现在就来一步一步讲解如何创建动态进度条吧 以Chrome浏览器webkit内核为例 目标 xff1a 单个矩形条背景 目标进度条 xff0c 背景是淡蓝色 xff0c 上面平铺了一层倾斜的深蓝色条
  • C语言-阶乘数列

    求Sn 61 1 43 2 43 3 43 4 43 5 43 43 n 之值 xff0c 其中n是一个数字 include lt stdio h gt include lt math h gt long long factorial in
  • 深入MTK平台bootloader启动之【 Pre-loader -> Lk】分析笔记

    1 bootloader到kernel启动总逻辑流程图 ARM架构中 xff0c EL0 EL1是必须实现 xff0c EL2 EL3是选配 xff0c ELx跟层级对应关系 xff1a EL0 app EL1 Linux kernel l
  • STM32输出模式详解

    本文为个人见解 xff0c 如有问题欢迎指正 首先需要明确输出 输入的意思 输出是指STM32控制外设 xff08 主要指输出高电平或低电平 xff09 或STM32发送数据给外设 xff0c 输入是指外设发送数据给STM32 输出模式有三
  • [OPNET学习总结]——SITL

    软件自带的SITL例程中 xff0c 出现如下error xff1a lt lt lt Recoverable Error gt gt gt Object repository construction failed due to erro
  • 漫谈程序员系列:程序员的生活就这样吗

    我当了快十年程序员了 xff0c 终于老得可以来谈谈程序员的生活是什么样子了 或许陈奕迅的 十年 中的一段歌词 xff0c 可以表示很多程序员和软件开发之间的感情纠葛 xff1a 十年之前 我不认识你 你不属于我 我们还是一样 陪在一个陌生
  • 程序员转行为什么这么难

    尽管我在 大龄程序员的未来在何方 这篇文章里比较乐观地介绍了程序员保持竞争力的几个方向 xff0c 但现实依然是残酷的 xff1a 很多人将不得不离开软件开发工作 xff0c 转型去从事其他职业 当你要这么做时 xff0c 就会感慨 xff
  • 使用http_parser解析URL

    用C语言编写http应用 xff0c 解析URL是一个繁琐的事儿 前几天使用http parser实现httpclient xff0c 发现里面提供了一个解析URL的方法http parser parse url xff0c 用起来相当方便
  • Android app 后台被杀恢复

    android 模拟应用因内存不足被后台杀死命令 https www jianshu com p effb4546b9aa adb shell am kill all 应用通过home键已经停留在后台使用 xff0c 杀掉所有后台程序 xf
  • Ubuntu查看linux系统版本号

    查看ubuntu版本 输入命令 cat proc version 显示如下 Linux version 5 0 0 13 generic buildd 64 lcy01 amd64 020 linux内核版本号 gcc version 8
  • Linux C Socket简介和实现

    1 网络中进程之间如何通信 xff1f 本地的进程间通信 xff08 IPC xff09 有很多种方式 xff0c 但可以总结为下面4类 xff1a 消息传递 xff08 管道 FIFO 消息队列 xff09 同步 xff08 互斥量 条件
  • C++ STL视频教程,初学者必备视频资料

    STL视频教程 初学者必备视频资料 我一个朋友做的 我转发到这里和大家分享 STL语音视频教程 下载地址 xff1a url 61 http www ctdisk com file 3388918 STL语音视频教程 7z url
  • QMessageBox简单用法(QT5.12)

    span class token comment for starf study span span class token macro property span class token directive hash span span
  • TOF相机 Realsense L515 与 Ipad pro Lidar Camera 对比

    最近好奇都是TOF 相机 L5151 和 Ipad pro 上带的深度相机模块有啥不一样 网上很少有相关的中文资料来介绍 原理上的差异 简单搜索了一下 在此小小总结 Apple Lidar Camera 苹果采用的激光是 VCSEL Ver
  • Arduino 读取GPS 数据发送解析并发布ROS topic(一)

    概述 通过Arduino收集GPS数据 xff0c 连接至电脑端 xff0c 在电脑端通过python对数据进行整理 xff0c 并通过发布 TOPIC xff0c 本部分主要记录如何通过Arduino读取GPS数据 接线方式 GPS 的
  • STM32 复位电路设计

    在此之前我是个只会抄写原理图的工程师 xff0c 每当遇到一个问题时 xff0c 确需要解决很久 xff0c 最根本的原因在于不明白其中的原理 xff0c 这次补充一下单片机复位电路设计 1 为什么要设计复位电路 xff1f 在做一件事情之
  • STM32核心板设计——电源设计

    1 STM32 数据手册电源部分研读 RTC电源管脚为V BAT 电源范围为1 8 3 6V xff0c 主要用于RTC时钟的供电 xff0c RTC在大部分场合用于保存一些重要的参数 xff0c 比如在电脑主板上用于保存boss的信息 x
  • stm32的复位电路问题

    现在比较流行的复位方式是这样的 xff1a 但我们都知道对于结构紧凑型硬件来说 xff0c 多一个电阻都是没必要的 在没有手动复位需求的场合 xff0c 能不能删掉按键与R24 xff0c 仅保留104电容 xff1f 通过阅读stm32
  • 外设驱动库开发笔记21:BME680环境传感器驱动

    环境传感器是一类我们很常用的传感器 它可以方便我们获取压力 温度 湿度以及空气质量等数据 在这一篇中 xff0c 我们将分析 BME680 环境传感器的功能 xff0c 并设计和实现 BME680 环境传感器的驱动 1 功能概述 BME68