基于STM32标准库的MS5837程序移植

  • 一、准备工作
  • 1. 硬件电路
  • 2. 新建工程
  • 二、开始移植
  • 1. IIC底层模拟
  • 2. MS5837移植
  • 3. 主函数编写
  • 4. 代码调试结果
  • 三、源代码下载


一、准备工作

1. 硬件电路

典型电路图:

mesa库移植 msd移植_控制器


实际硬件图:

mesa库移植 msd移植_控制器_02

2. 新建工程

基于标准库建立MDK工程,使用C++与C混合编程,工程目录如图:

mesa库移植 msd移植_mesa库移植_03

二、开始移植

1. IIC底层模拟

(1)编写头文件"myiic.h"如下:

#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"

#ifdef __cplusplus
extern "C" {
#endif

#define SDA_IN()  {GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=8<<12;}
#define SDA_OUT() {GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=3<<12;}

#define IIC_SCL    PAout(12) //SCL
#define IIC_SDA    PAout(11) //SDA	 
#define READ_SDA   PAin(11)  //输入SDA 
 
void IIC_Init(void);                //初始化IIC的IO口				 
void IIC_Start(void);								//发送IIC开始信号
void IIC_Stop(void);	  						//发送IIC停止信号
void IIC_Send_Byte(uint8_t txd);					//IIC发送一个字节
uint8_t IIC_Read_Byte(uint8_t ack);//IIC读取一个字节
uint8_t IIC_Wait_Ack(void); 							//IIC等待ACK信号
void IIC_Ack(void);									//IIC发送ACK信号
void IIC_NAck(void);								//IIC不发送ACK信号

#endif

#ifdef __cplusplus
}

#endif

(2)编写c文件"myiic.c"如下:

#include "myiic.h"
#include "delay.h"

//初始化IIC
void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA, ENABLE );	
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
 
	IIC_SCL=1;
	IIC_SDA=1;
	delay_ms(1);	
}
//产生IIC起始信号
void IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}	  
//产生IIC停止信号
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;//发送I2C总线结束信号
	delay_us(4);							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
uint8_t IIC_Wait_Ack(void)
{
	uint8_t ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
//不产生ACK应答		    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(uint8_t txd)
{                        
    uint8_t t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	    
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
uint8_t IIC_Read_Byte(uint8_t ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

2. MS5837移植

(1)编写头文件"MS5837.h"如下:

#ifndef __MS5837_H
#define __MS5837_H

#ifdef __cplusplus
extern "C" {
#endif

#include "stm32f10x.h"

class MS5837 {
public:
	static const float Pa;
	static const float bar;
	static const float mbar;

	static const uint8_t MS5837_30BA;
	static const uint8_t MS5837_02BA;

	MS5837();

	bool init();
	void setModel(uint8_t model);
	void setFluidDensity(float density);
	void read();

	float pressure(float conversion = 1.0f);
	float temperature();
	float depth();
	float altitude();

private:
	uint16_t C[8];
	uint32_t D1, D2;
	int32_t TEMP;
	int32_t P;
	uint8_t _model;

	float fluidDensity;
	void calculate();

	uint8_t crc4(uint16_t n_prom[]);
};

#ifdef __cplusplus
}
#endif

#endif

(2)编写cpp文件"MS5837.cpp",其中MS5837硬件复位、PROM的读取应当严格遵守数据手册提供的时序图编写:

MS5837复位时序图:

mesa库移植 msd移植_mesa库移植_04


MCU向MS5837读取数据时序图:

mesa库移植 msd移植_#include_05


MCU向MS5837发送数据转换指令并读取ADC数据时序图:

mesa库移植 msd移植_mesa库移植_06


根据以上时序图对移植的"MS5837.cpp"文件进行部分修改如下:

#include "MS5837.h"
#include "math.h"
#include "myiic.h"
#include "delay.h"
#include "usart.h"

#define MS5837_ADDR               0xEC	//0x76  
#define MS5837_RESET              0x1E
#define MS5837_ADC_READ           0x00
#define MS5837_PROM_READ          0xA0
#define MS5837_CONVERT_D1_8192    0x4A
#define MS5837_CONVERT_D2_8192    0x5A

const float MS5837::Pa = 100.0f;
const float MS5837::bar = 0.001f;
const float MS5837::mbar = 1.0f;

const uint8_t MS5837::MS5837_30BA = 0;
const uint8_t MS5837::MS5837_02BA = 1;


static uint16_t readFromMs5837(uint8_t add)
{
	uint8_t dataArr[2] = {0,0};
	
	IIC_Start();
	IIC_Send_Byte(MS5837_ADDR);
	IIC_Wait_Ack();
	delay_us(10);
	IIC_Send_Byte(add);
	IIC_Wait_Ack();
	IIC_Stop();
	delay_us(20);
	
	IIC_Start();
	IIC_Send_Byte(MS5837_ADDR|0x01);
	IIC_Wait_Ack();	
	delay_us(10);
	dataArr[0] = IIC_Read_Byte(1);
	delay_us(10);
	dataArr[1] = IIC_Read_Byte(0);
	IIC_Stop();
	delay_us(30);
	
	return (uint16_t)(dataArr[0]<<8)|dataArr[1];
}

MS5837::MS5837() {
	fluidDensity = 1029;
}

bool MS5837::init() {
	IIC_Start();
	IIC_Send_Byte(MS5837_ADDR);
	IIC_Wait_Ack();
	IIC_Send_Byte(MS5837_RESET);
	IIC_Wait_Ack();
	IIC_Stop();
	delay_ms(10);

	for ( uint8_t i = 0 ; i < 7 ; i++ ) {
		C[i] = readFromMs5837(MS5837_PROM_READ+i*2);
	}

	// Verify that data is correct with CRC
	uint8_t crcRead = C[0] >> 12;
	uint8_t crcCalculated = crc4(C);
	printf("crcRead:%X\tcrcCalculated:%X\n", crcRead, crcCalculated);
	delay_ms(10);

	if ( crcCalculated == crcRead ) {
		return true; // Initialization success
	}

	return false; // CRC fail
}

void MS5837::setModel(uint8_t model) {
	_model = model;
}

void MS5837::setFluidDensity(float density) {
	fluidDensity = density;
}

void MS5837::read() {
	// Request D1 conversion
	IIC_Start();
	IIC_Send_Byte(MS5837_ADDR);
	IIC_Wait_Ack();
	delay_us(10);
	IIC_Send_Byte(MS5837_CONVERT_D1_8192);
	IIC_Wait_Ack();
	IIC_Stop();
	delay_ms(20);
	
	IIC_Start();
	IIC_Send_Byte(MS5837_ADDR);
	IIC_Wait_Ack();
	delay_us(10);
	IIC_Send_Byte(MS5837_ADC_READ);
	IIC_Wait_Ack();
	IIC_Stop();
	delay_us(20);

	IIC_Start();
	IIC_Send_Byte(MS5837_ADDR|0x01);
	IIC_Wait_Ack();	
	delay_us(10);
	D1 = 0;
	D1 = IIC_Read_Byte(1);
	delay_us(10);
	D1 = (D1 << 8) | IIC_Read_Byte(1);
	delay_us(10);
	D1 = (D1 << 8) | IIC_Read_Byte(0);
	IIC_Stop();
	delay_us(45);
	
	// Request D2 conversion
	IIC_Start();
	IIC_Send_Byte(MS5837_ADDR);
	IIC_Wait_Ack();
	delay_us(10);
	IIC_Send_Byte(MS5837_CONVERT_D2_8192);
	IIC_Wait_Ack();
	IIC_Stop();
	delay_ms(20);

	IIC_Start();
	IIC_Send_Byte(MS5837_ADDR);
	IIC_Wait_Ack();
	delay_us(10);
	IIC_Send_Byte(MS5837_ADC_READ);
	IIC_Wait_Ack();
	IIC_Stop();
	delay_us(20);

	IIC_Start();
	IIC_Send_Byte(MS5837_ADDR|0x01);
	IIC_Wait_Ack();	
	D2 = 0;
	D2 = IIC_Read_Byte(1);
	delay_us(10);
	D2 = (D2 << 8) | IIC_Read_Byte(1);
	delay_us(10);
	D2 = (D2 << 8) | IIC_Read_Byte(0);
	IIC_Stop();
	delay_us(45);

	calculate();
}

void MS5837::calculate() {
	// Given C1-C6 and D1, D2, calculated TEMP and P
	// Do conversion first and then second order temp compensation
	int32_t dT = 0;
	int64_t SENS = 0;
	int64_t OFF = 0;
	int32_t SENSi = 0;
	int32_t OFFi = 0;  
	int32_t Ti = 0;    
	int64_t OFF2 = 0;
	int64_t SENS2 = 0;
	
	// Terms called
	dT = D2-uint32_t(C[5])*256l;
	if ( _model == MS5837_02BA ) {
		SENS = int64_t(C[1])*65536l+(int64_t(C[3])*dT)/128l;
		OFF = int64_t(C[2])*131072l+(int64_t(C[4])*dT)/64l;
		P = (D1*SENS/(2097152l)-OFF)/(32768l);
	} else {
		SENS = int64_t(C[1])*32768l+(int64_t(C[3])*dT)/256l;
		OFF = int64_t(C[2])*65536l+(int64_t(C[4])*dT)/128l;
		P = (D1*SENS/(2097152l)-OFF)/(8192l);
	}
	
	// Temp conversion
	TEMP = 2000l+int64_t(dT)*C[6]/8388608LL;
	
	//Second order compensation
	if ( _model == MS5837_02BA ) {
		if((TEMP/100)<20){         //Low temp
			Ti = (11*int64_t(dT)*int64_t(dT))/(34359738368LL);
			OFFi = (31*(TEMP-2000)*(TEMP-2000))/8;
			SENSi = (63*(TEMP-2000)*(TEMP-2000))/32;
		}
	} else {
		if((TEMP/100)<20){         //Low temp
			Ti = (3*int64_t(dT)*int64_t(dT))/(8589934592LL);
			OFFi = (3*(TEMP-2000)*(TEMP-2000))/2;
			SENSi = (5*(TEMP-2000)*(TEMP-2000))/8;
			if((TEMP/100)<-15){    //Very low temp
				OFFi = OFFi+7*(TEMP+1500l)*(TEMP+1500l);
				SENSi = SENSi+4*(TEMP+1500l)*(TEMP+1500l);
			}
		}
		else if((TEMP/100)>=20){    //High temp
			Ti = 2*(dT*dT)/(137438953472LL);
			OFFi = (1*(TEMP-2000)*(TEMP-2000))/16;
			SENSi = 0;
		}
	}
	
	OFF2 = OFF-OFFi;           //Calculate pressure and temp second order
	SENS2 = SENS-SENSi;
	
	TEMP = (TEMP-Ti);
	
	if ( _model == MS5837_02BA ) {
		P = (((D1*SENS2)/2097152l-OFF2)/32768l); 
	} else {
		P = (((D1*SENS2)/2097152l-OFF2)/8192l);
	}
}

float MS5837::pressure(float conversion) {
    if ( _model == MS5837_02BA ) {
        return P*conversion/100.0f;
    }
    else {
        return P*conversion/10.0f;
    }
}

float MS5837::temperature() {
	return TEMP/100.0f;
}

float MS5837::depth() {
	return (pressure(MS5837::Pa)-2022000)/(fluidDensity*9.80665*20.05)*100;
}

float MS5837::altitude() {
	return (1-pow((pressure()/1013.25),.190284))*145366.45*.3048;
}


uint8_t MS5837::crc4(uint16_t n_prom[]) {
	uint16_t n_rem = 0;

	n_prom[0] = ((n_prom[0]) & 0x0FFF);
	n_prom[7] = 0;

	for ( uint8_t i = 0 ; i < 16; i++ ) {
		if ( i%2 == 1 ) {
			n_rem ^= (uint16_t)((n_prom[i>>1]) & 0x00FF);
		} else {
			n_rem ^= (uint16_t)(n_prom[i>>1] >> 8);
		}
		for ( uint8_t n_bit = 8 ; n_bit > 0 ; n_bit-- ) {
			if ( n_rem & 0x8000 ) {
				n_rem = (n_rem << 1) ^ 0x3000;
			} else {
				n_rem = (n_rem << 1);
			}
		}
	}
	
	n_rem = ((n_rem >> 12) & 0x000F);

	return n_rem ^ 0x00;
}

3. 主函数编写

编写主函数cpp文件“main.cpp”:

#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "ms5837.h"
#include "myiic.h"

MS5837 sensor;
int main(void)
{	 
	delay_init();	    	 //延时函数初始化	  
  	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 	//串口初始化为115200	 	
	IIC_Init();			//IIC初始化 
	while (!sensor.init()){
    printf("Init failed!");
    printf("Are SDA/SCL connected correctly?");
    printf("Blue Robotics Bar30: White=SDA, Green=SCL");
    printf("\n\n\n");
    delay_ms(1000);
	delay_ms(1000);
	delay_ms(1000);
  	}
  	//sensor.setModel(MS5837::MS5837_02BA);
	sensor.setModel(MS5837::MS5837_30BA);
  	sensor.setFluidDensity(997); // kg/m^3 (freshwater, 1029 for seawater)
	while (1)
	 {
		sensor.read();
		printf("Depth: %.2f cm\t", sensor.depth()); 
		delay_ms(10);
		printf("Temprature: %.2f ℃\t", sensor.temperature());
		delay_ms(10);
		printf("Altitude: %.2f m\n", sensor.altitude()/100);  
		delay_ms(980);
		delay_ms(1000);
		delay_ms(1000);
	 }
}

4. 代码调试结果

使用串口调试助手对调试结果进行查看!可以看出,移植后的程序能够在stm32f1系列的MCU上正常运行,移植成功!

mesa库移植 msd移植_编程语言_07

三、源代码下载

已将工程关键源代码上传至github,如有需要,请自行下载!