Skip to content

Commit b673920

Browse files
committed
Merge branch 'working-fin-board-can'
2 parents bd69b4e + 93250bf commit b673920

7 files changed

Lines changed: 139 additions & 113 deletions

File tree

firmwares/fancyfins/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

firmwares/fancyfins/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ shared_types = { path = "../../shared_types" }
3434
w25q = "0.2.9"
3535
smart-leds = "0.4.0"
3636
ws2812-spi = "0.5.0"
37+
heapless = "0.8"
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use defmt::info;
2+
use smart_leds::RGB8;
3+
4+
/// This is unique to Wirth leds on the Hyacinth FancyFin hardware.
5+
pub const GREEN: RGB8 = RGB8::new(0, 255, 0);
6+
pub const RED: RGB8 = RGB8::new(255, 0, 0);
7+
pub const BLUE: RGB8 = RGB8::new(0, 0, 255);
8+
pub const WHITE: RGB8 = RGB8::new(255, 255, 255);
9+
pub const BLACK: RGB8 = RGB8::new(0, 0, 0);
10+
11+
/// Takes a percentage and lights up the leds up to (but not including) this part.
12+
/// Other leds will be turned off.
13+
pub fn mk_fill_percentage(percentage: u8, color: RGB8, buffer: &mut [RGB8], background: Option<RGB8>) {
14+
info!("mk_fill_percentage: {}", percentage);
15+
let percentage = percentage.clamp(0, 100);
16+
let top_position = (percentage as usize * buffer.len()) / 100;
17+
for i in 0..top_position {
18+
buffer[led_position_to_index(i as u32) as usize] = color;
19+
}
20+
if let Some(background) = background {
21+
for i in top_position..buffer.len() {
22+
buffer[led_position_to_index(i as u32) as usize] = background;
23+
}
24+
}
25+
}
26+
27+
/// Takes the position of the led counted from the bottom up, and returns the index in the chain.
28+
/// This is unique to Hyacinth FancyFin hardware.
29+
fn led_position_to_index(position: u32) -> u32 {
30+
let half = position / 2;
31+
10 + half - (position % 2) * (2 * half + 1)
32+
}

firmwares/fancyfins/src/can.rs

Lines changed: 45 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,32 @@
11
use defmt::*;
22
use embassy_executor::Spawner;
3-
use embassy_stm32::can::bxcan::{filter, Fifo, Frame, Id, StandardId};
3+
use embassy_stm32::can::bxcan::{Data, Fifo, Frame, Id, StandardId, filter};
44
use embassy_stm32::can::{Can, CanRx, CanTx};
55
use embassy_stm32::peripherals::CAN;
66
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
77

88
use embassy_sync::pubsub::{PubSubChannel, Publisher, Subscriber};
9-
use shared_types::{CanBusMessage, CanBusMessageId, FlightMode, TelemetryToPayloadMessage};
9+
use shared_types::FlightMode;
10+
//use shared_types::{CanBusMessage, CanBusMessageId, FlightMode, TelemetryToPayloadMessage};
11+
use heapless::Vec;
1012
use static_cell::StaticCell;
1113

14+
use shared_types::can::{
15+
CanMessage,
16+
structure::{Can2aFrame, CanFrameId},
17+
};
18+
1219
pub const CAN_QUEUE_SIZE: usize = 3; // TODO
1320
pub const NUM_CAN_SUBSCRIBERS: usize = 1; // TODO
1421
pub const NUM_CAN_PUBLISHERS: usize = 1; // TODO
1522

16-
pub type CanFrame = (u16, [u8; 8]);
17-
pub type CanInChannel = PubSubChannel<CriticalSectionRawMutex, CanFrame, CAN_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>;
18-
pub type CanInSubscriper =
19-
Subscriber<'static, CriticalSectionRawMutex, CanFrame, CAN_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>;
20-
pub type CanOutChannel = PubSubChannel<CriticalSectionRawMutex, CanFrame, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>;
21-
pub type CanOutPublisher = Publisher<'static, CriticalSectionRawMutex, CanFrame, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>;
22-
23-
pub static CAN_IN: StaticCell<CanInChannel> = StaticCell::new();
24-
pub static CAN_OUT: StaticCell<CanOutChannel> = StaticCell::new();
23+
pub type CanRxCh = PubSubChannel<CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>;
24+
pub type CanRxSub = Subscriber<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>;
25+
pub type CanTxCh = PubSubChannel<CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>;
26+
pub type CanTxPub = Publisher<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>;
2527

26-
pub const NUM_FLIGHT_MODE_SUBSCRIBERS: usize = 3;
27-
pub type FlightModeChannel = PubSubChannel<CriticalSectionRawMutex, FlightMode, 1, NUM_FLIGHT_MODE_SUBSCRIBERS, 1>;
28-
pub type FlightModeSubscriber =
29-
Subscriber<'static, CriticalSectionRawMutex, FlightMode, 1, NUM_FLIGHT_MODE_SUBSCRIBERS, 1>;
30-
pub static FLIGHT_MODE: StaticCell<FlightModeChannel> = StaticCell::new();
28+
pub static CAN_IN: StaticCell<CanRxCh> = StaticCell::new();
29+
pub static CAN_OUT: StaticCell<CanTxCh> = StaticCell::new();
3130

3231
static CAN: StaticCell<Can<'static, CAN>> = StaticCell::new();
3332
static CAN_TX: StaticCell<CanTx<'static, 'static, CAN>> = StaticCell::new();
@@ -36,8 +35,8 @@ static CAN_RX: StaticCell<CanRx<'static, 'static, CAN>> = StaticCell::new();
3635
pub async fn spawn(
3736
mut can: Can<'static, CAN>,
3837
spawner: Spawner,
39-
publisher: Publisher<'static, CriticalSectionRawMutex, CanFrame, CAN_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>,
40-
subscriber: Subscriber<'static, CriticalSectionRawMutex, CanFrame, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>,
38+
publisher: Publisher<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>,
39+
subscriber: Subscriber<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>,
4140
) {
4241
can.modify_config()
4342
.set_loopback(false)
@@ -46,8 +45,9 @@ pub async fn spawn(
4645
.leave_disabled();
4746

4847
let telemetry_filter = filter::Mask32::frames_with_std_id(
49-
StandardId::new(CanBusMessageId::TelemetryBroadcast(0).into()).unwrap(),
50-
StandardId::new(0x700).unwrap(),
48+
//StandardId::new(CanBusMessageId::TelemetryBroadcast(0).into()).unwrap(),
49+
StandardId::new(0x1af).unwrap(),
50+
StandardId::new(0).unwrap(),
5151
);
5252

5353
can.modify_filters().enable_bank(0, Fifo::Fifo0, telemetry_filter);
@@ -67,15 +67,15 @@ pub async fn spawn(
6767
#[embassy_executor::task]
6868
async fn run_tx(
6969
can_tx: &'static mut CanTx<'static, 'static, CAN>,
70-
mut subscriber: Subscriber<'static, CriticalSectionRawMutex, CanFrame, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>,
70+
mut subscriber: Subscriber<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, 1, NUM_CAN_PUBLISHERS>,
7171
) -> ! {
7272
loop {
73-
let (address, data) = subscriber.next_message_pure().await;
74-
let Some(sid) = StandardId::new(address) else {
73+
let msg: Can2aFrame = subscriber.next_message_pure().await.into();
74+
let Some(sid) = StandardId::new(msg.id.into()) else {
7575
continue;
7676
};
7777

78-
let frame = Frame::new_data(sid, data);
78+
let frame = Frame::new_data(sid, Data::new(msg.payload.as_slice()).unwrap());
7979
can_tx.write(&frame).await;
8080
can_tx.flush_all().await;
8181
}
@@ -84,46 +84,41 @@ async fn run_tx(
8484
#[embassy_executor::task]
8585
async fn run_rx(
8686
can_rx: &'static mut CanRx<'static, 'static, CAN>,
87-
publisher: Publisher<'static, CriticalSectionRawMutex, CanFrame, CAN_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>,
87+
publisher: Publisher<'static, CriticalSectionRawMutex, CanMessage, CAN_QUEUE_SIZE, NUM_CAN_SUBSCRIBERS, 1>,
8888
) -> ! {
8989
loop {
9090
match can_rx.read().await {
9191
Ok(envelope) => {
9292
let frame = envelope.frame;
93-
let Some(data) = frame.data() else {
93+
let Id::Standard(id) = frame.id() else {
94+
warn!("Unexpected extended 29bit Can Frame Id, dropping.");
9495
continue;
9596
};
96-
97-
let Id::Standard(sid) = frame.id() else {
98-
// EID package, skipping.
97+
let Some(frame_data) = frame.data() else {
98+
warn!("Unexpected non-data Can Frame, dropping.");
9999
continue;
100100
};
101-
let id_raw = sid.as_raw();
102101

103-
let Ok(data_array) = data.as_ref().try_into() else {
104-
// frame wasn't 8 bytes long, skip.
102+
let Ok(data) = Vec::from_slice(frame_data) else {
103+
warn!("Unexpected Can Frame data of lenght > 8 bytes, dropping.");
105104
continue;
106105
};
107-
108-
publisher.publish_immediate((id_raw, data_array));
106+
let Ok(id) = CanFrameId::try_from(id.as_raw()) else {
107+
warn!(
108+
"Can Frame id could not be mapped to a type, either the sender's or this software might be outdated. id: {}",
109+
id.as_raw()
110+
);
111+
continue;
112+
};
113+
let message = Can2aFrame { id, payload: data };
114+
let Ok(message_parsed) = CanMessage::try_from(message) else {
115+
warn!("Malformed Can Message payload, message could not be parsed into type, dropping.");
116+
continue;
117+
};
118+
publisher.publish_immediate(message_parsed); // TODO: think about publish immediate
109119
}
110120
Err(e) => {
111-
error!("Failed to read envelope: {:?}", Debug2Format(&e))
112-
}
113-
}
114-
}
115-
}
116-
117-
#[embassy_executor::task]
118-
pub async fn run_flight_mode_listener(
119-
mut can_subscriber: CanInSubscriper,
120-
flight_mode_publisher: Publisher<'static, CriticalSectionRawMutex, FlightMode, 1, NUM_FLIGHT_MODE_SUBSCRIBERS, 1>,
121-
) -> ! {
122-
loop {
123-
let (sid, data) = can_subscriber.next_message_pure().await;
124-
if sid == CanBusMessageId::TelemetryBroadcast(0).into() {
125-
if let Ok(Some(msg)) = TelemetryToPayloadMessage::parse(data) {
126-
flight_mode_publisher.publish_immediate(msg.mode);
121+
error!("Can Bus Error: Failed to read can envelope: {:?}", Debug2Format(&e))
127122
}
128123
}
129124
}

firmwares/fancyfins/src/leds.rs

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
use defmt::error;
2-
use embassy_executor;
1+
use defmt::*;
32
use embassy_stm32::peripherals::*;
4-
use embassy_stm32::spi::{Config, Phase, Polarity, Spi};
3+
use embassy_stm32::spi::{Config, Phase, Polarity};
54
use embassy_stm32::time::Hertz;
65
use embassy_time::{Duration, Timer};
76
use shared_types::FlightMode;
8-
use smart_leds::SmartLedsWrite;
7+
use shared_types::can::{CanMessage, TelemetryBroadcast};
98
use smart_leds::RGB8;
9+
use smart_leds::SmartLedsWrite;
1010
use ws2812_spi::Ws2812;
1111

12+
use animations::{BLACK, BLUE, GREEN, RED, WHITE};
13+
14+
use crate::animations::{self, mk_fill_percentage};
15+
1216
const NAVIGATION_LIGHT_PATTERN: [RGB8; 16] = [
1317
RGB8 {
1418
r: 0xff,
@@ -93,47 +97,53 @@ const NAVIGATION_LIGHT_PATTERN: [RGB8; 16] = [
9397
];
9498
const NAVIGATION_LIGHT_BLINK_DURATION_MILLIS: u64 = 50;
9599

96-
async fn boot_animation(leds: &mut Ws2812<Spi<'static, SPI3, DMA1_CH1, DMA1_CH2>>) {
97-
let mut colors = [RGB8::default(); 16];
98-
99-
for i in 0..16 {
100-
Timer::after(Duration::from_millis(20)).await;
101-
colors[i] = RGB8 {
102-
r: 0x00,
103-
g: 0xff,
104-
b: 0x00,
105-
};
106-
let _ = leds.write(colors);
100+
async fn boot_animation(
101+
buffer: &mut [RGB8],
102+
led_lightup_time: Duration,
103+
color: RGB8,
104+
mut update_cb: impl FnMut(&[RGB8]),
105+
) {
106+
for i in 0..buffer.len() {
107+
mk_fill_percentage(((100 * i) / buffer.len()) as u8, color, buffer, Some(BLACK));
108+
update_cb(buffer);
109+
Timer::after(led_lightup_time).await;
107110
}
108-
109111
Timer::after(Duration::from_millis(200)).await;
110-
let _ = leds.write([RGB8::default(); 16]);
112+
mk_fill_percentage(100, BLACK, buffer, Some(BLACK));
113+
update_cb(buffer);
111114
Timer::after(Duration::from_millis(200)).await;
112115
}
113116

114117
#[embassy_executor::task]
115118
pub async fn run(
116-
mut flight_mode_subscriber: crate::can::FlightModeSubscriber,
119+
mut can_rx_sub: crate::can::CanRxSub,
117120
spi: SPI3,
118121
led_signal_pin: PB5,
119122
dma_out: DMA1_CH1,
120123
dma_in: DMA1_CH2,
121124
) -> ! {
122-
let mut flight_mode = FlightMode::default();
123-
125+
// spi settings
124126
let mut config = Config::default();
125-
config.frequency = Hertz::khz(3500);
127+
config.frequency = Hertz::khz(2400);
126128
config.mode.polarity = Polarity::IdleLow;
127129
config.mode.phase = Phase::CaptureOnSecondTransition;
128130
let spi_bus = embassy_stm32::spi::Spi::new_txonly_nosck(spi, led_signal_pin, dma_out, dma_in, config);
129131

130132
let mut leds = Ws2812::new(spi_bus);
133+
let mut flight_mode = FlightMode::default();
134+
let mut buffer: [RGB8; 20] = [BLACK; 20];
131135

132-
boot_animation(&mut leds).await;
136+
boot_animation(&mut buffer, Duration::from_millis(20), GREEN, |b: &[RGB8]| {
137+
// TODO: get rid of ocpied
138+
let _ = leds.write(b.iter().copied());
139+
})
140+
.await;
133141

134142
loop {
135-
while let Some(new_fm) = flight_mode_subscriber.try_next_message_pure() {
136-
flight_mode = new_fm;
143+
while let Some(msg) = can_rx_sub.try_next_message_pure() {
144+
if let CanMessage::Telem(TelemetryBroadcast::FlightMode(new_fm)) = msg {
145+
flight_mode = new_fm;
146+
}
137147
}
138148

139149
if flight_mode >= FlightMode::Burn {

firmwares/fancyfins/src/main.rs

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,21 @@
22
#![no_main]
33

44
use embassy_executor::Spawner;
5+
use embassy_stm32::Config;
56
use embassy_stm32::adc::Adc;
67
use embassy_stm32::gpio::Input;
78
use embassy_stm32::peripherals::*;
89
use embassy_stm32::time::Hertz;
910
use embassy_stm32::wdg::IndependentWatchdog;
10-
use embassy_stm32::Config;
1111
use embassy_sync::pubsub::PubSubChannel;
1212
use embassy_time::{Delay, Duration, Ticker};
1313

1414
use {defmt_rtt as _, panic_probe as _};
1515

16+
mod animations;
1617
mod can;
1718
mod leds;
18-
mod strain_gauge;
19+
//mod strain_gauge;
1920

2021
use can::*;
2122

@@ -50,17 +51,6 @@ async fn main(low_priority_spawner: Spawner) {
5051
config.rcc.adcclk = Some(Hertz::mhz(14));
5152
let p = embassy_stm32::init(config);
5253

53-
// Remap CAN to be on PB8/9
54-
embassy_stm32::pac::AFIO.mapr().modify(|w| w.set_can1_remap(2));
55-
56-
let addr0 = Input::new(p.PA8, embassy_stm32::gpio::Pull::Up);
57-
let addr1 = Input::new(p.PA9, embassy_stm32::gpio::Pull::Up);
58-
let address = (addr0.is_low() as u8) << 1 + (addr1.is_low() as u8);
59-
// Fin #1 has a broken jumper, so hardcode this when flashing.
60-
//let address = 1;
61-
62-
defmt::info!("Fin #{} startup", address);
63-
6454
// Start watchdog
6555
let mut iwdg = IndependentWatchdog::new(p.IWDG, 512_000); // 512ms timeout
6656
iwdg.unleash();
@@ -69,36 +59,30 @@ async fn main(low_priority_spawner: Spawner) {
6959
let can_in = CAN_IN.init(PubSubChannel::new());
7060
let can_out = CAN_OUT.init(PubSubChannel::new());
7161
// Can RX on PB8 can TX on PB9
72-
let can = embassy_stm32::can::Can::new(p.CAN, p.PB8, p.PB9, Irqs);
62+
let can = embassy_stm32::can::Can::new(p.CAN, p.PA11, p.PA12, Irqs);
7363

7464
// Start main CAN RX/TX tasks
7565
can::spawn(can, low_priority_spawner, can_in.publisher().unwrap(), can_out.subscriber().unwrap()).await;
7666

77-
// Run a task which listens to CAN messages and publishes flight mode information
78-
let flight_mode = FLIGHT_MODE.init(PubSubChannel::new());
79-
low_priority_spawner
80-
.spawn(can::run_flight_mode_listener(can_in.subscriber().unwrap(), flight_mode.publisher().unwrap()))
81-
.unwrap();
82-
8367
// Run LED task
8468
low_priority_spawner
85-
.spawn(leds::run(flight_mode.subscriber().unwrap(), p.SPI3, p.PB5, p.DMA1_CH1, p.DMA1_CH2))
69+
.spawn(leds::run(can_in.subscriber().unwrap(), p.SPI3, p.PB5, p.DMA1_CH1, p.DMA1_CH2))
8670
.unwrap();
8771

88-
// Run strain gauge task. TODO: split flash into separate task?
89-
let mut adc = Adc::new(p.ADC1, &mut Delay);
90-
adc.set_sample_time(embassy_stm32::adc::SampleTime::Cycles239_5);
91-
92-
low_priority_spawner
93-
.spawn(strain_gauge::run(
94-
address,
95-
adc,
96-
p.PC0,
97-
p.PC1,
98-
p.PC2,
99-
p.PC3,
100-
flight_mode.subscriber().unwrap(),
101-
can_out.publisher().unwrap(),
102-
))
103-
.unwrap();
72+
// Run strain gauge task.
73+
// let mut adc = Adc::new(p.ADC1, &mut Delay);
74+
// adc.set_sample_time(embassy_stm32::adc::SampleTime::Cycles239_5);
75+
76+
//low_priority_spawner
77+
// .spawn(strain_gauge::run(
78+
// address,
79+
// adc,
80+
// p.PC0,
81+
// p.PC1,
82+
// p.PC2,
83+
// p.PC3,
84+
// flight_mode.subscriber().unwrap(),
85+
// can_out.publisher().unwrap(),
86+
// ))
87+
// .unwrap();
10488
}

0 commit comments

Comments
 (0)