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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2024.
use core::cell::Cell;
use core::mem::size_of;
use kernel::hil;
use kernel::ErrorCode;
pub struct Sg90<'a, P: hil::pwm::PwmPin> {
/// The underlying PWM generator to change the angle.
pwm_pin: &'a P,
/// Stores the angle everytime it changes.
current_angle: Cell<Option<usize>>,
}
impl<'a, P: hil::pwm::PwmPin> Sg90<'a, P> {
pub fn new(pwm_pin: &'a P) -> Sg90<'a, P> {
Sg90 {
pwm_pin,
current_angle: Cell::new(None),
}
}
}
impl<'a, P: hil::pwm::PwmPin> kernel::hil::servo::Servo<'a> for Sg90<'a, P> {
fn set_angle(&self, angle: u16) -> Result<(), ErrorCode> {
// The assert! macro ensures that the code will not compile on platforms
// where `usize` is smaller than `u16`.
const _: () = assert!(size_of::<usize>() >= size_of::<u16>());
if angle <= 180 {
self.current_angle.set(Some(angle as usize));
// As specified in the datasheet:
// https://www.friendlywire.com/projects/ne555-servo-safe/SG90-datasheet.pdf,
// the frequency used for sg90 servo is always 50hz.
const FREQUENCY_HZ: usize = 50;
// This calculates the pulse width in microseconds for a specific angle.
// 500 and 2000 miliseconds define the range within
// which the angle can be set to any position.
let pulse_width_us = 500 + 2000 / 180 * (angle as usize);
// The duty_cycle formula is (pulse_width/period)*100.
// The period is 20 000 miliseconds (also specified in the datasheet).
// If we simplify we're left with pulse_width/20.
// We also need to scale this to the maximum duty_cycle suported by the pin.
// We do this by multiplying the value we get from the
// get_maximum_duty_cycle() function with pulse_width/20 and divide it by 100.
// This leaves us with the below formula:
let duty_cycle = pulse_width_us * self.pwm_pin.get_maximum_duty_cycle() / 20000;
self.pwm_pin.start(FREQUENCY_HZ, duty_cycle)?;
Ok(())
} else {
Err(ErrorCode::INVAL)
}
}
fn get_angle(&self) -> Result<usize, ErrorCode> {
//The SG90 servomotor cannot return its angle.
Err(ErrorCode::NOSUPPORT)
}
}