1 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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2024.
//! This provides virtualized userspace access to a servomotor.
//!
//! Usage
//! -----
//!
//! ```ignore
//! use kernel::static_init;
//! let mux_pwm = components::pwm::PwmMuxComponent::new(&peripherals.pwm)
//! .finalize(components::pwm_mux_component_static!(rp2040::pwm::Pwm));
//!
//! let virtual_pwm_servo: &PwmPinUser<'static, rp2040::pwm::Pwm<'static>> =
//! components::pwm::PwmPinUserComponent::new(mux_pwm, rp2040::gpio::RPGpio::GPIO4)
//! .finalize(components::pwm_pin_user_component_static!(rp2040::pwm::Pwm));
//!
//! let sg90_servo = static_init!(
//! capsules_extra::sg90::Sg90<
//! 'static,
//! capsules_core::virtualizers::virtual_pwm::PwmPinUser<'static, rp2040::pwm::Pwm>,
//! >,
//! capsules_extra::sg90::Sg90::new(virtual_pwm_servo)
//! );
//!
//! // Here, we initialize an array of two SG90 servomotors as an example.
//! let multi_servo = static_init!(
//! [&'static dyn hil::servo::Servo<'static>; 2],
//! [sg90_servo, sg90_servo]
//! );
//! let servo = static_init!(
//! capsules_extra::servo::Servo<'static, 2>,
//! capsules_extra::servo::Servo::new(multi_servo)
//! );
//! ```
use kernel::hil;
use kernel::syscall::{CommandReturn, SyscallDriver};
use kernel::{ErrorCode, ProcessId};
/// Syscall driver number.
use capsules_core::driver;
pub const DRIVER_NUM: usize = driver::NUM::Servo as usize;
pub struct Servo<'a, const SERVO_COUNT: usize> {
/// The service capsule servo.
servo: &'a [&'a dyn hil::servo::Servo<'a>; SERVO_COUNT],
}
impl<'a, const SERVO_COUNT: usize> Servo<'a, SERVO_COUNT> {
pub fn new(servo: &'a [&'a dyn hil::servo::Servo<'a>; SERVO_COUNT]) -> Self {
Self { servo }
}
}
/// Provide an interface for userland.
impl<'a, const SERVO_COUNT: usize> SyscallDriver for Servo<'a, SERVO_COUNT> {
/// Command interface.
///
/// ### `command_num`
///
/// - `0`: Return Ok(()) if this driver is included on the platform.
/// - `1`: Returns an u32 representing the number of available servomotors.
/// - `2`: Changing the angle immediatelly.`servo_index` receives the index
/// corresponding to the servo whose angle we want to adjust
/// `angle` is used to receive a value between 0 and 180.
/// - `3`: Returning the current angle for a specific index.
fn command(
&self,
command_num: usize,
servo_index: usize,
angle: usize,
_processid: ProcessId,
) -> CommandReturn {
match command_num {
// Check whether the driver exists.
0 => CommandReturn::success(),
// Returns the number of available servomotors.
1 => CommandReturn::success_u32(SERVO_COUNT as u32),
// Change the angle immediately.
2 => {
if servo_index >= SERVO_COUNT {
CommandReturn::failure(ErrorCode::NODEVICE)
} else {
match angle.try_into() {
Ok(angle) => match self.servo[servo_index].set_angle(angle) {
Ok(()) => CommandReturn::success(),
Err(_) => CommandReturn::failure(ErrorCode::FAIL),
},
Err(_) => CommandReturn::failure(ErrorCode::INVAL),
}
}
}
// Return the current angle.
3 => {
if servo_index >= SERVO_COUNT {
CommandReturn::failure(ErrorCode::NODEVICE)
} else {
match self.servo[servo_index].get_angle() {
Ok(angle) => CommandReturn::success_u32(angle as u32),
Err(err) => CommandReturn::failure(err),
}
}
}
_ => CommandReturn::failure(ErrorCode::NOSUPPORT),
}
}
fn allocate_grant(&self, _process_id: ProcessId) -> Result<(), kernel::process::Error> {
Ok(())
}
}