嵌入式产品开发中,我们常常会有检测环境温度、压力、湿度的需求。如果有一个集成有这3个传感器的元件,无疑将是很方便的。博世的BME280就能实现这一要求。在这一篇中我们将讨论BME280的驱动设计与实现。

1、功能概述

BME280是一款专为移动应用而开发的集成环境传感器,其尺寸和低功耗是关键设计限制。该装置结合了单个高线性度,高精度压力,湿度和温度传感器,采用8引脚金属盖2.5 x 2.5 x0.93mm³LGA封装,设计用于低电流消耗(3.6μA@ 1Hz),长期稳定性高EMC稳健性。

1.1、硬件描述

BME280压力湿度温度传感器实现了高性能的温度压力湿度检测,包括有压力、温度、湿度3个传感器和一个高精度ADC及接口逻辑,其结构框图如下图所示:

湿度传感器具有极快的响应时间,可满足新兴应用的性能要求,例如环境感知和宽温度范围内的高精度。压力传感器是绝对气压传感器,具有极高的精度和分辨率,噪音极低。集成的温度传感器经过优化,噪音极低,分辨率高。它主要用于压力和湿度传感器的温度补偿,也可用于估算环境温度。

BME280压力温度传感器采用了小巧的8引脚LGA封装形式。其引脚排布就功能如下图所示:

BME280支持全套操作模式,可灵活地优化器件的功耗,分辨率和滤波器性能。

1.2、通讯接口

BME280压力温度传感器支持3种通讯接口方式:四线SPI、三线SPI以及I2C。在不同的接口模式下,各引脚的定义也是有差异的,关于这三种接口模式各引脚的定义如下:

对应3种不同的接口方式,BME280压力温度传感器存在三种与总线连接的方式。首先我们来看四线SPI接口方式,包括CSB片选、SCK时钟、SDI数字输入、SDO数字输出。其总线连接方式如下图:

接下来我们来看三线SPI接口方式,包括CSB片选、SCK时钟、SDI数字输入/SDO数字输出。其与4线SPI的区别是数字输入输出使用同一引脚,第3脚就是输入也是输出,而第5脚浮空。其总线连接方式如下图:

最后我们来看I2C接口方式,包括SCL时钟、SDA数字输入输出。在I2C接口模式下,第2脚CSB连接到高电平,以设置BME280压力温度传感器使用I2C接口。而第5脚则可以通过连接高电平或低电平来设置设备地址的最后一位,不可以浮空。所以根据第5脚电频不同,BMP280压力温度传感器的I2C设备7位地址为:0x76和0x77。其总线连接方式如下图:

BME280压力温度传感器在使用SPI接口时,支持SPI模式0(CPOL=CPHA=0)和模式3(CPOL=CPHA=1)。而在使用I2C接口时,支持标准模式、快速模式以及高速模式。接口的选择实际上是通过CSB的电位实现的,低电平时就是SPI,高电平时就是I2C。

1.3、内部寄存器

对BME280压力温度传感器的所有操作都是通过读写对应的寄存器来实现的。BME280压力温度传感器中所有的寄存器都是8位的。这些寄存器在存储器中的地址分配如下图所示。

在上图并未包括系统保留的寄存器,这些寄存器不可以进行写操作,读出来的值也是无意义的。接下来我们来详细描述上图中的这些寄存器。

先来看看两个比较特殊的寄存器。首先是ID寄存器,这个寄存器是只读的,而且其存储的值也固定为0x60,用来代表设备为BME280压力温度传感器。这个寄存器在系统上电后即可读取。还有复位寄存器,这个寄存器是只写的,固定向其写0xB6来实现BME280压力温度传感器的复位。同样只要系统上电后即可以写复位寄存器。

湿度控制寄存器用以配置湿度数据的测量精度。但是修改这个寄存器的值不会立即生效,必须对测量控制寄存器做了写操作之后才会有效。湿度控制寄存器的各位定义如下图:

状态寄存器是只读的,其实只使用了其中的两位,这两位分别表示数据测量是否完成和影响寄存器是否更新。下图是状态寄存器的详细说明:

测量控制寄存器是可读写的,用以配置BME280压力温度传感器数据获取的方式。分别配置温度采样、压力采样和工作模式。工作模式有三种:休眠模式、强制模式、正常模式。系统上电后即为休眠模式,通过这一寄存器的配置可以使BME280压力温度传感器进入强制模式或正常模式运行。湿度控制器修改后必须写这个寄存器才会生效。测量控制寄存器的各位定义如下图:

配置寄存器用于设置BME280压力温度湿度传感器的速率、过滤器以及接口模式。在休眠模式下写配置寄存器是允许的,但在正常模式下会被忽略,所以在系统复位后,进入正常模式前先写配置寄存器。配置寄存器各位的定义如下图所示:

压力数据寄存器存储有压力测量数据输出的原始值。使用了三个寄存器中的20位来下存储压力数据。压力数据寄存器各位的定义如下图所示:

温度数据寄存器存储有温度测量数据输出的原始值。使用了三个寄存器中的20位来下存储温度数据。温度数据寄存器各位的定义如下图所示:

湿度数据寄存器存储有湿度测量数据输出的原始值。使用了两个寄存器中的16位来下存储湿度数据。湿度数据寄存器各位的定义如下图所示:

此外还有校准数据寄存器,总共是41个寄存器,存储了计算压力温度最终值的厂家校准数据。这些校准寄存器的定义及地址分配如下图所示:

我们已经说过面向BME280压力温度湿度传感器的所有操作都是基于寄存器进行的,我们已经了解了BME280压力温度湿度传感器的各个寄存器,现在可以来实现它的操作了。

2、驱动设计与实现

上一节我们描述了BME280压力湿度温度传感器的引脚功能、数据存储格式以及通讯接口的基本内容。在这一节中我们将考虑如何设计并实现BME280压力湿度温度传感器的驱动程序。

2.1、对象定义

在使用一个对象之前我们需要获得一个对象。同样的我们想要BME280压力湿度温度传感器就需要先定义BME280压力湿度温度传感器的对象。

2.1.1、对象的抽象

我们要得到BME280压力湿度温度传感器对象,需要先分析其基本特性。一般来说,一个对象至少包含两方面的特性:属性与操作。接下来我们就来从这两个方面思考一下BME280压力湿度温度传感器的对象。

先来考虑属性,作为属性肯定是用于标识或记录对象特征的东西。我们来考虑BME280压力湿度温度传感器对象属性。BME280压力湿度温度传感器的ID寄存器用于标识设备是否为BME280;配置寄存器和测量控制寄存器都用关于系统配置,指示了设备的工作状态,所以我们将这几个寄存器定义为对象的属性。而使用的通讯接口决定了访问BME280压力湿度温度传感器的行为,所以我们需要记住这一配置;而校准数据则在计算数据时所要使用的,我们也需要记住这些参数,所以我们将它们也都定义为属性。在I2C接口模式时,设备地址是区分总线上设备的唯一标志,所以我们将其定义为属性。同样测量数据指示了设备当前的工作状态,我们将器作为属性。

接着我们还需要考虑BME280压力湿度温度传感器对象的操作问题。我们需要与BME280压力湿度温度传感器通讯就需要向其写数据并从其读数据,而不论是SPI接口还是I2C接口,读写操作都以来与具体的硬件平台,所以我们将他们作为对象的操作。此外,为控制时序,我们需要延时操作,而延时行为的实现亦依赖于具体的软硬件平台,所以我们将延时也作为对象的操作。

根据上述我们对BME280压力湿度温度传感器的分析,我们可以定义BME280压力湿度温度传感器的对象类型如下:

/*定义Bme280操作对象*/
typedef struct BME280Object{uint8_t chipID;                                //芯片IDuint8_t bmeAddress;                              //I2C通讯时的设备地址uint8_t config;                                       //配置寄存器uint8_t ctrlMeas;                                   //测量控制寄存器uint8_t ctrlHumi;                                   //湿度测量控制寄存器BME280PortType port;                         //接口选择BME280CalibParamType caliPara;      //校准参数float temperature;                                  //温度值float pressure;                                 //压力值float humidity;                                //湿度值void (*Read)(struct BME280Object *bme,uint8_t regAddress,uint8_t *rData,uint16_t rSize);       //读数据操作指针void (*Write)(struct BME280Object *bme,uint8_t regAddress,uint8_t command);    //写数据操作指针void (*Delayms)(volatile uint32_t nTime);       //延时操作指针void (*ChipSelect)(BME280CSType cs);                  //使用SPI接口时,片选操作
}BME280ObjectType;

2.1.2、对象初始化

我们知道,一个对象仅作声明是不能使用的,我们需要先对其进行初始化,所以这里我们来考虑BME280压力湿度温度传感器对象的初始化函数。一般来说,初始化函数需要处理几个方面的问题。一是检查输入参数是否合理;二是为对象的属性赋初值;三是对对象作必要的初始化配置。据此我们设计BME280压力湿度温度传感器对象的初始化函数如下:

/*实现BME280初始化配置*/
void BME280Initialization(BME280ObjectType *bme,         //BMP280对象uint8_t bmeAddress,                       //I2C接口是设备地址BME280PortType port,                  //接口选择BME280TimeSBType t_sb,           //间隔周期BME280IIRFilterType filter,         //过滤器BME280SPI3wUseType spi3W_en,  //3线SPI控制BME280TempSampleType osrs_t,       //温度精度BME280PresSampleType osrs_p,         //压力精度BME280PowerModeType mode,          //电源模式BME280HumiSampleType osrs_h,       //湿度精度BME280Read Read,                              //读数据操作指针BME280Write Write,                            //写数据操作指针BME280Delayms Delayms,                  //延时操作指针BME280ChipSelect ChipSelect            //片选操作指针)
{uint8_t try_count = 5;uint8_t regAddress=0;uint8_t command=0;if((bme==NULL)||(Read==NULL)||(Write==NULL)||(Delayms==NULL)){return;}bme->Read=Read;bme->Write=Write;bme->Delayms=Delayms;bme->chipID=0x00;bme->pressure=0.0;bme->temperature=0.0;bme->humidity=0.0;bme->bmeAddress=0x00;bme->port=port;if(bme->port==BME280_I2C){if((bmeAddress==0xEC)||(bmeAddress==0xEE)){bme->bmeAddress=bmeAddress;}bme->ChipSelect=NULL;}else{if(ChipSelect!=NULL){bme->ChipSelect=ChipSelect;}else{bme->ChipSelect=BME280ChipSelectDefault;}}bme->caliPara.t_fine=0;if(!ObjectIsValid(bme)){return;}while(try_count--){bme->chipID=ReadBME280Register(bme,REG_BME280_ID);if(0x60==bme->chipID){BME280SoftReset(bme);break;}}if(try_count){/*配置配置寄存器:间隔周期0.5ms、IIR滤波系数16、不使用SPI3线通讯*/regAddress=REG_CONFIG;command=t_sb|filter|spi3W_en;WriteBME280Register(bme,regAddress,command);/*配置测量控制寄存器:温度20位,压力20位,电源正常模式*/regAddress=REG_CTRL_MEAS;command=osrs_t|osrs_p|mode;WriteBME280Register(bme,regAddress,command);/*配置湿度测量控制寄存器*/regAddress=REG_CTRL_HUM;command=osrs_h;WriteBME280Register(bme,regAddress,command);bme->Delayms(10);bme->config=ReadBME280Register(bme,REG_CONFIG);bme->Delayms(10);bme->ctrlMeas=ReadBME280Register(bme,REG_CTRL_MEAS);bme->Delayms(10);bme->ctrlHumi=ReadBME280Register(bme,REG_CTRL_HUM);bme->Delayms(10);/*读取校准值*/GetBME280CalibrationData(bme);}
}

2.2、对象操作

我们已经完成了BME280压力湿度温度传感器对象类型的定义和对象初始化函数的设计。但我们的主要目标是获取对象的信息,接下来我们还要实现面向BME280压力湿度温度传感器的各类操作。

2.2.1、写寄存器

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

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

根据上述描述和时序图,我们可以实现写BME280压力湿度温度传感器寄存器的程序。

/* 向BME280寄存器写一个字节 */
static void WriteBME280Register(BME280ObjectType *bme,uint8_t regAddress,uint8_t command)
{if(ObjectIsValid(bme)){if(bme->port==BME280_SPI){regAddress&=0x7F;bme->ChipSelect(BME280CS_ENABLE);bme->Delayms(1);bme->Write(bme,regAddress,command);bme->Delayms(1);bme->ChipSelect(BME280CS_DISABLE);}else{bme->Write(bme,regAddress,command);}}
}

2.2.2、读寄存器

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

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

根据上述描述和时序图,我们可以实现读BME280压力温度湿度传感器寄存器的程序。

/*从BMP280寄存器读取一个字节*/
static uint8_t ReadBME280Register(BME280ObjectType *bme,uint8_t regAddress)
{uint8_t regValue=0xFF;if(ObjectIsValid(bme)){if(bme->port==BME280_SPI){regAddress |= 0x80;bme->ChipSelect(BME280CS_ENABLE);bme->Delayms(1);bme->Read(bme,regAddress,&regValue,1);bme->Delayms(1);bme->ChipSelect(BME280CS_DISABLE);}else{bme->Read(bme,regAddress,&regValue,1);}}return regValue;
}

3、驱动的使用

我们描述了BME280压力湿度温度传感器的基本原理并实现了驱动程序。我们还需要设计一个简单的应用来验证这一驱动设计是否合乎我们的要求,所以接下来我们就来设计一个简单的验证应用。

3.1、声明并初始化对象

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

BME280ObjectType bme280;

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

BME280ObjectType *bme,BMP280对象

uint8_t bmeAddress,I2C接口是设备地址

BME280PortType port,接口选择

BME280TimeSBType t_sb,间隔周期

BME280IIRFilterType filter,过滤器

BME280SPI3wUseType spi3W_en,3线SPI控制

BME280TempSampleType osrs_t,温度精度

BME280PresSampleType osrs_p,压力精度

BME280PowerModeType mode,电源模式

BME280HumiSampleType osrs_h,湿度精度

BME280Read Read,读数据操作指针

BME280Write Write,写数据操作指针

BME280Delayms Delayms,延时操作指针

BME280ChipSelect ChipSelect,片选操作指针

对于这些参数,对象变量我们已经定义了。接口选择、间隔周期、过滤器、3线SPI控制、温度精度、压力精度、湿度精度、电源模式等都是枚举量我们根据实际情况输入即可。而使用I2C接口时需要的设备地址,也按具体地址给入就好。主要的是我们需要定义几个函数,并将函数指针作为参数。这几个函数的类型如下:

/* 定义读数据操作函数指针类型 */
typedef void (*BME280Read)(struct BME280Object *bme,uint8_t regAddress,uint8_t *rData,uint16_t rSize);/* 定义写数据操作函数指针类型 */
typedef void (*BME280Write)(struct BME280Object *bme,uint8_t regAddress,uint8_t command);/* 定义延时操作函数指针类型 */
typedef  void (*BME280Delayms)(volatile uint32_t nTime);/* 定义使用SPI接口时,片选操作函数指针类型 */
typedef  void (*BME280ChipSelect)(BME280CSType cs);

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

/*读BME280寄存器值*/
static void ReadDataFromBME280(BME280ObjectType *bme280,uint8_t regAddress,uint8_t *rData,uint16_t rSize)
{HAL_I2C_Master_Transmit(&bme280hi2c, bme280->bmeAddress,&regAddress,1,1000);HAL_I2C_Master_Receive(&bme280hi2c, bme280->bmeAddress+1,rData, rSize, 1000);
}/*写BME280寄存器值*/
static void WriteDataToBME280(BME280ObjectType *bme280,uint8_t regAddress,uint8_t command)
{uint8_t pData[2];pData[0]=regAddress;pData[1]=command;HAL_I2C_Master_Transmit(&bme280hi2c,bme280->bmeAddress, pData, 2,1000);
}

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

BME280Initialization(&bme280,  //BME280对象0xEC,           //I2C接口是设备地址BME280_I2C,    //接口选择BME280_T_SB_0P5,       //间隔周期BME280_IIR_FILTER_COEFF_X16,        //过滤器BME280_SPI3W_DISABLE,                      //3线SPI控制BME280_TEMP_SAMPLE_X16, //温度精度BME280_PRES_SAMPLE_X16,          //压力精度BME280_POWER_NORMAL_MODE,                                  //电源模式BME280_HUMI_SAMPLE_X16,                       //湿度精度ReadDataFromBME280,                                   //读数据操作指针WriteDataToBME280,            //写数据操作指针HAL_Delay,                     //延时操作指针NULL                                //片选操作指针);

3.2、基于对象进行操作

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

/*获取大气压力和温度*/
void BME280GetEnvironmentalData(void)
{float pressure;                   //压力值float temperature;      //温度值float humidity;                  //湿度值GetBME280Measure(&bme280);pressure=bme280.pressure;temperature=bme280.temperature;humidity=bme280.humidity;
}

4、应用总结

BME280压力湿度温度传感器的驱动已经实现并做了简单的应用。在我们测试时,得到的数据与其它方法获得的温度压力数据基本是一致的,这说明我们的驱动程序总体来说是正确的。

BME280压力湿度温度传感器支持SPI和I2C两种接口,而且SPI也支持3线和4线模式,但我们在测试应用中只使用了I2C接口,SPI接口还有待测试。

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

BME280压力湿度温度传感器在使用SPI接口时,支持SPI模式0(CPOL=CPHA=0)和模式3(CPOL=CPHA=1)。而在使用I2C接口时,支持标准模式、快速模式以及高速模式。而且在使用I2C接口时,SDO引脚必须接高电平或低电平,以确定设备地址。

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

欢迎关注:

外设驱动库开发笔记20:BME280压力湿度温度传感器驱动相关推荐

  1. 外设驱动库开发笔记13:MLX90614红外温度传感器驱动

    红外温度传感器一般用于非接触式的温度检测.在我们的系统中经常会有这样的需求.所以我们将其设计为通用的驱动库以备复用.这一篇我们将讲述MLX90614红外温度传感器驱动的设计与实现. 1.功能概述 ML ...

  2. 外设驱动库开发笔记12:TSEV01CL55红外温度传感器驱动

    有时候我们需要检测一些无法直接接触的器件的温度.为了实现这一需求,我们通常会选择红外温度传感器来实现这一功能.考虑到复用的问题,我们一般会将操作元器件的代码抽象为驱动函数以备调用.这里我们就来设计并实 ...

  3. 外设驱动库开发笔记11:SHT3x系列温湿度传感器驱动

    在我们的产品中经常会遇到温湿度检测的需求.可以用于检测温湿度的传感器元件也有很多.我们经常使用的SHT各系列数字温湿度传感器来实现应用需求.在这里我们将设计并实现SHT3x系列温湿度传感器的驱动. 1 ...

  4. 外设驱动库开发笔记10:SHT2x系列温湿度传感器驱动

    温湿度检测是嵌入式编程中经常应用到的一项功能.在我们的产品中亦经常使用.SHT2x系列温湿度传感器作为一种高精度低成本的集成模块,一直应用于我们的产品中.在这里我们讨论如何封装SHT2x系列温湿度传感 ...

  5. 外设驱动库开发笔记9:SHT1x系列温湿度传感器驱动

    在我们的产品中,经常需要检测温湿度数据.有很多检测温湿度的方法和模块,其中SHT1x系列温湿度传感器就是一种成本较低使用方便的温湿度检测模块.下面我们就来说一说如何实现SHT1x系列温湿度传感器的驱动 ...

  6. python实现sht驱动_【技术】外设驱动库开发笔记9:SHT1x系列温湿度传感器驱动

    在我们的产品中,经常需要检测温湿度数据.有很多检测温湿度的方法和模块,其中SHT1x系列温湿度传感器就是一种成本较低使用方便的温湿度检测模块.下面我们就来说一说如何实现SHT1x系列温湿度传感器的驱动 ...

  7. 外设驱动库开发笔记32:HLPM025K3 PM2.5传感器驱动

      现在人们对大气环境及室内环境都比较关注.PM2.5在生活中也是常见的词汇.在有些产品中就要求检测PM2.5的数值.检测PM2.5的手段多种多样,在要求不高时我们通常可以采用激光模块.在这一篇中,我 ...

  8. 外设驱动库开发笔记45:MS4515DO压力传感器驱动

      很多时候我们需要检测流量和压力这些参数,比如我们要检测大气压,或者通过测量差压来获得输送流体的流量等,都需要用到压力传感器.这一篇我们就来讨论MS4515DO压力传感器的数据获取. 1.功能概述 ...

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

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

最新文章

  1. 零基础入门NLP - 新闻文本分类
  2. 8g ubuntu 树莓派4b_3D 打印制造树莓派 4B 平板电脑
  3. OPhone2.0应该重在中文应用体验
  4. PatBlt - [Daliy APIs]
  5. 金山云最新财报:Q4营收7.27亿,同比增长81%
  6. 框架controller找不到_SpingBoot框架知识详解
  7. C++ Vector 使用心得
  8. KMP算法的核心,是一个被称为部分匹配表(Partial Match Table)的数组以及next数组求解
  9. 优秀的程序员是如何处理技术 Bug 的?
  10. 正向混合云和反向混合云解析
  11. 为什么学霸不情愿帮助同学回答问题?
  12. Hexo博客新建文章以及Next主题多层级分类
  13. 机器学习导论第二章--学习心得1
  14. Android 测试技能树
  15. CSS Tricks - 你应该知道的 CSS 技巧
  16. Zynga公布2019年第三季度财务业绩
  17. pmp知识点(12)-项目采购管理
  18. Cloning A Database Home And Changing The User/Group That Owns It
  19. python人工智能的重要性_为什么说Python是AI时代必备技能?
  20. python制作epub文件代码

热门文章

  1. CRISP-DM:数据挖掘标准流程
  2. 【HTML/CSS】SEO的概念和实现
  3. ASP.NET MVC教程六:两个配置文件详解
  4. spring boot 三种入参
  5. 从工程文化和运维理念理解Netflix
  6. python3学习笔记(2)_list-tuple
  7. 补鞋匠---Cobbler 服务器自动搭建
  8. 查看Oracle有哪些表或者视图
  9. 程序员在群询问破解软件
  10. this的用法添加样式给tr或者div