MEMS/传感技术
很多时候我们需要检测被控对象的绝对压力,而且在我们的多款产品中也有这样的需求。当然检测绝对压力的传感器有很多,我们经常使用MS5803来实现压力检测。本篇中我们将设计并实现MS5803系列压力传感器的驱动。
MS5803系列产品包含压阻传感器和传感器接口IC。MS5803系列压力传感器的主要功能是将未补偿的压阻压力传感器的模拟输出电压24位数字值,以及提供一个24位数字值的温度传感器。
MS5803系列压力传感器是新一代高分辨率高度计传感器,它是为高度计和高度分辨率为20厘米的变压计优化。能够同时获得压力值和温度值,其中压力测量范围为10-1100mbar,温度的测量范围是-40-85摄氏度。MS5803系列压力传感器各引脚功能如下:
MS5803系列压力传感器具有SPI和I2C总线接口。MS5803系列压力传感器模块包括一个高线性压力传感器和一个超低功率24位ΔΣADC与内部工厂校准系数。它提供一个精确的数字24位压力和温度值和不同的操作模式。传感器内部结构图如下:
我们使用MS5803系列压力传感器时,需要做的就是选择不同的通讯接口与其实现数字通讯的过程。
MS5803系列压力传感器支持SPI和I2C总线通讯,通过协议选择引脚PS来决定采用什么接口。将协议选择引脚PS拉低,选择SPI协议,将PS拉高,激活I2C总线协议。在不同协议下相关引脚的定义如下:
在不同协议下各引脚所支持的参数是有些许差异的。在SPI接口模式下时钟引脚最大可达20MHz;在I2C接口模式下时钟引脚最大可达200kHz。具体的参数如下图:
在SPI模式下,SCLK作为外部输入时钟,SDI作为串行数据输入,支持Mode0和Mode3的时钟极性和相位。传感器的响应数据输出为SDO引脚,片选信号为CSB引脚。SPI模式下接线示意图如下:
在I2C模式下,SCLK为外部串行时钟输入,SDA为串行数据通讯。CSB引脚作为地址选择,可以链接到VDD或者GND,这也意味着MS5803可以在一条I2C总线接两个设备。在CSP接高电平时,地址为0x76(1110110 b),而CSB接低电平时,地址为0x77 (1110111 b)这个地址是高七位,最后以为有读写命令来决定。实现写命令时,最后一位为0,实现读命令时,最后一位为1。I2C模式下接线图如下:
无论是在SPI模式还是在I2C模式,MS5803系列压力传感器都是通过5类命令实现的。这些命令包括:复位、读取出厂校准值、数据1转换(压力值数据)、数据2转换(温度值数据)和读取ADC的转换结果。具体命令定义如下:
从上图,我们知道每个命令的大小为1字节(8位)。需要说明的是,ADC读取指令后,设备将返回24位结果,PROM读取则是16位结果。PROM的地址使用Ad2、Ad1和Ad0位嵌入到PROM读命令中。关于这128bit PROM的地址分配如下图所示:
MS5803系列压力传感器每个模块都是在两个温度和两个压力下单独校准的。这6个必要的系数来补偿工艺变化和温度变化,就存储在每个模块的128bit PROM中。这些6个系数必须由软件读取,并在程序中使用,将D1和D2转换成补偿的压力和温度值。
我们已经了解了MS5803压力传感器基本操作。在此基础上,我们将设计并实现MS5803压力传感器的驱动程序。
在使用一个对象之前我们需要获得一个对象。同样的我们想要MS5803压力传感器就需要先定义MS5803压力传感器的对象。
我们要得到MS5803压力传感器对象,需要先分析其基本特性。一般来说,一个对象至少包含两方面的特性:属性与操作。接下来我们就来从这两个方面思考一下MS5803压力传感器的对象。
先来考虑属性,作为属性肯定是用于标识或记录对象特征的东西。我们来考虑MS5803压力传感器对象属性。首先MS5803有一系列的产品,不同产品线的补偿计算方式有很大差异,所以我们将产品类型作为属性以区别不同的产品。MS5803拥有SPI和I2C两种通讯方式可供选择,为了确定具体设备所采用的接口,我们将其作为对象的属性以记录其使用的接口方式。在采用I2C接口模式时,每台设备都有一个设备地址,这个地址用于区分同一总线上不同的设备,所以我们将地址作为对象的属性。而对于MS5803每台都有6个校准参数,这些参数在测量是需要用到,所以我们需要保存这些参数,所以我们将其作为属性。
接着我们还需要考虑MS5803压力传感器对象的操作问题。无论是使用SPI接口还是使用I2C接口我们都需要向MS5803写命令和从MS5803读数据,而读写行为都与所处的硬件平台相关,所以我们将读和写数据作为对象的操作。为控制时序,我们需要延时操作,但延时操作也与具体的软硬件平台相关,所以我们将其作为对象的操作。在使用SPI接口方式时,若使用软件控制片选信号,则会依赖于硬件,我们将针对片选信号的操作作为对象的操作。
根据上述我们对MS5803压力传感器的分析,我们可以定义MS5803压力传感器的对象类型如下:
1 typedef struct MS5803Object {
2 uint8_t devAddress; //设备地址
3 MS5803PortType port; //通讯端口协议
4 MS5803ModelType model; //设备的类型
5 uint16_t caliData[6]; //校准数据
6 float temperature;
7 float pressure;
8 void (*Write)(struct MS5803Object *ms,uint8_t command);//向MS5803写信息
9 void (*Read)(struct MS5803Object *ms,uint8_t *rData,uint16_t rSize);//从MS5803读数据
10 void (*ChipSelcet)(MS5803CSType en); //片选信号,用于SPI接口
11 void (*Delayms)(volatile uint32_t nTime); //毫秒秒延时函数
12 }MS5803ObjectType;
我们知道,一个对象仅作声明是不能使用的,我们需要先对其进行初始化,所以这里我们来考虑MS5803压力传感器对象的初始化函数。一般来说,初始化函数需要处理几个方面的问题。一是检查输入参数是否合理;二是为对象的属性赋初值;三是对对象作必要的初始化配置。据此我们设计MS5803压力传感器对象的初始化函数如下:
1 /* MS5803对象初始化 */
2 void MS5803Initialization(MS5803ObjectType *ms, //MS5803对象
3 MS5803ModelType model, //类型
4 MS5803PortType port, //通讯端口
5 uint8_t address, //I2C设备地址
6 MS5803Write write, //写数据函数
7 MS5803Read read, //读数据函数
8 MS5803ChipSelcet cs, //SPI片选信号
9 MS5803Delayms delayms //毫秒延时
10 )
11 {
12 if((ms==NULL)||(write==NULL)||(read==NULL)||(delayms==NULL))
13 {
14 return;
15 }
16 ms->Write=write;
17 ms->Read=read;
18 ms->Delayms=delayms;
19
20 ms->model=model;
21 ms->port=port;
22
23 if(port==I2C)
24 {
25 if((address==0x76)||(address==0x77))
26 {
27 ms->devAddress=(address<<1);
28 }
29 else if((address==0xEC)||(address==0xEE))
30 {
31 ms->devAddress=address;
32 }
33 else
34 {
35 ms->devAddress=0x00;
36 }
37
38 ms->ChipSelcet=cs;
39 }
40 else
41 {
42 ms->devAddress=0xFF;
43
44 if(cs==NULL)
45 {
46 ms->ChipSelcet=MS5803ChipSelect;
47 }
48 else
49 {
50 ms->ChipSelcet=cs;
51 }
52 }
53
54 ms->pressure=0.0;
55 ms->temperature=0.0;
56
57 ResetForMS5803(ms);
58
59 GetMS5803CalibrationData(ms);
60 }
我们已经完成了MS5803压力传感器对象类型的定义和对象初始化函数的设计。但我们的主要目标是获取对象的信息,接下来我们还要实现面向MS5803压力传感器的各类操作。
对MS5803压力传感器的其实就是基于前面我们所说的命令来实现的。根据这些命令的作用,我们可以大致分为三个方面:设备复位、ADC转换配置及数据获取、校准系数的获取。下面我么五年就从这三个方面来看一看MS5803压力传感器的操作。
复位操作可以在电源供电后的任意时刻发送,但一般要求在开机后首先发送复位程序。这样可以确保校准PROM加载到内部寄存器。当然复位操作也可以用来重置设备ROM以便从我们未知的情况中恢复。
在使用SPI通讯接口时,无论是模式0还是模式3其操作是一样的,SPI方式下的时序图如下:
在I2C通讯接口时,有时候通讯可能会出现SDA被未定义的状态阻塞的时候,可以通过复位操作来恢复通讯。I2C方式下的时序图如下:
1 /*复位MS5803操作*/
2 void ResetForMS5803(MS5803ObjectType *ms)
3 {
4 uint8_t command=COMMAND_RESET;
5 /*下发复位命令*/
6 ms->Write(ms,command);
7
8 ms->Delayms(3);
9 }
校准值是出厂时厂家校准的各种系数,每台设备都有差异,但每台设备是固定不变的,只需要一次读取就可以了,共有6个系数,均为16为整数。
用户复位后,执行一次PROM的read命令,读取校准PROM的内容,计算校准系数。关于PROM我们前面已经描述过了,总共有8个地址,地址0包含工厂数据和设置,地址1-6校准系数,地址7包含串行代码和CRC。
在SPI接口通讯模式下,我们发送都对应地址的命令,等待接收数据即可。命令为8位,返回数据为16位,时序图如下:
在I2C接口通讯模式下,PROM读命令由两部分组成。第一个命令将系统设置为PROM读模式。第二部分从系统中获取数据。首先发送读系数的命令,然后读取就可以了,每次读取1个,分6次读取。I2C模式下的时序图如下所示:
1 /*读取MS5803内存寄存器的数据*/
2 static uint16_t ReadPromFromMS5803(MS5803ObjectType *ms,uint8_t command)
3 {
4 /*下发读取指定内存单元的命令*/
5 ms->Write(ms,command);
6
7 /*接收读取的指定内存单元的值*/
8 uint8_t promValue[2];
9 ms->Read(ms,promValue,2);
10
11 uint16_t result=(uint16_t)promValue[0];
12 result=(result<<8)+(uint16_t)promValue[1];
13
14 return result;
15 }
读取转换结果值是我们的目的,可以读取温度和压力两个量,不过一次只能读一个。首先发送命令设定采集压力还是温度,并设定精度。然后发送读取的命令,最后读取对应的值。再使用校准系数计算出最终的物理值。
对于配置转换及读取转换的结果,在SPI接口模式下,按两步实现:先设置转换精度,等待转换结束再读取数据。具体的时序图如下:
对于配置转换及读取转换的结果,在I2C接口模式下,按三步实现:先设置转换精度,等待转换结束发送读ADC命令,最后读取数据。具体的时序图如下:
1 /*读取MS5803ADC的转换值*/
2 static uint32_t ReadConversionFromMS5803(MS5803ObjectType *ms,uint8_t command)
3 {
4 /*下发转化对象及精度配置命令*/
5 ms->Write(ms,command);
6
7 ms->Delayms(10);
8
9 /*下发读取ADC的命令*/
10 ms->Write(ms,COMMAND_ADC_READ);
11
12 if(ms->port==I2C)
13 {
14 ms->Delayms(10);
15 }
16
17 /*接收读取的ADC转换结果*/
18 uint8_t adcValue[3];
19 ms->Read(ms,adcValue,3);
20
21 uint32_t result=(uint32_t)adcValue[0];
22 result=(result<<8)+(uint32_t)adcValue[1];
23 result=(result<<8)+(uint32_t)adcValue[2];
24
25 return result;
26 }
我们已经设计并实现了MS5803压力传感器的驱动程序,这个驱动程序是否符合要求还需要验证。这一节我们就来设计一个简单的应用验证这一驱动程序。
3.1 、声明并初始化对象
使用基于对象的操作我们需要先得到这个对象,所以我们先要使用前面定义的MS5803压力传感器对象类型声明一个MS5803压力传感器对象变量,具体操作格式如下:
MS5803ObjectType ms5803;
声明了这个对象变量并不能立即使用,我们还需要使用驱动中定义的初始化函数对这个变量进行初始化。这个初始化函数所需要的输入参数如下:
MS5803ObjectType *ms,MS5803对象
MS5803ModelType model,类型
MS5803PortType port,通讯端口
uint8_t address,I2C设备地址
MS5803Write write,写数据函数
MS5803Read read,读数据函数
MS5803ChipSelcet cs,SPI片选信号
MS5803Delayms delayms,毫秒延时
对于这些参数,对象变量我们已经定义了。设备类型和通讯端口都是枚举,我们只需要根据实际的配置情况选择就可以了。设备地址则在采用I2C通讯时,根据实际输入。我们主要需要关注的是定义几个函数,并将函数指针作为参数。这几个函数的类型如下:
1 /*向MS5803下发指令,指令格式均为1个字节*/
2 typedef void (*MS5803Write)(MS5803ObjectType *ms,uint8_t command);
3
4 /*从MS5803读取多个字节数据的值*/
5 typedef void (*MS5803Read)(MS5803ObjectType *ms,uint8_t *rData,uint16_t rSize);
6
7 /*片选信号,用于SPI接口*/
8 typedef void (*MS5803ChipSelcet)(MS5803CSType en);
9
10 /*毫秒秒延时函数*/
11 typedef void (*MS5803Delayms)(volatile uint32_t nTime);
对于这几个函数我们根据样式定义就可以了,具体的操作可能与使用的硬件平台有关系。若采用的SPI接口则需注意片选操作,片选操作函数用于多设备需要软件操作时,如采用硬件片选可以传入NULL即可。同样如果采用的是I2C接口,则片选可以传入NULL即可。具体函数定义如下:
1 /*通过I2C1接口下发命令*/
2 static void SendCommandToMS5803(MS5803ObjectType *ms,uint8_t command)
3 {
4 HAL_I2C_Master_Transmit(&ms5803hi2c,ms->devAddress,&command,1,1000);
5 }
6
7 /*通过I2C1接口读取数据*/
8 static void GetDatasFromMS5803(MS5803ObjectType *ms,uint8_t *rData,uint16_t rSize)
9 {
10 HAL_I2C_Master_Receive(&ms5803hi2c,ms->devAddress,rData,rSize,1000);
11 }
对于延时函数我们可以采用各种方法实现。我们采用的STM32平台和HAL库则可以直接使用HAL_Delay()函数。于是我们可以调用初始化函数如下:
MS5803Initialization(&ms5803,MS580302BA,I2C,0xEC,SendCommandToMS5803,GetDatasFromMS5803,NULL,HAL_Delay);
这里我们使用的型号是MS580302BA,采用I2C接口,地址为0xEC。因为使用的是I2C接口所以片选输入NULL。
我们定义了对象变量并使用初始化函数给其作了初始化。接着我们就来考虑操作这一对象获取我们想要的数据。我们在驱动中已经将获取数据并转换为转换值的比例值,接下来我们使用这一驱动开发我们的应用实例。
1 /*获取压力变送器数据*/
2 void GetPressureSenserData(void)
3 {
4 float pressure=0.0;
5 float temperature=0.0;
6
7 GetMS5803ConversionValue(&ms5803,MS5803_OSR4096,MS5803_OSR4096);
8
9 pressure=ms5803.pressure;
10 temperature=ms5803.temperature;
11 }
在本篇中,我们实现了MS5803压力传感器的驱动程序,并基于驱动程序开发了简单的验证应用。我们也多次在项目中使用MS5803压力传感器,使用的就是这一套驱动程序,应用的效果非常不错。
在使用I2C接口时,引脚CSB应连接到VDD或GND。因为MS5803的地址位仅有1位是可以通过CSB设定的,所以一条I2C总线最多只能挂2个MS5803模块。当CSB接VDD时地址最低位为1;当CSB接GND时地址最低位为0。
在使用驱动时需注意,采用SPI接口的器件需要考虑片选操作的问题。如果片选信号是通过硬件电路来实现的,我们在初始化时给其传递NULL值。如果是软件操作片选则传递我们编写的片选操作函数。
源码下载:https://github.com/foxclever/ExPeriphDriver
全部0条评论
快来发表一下你的评论吧 !