GCC Code Coverage Report


Directory: libhal-mpl/
File: libhal-mpl/src/mpl3115a2.cpp
Date: 2024-03-31 12:24:12
Exec Total Coverage
Lines: 0 117 0.0%
Functions: 0 14 0.0%
Branches: 0 108 0.0%

Line Branch Exec Source
1 #include <libhal-mpl/mpl3115a2.hpp>
2
3 #include <array>
4
5 #include <libhal-util/i2c.hpp>
6
7 #include "mpl3115a2_reg.hpp"
8
9 using namespace std::literals;
10 namespace hal::mpl {
11 namespace {
12 /**
13 * @brief Set the ctrl_reg1_alt bit in ctrl_reg1 to the value corresponding to
14 * 'mode'
15 * @param p_i2c The I2C peripheral used for communication with the device.
16 * @param p_mode: The desired operation mode
17 */
18 ✗ hal::status set_mode(hal::i2c* p_i2c, mpl3115a2::mode p_mode)
19 {
20 // Read value of ctrl_reg1
21 ✗ auto ctrl_buffer =
22 ✗ HAL_CHECK(hal::write_then_read<1>(*p_i2c,
23 device_address,
24 std::array<hal::byte, 1>{ ctrl_reg1 },
25 hal::never_timeout()));
26
27 ✗ hal::byte reg_val = ctrl_buffer[0];
28 ✗ if ((reg_val & ctrl_reg1_alt) != static_cast<int>(p_mode)) {
29 // Set mode ctrl reg bit to binary value of 'mode'
30 ✗ if (p_mode == mpl3115a2::mode::barometer) {
31 ✗ reg_val &= ~ctrl_reg1_alt; // Set the bit to 0
32 } else {
33 ✗ reg_val |= ctrl_reg1_alt; // Set the bit to 1
34 }
35 }
36
37 ✗ HAL_CHECK(hal::write(*p_i2c,
38 device_address,
39 std::array<hal::byte, 2>{ ctrl_reg1, reg_val },
40 hal::never_timeout()));
41
42 ✗ return hal::success();
43 }
44
45 struct modify_reg_param_t
46 {
47 hal::byte address;
48 hal::byte bits_to_set;
49 };
50
51 /**
52 * @brief Set bits in a register without overwriting existing register state
53 * @param p_i2c The I2C peripheral used for communication with the device.
54 * @param p_reg_addr: 8 bit register address
55 * @param p_bits_to_set: 8 bit value specifying which bits to set in register
56 */
57 ✗ hal::status modify_reg_bits(hal::i2c* p_i2c, modify_reg_param_t p_reg)
58 {
59 // Read old register value
60 ✗ auto reg_buffer =
61 ✗ HAL_CHECK(hal::write_then_read<1>(*p_i2c,
62 device_address,
63 std::array<hal::byte, 1>{ p_reg.address },
64 hal::never_timeout()));
65
66 // Set specified bits while maintaining old values
67 ✗ hal::byte updated_reg = reg_buffer[0] | p_reg.bits_to_set;
68 ✗ HAL_CHECK(hal::write(*p_i2c,
69 device_address,
70 std::array<hal::byte, 2>{ p_reg.address, updated_reg },
71 hal::never_timeout()));
72
73 ✗ return hal::success();
74 }
75
76 /**
77 * @brief Wait for the reset bit in ctrl_reg1 to be set.
78 Catches and ignores expected std::errc::no_such_device_or_address.
79 * @param p_i2c The I2C peripheral used for communication with the device.
80 */
81 ✗ hal::status poll_reset(hal::i2c* p_i2c)
82 {
83 ✗ bool flag_set = true;
84 ✗ uint16_t retries = 0;
85
86 // Lambda function to poll ctrl_reg1 reset flag
87 ✗ auto poll_function = [&p_i2c, &flag_set]() -> hal::status {
88 ✗ std::array<hal::byte, 1> status_buffer{};
89 ✗ HAL_CHECK(hal::write_then_read(*p_i2c,
90 device_address,
91 std::array<hal::byte, 1>{ ctrl_reg1 },
92 status_buffer,
93 hal::never_timeout()));
94 ✗ flag_set = ((status_buffer[0] & ctrl_reg1_rst) != 0);
95 ✗ return hal::success();
96 ✗ };
97
98 // std::errc error code handler. std::errc::no_such_device_or_address is
99 // expected during reset as the device comes online.
100 ✗ auto err_handler = [](std::errc e_code) -> hal::status {
101 ✗ if (e_code != std::errc::no_such_device_or_address) {
102 ✗ return hal::new_error(e_code);
103 }
104 ✗ return hal::success();
105 };
106
107 // Perform polling
108 ✗ while (flag_set && (retries < mpl3115a2::default_max_polling_retries)) {
109 ✗ HAL_CHECK(hal::attempt(poll_function, err_handler));
110 ✗ retries++;
111 }
112
113 ✗ return hal::success();
114 }
115
116 struct poll_flag_param_t
117 {
118
119 /// 8 bit value specifying the register address
120 hal::byte address;
121 /// 8 bit value specifying which bit(s) to check
122 hal::byte flag;
123 /// The state of the bit to finish polling
124 bool desired_state;
125 };
126
127 /**
128 * @brief Wait for a specified flag bit in a register to be set to the desired
129 * state.
130 * @param p_i2c The I2C peripheral used for communication with the device.
131 * reached, the function will exit.
132 */
133 ✗ hal::status poll_flag(hal::i2c* p_i2c, poll_flag_param_t p_poll)
134 {
135 ✗ std::array<hal::byte, 1> status_payload{ p_poll.address };
136 ✗ std::array<hal::byte, 1> status_buffer{};
137 ✗ uint16_t retries = 0;
138 ✗ bool flag_set = true;
139
140 ✗ while (flag_set && (retries < mpl3115a2::default_max_polling_retries)) {
141 ✗ HAL_CHECK(hal::write_then_read(*p_i2c,
142 device_address,
143 status_payload,
144 status_buffer,
145 hal::never_timeout()));
146
147 ✗ if (p_poll.desired_state) {
148 ✗ flag_set = ((status_buffer[0] & p_poll.flag) == 0);
149 } else {
150 ✗ flag_set = ((status_buffer[0] & p_poll.flag) != 0);
151 }
152 ✗ retries++;
153 }
154
155 ✗ return hal::success();
156 }
157
158 /**
159 * @brief Trigger one-shot measurement by setting ctrl_reg1_ost bit in
160 * ctrl_reg1.
161 * @param p_i2c The I2C peripheral used for communication with the device.
162 */
163 ✗ hal::status initiate_one_shot(hal::i2c* p_i2c)
164 {
165 // Wait for one-shot flag to clear
166 ✗ poll_flag(
167 p_i2c,
168 { .address = ctrl_reg1, .flag = ctrl_reg1_ost, .desired_state = false });
169
170 // Set ost bit in ctrl_reg1 - initiate one shot measurement
171 ✗ HAL_CHECK(modify_reg_bits(
172 p_i2c, { .address = ctrl_reg1, .bits_to_set = ctrl_reg1_ost }));
173
174 ✗ return hal::success();
175 }
176 } // namespace
177
178 ✗ mpl3115a2::mpl3115a2(hal::i2c& p_i2c)
179 ✗ : m_i2c(&p_i2c)
180 ✗ , m_sensor_mode(mode::altimeter)
181 {
182 ✗ }
183
184 ✗ result<mpl3115a2> mpl3115a2::create(hal::i2c& p_i2c)
185 {
186 ✗ mpl3115a2 mpl_dev(p_i2c);
187
188 // sanity check
189 ✗ auto whoami_buffer =
190 ✗ HAL_CHECK(hal::write_then_read<1>(p_i2c,
191 device_address,
192 std::array<hal::byte, 1>{ whoami_r },
193 hal::never_timeout()));
194
195 ✗ if (whoami_buffer[0] != 0xC4) {
196 ✗ return hal::new_error(std::errc::no_such_device);
197 }
198
199 // software reset
200 ✗ modify_reg_bits(&p_i2c,
201 { .address = ctrl_reg1, .bits_to_set = ctrl_reg1_rst });
202
203 ✗ poll_reset(&p_i2c);
204
205 // set oversampling ratio to 2^128 and set altitude mode
206 ✗ modify_reg_bits(
207 &p_i2c,
208 { .address = ctrl_reg1, .bits_to_set = ctrl_reg1_os128 | ctrl_reg1_alt });
209
210 // enable data ready events for pressure/altitude and temperature
211 ✗ std::array<hal::byte, 2> dr_payload{
212 pt_data_cfg_r, pt_data_cfg_tdefe | pt_data_cfg_pdefe | pt_data_cfg_drem
213 };
214 ✗ HAL_CHECK(
215 hal::write(p_i2c, device_address, dr_payload, hal::never_timeout()));
216
217 ✗ return mpl_dev;
218 }
219
220 ✗ hal::status mpl3115a2::set_sea_pressure(float p_sea_level_pressure)
221 {
222 // divide by 2 to convert to 2Pa per LSB
223 ✗ auto two_pa = static_cast<std::uint16_t>(p_sea_level_pressure / 2.0f);
224 ✗ auto two_pa_hi = static_cast<hal::byte>((two_pa & 0xFF00) >> 8);
225 ✗ auto two_pa_lo = static_cast<hal::byte>(two_pa & 0x00FF);
226
227 // write result to register
228 ✗ std::array<hal::byte, 3> slp_payload = {
229 bar_in_msb_r,
230 two_pa_hi, // msb
231 two_pa_lo // lsb
232 ✗ };
233
234 ✗ HAL_CHECK(
235 hal::write(*m_i2c, device_address, slp_payload, hal::never_timeout()));
236
237 ✗ return hal::success();
238 }
239
240 ✗ hal::status mpl3115a2::set_altitude_offset(int8_t p_offset)
241 {
242 ✗ std::array<hal::byte, 2> offset_payload = { off_h_r, hal::byte(p_offset) };
243 ✗ HAL_CHECK(
244 hal::write(*m_i2c, device_address, offset_payload, hal::never_timeout()));
245
246 ✗ return hal::success();
247 }
248
249 ✗ hal::result<mpl3115a2::temperature_read_t> mpl3115a2::read_temperature()
250 {
251 ✗ constexpr float temp_conversion_factor = 256.0f;
252
253 ✗ initiate_one_shot(m_i2c);
254
255 ✗ poll_flag(m_i2c,
256 { .address = status_r, .flag = status_tdr, .desired_state = true });
257
258 // Read data from out_t_msb_r and out_t_lsb_r
259 ✗ auto temp_buffer =
260 ✗ HAL_CHECK(hal::write_then_read<2>(*m_i2c,
261 device_address,
262 std::array<hal::byte, 1>{ out_t_msb_r },
263 hal::never_timeout()));
264
265 ✗ auto temp_reading = (temp_buffer[0] << 8) | temp_buffer[1];
266 ✗ return mpl3115a2::temperature_read_t{
267 ✗ static_cast<float>(temp_reading) / temp_conversion_factor,
268 ✗ };
269 }
270
271 ✗ hal::result<mpl3115a2::pressure_read_t> mpl3115a2::read_pressure()
272 {
273 // Note: 64 -> Pa, 6400 -> kPa
274 ✗ constexpr float pressure_conversion_factor = 64.0f;
275
276 ✗ if (m_sensor_mode != mode::barometer) {
277 ✗ set_mode(m_i2c, mode::barometer);
278 ✗ m_sensor_mode = mode::barometer;
279 }
280
281 ✗ initiate_one_shot(m_i2c);
282
283 ✗ poll_flag(m_i2c,
284 { .address = status_r, .flag = status_pdr, .desired_state = true });
285
286 // Read data from out_p_msb_r, out_p_csb_r, and out_p_lsb_r
287 ✗ auto pres_buffer =
288 ✗ HAL_CHECK(hal::write_then_read<3>(*m_i2c,
289 device_address,
290 std::array<hal::byte, 1>{ out_p_msb_r },
291 hal::never_timeout()));
292
293 ✗ uint32_t pressure_reading = uint32_t(pres_buffer[0]) << 16 |
294 ✗ uint32_t(pres_buffer[1]) << 8 |
295 ✗ uint32_t(pres_buffer[2]);
296
297 ✗ return mpl3115a2::pressure_read_t{
298 ✗ static_cast<float>(pressure_reading) / pressure_conversion_factor,
299 ✗ };
300 }
301
302 ✗ hal::result<mpl3115a2::altitude_read_t> mpl3115a2::read_altitude()
303 {
304 ✗ constexpr float altitude_conversion_factor = 65536.0f;
305
306 ✗ if (m_sensor_mode != mode::altimeter) {
307 ✗ set_mode(m_i2c, mode::altimeter);
308 ✗ m_sensor_mode = mode::altimeter;
309 }
310
311 ✗ initiate_one_shot(m_i2c);
312
313 ✗ poll_flag(m_i2c,
314 { .address = status_r, .flag = status_pdr, .desired_state = true });
315
316 // Read data from out_p_msb_r, out_p_csb_r, and out_p_lsb_r
317 ✗ auto alt_buffer =
318 ✗ HAL_CHECK(hal::write_then_read<3>(*m_i2c,
319 device_address,
320 std::array<hal::byte, 1>{ out_p_msb_r },
321 hal::never_timeout()));
322
323 ✗ int32_t alt_reading = int32_t(alt_buffer[0]) << 24 |
324 ✗ int32_t(alt_buffer[1]) << 16 |
325 ✗ int32_t(alt_buffer[2]) << 8;
326
327 ✗ return mpl3115a2::altitude_read_t{
328 ✗ static_cast<float>(alt_reading) / altitude_conversion_factor,
329 ✗ };
330 }
331
332 } // namespace hal::mpl
333