refactor: move firmware workspace under firmware
This commit is contained in:
1 parent
153b0bf262
commit
f03fc119a4
222 files changed
+56
-13
No files matched your search
@@ -0,0 +1,257 @@
|
||||
//! F722 transport and vertical-route adapters for the portable data channel.
|
||||
|
||||
use super::{VerticalRoute, peripherals};
|
||||
use kaptan_data_channel::{DmaCompletion, FrameAdmission, FrameSink, TxHealth, VerticalDataConfig};
|
||||
use kaptan_dma::TxCompletion;
|
||||
use kaptan_mavlink::{
|
||||
MavlinkFrame,
|
||||
encoder::DistanceSensorConfig,
|
||||
transmitter::{FrameClass, MavlinkTransmitter, TransmitFaultKind},
|
||||
};
|
||||
|
||||
// Board endpoint, baud, sensor topology, and mounting remain immutable
|
||||
// build-time policy. They are not ground-station-editable in this telemetry
|
||||
// image: the F722 has no authenticated config ingress or activation runtime.
|
||||
pub(crate) const UART1_CLOCK_HZ: u32 = 108_000_000;
|
||||
pub(crate) const UART2_CLOCK_HZ: u32 = 54_000_000;
|
||||
pub(crate) const SERVICE_HZ: usize = 100;
|
||||
|
||||
/// Fixed service-timer configuration for the validated Pll216 profile.
|
||||
pub(crate) const SERVICE_TIMER_CONFIG: peripherals::Tim2PeriodicConfig =
|
||||
peripherals::Tim2PeriodicConfig {
|
||||
prescaler: 10_799,
|
||||
auto_reload: 99,
|
||||
};
|
||||
|
||||
/// Maps a selected vertical electrical route to development data-channel metadata.
|
||||
///
|
||||
/// The values are telemetry descriptions only; they neither calibrate a flight
|
||||
/// estimator nor authorize an `ExternalControlOnly` profile.
|
||||
pub(crate) fn vertical_data_config(route: VerticalRoute) -> VerticalDataConfig {
|
||||
let distance =
|
||||
|minimum_distance_centimetres, maximum_distance_centimetres| DistanceSensorConfig {
|
||||
sensor_id: 0,
|
||||
orientation:
|
||||
kaptan_mavlink::message::MavSensorOrientation::MAV_SENSOR_ROTATION_PITCH_270,
|
||||
minimum_distance_centimetres,
|
||||
maximum_distance_centimetres,
|
||||
};
|
||||
|
||||
match route {
|
||||
VerticalRoute::None => VerticalDataConfig {
|
||||
distance: None,
|
||||
optical_flow_sensor_id: None,
|
||||
maximum_age_ticks: 10,
|
||||
},
|
||||
VerticalRoute::Mtf01 => VerticalDataConfig {
|
||||
distance: Some(distance(2, 800)),
|
||||
optical_flow_sensor_id: Some(0),
|
||||
maximum_age_ticks: 10,
|
||||
},
|
||||
VerticalRoute::LidarLiteV3 => VerticalDataConfig {
|
||||
distance: Some(distance(20, 4_000)),
|
||||
optical_flow_sensor_id: None,
|
||||
maximum_age_ticks: 10,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// The two F722 DMA engines behind the portable data-channel transport.
|
||||
enum F722MavlinkTransport {
|
||||
Usart1(MavlinkTransmitter<peripherals::DcacheDisabledDmaMemory, peripherals::Usart1TxDma, 4>),
|
||||
Usart2(MavlinkTransmitter<peripherals::DcacheDisabledDmaMemory, peripherals::Usart2TxDma, 4>),
|
||||
}
|
||||
|
||||
/// Sole F722 MAVLink DMA owner plus unconfigurable transport-loss accounting.
|
||||
pub(crate) struct F722MavlinkTransmitter {
|
||||
inner: F722MavlinkTransport,
|
||||
health: TxHealth,
|
||||
pending_faults: u8,
|
||||
}
|
||||
|
||||
impl F722MavlinkTransmitter {
|
||||
pub(crate) fn usart1(
|
||||
rcc: &stm32f7::stm32f722::RCC,
|
||||
uart: stm32f7::stm32f722::USART1,
|
||||
baud: u32,
|
||||
) -> Result<Self, ()> {
|
||||
let uart = peripherals::Usart1Tx::new(rcc, uart, UART1_CLOCK_HZ, baud).map_err(|_| ())?;
|
||||
// SAFETY: platform initialization disables D-cache before this object
|
||||
// is created, and the active MAVLink frame remains queue-owned until
|
||||
// this platform receives a terminal DMA completion notification.
|
||||
let memory = unsafe { peripherals::DcacheDisabledDmaMemory::new() };
|
||||
Ok(Self {
|
||||
inner: F722MavlinkTransport::Usart1(MavlinkTransmitter::new(
|
||||
memory,
|
||||
peripherals::Usart1TxDma::new(rcc, uart),
|
||||
)),
|
||||
health: TxHealth::default(),
|
||||
pending_faults: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn usart2(
|
||||
rcc: &stm32f7::stm32f722::RCC,
|
||||
uart: stm32f7::stm32f722::USART2,
|
||||
baud: u32,
|
||||
) -> Result<Self, ()> {
|
||||
let uart = peripherals::Usart2Tx::new(rcc, uart, UART2_CLOCK_HZ, baud).map_err(|_| ())?;
|
||||
// SAFETY: identical DMA-memory invariant to the USART1 route above;
|
||||
// only the fixed platform DMA endpoint differs.
|
||||
let memory = unsafe { peripherals::DcacheDisabledDmaMemory::new() };
|
||||
Ok(Self {
|
||||
inner: F722MavlinkTransport::Usart2(MavlinkTransmitter::new(
|
||||
memory,
|
||||
peripherals::Usart2TxDma::new(rcc, uart),
|
||||
)),
|
||||
health: TxHealth::default(),
|
||||
pending_faults: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameSink for F722MavlinkTransmitter {
|
||||
fn send(&mut self, class: FrameClass, frame: MavlinkFrame) -> FrameAdmission {
|
||||
let (admission, start_fault) = match &mut self.inner {
|
||||
F722MavlinkTransport::Usart1(transmitter) => {
|
||||
let outcome = transmitter.admit_and_start(class, frame);
|
||||
(
|
||||
outcome.admission,
|
||||
outcome.start.err().map(|fault| fault.kind()),
|
||||
)
|
||||
}
|
||||
F722MavlinkTransport::Usart2(transmitter) => {
|
||||
let outcome = transmitter.admit_and_start(class, frame);
|
||||
(
|
||||
outcome.admission,
|
||||
outcome.start.err().map(|fault| fault.kind()),
|
||||
)
|
||||
}
|
||||
};
|
||||
if !matches!(
|
||||
admission,
|
||||
kaptan_mavlink::transmitter::EnqueueResult::Enqueued
|
||||
) {
|
||||
self.health.admission_loss_count = self.health.admission_loss_count.saturating_add(1);
|
||||
}
|
||||
if let Some(fault) = start_fault {
|
||||
self.record_fault(fault);
|
||||
}
|
||||
FrameAdmission::Retained(admission)
|
||||
}
|
||||
|
||||
fn on_dma_complete(&mut self, completion: TxCompletion) -> DmaCompletion {
|
||||
let start_fault = match &mut self.inner {
|
||||
F722MavlinkTransport::Usart1(transmitter) => transmitter
|
||||
.on_dma_complete(completion)
|
||||
.err()
|
||||
.map(|error| error.kind()),
|
||||
F722MavlinkTransport::Usart2(transmitter) => transmitter
|
||||
.on_dma_complete(completion)
|
||||
.err()
|
||||
.map(|error| error.kind()),
|
||||
};
|
||||
let terminal = matches!(
|
||||
completion,
|
||||
TxCompletion::TransferFault | TxCompletion::Halted
|
||||
);
|
||||
if let Some(fault) = start_fault {
|
||||
self.record_fault(fault);
|
||||
}
|
||||
if terminal {
|
||||
self.record_fault(TransmitFaultKind::Terminal);
|
||||
}
|
||||
if start_fault.is_some() || terminal {
|
||||
DmaCompletion::Fault
|
||||
} else {
|
||||
DmaCompletion::Complete
|
||||
}
|
||||
}
|
||||
|
||||
fn health(&self) -> TxHealth {
|
||||
self.health
|
||||
}
|
||||
|
||||
fn take_transport_fault(&mut self) -> Option<TransmitFaultKind> {
|
||||
let (bit, fault) = if self.pending_faults & 1 != 0 {
|
||||
(1, TransmitFaultKind::Memory)
|
||||
} else if self.pending_faults & 2 != 0 {
|
||||
(2, TransmitFaultKind::Engine)
|
||||
} else if self.pending_faults & 4 != 0 {
|
||||
(4, TransmitFaultKind::Terminal)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
self.pending_faults &= !bit;
|
||||
Some(fault)
|
||||
}
|
||||
}
|
||||
|
||||
impl F722MavlinkTransmitter {
|
||||
fn record_fault(&mut self, fault: TransmitFaultKind) {
|
||||
self.health.dma_fault_count = self.health.dma_fault_count.saturating_add(1);
|
||||
self.pending_faults |= match fault {
|
||||
TransmitFaultKind::Memory => {
|
||||
self.health.memory_fault_count = self.health.memory_fault_count.saturating_add(1);
|
||||
1
|
||||
}
|
||||
TransmitFaultKind::Engine => {
|
||||
self.health.engine_fault_count = self.health.engine_fault_count.saturating_add(1);
|
||||
2
|
||||
}
|
||||
TransmitFaultKind::Terminal => {
|
||||
self.health.terminal_fault_count =
|
||||
self.health.terminal_fault_count.saturating_add(1);
|
||||
4
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Board-route status light; it is not part of a board declaration API.
|
||||
pub(crate) enum F722StatusLed {
|
||||
Kakute(peripherals::ActiveLowLedPd2),
|
||||
Geprc(GeprcStatusLed),
|
||||
}
|
||||
|
||||
impl F722StatusLed {
|
||||
pub(crate) fn toggle(&mut self) {
|
||||
match self {
|
||||
Self::Kakute(led) => led.toggle(),
|
||||
Self::Geprc(led) => led.toggle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// GEPRC GEP-F722-HD V2 blue status LED on PA13.
|
||||
pub(crate) struct GeprcStatusLed {
|
||||
port: stm32f7::stm32f722::GPIOA,
|
||||
is_high: bool,
|
||||
}
|
||||
|
||||
impl GeprcStatusLed {
|
||||
pub(crate) fn new(rcc: &stm32f7::stm32f722::RCC, port: stm32f7::stm32f722::GPIOA) -> Self {
|
||||
rcc.ahb1enr().modify(|_, write| write.gpioaen().set_bit());
|
||||
port.moder().modify(|_, write| write.moder13().output());
|
||||
port.otyper().modify(|_, write| write.ot13().push_pull());
|
||||
port.ospeedr()
|
||||
.modify(|_, write| write.ospeedr13().low_speed());
|
||||
port.pupdr().modify(|_, write| write.pupdr13().floating());
|
||||
port.bsrr().write(|write| write.br13().set_bit());
|
||||
Self {
|
||||
port,
|
||||
is_high: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle(&mut self) {
|
||||
self.is_high = !self.is_high;
|
||||
self.port.bsrr().write(|write| {
|
||||
if self.is_high {
|
||||
write.bs13().set_bit()
|
||||
} else {
|
||||
write.br13().set_bit()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user