BMP180气压传感器
1BMP180是一款高精度、小体积、低能耗的压力传感器,可以轻松应用在移动设备中.在测量海拔高度时,传统的做法是通过测量某一高度的大气压力,再经过换算才能得到高度数据。BMP180不仅可以实时的测量大气压力,还能测量实时温度。同时它还具有IIC总线接口,便于单片机进行访问。另外它的使用也很方便,不需要太多的操作就可读取到气压及测量数据
模块来源
采购链接:
https://detail.tmall.com/item.htm?abbucket=0&id=521331189285&ns=1&spm=a21n57.1.0.0.71d8523cm3qMhZ
资料下载链接:
https://pan.baidu.com/s/1miTIphm
规格参数
工作电压:1.8~3.6V
工作电流:0.1~1000uA
温度精度:±1℃
温度范围:0~65℃
气压范围:300~1100 hPa
气压精度:1 hPa
输出方式: IIC
管脚数量:3 Pin
移植过程
首先要获取资料,查看数据手册应如何实现,再移植至我们的工程。
查看资料
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;
(2)读0xf6(16-23位)、0xf7(8-15位)和0xf8(0-7位)三个寄存器;
(3)进行换算: UP=MSB <<16 + LSB<<8 + XLSB >> (8-oss(这个同温度初始值读取一样))。 - 根据第一步读出来的校准系数和第二步读出来的UT、UP进行换算,最后得出来的T(温度,每个数值代表0.1摄氏度),p(气压,每个数值代表1帕)。
引脚选择
移植至工程
移植步骤中的导入.c和.h文件与上一节相同,只是将.c和.h文件更改为bsp_bmp180.c与bsp_bmp180.h。移植完成后面修改相关代码。
详细可见【TTP224触摸传感器】中的移植至工程目录。这里不再过多讲述。移植完成后面修改相关代码。
在文件bsp_bmp180.c中,编写如下代码
/******************************************************************************
* 测试硬件:立创开发板·GD32E230C8T6 使用主频72Mhz 晶振8Mhz
* 版 本 号: V1.0
* 修改作者: www.lckfb.com
* 修改日期: 2023年11月02日
* 功能介绍:
*****************************************************************************
* 梁山派软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 其余模块移植手册:【立创·GD32E230C8T6开发板】模块移植手册
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
******************************************************************************/
#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 );
delay_us(2);
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[] = {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[] = Read_Byte();
IIC_Send_Ack(1);
IIC_Stop();
delay_1ms(1);
}
if( len == 2 ) return ( (dat[]<<8) | dat[] );
if( len == 3 ) return (( (dat[]<<16) | (dat[]<<8) | (dat[]) ) >> 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
410
411
412
413
414
在文件bsp_bmp180.h中,编写如下代码
/******************************************************************************
* 测试硬件:立创开发板·GD32E230C8T6 使用主频72Mhz 晶振8Mhz
* 版 本 号: V1.0
* 修改作者: www.lckfb.com
* 修改日期: 2023年11月02日
* 功能介绍:
*****************************************************************************
* 梁山派软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 其余模块移植手册:【立创·GD32E230C8T6开发板】模块移植手册
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
******************************************************************************/
#ifndef _BSP_BMP180_H_
#define _BSP_BMP180_H_
#include "gd32e23x.h"
//端口移植
#define RCU_SDA RCU_GPIOA
#define PORT_SDA GPIOA
#define GPIO_SDA GPIO_PIN_2
#define RCU_SCL RCU_GPIOA
#define PORT_SCL GPIOA
#define GPIO_SCL GPIO_PIN_1
//设置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
45
46
47
48
49
50
移植验证
在自己工程中的main主函数中,编写如下
/******************************************************************************
* 测试硬件:立创开发板·GD32E230C8T6 使用主频72Mhz 晶振8Mhz
* 版 本 号: V1.0
* 修改作者: www.lckfb.com
* 修改日期: 2023年11月02日
* 功能介绍:
*****************************************************************************
* 梁山派软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 其余模块移植手册:【立创·GD32E230C8T6开发板】模块移植手册
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
******************************************************************************/
#include "gd32e23x.h"
#include "systick.h"
#include <stdio.h>
#include "bsp_usart.h"
#include "bsp_bmp180.h"
int main(void)
{
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
移植现象:每隔一秒测量一次温度、气压和高度,并发送至串口0