Skip to content

Commit 09f6b4e

Browse files
author
Connor Truono
committed
Add accelerometer self-test function to LIS2DW12 driver
1 parent d772ff0 commit 09f6b4e

File tree

2 files changed

+210
-1
lines changed

2 files changed

+210
-1
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ rust-version = "1.85"
1010
# dependencies for all targets
1111
bilge = "0.2.0"
1212
embedded-hal-async = "1.0.0"
13+
embassy-time = { git = "https://github.com/embassy-rs/embassy" }
1314

1415
[dev-dependencies]
1516
embedded-hal-mock = { version = "0.11.1", features = ["embedded-hal-async"] }

src/lib.rs

Lines changed: 209 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
#![cfg_attr(not(test), no_std)]
1212

13+
use crate::Reserved0::Res0;
1314
use embedded_hal_async::i2c::I2c;
1415

1516
pub mod registers;
@@ -289,11 +290,218 @@ impl<I2C: embedded_hal_async::i2c::I2c> Lis2dw12<I2C> {
289290
/// Returns the previous value of the Control3 register
290291
pub async fn set_self_test_mode(&mut self, self_test: Control3SelfTest) -> Result<u8, I2C::Error> {
291292
// Create ControlReg3 with self test field (the others will not be modified due to the update mask)
292-
let reg: u8 = ControlReg3::new(false, false, Reserved0::Res0, false, false, false, self_test).into();
293+
let reg: u8 = ControlReg3::new(false, false, Res0, false, false, false, self_test).into();
293294
self.modify_reg_field(Register::Control3, reg, SELF_TEST_MODE_MASK)
294295
.await
295296
}
296297

298+
pub async fn accel_self_test(&mut self) -> Result<bool, I2C::Error> {
299+
// Self-test parameters
300+
const LOW_DIFF_MGS: f32 = 70.0;
301+
const HIGH_DIFF_MGS: f32 = 1500.0;
302+
const TEST_SAMPLES: usize = 5;
303+
const TEST_STAGE_SLEEP_MS: usize = 100;
304+
const MAX_SAMPLE_ATTEMPTS: usize = 2 * TEST_SAMPLES * TEST_STAGE_SLEEP_MS;
305+
306+
let mut avg_x_pos: f32 = 0.0;
307+
let mut avg_y_pos: f32 = 0.0;
308+
let mut avg_z_pos: f32 = 0.0;
309+
310+
let mut avg_x_neg: f32 = 0.0;
311+
let mut avg_y_neg: f32 = 0.0;
312+
let mut avg_z_neg: f32 = 0.0;
313+
314+
let mut avg_x_unbiased: f32 = 0.0;
315+
let mut avg_y_unbiased: f32 = 0.0;
316+
let mut avg_z_unbiased: f32 = 0.0;
317+
318+
// 1. Configure self-test settings
319+
// Control 1: 1600 HZ, High-Performance, 14bit resolution, LowPower1, 50 HZ
320+
let control1: u8 = self
321+
.modify_reg_field(
322+
Register::Control1,
323+
ControlReg1::new(
324+
registers::Control1LowPowerMode::LowPower1,
325+
registers::Control1ModeSelect::HighPerformance,
326+
registers::Control1DataRate::HiLo50Hz,
327+
)
328+
.into(),
329+
0xFF, // Update the entire register
330+
)
331+
.await?;
332+
333+
// Control 2: Enable Block Data Update
334+
let control2: u8 = self
335+
.modify_reg_field(
336+
Register::Control2,
337+
ControlReg2::new(
338+
false, false, false, true, // Block Data Update:
339+
false, Res0, false, false,
340+
)
341+
.into(),
342+
0b1000,
343+
)
344+
.await?;
345+
346+
// Control 3: Single Data Conversion disabled
347+
let control3: u8 = self
348+
.modify_reg_field(
349+
Register::Control3,
350+
ControlReg3::new(
351+
false, // Single data conversion mode disabled
352+
false, // Single data conversion mode enabled by INT2 external trigger
353+
Res0,
354+
false,
355+
false,
356+
false,
357+
registers::Control3SelfTest::NormalMode, // Enabled after all controls set
358+
)
359+
.into(),
360+
0b11, // Single data conversion mode bits
361+
)
362+
.await?;
363+
364+
// Control 6: Low noise Config Disabled, Full Scale Range 4g
365+
let control6: u8 = self
366+
.modify_reg_field(
367+
Register::Control6,
368+
ControlReg6::new(
369+
[Res0, Res0],
370+
false, // Disable low noise
371+
false,
372+
Control6FullScale::Scale4g,
373+
Control6BandwidthSelection::ODRdiv2,
374+
)
375+
.into(),
376+
0b00110100, // Full Scale + Low Noise
377+
)
378+
.await?;
379+
380+
// 2. Record unbiased accelerometer samples
381+
embassy_time::Timer::after_millis(TEST_STAGE_SLEEP_MS as u64).await;
382+
self.flush_samples().await?;
383+
384+
let mut sample: usize = 0;
385+
let mut attempts: usize = 0;
386+
while sample < TEST_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS {
387+
if self.status().await?.drdy() {
388+
let unbiased_acc: (f32, f32, f32) = self.acc_mgs().await?;
389+
avg_x_unbiased += unbiased_acc.0;
390+
avg_y_unbiased += unbiased_acc.1;
391+
avg_z_unbiased += unbiased_acc.2;
392+
sample += 1;
393+
}
394+
attempts += 1;
395+
}
396+
if sample == 0 {
397+
return Ok(false);
398+
}
399+
avg_x_unbiased /= TEST_SAMPLES as f32;
400+
avg_y_unbiased /= TEST_SAMPLES as f32;
401+
avg_z_unbiased /= TEST_SAMPLES as f32;
402+
403+
// 3. Enable Self-Test mode, beginning with Positive Sign
404+
let mut _control3 = self
405+
.set_self_test_mode(registers::Control3SelfTest::PositiveSign)
406+
.await?;
407+
408+
embassy_time::Timer::after_millis(TEST_STAGE_SLEEP_MS as u64).await;
409+
self.flush_samples().await?;
410+
411+
// 4. Record positive accelerometer samples
412+
sample = 0;
413+
attempts = 0;
414+
while sample < TEST_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS {
415+
if self.status().await?.drdy() {
416+
let pos_acc: (f32, f32, f32) = self.acc_mgs().await?;
417+
avg_x_pos += pos_acc.0;
418+
avg_y_pos += pos_acc.1;
419+
avg_z_pos += pos_acc.2;
420+
sample += 1;
421+
}
422+
attempts += 1;
423+
}
424+
if sample == 0 {
425+
return Ok(false);
426+
}
427+
avg_x_pos /= TEST_SAMPLES as f32;
428+
avg_y_pos /= TEST_SAMPLES as f32;
429+
avg_z_pos /= TEST_SAMPLES as f32;
430+
431+
// 5. Set Negative Sign Self-Test
432+
_control3 = self
433+
.set_self_test_mode(registers::Control3SelfTest::NegativeSign)
434+
.await?;
435+
436+
embassy_time::Timer::after_millis(TEST_STAGE_SLEEP_MS as u64).await;
437+
self.flush_samples().await?;
438+
439+
// 6. Record negative self-test accelerometer samples
440+
sample = 0;
441+
attempts = 0;
442+
while sample < TEST_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS {
443+
if self.status().await?.drdy() {
444+
let neg_acc: (f32, f32, f32) = self.acc_mgs().await?;
445+
avg_x_neg += neg_acc.0;
446+
avg_y_neg += neg_acc.1;
447+
avg_z_neg += neg_acc.2;
448+
sample += 1;
449+
}
450+
attempts += 1;
451+
}
452+
if sample == 0 {
453+
return Ok(false);
454+
}
455+
avg_x_neg /= TEST_SAMPLES as f32;
456+
avg_y_neg /= TEST_SAMPLES as f32;
457+
avg_z_neg /= TEST_SAMPLES as f32;
458+
459+
// 7. Reset the changed accelerometer registers to their previous setting
460+
self.write_reg(Register::Control1, control1).await?;
461+
self.write_reg(Register::Control2, control2).await?;
462+
self.write_reg(Register::Control3, control3).await?;
463+
self.write_reg(Register::Control6, control6).await?;
464+
465+
// 8. Compare differences to expected values
466+
// Average across test samples
467+
let pos_dif_x: f32 = avg_x_pos - avg_x_unbiased;
468+
let pos_dif_y: f32 = avg_y_pos - avg_y_unbiased;
469+
let pos_dif_z: f32 = avg_z_pos - avg_z_unbiased;
470+
let neg_dif_x: f32 = avg_x_unbiased - avg_x_neg;
471+
let neg_dif_y: f32 = avg_y_unbiased - avg_y_neg;
472+
let neg_dif_z: f32 = avg_z_unbiased - avg_z_neg;
473+
474+
// Ensure all differences line up within defined range
475+
let res: bool = pos_dif_x > LOW_DIFF_MGS
476+
&& pos_dif_x < HIGH_DIFF_MGS
477+
&& pos_dif_y > LOW_DIFF_MGS
478+
&& pos_dif_y < HIGH_DIFF_MGS
479+
&& pos_dif_z > LOW_DIFF_MGS
480+
&& pos_dif_z < HIGH_DIFF_MGS
481+
&& neg_dif_x > LOW_DIFF_MGS
482+
&& neg_dif_x < HIGH_DIFF_MGS
483+
&& neg_dif_y > LOW_DIFF_MGS
484+
&& neg_dif_y < HIGH_DIFF_MGS
485+
&& neg_dif_z > LOW_DIFF_MGS
486+
&& neg_dif_z < HIGH_DIFF_MGS;
487+
488+
Ok(res)
489+
}
490+
491+
/// Flush Samples: Reads an accelerometer sample if the data is ready
492+
/// For use in Block Data Update mode
493+
async fn flush_samples(&mut self) -> Result<u8, I2C::Error> {
494+
let mut samples: u8 = 0;
495+
496+
let status: StatusReg = self.status().await?;
497+
if status.drdy() {
498+
let _acc = self.acc().await?;
499+
samples += 1;
500+
}
501+
502+
Ok(samples)
503+
}
504+
297505
// -------------------------- Helper Functions --------------------------
298506

299507
/// Converts i16 temperature representation to degrees Celsius

0 commit comments

Comments
 (0)