actuator: make arming sequences owner-local
This commit is contained in:
1 parent
f200e88ef0
commit
c44286bdcf
3 files changed
+59
-83
No files matched your search
@@ -1,7 +1,6 @@
|
|||||||
//! DShot framing, timing, and arming policy.
|
//! DShot framing, timing, and arming policy.
|
||||||
|
|
||||||
use crate::ArmingDuration;
|
use crate::ArmingDuration;
|
||||||
use core::sync::atomic::{AtomicBool, AtomicU16, Ordering};
|
|
||||||
|
|
||||||
pub const MIN_THROTTLE: u16 = 48;
|
pub const MIN_THROTTLE: u16 = 48;
|
||||||
pub const MOTOR_STOP: u16 = 0;
|
pub const MOTOR_STOP: u16 = 0;
|
||||||
@@ -147,58 +146,50 @@ impl FrameValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ArmSequence {
|
pub struct ArmSequence {
|
||||||
armed_throttle: AtomicU16,
|
armed_throttle: Throttle,
|
||||||
zero_frames: AtomicU16,
|
zero_frames: u16,
|
||||||
remaining: AtomicU16,
|
remaining: u16,
|
||||||
armed: AtomicBool,
|
armed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArmSequence {
|
impl ArmSequence {
|
||||||
pub const fn new(armed_throttle: Throttle, zero_frames: u16) -> Self {
|
pub const fn new(armed_throttle: Throttle, zero_frames: u16) -> Self {
|
||||||
Self {
|
Self {
|
||||||
armed_throttle: AtomicU16::new(armed_throttle.get()),
|
armed_throttle,
|
||||||
zero_frames: AtomicU16::new(zero_frames),
|
zero_frames,
|
||||||
remaining: AtomicU16::new(0),
|
remaining: 0,
|
||||||
armed: AtomicBool::new(false),
|
armed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arm(&self) {
|
/// Starts the fixed configured stop-frame sequence. The driver which owns
|
||||||
self.remaining
|
/// this state must acquire external safety authorization before calling
|
||||||
.store(self.zero_frames.load(Ordering::Acquire), Ordering::Release);
|
/// this method and before every subsequent nonzero output commit.
|
||||||
self.armed.store(true, Ordering::Release);
|
pub fn arm(&mut self) {
|
||||||
|
self.remaining = self.zero_frames;
|
||||||
|
self.armed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_zero_frames(&self, zero_frames: u16) {
|
/// Stops the sequence; the physical output owner must emit an explicit
|
||||||
self.zero_frames.store(zero_frames, Ordering::Release);
|
/// stop frame independently of this local state transition.
|
||||||
|
pub fn disarm(&mut self) {
|
||||||
|
self.armed = false;
|
||||||
|
self.remaining = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_armed_throttle(&self, armed_throttle: Throttle) {
|
pub const fn is_armed(&self) -> bool {
|
||||||
self.armed_throttle
|
self.armed
|
||||||
.store(armed_throttle.get(), Ordering::Release);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disarm(&self) {
|
pub fn next_frame(&mut self) -> Option<FrameValue> {
|
||||||
self.armed.store(false, Ordering::Release);
|
|
||||||
self.remaining.store(0, Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_armed(&self) -> bool {
|
|
||||||
self.armed.load(Ordering::Acquire)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_frame(&self) -> Option<FrameValue> {
|
|
||||||
if !self.is_armed() {
|
if !self.is_armed() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if self.remaining.load(Ordering::Acquire) != 0 {
|
if self.remaining != 0 {
|
||||||
self.remaining.fetch_sub(1, Ordering::AcqRel);
|
self.remaining -= 1;
|
||||||
return Some(FrameValue::Stop);
|
return Some(FrameValue::Stop);
|
||||||
}
|
}
|
||||||
match Throttle::new(self.armed_throttle.load(Ordering::Acquire)) {
|
Some(FrameValue::Throttle(self.armed_throttle))
|
||||||
Some(throttle) => Some(FrameValue::Throttle(throttle)),
|
|
||||||
None => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +228,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn arm_sequence_holds_stop_for_the_configured_interval() {
|
fn arm_sequence_holds_stop_for_the_configured_interval() {
|
||||||
let throttle = Throttle::new(300).unwrap();
|
let throttle = Throttle::new(300).unwrap();
|
||||||
let sequence = ArmSequence::new(throttle, 2);
|
let mut sequence = ArmSequence::new(throttle, 2);
|
||||||
sequence.arm();
|
sequence.arm();
|
||||||
assert_eq!(sequence.next_frame(), Some(FrameValue::Stop));
|
assert_eq!(sequence.next_frame(), Some(FrameValue::Stop));
|
||||||
assert_eq!(sequence.next_frame(), Some(FrameValue::Stop));
|
assert_eq!(sequence.next_frame(), Some(FrameValue::Stop));
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
//! Protocol-neutral motor-output policy.
|
//! Protocol-neutral physical-motor protocol reference.
|
||||||
|
//!
|
||||||
|
//! This crate deliberately remains in the workspace for future, separately
|
||||||
|
//! reviewed physical-flight work. No checked-in board platform links it, and
|
||||||
|
//! its types model DShot/PWM framing and
|
||||||
|
//! local arming sequences only; they grant neither peripheral access nor
|
||||||
|
//! flight authority.
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
@@ -27,13 +33,3 @@ pub enum MotorProtocol {
|
|||||||
Pwm(pwm::Config),
|
Pwm(pwm::Config),
|
||||||
Dshot(dshot::Config),
|
Dshot(dshot::Config),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flight-control and MAVLink boundary for motor outputs.
|
|
||||||
pub trait MotorOutput {
|
|
||||||
/// Enables the configured armed output sequence.
|
|
||||||
fn arm(&mut self);
|
|
||||||
/// Stops output and returns all motor channels to their safe state.
|
|
||||||
fn disarm(&mut self);
|
|
||||||
/// Returns whether the output has accepted an arm request.
|
|
||||||
fn is_armed(&self) -> bool;
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
//! Conventional PWM framing and arming policy.
|
//! Conventional PWM framing and arming policy.
|
||||||
|
|
||||||
use crate::ArmingDuration;
|
use crate::ArmingDuration;
|
||||||
use core::sync::atomic::{AtomicBool, AtomicU16, Ordering};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@@ -104,60 +103,50 @@ pub enum FrameValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ArmSequence {
|
pub struct ArmSequence {
|
||||||
armed_idle: AtomicU16,
|
armed_idle: PulseWidth,
|
||||||
minimum_frames: AtomicU16,
|
minimum_frames: u16,
|
||||||
remaining: AtomicU16,
|
remaining: u16,
|
||||||
armed: AtomicBool,
|
armed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArmSequence {
|
impl ArmSequence {
|
||||||
pub const fn new(armed_idle: PulseWidth, minimum_frames: u16) -> Self {
|
pub const fn new(armed_idle: PulseWidth, minimum_frames: u16) -> Self {
|
||||||
Self {
|
Self {
|
||||||
armed_idle: AtomicU16::new(armed_idle.microseconds()),
|
armed_idle,
|
||||||
minimum_frames: AtomicU16::new(minimum_frames),
|
minimum_frames,
|
||||||
remaining: AtomicU16::new(0),
|
remaining: 0,
|
||||||
armed: AtomicBool::new(false),
|
armed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arm(&self) {
|
/// Starts the fixed configured minimum-frame sequence. The driver which
|
||||||
self.remaining.store(
|
/// owns this state must acquire external safety authorization before
|
||||||
self.minimum_frames.load(Ordering::Acquire),
|
/// calling this method and before every subsequent non-minimum commit.
|
||||||
Ordering::Release,
|
pub fn arm(&mut self) {
|
||||||
);
|
self.remaining = self.minimum_frames;
|
||||||
self.armed.store(true, Ordering::Release);
|
self.armed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_minimum_frames(&self, minimum_frames: u16) {
|
/// Stops the sequence; the physical output owner must drive its explicit
|
||||||
self.minimum_frames.store(minimum_frames, Ordering::Release);
|
/// minimum pulse independently of this local state transition.
|
||||||
|
pub fn disarm(&mut self) {
|
||||||
|
self.armed = false;
|
||||||
|
self.remaining = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_armed_idle(&self, armed_idle: PulseWidth) {
|
pub const fn is_armed(&self) -> bool {
|
||||||
self.armed_idle
|
self.armed
|
||||||
.store(armed_idle.microseconds(), Ordering::Release);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disarm(&self) {
|
pub fn next_frame(&mut self) -> Option<FrameValue> {
|
||||||
self.armed.store(false, Ordering::Release);
|
|
||||||
self.remaining.store(0, Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_armed(&self) -> bool {
|
|
||||||
self.armed.load(Ordering::Acquire)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_frame(&self) -> Option<FrameValue> {
|
|
||||||
if !self.is_armed() {
|
if !self.is_armed() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if self.remaining.load(Ordering::Acquire) != 0 {
|
if self.remaining != 0 {
|
||||||
self.remaining.fetch_sub(1, Ordering::AcqRel);
|
self.remaining -= 1;
|
||||||
return Some(FrameValue::Minimum);
|
return Some(FrameValue::Minimum);
|
||||||
}
|
}
|
||||||
match PulseWidth::new(self.armed_idle.load(Ordering::Acquire)) {
|
Some(FrameValue::ArmedIdle(self.armed_idle))
|
||||||
Some(pulse) => Some(FrameValue::ArmedIdle(pulse)),
|
|
||||||
None => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +165,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn sequence_holds_minimum_before_armed_idle() {
|
fn sequence_holds_minimum_before_armed_idle() {
|
||||||
let pulse = PulseWidth::new(1_100).unwrap();
|
let pulse = PulseWidth::new(1_100).unwrap();
|
||||||
let sequence = ArmSequence::new(pulse, 2);
|
let mut sequence = ArmSequence::new(pulse, 2);
|
||||||
sequence.arm();
|
sequence.arm();
|
||||||
assert_eq!(sequence.next_frame(), Some(FrameValue::Minimum));
|
assert_eq!(sequence.next_frame(), Some(FrameValue::Minimum));
|
||||||
assert_eq!(sequence.next_frame(), Some(FrameValue::Minimum));
|
assert_eq!(sequence.next_frame(), Some(FrameValue::Minimum));
|
||||||
|
|||||||
Reference in new issue
Block a user