use embassy_sync::{
blocking_mutex::raw::CriticalSectionRawMutex,
watch::{Receiver, Sender, Watch},
};
use motor_mixers::MotorMixerMessage;
#[cfg(feature = "rpm_filters")]
use motor_mixers::RpmNotchFilterBankConfig;
use sensor_fusion::{MadgwickFilterf32, SensorFusion};
use simple_bitset::BitSet64;
use crate::{
config::{FastConfigItem, FastConfigSubscriber},
flight::{FilterAccGyro, FlightController, ImuFilterBank, ImuFilterBankConfig, RcControls, VehicleControl},
sensors::{GyroPidMessage, SetpointMessage},
tasks::{imu_task::IMU_SIGNAL, motor_mixer_task::MOTOR_MIXER_SIGNAL, rx_task::RxReceiver},
};
#[cfg(feature = "gps")]
use crate::tasks::gps_task::GPS_YAW_HEADING_SIGNAL;
#[cfg(feature = "multicore")]
static EXECUTOR_CORE1: embassy_executor::InterruptExecutor = InterruptExecutor::new();
#[cfg(feature = "multicore")]
fn core1_entry(ctx_ptr: usize) -> ! {
let ctx = unsafe { &mut *(ctx_ptr as *mut GyroPidContext) };
let spawner = EXECUTOR_CORE1.start(interrupt::IO_IRQ_BANK0);
spawner.spawn(gyro_pid_task(ctx)).unwrap();
loop {
cortex_m::asm::wfi();
}
}
const GYRO_PID_WATCH_COUNT: usize = 3;
static GYRO_PID_WATCH: Watch<CriticalSectionRawMutex, GyroPidMessage, GYRO_PID_WATCH_COUNT> = Watch::new();
type GyroPidSender = Sender<'static, CriticalSectionRawMutex, GyroPidMessage, GYRO_PID_WATCH_COUNT>;
pub fn gyro_pid_sender() -> GyroPidSender {
GYRO_PID_WATCH.sender()
}
#[allow(unused)]
pub type GyroPidReceiver = Receiver<'static, CriticalSectionRawMutex, GyroPidMessage, GYRO_PID_WATCH_COUNT>;
#[allow(unused)]
#[allow(clippy::expect_used)]
pub fn gyro_pid_receiver() -> GyroPidReceiver {
GYRO_PID_WATCH.receiver().expect("gyro_pid receiver failed")
}
const SETPOINT_WATCH_COUNT: usize = 3;
static SETPOINT_WATCH: Watch<CriticalSectionRawMutex, SetpointMessage, SETPOINT_WATCH_COUNT> = Watch::new();
type SetpointSender = Sender<'static, CriticalSectionRawMutex, SetpointMessage, SETPOINT_WATCH_COUNT>;
pub fn setpoint_sender() -> SetpointSender {
SETPOINT_WATCH.sender()
}
pub type SetpointReceiver = Receiver<'static, CriticalSectionRawMutex, SetpointMessage, SETPOINT_WATCH_COUNT>;
#[allow(unused)]
#[allow(clippy::expect_used)]
pub fn setpoint_receiver() -> SetpointReceiver {
SETPOINT_WATCH.receiver().expect("setpoint receiver failed")
}
pub struct GyroPidContext {
pub rx_receiver: RxReceiver,
pub gyro_pid_sender: GyroPidSender,
pub setpoint_sender: SetpointSender,
pub fast_config_subscriber: FastConfigSubscriber,
pub imu_filters: ImuFilterBank,
pub sensor_fusion: MadgwickFilterf32,
pub flight_controller: FlightController,
pub rc_controls: RcControls,
pub rc_modes: BitSet64,
}
impl GyroPidContext {
pub fn new(
rx_receiver: RxReceiver,
gyro_pid_sender: GyroPidSender,
setpoint_sender: SetpointSender,
fast_config_subscriber: FastConfigSubscriber,
imu_filter_bank_config: ImuFilterBankConfig,
#[cfg(feature = "rpm_filters")] rpm_notch_filter_bank_config: RpmNotchFilterBankConfig,
#[cfg(feature = "rpm_filters")] looptime_seconds: f32,
) -> Self {
Self {
rx_receiver,
gyro_pid_sender,
setpoint_sender,
fast_config_subscriber,
#[cfg(not(feature = "rpm_filters"))]
imu_filters: ImuFilterBank::with_config(imu_filter_bank_config),
#[cfg(feature = "rpm_filters")]
imu_filters: ImuFilterBank::with_config_and_notch(
imu_filter_bank_config,
rpm_notch_filter_bank_config,
looptime_seconds,
),
sensor_fusion: MadgwickFilterf32::new(),
flight_controller: FlightController::new(),
rc_controls: RcControls::new(),
rc_modes: BitSet64::new(),
}
}
}
#[embassy_executor::task]
pub async fn gyro_pid_task(ctx: &'static mut GyroPidContext) {
log::info!(" GYRO_PID: task started");
let mut time_us: u32 = 0;
let mut loop_count: u32 = 0;
let mut gyro_pid_send_count: u32 = 0;
let gyro_pid_denominator = 10;
loop {
let imu_data = IMU_SIGNAL.wait().await;
let delta_t = imu_data.delta_t;
let gyro_rps_unfiltered = imu_data.gyro_rps;
let (acc, gyro_rps) = ctx.imu_filters.update(imu_data.acc, imu_data.gyro_rps, delta_t);
#[cfg(feature = "gps")]
if let Some(gps_yaw_heading) = GPS_YAW_HEADING_SIGNAL.try_take() {
_ = ctx.sensor_fusion.correct_yaw(gps_yaw_heading.yaw_heading_radians, gps_yaw_heading.delta_t);
}
let orientation = ctx.sensor_fusion.fuse_acc_gyro(acc, gyro_rps, delta_t);
if let Some(rx_message) = ctx.rx_receiver.try_changed() {
ctx.rc_controls = rx_message.controls;
ctx.rc_modes = rx_message.rc_modes;
}
let (motor_commands, setpoints_updated) = ctx.flight_controller.calculate_motor_commands(
gyro_rps,
orientation,
delta_t,
ctx.rc_controls,
ctx.rc_modes,
);
MOTOR_MIXER_SIGNAL.signal(MotorMixerMessage::from(motor_commands));
gyro_pid_send_count += 1;
if gyro_pid_send_count >= gyro_pid_denominator {
gyro_pid_send_count = 0;
let gyro_pid_message =
GyroPidMessage { orientation, acc, gyro_rps, gyro_rps_unfiltered, time_us, ..Default::default() };
ctx.gyro_pid_sender.send(gyro_pid_message);
if setpoints_updated {
let mut setpoint_message = SetpointMessage::new();
setpoint_message.time_us = time_us;
setpoint_message.rc_modes = ctx.rc_modes;
setpoint_message.setpoints = [
ctx.rc_controls.roll_stick_dps,
ctx.rc_controls.pitch_stick_dps,
ctx.rc_controls.yaw_stick_dps,
ctx.rc_controls.throttle_stick,
];
setpoint_message.failsafe_phase = ctx.rc_controls.failsafe;
ctx.setpoint_sender.send(setpoint_message);
}
}
if let Some(wait_result) = ctx.fast_config_subscriber.try_next_message()
&& let embassy_sync::pubsub::WaitResult::Message(fast_config_item) = wait_result
{
match fast_config_item {
FastConfigItem::RollRate(gains) => {
ctx.flight_controller.set_pid_gains(FlightController::ROLL_RATE_DPS, gains);
}
FastConfigItem::PitchRate(gains) => {
ctx.flight_controller.set_pid_gains(FlightController::PITCH_RATE_DPS, gains);
}
FastConfigItem::YawRate(gains) => {
ctx.flight_controller.set_pid_gains(FlightController::YAW_RATE_DPS, gains);
}
FastConfigItem::RollAngle(gains) => {
ctx.flight_controller.set_pid_gains(FlightController::ROLL_ANGLE_DEGREES, gains);
}
FastConfigItem::PitchAngle(gains) => {
ctx.flight_controller.set_pid_gains(FlightController::PITCH_ANGLE_DEGREES, gains);
}
}
}
time_us = time_us.wrapping_add(125);
if loop_count.is_multiple_of(100) {
log::info!(" GYRO_PID: loop {loop_count}");
}
loop_count = loop_count.wrapping_add(1); }
}