capsules_extra/sg90.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2024.
4
5use core::cell::Cell;
6use core::mem::size_of;
7use kernel::hil;
8use kernel::ErrorCode;
9pub struct Sg90<'a, P: hil::pwm::PwmPin> {
10 /// The underlying PWM generator to change the angle.
11 pwm_pin: &'a P,
12 /// Stores the angle everytime it changes.
13 current_angle: Cell<Option<usize>>,
14}
15
16impl<'a, P: hil::pwm::PwmPin> Sg90<'a, P> {
17 pub fn new(pwm_pin: &'a P) -> Sg90<'a, P> {
18 Sg90 {
19 pwm_pin,
20 current_angle: Cell::new(None),
21 }
22 }
23}
24
25impl<'a, P: hil::pwm::PwmPin> kernel::hil::servo::Servo<'a> for Sg90<'a, P> {
26 fn set_angle(&self, angle: u16) -> Result<(), ErrorCode> {
27 // The assert! macro ensures that the code will not compile on platforms
28 // where `usize` is smaller than `u16`.
29 const _: () = assert!(size_of::<usize>() >= size_of::<u16>());
30 if angle <= 180 {
31 self.current_angle.set(Some(angle as usize));
32 // As specified in the datasheet:
33 // https://www.friendlywire.com/projects/ne555-servo-safe/SG90-datasheet.pdf,
34 // the frequency used for sg90 servo is always 50hz.
35 const FREQUENCY_HZ: usize = 50;
36 // This calculates the pulse width in microseconds for a specific angle.
37 // 500 and 2000 miliseconds define the range within
38 // which the angle can be set to any position.
39 let pulse_width_us = 500 + 2000 / 180 * (angle as usize);
40 // The duty_cycle formula is (pulse_width/period)*100.
41 // The period is 20 000 miliseconds (also specified in the datasheet).
42 // If we simplify we're left with pulse_width/20.
43 // We also need to scale this to the maximum duty_cycle suported by the pin.
44 // We do this by multiplying the value we get from the
45 // get_maximum_duty_cycle() function with pulse_width/20 and divide it by 100.
46 // This leaves us with the below formula:
47 let duty_cycle = pulse_width_us * self.pwm_pin.get_maximum_duty_cycle() / 20000;
48 self.pwm_pin.start(FREQUENCY_HZ, duty_cycle)?;
49 Ok(())
50 } else {
51 Err(ErrorCode::INVAL)
52 }
53 }
54
55 fn get_angle(&self) -> Result<usize, ErrorCode> {
56 //The SG90 servomotor cannot return its angle.
57 Err(ErrorCode::NOSUPPORT)
58 }
59}