基于STM32标准库的MS5837程序移植
- 一、准备工作
- 1. 硬件电路
- 2. 新建工程
- 二、开始移植
- 1. IIC底层模拟
- 2. MS5837移植
- 3. 主函数编写
- 4. 代码调试结果
- 三、源代码下载
一、准备工作
1. 硬件电路
典型电路图:
实际硬件图:
2. 新建工程
基于标准库建立MDK工程,使用C++与C混合编程,工程目录如图:
二、开始移植
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复位时序图:
MCU向MS5837读取数据时序图:
MCU向MS5837发送数据转换指令并读取ADC数据时序图:
根据以上时序图对移植的"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上正常运行,移植成功!
三、源代码下载
已将工程关键源代码上传至github,如有需要,请自行下载!