本帖最后由 mohanwei 于 2014-12-19 22:37 编辑
MS5611的I2C版驱动,I2C底层操作接口基于上一贴的驱动库
这份代码结构不尽合理,但是为了不做大改动,先只是在作者的基础上把SPI读写改为I2C,等飞机飞稳后,下一次迭代再改进。
MS5611.c
改动:2014-12-19 调试时发现MS5611无法读取,观察波形发现是发送起始位、从地址后,MS5611没有发出应答,核实手册,发现是原先对CSB的理解有误……从地址的bit1应该是CSB的反码。修改后(参考下面代码的红色字体部分)可以正常读取温度和气压,用手指摸MS5611,能看到温度值升高几度,拿开手指过一会温度又会降下来,说明IC正常工作了。
#define DEBUG_MODULE "MS5611"
#include "includes.h"
/*
COMMANDS
The MS5611-01BA has only five basic commands:
1. Reset
2. Read PROM (128 bit of calibration words)
3. D1 conversion
4. D2 conversion
5. Read ADC result (24 bit pressure / temperature)
*/
//在我们的电路里,MS5611的CSB接地,bit1为CSB的反码,所以I2C从地址为1110 111x(x为读写标记):
#define MS5611_ReadAddr 0xEF //从地址+读标记
#define MS5611_WriteAddr 0xEE //从地址+写标记
#define EXTRA_PRECISION 5 // trick to add more precision to the pressure and temp readings
#define MS5611_CONVERSION_TIME_MS 10 // conversion time in milliseconds. 10 is minimum
#define PRESSURE_PER_TEMP 5 // Length of reading cycle: 1x temp, rest pressure. Good values: 1-10
#define FIX_TEMP 25 // Fixed Temperature. ASL is a function of pressure and temperature, but as the temperature changes so much (blow a little towards the flie and watch it drop 5 degrees) it corrupts the ASL estimates.
// TLDR: Adjusting for temp changes does more harm than good.
typedef struct
{
uint16_t psens;
uint16_t off;
uint16_t tcs;
uint16_t tco;
uint16_t tref;
uint16_t tsens;
} CalReg;
//static uint8_t devAddr;
//static I2C_TypeDef *I2Cx;
static bool isInit;
static CalReg calReg;
static uint32_t lastPresConv;
static uint32_t lastTempConv;
static int32_t tempCache;
static uint8_t readState=0;
static uint32_t lastConv=0;
static int32_t tempDeltaT;
bool ms5611Init(void)
{
if (isInit)
return TRUE;
ms5611Reset(); // reset the device to populate its internal PROM registers
vTaskDelay(M2T(5));
if (ms5611ReadPROM() == FALSE) // reads the PROM into object variables for later use
{
return FALSE;
}
isInit = TRUE;
return TRUE;
}
bool ms5611SelfTest(void)
{
bool testStatus = TRUE;
int32_t rawPress;
int32_t rawTemp;
int32_t deltaT;
float pressure;
float temperature;
if (!isInit)
return FALSE;
ms5611StartConversion(MS5611_D1 + MS5611_OSR_4096);
vTaskDelay(M2T(MS5611_CONVERSION_TIME_MS));
rawPress = ms5611GetConversion(MS5611_D1 + MS5611_OSR_4096);
ms5611StartConversion(MS5611_D2 + MS5611_OSR_4096);
vTaskDelay(M2T(MS5611_CONVERSION_TIME_MS));
rawTemp = ms5611GetConversion(MS5611_D2 + MS5611_OSR_4096);
deltaT = ms5611CalcDeltaTemp(rawTemp);
temperature = ms5611CalcTemp(deltaT);
pressure = ms5611CalcPressure(rawPress, deltaT);
if (ms5611EvaluateSelfTest(MS5611_ST_PRESS_MIN, MS5611_ST_PRESS_MAX, pressure, "pressure") &&
ms5611EvaluateSelfTest(MS5611_ST_TEMP_MIN, MS5611_ST_TEMP_MAX, temperature, "temperature"))
{
DEBUG_PRINT("Self test [OK].\n");
}
else
{
testStatus = FALSE;
}
return testStatus;
}
bool ms5611EvaluateSelfTest(float min, float max, float value, char* string)
{
if (value < min || value > max)
{
DEBUG_PRINT("Self test %s [FAIL]. low: %0.2f, high: %0.2f, measured: %0.2f\n",
string, min, max, value);
return FALSE;
}
return TRUE;
}
float ms5611GetPressure(uint8_t osr)
{
// see datasheet page 7 for formulas
int32_t rawPress;
int64_t dT, off, sens;
rawPress = ms5611RawPressure(osr);
dT = (int64_t)ms5611GetDeltaTemp(osr);
if (dT == 0)
{
return 0;
}
off = (((int64_t)calReg.off) << 16) + ((calReg.tco * dT) >> 7);
sens = (((int64_t)calReg.psens) << 15) + ((calReg.tcs * dT) >> 8);
if (rawPress != 0)
{
return ((((rawPress * sens) >> 21) - off) >> (15 - EXTRA_PRECISION))
/ ((1 << EXTRA_PRECISION) * 100.0);
}
else
{
return 0;
}
}
float ms5611CalcPressure(int32_t rawPress, int32_t dT)
{
int64_t off;
int64_t sens;
if (rawPress == 0 || dT == 0)
{
return 0;
}
off = (((int64_t)calReg.off) << 16) + ((calReg.tco * (int64_t)dT) >> 7);
sens = (((int64_t)calReg.psens) << 15) + ((calReg.tcs * (int64_t)dT) >> 8);
return ((((rawPress * sens) >> 21) - off) >> (15 - EXTRA_PRECISION))
/ ((1 << EXTRA_PRECISION) * 100.0);
}
float ms5611GetTemperature(uint8_t osr)
{
// see datasheet page 7 for formulas
int32_t dT;
dT = ms5611GetDeltaTemp(osr);
if (dT != 0)
{
return ms5611CalcTemp(dT);
}
else
{
return 0;
}
}
int32_t ms5611GetDeltaTemp(uint8_t osr)
{
int32_t rawTemp = ms5611RawTemperature(osr);
if (rawTemp != 0)
{
return ms5611CalcDeltaTemp(rawTemp);
}
else
{
return 0;
}
}
float ms5611CalcTemp(int32_t deltaT)
{
if (deltaT == 0)
{
return 0;
}
else
{
return (float)(((1 << EXTRA_PRECISION) * 2000)
+ (((int64_t)deltaT * calReg.tsens) >> (23 - EXTRA_PRECISION)))
/ ((1 << EXTRA_PRECISION)* 100.0);
}
}
int32_t ms5611CalcDeltaTemp(int32_t rawTemp)
{
if (rawTemp == 0)
{
return 0;
}
else
{
return rawTemp - (((int32_t)calReg.tref) << 8);
}
}
int32_t ms5611RawPressure(uint8_t osr)
{
uint32_t now = xTaskGetTickCount();
if (lastPresConv != 0 && (now - lastPresConv) >= MS5611_CONVERSION_TIME_MS)
{
lastPresConv = 0;
return ms5611GetConversion(MS5611_D1 + osr);
}
else
{
if (lastPresConv == 0 && lastTempConv == 0)
{
ms5611StartConversion(MS5611_D1 + osr);
lastPresConv = now;
}
return 0;
}
}
int32_t ms5611RawTemperature(uint8_t osr)
{
uint32_t now = xTaskGetTickCount();
if (lastTempConv != 0 && (now - lastTempConv) >= MS5611_CONVERSION_TIME_MS)
{
lastTempConv = 0;
tempCache = ms5611GetConversion(MS5611_D2 + osr);
return tempCache;
}
else
{
if (lastTempConv == 0 && lastPresConv == 0)
{
ms5611StartConversion(MS5611_D2 + osr);
lastTempConv = now;
}
return tempCache;
}
}
// see page 11 of the datasheet
bool ms5611StartConversion(uint8_t command)
{
bool result = FALSE;//预置执行结果为:失败
I2C_Start();//起始条件
I2C_Send_Byte(MS5611_WriteAddr);//发送从地址+写标记
if(I2C_Rec_Ack()==0)//收到应答
{
I2C_Send_Byte(command);//发送启动转换命令
if(I2C_Rec_Ack()==0)//收到应答
{
result = TRUE;//成功
}
}
I2C_Stop();//停止条件
return(result);
}
int32_t ms5611GetConversion(uint8_t command)
{
int32_t conversion = 0;
// uint8_t buffer[MS5611_D1D2_SIZE];
bool result = FALSE;//预置执行结果为:失败
// start read sequence
I2C_Start();//起始条件
I2C_Send_Byte(MS5611_WriteAddr);//发送从地址+写标记
if(I2C_Rec_Ack()==0)//收到应答
{
I2C_Send_Byte(0x00);//发送读取ADC命令
if(I2C_Rec_Ack()==0)//收到应答
{
result = TRUE;//成功
}
}
I2C_Stop();//停止条件
// i2cdevWriteByte(I2Cx, devAddr, I2CDEV_NO_MEM_ADDR, 0);
// Read conversion
if(result)
{
result = _Fail;//预置执行结果为:失败
I2C_Start();//起始条件
I2C_Send_Byte(MS5611_ReadAddr);//发送从地址+读标记
if(I2C_Rec_Ack()==0)//收到应答
{
conversion = ((u32)I2C_Receive_Byte())<<16;//读取24位数据的高字节
I2C_ACK();//送出ACK
conversion += ((u32)I2C_Receive_Byte())<<8;//读取24位数据的中字节
I2C_ACK();//送出ACK
conversion += ((u32)I2C_Receive_Byte())<<0;//读取24位数据的低字节
I2C_NAK();//送出NAK
result = TRUE;//成功
}
I2C_Stop();//停止条件
}
// i2cdevRead(I2Cx, devAddr, I2CDEV_NO_MEM_ADDR, MS5611_D1D2_SIZE, buffer);
// conversion = ((int32_t)buffer[0] << 16) |
// ((int32_t)buffer[1] << 8) | buffer[2];
return conversion;
}
/**
* Reads factory calibration and store it into object variables.
*/
bool ms5611ReadPROM(void)//从MS5611读取出厂校准值
{
uint16_t* pCalRegU16 = (uint16_t*)&calReg;
int32_t i = 0;
bool result = FALSE;//预置执行结果为:失败
for (i = 0; i < MS5611_PROM_REG_COUNT; i++)
{
//第1步:设置MS5611进入“读PROM”模式:
result = _Fail;//预置执行结果为:失败
I2C_Start();//起始条件
I2C_Send_Byte(MS5611_WriteAddr);//发送从地址+写标记
if(I2C_Rec_Ack()==0)//收到应答
{
I2C_Send_Byte(MS5611_PROM_BASE_ADDR + (i * MS5611_PROM_REG_SIZE));//发送读PROM命令
if(I2C_Rec_Ack()==0)//收到应答
{
result = TRUE;//成功
}
}
I2C_Stop();//停止条件
//第2步:读取校准值:
if(result)
{
result = _Fail;//预置执行结果为:失败
I2C_Start();//起始条件
I2C_Send_Byte(MS5611_ReadAddr);//发送从地址+读标记
if(I2C_Rec_Ack()==0)//收到应答
{
pCalRegU16 = I2C_Receive_Byte();//读取16位数据的高字节
I2C_ACK();//送出ACK
pCalRegU16 <<= 8;
pCalRegU16 += I2C_Receive_Byte();//读取16位数据的低字节
I2C_NAK();//送出NAK
result = TRUE;//成功
}
I2C_Stop();//停止条件
}
}
return result;
}
/**
* Send a reset command to the device. With the reset command the device
* populates its internal registers with the values read from the PROM.
*/
bool ms5611Reset(void)//向MS5611发送复位命令
{
bool result = FALSE;//预置执行结果为:失败
I2C_Start();//起始条件
I2C_Send_Byte(MS5611_WriteAddr);//发送从地址+写标记
if(I2C_Rec_Ack()==0)//收到应答
{
I2C_Send_Byte(MS5611_CMD_RESET);//发送复位命令
if(I2C_Rec_Ack()==0)//收到应答
{
result = TRUE;//成功
}
}
I2C_Stop();//停止条件
return(result);
}
/**
* Gets pressure, temperature and above sea level altitude estimate (asl).
* Best called at 100hz. For every PRESSURE_PER_TEMP-1 pressure readings temp is read once.
* Effective 50-90hz baro update and 50-10hz temperature update if called at 100hz.
*/
void ms5611GetData(float* pressure, float* temperature, float* asl)
{
int32_t tempPressureRaw, tempTemperatureRaw;
// Dont reader faster than we can
uint32_t now = xTaskGetTickCount();
if ((now - lastConv) < MS5611_CONVERSION_TIME_MS)
{
return;
}
lastConv = now;
if (readState == 0)
{
// read temp
++readState;
tempTemperatureRaw = ms5611GetConversion(MS5611_D2 + MS5611_OSR_DEFAULT);
tempDeltaT = ms5611CalcDeltaTemp(tempTemperatureRaw);
*temperature = ms5611CalcTemp(tempDeltaT);
// cmd to read pressure
ms5611StartConversion(MS5611_D1 + MS5611_OSR_DEFAULT);
}
else
{
// read pressure
++readState;
tempPressureRaw = ms5611GetConversion(MS5611_D1 + MS5611_OSR_DEFAULT);
*pressure = ms5611CalcPressure(tempPressureRaw, tempDeltaT);
*asl = ms5611PressureToAltitude(pressure);
if (readState == PRESSURE_PER_TEMP){
// cmd to read temp
ms5611StartConversion(MS5611_D2 + MS5611_OSR_DEFAULT);
readState = 0;
}
else
{
// cmd to read pressure
ms5611StartConversion(MS5611_D1 + MS5611_OSR_DEFAULT);
}
}
}
//TODO: pretty expensive function. Rather smooth the pressure estimates and only call this when needed
/**
* Converts pressure to altitude above sea level (ASL) in meters
*/
float ms5611PressureToAltitude(float* pressure/*, float* ground_pressure, float* ground_temp*/)
{
if (*pressure > 0)
{
//return (1.f - pow(*pressure / CONST_SEA_PRESSURE, CONST_PF)) * CONST_PF2;
//return ((pow((1015.7 / *pressure), CONST_PF) - 1.0) * (25. + 273.15)) / 0.0065;
return ((pow((1015.7 / *pressure), CONST_PF) - 1.0) * (FIX_TEMP + 273.15)) / 0.0065;
}
else
{
return 0;
}
}
|