| 本帖最后由 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;
 }
 }
 
 |