目的
学习MS5611的原理和MS5611初始化方法、通过使用 SPI 驱动访问气压高度等传感数据操作数据输出寄存器获取得到MS5611实时数据。
实验原理
MS5611气压传感器是由MEAS (瑞士)推出的一款SPI和IIC总线接口的新一代高分辨率压传感器,分辨率可达到10cm。该传感器模块包括一个高线性度的压力传感器和一个超低功耗的24位Σ模数转换器(工厂校准系数)。MS5611提供了一个精确的24位数字压力值和温度值以及不同的操作模式,可以提高转换速度并优化电流消耗。高分辨率的温度输出无须额外传感器可实现高度计/温度计功能。可以与几乎任何微控制器连接。通信协议简单,无需在设备内部寄存器编程。尺寸小可以集成在移动设备中。这款传感器采用领先的MEMS技术并得益于MEAS (瑞士)十余年的成熟设计以及大批量制造经验,保证产品具有高稳定性以及非常低的压力信号滞后。
性能参数
最大工作范围
电气特性
模数转换(ADC)
SPI模式
外部微控制器通过输入SCLK(串行时钟)和SDI(串行数据)来传输数据。在SPI模式下时钟极性和相位允许同时模式0和模式3。SDO(串行数据)引脚为传感器的响应输出。CSB(芯片选择)引脚用来控制芯片使能/禁用,所以,其他设备可以共用同组SPI总线。在命令发送完毕或命令执行结束(例如结束的转换)时CSB引脚将被拉高。在SPI总线空闲模式下模块有较好的噪声性能和在ADC转换时与其他设备链接。
SPI命令
下表描述中每个命令的大小数是1字节(8位)。执行ADC read指令后将会返回一个24-bit的结果,执行PROM read指令后返回16-bit的结果。存储器(PROM)的地址在PROM read命令中的a2、a1以及a0位。
步骤
- 驱动MS5611传感器以SPI通讯协议为基础来编写。在bsp_spi.c编写SPI1_ReadWriteByte()读写函数,具体代码如下。
// SPI1读写一个字节
uint8_t SPI1_ReadWriteByte(uint8_t Data)
{
uint8_t retry=0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) { if(retry++>200) { spi_lost++; return 0xFF;} } // 发送缓存标志位为空
SPI_I2S_SendData(SPI1, Data); // 通过外设SPI1发送一个数据
retry=0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) { if(retry++>200) { spi_lost++; return 0xFF;} } //接收缓存标志位不为空
return SPI_I2S_ReceiveData(SPI1); // 通过SPI1返回接收数据
}
- 新建文件bsp_ms5611.c和bsp_ms5611.h。将文件添加到工程中,在文件bsp_ms5611.c中编写寄存器、读寄存器、连续写以及连续读函数。具体代码如下:
// MS5611写寄存器
static void MS5611_Write_Byte(uint8_t REG_Address, uint8_t REG_data)
{
MS5611_CS(0); // 使能SPI传输
SPI1_ReadWriteByte(REG_Address); // 发送寄存器号
SPI1_ReadWriteByte(REG_data); // 写入寄存器的值
MS5611_CS(1); // 禁止SPI传输
}
// MS5611读寄存器
uint8_t MS5611_Read_Byte(uint8_t REG_Address)
{
MS5611_CS(0); // 使能SPI传输
SPI1_ReadWriteByte(REG_Address); // 发送寄存器号
uint8_t REG_data = SPI1_ReadWriteByte(0); // 读取寄存器内容
MS5611_CS(1); // 禁止SPI传输
return REG_data; // 返回状态值
}
// MS5611连续写
void MS5611_Write_NByte(uint8_t REG_Address, uint8_t * buf, uint8_t len)
{
MS5611_CS(0); // 使能SPI传输
SPI1_ReadWriteByte(REG_Address); // 发送寄存器值(位置), 并读取状态值
for(uint8_t count=0; count<len; count++)
SPI1_ReadWriteByte(*buf++); // 写入数据
MS5611_CS(1); // 禁止SPI传输
}
// MS5611连续读
static void MS5611_Read_NByte(uint8_t REG_Address, uint8_t * buf, uint8_t len)
{
MS5611_CS(0); // 使能SPI传输
SPI1_ReadWriteByte(REG_Address); // 发送寄存器地址,并读取状态值
for(uint8_t count=0; count<len; count++)
buf[count] = SPI1_ReadWriteByte(0); // 读出数据
MS5611_CS(1); // 禁止SPI传输
}
- 在文件bsp_ms5611.c中编写ms5611复位函数。具体代码如下:
// 复位
static inline void MS5611_Reset(void)
{
MS5611_Write_Byte(CMD_RESET, 1);
}
- 在文件bsp_ms5611.c中编写ms5611读取校准信息函数。具体代码如下:
// 读取校准消息
static void MS5611_Read_Prom(void)
{
uint8_t buffer[2] = { 0, 0 };
for (uint8_t i=0; i<PROM_CNT; i++)
{
MS5611_Read_NByte(CMD_PROM_RD + i * 2, buffer, 2); // send PROM READ command
ms5611.C[i] = ((uint16_t)buffer[0]) << 8 | buffer[1];
}
}
- 在文件bsp_ms5611.c中编写ms5611AD转换物理量函数以及计算高度值函数。具体代码如下:
// AD值转换为物理量
static void MS5611_Calculate_Pressure(void)
{
static int32_t _TEMP;
static int64_t _OFF;
static int64_t _SENS;
uint32_t D2 = (T_buf[0] << 16) | (T_buf[1] << 8) | T_buf[2];
uint32_t D1 = (P_buf[0] << 16) | (P_buf[1] << 8) | P_buf[2];
/* temperature offset (in ADC units) */
int32_t dT = (int32_t)D2 - ((int32_t)ms5611.C[5] << 8);
/* absolute temperature in centidegrees - note intermediate value is outside 32-bit range */
_TEMP = 2000 + (((int64_t)dT * ms5611.C[6]) >> 23);
_OFF = ((int64_t)ms5611.C[2] << 16) + (((int64_t)dT * ms5611.C[4]) >> 7);
_SENS = ((int64_t)ms5611.C[1] << 15) + (((int64_t)dT * ms5611.C[3]) >> 8);
/* MS5611 temperature compensation */
if (_TEMP < 2000) // temperature lower than 20degC
{
int32_t T2 = ((int64_t)dT * (int64_t)dT) >> 31;
int64_t f = ((int64_t)_TEMP - 2000) * ((int64_t)_TEMP - 2000);
int64_t OFF2 = 5 * f >> 1;
int64_t SENS2 = 5 * f >> 2;
if (_TEMP < -1500) { // temperature lower than -15degC
int64_t f2 = ((int64_t)_TEMP + 1500) * ((int64_t)_TEMP + 1500);
OFF2 += 7 * f2;
SENS2 += 11 * f2 >> 1;
}
_TEMP -= T2;
_OFF -= OFF2;
_SENS -= SENS2;
}
/* pressure calculation, result in Pa */
ms5611.pressure = ((((int64_t)D1 * _SENS) >> 21) - _OFF) >> 15;
ms5611.temperature = _TEMP;
}
static void MS5611_Calculate_Altitube(void)
{
static TickType_t last_time;
TickType_t time = xTaskGetTickCount();
float dt = (time - last_time) * portTICK_RATE_MS * MS2S;
last_time = time;
/*
* Solve:
*
* / -(aR / g) \
* | (p / p1) . T1 | - T1
* \ /
* h = ------------------------------- + h1
* a
*/
ms5611.altitube_raw = (((powf((ms5611.pressure / _msl_pressure), (-(a * R) / g))) * T1) - T1) / a; // 根据气压计算高度值
ms5611.altitube = LPButter_BaroAlt(ms5611.altitube_raw);
ms5611.speed = (ms5611.altitube - ms5611.last_altitube) / dt; // 计算气压计速度
ms5611.last_altitube = ms5611.altitube;
ms5611.update = true;
}
- 在文件bsp_ms5611.c中编写ms5611初始化函数、数据更新函数、CRC校准函数。具体代码如下:
// MS5611初始化
void MS5611_Init(void)
{
MS5611_Reset(); // 复位
vTaskDelay(10/portTICK_RATE_MS);
MS5611_Read_Prom();
MS5611_Check();
ms5611.enabled = true;
ms5611.present = true;
}
// MS5611更新数据
void MS5611_Update()
{
MS5611_Write_Byte(CMD_ADC_CONV | CMD_ADC_D2 | CMD_ADC_4096, 1); // 开始温度转换
vTaskDelay(10/portTICK_RATE_MS); // 等待转换完成
MS5611_Read_NByte(CMD_ADC_READ, T_buf, 3); // 读取转换的温度 8569150
MS5611_Write_Byte(CMD_ADC_CONV | CMD_ADC_D1 | CMD_ADC_4096, 1); // 开始气压转换
vTaskDelay(10/portTICK_RATE_MS); // 等待转换完成
MS5611_Read_NByte(CMD_ADC_READ, P_buf, 3); // 读取转换的气压 9085466
MS5611_Calculate_Pressure(); // 计算气压值
MS5611_Calculate_Altitube(); // 计算高度
// 计算气压计高度偏移
if(!mav.arm)
{
ms5611.altitube_offset = ms5611.altitube;
}
}
// CRC校验,成功返回true
static bool ms5611_crc(uint16_t *prom)
{
int32_t i, j;
uint32_t res = 0;
uint8_t zero = 1;
uint8_t crc = prom[7] & 0xF;
prom[7] &= 0xFF00;
// if eeprom is all zeros, we're probably fucked - BUT this will return valid CRC lol
for (i = 0; i < 8; i++) {
if (prom[i] != 0)
zero = 0;
}
if (zero)
return false;
for (i = 0; i < 16; i++) {
if (i & 1)
res ^= ((prom[i >> 1]) & 0x00FF);
else
res ^= (prom[i >> 1] >> 8);
for (j = 8; j > 0; j--) {
if (res & 0x8000)
res ^= 0x1800;
res <<= 1;
}
}
prom[7] |= crc;
if (crc == ((res >> 12) & 0xF))
return true;
return false;
}
void MS5611_Check()
{
ms5611.health = ms5611_crc(ms5611.C);
}
- 最后将数据打印出来即可。
更多交流欢迎关注作者抖音号:81849645041