基于STM32平台的BMP180测试(模拟IIC)

1.测试描述:

使用模拟IIC,从BMP180中获取ID号、温度值、气压值以及计算海拔高度。

2.测试准备:

硬件平台:原子战舰V3开发板
测试工具:逻辑分析仪、串口调试工具


3.数据手册解读:

(1) 首先是多个字节的读取时序图,从这里也可以看出BMP180芯片的地址+写信号是0xEE,地址+读信号为0xEF,当然手册前面也有提到过,有兴趣的可以自己去查看一下手册。

基于STM32平台的BMP180测试(模拟IIC)_第1张图片

(2) 然后本人测试时默认使用的是下图画横线部分的部分,也就是气压这块使用的是低功耗模式。OSS的值要注意,因为后期的计算需要这个,并且读取UT值时也要做对应的处理。

基于STM32平台的BMP180测试(模拟IIC)_第2张图片

(3) 下图就是手册里给出的利用BMP180里的参数计算气压和温度的流程图。特别注意流程里的OSS,由于本测试使用的OSS = 0,所以在实际代码中省去了这一部分。

基于STM32平台的BMP180测试(模拟IIC)_第3张图片

4.测试代码:

由于本工程是直接使用原子战舰的标准例程-库函数版本\实验23 IIC实验源码改过来的,所以模拟IIC部分使用的原子这块的代码。BMP180部分由本人编写。
(1) 首先是BMP180.C的代码片

#include "bmp180.h"
#include "delay.h"
#include "math.h"

//存储BMP180数据的结构
_bmp180 bmp180;

//BMP180初始化
//对使用的IIC端口进行初始化
void BMP_Init(void)
{
    IIC_Init();
}

//写一个数据到BMP180
void BMP_WriteOneByte(uint8_t WriteAddr,uint8_t DataToWrite)
{
    IIC_Start();

    IIC_Send_Byte(0xEE);
    IIC_Wait_Ack();

    IIC_Send_Byte(WriteAddr);
    IIC_Wait_Ack();

    IIC_Send_Byte(DataToWrite);
    IIC_Wait_Ack();
    IIC_Stop();
}

//从BMP180读一个字节数据
uint8_t BMP_ReadOneByte(uint8_t ReadAddr)
{
    uint8_t data = 0;

    IIC_Start();

    IIC_Send_Byte(0xEE);
    IIC_Wait_Ack();

    IIC_Send_Byte(ReadAddr);
    IIC_Wait_Ack();

    IIC_Start();

    IIC_Send_Byte(0xEF);
    IIC_Wait_Ack();

    data = IIC_Read_Byte(1);
    IIC_Stop();

    return data;
}

//从BMP180读一个16位的数据
short BMP_ReadTwoByte(uint8_t ReadAddr)
{
    short data;
    uint8_t msb,lsb;

    IIC_Start();

    IIC_Send_Byte(0xEE);
    IIC_Wait_Ack();

    IIC_Send_Byte(ReadAddr);
    IIC_Wait_Ack();

    IIC_Start();

    IIC_Send_Byte(0xEF);
    IIC_Wait_Ack();

    msb = IIC_Read_Byte(1);
    lsb = IIC_Read_Byte(0);

    IIC_Stop();

    data = msb*256 + lsb;

    return data;
}

//从BMP180的获取计算参数
void BMP_ReadCalibrationData(void)
{
    bmp180.AC1 = BMP_ReadTwoByte(0xAA);
    bmp180.AC2 = BMP_ReadTwoByte(0xAC);
    bmp180.AC3 = BMP_ReadTwoByte(0xAE);
    bmp180.AC4 = BMP_ReadTwoByte(0xB0);
    bmp180.AC5 = BMP_ReadTwoByte(0xB2);
    bmp180.AC6 = BMP_ReadTwoByte(0xB4);
    bmp180.B1  = BMP_ReadTwoByte(0xB6);
    bmp180.B2  = BMP_ReadTwoByte(0xB8);
    bmp180.MB  = BMP_ReadTwoByte(0xBA);
    bmp180.MC  = BMP_ReadTwoByte(0xBC);
    bmp180.MD  = BMP_ReadTwoByte(0xBE);
}

//从BMP180读取未修正的温度
long BMP_Read_UT(void)
{
    long temp = 0;
    BMP_WriteOneByte(0xF4,0x2E);

    delay_ms(5);
    temp = (long)BMP_ReadTwoByte(0xF6);
    return temp;
}

//从BMP180读取未修正的大气压
long BMP_Read_UP(void)
{
    long pressure = 0;

    BMP_WriteOneByte(0xF4,0x34);
    delay_ms(5);

    pressure = (long)BMP_ReadTwoByte(0xF6);
    //pressure = pressure + BMP_ReadOneByte(0xf8);
    pressure &= 0x0000FFFF;

    return pressure;
}

//用获取的参数对温度和大气压进行修正,并计算海拔
void BMP_UncompemstatedToTrue(void)
{
    bmp180.UT = BMP_Read_UT();//第一次读取错误
    bmp180.UT = BMP_Read_UT();//进行第二次读取修正参数
    bmp180.UP = BMP_Read_UP();

    bmp180.X1 = ((bmp180.UT - bmp180.AC6) * bmp180.AC5) >> 15;
    bmp180.X2 = (((long)bmp180.MC) << 11) / (bmp180.X1 + bmp180.MD);
    bmp180.B5 = bmp180.X1 + bmp180.X2;
    bmp180.Temp  = (bmp180.B5 + 8) >> 4;

    bmp180.B6 = bmp180.B5 - 4000;
    bmp180.X1 = ((long)bmp180.B2 * (bmp180.B6 * bmp180.B6 >> 12)) >> 11;
    bmp180.X2 = ((long)bmp180.AC2) * bmp180.B6 >> 11;
    bmp180.X3 = bmp180.X1 + bmp180.X2;

    bmp180.B3 = ((((long)bmp180.AC1) * 4 + bmp180.X3) + 2) /4;
    bmp180.X1 = ((long)bmp180.AC3) * bmp180.B6 >> 13;
    bmp180.X2 = (((long)bmp180.B1) *(bmp180.B6*bmp180.B6 >> 12)) >>16;
    bmp180.X3 = ((bmp180.X1 + bmp180.X2) + 2) >> 2;
    bmp180.B4 = ((long)bmp180.AC4) * (unsigned long)(bmp180.X3 + 32768) >> 15;
    bmp180.B7 = ((unsigned long)bmp180.UP - bmp180.B3) * 50000;

    if(bmp180.B7 < 0x80000000)
    {
        bmp180.p = (bmp180.B7 * 2) / bmp180.B4;     
    }
    else
    {
        bmp180.p = (bmp180.B7 / bmp180.B4) * 2;
    }

    bmp180.X1 = (bmp180.p >> 8) * (bmp180.p >>8);
    bmp180.X1 = (((long)bmp180.X1) * 3038) >> 16;
    bmp180.X2 = (-7357 * bmp180.p) >> 16;

    bmp180.p = bmp180.p + ((bmp180.X1 + bmp180.X2 + 3791) >> 4);

    bmp180.altitude = 44330 * (1-pow(((bmp180.p) / 101325.0),(1.0/5.255)));  
}

(2) 然后是BMP180.H部分

#ifndef _BMP180_H_
#define _BMP180_H_

#include "myiic.h"

typedef struct __BMP180
{
    short AC1;
    short AC2;
    short AC3;
    unsigned short AC4;
    unsigned short AC5;
    unsigned short AC6;
    short B1;
    short B2;
    short MB;
    short MC;
    short MD;
    long UT;
    long UP;
    long X1;
    long X2;
    long X3;
    long B3;
    unsigned long B4;
    long B5;
    long B6;
    long B7;
    long p;
    long Temp;
    float altitude;
}_bmp180;

extern _bmp180 bmp180;

void BMP_Init(void);
uint8_t BMP_ReadOneByte(uint8_t ReadAddr);
void BMP_WriteOneByte(uint8_t WriteAddr,uint8_t DataToWrite);
short BMP_ReadTwoByte(uint8_t ReadAddr);
void BMP_ReadCalibrationData(void);
long BMP_Read_UT(void);
long BMP_Read_UP(void);
void BMP_UncompemstatedToTrue(void);

#endif

由于是测试,所以将所有的参数都建了一个结构体,方便DEBUG时通过WATCH窗口查看,实际使用当中可以将一些非必要的参数做成临时变量。

5.测试时遇到的问题:

在BMP_UncompemstatedToTrue函数中,开头时首先读取UT和UP的值。开始时两者都只是读取一次,测算时发现UT值并未正确读出,为0,从而导致算出来的实际温度值在-71度左右,海拔在2500多米。通过逻辑分析仪监测,发现在第一次读取数据时,模拟IIC部分发出的数据是错误的。如下图画线处所示:
基于STM32平台的BMP180测试(模拟IIC)_第4张图片
但是从图中可以看出,后面部分是正常的。针对这个现象我对延时、底层驱动模块都做了改变和测试,没有任何的改观。所以后面将UT值读取两次,就是为了消除这个错误,这也是一个折中的办法。经修改后实测读取正常。

6.测试结果:

基于STM32平台的BMP180测试(模拟IIC)_第5张图片

本人在上海,平均海拔在4m的样子,温度值基本一致。由于海拔、温度、气压三者之间的联系,所以该气压也大致可以认定为测算出的实际大气压。

7.测试源码:

链接:https://pan.baidu.com/s/1qYmD2VU

你可能感兴趣的:(stm32)