rotor-control-stm32/src/movement.rs

190 lines
6.8 KiB
Rust
Raw Permalink Normal View History

2022-08-24 20:08:51 +02:00
use embassy_stm32::adc::Adc;
2022-08-21 22:19:34 +02:00
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::peripherals;
2022-08-28 22:03:17 +02:00
use embassy_time::{with_timeout, Delay, Duration, Timer};
use embassy_util::blocking_mutex::raw::ThreadModeRawMutex;
use embassy_util::channel::mpmc::{Receiver, Sender};
2022-08-21 22:19:34 +02:00
use embassy_util::{select, Either};
2022-08-24 20:08:51 +02:00
use futures_util::future::join;
use heapless::Vec;
use crate::usb::Gs232Cmd;
use crate::{AzElPair, RotorState};
2022-08-28 22:03:17 +02:00
// ADC reading for azimuth 0°
2022-08-24 20:08:51 +02:00
const AZ_MIN_READING: f32 = 0.0;
2022-08-28 22:03:17 +02:00
// ADC reading for azimuth 360°
2022-08-24 20:08:51 +02:00
const AZ_MAX_READING: f32 = 4096.0;
2022-08-28 22:03:17 +02:00
// Range of motion for azimuth (0 to AZ_RANGE)
2022-08-24 20:08:51 +02:00
const AZ_RANGE: f32 = 360.0;
2022-09-05 19:21:32 +02:00
// Tolerance for the azimuth setpoint
const AZ_SLOP : i16 = 1;
2022-08-24 20:08:51 +02:00
2022-08-28 22:03:17 +02:00
// ADC reading for elevation 0°
2022-08-24 20:08:51 +02:00
const EL_MIN_READING: f32 = 0.0;
2022-08-28 22:03:17 +02:00
// ADC reading for elevation 360°
2022-08-24 20:08:51 +02:00
const EL_MAX_READING: f32 = 4096.0;
2022-08-28 22:03:17 +02:00
// Range of motion for elevantion (0 to EL_RANGE)
2022-09-05 19:21:32 +02:00
const EL_RANGE: f32 = 180.0;
// Tolerance for the elevation setpoint
const EL_SLOP : i16 = 1;
2022-08-24 20:08:51 +02:00
#[embassy_executor::task]
pub async fn movement_task(
2022-08-24 20:08:51 +02:00
adc1: peripherals::ADC1,
mut az_pin: peripherals::PA0,
mut el_pin: peripherals::PA1,
cw_pin: peripherals::PA2,
ccw_pin: peripherals::PA3,
up_pin: peripherals::PA4,
down_pin: peripherals::PA5,
cmd_receiver: Receiver<'static, ThreadModeRawMutex, Gs232Cmd, 1>,
pos_sender: Sender<'static, ThreadModeRawMutex, AzElPair, 1>,
state_sender: Sender<'static, ThreadModeRawMutex, RotorState, 1>,
) {
2022-08-28 22:03:17 +02:00
// Initialize the rotor state
let mut rotor_state = RotorState {
actual_pos: AzElPair { az: 0, el: 0 },
setpoint_pos: AzElPair { az: 0, el: 0 },
2022-08-24 20:08:51 +02:00
stopped: true,
};
2022-08-28 22:03:17 +02:00
// Setup output pins for moving the rotor
2022-08-21 22:19:34 +02:00
let mut cw_pin = Output::new(cw_pin, Level::Low, Speed::Low);
let mut ccw_pin = Output::new(ccw_pin, Level::Low, Speed::Low);
let mut up_pin = Output::new(up_pin, Level::Low, Speed::Low);
let mut down_pin = Output::new(down_pin, Level::Low, Speed::Low);
2022-08-28 22:03:17 +02:00
// Setup the ADC for reading the rotor positions
let mut adc = Adc::new(adc1, &mut Delay);
// Do an initial ADC reading to initialize the averages
2022-08-24 20:08:51 +02:00
let az_reading = adc.read(&mut az_pin) as f32;
let el_reading = adc.read(&mut el_pin) as f32;
let mut az_average = Average::new(az_reading);
let mut el_average = Average::new(el_reading);
loop {
2022-08-28 22:03:17 +02:00
// Wait until either a new command has been received or 100ms have elapsed
2022-08-21 22:19:34 +02:00
match select(
cmd_receiver.recv(),
Timer::after(Duration::from_millis(100)),
)
.await
{
2022-08-28 22:03:17 +02:00
// A new command has been received. This task only cares about MoveTo and Stop.
2022-08-21 22:19:34 +02:00
Either::First(cmd) => match cmd {
2022-08-28 22:03:17 +02:00
// Move to command. Update the setpoint pair in the rotor state
2022-08-21 22:19:34 +02:00
Gs232Cmd::MoveTo(pair) => {
rotor_state.setpoint_pos = pair;
rotor_state.stopped = false;
}
2022-08-28 22:03:17 +02:00
// Stop command. Set the stopped flag.
2022-08-21 22:19:34 +02:00
Gs232Cmd::Stop => {
rotor_state.stopped = true;
}
2022-08-28 22:03:17 +02:00
// Everthing elese is an noop.
2022-08-21 22:19:34 +02:00
_ => {}
},
2022-08-28 22:03:17 +02:00
// Second case of the select statement. Timer has elapsed.
2022-08-21 22:19:34 +02:00
Either::Second(_) => {
2022-08-28 22:03:17 +02:00
// First read the current rotor position
2022-08-24 20:08:51 +02:00
let az_reading = adc.read(&mut az_pin) as f32;
let el_reading = adc.read(&mut el_pin) as f32;
2022-08-28 22:03:17 +02:00
// Apply the averaging filters
2022-08-24 20:08:51 +02:00
az_average.add(az_reading);
el_average.add(el_reading);
2022-08-28 22:03:17 +02:00
// Calculate the position in degreee
2022-08-24 20:08:51 +02:00
let az_actual = (az_average.average() - AZ_MIN_READING)
/ (AZ_MAX_READING - AZ_MIN_READING)
* AZ_RANGE;
let el_actual = (el_average.average() - EL_MIN_READING)
/ (EL_MAX_READING - EL_MIN_READING)
* EL_RANGE;
2022-08-28 22:03:17 +02:00
// Update the rotor state
2022-08-24 20:08:51 +02:00
rotor_state.actual_pos.az = az_actual as u16;
rotor_state.actual_pos.el = el_actual as u16;
2022-09-05 19:21:32 +02:00
let delta_az =
rotor_state.setpoint_pos.az as i16 - rotor_state.actual_pos.az as i16;
let delta_el =
rotor_state.setpoint_pos.el as i16 - rotor_state.actual_pos.el as i16;
if !rotor_state.stopped && delta_az > AZ_SLOP {
2022-08-28 22:03:17 +02:00
// Azimuth needs to move clockwise
2022-08-21 22:19:34 +02:00
cw_pin.set_high();
ccw_pin.set_low();
2022-09-05 19:21:32 +02:00
} else if !rotor_state.stopped && delta_az < -AZ_SLOP {
2022-08-28 22:03:17 +02:00
// Azimuth needs to move counter clockwise
2022-08-21 22:19:34 +02:00
cw_pin.set_low();
ccw_pin.set_high();
} else {
2022-08-28 22:03:17 +02:00
// Either azimuth is on the setpoint or the rotor has beend stopped.
2022-08-21 22:19:34 +02:00
cw_pin.set_low();
ccw_pin.set_low();
}
2022-09-05 19:21:32 +02:00
if !rotor_state.stopped && delta_el > EL_SLOP {
2022-08-28 22:03:17 +02:00
// Elevation needs to move up
2022-08-21 22:19:34 +02:00
up_pin.set_high();
down_pin.set_low();
2022-09-05 19:21:32 +02:00
} else if !rotor_state.stopped && delta_el < -EL_SLOP {
2022-08-28 22:03:17 +02:00
// Elevation needs to move down
2022-08-21 22:19:34 +02:00
up_pin.set_low();
down_pin.set_high();
} else {
2022-08-28 22:03:17 +02:00
// Either elevation is on the setpoint or the rotor has beend stopped.
2022-08-21 22:19:34 +02:00
up_pin.set_low();
down_pin.set_low();
}
2022-08-28 22:03:17 +02:00
// Send the state to the display task and the position usb.
// Use timeouts to prevent blocking if display or usb task are unresponsive.
2022-08-21 22:19:34 +02:00
join(
2022-08-28 22:03:17 +02:00
with_timeout(
Duration::from_millis(100),
pos_sender.send(rotor_state.actual_pos),
),
2022-08-28 00:38:01 +02:00
with_timeout(Duration::from_millis(100), state_sender.send(rotor_state)),
2022-08-21 22:19:34 +02:00
)
.await;
}
};
}
}
2022-08-24 20:08:51 +02:00
2022-08-28 22:03:17 +02:00
// Simple sliding average filter
2022-08-24 20:08:51 +02:00
struct Average {
pos: usize,
2022-09-05 19:21:32 +02:00
data: Vec<f32, 5>,
2022-08-24 20:08:51 +02:00
}
impl Average {
2022-08-28 22:03:17 +02:00
// Create a new filter and prefill the state using an initial value
2022-08-24 20:08:51 +02:00
fn new(initial: f32) -> Average {
2022-09-05 19:21:32 +02:00
let mut data: Vec<f32, 5> = Vec::new();
data.resize(5, initial).unwrap();
2022-08-24 20:08:51 +02:00
Average { pos: 0, data }
}
2022-08-28 22:03:17 +02:00
// Adds a new value to internal state
2022-08-24 20:08:51 +02:00
fn add(&mut self, sample: f32) {
self.data[self.pos] = sample;
self.pos = (self.pos + 1) % self.data.len();
}
2022-08-28 22:03:17 +02:00
// Calculate the average value from the internal state
2022-08-24 20:08:51 +02:00
fn average(&self) -> f32 {
let mut sum = 0.0;
for sample in &self.data {
sum += sample;
}
sum / self.data.len() as f32
}
}