STM32F4无人机气压计数值获取

目的

学习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​​​​​​

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奚海蛟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值