-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
469: Sound Pressure API r=jrvanwhy a=SheepSeb ## Pull Request Overview This pull request adds: - a sound pressure API , similar to the [libtock-c version]( https://github.com/tock/libtock-c/blob/master/libtock/sound_pressure.h ). The API provides a function that simulates a synchronous sound pressusre reading and also functions to initiate a reading, register/unregister a listener, check for driver existence, enable/disable the sound pressure measurement - an example app which samples the sound pressure sensor every 1 second and prints the measured sound pressure - a ```fake``` sound pressure driver, for testing - unit tests for the fake driver and for the user space library ## Testing Strategy This PR was tested with the unit tests and by running the example app on a microbit v2 board ## Documentation Updated no updates are required Co-authored-by: sebastian-nae <naesebi2000@gmail.com> Co-authored-by: Sebastian Nae <sebnae@Sebastians-MacBook-Pro.local> Co-authored-by: Sebastian Nae <naesebi2000@gmail.com>
- Loading branch information
Showing
9 changed files
with
382 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[package] | ||
name = "libtock_sound_pressure" | ||
version = "0.1.0" | ||
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"] | ||
license = "MIT/Apache-2.0" | ||
edition = "2018" | ||
repository = "https://www.github.com/tock/libtock-rs" | ||
description = "libtock sound pressure driver" | ||
|
||
[dependencies] | ||
libtock_platform = { path = "../../platform" } | ||
|
||
[dev-dependencies] | ||
libtock_unittest = { path = "../../unittest" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
#![no_std] | ||
|
||
use core::cell::Cell; | ||
use libtock_platform::{ | ||
share, subscribe::OneId, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall, | ||
}; | ||
|
||
pub struct SoundPressure<S: Syscalls>(S); | ||
|
||
impl<S: Syscalls> SoundPressure<S> { | ||
/// Returns Ok() if the driver was present.This does not necessarily mean | ||
/// that the driver is working. | ||
pub fn exists() -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, EXISTS, 0, 0).to_result() | ||
} | ||
|
||
/// Initiate a pressure measurement. | ||
/// This function is used both for synchronous and asynchronous readings | ||
pub fn read() -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, READ_PRESSURE, 0, 0).to_result() | ||
} | ||
|
||
/// Register an events listener | ||
pub fn register_listener<'share, F: Fn(u32)>( | ||
listener: &'share SoundPressureListener<F>, | ||
subscribe: share::Handle<Subscribe<'share, S, DRIVER_NUM, 0>>, | ||
) -> Result<(), ErrorCode> { | ||
S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener) | ||
} | ||
|
||
/// Unregister the events listener | ||
pub fn unregister_listener() { | ||
S::unsubscribe(DRIVER_NUM, 0) | ||
} | ||
|
||
/// Enable sound pressure measurement | ||
pub fn enable() -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, 2, 0, 0).to_result() | ||
} | ||
|
||
/// Disable sound pressure measurement | ||
pub fn disable() -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, 3, 0, 0).to_result() | ||
} | ||
|
||
/// Initiate a synchronous pressure measurement. | ||
/// Returns Ok(pressure_value) if the operation was successful | ||
/// pressure_value is between 0 and 255 | ||
pub fn read_sync() -> Result<u8, ErrorCode> { | ||
let pressure_cell: Cell<Option<u32>> = Cell::new(None); | ||
let listener = SoundPressureListener(|pressure_val| { | ||
pressure_cell.set(Some(pressure_val)); | ||
}); | ||
share::scope(|subscribe| { | ||
Self::register_listener(&listener, subscribe)?; | ||
Self::read()?; | ||
while pressure_cell.get() == None { | ||
S::yield_wait(); | ||
} | ||
match pressure_cell.get() { | ||
None => Err(ErrorCode::Fail), | ||
Some(pressure_val) => { | ||
if !(0..=256).contains(&pressure_val) { | ||
Err(ErrorCode::Invalid) | ||
} else { | ||
Ok(pressure_val as u8) | ||
} | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
|
||
pub struct SoundPressureListener<F: Fn(u32)>(pub F); | ||
impl<F: Fn(u32)> Upcall<OneId<DRIVER_NUM, 0>> for SoundPressureListener<F> { | ||
fn upcall(&self, pressure_val: u32, _arg1: u32, _arg2: u32) { | ||
(self.0)(pressure_val); | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Driver number and command IDs | ||
// ----------------------------------------------------------------------------- | ||
|
||
const DRIVER_NUM: u32 = 0x60006; | ||
|
||
// Command IDs | ||
|
||
const EXISTS: u32 = 0; | ||
const READ_PRESSURE: u32 = 1; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
use core::cell::Cell; | ||
use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn}; | ||
use libtock_unittest::fake; | ||
|
||
type SoundPressure = super::SoundPressure<fake::Syscalls>; | ||
|
||
#[test] | ||
fn no_driver() { | ||
let _kernel = fake::Kernel::new(); | ||
assert_eq!(SoundPressure::exists(), Err(ErrorCode::NoDevice)); | ||
} | ||
|
||
#[test] | ||
fn driver_check() { | ||
let kernel = fake::Kernel::new(); | ||
let driver = fake::SoundPressure::new(); | ||
kernel.add_driver(&driver); | ||
|
||
assert_eq!(SoundPressure::exists(), Ok(())); | ||
} | ||
|
||
#[test] | ||
fn driver_busy() { | ||
let kernel = fake::Kernel::new(); | ||
let driver = fake::SoundPressure::new(); | ||
kernel.add_driver(&driver); | ||
|
||
assert_eq!(SoundPressure::read(), Ok(())); | ||
assert!(driver.is_busy()); | ||
|
||
assert_eq!(SoundPressure::read(), Err(ErrorCode::Busy)); | ||
assert_eq!(SoundPressure::read_sync(), Err(ErrorCode::Busy)); | ||
} | ||
|
||
#[test] | ||
fn read_pressure() { | ||
let kernel = fake::Kernel::new(); | ||
let driver = fake::SoundPressure::new(); | ||
kernel.add_driver(&driver); | ||
|
||
let pressure_cell: Cell<Option<u32>> = Cell::new(None); | ||
let listener = crate::SoundPressureListener(|pressure_val| { | ||
pressure_cell.set(Some(pressure_val)); | ||
}); | ||
|
||
share::scope(|subscribe| { | ||
assert_eq!(SoundPressure::read(), Ok(())); | ||
driver.set_value(100); | ||
assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); | ||
|
||
assert_eq!( | ||
SoundPressure::register_listener(&listener, subscribe), | ||
Ok(()) | ||
); | ||
assert_eq!(SoundPressure::read(), Ok(())); | ||
driver.set_value(100); | ||
assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); | ||
assert_eq!(pressure_cell.get(), Some(100)); | ||
|
||
SoundPressure::unregister_listener(); | ||
assert_eq!(SoundPressure::read(), Ok(())); | ||
driver.set_value(100); | ||
assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); | ||
}); | ||
} | ||
|
||
#[test] | ||
fn read_pressure_sync() { | ||
let kernel = fake::Kernel::new(); | ||
let driver = fake::SoundPressure::new(); | ||
kernel.add_driver(&driver); | ||
|
||
driver.set_value_sync(100); | ||
assert_eq!(SoundPressure::read_sync(), Ok(100)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
//! This example shows how to use the sound pressure driver. | ||
//! It checks for the sound pressure driver and samples the sensor every second. | ||
|
||
#![no_main] | ||
#![no_std] | ||
|
||
use core::fmt::Write; | ||
use libtock::console::Console; | ||
|
||
use libtock::alarm::{Alarm, Milliseconds}; | ||
use libtock::runtime::{set_main, stack_size}; | ||
use libtock::sound_pressure::SoundPressure; | ||
|
||
set_main! {main} | ||
stack_size! {0x200} | ||
|
||
fn main() { | ||
if SoundPressure::exists().is_err() { | ||
writeln!(Console::writer(), "Sound pressure driver not found").unwrap(); | ||
return; | ||
} | ||
|
||
writeln!(Console::writer(), "Sound pressure driver found").unwrap(); | ||
let enable = SoundPressure::enable(); | ||
match enable { | ||
Ok(()) => { | ||
writeln!(Console::writer(), "Sound pressure driver enabled").unwrap(); | ||
loop { | ||
match SoundPressure::read_sync() { | ||
Ok(sound_pressure_val) => writeln!( | ||
Console::writer(), | ||
"Sound Pressure: {}\n", | ||
sound_pressure_val | ||
) | ||
.unwrap(), | ||
Err(_) => { | ||
writeln!(Console::writer(), "error while reading sound pressure",).unwrap() | ||
} | ||
} | ||
Alarm::sleep_for(Milliseconds(1000)).unwrap(); | ||
} | ||
} | ||
Err(_e) => writeln!(Console::writer(), "Sound pressure driver enable failed",).unwrap(), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
//! Fake implementation of the SoundPressure API, documented here: | ||
//! | ||
//! Like the real API, `SoundPressure` controls a fake sound pressure sensor. It provides | ||
//! a function `set_value` used to immediately call an upcall with a sound pressure value read by the sensor | ||
//! and a function 'set_value_sync' used to call the upcall when the read command is received. | ||
use crate::{DriverInfo, DriverShareRef}; | ||
use libtock_platform::{CommandReturn, ErrorCode}; | ||
use std::cell::Cell; | ||
|
||
// The `upcall_on_command` field is set to Some(value) if an upcall(with value as its argument) should be called when read command is received, | ||
// or None otherwise. It was needed for testing `read_sync` library function which simulates a synchronous sound pressure read, | ||
// because it was impossible to schedule an upcall during the `synchronous` read in other ways. | ||
pub struct SoundPressure { | ||
busy: Cell<bool>, | ||
upcall_on_command: Cell<Option<u8>>, | ||
share_ref: DriverShareRef, | ||
} | ||
|
||
impl SoundPressure { | ||
pub fn new() -> std::rc::Rc<SoundPressure> { | ||
std::rc::Rc::new(SoundPressure { | ||
busy: Cell::new(false), | ||
upcall_on_command: Cell::new(None), | ||
share_ref: Default::default(), | ||
}) | ||
} | ||
|
||
pub fn is_busy(&self) -> bool { | ||
self.busy.get() | ||
} | ||
|
||
pub fn set_value(&self, value: u8) { | ||
if self.busy.get() { | ||
self.share_ref | ||
.schedule_upcall(0, (value as u32, 0, 0)) | ||
.expect("Unable to schedule upcall"); | ||
self.busy.set(false); | ||
} | ||
} | ||
|
||
pub fn set_value_sync(&self, value: u8) { | ||
self.upcall_on_command.set(Some(value)); | ||
} | ||
} | ||
|
||
impl crate::fake::SyscallDriver for SoundPressure { | ||
fn info(&self) -> DriverInfo { | ||
DriverInfo::new(DRIVER_NUM).upcall_count(1) | ||
} | ||
|
||
fn register(&self, share_ref: DriverShareRef) { | ||
self.share_ref.replace(share_ref); | ||
} | ||
|
||
fn command(&self, command_id: u32, _argument0: u32, _argument1: u32) -> CommandReturn { | ||
match command_id { | ||
EXISTS => crate::command_return::success(), | ||
|
||
READ_PRESSURE => { | ||
if self.busy.get() { | ||
return crate::command_return::failure(ErrorCode::Busy); | ||
} | ||
self.busy.set(true); | ||
if let Some(val) = self.upcall_on_command.take() { | ||
self.set_value(val as u8); | ||
} | ||
crate::command_return::success() | ||
} | ||
_ => crate::command_return::failure(ErrorCode::NoSupport), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
// ----------------------------------------------------------------------------- | ||
// Driver number and command IDs | ||
// ----------------------------------------------------------------------------- | ||
|
||
const DRIVER_NUM: u32 = 0x60006; | ||
|
||
// Command IDs | ||
const EXISTS: u32 = 0; | ||
const READ_PRESSURE: u32 = 1; |
Oops, something went wrong.