CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
Ardupilot

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: Ardupilot/ardupilot
Path: blob/master/libraries/AP_Baro/AP_Baro_BMP388.cpp
Views: 1798
1
/*
2
This program is free software: you can redistribute it and/or modify
3
it under the terms of the GNU General Public License as published by
4
the Free Software Foundation, either version 3 of the License, or
5
(at your option) any later version.
6
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
11
12
You should have received a copy of the GNU General Public License
13
along with this program. If not, see <http://www.gnu.org/licenses/>.
14
*/
15
#include "AP_Baro_BMP388.h"
16
17
#if AP_BARO_BMP388_ENABLED
18
19
#include <utility>
20
#include <AP_Math/AP_Math.h>
21
22
extern const AP_HAL::HAL &hal;
23
24
#define BMP388_MODE_SLEEP 0
25
#define BMP388_MODE_FORCED 1
26
#define BMP388_MODE_NORMAL 3
27
#define BMP388_MODE BMP388_MODE_NORMAL
28
29
#define BMP388_ID 0x50
30
#define BMP390_ID 0x60
31
32
#define BMP388_REG_ID 0x00
33
#define BMP388_REV_ID_ADDR 0x01
34
#define BMP388_REG_ERR 0x02
35
#define BMP388_REG_STATUS 0x03
36
#define BMP388_REG_PRESS 0x04 // 24 bit
37
#define BMP388_REG_TEMP 0x07 // 24 bit
38
#define BMP388_REG_TIME 0x0C // 24 bit
39
#define BMP388_REG_EVENT 0x10
40
#define BMP388_REG_INT_STS 0x11
41
#define BMP388_REG_FIFO_LEN 0x12 // 9 bit
42
#define BMP388_REG_FIFO_DATA 0x14
43
#define BMP388_REG_FIFO_WTMK 0x15 // 9 bit
44
#define BMP388_REG_FIFO_CNF1 0x17
45
#define BMP388_REG_FIFO_CNF2 0x18
46
#define BMP388_REG_INT_CTRL 0x19
47
#define BMP388_REG_PWR_CTRL 0x1B
48
#define BMP388_REG_OSR 0x1C
49
#define BMP388_REG_ODR 0x1D
50
#define BMP388_REG_CONFIG 0x1F
51
#define BMP388_REG_CMD 0x7E
52
53
#define BMP388_REG_CAL_P 0x36
54
#define BMP388_REG_CAL_T 0x31
55
56
AP_Baro_BMP388::AP_Baro_BMP388(AP_Baro &baro, AP_HAL::OwnPtr<AP_HAL::Device> _dev)
57
: AP_Baro_Backend(baro)
58
, dev(std::move(_dev))
59
{
60
}
61
62
AP_Baro_Backend *AP_Baro_BMP388::probe(AP_Baro &baro,
63
AP_HAL::OwnPtr<AP_HAL::Device> _dev)
64
{
65
if (!_dev) {
66
return nullptr;
67
}
68
69
AP_Baro_BMP388 *sensor = NEW_NOTHROW AP_Baro_BMP388(baro, std::move(_dev));
70
if (!sensor || !sensor->init()) {
71
delete sensor;
72
return nullptr;
73
}
74
return sensor;
75
}
76
77
bool AP_Baro_BMP388::init()
78
{
79
if (!dev) {
80
return false;
81
}
82
WITH_SEMAPHORE(dev->get_semaphore());
83
84
dev->set_speed(AP_HAL::Device::SPEED_HIGH);
85
86
// setup to allow reads on SPI
87
if (dev->bus_type() == AP_HAL::Device::BUS_TYPE_SPI) {
88
dev->set_read_flag(0x80);
89
}
90
91
// normal mode, temp and pressure
92
dev->write_register(BMP388_REG_PWR_CTRL, 0x33, true);
93
94
uint8_t whoami;
95
if (!read_registers(BMP388_REG_ID, &whoami, 1)) {
96
return false;
97
}
98
99
switch (whoami) {
100
case BMP388_ID:
101
dev->set_device_type(DEVTYPE_BARO_BMP388);
102
break;
103
case BMP390_ID:
104
dev->set_device_type(DEVTYPE_BARO_BMP390);
105
break;
106
default:
107
return false;
108
}
109
110
// read the calibration data
111
read_registers(BMP388_REG_CAL_P, (uint8_t *)&calib_p, sizeof(calib_p));
112
read_registers(BMP388_REG_CAL_T, (uint8_t *)&calib_t, sizeof(calib_t));
113
114
scale_calibration_data();
115
116
dev->setup_checked_registers(4);
117
118
// normal mode, temp and pressure
119
dev->write_register(BMP388_REG_PWR_CTRL, 0x33, true);
120
121
instance = _frontend.register_sensor();
122
123
set_bus_id(instance, dev->get_bus_id());
124
125
// request 50Hz update
126
dev->register_periodic_callback(20 * AP_USEC_PER_MSEC, FUNCTOR_BIND_MEMBER(&AP_Baro_BMP388::timer, void));
127
128
return true;
129
}
130
131
132
133
// accumulate a new sensor reading
134
void AP_Baro_BMP388::timer(void)
135
{
136
uint8_t buf[7];
137
138
if (!read_registers(BMP388_REG_STATUS, buf, sizeof(buf))) {
139
return;
140
}
141
const uint8_t status = buf[0];
142
if ((status & 0x20) != 0) {
143
// we have pressure data
144
update_pressure((buf[3] << 16) | (buf[2] << 8) | buf[1]);
145
}
146
if ((status & 0x40) != 0) {
147
// we have temperature data
148
update_temperature((buf[6] << 16) | (buf[5] << 8) | buf[4]);
149
}
150
151
dev->check_next_register();
152
}
153
154
// transfer data to the frontend
155
void AP_Baro_BMP388::update(void)
156
{
157
WITH_SEMAPHORE(_sem);
158
159
if (pressure_count == 0) {
160
return;
161
}
162
163
_copy_to_frontend(instance,
164
pressure_sum/pressure_count,
165
temperature);
166
167
pressure_sum = 0;
168
pressure_count = 0;
169
}
170
171
/*
172
convert calibration data from NVM values to values ready for
173
compensation calculations
174
*/
175
void AP_Baro_BMP388::scale_calibration_data(void)
176
{
177
// note that this assumes little-endian MCU
178
calib.par_t1 = calib_t.nvm_par_t1 * 256.0;
179
calib.par_t2 = calib_t.nvm_par_t2 / 1073741824.0f;
180
calib.par_t3 = calib_t.nvm_par_t3 / 281474976710656.0f;
181
182
calib.par_p1 = (calib_p.nvm_par_p1 - 16384) / 1048576.0f;
183
calib.par_p2 = (calib_p.nvm_par_p2 - 16384) / 536870912.0f;
184
calib.par_p3 = calib_p.nvm_par_p3 / 4294967296.0f;
185
calib.par_p4 = calib_p.nvm_par_p4 / 137438953472.0;
186
calib.par_p5 = calib_p.nvm_par_p5 * 8.0f;
187
calib.par_p6 = calib_p.nvm_par_p6 / 64.0;
188
calib.par_p7 = calib_p.nvm_par_p7 / 256.0f;
189
calib.par_p8 = calib_p.nvm_par_p8 / 32768.0f;
190
calib.par_p9 = calib_p.nvm_par_p9 / 281474976710656.0f;
191
calib.par_p10 = calib_p.nvm_par_p10 / 281474976710656.0f;
192
calib.par_p11 = calib_p.nvm_par_p11 / 36893488147419103232.0f;
193
}
194
195
/*
196
update temperature from raw sample
197
*/
198
void AP_Baro_BMP388::update_temperature(uint32_t data)
199
{
200
float partial1 = data - calib.par_t1;
201
float partial2 = partial1 * calib.par_t2;
202
203
WITH_SEMAPHORE(_sem);
204
temperature = partial2 + sq(partial1) * calib.par_t3;
205
}
206
207
/*
208
update pressure from raw pressure data
209
*/
210
void AP_Baro_BMP388::update_pressure(uint32_t data)
211
{
212
float partial1 = calib.par_p6 * temperature;
213
float partial2 = calib.par_p7 * powf(temperature, 2);
214
float partial3 = calib.par_p8 * powf(temperature, 3);
215
float partial_out1 = calib.par_p5 + partial1 + partial2 + partial3;
216
217
partial1 = calib.par_p2 * temperature;
218
partial2 = calib.par_p3 * powf(temperature, 2);
219
partial3 = calib.par_p4 * powf(temperature, 3);
220
float partial_out2 = data * (calib.par_p1 + partial1 + partial2 + partial3);
221
222
partial1 = powf(data, 2);
223
partial2 = calib.par_p9 + calib.par_p10 * temperature;
224
partial3 = partial1 * partial2;
225
float partial4 = partial3 + powf(data, 3) * calib.par_p11;
226
float press = partial_out1 + partial_out2 + partial4;
227
228
WITH_SEMAPHORE(_sem);
229
230
pressure_sum += press;
231
pressure_count++;
232
}
233
234
/*
235
read registers, special SPI handling needed
236
*/
237
bool AP_Baro_BMP388::read_registers(uint8_t reg, uint8_t *data, uint8_t len)
238
{
239
// when on I2C we just read normally
240
if (dev->bus_type() != AP_HAL::Device::BUS_TYPE_SPI) {
241
return dev->read_registers(reg, data, len);
242
}
243
// for SPI we need to discard the first returned byte. See
244
// datasheet for explanation
245
uint8_t b[len+2];
246
b[0] = reg | 0x80;
247
memset(&b[1], 0, len+1);
248
if (!dev->transfer(b, len+2, b, len+2)) {
249
return false;
250
}
251
memcpy(data, &b[2], len);
252
return true;
253
}
254
255
#endif // AP_BARO_BMP388_ENABLED
256
257