2.23 BMP180 气压传感器
1BMP180 是一款高精度、小体积、低能耗的压力传感器,可以轻松应用在移动设备中.在测量海拔高度时,传统的做法是通过测量某一高度的大气压力,再经过换算才能得到高度数据。BMP180 不仅可以实时的测量大气压力,还能测量实时温度。同时它还具有 IIC 总线接口,便于单片机进行访问。另外它的使用也很方便,不需要太多的操作就可读取到气压及测量数据
2.23.1 模块来源
采购链接: GY-68 BMP180 新款 BOSCH 温度 代替 BMP085 气压传感器模块 资料下载链接: https://pan.baidu.com/s/1miTIphm
**图 2.23.1-1 产品实物展示**2.23.2 规格参数
**工作电压:**1.8~3.6V **工作电流:**0.1~1000uA 温度精度:±1℃ **温度范围:**0~65℃ **气压范围:**300~1100 hPa **气压精度:**1 hPa **输出方式: ****IIC** **管脚数量:**3 Pin 文件 2.23.2-1 产品规格书
2.23.3 移植过程
我们的目标是在梁山派 GD32F470 上能够测量环境温度、气压、高度。首先要获取资料,查看数据手册应如何实现,再移植至我们的工程。
2.23.3.1 查看资料
BMP180 共有四种工作模式,每种模式有不同的采样数量、转换速度和噪声等参数的不同。可以通过写入 ctrl_meas 寄存器来设置模式,默认为第一个 ultra low power 超低功耗。
BMP180 的气压和温度数值并不是可以直接读取的,每个不同的传感器中,都有自己独特的校准数值,存储在内置的 E2PROM 存储器中。当微处理器读取传感器的原始温度和气压数值后,再根据 E2PROM 中的校准数值进行转换,才能得到真正的温度、气压数据。每个校准数值的存储位置如下,微处理器通过这些地址读取校准数值。
和所有的 IIC 总线器件一样,BMP180 也有一个器件的固定地址,根据其数据手册,出厂时默认 BMP180 的从机地址为 0xEE(写入方向),或 0xEF(读出方向)。
以下为读取温度与气压的步骤:
- 把 16 位的校准数值读取到单片机中,可以看到一共有 11 个数值。需要注意的是高位存储在 MSB 地址,低位存储在 LSB 地址。例如数值 AC1,高八位存储在 0xAA 地址,低八位存储在 0xAB 地址。
- 温度初始值读取步骤: (1)往寄存器 0xf4 写入 0x2e,等待 4、5ms;
(2)读 0xf6(高八位)和 0xf7(低八位)两个寄存器;
(3)进行换算: UT=MSB <<8 +LSB。
- 气压初始值读取步骤:
(1)往寄存器 0xf4 写入 0x34(如果不是默认的工作模式,需要加上 oss 左移六位的结果,oss 为设置工作模式 的寄存器 0xf4 的 bit7、bit6 位),等待 4、5ms;
(3)进行换算: UP=MSB <<16 + LSB<<8 + XLSB >> (8-oss(这个同温度初始值读取一样))。
- 根据第一步读出来的校准系数和第二步读出来的 UT、UP 进行换算,最后得出来的 T(温度,每个数值代表 0.1 摄氏度),p(气压,每个数值代表 1 帕)。
2.23.3.2 引脚选择
######### MS5611 | ######### 立创·梁山派 | ######### 接线图 |
---|
2.23.3.3 移植至工程
移植步骤中的导入.c 和.h 文件与上一节相同,只是将.c 和.h 文件更改为 bsp_bmp180.c 与 bsp_bmp180.h。见 2.2.3.3 移植至工程。这里不再过多讲述。移植完成后面修改相关代码。
在文件 bsp_bmp180.c 中,编写如下代码。
/********************************************************************************
* 文 件 名: bsp_bmp180.c
* 版 本 号: 初版
* 修改作者: LC
* 修改日期: 2022年04月26日
* 功能介绍: 气压传感器bmp180的底层驱动
* 使用说明:
******************************************************************************
* 开发板官网:www.lckfb.com
*********************************************************************************/
#include "bsp_bmp180.h"
#include "bsp_usart.h"
#include "stdio.h"
#include "systick.h"
#include "math.h"
typedef struct _BMP180_STRUCT{
short AC1;
short AC2;
short AC3;
uint16_t AC4;
uint16_t AC5;
uint16_t AC6;
short B1;
short B2;
short MB;
short MC;
short MD;
}_BMP180_PARAM_;
_BMP180_PARAM_ param={0};
long B5 = 0;
/******************************************************************
* 函 数 名 称:BMP180_GPIO_Init
* 函 数 说 明:BMP180的引脚初始化
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void BMP180_GPIO_Init(void)
{
/* 使能时钟 */
rcu_periph_clock_enable(RCU_SCL);
rcu_periph_clock_enable(RCU_SDA);
/* 配置SCL为输出模式 */
gpio_mode_set(PORT_SCL,GPIO_MODE_OUTPUT,GPIO_PUPD_PULLUP,GPIO_SCL);
/* 配置为推挽输出 50MHZ */
gpio_output_options_set(PORT_SCL,GPIO_OTYPE_OD,GPIO_OSPEED_50MHZ,GPIO_SCL);
/* 配置SDA为输出模式 */
gpio_mode_set(PORT_SDA,GPIO_MODE_OUTPUT,GPIO_PUPD_PULLUP,GPIO_SDA);
/* 配置为推挽输出 50MHZ */
gpio_output_options_set(PORT_SDA,GPIO_OTYPE_OD,GPIO_OSPEED_50MHZ,GPIO_SDA);
}
/******************************************************************
* 函 数 名 称:IIC_Start
* 函 数 说 明:IIC起始时序
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Start(void)
{
SDA_OUT();
SDA(1);
delay_us(5);
SCL(1);
delay_us(5);
SDA(0);
delay_us(5);
SCL(0);
delay_us(5);
}
/******************************************************************
* 函 数 名 称:IIC_Stop
* 函 数 说 明:IIC停止信号
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Stop(void)
{
SDA_OUT();
SCL(0);
SDA(0);
SCL(1);
delay_us(5);
SDA(1);
delay_us(5);
}
/******************************************************************
* 函 数 名 称:IIC_Send_Ack
* 函 数 说 明:主机发送应答或者非应答信号
* 函 数 形 参:0发送应答 1发送非应答
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Send_Ack(unsigned char ack)
{
SDA_OUT();
SCL(0);
SDA(0);
delay_us(5);
if(!ack) SDA(0);
else SDA(1);
SCL(1);
delay_us(5);
SCL(0);
SDA(1);
}
/******************************************************************
* 函 数 名 称:I2C_WaitAck
* 函 数 说 明:等待从机应答
* 函 数 形 参:无
* 函 数 返 回:0有应答 1超时无应答
* 作 者:LC
* 备 注:无
******************************************************************/
unsigned char I2C_WaitAck(void)
{
char ack = 0;
unsigned char ack_flag = 10;
SCL(0);
SDA(1);
SDA_IN();
delay_us(5);
SCL(1);
delay_us(5);
while( (SDA_GET()==1) && ( ack_flag ) )
{
ack_flag--;
delay_us(5);
}
if( ack_flag <= 0 )
{
IIC_Stop();
return 1;
}
else
{
SCL(0);
SDA_OUT();
}
return ack;
}
/******************************************************************
* 函 数 名 称:Send_Byte
* 函 数 说 明:写入一个字节
* 函 数 形 参:dat要写人的数据
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void Send_Byte(uint8_t dat)
{
int i = 0;
SDA_OUT();
SCL(0);//拉低时钟开始数据传输
for( i = 0; i < 8; i++ )
{
SDA( (dat & 0x80) >> 7 );
__nop();
SCL(1);
delay_us(5);
SCL(0);
delay_us(5);
dat<<=1;
}
}
/******************************************************************
* 函 数 名 称:Read_Byte
* 函 数 说 明:IIC读时序
* 函 数 形 参:无
* 函 数 返 回:读到的数据
* 作 者:LC
* 备 注:无
******************************************************************/
unsigned char Read_Byte(void)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
SCL(0);
delay_us(5);
SCL(1);
delay_us(5);
receive<<=1;
if( SDA_GET() )
{
receive|=1;
}
delay_us(5);
}
SCL(0);
return receive;
}
/******************************************************************
* 函 数 名 称:BMP180_Write_Cmd
* 函 数 说 明:向BMP180写入一个字节数据
* 函 数 形 参:regaddr寄存器地址 cmd写入的数据
* 函 数 返 回:无
* 作 者:LC
* 备 注:regaddr=0xf4, cmd=0X2E
******************************************************************/
void BMP180_Write_Cmd(uint8_t regaddr,uint8_t cmd)
{
IIC_Start();//起始信号
Send_Byte(0XEE);//器件地址+写
if( I2C_WaitAck() == 1 ) printf("Write_Cmd NACK -1\r\n");
Send_Byte(regaddr);
if( I2C_WaitAck() == 1 ) printf("Write_Cmd NACK -2\r\n");
Send_Byte(cmd);
if( I2C_WaitAck() == 1 ) printf("Write_Cmd NACK -3\r\n");
IIC_Stop();
}
/******************************************************************
* 函 数 名 称:BMP180_Read16
* 函 数 说 明:读取BMP180数据
* 函 数 形 参:regaddr读取的地址 len读取的长度
* 函 数 返 回:读取到的数据
* 作 者:LC
* 备 注:无
******************************************************************/
uint16_t BMP180_Read16(uint16_t regaddr,uint8_t len)
{
int timeout = 0;
uint16_t dat[3] = {0};
int i =0;
for( i = 0; i < len; i++ )
{
IIC_Start();//起始信号
Send_Byte(0XEE);//器件地址+写
if( I2C_WaitAck() == 1 ) printf("Read_Reg NACK -1\r\n");
Send_Byte(regaddr+i);
if( I2C_WaitAck() == 1 ) printf("Read_Reg NACK -2\r\n");
do{
timeout++;
delay_1ms(1);
IIC_Start();//起始信号
Send_Byte(0XEF);//器件地址+读
}while(I2C_WaitAck() == 1 && (timeout < 5) );
dat[i] = Read_Byte();
IIC_Send_Ack(1);
IIC_Stop();
delay_1ms(1);
}
if( len == 2 ) return ( (dat[0]<<8) | dat[1] );
if( len == 3 ) return (( (dat[0]<<16) | (dat[1]<<8) | (dat[2]) ) >> 8);
return 0;
}
/******************************************************************
* 函 数 名 称:BMP180_Get_Temperature
* 函 数 说 明:读取温度单位℃
* 函 数 形 参:无
* 函 数 返 回:温度
* 作 者:LC
* 备 注:无
******************************************************************/
float BMP180_Get_Temperature(void)
{
long UT = 0;
long X1 = 0, X2 = 0;
BMP180_Write_Cmd(0XF4, 0X2E);
delay_1ms(6);
UT = BMP180_Read16(0xf6,2);
X1 = ((long)UT - param.AC6) * param.AC5 / 32768.0;
X2 = ((long)param.MC * 2048.0) / ( X1 + param.MD );
B5 = X1 + X2;
return ((B5+8)/16.0)*0.1f;
}
/******************************************************************
* 函 数 名 称:BMP180_Get_Pressure
* 函 数 说 明:读取气压,单位Pa
* 函 数 形 参:无
* 函 数 返 回:当前气压,单位Pa
* 作 者:LC
* 备 注:无
******************************************************************/
float BMP180_Get_Pressure(void)
{
long UP = 0;
uint8_t oss = 0;
long X1 = 0, X2 = 0;
BMP180_Get_Temperature();
BMP180_Write_Cmd(0XF4, (0X34+(oss<<6)));
delay_1ms(10);
UP = BMP180_Read16(0xf6,3);
int32_t B6 = B5 - 4000;
X1 = (B6 * B6 >> 12) * param.B2 >> 11;
X2 = param.AC2 * B6 >> 11;
int32_t X3 = X1 + X2;
int32_t B3 = (((param.AC1 << 2) + X3) + 2) >> 2;
X1 = param.AC3 * B6 >> 13;
X2 = (B6 * B6 >> 12) * param.B1 >> 16;
X3 = (X1 + X2 + 2) >> 2;
uint32_t B4 = param.AC4 * (uint32_t)(X3 + 32768) >> 15;
uint32_t B7 = ((uint32_t)UP - B3) * 50000;
int32_t p;
if(B7 < 0x80000000){
p = (B7 << 1) / B4;
}else{
p = B7/B4 << 1;
}
X1 = (p >> 8) * (p >> 8);
X1 = (X1 * 3038) >> 16;
X2 = (-7375 * p) >> 16;
p = p + ((X1 + X2 + 3791) >> 4);
return p;
}
/******************************************************************
* 函 数 名 称:BMP180_Get_Altitude
* 函 数 说 明:计算海拔高度
* 函 数 形 参:p=当前气压
* 函 数 返 回:海拔高度
* 作 者:LC
* 备 注:无
******************************************************************/
float BMP180_Get_Altitude(float p)
{
//#define PRESSURE_OF_SEA 101325.0f // 参考海平面压强
float altitude = 0;
altitude = 44330*(1 - pow((p)/ 101325.0f, 1.0f / 5.255f));
printf("altitude = %.2f\r\n",altitude);
return altitude;
}
/******************************************************************
* 函 数 名 称:BMP180_Get_param
* 函 数 说 明:获取出厂校准值
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void BMP180_Get_param(void)
{
param.AC1 = BMP180_Read16(0xaa,2);
param.AC2 = BMP180_Read16(0xac,2);
param.AC3 = BMP180_Read16(0xae,2);
param.AC4 = BMP180_Read16(0xb0,2);
param.AC5 = BMP180_Read16(0xb2,2);
param.AC6 = BMP180_Read16(0xb4,2);
param.B1 = BMP180_Read16(0xb6,2);
param.B2 = BMP180_Read16(0xb8,2);
param.MB = BMP180_Read16(0xba,2);
param.MC = BMP180_Read16(0xbc,2);
param.MD = BMP180_Read16(0xbe,2);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
在文件 bsp_bmp180.h 中,编写如下代码。
/********************************************************************************
* 文 件 名: bsp_bmp180.h
* 版 本 号: 初版
* 修改作者: LC
* 修改日期: 2022年04月26日
* 功能介绍:
******************************************************************************
* 开发板官网:www.lckfb.com
*********************************************************************************/
#ifndef _BSP_BMP180_H_
#define _BSP_BMP180_H_
#include "gd32f4xx.h"
//端口移植
#define RCU_SDA RCU_GPIOF
#define PORT_SDA GPIOF
#define GPIO_SDA GPIO_PIN_9
#define RCU_SCL RCU_GPIOF
#define PORT_SCL GPIOF
#define GPIO_SCL GPIO_PIN_7
//设置SDA输出模式
#define SDA_OUT() gpio_mode_set(PORT_SDA,GPIO_MODE_OUTPUT,GPIO_PUPD_PULLUP,GPIO_SDA)
//设置SDA输入模式
#define SDA_IN() gpio_mode_set(PORT_SDA,GPIO_MODE_INPUT,GPIO_PUPD_PULLUP,GPIO_SDA)
//获取SDA引脚的电平变化
#define SDA_GET() gpio_input_bit_get(PORT_SDA,GPIO_SDA)
//SDA与SCL输出
#define SDA(x) gpio_bit_write(PORT_SDA,GPIO_SDA, (x?SET:RESET))
#define SCL(x) gpio_bit_write(PORT_SCL,GPIO_SCL, (x?SET:RESET))
void BMP180_GPIO_Init(void);
float BMP180_Get_Temperature(void);
float BMP180_Get_Pressure(void);
void BMP180_Write_Cmd(uint8_t regaddr,uint8_t cmd);
void BMP180_Get_param(void);
float BMP180_Get_Altitude(float p);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2.23.4 移植验证
在自己工程中的 main 主函数中,编写如下。
/********************************************************************************
* 文 件 名: main.c
* 版 本 号: 初版
* 修改作者: LC
* 修改日期: 2022年04月19日
* 功能介绍:
******************************************************************************
* 开发板官网:www.lckfb.com
*********************************************************************************/
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "sys.h"
#include "bsp_usart.h"
#include "bsp_bmp180.h"
/************************************************
函数名称 : main
功 能 : 主函数
参 数 : 无
返 回 值 : 无
作 者 : LC
*************************************************/
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 优先级分组
systick_config(); // 滴答定时器初始化
usart_gpio_config(9600U); // 串口0初始化
BMP180_GPIO_Init();
BMP180_Get_param();
printf("start\r\n");
while(1)
{
printf("temp = %.2f\r\n", BMP180_Get_Temperature() );
printf("Pressure = %.2f\r\n", BMP180_Get_Pressure() );
BMP180_Get_Altitude(BMP180_Get_Pressure());
delay_1ms(1000);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
移植现象:每隔一秒测量一次温度、气压和高度,并发送至串口 0。
移植成功示例,见文件 2.23.4-1 。
文件 2.23.4-1 移植成功示例